Skip to content

feat(vk): time a capture's GPU wait for its late pages apart in the log - #1243

Merged
hckrman101 merged 1 commit into
NotRexed:mainfrom
Nitnek:fix/capture-wait-log
Nov 9, 2026
Merged

hckrman101 merged 1 commit into
NotRexed:mainfrom
Nitnek:fix/capture-wait-log

Conversation

@Nitnek

@Nitnek Nitnek commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

What changed

take_capture waits for the GPU (vk_timeline_wait(c->late_value)) before it copies the pages that a submission still on the GPU writes back. That wait was counted in "retires and stamps". So the 600-take log line could not tell the replay thread waiting for the GPU from its own bookkeeping.

The wait and the pools_mu acquisition are now timed apart, and the line reads:

vk: a capture taken (ms): all 1.932; GPU wait for late pages 1.806 (99 of 600 takes), pools lock 0.000, retires and stamps 0.112, bytes copied 0.000; ...

"Retires and stamps" now starts once pools_mu is held. Nothing else changes: no new switch, no behaviour change.

Why

This is the cost behind part of #995 (low FPS with low GPU use). The fewer pools have a VRAM mirror, the more often a submit waits for the GPU here, about six captures a frame. Until now it only showed as an unexplained "retires and stamps". With this line a player's hookshot_log.txt tells at once whether a small mirror budget is what holds the frame rate. That covers RX_VK_MIRROR_MB set low, the VRAM/8 default of 6 GB cards, and the 4GB preset's 256 MB. Measurements are in this #995 comment.

How it was tested

RTX 3070 8 GB, Windows 11, main at 09e4eb5 plus this commit. Scripted Hyrule Field run: load the save, run the same 45 s route.

RX_VK_MIRROR_MB avg FPS GPU wait for late pages all
1024 40.9 0.64-1.81 ms (~100 of 600 takes) 0.83-1.93 ms
2048 (the 8 GB default) 52.1 0.04-0.12 ms 0.21-0.27 ms

tests/test_capture_wait_log.py (new) checks that the wait is timed on its own, that "retires and stamps" starts after the lock, and that the line names and resets both. It fails on main and passes here. The other tests reading vk_memory.cpp still pass: bind_memo, capture_copy_runs, mem_trim, parallel_rb_refresh, pool_span, report_words, submitted_page, vertex_bind, wb_diff_split, wb_pages (tests/run_all.py, llvm-mingw).

No game data included.

🤖 Generated with Claude Code

take_capture waits for the GPU (vk_timeline_wait(c->late_value)) before it copies the pages a submission still on
the GPU writes back. That wait was counted in "retires and stamps", so the 600-take log line could not tell the
replay thread waiting for the GPU from its own bookkeeping. The wait grows as fewer pools have a VRAM mirror: with
RX_VK_MIRROR_MB=1024 on an RTX 3070 it was 1.5-1.8 ms of a 1.7-1.9 ms capture, six captures a frame, and Hyrule
Field ran at ~41 fps instead of ~52 (issue 995).

The line now reads "all; GPU wait for late pages (takes that waited), pools lock, retires and stamps, bytes copied".
"Retires and stamps" starts once pools_mu is held. tests/test_capture_wait_log.py checks the timing and the line.

Assisted-by: Claude Code (Claude Opus 5.5)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Nitnek

Nitnek commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

The two red "Python source checks" jobs fail on tests/test_output_resolution.py, not on this change. That test fails the same way on main (09e4eb5 and 5d1fe99, run locally): it still expects hos_rt_pool_size(305 MB) == 305 MB, while #1202 gave the render-target pool a 512 MiB minimum. #1254 updates the test. tests/test_capture_wait_log.py and the other vk_memory.cpp tests pass on both runners.

@hckrman101 hckrman101 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I checked the timing split in take_capture: tk1 is still taken after the lock, so measuring retires and stamps from tk_locked is right, and the wait and lock are now separate counters that reset together with the late-take count. The new test is picked up by run_all.py's tests/test_*.py glob and passes on both runners.

The two red Python source checks are only tests/test_output_resolution.py, which is the known failure on main (not this PR).

@hckrman101
hckrman101 merged commit a8b9ba0 into NotRexed:main Nov 9, 2026
8 of 10 checks passed
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