test(vk): the recorder own/idle checks with slot identities and the real wait (review 1195 of #162) #342

Merged
HumanBean merged 12 commits from klimovich008/Longshot:test/recorder-own-idle-review into main 2026-11-14 20:59:23 +00:00
Contributor

Why

mooki's review of #162 (review 1195) asked for tests that would catch a slot's own reset hitting the wrong pool, and for the
recorder's new idle wait to be tested through the real operating-system wait and an orderly shutdown. #162 was merged before those
tests were pushed, so they come here. Tests only: no renderer or host code changes.

What changed

  • tests: tests/recorder_own_idle_test.cpp, own_slots cases. Two recording slots, each with its own command pool, command
    buffer, descriptor pool, extra pool and a set from each pool. Slot 1's binds are held in the recorder while slot 0 comes round,
    its GPU work completing 20 ms late. The fake driver knows which object each held packet names and records a fault for a reset,
    begin or destruction of an object still in use, or a slot reused before its GPU work completed. own_slots_wrong (slot 1's pool
    reset) and own_slots_early (slot 0 reused early) show it catches each.

  • tests: real_ cases (Windows) through vk_replay.cpp's own WaitOnAddress adapter, cut from the source:

    • a publish wakes the recorder asleep on its ring, in a wait given 10 s, past the 3 s a case allows. A run counts only when the
      wait that returned began before the publish and blocked 15 ms or more; otherwise it is retried, up to 20 times;
    • with nothing published and the wake off, the 2 ms timeout ends each wait (FALSE with ERROR_TIMEOUT);
    • a publish just before a 10 s wait, with the wake off, is found by the compare;
    • a stop while asleep returns;
    • both switches together.

    Each ends with vkDestroyDevice's teardown (the recorder stopped and joined), its watchdogs joined, and a normal return.

  • tests: tests/test_recorder_own_idle.py copies every header vk_work.h and vk_recorder.h include from gpu/vk, so #60's and
    #288's gpu_wait_stats.h comes along whichever lands first. Its source check reads the code without comments. It also pins
    recording_wait(r) before rec::slot_begin(), and the slot's own handles in the OwnSlot blocks
    (draw_begin_recording(r.slot, ...), slot_begin(rec_end_pos[r.slot])).

  • tests: three new broken variants must fail: the wait's timeout made infinite, the adapter's wake doing nothing, and the
    adapter's wait comparing against the ring counter as it is now. A broken real-wait build counts as caught when any of three runs
    fails it, since WaitOnAddress may return early. The source check has new controls too: recording_wait commented out in
    either comment style, and another slot's descriptor pools.

Flags and settings

None.

Measurements

A test-only change, measured by its checks.

main: cf605c85 (2026-11-14)
this PR: test/recorder-own-idle-review @ 53fe0b5e
Hardware: RX 9070 XT, Ryzen 7 5700X3D, Windows 11 (no GPU used: the fixture fakes Vulkan, and the real_ cases use the OS wait)
Scene: none (game-free)
Method: python -I -B tests/test_recorder_own_idle.py --cxx clang++ --headers <Vulkan-Headers> with llvm-mingw, three times in a
row on each commit, and the suite (tests/run_all.py)
How to reproduce: the command above on Windows (the real_ cases skip, saying so, elsewhere)

main @ cf605c85 this PR @ 53fe0b5e
cases 14 22
broken variants that must fail 6 9
source-check controls 2 5
runs in a row passing 1 3 on each of the 11 commits

Testing

Tested: the test on Windows, llvm-mingw clang, three times in a row on every commit, and on this head with
test_recorder_sync, test_draw_stream_visibility, test_fsr_uniform_ownership, test_fsr_image_retirement and
test_recording_admission (all pass). tests/run_all.py on this head: 519 passed, 5 failed, 35 skipped; the five (test_module_ids, test_pad_layout, test_framegen_resources, test_tested_configurations, test_oot_state_tap) fail the same on a clean main cf605c85. The tests went through eleven rounds of our own second-opinion review. Each
round found timing or lifetime gaps in them (all fixed in these commits), and the last round is clean.

Not tested: the real_ cases on Linux: vk_replay.cpp's Linux adapter is host/posix's futex, which this test does not build.
They skip there with a line saying so (the Linux futex path ran in the game in #162's T-GTM9 test).

Needs other testers: None.

Screenshots

Nothing on screen.

Risk and rollback

Tests only. The real_ cases use the real OS wait, so a heavily loaded runner could make one retry more; a run that cannot
conclude fails after 20 attempts with a line saying so. Reverting the commits is enough.

Reviewer notes

The answers to review 1195's five points are on #162 (it was merged before this was pushed).

Issues

See #162

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main.
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing.
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

If applicable

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • A check under tests/ that reads changed source updated in this PR, not weakened.

🤖 Generated with Claude Code

## Why mooki's review of #162 (review 1195) asked for tests that would catch a slot's own reset hitting the wrong pool, and for the recorder's new idle wait to be tested through the real operating-system wait and an orderly shutdown. #162 was merged before those tests were pushed, so they come here. Tests only: no renderer or host code changes. ## What changed - tests: `tests/recorder_own_idle_test.cpp`, `own_slots` cases. Two recording slots, each with its own command pool, command buffer, descriptor pool, extra pool and a set from each pool. Slot 1's binds are held in the recorder while slot 0 comes round, its GPU work completing 20 ms late. The fake driver knows which object each held packet names and records a fault for a reset, begin or destruction of an object still in use, or a slot reused before its GPU work completed. `own_slots_wrong` (slot 1's pool reset) and `own_slots_early` (slot 0 reused early) show it catches each. - tests: `real_` cases (Windows) through `vk_replay.cpp`'s own `WaitOnAddress` adapter, cut from the source: - a publish wakes the recorder asleep on its ring, in a wait given 10 s, past the 3 s a case allows. A run counts only when the wait that returned began before the publish and blocked 15 ms or more; otherwise it is retried, up to 20 times; - with nothing published and the wake off, the 2 ms timeout ends each wait (FALSE with `ERROR_TIMEOUT`); - a publish just before a 10 s wait, with the wake off, is found by the compare; - a stop while asleep returns; - both switches together. Each ends with `vkDestroyDevice`'s teardown (the recorder stopped and joined), its watchdogs joined, and a normal return. - tests: `tests/test_recorder_own_idle.py` copies every header `vk_work.h` and `vk_recorder.h` include from `gpu/vk`, so #60's and #288's `gpu_wait_stats.h` comes along whichever lands first. Its source check reads the code without comments. It also pins `recording_wait(r)` before `rec::slot_begin()`, and the slot's own handles in the `OwnSlot` blocks (`draw_begin_recording(r.slot, ...)`, `slot_begin(rec_end_pos[r.slot])`). - tests: three new broken variants must fail: the wait's timeout made infinite, the adapter's wake doing nothing, and the adapter's wait comparing against the ring counter as it is now. A broken real-wait build counts as caught when any of three runs fails it, since `WaitOnAddress` may return early. The source check has new controls too: `recording_wait` commented out in either comment style, and another slot's descriptor pools. ## Flags and settings None. ## Measurements A test-only change, measured by its checks. main: cf605c85 (2026-11-14) this PR: test/recorder-own-idle-review @ 53fe0b5e Hardware: RX 9070 XT, Ryzen 7 5700X3D, Windows 11 (no GPU used: the fixture fakes Vulkan, and the real_ cases use the OS wait) Scene: none (game-free) Method: `python -I -B tests/test_recorder_own_idle.py --cxx clang++ --headers <Vulkan-Headers>` with llvm-mingw, three times in a row on each commit, and the suite (`tests/run_all.py`) How to reproduce: the command above on Windows (the `real_` cases skip, saying so, elsewhere) | | main @ cf605c85 | this PR @ 53fe0b5e | |---|---|---| | cases | 14 | 22 | | broken variants that must fail | 6 | 9 | | source-check controls | 2 | 5 | | runs in a row passing | 1 | 3 on each of the 11 commits | ## Testing **Tested:** the test on Windows, llvm-mingw clang, three times in a row on every commit, and on this head with `test_recorder_sync`, `test_draw_stream_visibility`, `test_fsr_uniform_ownership`, `test_fsr_image_retirement` and `test_recording_admission` (all pass). `tests/run_all.py` on this head: 519 passed, 5 failed, 35 skipped; the five (`test_module_ids`, `test_pad_layout`, `test_framegen_resources`, `test_tested_configurations`, `test_oot_state_tap`) fail the same on a clean main cf605c85. The tests went through eleven rounds of our own second-opinion review. Each round found timing or lifetime gaps in them (all fixed in these commits), and the last round is clean. **Not tested:** the `real_` cases on Linux: `vk_replay.cpp`'s Linux adapter is `host/posix`'s futex, which this test does not build. They skip there with a line saying so (the Linux futex path ran in the game in #162's T-GTM9 test). **Needs other testers:** None. ## Screenshots Nothing on screen. ## Risk and rollback Tests only. The `real_` cases use the real OS wait, so a heavily loaded runner could make one retry more; a run that cannot conclude fails after 20 attempts with a line saying so. Reverting the commits is enough. ## Reviewer notes The answers to review 1195's five points are on #162 (it was merged before this was pushed). ## Issues See #162 ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [x] Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### If applicable - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
own_slots: two slots with their own pools, buffers and sets, one slot's
packets held while the other comes round with its GPU work late; the fakes
know which object each held packet names and fail a reset, begin or
destruction of an object in use or a slot reused early (two self-checks show
they catch each). real_ (Windows): vk_replay.cpp's own WaitOnAddress adapter,
cut from the source: a parked publish, recovery by the timeout with no wake,
a publish just before the wait, stop while parked, both switches together;
each ends with vkDestroyDevice's teardown and a normal return. The fixture
copies every header vk_work.h and vk_recorder.h include from gpu/vk (#60's
gpu_wait_stats.h comes along). The source check pins recording_wait before
slot_begin and the slot's own handles in the OwnSlot blocks.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
real_parked and real_race wait with no timeout, so only the wake or the
compare can end the wait (two adapter controls: a wake that does nothing, a
wait comparing against W as it is now); real_timeout counts timed-out returns
with nothing published and no wake; the idle hook is set once before the
recorder starts; the source checks read code without comments and pin
draw_begin_recording(r.slot, ...) and slot_begin(rec_end_pos[r.slot]).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
real_parked counts a run only when the wait that returned began before the
publish and blocked 15 ms or more (retried otherwise), so a compare at the
wait's start can't stand in for the wake; real_race arms its hook while the
waits still time out, with no setup wake; real_timeout counts only returns
that are timeouts (FALSE with ERROR_TIMEOUT); the watchdogs are joined
before a normal return; the source checks also strip block comments.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A stop's wake that lands just before such a wait starts now costs the
teardown at most 10 s instead of a hang; within the 3 s a case allows, only
the wake or the compare still ends them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
test(vk): real_parked settles 30 ms between attempts and makes up to 20 (sol r12)
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 3s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
53fe0b5ef3
Co-Authored-By: Claude Opus 5.5 <[email protected]>
klimovich008 changed title from WIP: test(vk): the recorder own/idle checks with slot identities and the real wait (review 1195 of #162) to test(vk): the recorder own/idle checks with slot identities and the real wait (review 1195 of #162) 2026-11-14 02:47:23 +00:00
Merge main 543846eb into test/recorder-own-idle-review
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 15s
Website / Website on Cloudflare (pull_request_target) Has been skipped
bb552e0719
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Author
Contributor

Merged main 543846eb (bb552e07, no force-push). The only conflict was tests/test_recorder_own_idle.py: main copied gpu_wait_stats.h into the fixture's include folder by name (the #60 interplay mooki noted); this PR's version already copies every header vk_work.h and vk_recorder.h include, which covers it, so that side is kept. test_recorder_own_idle (all cases and broken variants), test_recorder_sync and test_gpu_wait_stats pass on the merge; the full suite follows.

Merged main 543846eb (bb552e07, no force-push). The only conflict was tests/test_recorder_own_idle.py: main copied gpu_wait_stats.h into the fixture's include folder by name (the #60 interplay mooki noted); this PR's version already copies every header vk_work.h and vk_recorder.h include, which covers it, so that side is kept. test_recorder_own_idle (all cases and broken variants), test_recorder_sync and test_gpu_wait_stats pass on the merge; the full suite follows.
Author
Contributor

Full suite on the merge (bb552e07, main 543846eb, Windows, llvm-mingw 20261104, --jobs 4): 532 passed, 3 failed, 36 skipped. The same 3 fail on clean main 543846eb here (532/3/36): tests/test_oot_state_tap.py, tests/test_tested_configurations.py, tests/test_walk_key.py.

Full suite on the merge (bb552e07, main 543846eb, Windows, llvm-mingw 20261104, --jobs 4): 532 passed, 3 failed, 36 skipped. The same 3 fail on clean main 543846eb here (532/3/36): tests/test_oot_state_tap.py, tests/test_tested_configurations.py, tests/test_walk_key.py.
HumanBean deleted branch test/recorder-own-idle-review 2026-11-14 20:59:24 +00:00
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 15s
Website / Website on Cloudflare (pull_request_target) Has been skipped
Some workflows are waiting to be reviewed.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
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/Longshot!342
No description provided.