docs(tested): the recorder thread in the Market while moving, interleaved off and on #389

Open
GrimVisage wants to merge 1 commit from GrimVisage/Longshot:docs/tested-market-recorder-moving into main
Contributor

Why

The recorder thread's runs in #369 were standing still; the team asked how it does while moving and turning the camera. These runs answer that: in the Market it takes 3 to 4 ms a frame off the replay and evens out frame pacing, but fps stays level, because moving is bound by the game's own CPU work rather than the replay.

What changed

  • docs: eleven files under docs/tested/, 2026-11-13-market-recorder-moving-grimvisage-*.md, one run each, in the format of docs/tested/README.md, all main 5a8ae7e3 with RX_VK_RECORDER off and thread, Ryzen 5 5600X and RX 9070 XT on Fedora 45 (RADV 26.2.6), the Market moving in three measured segments, uncapped:
    • run01 to run04: the comparison, four runs by hand interleaved off, thread, thread, off;
    • route-record: one more run by hand (off) whose controller inputs were recorded;
    • route-replay-run01 to run04: that recording replayed through a virtual pad, interleaved off, thread, thread, off; the route drifted from run to run (run 4 ended outside the Market), so they are recorded but not used for the comparison;
    • route-sampled-off and route-sampled-thread: the replay once a side with the all-thread sampler on, the source of the profile under Reviewer notes (their fps is lowered by the sampler).
  • The frozen table is untouched and no assembled view is committed.

Flags and settings

None.

Measurements

A docs change: measured by its check (CONTRIBUTING.md, "Measurements": docs, test output on main and with this PR).

