Skip to content

OoT exposure flashes: CPU reads of GPU results wait for the work that writes them - #296

Merged
shayderrr merged 2 commits into
NotRexed:mainfrom
evilmfkr:pr/exposure-flicker
Nov 7, 2026
Merged

shayderrr merged 2 commits into
NotRexed:mainfrom
evilmfkr:pr/exposure-flicker

Conversation

@evilmfkr

@evilmfkr evilmfkr commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Fixes the white flashes / fog pulsing at Kokiri Forest (several times a second, while standing still).

What it was. Not the fog: OoT's auto-exposure hunting in 0.2 EV steps (~15% brightness each). The game meters the
scene on the GPU: a pass adds the frame's brightness into a small buffer, and the CPU reads it after an invalidate and
stores 0 back (f_80357060). On the console that memory is coherent. Here the GPU's result reaches guest memory only with
the write-back at the end of its submission, and the game read it as soon as it had handed the work over: one frame it
read 0, then the old sum came back over the CPU's 0 (a whole-page sync of the captured page) and the next frame added to
it (2x). The exposure logic saw 0 / 1x / 2x and kept correcting.

The fix. The pages each submission writes into guest memory (write-backs from a mirror, small bindings written in
place) are noted with its timeline value; invalidate waits (replay caught up, then that value) before the CPU reads a
range a GPU write ever reached. RX_VK_NO_READBACK_WAIT=1 restores the old behaviour.

Measured at the Kokiri rock, 300 screen captures in ~4.5 s at normal speed (GDI, so the game isn't slowed): 17-84
brightness jumps before, 0-1 after; ~46 fps both ways (RTX 4060 Ti). Confirmed in play: the flashes are gone.

Diagnostics added: RX_VK_LOG_READBACK=<present> (what the CPU is about to read at each invalidate),
RX_VK_LOG_READBACK_WAITS=1, RX_VK_WATCH_CPU=<present>:<addr>[,...] (CPU writes to those pages with the writer's stack;
the host page watch now also logs the host pc, which hookshot.map turns into the guest function),
RX_VK_READBACK_NO_CATCHUP=1 (A/B).

Also adds EvilBoyce (Discord: evilmfkr) to the README credits.

🤖 Generated with Claude Code

evilmfkr and others added 2 commits November 7, 2026 13:27
…exposure flashes)

OoT meters the scene's brightness on the GPU: a pass adds the frame's sum into a small buffer, and the CPU
reads it after an invalidate and sets it back to 0 (f_80357060). On the console that memory is shared and
coherent. Here the GPU works in its mirror and the results reach guest memory with the write-back at the end
of their submission, while the game reads them as soon as it has handed the work over: it read 0 one frame;
then the old sum came back over the CPU's 0 (a whole-page sync of the captured page) and the next frame added
to it (2x). The auto-exposure stepped 0.2 EV up and down several times a second: the white flashes / fog
flicker.

Now the pages each submission writes into guest memory (write-backs from a mirror, small bindings written in
place) are noted with its timeline value, and invalidate waits (replay caught up, then that value) before the
CPU reads a range a GPU write ever reached. Measured at the Kokiri rock (300 screen captures in ~4.5 s):
17-84 brightness jumps before, 0-1 after; fps unchanged. RX_VK_NO_READBACK_WAIT=1: as before.

Diagnostics: RX_VK_LOG_READBACK=<present> (what the CPU is about to read at each invalidate),
RX_VK_LOG_READBACK_WAITS=1, RX_VK_WATCH_CPU=<present>:<addr>[,...] (CPU writes to those pages with the
writer's stack; the host page watch now also logs the host pc, which hookshot.map turns into the guest
function), RX_VK_READBACK_NO_CATCHUP=1 (A/B).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rhino-oot pushed a commit to rhino-oot/Hookshot that referenced this pull request Nov 7, 2026
…wait for the work that writes them (exposure flashes)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bXgPZ5JFp3LvmLQw1BDo2
@shayderrr

Copy link
Copy Markdown
Collaborator

