fix(vk): a pool whose mirror found no VRAM is asked for again, not refused for good #2618
Open
aquinas22 wants to merge 1 commit from
aquinas22/fix/mirror-no-vram-retry into main
pull from: aquinas22/fix/mirror-no-vram-retry
merge into: MagicBeanDev:main
MagicBeanDev:main
MagicBeanDev:github/pr/2893
MagicBeanDev:github/pr/2890
MagicBeanDev:github/pr/2887
MagicBeanDev:github/pr/2878
MagicBeanDev:github/pr/2875
MagicBeanDev:github/pr/2874
MagicBeanDev:github/pr/2873
MagicBeanDev:github/pr/2871
MagicBeanDev:github/pr/2756
MagicBeanDev:github/pr/2897
MagicBeanDev:github/pr/2896
MagicBeanDev:github/pr/2718
MagicBeanDev:github/pr/2842
MagicBeanDev:github/pr/2691
MagicBeanDev:github/pr/2895
MagicBeanDev:github/pr/2743
MagicBeanDev:github/pr/2765
MagicBeanDev:github/pr/2708
MagicBeanDev:github/pr/2732
MagicBeanDev:github/pr/2755
MagicBeanDev:github/pr/2817
MagicBeanDev:github/pr/2876
MagicBeanDev:release-please--branches--main
MagicBeanDev:release-please--branches--main--release-notes
MagicBeanDev:github/pr/2778
MagicBeanDev:github/pr/2888
MagicBeanDev:github/pr/2866
MagicBeanDev:github/pr/2877
MagicBeanDev:github/pr/2800
MagicBeanDev:github/pr/2829
MagicBeanDev:github/pr/2850
MagicBeanDev:github/pr/2889
MagicBeanDev:github/pr/2882
MagicBeanDev:github/pr/2860
MagicBeanDev:github/pr/2849
MagicBeanDev:github/pr/2886
MagicBeanDev:github/pr/2794
MagicBeanDev:github/pr/2840
MagicBeanDev:github/pr/2830
MagicBeanDev:github/pr/2834
MagicBeanDev:github/pr/2719
MagicBeanDev:github/pr/2742
MagicBeanDev:github/pr/2741
MagicBeanDev:github/pr/2820
MagicBeanDev:github/pr/2771
MagicBeanDev:github/pr/2859
MagicBeanDev:github/pr/2858
MagicBeanDev:github/pr/2823
MagicBeanDev:github/pr/2757
MagicBeanDev:github/pr/2769
MagicBeanDev:github/pr/2844
MagicBeanDev:github/pr/2843
MagicBeanDev:github/pr/2673
MagicBeanDev:github/pr/2855
MagicBeanDev:github/pr/2739
MagicBeanDev:github/pr/2807
MagicBeanDev:github/pr/2851
MagicBeanDev:github/pr/2789
MagicBeanDev:github/pr/2814
MagicBeanDev:github/pr/2818
MagicBeanDev:github/pr/2774
MagicBeanDev:github/pr/2772
MagicBeanDev:github/pr/2750
MagicBeanDev:github/pr/2786
MagicBeanDev:github/pr/2785
MagicBeanDev:github/pr/2782
MagicBeanDev:github/pr/2779
MagicBeanDev:github/pr/2775
MagicBeanDev:github/pr/2706
MagicBeanDev:github/pr/2766
MagicBeanDev:github/pr/2747
MagicBeanDev:github/pr/2759
MagicBeanDev:github/pr/2745
MagicBeanDev:github/pr/2740
MagicBeanDev:github/pr/2733
MagicBeanDev:github/pr/2725
MagicBeanDev:github/pr/2727
MagicBeanDev:github/pr/2728
MagicBeanDev:github/pr/2729
MagicBeanDev:github/pr/2721
MagicBeanDev:github/pr/2722
MagicBeanDev:github/pr/2715
MagicBeanDev:github/pr/2714
MagicBeanDev:github/pr/2707
MagicBeanDev:github/pr/2703
MagicBeanDev:github/pr/2705
MagicBeanDev:github/pr/2697
MagicBeanDev:github/pr/2679
MagicBeanDev:github/pr/2676
MagicBeanDev:github/pr/2669
MagicBeanDev:docs/what-hookshot-connects-to
MagicBeanDev:fix/hdr-encode-test
MagicBeanDev:fix/main-tests
MagicBeanDev:fix/play-cache-files
MagicBeanDev:perf/game-barrier-order
MagicBeanDev:feat/heap-lazy
MagicBeanDev:feat/texture-detail
MagicBeanDev:perf/astc-opaque-bc1
MagicBeanDev:fix/fs-dir-lock
MagicBeanDev:fix/framegen-native-kept
MagicBeanDev:fix/update-no-downgrade
MagicBeanDev:port/k10-gcn-f16-mirrors
MagicBeanDev:fix/framegen-mult-cap
MagicBeanDev:chore/release-as-2.1.1
MagicBeanDev:perf/pcache-load-switch
MagicBeanDev:fix/prepare-progress-log
MagicBeanDev:fix/update-closes-stale-game
MagicBeanDev:fix/hdr-scaled-sampled
MagicBeanDev:perf/first-start-preset
MagicBeanDev:perf/zero-upload
MagicBeanDev:fix/vcore-escape-2s
MagicBeanDev:test/fake-gh-atomic
MagicBeanDev:perf/batch-idle-flush
MagicBeanDev:test/finish-stub-a
MagicBeanDev:chore/record-2.1.0
MagicBeanDev:fix/main-red-tests
MagicBeanDev:fix/test-display-startup-driver-cache
MagicBeanDev:feat/mesh-shader-mode
MagicBeanDev:fix/menu-probes-at-startup
MagicBeanDev:fix/fps-counter-classic
MagicBeanDev:fix/test-log-batch-flake
MagicBeanDev:fix/narrow-screen-hud
MagicBeanDev:ci/prereleases
MagicBeanDev:release/v2.0.15
MagicBeanDev:hotfix/v2.0.15
MagicBeanDev:fix/present-ahead-replay
MagicBeanDev:release/v2.0.13
MagicBeanDev:fix/bracket-paths-2013
MagicBeanDev:fix/hdr-overlays-2013
MagicBeanDev:fix/fps-counter-scale-2013
MagicBeanDev:fix/stick-seen-at-rest
MagicBeanDev:hotfix/menu-fits-screen
MagicBeanDev:fix/f1-always-opens-menu
MagicBeanDev:fix/ini-keep-lines
MagicBeanDev:claude/cranky-pike-7cf247
MagicBeanDev:perf/texture-answer-flat
MagicBeanDev:fix/stamp-test-modules
MagicBeanDev:fix/appimage-fonts
MagicBeanDev:fix/display-awake
MagicBeanDev:release/v2.0.12
MagicBeanDev:fix/discord-windows
MagicBeanDev:rc/v2.0.12-tests
MagicBeanDev:fix/dlss-keep-feature
MagicBeanDev:rc/v2.0.12
MagicBeanDev:button-icons
No reviewers
Labels
Clear labels
accessibility
Barrier affecting people with disabilities
autorelease: pending
autorelease: tagged
bug
Something isn't working
dependencies
Pull requests that update a dependency file
documentation
Improvements or additions to documentation
duplicate
This issue or pull request already exists
enhancement
New feature or request
github_actions
Pull requests that update GitHub Actions code
good first issue
Good for newcomers
help wanted
Extra attention is needed
invalid
This doesn't seem right
question
Further information is requested
testing wanted
wontfix
This will not be worked on
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
Milestone
Clear milestone
No items
No milestone
Assignees
Clear assignees
No assignees
2 participants
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2618
Loading…
Reference in a new issue
No description provided.
Delete branch "aquinas22/fix/mirror-no-vram-retry"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
On cards with little VRAM, the scene's hottest memory pool can lose its VRAM mirror for the whole session: when
mirror_make's allocation fails once (VRAM full of textures and retired pools' mirrors at that moment), the pool isMIRROR_REFUSEDfor good and the GPU reads it over PCIe from then on. This asks for it again 64 submissions later instead.What I saw. RTX A1000 Laptop (4 GB), Windows, v2.1.0, Kokiri Forest. One 144 MB pool takes ~2.3 GB of the ~2.4 GB the GPU reads over PCIe in 600 submits (the
bound in guest memory, by poollog line), state 3 (refused). The process is over what the driver allows (~4.2 of 3.3 GB), so VMA'sWITHIN_BUDGETallocation fails at the pool's first binding; allocating without the budget flag fails too here (NVIDIA does not oversubscribe), so a "make it anyway" fallback is not an option. Whether the pool got its mirror depended on load timing, which made frame rates swing between launches of the same build.The change. On an allocation failure
mirror_makeleaves the poolMIRROR_NONEand notes the submission (Pool::no_vram_epoch);gpu_buffer_bindneither makes nor asks for its mirror again until 64 submissions on, so a full VRAM costs one failed allocation per pool per 64 submissions, not one a binding. By then colder or retired pools' mirrors have usually been given back. Both outcomes are logged (8 lines each at most).RX_VK_MIRROR_NO_RETRY=1keeps the old behaviour; documented indocs/development.md.Measured (headless,
HOS_ENV_FILEper run, same save, ~4240 draws a frame, 90 s after 45 s of play; with #1377's mirrors-by-heat andRX_VK_WB_MAX_KB=64, built on v2.1.0):The pool was mirrored ~70 submissions after its first failure in every run. A third launch measured after only 20 s of play read 25.1 fps while mirrors were still arriving.
Checks.
tests/run_all.pyfor every test that readsvk_memory.cppordocs/development.mdpasses locally on Windows (llvm-mingw), excepttest_env_on(HOS_FPS_LOGinhost/oot_high_fps.c, failing on main too) andtest_vram_budget_row(fails on main locally, unrelated).test_parallel_mirror_makestill finds!paron the first-binding line.Independent of #1377, which makes the retry more likely to find room (it gives back colder mirrors for hot pools).
Measured on a 10 GB card too: the refusal is not only a 4 GB problem.
Setup. RTX 3080 10 GB, driver 615.78, Linux (GNOME on Wayland), i7-11700, 2560x1440 at 100% render scale, cap 170, frame generation off. Built from upstream
mainof 2026-11-11 with the renderer's profile and ThinLTO, with and without this PR's commit (58834871) cherry-picked. The renderer was swapped between runs and everything else held fixed.RX_VK_WB_MAX_KBwas unset. Runs were interleaved, each started below 70 C, with other programs at 2.5-4.7% of the CPU.scene_changes(the bench route that teleports through several maps; 20 s measured):bound in guest memory ... state 3, 84 log lines)With the PR, 8 pools a run (16-240 MB) hit "no VRAM for its mirror" during the scene loads. All of them got their mirror 64-87 submissions later; one 240 MB pool took 468. VRAM was at ~7.8 of 9.4 GB.
Without it, the refused pool's binds in guest memory raise the in-place part, so every readback waits for its whole submission. That wait is what costs the frame rate here, more than the PCIe reads themselves.
The same refusal turned up in a Hyrule Field run on this card. A 224 MB pool, merged from three imports during the teleport while VRAM was at 8.05 of 9.34 GB, stayed refused, and the run measured 52.2 fps. The runs that found room measured 62.9-71.5.
The 1% lows (the scene loads themselves) are unchanged. I saw no visual difference, but I did not check screenshots.
I merged this into current main. The only conflict is in docs/development.md, where #2651 added the RX_VK_CAPTURE_KEEP_OLD row in the same spot; both rows can stay. The code merges cleanly. Every test that reads vk_memory.cpp or development.md passes on Linux, including test_import_buffer_binding, test_texel_buffer_ownership, test_parallel_mirror_make and test_mirror_inherit.
The logic is sound. A failed create_device_buffer leaves the pool MIRROR_NONE with no_vram_epoch set, and gpu_buffer_bind neither makes nor queues its mirror for 64 submissions. A success logs how long it took and clears the epoch. Two small notes, neither blocking:
Your A1000 numbers and sheepbun's RTX 3080 runs (43.9/45.5 against 68.7/61.4 fps in scene_changes) both come from the game. Please rebase; once it's rebased, I'd merge it.