#2419 (fence wait covers every part of a split submission) costs about 9 fps on NVIDIA at 4K: 60 → 51 in Hyrule Field #2628

Open
opened 2026-11-12 03:21:56 +00:00 by Zarigis · 1 comment
Zarigis commented 2026-11-12 03:21:56 +00:00 (Migrated from github.com)

Since #2419, a scene that held 60 fps on an RTX 4080 SUPER at 4K runs at about 51. A bisect of main lands on that commit as one clean step, and restoring the old behaviour of those lines brings 60 back.

#2419 fixes real corruption on GPUs with no VRAM mirror of the buffer pools, so I'm not asking for a revert. Could the full wait apply only where the pools are read in place (no mirror), and the earlier return stay where they are mirrored?

Measurements

Hyrule Field (child, standing still, then turning the camera), 4K output, 3D at 100%, the game's own upscaler, frame generation off, 60 cap, windowed. Game frames from the log's vi: N frames presented lines; one run each.

Build Standing Turning
main at bebb0c64 (#2420, the commit before) 60.0 60.0
main at e0dbf40c (#2419) 51.1 53.2
main at 6f7ab2a7 51.9 54.0
main at 6f7ab2a7, value_rec keeps the first part's recording again 60.0 59.1

Kokiri Forest on 6f7ab2a7: 47-49 standing, where it was 58 on v2.1.0.

GPU time stays about 3 ms a frame in the slow runs, so the time goes to the game's thread waiting, not to GPU load.

Bisect

First-parent commits of d7a73062..6f7ab2a7 (141), seven steps, each a build and one run on an otherwise idle machine (no other GPU programs; 1.4-2.0 GB of VRAM in use by the desktop):

Commit Standing
7759a6f3 (#2195) 60.0
4a53c897 (#2429) 59.1
6c8c1bfb (#2378) 59.9
bebb0c64 (#2420) 60.0
e0dbf40c (#2419) 51.1
b4f755d6 (#2416) 51.3
4b5266a0 (#2291) 49.8
The change tested

In submit_recording (gpu/vk/vk_replay.cpp), behind a local switch, the value's recording is left at the first part's, as emplace did before:

if (it == value_rec.end()) value_rec.emplace(v, seq);
else if (!first_part && it->second < seq) it->second = seq;

I saw no corruption in the field run with it, but I only looked at that one scene. The PR's description says the damage comes from later parts reading guest memory in place; with the pools mirrored they read the mirror, which is why I think the early return is safe there. I have not verified that for every path (the exposure readback wait in particular).

Setup
  • RTX 4080 SUPER 16 GB, i9-14900KF, Windows 11, HDR10.
  • Builds are main plus local test tooling (scripted input) and #1377; RX_VK_PARALLEL=3 in all the runs above.
  • With parallel recording off (RX_VK_PARALLEL=0) the loss is larger: 6f7ab2a7 gives 42.0 standing and 45.8 turning in the same scene. Yesterday's main (0ffc004) held 58.6-59.4 there with it off; I have not rerun the commit before #2419 with it off.
Since #2419, a scene that held 60 fps on an RTX 4080 SUPER at 4K runs at about 51. A bisect of main lands on that commit as one clean step, and restoring the old behaviour of those lines brings 60 back. #2419 fixes real corruption on GPUs with no VRAM mirror of the buffer pools, so I'm not asking for a revert. Could the full wait apply only where the pools are read in place (no mirror), and the earlier return stay where they are mirrored? <details><summary>Measurements</summary> Hyrule Field (child, standing still, then turning the camera), 4K output, 3D at 100%, the game's own upscaler, frame generation off, 60 cap, windowed. Game frames from the log's `vi: N frames presented` lines; one run each. | Build | Standing | Turning | |---|---|---| | main at bebb0c64 (#2420, the commit before) | 60.0 | 60.0 | | main at e0dbf40c (#2419) | 51.1 | 53.2 | | main at 6f7ab2a7 | 51.9 | 54.0 | | main at 6f7ab2a7, `value_rec` keeps the first part's recording again | 60.0 | 59.1 | Kokiri Forest on 6f7ab2a7: 47-49 standing, where it was 58 on v2.1.0. GPU time stays about 3 ms a frame in the slow runs, so the time goes to the game's thread waiting, not to GPU load. </details> <details><summary>Bisect</summary> First-parent commits of d7a73062..6f7ab2a7 (141), seven steps, each a build and one run on an otherwise idle machine (no other GPU programs; 1.4-2.0 GB of VRAM in use by the desktop): | Commit | Standing | |---|---| | 7759a6f3 (#2195) | 60.0 | | 4a53c897 (#2429) | 59.1 | | 6c8c1bfb (#2378) | 59.9 | | bebb0c64 (#2420) | 60.0 | | e0dbf40c (#2419) | 51.1 | | b4f755d6 (#2416) | 51.3 | | 4b5266a0 (#2291) | 49.8 | </details> <details><summary>The change tested</summary> In `submit_recording` (`gpu/vk/vk_replay.cpp`), behind a local switch, the value's recording is left at the first part's, as `emplace` did before: ```cpp if (it == value_rec.end()) value_rec.emplace(v, seq); else if (!first_part && it->second < seq) it->second = seq; ``` I saw no corruption in the field run with it, but I only looked at that one scene. The PR's description says the damage comes from later parts reading guest memory in place; with the pools mirrored they read the mirror, which is why I think the early return is safe there. I have not verified that for every path (the exposure readback wait in particular). </details> <details><summary>Setup</summary> - RTX 4080 SUPER 16 GB, i9-14900KF, Windows 11, HDR10. - Builds are main plus local test tooling (scripted input) and #1377; `RX_VK_PARALLEL=3` in all the runs above. - With parallel recording off (`RX_VK_PARALLEL=0`) the loss is larger: 6f7ab2a7 gives 42.0 standing and 45.8 turning in the same scene. Yesterday's main (0ffc004) held 58.6-59.4 there with it off; I have not rerun the commit before #2419 with it off. </details>
Owner

@mooki000 commented on GitHub · 2026-11-12 14:12 UTC · original

The mirrored-pool readback wait is now handled by #2602, merged into main after the revisions in your table. It keeps the full wait where in-place GPU readers require it rather than reverting #2419. Could you repeat your 4K case on a build containing #2602, with the normal safety settings, and share the log?

> [@mooki000](https://github.com/mooki000) commented on GitHub · 2026-11-12 14:12 UTC · [original](https://github.com/NotRexed/Hookshot/issues/2628#issuecomment-6825952120) The mirrored-pool readback wait is now handled by #2602, merged into main after the revisions in your table. It keeps the full wait where in-place GPU readers require it rather than reverting #2419. Could you repeat your 4K case on a build containing #2602, with the normal safety settings, and share the log? <!-- github-sync:c:6825952120 -->
Commenting is not possible because the repository is archived.
No milestone
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#2628
No description provided.