Thanks

@shayderrr
shayderrr merged commit b198d61 into NotRexed:main Nov 7, 2026
1 check passed
juniortesla53-cpu added a commit to juniortesla53-cpu/Zelda-OoT-Hookshot-RX5700XT that referenced this pull request Nov 8, 2026
Upstream's NotRexed#296 waits for the GPU before a CPU readback of GPU results. On
a Ryzen 5 5500 + RX 5700 XT ~4% of readbacks waited ~18.6 ms each: the
game's threads spun in their sleeps and fps fell from ~43 to ~30.

The wait is off by default here (RX_VK_READBACK_WAIT=1: on). What it
protected, OoT's exposure meter (the GPU adds into it, the CPU resets it
to 0), is kept right by the readback-page refresh from YunaraDebugs'
PR NotRexed#430: each submission first copies the pages the CPU reads back from
guest memory into the mirror, last of the uploads (RX_VK_NO_RB_REFRESH=1:
off). Every write-back here is on the queue in order, so the PR's DMA
condition is not needed.

Tested in Kokiri: fps back to ~40 fullscreen, no white flashes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Arkex-ai added a commit to Arkex-ai/Hookshot that referenced this pull request Nov 12, 2026
Compute partition hashes and weight scaling once per block, and use an
equivalent endpoint-encoding boundary search. Preserve Hookshot tiling,
bounds, formats and upload policy. Regenerate the Vulkan 1.1 shader header
and compare the actual partition/search functions against frozen references.

Adapted from Lichii NotRexed#296 by justr4mzi, based on SmileyTatsu87's changes;
retain the existing Eden/yuzu SPDX notices and source attribution.

Assisted-by: OpenAI Codex
Arkex-ai added a commit to Arkex-ai/Hookshot that referenced this pull request Nov 12, 2026
Compute partition hashes and weight scaling once per block, and use an
equivalent endpoint-encoding boundary search. Preserve Hookshot tiling,
bounds, formats and upload policy. Regenerate the Vulkan 1.1 shader header
and compare the actual partition/search functions against frozen references.

Adapted from Lichii NotRexed#296 by justr4mzi, based on SmileyTatsu87's changes;
retain the existing Eden/yuzu SPDX notices and source attribution.

Assisted-by: OpenAI Codex
Arkex-ai added a commit to Arkex-ai/Hookshot that referenced this pull request Nov 12, 2026
Compute partition hashes and weight scaling once per block, and use an
equivalent endpoint-encoding boundary search. Preserve Hookshot tiling,
bounds, formats and upload policy. Regenerate the Vulkan 1.1 shader header
and compare the actual partition/search functions against frozen references.

Adapted from Lichii NotRexed#296 by justr4mzi, based on SmileyTatsu87's changes;
retain the existing Eden/yuzu SPDX notices and source attribution.

Assisted-by: OpenAI Codex
Arkex-ai added a commit to Arkex-ai/Hookshot that referenced this pull request Nov 12, 2026
Compute partition hashes and weight scaling once per block, and use an
equivalent endpoint-encoding boundary search. Preserve Hookshot tiling,
bounds, formats and upload policy. Regenerate the Vulkan 1.1 shader header
and compare the actual partition/search functions against frozen references.

Adapted from Lichii NotRexed#296 by justr4mzi, based on SmileyTatsu87's changes;
retain the existing Eden/yuzu SPDX notices and source attribution.

Assisted-by: OpenAI Codex
Arkex-ai added a commit to Arkex-ai/Hookshot that referenced this pull request Nov 12, 2026
Compute partition hashes and weight scaling once per block, and use an
equivalent endpoint-encoding boundary search. Preserve Hookshot tiling,
bounds, formats and upload policy. Regenerate the Vulkan 1.1 shader header
and compare the actual partition/search functions against frozen references.

Adapted from Lichii NotRexed#296 by justr4mzi, based on SmileyTatsu87's changes;
retain the existing Eden/yuzu SPDX notices and source attribution.

Assisted-by: OpenAI Codex
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.

2 participants