Skip to content

fix(vulkan): track writes from direct buffer bindings - #1133

Merged
hckrman101 merged 6 commits into
NotRexed:mainfrom
monsterlag:fix/direct-buffer-write-bookkeeping
Nov 10, 2026
Merged

hckrman101 merged 6 commits into
NotRexed:mainfrom
monsterlag:fix/direct-buffer-write-bookkeeping

Conversation

@monsterlag

@monsterlag monsterlag commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

With buffer mirrors disabled or unavailable, CPU readback can miss GPU-written values because an early direct binding does not record the write's pages or epoch. Record that bookkeeping on both direct returns in gpu_buffer_bind, addressing the source gap reported in #1126. Credit to @drpowerup for the original observation.

note_direct_write marks the current mirror epoch and records the translated, pool-clamped guest range for writes up to 1 MiB. Read-only bindings, mirrored writebacks, the current WB_MAX policy and upstream shadow_forget behavior remain intact. This does not include the separate #1150 import policy.

This refresh normally merges main at 117eae79, including #1858's presentation link repair and #1793's shared source-check repairs, without rewriting history. The runner, API header and workflow are unchanged from that base; the test declares its own RUN_ALL_ARGS. The fixture extracts the actual production shadow_forget and checks precisely which pool-relative shadow pages are invalidated. Every original case and assertion is preserved. All 15 extracted production inputs, both complete fixture files and the runner are byte-identical to the previously tested 7ca77cb head; upstream's imported-buffer destruction changes are outside those inputs.

Validation on this refresh:

  • Identical final fixture against unmodified current-main production code: seven intended behavioral failures and five passing controls, on Windows and Linux.
  • Fixed actual-function fixture: all 12 cases pass on Windows LLVM and Linux GCC. Strict selected runner passes without failures or skips on both platforms.
  • Removing only the upstream shadow-invalidation call causes seven intended bitmap failures while five controls pass, showing that the new assertions protect that behavior too.
  • Binding memo and null/unbound/empty/bound SSBO range checks pass on both platforms; the five upstream CPU-shadow policy checks pass on Windows.
  • The three-file topic diff passes the whitespace check. Whitespace already present in upstream test/vendor files was preserved.

Historical hardware reports remain attributed to their authors and earlier code: @DarkGhostHunter reported no more blinking wrong textures on an RDNA3 iGPU. @djanice1980 reported steady Castle-gate ground on Radeon 8060S/RADV, with about 9 ms/frame of readback-wait cost; that test applied page notes without the epoch line.

All eight hosted checks now pass on current head 2babca1a: Windows/Linux source suites, Windows Builder packaging, Windows Vulkan, Linux AppImage, both native lowering checks and workflow lint. Release-upload checks are correctly skipped for a pull request. The normal merges incorporate #1858's presentation definitions and #1793's shared source-check repairs.

Gaps: no full local suite, current-head GPU/game or timeline test, or new iGPU measurement. The fixture executes actual binding/page-table/shadow functions with authored pool, mirror, host and hazard boundaries; it does not allocate a real Vulkan device or access a game dump. Hardware reports above remain historical and attributed to the original testers.

Record the direct write epoch and small result pages when mirrors are disabled, unavailable or refused. Match the existing later direct path so CPU invalidation can recognize these GPU writes.

Add twelve production-extraction cases for page registration, epoch-only writes and existing readonly/mirrored controls. Based on the source observation in issue NotRexed#1126 by drpowerup.

Assisted-by: OpenAI Codex

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Normally merge main 7dbdcdc, preserving its page-table representation,
parallel mirror guard, mirrored-write hazard callback and all test entries.
Keep the original early-direct write epoch/page-note fix unchanged.

Extract current page-table dimensions, include unique_ptr support and
record the existing hazard callback in the authored fixture. Preserve all
twelve assertions. Current-main negative: seven failures and five controls;
fixed Windows GNU/MSVC-target and Linux checks pass.

Assisted-by: OpenAI Codex

Co-Authored-By: OpenAI Codex <noreply@openai.com>
@DarkGhostHunter

Copy link
Copy Markdown

Can confirm it works on iGPU (RDNA3): no more blinking wrong textures. Had a very hard stop on entering the market but probably shaders, after a few seconds it run again like normal. Performance is acceptable, it seems there is a lot of work to do there.

@djanice1980

Copy link
Copy Markdown

Hardware test on an integrated GPU with the mirror off, as asked above. Radeon 8060S (RADV STRIX_HALO, Mesa driver 0x6802006), CachyOS, kernel 7.3.3. Built from main 5609af4 plus the same wb_pages_note lines in the no-mirror return of gpu_buffer_bind (without the direct_write_epoch line).