main: 0caf0618 (the branch's base)
this PR: docs/tested-market-recorder-moving @ 7fbbb141
Hardware: Ryzen 5 5600X, 31 GB, Fedora 45 (kernel 7.3.3), Python 3.15.2
Scene: none (game-free checks)
Method: python -I -B tests/test_tested_configurations.py, and python -I -B tests/run_all.py on this PR with the failing checks run again on main
How to reproduce: the two commands above from the repository root

main @ 0caf0618 this PR @ 7fbbb141
tests/test_tested_configurations.py passes (without these files) 25 tests OK, with the eleven files
tests/run_all.py test_menu_keys, test_module_ids, test_pad_layout fail 482 passed, 3 failed (the same three), 76 skipped
$ python -I -B tests/test_tested_configurations.py
Ran 25 tests in 0.031s
OK

$ python -I -B tests/run_all.py
482 passed, 3 failed, 76 skipped in 84 s (linux, 12 at once)
(failed: tests/test_menu_keys.py, tests/test_module_ids.py, tests/test_pad_layout.py; each also fails alone on main 0caf0618)

The runs these files record are under Reviewer notes.

Testing

Tested: the files' format check (tests/test_tested_configurations.py) and the whole game-free suite on Linux (Fedora 45); the three failures there also fail on main 0caf0618 on this PC. The runs themselves were made in the game on this PC (Ryzen 5 5600X, RX 9070 XT, RADV 26.2.6), as Reviewer notes describes; no popping, culling or other visual problems were seen while moving.

Not tested: the website's catalogue built from these files (it reads docs/tested/ directly; not run here).

Needs other testers: None

Screenshots

Nothing on screen.

Risk and rollback

Docs only: nothing the game, the builds or the checks run changes. Reverting the commit removes the files.

Reviewer notes

The runs, compared as the switch off against on (both main @ 5a8ae7e3, the same build; main against a PR wasn't the comparison because the switch is already on main). Each run: a fresh copy of one child Market save, uncapped (HOS_FPS_MAX=240), HOS_PRESENT_TRACE on; from the gate, moving by hand on spoken cues: 60 s running around the fountain, 60 s running around it while turning the camera, 30 s standing while turning the camera, each measured; interleaved off, thread, thread, off after earlier warm runs. Frame times from the present trace; replay and GPU times are the medians of the log's status lines in each window. Medians of the two runs a side.

Baseline: main @ 5a8ae7e3, RX_VK_RECORDER unset

main @ 5a8ae7e3, recorder off (run 1 / run 4) main @ 5a8ae7e3, RX_VK_RECORDER=thread (run 2 / run 3) Δ (medians)
running: fps 37.9 / 37.8 38.1 / 38.6 +1.2%
running: 1% low 24.0 / 24.4 25.8 / 25.9 +7%
running: p99 frame time 37.72 / 39.00 ms 36.13 / 34.49 ms -3.05 ms
running: frame-to-frame change 4.31 / 4.47 ms 3.63 / 3.43 ms -20%
running: replay submit a frame 15.70 / 16.08 ms 12.69 / 11.61 ms -3.74 ms
running + camera: fps 36.3 / 35.75 36.1 / 35.7 -0.3%
running + camera: frame-to-frame change 4.83 / 5.14 ms 4.36 / 4.20 ms -13%
running + camera: replay submit a frame 16.82 / 16.36 ms 13.35 / 13.06 ms -3.39 ms
standing + camera: fps 37.2 / 37.7 38.3 / 36.0 -0.7%
standing + camera: frame-to-frame change 4.53 / 4.83 ms 4.07 / 4.64 ms -7%
standing + camera: replay submit a frame 16.58 / 15.95 ms 12.73 / 13.47 ms -3.17 ms
GPU time a frame (all segments) 11.5 to 12.1 ms 11.5 to 12.1 ms none

Moving, the frame is 26 to 27 ms against 12 to 17 ms of replay, so the replay's saving does not reach the fps. The sampled pair (route-sampled-*: HOS_PROFILE=5, HOS_SAMPLE_ALL, scripts/crit.py) over the same segments: no thread is saturated (Presentation 35 to 38%, BaseProcExecutor0 to 5 26 to 31% each); sead::MainThread is blocked in 65% of instants, mostly in f_8007fe60, then a sleep under f_800998a0 and f_83acfb90, while the workers run f_805902c0 most (55 to 73% of those instants). Moving looks bound by the game's own job chain, not the renderer.

A status line from each side, in the running + camera window:

recorder off (run 1)
[hos Presentation Thread] vk: 7080 presents, 35.7 fps; a frame: 2152 draws (0 skipped), 173 dispatches, 192 passes, 177 barriers, 6 submits, 0.5 uploads (0.2 MB), 82 ub updates (57 overlays), GPU-bound wait 0.1 ms; 24951 pipelines (1727 ms), 0 programs (10 failed), 2922 textures (4716 MB)
[hos Presentation Thread] vk: a frame's replay time (ms): submit 16.82, present 0.15, draw 10.15, descriptors 2.99, texture lookups 0.40, uploads 0.02, pipelines 0.44, programs 1.34, commands 4.49, dispatch 1.37, memory sync 0.62, queue submit 1.74, slot wait 0.08
[hos Presentation Thread] vk: GPU time 12.1 ms a frame (the longest submission 3.2 ms); 20.5 textures made a frame

RX_VK_RECORDER=thread (run 2)
[hos host] vk: RX_VK_RECORDER=thread: vkCmd* packed on the replay thread and run in order by a recorder thread; the replay waits for it at each command buffer begin, end and reset, submit, wait, readback, destruction and replay lock release (serial replay only); ring 8192 KB, spin adaptive 20-200 us
[hos Presentation Thread] vk: 6960 presents, 35.3 fps; a frame: 2152 draws (0 skipped), 167 dispatches, 190 passes, 176 barriers, 6 submits, 0.6 uploads (0.2 MB), 82 ub updates (57 overlays), GPU-bound wait 0.1 ms; 24965 pipelines (1249 ms), 0 programs (10 failed), 2968 textures (4792 MB)
[hos Presentation Thread] vk: a frame's replay time (ms): submit 13.71, present 0.15, draw 7.80, descriptors 2.98, texture lookups 0.29, uploads 0.03, pipelines 0.46, programs 1.39, commands 2.13, dispatch 1.04, memory sync 0.60, queue submit 1.58, slot wait 0.08
[hos Presentation Thread] vk: GPU time 12.1 ms a frame (the longest submission 3.9 ms); 21.8 textures made a frame

Moving by hand varies the route: draws a frame ranged 1,766 to 2,274 running and 2,040 to 2,195 in the camera segments. Replaying a recorded controller route was tried for consistency (route-* files) and drifted further (one replay ended outside the Market), so the four hand runs are the comparison; the replays agree in direction where they stayed in the Market.

Issues

See #369

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 file in docs/tested/ for each new measured run (format in docs/tested-configurations.md; no appended row or assembled view).
  • Docs updated.

🤖 Generated with Claude Code

## Why The recorder thread's runs in #369 were standing still; the team asked how it does while moving and turning the camera. These runs answer that: in the Market it takes 3 to 4 ms a frame off the replay and evens out frame pacing, but fps stays level, because moving is bound by the game's own CPU work rather than the replay. ## What changed - docs: eleven files under `docs/tested/`, `2026-11-13-market-recorder-moving-grimvisage-*.md`, one run each, in the format of `docs/tested/README.md`, all main 5a8ae7e3 with `RX_VK_RECORDER` off and `thread`, Ryzen 5 5600X and RX 9070 XT on Fedora 45 (RADV 26.2.6), the Market moving in three measured segments, uncapped: - `run01` to `run04`: the comparison, four runs by hand interleaved off, thread, thread, off; - `route-record`: one more run by hand (off) whose controller inputs were recorded; - `route-replay-run01` to `run04`: that recording replayed through a virtual pad, interleaved off, thread, thread, off; the route drifted from run to run (run 4 ended outside the Market), so they are recorded but not used for the comparison; - `route-sampled-off` and `route-sampled-thread`: the replay once a side with the all-thread sampler on, the source of the profile under Reviewer notes (their fps is lowered by the sampler). - The frozen table is untouched and no assembled view is committed. ## Flags and settings None. ## Measurements A docs change: measured by its check (CONTRIBUTING.md, "Measurements": `docs`, test output on main and with this PR). main: 0caf0618 (the branch's base) this PR: docs/tested-market-recorder-moving @ 7fbbb141 Hardware: Ryzen 5 5600X, 31 GB, Fedora 45 (kernel 7.3.3), Python 3.15.2 Scene: none (game-free checks) Method: `python -I -B tests/test_tested_configurations.py`, and `python -I -B tests/run_all.py` on this PR with the failing checks run again on main How to reproduce: the two commands above from the repository root | | main @ 0caf0618 | this PR @ 7fbbb141 | |---|---|---| | `tests/test_tested_configurations.py` | passes (without these files) | 25 tests OK, with the eleven files | | `tests/run_all.py` | `test_menu_keys`, `test_module_ids`, `test_pad_layout` fail | 482 passed, 3 failed (the same three), 76 skipped | ``` $ python -I -B tests/test_tested_configurations.py Ran 25 tests in 0.031s OK $ python -I -B tests/run_all.py 482 passed, 3 failed, 76 skipped in 84 s (linux, 12 at once) (failed: tests/test_menu_keys.py, tests/test_module_ids.py, tests/test_pad_layout.py; each also fails alone on main 0caf0618) ``` The runs these files record are under Reviewer notes. ## Testing **Tested:** the files' format check (`tests/test_tested_configurations.py`) and the whole game-free suite on Linux (Fedora 45); the three failures there also fail on main 0caf0618 on this PC. The runs themselves were made in the game on this PC (Ryzen 5 5600X, RX 9070 XT, RADV 26.2.6), as Reviewer notes describes; no popping, culling or other visual problems were seen while moving. **Not tested:** the website's catalogue built from these files (it reads `docs/tested/` directly; not run here). **Needs other testers:** None ## Screenshots Nothing on screen. ## Risk and rollback Docs only: nothing the game, the builds or the checks run changes. Reverting the commit removes the files. ## Reviewer notes The runs, compared as the switch off against on (both main @ 5a8ae7e3, the same build; main against a PR wasn't the comparison because the switch is already on main). Each run: a fresh copy of one child Market save, uncapped (`HOS_FPS_MAX=240`), `HOS_PRESENT_TRACE` on; from the gate, moving by hand on spoken cues: 60 s running around the fountain, 60 s running around it while turning the camera, 30 s standing while turning the camera, each measured; interleaved off, thread, thread, off after earlier warm runs. Frame times from the present trace; replay and GPU times are the medians of the log's status lines in each window. Medians of the two runs a side. Baseline: main @ 5a8ae7e3, `RX_VK_RECORDER` unset | | main @ 5a8ae7e3, recorder off (run 1 / run 4) | main @ 5a8ae7e3, `RX_VK_RECORDER=thread` (run 2 / run 3) | Δ (medians) | |---|---|---|---| | running: fps | 37.9 / 37.8 | 38.1 / 38.6 | +1.2% | | running: 1% low | 24.0 / 24.4 | 25.8 / 25.9 | +7% | | running: p99 frame time | 37.72 / 39.00 ms | 36.13 / 34.49 ms | -3.05 ms | | running: frame-to-frame change | 4.31 / 4.47 ms | 3.63 / 3.43 ms | -20% | | running: replay submit a frame | 15.70 / 16.08 ms | 12.69 / 11.61 ms | -3.74 ms | | running + camera: fps | 36.3 / 35.75 | 36.1 / 35.7 | -0.3% | | running + camera: frame-to-frame change | 4.83 / 5.14 ms | 4.36 / 4.20 ms | -13% | | running + camera: replay submit a frame | 16.82 / 16.36 ms | 13.35 / 13.06 ms | -3.39 ms | | standing + camera: fps | 37.2 / 37.7 | 38.3 / 36.0 | -0.7% | | standing + camera: frame-to-frame change | 4.53 / 4.83 ms | 4.07 / 4.64 ms | -7% | | standing + camera: replay submit a frame | 16.58 / 15.95 ms | 12.73 / 13.47 ms | -3.17 ms | | GPU time a frame (all segments) | 11.5 to 12.1 ms | 11.5 to 12.1 ms | none | Moving, the frame is 26 to 27 ms against 12 to 17 ms of replay, so the replay's saving does not reach the fps. The sampled pair (`route-sampled-*`: `HOS_PROFILE=5`, `HOS_SAMPLE_ALL`, `scripts/crit.py`) over the same segments: no thread is saturated (Presentation 35 to 38%, BaseProcExecutor0 to 5 26 to 31% each); `sead::MainThread` is blocked in 65% of instants, mostly in f_8007fe60, then a sleep under f_800998a0 and f_83acfb90, while the workers run f_805902c0 most (55 to 73% of those instants). Moving looks bound by the game's own job chain, not the renderer. A status line from each side, in the running + camera window: ``` recorder off (run 1) [hos Presentation Thread] vk: 7080 presents, 35.7 fps; a frame: 2152 draws (0 skipped), 173 dispatches, 192 passes, 177 barriers, 6 submits, 0.5 uploads (0.2 MB), 82 ub updates (57 overlays), GPU-bound wait 0.1 ms; 24951 pipelines (1727 ms), 0 programs (10 failed), 2922 textures (4716 MB) [hos Presentation Thread] vk: a frame's replay time (ms): submit 16.82, present 0.15, draw 10.15, descriptors 2.99, texture lookups 0.40, uploads 0.02, pipelines 0.44, programs 1.34, commands 4.49, dispatch 1.37, memory sync 0.62, queue submit 1.74, slot wait 0.08 [hos Presentation Thread] vk: GPU time 12.1 ms a frame (the longest submission 3.2 ms); 20.5 textures made a frame RX_VK_RECORDER=thread (run 2) [hos host] vk: RX_VK_RECORDER=thread: vkCmd* packed on the replay thread and run in order by a recorder thread; the replay waits for it at each command buffer begin, end and reset, submit, wait, readback, destruction and replay lock release (serial replay only); ring 8192 KB, spin adaptive 20-200 us [hos Presentation Thread] vk: 6960 presents, 35.3 fps; a frame: 2152 draws (0 skipped), 167 dispatches, 190 passes, 176 barriers, 6 submits, 0.6 uploads (0.2 MB), 82 ub updates (57 overlays), GPU-bound wait 0.1 ms; 24965 pipelines (1249 ms), 0 programs (10 failed), 2968 textures (4792 MB) [hos Presentation Thread] vk: a frame's replay time (ms): submit 13.71, present 0.15, draw 7.80, descriptors 2.98, texture lookups 0.29, uploads 0.03, pipelines 0.46, programs 1.39, commands 2.13, dispatch 1.04, memory sync 0.60, queue submit 1.58, slot wait 0.08 [hos Presentation Thread] vk: GPU time 12.1 ms a frame (the longest submission 3.9 ms); 21.8 textures made a frame ``` Moving by hand varies the route: draws a frame ranged 1,766 to 2,274 running and 2,040 to 2,195 in the camera segments. Replaying a recorded controller route was tried for consistency (`route-*` files) and drifted further (one replay ended outside the Market), so the four hand runs are the comparison; the replays agree in direction where they stayed in the Market. ## Issues See #369 ## 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 file in `docs/tested/` for each new measured run (format in `docs/tested-configurations.md`; no appended row or assembled view). - [x] Docs updated. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
docs(tested): the recorder thread in the Market while moving, interleaved off and on
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 / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (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 3s
Website / Website on Cloudflare (pull_request_target) Has been skipped
7fbbb141b1
Eleven runs on the same build and save, each with three measured segments: running around the
fountain, running while turning the camera, standing while turning the camera. Four by hand,
interleaved off, thread, thread, off, are the comparison: the replay's submit time drops 3 to 4 ms a
frame with the recorder thread, fps stays level, and frame pacing is more even. Also recorded: the
run whose controller inputs were recorded, four replays of that route (it drifted from run to run),
and one sampled replay a side, the source of the thread profile.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
GrimVisage changed title from WIP: docs(tested): the recorder thread in the Market while moving, interleaved off and on to docs(tested): the recorder thread in the Market while moving, interleaved off and on 2026-11-14 07:43:35 +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 / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (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 3s
Website / Website on Cloudflare (pull_request_target) Has been skipped
This pull request doesn't have enough approvals yet. 0 of 1 approvals granted.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'docs/tested-market-recorder-moving:GrimVisage-docs/tested-market-recorder-moving'
git switch 'GrimVisage-docs/tested-market-recorder-moving'
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!389
No description provided.