Repository navigation
perf: reduce map picker memory with a page-backed thumbnail cache - #47
Merged
Merged
Conversation
This also adds a new "Prefer Reduced Motion" and "Blur Modals" options in order to tweak for those bad hardwares
Owner
|
Unfortunately needed to revert the blur optimization: it was VERY clever but it didn't respect window geometry (was looking very bad on wayland) + animations, so i added some toggles for animations and motion on the appearance tab, will revisit some form of it in the future tho. Also to make you happy i added a GruvBox palette. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a page-backed (unmanaged) thumbnail cache for map picker backgrounds and removes the two things that made the picker grow the process by hundreds of megabytes. Measured on Windows x64, 2880x1800 @175%, the real app window, the real picker and a 95-mapset / 87-background library.
Commits
feat: add OS page allocator for unmanaged bitmap buffers— newUtils/PageAllocator.cs(VirtualAlloc/mmap),AllowUnsafeBlocksin the csproj (required byLibraryImport).feat: add page-backed thumbnail cache with Avalonia decode and resize— newUtils/BitmapStorageHandler.cs:Load(path)decodes withBitmap.DecodeToWidth, center-crops to the 2:1 page and blits straight into a pre-allocated page; path-keyed cache hits skip decode and blit entirely.feat: serve map picker card backgrounds from the thumbnail cache— one line inSongMapsetCardViewModel.BuildBackgroundImage.perf: virtualize the map picker mapset list—ItemsPanelStackPanel→VirtualizingStackPanel.perf: freeze the modal backdrop blur instead of animating a live effect—ModalHostno longer keeps a liveBlurEffecton the window shell.Why
1. A live
BlurEffectoverWindowShell(which contains the whole app UI), animated 0→10 px over 260 ms, with aBitmapCache. Every composited frame re-blurs a full window-sized surface (~20 MB at 2880x1760) and the native allocator never returns it. Because the effect stays attached while the modal is open, any animation above it re-blurs the window — that also covers the picker's compositor-driven smooth scrolling.2. The picker list was not virtualized.
ItemsControl+StackPanelrealized every loaded card (~1.2 MB each) and paging appends 20 more per scroll, so memory grew with the library.Measurements
For context, an empty maximized window in a bare Avalonia app on this display already measures 245.9 MB (77 MB before the window) — that floor, not the picker, is most of the process footprint.
Trade-offs / behaviour changes
BitmapStorageInfo.BitmapStorageHandler.Shared), committed lazily on first use (20 × 512 KiB) and returned to the OS at exit.VirtualizingStackPanelthe scroll extent is estimated while further pages load; worth an eyeball on a very large library.BeatmapSourceCardstill useArtworkBitmapUtils.DecodePreviewat 800/1200/480 — unchanged here.Notes for review
new Bitmap(PixelFormat, AlphaFormat, IntPtr, …)copies into anSKBitmap, and the uncopied route (protected Bitmap(IBitmapImpl)) needsIDrawableBitmapImpl, which is internal toAvalonia.Skia. So aLoadcosts one 512 KiB memcpy; the cache saves the decode + crop, not the copy.RenderTargetBitmap.Render(Visual)passes (blur applied through an off-treeImagewithBlurEffect). Scaling a render target through its DPI is not reliable on this window: with the extended-client-area window the content is drawn 1:1 into the smaller bitmap, which shows up as a magnified, cropped backdrop.#if windowswas never defined (SDK definesWINDOWS, and only for OS-specific TFMs), so the UNIX allocator branch was the one every Windows build ran. Also fixed:MEM_LARGE_PAGEScan never be satisfied by a 512 KiB page (needs 2 MiB granularity +SeLockMemoryPrivilege), andVirtualFreemust be called withMEM_RELEASEalone anddwSize == 0.Testing
No permanent tests added —
MapWizard.Testshas no Avalonia host. Verified with throwaway harnesses (deleted) that drove the realMainWindow, the real picker view model and the real song library:SetBackgroundActive; 25-card scroll sweep; reactivation served from the cached page rather than a re-decode (file replaced in place to prove it).dotnet buildis clean (one pre-existingMainWindow.axamlAVLN3001 warning).Untested here: the UNIX
mmappath (no Linux/macOS available).