Before: shaded ground at the Hyrule Castle gate flipped between two colors several times a second (stone floor mean sRGB 68/61/50 vs 62/76/92 in two screenshots; sunlit areas, sky and HUD within 3%). No "readbacks of GPU results" line in the log. After: steady.

Frame-time A/B, same save, standing still, about 1335 draws a frame, frame generation and frame pacing off, one build with switches:

with the change RX_VK_MIRROR=1 RX_VK_NO_READBACK_WAIT=1
picture steady steady heavy flicker
fps ~35 (32-37) ~40 (38-44) ~52 (51-53)
slowest frame per 600 37-45 ms 33-35 ms 26-30 ms
readbacks that waited, of 3000 660-935 1340-1490 none
each wait 2.7-4.7 ms 0.7-1.4 ms -
VRAM in use 4.2 GB 5.8 GB 4.2 GB

So the change fixes the flicker and costs about 9 ms a frame here; the mirror costs about 6 ms. For comparison, an RTX 5070 Ti with RX_VK_MIRROR=0 also stopped flickering with the change, with waits of 0.24 ms each (0.13 ms with the mirror on). I can rerun anything that would help.

Normally merge main 6add2f1 and preserve all upstream runner entries.
Share epoch and small-result page tracking between the two direct paths
through note_direct_write, without importing the draft readback policy.

Adapt the actual-function fixture for main's read-only asynchronous-import
branch. Preserve all twelve original cases and eighteen require checks;
add an assertion that no asynchronous streaming copy occurs.

The same final fixture fails seven intended cases on unmodified main and
passes all twelve with the fix. Four focused strict checks pass on both
Windows and Linux, sequentially. No GPU or game validation was performed.

Assisted-by: OpenAI Codex
Co-Authored-By: OpenAI Codex <noreply@openai.com>
@hclaria

hclaria commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

Hardware test on a Steam Deck OLED (AMD Custom APU 0932, RADV VANGOGH, SteamOS, kernel 7.2.7-valve2), mirrors off as on every integrated GPU.

Not this branch itself: I tested the equivalent change on main d8f0513 (plus #1625 and #1574), a wb_pages_note for written bindings of 1 MB or less in the no-mirror early return of gpu_buffer_bind, without the direct_write_epoch line.

Before: in Kokiri Forest the whole picture flickered several times a second, all at once: foliage going from dark green to brightly lit and back. It looks like the exposure stepping, not textures going missing. The log had VRAM mirror of buffer pools off and never printed a readbacks of GPU results line, over tens of thousands of frames.

What pointed at the wait:

  • RX_VK_MIRROR=1 removed the flicker, with RX_VK_MIRROR_MB=1024 and with 256.
  • RX_VK_MIRROR=1 with RX_VK_NO_READBACK_WAIT=1 brought it back. So it is the readback wait that fixes it, and the mirrors only mattered because their write-backs fed wb_pages.
  • RX_VK_WAIT_GPU=1 alone did not help.

After, mirrors off: no flicker in several runs of a few minutes each. The log now shows readbacks of GPU results: ~930 of 3000 waited for their write-back, 1.4-1.8 ms each.

Cost here: about 30-31 fps (flickering) to about 26.5 fps at ~2,000 draws. The GPU takes ~17 ms a frame on this APU, so each wait is long. With RX_VK_LOG_READBACK_WAITS=1, two alternating ranges of 0x3980 bytes account for nearly all of it, about 5 ms a wait (up to 14 ms), on ModuleSystemWorker threads. The other ranges wait under 0.6 ms. Compared with getting the same fix through RX_VK_MIRROR=1, this is 1-2 fps faster and leaves about 1 GB more memory free.

monsterlag and others added 3 commits November 10, 2026 13:27
Keep upstream shadow invalidation and writeback policy, extract the actual shadow helper in the fixture, and verify precise page invalidation. Register the test through RUN_ALL_ARGS while preserving main runner/API/workflow.

Assisted-by: OpenAI Codex

Co-Authored-By: Codex <noreply@openai.com>
@Hashyirama

Copy link
Copy Markdown

Tested on a Lenovo Legion Go (Ryzen Z1 Extreme, Radeon 780M iGPU), Windows. On v2.0.14 (which doesn't include this PR) I get constant white flashes in Kokiri Forest, and RX_VK_MIRROR=1 removes them completely, which points at the non-mirror path this PR fixes. I'll test 2.1.0-rc without the env switch and report back.

@DarkGhostHunter

Copy link
Copy Markdown

Tested on a Lenovo Legion Go (Ryzen Z1 Extreme, Radeon 780M iGPU), Windows. On v2.0.14 (which doesn't include this PR) I get constant white flashes in Kokiri Forest, and RX_VK_MIRROR=1 removes them completely, which points at the non-mirror path this PR fixes. I'll test 2.1.0-rc without the env switch and report back.

I tested 2.1.0-RC2 and I can confirm there are no white flashes, at least on Linux.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants