perf(vk): the 90% rule and the driver's limit evict their excess, not an eighth of the texture cache #2826
Closed
github-sync
wants to merge 1 commit from
github/pr/2826 into main
pull from: github/pr/2826
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!2826
Loading…
Reference in a new issue
No description provided.
Delete branch "github/pr/2826"
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 the texture cache sits right at the 90% line in the larger scenes. When it is a few MB over, the pass lets go of an eighth of the cache, textures that were on screen a second ago, and they are made again within the camera's turn: a loop of evictions, uploads and compaction runs that shows as a long frame every second or two.
rule_targetcuts to 7/8 of the rules' budget once the textures hold more than it. That is right for a budget that is a size (the card's or a preset's).2,873 MBof textures, 62 MB over the 90% line. The pass's target was7/8 x 2,811 = 2,460 MB: ~400 MB of textures unused for 60 presents go, all in view again a few seconds later. The log shows it as a sawtooth,textures retired ... budget 386 / 362 / 295and the cache going 2.95 -> 2.5 -> 2.95 GB, with 8-9 uploads a frame, where the same scene loaded from a save (just under the line) has 0.What
vram_compact::rule_target(total, size_budget, rule_budget): the budget's size still asks for its 7/8. A rule budget below the size (the 90% rule, the driver's limit) asks for its excess andkExcessSlack(64 MB) more, so that not every pass is one. The lower of the two when both apply.texture_framekeeps the budget as it was before the 90% rule (size_budget) and passes both.RX_VK_EVICT_EXCESS=0: to 7/8 of their budget, as before.RX_VK_COMPACT, the default) goes throughrule_target; with the compaction off nothing changes.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. 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 other half of the same stutter is #2825 (the previous scene's oversized mirrors), so both are shown alone and together.Not tested
Tests
tests/test_vram_compact.cpp: the three-argumentrule_target(no rule, the excess alone with the Market's numbers, the size's 7/8, both, a budget under the slack), next to the existing cases, which are unchanged.tests/test_vram_compact.pycheckstexture_framekeepssize_budgetand calls it, with the switch.python -I -B tests/run_all.py vram_compact texture_squeeze vram_pressure vram_budget preset_texture target_image(the checks that read this code): 6 passed. The full suite was run on #2825's branch (460 passed; the 2 that fail there fail onmainhere too), not again on this one.🤖 Generated with Claude Code
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):rule_budgethere is the lower of the 90% rule's and the 97% rule's budgets, so the driver's limit gets the 64 MB slack too (see the review body). To keep the deeper cut at the limit,texture_framecould pass the 90% rule's budget here and keep the old two-argument form for the 97% part, taking the lower of the two.This is the 97% rule's contribution to
rule_budget, which now also goes through the excess-only target. #2711 changes the condition above, so expect a small rebase.Thanks, the diagnosis reads right to me, and the change is small and contained. The 90% rule's budget is "total less the excess", not a size, so a 7/8 cut of it is ~an eighth of the cache for a few MB over the line. The hold was already fixed the same way. I traced it through
texture_frameand found no correctness problems:total_bytescounts allocation size (bytes + pad), andhold_metcounts the chosen textures the same way. The excess comes from the driver's count less the textures' pool free space, and an evicted texture's allocation goes straight into that free space. So a pass that takes excess + 64 MB really does bring the 90% rule's pressure 64 MB under the line by the next budget pass, ten presents later, oncedeadhas been freed.rule_tois only used in the hold's pass, for textures unused 1-10 s. Older ones still go to the 85/80 lines. Each pass is LRU and bounded, and the next one needs 64 MB of new pressure. Where the scene's working set is really bigger than the room under the line, you'll see more small passes instead of fewer big ones. That's an improvement: re-uploads can only go down when less goes per pass. One thing it doesn't fix: an evicted texture whose memory isn't in the textures' pool doesn't lower the count, so the next pass sees the same excess. That happened with the 7/8 cut too, only bigger, and #2711 handles that case for the 97% rule.budget / 8 * 7).One thing the description gets wrong, and it matters on small cards. "Not tested" says the 97% rule is unchanged and still takes its share. With the compaction on, the 97% rule's cut now always goes through
rule_targetas excess + 64 MB too. It only fires while the blocks hold less than an eighth of the allowance free, so the reclaim is always under the 17 points that would put its budget below the reserve's. That makesevict_tonon-zero and the pass is the hold's. The title says this, but the reasoning about 4-6 GB cards doesn't. At 97% the driver is about to page VRAM out, which costs far more than re-uploading a texture, so the eighth of headroom was doing something there. I'd either keep the 7/8 cut for the driver's limit and take only the 90% rule's excess (track the 90% rule's budget apart from the 97% one), or show it holds on a smaller allowance.RX_VK_TEST_ALLOWED_MB=5632on the 3060 Ti gives a 6 GB card's allowance, and a few minutes of the field and the Market with the 97% line's log lines would do.On the numbers (thanks for posting the correction quickly): with the default DLSS model (K), the only measurement is #2825 + #2826 against the main stack, two runs each, and that's within noise. Since this one changes the default eviction for every card, could you add the pair that isolates it with K: #2825 alone vs #2825 + this PR, same Market-from-the-field route? Please include the count the description already uses: uploads a frame and the
textures retiredsawtooth. That shows the mechanism much more clearly than fps does. A base without #2731's old default-on state would also match what will land.CI is green and the branch merges cleanly. It will need a small rebase after #2711, which edits the 97% rule's condition three lines above your hunk; the two are complementary. #2834 touches
vram_compact.hand its tests in other places.Edit: a same-route A/B shows a much smaller effect than the play-session numbers below (6% fewer textures let go, the same fps). See the correction.
Data from a Steam Deck, where the budget's size is what evicts: the same sawtooth, and the same remedy seems to work there
This PR is not in my renderer, but reading it explained what I see on a Steam Deck. Here are the numbers, in case they help for the case the PR leaves at 7/8: a budget that is a size.
Setup. Steam Deck OLED (Van Gogh
0x1435, 16 GB), SteamOS 3.9.5, kernel 7.2.7-valve2, Mesa 26.2.0-devel (RADV). Game built from main atc544828f. Renderer:c544828fwith #2789, #2644, #2774, #2731, #2767 and #2649 merged, plus the readback test patch of this comment, in both sessions below.RX_VK_VRAM_MB=2000,RX_VK_TEX_MAX_MB=128,RX_VK_TEX_MAX_DIM=1024,RX_VK_LOW_SPEC=1.Which rule evicts here. The driver's count stays under the 90% line (
VRAM: 4743 MB used of 5977 MB allowed (1647 MB of it free in the blocks); textures 1976 MB (budget 2000 MB)), so what evicts is the budget's size, and the cuts land at 1,742 to 1,750 MB:budget / 8 * 7. The budget is that low because on this APU the textures are system RAM, and RAM is what runs out.As it is, in play (13 minutes): 170 passes for the budget, 16,485 textures let go (1,250 a minute); 51
a frame's uploads took ...lines, 5.9 s in all.With the excess and 64 MB instead (7 lines, below:
RX_VK_EVICT_SLACK_MB=64makes the plain pass stop at the budget less 64 MB), 10 minutes in play: 68 passes, 6,811 textures (663 a minute), the cache never under 1,920 MB after a pass; 11 such upload lines, 1.0 s in all.Limits. One Deck, one session each, two different routes. A system memory setting also changed between the two sessions (
MemoryLow8G to 6G, the edit at the top of this comment), which probably accounts for part of the fps difference in the heaviest scenes. The eviction counts and the upload lines are the numbers I trust; the fps table is there for completeness.So where the budget is a size well under what the driver allows, cutting to 7/8 of it seems to cost what this PR removes for the 90% rule. Whether that belongs here, in a follow-up or behind a switch is yours to judge. I am glad to run a build on this Deck and report.
The test patch (gpu/vk/vk_texture.cpp, on main
c544828fwith the six PRs above)I accept this as is, including the 97% path now taking the excess plus 64 MB too. Approved as the maintainer.
Not a blocker, but please keep an eye on 4-6 GB cards. If you can, post a run with
RX_VK_TEST_ALLOWED_MB=5632(the field and the Market, with the 97% line's log lines). This one goes together with #2825. #2711 edits the 97% condition three lines above your hunk, so whichever lands second may need a small rebase.Thanks for the review and the merge. Two notes for the record:
85ba2fd7to this branch a few minutes after the merge, so it is not in main. I'll run the 6 GB model you asked for (RX_VK_TEST_ALLOWED_MB=5632, the field and the Market, with the 97% line's lines) on main as merged, and open that commit as a follow-up only if the run shows the limit needs its eighth.bd9b07e6(the arms byRX_VK_MIRROR_TRIM/RX_VK_EVICT_EXCESS); I'll post them here and on #2825 with the uploads and thetextures retiredcounts.@hclaria thank you for the Deck numbers: 1,250 -> 663 textures a minute and 5.9 -> 1.0 s of upload stalls is the same sawtooth one rule over. That case (a budget that is a size, well under what the driver allows) is not touched by this PR, and your seven-line patch is the natural follow-up. It is yours to open if you want it; I only have this one NVIDIA card, so I could not measure it where it matters.
The K-model runs for this PR and #2825 apart, on main as merged, are in this comment on #2825: on top of #2825 this one takes the textures let go for the budget from ~10,700 to ~4,900 in 80 s, the uploads from 2.7 to 1.5 a frame and the replay's submit from 12.2 to 11.4 ms, at the same frame rate within three runs. The 6 GB model run is still to come.
Correction to my numbers above: on the same route the effect is small, and I am not opening a follow-up for it
I ran the A/B the earlier comment lacked, and it does not hold up the play-session figures.
Setup. Steam Deck OLED (Van Gogh
0x1435, 16 GB), SteamOS 3.9.5, Mesa 26.2.0-devel (RADV). Game built from main atc544828f. Renderer: main at284c9121(it has this PR) plus the size-budget change, one binary for both arms,RX_VK_EVICT_SIZE_EXCESS=0against the default.RX_VK_VRAM_MB=2000,RX_VK_LOW_SPEC=1. Headless, a save in play, Link standing still and the camera turning the same way for the whole run, 240 s measured, runs alternated.a frame's uploads tooklinesSo: 6% fewer textures let go, 5% fewer uploads, fewer long upload frames, the same fps within the noise, and a wider spread of frame times with the change (more passes, each with its own work).
What was wrong with the earlier numbers. The two play sessions were not the same route, and a system memory setting changed between them. A first scripted A/B with Link walking had the same fault: the two arms ended up looking at different views (1,320 against 1,961 draws), and two runs of the same arm differed by a third in textures let go (11,696 and 8,757). Only the turning camera gave runs that repeat.
Why the cut matters so little here. Over a full turn this scene uses more textures than the 2,000 MB budget holds, so the budget lets textures go at the rate new ones come in, whatever the size of each cut. The cut only decides how far under the line the cache sits (1,750 or 1,930 MB), a few percent of capacity. The lever on this machine is the budget's size, not the rule.
The patch stays in the comment above for anyone who wants to measure it on other hardware. I have added a note at the top of that comment pointing here.