fix(vk): HUD and lighting artifacts on GPUs without buffer mirrors #1795
Open
Brett-D wants to merge 4 commits from
Brett-D/fix/igpu-ub-snapshot into main
pull from: Brett-D/fix/igpu-ub-snapshot
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!1795
Loading…
Reference in a new issue
No description provided.
Delete branch "Brett-D/fix/igpu-ub-snapshot"
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 integrated GPUs without VRAM mirrors, a uniform buffer bound in place is the game's own memory, and the game rewrites it right after the submit while the GPU still runs. #2464 fixed that for Intel; this PR extends the same copy to the other integrated GPUs (measured on a Steam Deck) and reuses the copy within a recording.
What it changes
RX_VK_UB_SNAPSHOTis on when the pools have no mirror and the GPU is Intel (#2464's rule, kept) or integrated (VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU). Discrete GPUs without mirrors (GCN since #2343,RX_VK_MIRROR=0) keep main's default (off) until someone measures them.RX_VK_UB_SNAPSHOT_REUSE=0turns it off). A copy whose read failed is not kept.env_onreads a switch (unset uses the default; 0/off/no/false/empty is off).Steam Deck, current main (
6749ba21+ this)Kokiri Forest, Game Mode, 30 s frame logs, 80 screenshots a start. Frame generation was on, so FPS is presented; the game ran ~14-15 real.
With the snapshot off on current main, the screenshots are clean: #2419 did remove the slabs and stretched HUD seen before. The copy is still needed on the Deck for stability, at ~1 fps. The Linux suite in an Ubuntu 22.04 builder fails only what main fails there (
test_prof_clock,test_cpp_preflight: the container's g++ 11).@NotRexed When you have time, could you please review this? It fixes HUD and lighting artifacts on integrated GPUs (measured on a Steam Deck); discrete GPUs keep their current behavior. Thank you!
Heads-up: this conflicts with main now (a lot landed today). Could you merge main into the branch (or rebase) and resolve the conflicts? Thanks!
Merged current main (
f8efe05) into the branch. The only conflict was tests/run_all.py: main now reads each test's own declarations, so the two new tests declareRUN_ALL_ARGS = ["--cxx", "{cxx}"](and skip on Windows, as their fixtures set the switch with POSIX setenv). The diff against main is the three commits again. Linux game-free suite: identical to clean main apart from the two added passing tests; a full Linux x86-64-v3 game build of main plus this compiles with 0 failures.Thanks for the merge with main and for the Steam Deck numbers, they make this easy to follow.
Uniform-buffer snapshot (commits 1-2): looks right to me. Binding a UB in place on a no-mirror device reads live guest memory, which breaks design rule 2 ("nothing the replay reads comes from live guest memory"). Copying through
memory_read_as_submittedis the fix that rule asks for, and discrete GPUs keep their path. On the reuse cache, I checked:cap_view_genis bumped on every page stamp, every collected write and every capture publish, so a hit at the same view gives the bytes a fresh read would. That's the same rule the existingsubmitted_pagecache uses.ub_overlayruns before the cache, so inline UB updates later in the recording still win.c1_atrelies on), and the map is cleared per recording indraw_begin_recording.curis thread_local, so there's no sharing between threads.Nit: when
memory_read_as_submittedreturns false, the unread copy is now cached and served to later bindings. That's harmless in practice, since each binding used to get its own unread copy anyway.Event waits off without mirrors (commit 3): I think this needs NotRexed's call, and I'd rather it went in separately.
docs/design.md§4 rule 1 says cross-queue waits are honoured andRX_VK_EVENT_WAITS=0is "for A/B only". This makes it the default on every no-mirror device, including Windows iGPUs nobody has tested, and the doc isn't updated. The comment abovesync_waits_onlists what the ordering fixed: lit surfaces black in 3-9% of frames, and passes reading another queue's output before it was written (55% of waits). Your 80-shot luminance numbers suggest that didn't come back in Kokiri, but that's one scene on one device.register_texture_queued/register_sampler_queuedapply at once on the game's thread. So a submission that stops at a wait and resumes later resolves handles against registrations made in the meantime, which breaks rule 3. Discrete GPUs only hit it less because the replay is less far behind. A fix that holds registrations while a queue is stopped, the wayheld_tex_releasesalready holds releases, would keep the ordering everywhere and fix the cause. Gating onmemory_mirrors_on()uses "has mirrors" as a stand-in for "the replay runs far behind": a discrete user withRX_VK_MIRROR=0silently loses event ordering, and a Deck user withRX_VK_MIRROR=1gets the bug back.CI: the three failing Python jobs fail on
test_make_game_ngx.pyandtest_pipeline_state_portable.py, which fail on main too (run 42942398373). The two new tests pass on Linux and are declared withRUN_ALL_ARGS/RUN_ALL_SKIP. The Windows back-end build is green.My suggestion: land commits 1-2 as they are, and either split commit 3 out or get NotRexed's sign-off on it, with
docs/design.mdrule 1 updated if it stays.Nit: the return value is ignored, so with reuse on, an unreadable range's copy gets cached and reused. Not a regression (each binding used to get its own unread copy), but
if (memory_read_as_submitted(...) && view)on the insert below would avoid it.@ -1754,0 +1758,4 @@static const bool v = [] {const char* e = getenv("RX_VK_EVENT_WAITS");const bool on = e ? atoi(e) != 0 : memory_mirrors_on();logf("vk: queues %s (RX_VK_EVENT_WAITS=%d)", on ? "ordered by their events" : "replayed in submit order", on ? 1 : 0);This ties the queue-ordering default to the mirror setting, so
RX_VK_MIRRORnow changes two unrelated things. The cause you describe (a stopped submission seeing registrations made since) looks likeregister_texture_queuedapplying immediately when!async_on(). Holding registrations whileblocked_n> 0, as releases already are, might fix it for every GPU without dropping the ordering. Either way, this goes against design.md §4 rule 1, so that rule needs updating if this stays.Related finding from Steam Deck testing with this PR's defaults (no mirrors, so
RX_VK_EVENT_WAITS=0): on current main,invalidate()'s readback wait can block forever inwait_value(). It hung the game after a save loaded in 2 of 8 starts. #2222 bounds that wait at 250 ms (0 of 4 hung, gameplay unchanged). The two PRs are independent, but #2222 is the one that matters for anyone running withRX_VK_EVENT_WAITS=0.Two things on main change the picture since the Deck numbers were taken. #2419 (merged) fixed fence waits returning after the first part of a split submission, and its description lists the same Deck symptoms (stretched HUD, white grass, whole-screen flashes). Could you re-run the 40-shot test on current main with RX_VK_UB_SNAPSHOT unset? If the slabs are gone, the default flip may not be needed at all. Second, #2343 turned mirrors off by default on GCN discrete cards (vk_memory.cpp:2102). Keying on memory_mirrors_on() now also flips both defaults on RX 400/500 and Vega, and nobody has measured that. The rebase conflicts with main's env_on read at vk_draw.cpp:2774. The new reads use getenv/atoi and treat an empty RX_VK_UB_SNAPSHOT as on, which is the opposite of env_on (#2042), so please switch them to env_on. hckrman101's point on commit 3 (event waits off without mirrors, against docs/design.md rule 1) is still open. I'd split that commit out.
This now conflicts with main in
gpu/vk/vk_draw.cpp. Could you merge main into the branch (or rebase) when you get a chance? I'll take another look once it's up to date.@Joey-Magnus @hckrman101 Thanks for the reviews. I merged current main (
46780503) and changed the PR as you asked:RX_VK_UB_SNAPSHOTunset (main's default on AMD is off): the screenshots are clean now. No slabs and no stretched HUD in 160 shots, so #2419 fixed those. But 1 of 3 starts froze in a conversation, and the log shows a capture's and a readback's wait for write-back N timing out with the timeline stuck at N-1. Main's own defaults (snapshot off, event waits on) went 1 good start of 3: one stopped up to 767 ms at an event wait (10.7 fps, p95 469 ms), and one stalled while loading. With the copy, there were 0 freezes in ~25 starts, at ~1 fps. So I've kept the default on, narrowed as below.VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, with no mirrors", so mirrorless discrete GPUs keep main's behaviour.memory_mirrors_on()is gone and the PR uses main'spools_mirrored().RX_VK_UB_SNAPSHOTandRX_VK_UB_SNAPSHOT_REUSEfollowhookshot_env.h. The test covers unset, empty, 0/off/no/false and 1/on/yes/true, plus integrated, discrete and Intel.memory_read_as_submittedfailed is no longer kept for reuse.blocked_n > 0). If that commit comes back, it will be its own PR with the design.md rule 1 update.My points are handled. The default now applies to Intel or VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU without mirrors, using main's pools_mirrored(), so GCN discrete cards keep main's behaviour. Both switches follow env_on's rule, a copy whose read failed isn't kept, and the event-wait commit is gone. Thanks for re-running on current main with the snapshot off, which answers whether the copy is still needed. I merged d89045e into current main (clean) and ran test_ub_snapshot_default and test_env_on, and both pass. The only red CI job is test_release_scripts, which is flaky.
One thing for maintainers: INTEGRATED_GPU covers more than the Deck. AMD APUs on Windows (the Ally, 680M and 780M laptops) get the copy by default too, and it was only measured on the Deck. Design rule 2 asks for the copy, so I think it's the right default, but it's worth knowing.
The title no longer matches the change. Since #2419 the HUD and lighting artifacts are gone, and what this fixes now is the Deck freezing in conversations. Something like "fix(vk): copy uniform buffers as submitted on integrated GPUs without mirrors" would read better in the changelog.