perf(vk): an import mostly outside every game pool is trimmed back to its live parts #2825
Closed
github-sync
wants to merge 3 commits from
github/pr/2825 into main
pull from: github/pr/2825
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
1 participant
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!2825
Loading…
Reference in a new issue
No description provided.
Delete branch "github/pr/2825"
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?
Why. On an 8 GB card, Castle Town Market runs at ~54 fps with almost no long frames when it is loaded from a save, and at ~45 fps with a long frame every second or two when the player walks in from Hyrule Field, for as long as they stay. Part of the difference is VRAM the previous scene's pools still hold: imports never shrink.
0x108c711000leaves a 224 MB mirror where the Market loaded from a save has 48 MB; the culling lists' pool is 152 MB where it has 104 MB. With their shadows that is ~260 MB the scene does not bind, on a card where the Market's textures sit right at the 90% line.This is the launch-to-launch "fast or slow Market" from #995 and my note on #2685: it was not the launch, it was whether the save was in the Market or in the field.
What
gpu/vk/mirror_trim.h(rules only) andmirrors_trim()invk_memory.cpp, called frommemory_begin_submissionevery 64 submissions:retire_pool, as a pool made again larger is, and each part gets its page table entries and a mirror in the same submission's uploads, before anything is recorded. Each new mirror takes what the GPU wrote from the old one throughmirror_make's existing inherit (pages the CPU stamped since come from guest memory, as there). The old mirror goes once the GPU is done with it.RX_VK_MIRROR_TRIM=0: off. One log line a trim.What is given up: what the GPU wrote into the dead part of the old mirror and never wrote back. No game pool covers that memory any more, so nothing can bind it; a pool the game makes there later gets a mirror from guest memory, as any new pool does.
Measured
Note: every run below had this PC's DLSS model row on Transformer 2 (L), not the default K: the harder case (more GPU time and VRAM, #2836). With K the gain is smaller; those numbers are in the comment below.
Windows 11, RTX 3060 Ti 8 GB (driver 32.0.16.2114), Ryzen 5 5600X, 2560x1440 DLSS.
mainba005ca4with #2711, #2731 (atbce3d17e, where it was still on by default), #2685 and #2709 merged locally (the VRAM picture on this card without #2711 is the #2690 regression). Load the save, teleport to Hyrule Field, 10 s there, teleport to the Market, settle, then 80 s of the camera turning.RX_VK_FRAMEGEN=0,HOS_FPS_MAX=240. Three runs an arm, arms alternated. The fourth arm adds #2826 (the 90% rule evicting its excess instead of an eighth of the cache), which is the other half of the same stutter.pool 0x108c000000..0x109a400000 (228 MB) trimmed to its 1 live part(s), 48 MBand0x1178400000..0x1181c00000 (152 MB) ... 104 MB. Mirrors at the end of a run: 1,584 MB against 1,820 MB. The new imports took under 2 ms each (noimported inline, which is logged from 2 ms).A session: two rounds of 12 scenes by teleport (Hyrule Field, Kokiri Forest, Market, Kakariko, Lost Woods, Goron City, Zora's Domain, Graveyard, Death Mountain Trail, Zora's River, Sacred Forest Meadow, Gerudo Valley), 20 s of camera turning in each, with the flash detector on (
RX_VK_FLASH=1,RX_VK_SHOT_EVERY=1): 24 of 24 scenes reached, 13 trims (600 MB given back in all), no flash detected, no device loss, no failed import. The mirrors moved between 1.2 and 2.0 GB and ended at 1,656 MB; the same tour before this sat at the 2,000 MB budget. Screenshots after the camera turn in Kokiri Forest (foliage, grass), Hyrule Field and the Market look right; I did not watch the runs live.Not tested
retired_give_back.h), which this does not change but does feed.RX_VK_PARALLEL(the trim does not run inside its segments, and was not run with it on).Tests
tests/test_mirror_trim.pycompiles and runstests/test_mirror_trim.cpp(the Market's case, imports their pools fill, a small dead share, no live pool, parts joined and kept apart, too many parts, the waits and the count that starts over, the VRAM gate) and checksvk_memory.cppkeeps the order: parts imported before the retire, mirrors made before the old one stops waiting to be inherited, room in the budget first.nx2/test_import_windows.py's model ofPoolgets the two new fieldspool_lookupcarries over;tests/test_texel_buffer_ownership.pycounts the trim's rollback among the callers ofdestroy_import_buffer(10).python -I -B tests/run_all.pyon Windows: 460 passed, 2 failed, 34 skipped; the two (nx2/test_nup_paths.py,tests/test_input_background.py) fail on unchangedmainhere too.🤖 Generated with Claude Code
CI note: the one red check (Python source checks, ubuntu part 2/2) is
tests/test_release_scripts.py'stest_nightly_two_runs_at_once_end_at_the_newer_commit(AssertionError: 1 != 0 : nightly: moved forward from 1111... to bbbb...), the two-uploads-at-once case that has failed at random before (#2644's description mentions it). This PR touchesgpu/vk, its tests and one doc row only; the other 233 checks of that part passed, and #2826, opened a minute later on the same base, is green. A re-run should clear it; I can't trigger one from a fork.A correction to the measurements above, found an hour after opening this: this PC's DLSS model row was set to Transformer 2 (L), not the default (K), for every run in the description. L costs ~4 ms more GPU time a frame on this card (#2836) and, judging by the count, more VRAM too, so it is the harder case. The same test with the default model (
RX_VK_DLSS_PRESET=K, everything else as in the description, 80 s, two runs an arm, alternated; "both" is #2825 + #2826):This clears
inherit_pendingon the old pool whether or not each part actually got a mirror.mirror_makecan leave a part without one: there'smirror_budget(), where the check inmirrors_trimcounts onlykeptand not the other pools already inmirror_wanted, which come first in the list; there'sRX_VK_MIRROR_TIGHT; and there'screate_device_bufferfailing, which is more likely here because this only runs over 80% VRAM. A part left like that points at its import, and the old mirror is then freed once the GPU is done with it. If the old pool wasgpu_onlyorgpu_written, what the GPU wrote in the live part is gone, and the part's mirror is later made from guest memory.Suggestion: clear
trimmed/inherit_pendingonly when every part'smstateisMIRROR_ON. Otherwise leave it pending, so that the part's nextmirror_makestill inherits, and the existingkIdleEpochslimit still bounds how long that can take. You could also count the currentmirror_wantedin the room check (comment below). A check for this intest_mirror_trim.pywould be good too.The room check should also count what's already in
mirror_wanted(asmemory_begin_submissiondoes just after). Otherwise the parts can be the ones refused for budget, as described above.Nit: with
atoi,RX_VK_MIRROR_TRIM=on(ortrue/yes) turns the trim off. The docs' on/off rule accepts those words, so I'd parse it the way the other switches do: for example off only when the value is one of0/off/false/no, like theenv_switchhelpers invk_dlss.cpp/vk_fsr4.cpp.Thanks, this is a well-argued change, and the honest correction about the DLSS model row helps a lot. The rules header is easy to follow, and so is the "import all parts first, then retire" order. Reusing
retire_pooland the existing inherit means in-flight work is covered the same way a pool made again larger is: the old import and mirror stay untilretired_valueis done, the page table entries go at retirement, and the parts' mirrors are made in this submission's uploads before anything is recorded. I don't see a sync problem there.One real issue (inline): the old mirror stops waiting to be inherited even when a part didn't get its mirror. Then what the GPU wrote there is lost for that part, which is the Kokiri-foliage class of bug that the inherit was added for.
Other points:
import_chunk()is still 16 MB on main and at this head. 4 MB comes from #2685, which was in your local stack, as is themirror_on()stub this PR adds tonx2/test_import_windows.py(the same line #2685 adds). On main alone the parts round out to 16 MB, so less is given back (the culling lists' 152 MB import is close to the quarter line), and the measured numbers don't describe this PR on its own. I'd rather land #2685 first and this after it. Please say that in the description; I'll handle the order.trimmed tolines and no device loss would do.#include "wb_learn.h", so a merge of main fixes it.tests/test_release_scripts.py's two-nightlies-at-once case, which has nothing to do with this PR. I'll re-run it.memory_begin_submissionand retirement (finalized pools, no inherit), so whichever lands second will need a small rebase. Your description already explains how the two cover different cases.I pushed two commits on top of yours (a fast-forward, nothing of yours rewritten):
daf6b17: merges main. The only conflict was the include line, and bothmirror_trim.handwb_learn.hare kept.18a733a: the fix for the refused-mirror case from my last review. A newtrims_settle()keeps the old mirror waiting to be inherited until every pool over its range has a mirror of its own. A part refused one (budget,RX_VK_MIRROR_TIGHT, a failed allocation) still inherits from it when its mirror is made later, as a pool made again larger does, andkIdleEpochsstill bounds the wait. A part refused for a failed allocation is tried again at its next binding. The room check now also counts what's already inmirror_wanted, and no trim runs while the tight rule would refuse every mirror.RX_VK_MIRROR_TRIMis read withenv_value_off, soon/true/yesno longer turn it off.test_mirror_trim.pynow also compilestrims_settleand checks that a part refused its mirror keeps the old one.The related checks pass locally, and so does the renderer build with the ABI check. Approved as the maintainer. Still welcome but not blocking: the K-model runs I asked for, and an AMD or Linux run with the
trimmed tolines in the log.The runs with the default DLSS model (K) that were asked for, on
mainas merged (bd9b07e6: #2711, #2685, #2624, #2731 opt-in, #2825 with the maintainer's commit, #2826). One build; the arms are the two switches (RX_VK_MIRROR_TRIM=0,RX_VK_EVICT_EXCESS=0), so "off / off" is main without the two.Windows 11, RTX 3060 Ti 8 GB (driver 32.0.16.2114), Ryzen 5 5600X, 2560x1440,
RX_VK_DLSS_PRESET=K. Save, teleport to Hyrule Field, 10 s, teleport to the Market, settle, 80 s of the camera turning.RX_VK_FRAMEGEN=0,HOS_FPS_MAX=240. Three launches an arm, alternated. The counts are from the log inside the measured window: uploads from the 120-present lines, the rest from thetextures retiredandVRAM compactionlines.The 6 GB model (
RX_VK_TEST_ALLOWED_MB=5632) for #2826's 97% path is still to come, later today.