WIP: Vulkan: no one-frame exposure flashes from the CPU overwriting GPU-written words (stopgap: frame wait) #513

Merged
hckrman101 merged 1 commit from flicker-frame-wait into main 2026-11-08 09:12:23 +00:00
hckrman101 commented 2026-11-08 07:34:45 +00:00 (Migrated from github.com)

Fixes the one-frame brightness flashes (haze, sun shafts, exposure jumping for a frame) on Linux.

OoT's CPU and GPU overlap: the CPU prepares frame N+1 while the GPU is still on frame N, and some of OoT's queues submit work before what it depends on is ready (the console's GPU simply waits). #523 fixes the queue-ordering side; this PR fixes the remaining race.

Depends on #523 (merge that first). Measured on the same build, 90 s each, Kokiri Forest:

flash events fps
this PR + #523 0, 1 ~50
this PR alone 45, 19 ~38

The fix: each present waits for its frame's GPU work

The flashes left after the queue-ordering fixes in #523. Some data is written by both the GPU and the CPU (parameters the passes before the exposure meter read). The GPU's writes reach guest memory only when its frame finishes, but the CPU, already on the next frame, wrote to the same 4 KB page before that and the page's capture held the GPU's old words; the capture's sync then put those old words over the GPU's new ones. That frame's scene, and the exposure meter's samples of it, came out too bright, and the exposure reacted the frame after (flashes came in pairs).

Stopgap: this change is meant to be replaced once the proper fix (WIP, see Notes) lands; it trades a frame of CPU/GPU overlap for correctness until then.

The present now waits for its frame's submissions, so the GPU's writes have landed before the CPU's next capture. RX_VK_WAIT_GPU=-1: no wait (old behaviour); =<n>: n frames ahead.

In-session A/B on an RTX 3090 (the wait toggled every 15 s in one session, so both halves see the same scenes; flashes counted from the frame log):

run without wait with wait
1 5 flashes / 3566 frames 0 / 3390
2 70 flashes / 3559 frames 0 / 2790
exposure meter bad reads (run 2) 1818 / 149636 13 / 109364

Mean brightness unchanged (86.8 / 86.8 in run 1), ~60 fps both ways. Also played normally on Linux: no flashes.

Notes

  • The wait gives up one frame of CPU/GPU overlap, so a GPU-bound PC could lose some fps. I'm working on a narrower fix that keeps the overlap: syncing only the words the CPU actually changed on pages the GPU also writes. It will replace this change.
  • Feedback on AMD/Intel and slower GPUs welcome.
Fixes the one-frame brightness flashes (haze, sun shafts, exposure jumping for a frame) on Linux. OoT's CPU and GPU overlap: the CPU prepares frame N+1 while the GPU is still on frame N, and some of OoT's queues submit work before what it depends on is ready (the console's GPU simply waits). #523 fixes the queue-ordering side; this PR fixes the remaining race. **Depends on #523** (merge that first). Measured on the same build, 90 s each, Kokiri Forest: | | flash events | fps | |---|---|---| | this PR + #523 | 0, 1 | ~50 | | this PR alone | 45, 19 | ~38 | ## The fix: each present waits for its frame's GPU work The flashes left after the queue-ordering fixes in #523. Some data is written by both the GPU and the CPU (parameters the passes before the exposure meter read). The GPU's writes reach guest memory only when its frame finishes, but the CPU, already on the next frame, wrote to the same 4 KB page before that and the page's capture held the GPU's old words; the capture's sync then put those old words over the GPU's new ones. That frame's scene, and the exposure meter's samples of it, came out too bright, and the exposure reacted the frame after (flashes came in pairs). **Stopgap:** this change is meant to be replaced once the proper fix (WIP, see Notes) lands; it trades a frame of CPU/GPU overlap for correctness until then. The present now waits for its frame's submissions, so the GPU's writes have landed before the CPU's next capture. `RX_VK_WAIT_GPU=-1`: no wait (old behaviour); `=<n>`: n frames ahead. In-session A/B on an RTX 3090 (the wait toggled every 15 s in one session, so both halves see the same scenes; flashes counted from the frame log): | run | without wait | with wait | |---|---|---| | 1 | 5 flashes / 3566 frames | **0** / 3390 | | 2 | 70 flashes / 3559 frames | **0** / 2790 | | exposure meter bad reads (run 2) | 1818 / 149636 | 13 / 109364 | Mean brightness unchanged (86.8 / 86.8 in run 1), ~60 fps both ways. Also played normally on Linux: no flashes. ## Notes - The wait gives up one frame of CPU/GPU overlap, so a GPU-bound PC could lose some fps. I'm working on a narrower fix that keeps the overlap: syncing only the words the CPU actually changed on pages the GPU also writes. It will replace this change. - Feedback on AMD/Intel and slower GPUs welcome.
hckrman101 commented 2026-11-08 07:53:23 +00:00 (Migrated from github.com)

Marking this as a draft: I've traced the remaining race to a single page that both the CPU and GPU write, and I'm working on an updated fix that syncs only the words the CPU actually changed there. That should keep the CPU/GPU overlap instead of waiting at every present. I'll update this PR when it's tested.

Marking this as a draft: I've traced the remaining race to a single page that both the CPU and GPU write, and I'm working on an updated fix that syncs only the words the CPU actually changed there. That should keep the CPU/GPU overlap instead of waiting at every present. I'll update this PR when it's tested.
Commenting is not possible because the repository is archived.
No description provided.