perf(vk): three small costs of the replay thread removed: unread bookkeeping without mirrors, 16 vertex bindings a change, a lock a sampled texture #590

Merged
HumanBean merged 6 commits from p0lux/Longshot:perf/three-cpu-costs into main 2026-11-15 19:19:10 +00:00
Contributor

Type: perf · Risk: low · Tested on: Linux (SteamOS), Steam Deck OLED (RADV VANGOGH) · Needs: Windows, any GPU (in the game)

Why

In heavy places the frame is limited by the thread that replays the game's draws. On a Steam Deck that thread spent about 3% of its time on three things that buy nothing: notes kept for write-backs that a GPU without VRAM mirrors never makes, 16 vertex bindings handed to the driver when one changed, and a lock taken for every sampled texture to ask a question whose answer is nearly always no.

What changed

Three commits, each removing one of those costs and keeping the result the same. A fourth updates three test fixtures that cut the changed code out of the source, and a fifth moves an include.

  • memory: no bookkeeping where there are no mirrors (gpu/vk/vk_memory.cpp). memory_cpu_owned (the report words the replay wrote on the CPU), shadow_written_to and guest_cpu_wrote (the guest pages the CPU wrote) note things that only a mirror's write-back reads (cut_cpu_words, shadows_forget_written, both under if (!writebacks.empty())). Without mirrors (mirror_on() false: an integrated GPU, a GCN card, RX_VK_MIRROR=0) no write-back is ever noted, so the words piled up to 65,536 to be sorted, merged and dropped, and the page lists to 8 million entries (32 MB) before being dropped. They now return at once there.
  • draw: only the vertex bindings that changed (gpu/vk/vk_draw.cpp). vertex_bind_range gives the bindings that differ from what the command buffer has, and draw() binds that range instead of all 16; all 16 are still remembered.
  • textures: a table of the regions the GPU wrote (gpu/vk/wrote_regions.h, new; gpu/vk/vk_texture.cpp). texture_gpu_wrote notes the 2 MB regions a GPU buffer write reaches (64 KB of bits, never cleared), before the map of written ranges has the write. gpu_wrote_into answers no from the table when the texture's storage is in none of them; otherwise the map answers under its lock, as before.
  • docs: a row for RX_VK_VB_RANGE in docs/development.md; one run file in docs/tested/.

Measurements

Verdict: it helps the replay thread: 4.8% fewer instructions and 3.8% fewer cycles a frame, 1.7 ms less replay, 28.5 to 29.9 fps on a Steam Deck in a heavy view. In a build with the committed play profile the functions it changes are compiled without their profile, and it is level on fps (31.5 against 31.5) with 2.8% fewer instructions.

Hardware: SteamOS 3.9.5 (kernel 7.2.7-valve2), Steam Deck OLED: AMD Custom APU 0932 (4 cores, 8 threads), RADV VANGOGH, Mesa 26.2.0-devel, 16 GB shared with the GPU; the GPU's clock fixed at 1600 MHz
Scene: a heavy view from a save, standing still, about 2,150 draws a frame; no screen (headless), handheld profile, Minimum preset, the game's own upscaler, no frame generation, no cap; mesh programs on the compute path, no VRAM mirrors
Method: the game and host built from main 9c9d69ed; the back end built alone (clang, same flags) from main 414f56e2 (nothing under gpu/ differs from 9c9d69ed) and from this PR's three commits on it, and swapped in that game folder. Each run starts the game, loads the save by script, waits 3 log windows, then measures 720 presents: fps by the clock, the log's replay time, the render thread's user-space cycles and instructions a frame (perf stat -t). Two runs each after a warm-up start, main then this PR, the second round in the reverse order; the median of two is their mean. No per-frame log in these runs, so no p99 or 1% low: the target is the replay thread's work, which the instruction count gives independently of the CPU's frequency.
How to reproduce: a save in a place with over 2,000 draws a frame, RX_VK_VB_RANGE=0 against unset for the vertex bindings; the other two have no switch and need the two builds

this PR: perf/three-cpu-costs @ 243c3a0d (its three code commits on main 414f56e2; on the fork as measured/three-cpu-costs-243c3a0d). The branch is now on main 25726217: git range-diff shows the same three patches (cf67e86b, 1c2bdc6b, dd342a08). Main's commits in between touch vk_draw.cpp and vk_texture.cpp elsewhere (the pipeline cache, dispatch, texture_frame), not texture_gpu_wrote, gpu_wrote_into or the vertex bindings of draw(), and not vk_memory.cpp at all, so the numbers hold.

Built without a play profile (medians; each run in brackets, in the order run):

Before: main @ 414f56e2 After: this PR @ 243c3a0d Δ
fps 28.54 (28.62 / 28.46) 29.92 (29.20 / 30.64) +1.38
replay a frame 31.15 ms (31.12 / 31.17) 29.49 ms (30.04 / 28.94) -1.66 ms
render thread: instructions a frame 62.19 M (62.38 / 61.99) 59.20 M (59.07 / 59.33) -4.8%
render thread: cycles a frame 78.96 M (79.15 / 78.77) 75.96 M (76.32 / 75.59) -3.8%

Built with the committed play profile (RX_VK_PGO=<assets/profiles/longshot_vk.profdata.xz, unpacked>; the build log says built with the profile):

Before: main @ 414f56e2, play profile After: this PR @ 243c3a0d, play profile Δ
fps 31.49 (30.68 / 32.30) 31.47 (30.30 / 32.63) -0.02
replay a frame 26.75 ms (26.99 / 26.50) 26.26 ms (26.49 / 26.02) -0.49 ms
render thread: instructions a frame 56.12 M (56.19 / 56.04) 54.53 M (54.61 / 54.44) -2.8%
render thread: cycles a frame 68.88 M (68.99 / 68.76) 68.35 M (68.91 / 67.78) -0.8%
main, no profile       2400 presents, 27.4 fps; a frame: 2167 draws (0 skipped), 144 dispatches, 194 passes, 163 barriers, 6 submits
main, no profile       2280 presents, 26.5 fps; a frame: 2158 draws (0 skipped), 144 dispatches, 193 passes, 163 barriers, 6 submits
this PR, no profile    2280 presents, 28.8 fps; a frame: 2147 draws (0 skipped), 144 dispatches, 194 passes, 163 barriers, 6 submits
this PR, no profile    2160 presents, 29.7 fps; a frame: 2150 draws (0 skipped), 144 dispatches, 194 passes, 163 barriers, 6 submits
main, profile          2400 presents, 31.1 fps; a frame: 2156 draws (0 skipped), 139 dispatches, 193 passes, 154 barriers, 6 submits
this PR, profile       2280 presents, 32.1 fps; a frame: 2148 draws (0 skipped), 138 dispatches, 194 passes, 152 barriers, 6 submits

Read with care. Two runs each on one machine and one scene; the fps of two runs of one renderer differ by up to 2 (the play profile's arms went from 30.3-30.7 in the first round to 32.3-32.6 in the second), while the instruction counts repeat within 0.6%. The three commits were not measured apart. With the play profile, draw(), gpu_wrote_into, texture_gpu_wrote, memory_cpu_owned, shadow_written_to and guest_cpu_wrote no longer match the recording and are compiled as if there were none, which is what takes back most of the gain there until the profile is recorded again (assets/profiles/README.md: "a function changed since the recording is compiled as if there were no profile").

Flags and settings

One new A/B switch; the other two changes have none.

Flag What it is Before this PR After this PR Effect Helps / doesn't help / untested Recommendation
RX_VK_VB_RANGE=0 all 16 vertex bindings bound whenever one changed that behaviour, always unset: only the bindings that changed are bound none on the picture; fewer bindings handed to the driver helps: Steam Deck (RADV) above; untested: other drivers leave unset: on for everyone, the switch is for A/B only

Testing

Tested:

  • python -I -B tests/run_all.py on macOS with the Vulkan headers given: 499 passed, 41 failed, 39 skipped; the 41 fail the same way on main 25726217 on that machine (496 passed, 41 failed: no /proc, no posix_fallocate, Python 3.9, the Linux launch scripts and the like).
  • New: tests/test_mirror_bookkeeping.py (the three functions cut out of the source, without mirrors and with, and that only a write-back reads what they keep), tests/test_vertex_bind_range.py, tests/test_wrote_regions.py. Each fails on main. Updated, no check changed: tests/test_report_words.py, tests/test_wb_diff_stale_shadow.py (their pools have mirrors: a mirror_on() stand-in) and tests/test_fallback_pipeline_state.py (takes vertex_bind_range from the source).
  • In the game, a Steam Deck OLED, without a screen: the 9 starts above, no error, no device lost, 0 GPU timeouts in the kernel's log. With the picture, by a person: a build holding these commits with other renderer-only ones and the play profile, played on the same Deck on 2026-11-14: nothing wrong seen on screen.
  • Linux: the back end builds from these commits on main 414f56e2 (clang, on the Deck): 0 failed.
  • Windows: the back end cross-builds from the same commits with llvm-mingw 20261104 on Linux: 0 failed, longshot_vk.dll made. Not run.

Not tested:

  • A GPU with VRAM mirrors in the game: the bookkeeping there is as before (the fixtures run it with mirrors), but no run was made.
  • Windows in the game; other drivers' handling of a partial vkCmdBindVertexBuffers2.
  • Other scenes; a long session.

Must be tested:

  • Windows, any GPU: a short play through a few areas with this PR, expecting nothing drawn differently (by hand: models and terrain intact, no flicker; or Test Farm: @Beanstalk windows, a walk through Kokiri Forest and Hyrule Field)

Risks

What could break: a draw's vertex streams, if a driver mishandles a partial bind (models missing or stretched); a texture the GPU wrote not uploaded again, if a write's regions were not noted (the table is filled before the map and never cleared, and addresses past 40 bits share bits, so it can only say "ask the map" too often); the mirrors' write-backs, if a mirror existed while mirror_on() said no (a pool gets one only in mirror_make, whose two callers, gpu_buffer_bind and memory_begin_submission, are both behind mirror_on()).

Turning it off: RX_VK_VB_RANGE=0 for the vertex bindings; reverting the commit for each of the other two.

Compatibility: Nothing changes: no shader or pipeline cache is rebuilt (no stamped file is touched), MKWVK_API_VERSION is as it was.

Issues

See #402

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how.
  • Rebased on current main.
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named).
  • 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.

When it applies

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • Switches documented (docs/development.md; the README for a menu row) and shipped at their recommended value.
  • A file in docs/tested/ for each new measured run (format in docs/tested-configurations.md).
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths.
  • Docs updated.
**Type:** perf · **Risk:** low · **Tested on:** Linux (SteamOS), Steam Deck OLED (RADV VANGOGH) · **Needs:** Windows, any GPU (in the game) ## Why In heavy places the frame is limited by the thread that replays the game's draws. On a Steam Deck that thread spent about 3% of its time on three things that buy nothing: notes kept for write-backs that a GPU without VRAM mirrors never makes, 16 vertex bindings handed to the driver when one changed, and a lock taken for every sampled texture to ask a question whose answer is nearly always no. ## What changed Three commits, each removing one of those costs and keeping the result the same. A fourth updates three test fixtures that cut the changed code out of the source, and a fifth moves an include. - **memory: no bookkeeping where there are no mirrors** (`gpu/vk/vk_memory.cpp`). `memory_cpu_owned` (the report words the replay wrote on the CPU), `shadow_written_to` and `guest_cpu_wrote` (the guest pages the CPU wrote) note things that only a mirror's write-back reads (`cut_cpu_words`, `shadows_forget_written`, both under `if (!writebacks.empty())`). Without mirrors (`mirror_on()` false: an integrated GPU, a GCN card, `RX_VK_MIRROR=0`) no write-back is ever noted, so the words piled up to 65,536 to be sorted, merged and dropped, and the page lists to 8 million entries (32 MB) before being dropped. They now return at once there. - **draw: only the vertex bindings that changed** (`gpu/vk/vk_draw.cpp`). `vertex_bind_range` gives the bindings that differ from what the command buffer has, and `draw()` binds that range instead of all 16; all 16 are still remembered. - **textures: a table of the regions the GPU wrote** (`gpu/vk/wrote_regions.h`, new; `gpu/vk/vk_texture.cpp`). `texture_gpu_wrote` notes the 2 MB regions a GPU buffer write reaches (64 KB of bits, never cleared), before the map of written ranges has the write. `gpu_wrote_into` answers no from the table when the texture's storage is in none of them; otherwise the map answers under its lock, as before. - docs: a row for `RX_VK_VB_RANGE` in `docs/development.md`; one run file in `docs/tested/`. ## Measurements **Verdict:** it helps the replay thread: 4.8% fewer instructions and 3.8% fewer cycles a frame, 1.7 ms less replay, 28.5 to 29.9 fps on a Steam Deck in a heavy view. In a build with the committed play profile the functions it changes are compiled without their profile, and it is level on fps (31.5 against 31.5) with 2.8% fewer instructions. Hardware: SteamOS 3.9.5 (kernel 7.2.7-valve2), Steam Deck OLED: AMD Custom APU 0932 (4 cores, 8 threads), RADV VANGOGH, Mesa 26.2.0-devel, 16 GB shared with the GPU; the GPU's clock fixed at 1600 MHz Scene: a heavy view from a save, standing still, about 2,150 draws a frame; no screen (headless), handheld profile, Minimum preset, the game's own upscaler, no frame generation, no cap; mesh programs on the compute path, no VRAM mirrors Method: the game and host built from main 9c9d69ed; the back end built alone (clang, same flags) from main 414f56e2 (nothing under `gpu/` differs from 9c9d69ed) and from this PR's three commits on it, and swapped in that game folder. Each run starts the game, loads the save by script, waits 3 log windows, then measures 720 presents: fps by the clock, the log's replay time, the render thread's user-space cycles and instructions a frame (`perf stat -t`). Two runs each after a warm-up start, main then this PR, the second round in the reverse order; the median of two is their mean. No per-frame log in these runs, so no p99 or 1% low: the target is the replay thread's work, which the instruction count gives independently of the CPU's frequency. How to reproduce: a save in a place with over 2,000 draws a frame, `RX_VK_VB_RANGE=0` against unset for the vertex bindings; the other two have no switch and need the two builds this PR: perf/three-cpu-costs @ 243c3a0d (its three code commits on main 414f56e2; on the fork as `measured/three-cpu-costs-243c3a0d`). The branch is now on main 25726217: `git range-diff` shows the same three patches (cf67e86b, 1c2bdc6b, dd342a08). Main's commits in between touch `vk_draw.cpp` and `vk_texture.cpp` elsewhere (the pipeline cache, `dispatch`, `texture_frame`), not `texture_gpu_wrote`, `gpu_wrote_into` or the vertex bindings of `draw()`, and not `vk_memory.cpp` at all, so the numbers hold. Built without a play profile (medians; each run in brackets, in the order run): | | Before: main @ 414f56e2 | After: this PR @ 243c3a0d | Δ | |---|---|---|---| | fps | 28.54 (28.62 / 28.46) | 29.92 (29.20 / 30.64) | +1.38 | | replay a frame | 31.15 ms (31.12 / 31.17) | 29.49 ms (30.04 / 28.94) | -1.66 ms | | render thread: instructions a frame | 62.19 M (62.38 / 61.99) | 59.20 M (59.07 / 59.33) | -4.8% | | render thread: cycles a frame | 78.96 M (79.15 / 78.77) | 75.96 M (76.32 / 75.59) | -3.8% | Built with the committed play profile (`RX_VK_PGO=<assets/profiles/longshot_vk.profdata.xz, unpacked>`; the build log says `built with the profile`): | | Before: main @ 414f56e2, play profile | After: this PR @ 243c3a0d, play profile | Δ | |---|---|---|---| | fps | 31.49 (30.68 / 32.30) | 31.47 (30.30 / 32.63) | -0.02 | | replay a frame | 26.75 ms (26.99 / 26.50) | 26.26 ms (26.49 / 26.02) | -0.49 ms | | render thread: instructions a frame | 56.12 M (56.19 / 56.04) | 54.53 M (54.61 / 54.44) | -2.8% | | render thread: cycles a frame | 68.88 M (68.99 / 68.76) | 68.35 M (68.91 / 67.78) | -0.8% | ``` main, no profile 2400 presents, 27.4 fps; a frame: 2167 draws (0 skipped), 144 dispatches, 194 passes, 163 barriers, 6 submits main, no profile 2280 presents, 26.5 fps; a frame: 2158 draws (0 skipped), 144 dispatches, 193 passes, 163 barriers, 6 submits this PR, no profile 2280 presents, 28.8 fps; a frame: 2147 draws (0 skipped), 144 dispatches, 194 passes, 163 barriers, 6 submits this PR, no profile 2160 presents, 29.7 fps; a frame: 2150 draws (0 skipped), 144 dispatches, 194 passes, 163 barriers, 6 submits main, profile 2400 presents, 31.1 fps; a frame: 2156 draws (0 skipped), 139 dispatches, 193 passes, 154 barriers, 6 submits this PR, profile 2280 presents, 32.1 fps; a frame: 2148 draws (0 skipped), 138 dispatches, 194 passes, 152 barriers, 6 submits ``` Read with care. Two runs each on one machine and one scene; the fps of two runs of one renderer differ by up to 2 (the play profile's arms went from 30.3-30.7 in the first round to 32.3-32.6 in the second), while the instruction counts repeat within 0.6%. The three commits were not measured apart. With the play profile, `draw()`, `gpu_wrote_into`, `texture_gpu_wrote`, `memory_cpu_owned`, `shadow_written_to` and `guest_cpu_wrote` no longer match the recording and are compiled as if there were none, which is what takes back most of the gain there until the profile is recorded again (assets/profiles/README.md: "a function changed since the recording is compiled as if there were no profile"). ### Flags and settings One new A/B switch; the other two changes have none. | Flag | What it is | Before this PR | After this PR | Effect | Helps / doesn't help / untested | Recommendation | |---|---|---|---|---|---|---| | `RX_VK_VB_RANGE=0` | all 16 vertex bindings bound whenever one changed | that behaviour, always | unset: only the bindings that changed are bound | none on the picture; fewer bindings handed to the driver | helps: Steam Deck (RADV) above; untested: other drivers | leave unset: on for everyone, the switch is for A/B only | ## Testing **Tested:** - `python -I -B tests/run_all.py` on macOS with the Vulkan headers given: 499 passed, 41 failed, 39 skipped; the 41 fail the same way on main 25726217 on that machine (496 passed, 41 failed: no `/proc`, no `posix_fallocate`, Python 3.9, the Linux launch scripts and the like). - New: `tests/test_mirror_bookkeeping.py` (the three functions cut out of the source, without mirrors and with, and that only a write-back reads what they keep), `tests/test_vertex_bind_range.py`, `tests/test_wrote_regions.py`. Each fails on main. Updated, no check changed: `tests/test_report_words.py`, `tests/test_wb_diff_stale_shadow.py` (their pools have mirrors: a `mirror_on()` stand-in) and `tests/test_fallback_pipeline_state.py` (takes `vertex_bind_range` from the source). - In the game, a Steam Deck OLED, without a screen: the 9 starts above, no error, no device lost, 0 GPU timeouts in the kernel's log. With the picture, by a person: a build holding these commits with other renderer-only ones and the play profile, played on the same Deck on 2026-11-14: nothing wrong seen on screen. - Linux: the back end builds from these commits on main 414f56e2 (clang, on the Deck): 0 failed. - Windows: the back end cross-builds from the same commits with llvm-mingw 20261104 on Linux: 0 failed, `longshot_vk.dll` made. Not run. **Not tested:** - A GPU with VRAM mirrors in the game: the bookkeeping there is as before (the fixtures run it with mirrors), but no run was made. - Windows in the game; other drivers' handling of a partial `vkCmdBindVertexBuffers2`. - Other scenes; a long session. **Must be tested:** - Windows, any GPU: a short play through a few areas with this PR, expecting nothing drawn differently (by hand: models and terrain intact, no flicker; or Test Farm: @Beanstalk windows, a walk through Kokiri Forest and Hyrule Field) ## Risks **What could break:** a draw's vertex streams, if a driver mishandles a partial bind (models missing or stretched); a texture the GPU wrote not uploaded again, if a write's regions were not noted (the table is filled before the map and never cleared, and addresses past 40 bits share bits, so it can only say "ask the map" too often); the mirrors' write-backs, if a mirror existed while `mirror_on()` said no (a pool gets one only in `mirror_make`, whose two callers, `gpu_buffer_bind` and `memory_begin_submission`, are both behind `mirror_on()`). **Turning it off:** `RX_VK_VB_RANGE=0` for the vertex bindings; reverting the commit for each of the other two. **Compatibility:** Nothing changes: no shader or pipeline cache is rebuilt (no stamped file is touched), `MKWVK_API_VERSION` is as it was. ## Issues See #402 ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). - [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. ### When it applies - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] Switches documented (docs/development.md; the README for a menu row) and shipped at their recommended value. - [x] A file in `docs/tested/` for each new measured run (format in `docs/tested-configurations.md`). - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. - [x] Docs updated.
A mirror's write-back reads two things noted as the replay goes: the report words the replay wrote on the CPU (memory_cpu_owned), which it leaves out, and the guest pages the CPU wrote (shadow_written_to), which its diff reads again. Where the pools have no mirrors (an integrated GPU, a GCN card, RX_VK_MIRROR=0) no write-back is ever made: the words piled up to 65,536 and were sorted, merged and dropped, the page lists to 8 million entries (32 MB) before being dropped, about 1 % of the replay thread on a Steam Deck in bursts. memory_cpu_owned, shadow_written_to and guest_cpu_wrote return at once there.

tests/test_mirror_bookkeeping.py runs them, cut out of the source, without mirrors and with, and checks that only a write-back reads what they keep.

Assisted-by: Claude Fable 5.2

Co-Authored-By: Claude Fable 5.2 <[email protected]>
The driver takes every binding it is handed. A draw whose streams differed from the last ones bound handed it all 16, the unused ones with the zero buffer again: 1.1 % of the replay thread on a Steam Deck, in the driver. vertex_bind_range gives the bindings that differ from what the command buffer has, and only that range is bound; all 16 are still remembered. RX_VK_VB_RANGE=0: all 16 at each change, as before.

tests/test_vertex_bind_range.py runs vertex_bind_range, cut out of the source, and checks draw() binds its range.

Assisted-by: Claude Fable 5.2

Co-Authored-By: Claude Fable 5.2 <[email protected]>
Each texture a submission samples asked gpu_wrote_into whether a GPU buffer write reached its storage: a lock and a walk of the map of written ranges, 0.6 % of the replay thread on a Steam Deck, for an answer that is nearly always no. texture_gpu_wrote now notes the 2 MB regions a write reaches (gpu/vk/wrote_regions.h, 64 KB of bits, never cleared), before the map has the write, and gpu_wrote_into answers no from them when the texture's storage is in none; otherwise the map answers, as before.

tests/test_wrote_regions.py runs the table and checks the order of both uses.

Assisted-by: Claude Fable 5.2

Co-Authored-By: Claude Fable 5.2 <[email protected]>
test_report_words and test_wb_diff_stale_shadow cut memory_cpu_owned, shadow_written_to and guest_cpu_wrote, which now ask mirror_on(): their pools have mirrors, so their stand-in answers yes. test_fallback_pipeline_state cuts draw()'s vertex bindings, which now call vertex_bind_range: it takes that function from the source too. No check is changed.

Assisted-by: Claude Fable 5.2

Co-Authored-By: Claude Fable 5.2 <[email protected]>
So this branch and the all-zero check's (which adds guest_holes.h after target_overlap.h) do not add a line at the same place.

Assisted-by: Claude Fable 5.2

Co-Authored-By: Claude Fable 5.2 <[email protected]>
docs(tested): three small costs of the replay thread on a Steam Deck OLED, without and with the play profile
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 5s
beanstalk/approval Test farm not requested
Website / Website on Cloudflare (pull_request_target) Has been skipped
510dcd3a27
Main 414f56e2 against the three commits on it (243c3a0d), a heavy view, 2 runs each, alternated; each built without a play profile and with the committed one.

Assisted-by: Claude Fable 5.2

Co-Authored-By: Claude Fable 5.2 <[email protected]>
p0lux changed title from WIP: perf(vk): three small costs of the replay thread removed: unread bookkeeping without mirrors, 16 vertex bindings a change, a lock a sampled texture to perf(vk): three small costs of the replay thread removed: unread bookkeeping without mirrors, 16 vertex bindings a change, a lock a sampled texture 2026-11-14 21:29:54 +00:00
HumanBean approved these changes 2026-11-15 19:03:43 +00:00
HumanBean left a comment

Read all three code changes and the tests.

  • Vertex bindings: vertex_bind_range looks right. It returns the span of bindings that differ, or all 16 when nothing is valid yet. All 16 are still remembered in last_vb, and last_vb_valid is still reset wherever the command buffer state is reset. That draw is the only vkCmdBindVertexBuffers2 call, and the partial bind is legal with dynamic strides.
  • Mirror bookkeeping: mirror_on() is a once-computed static, so it can't flip mid-run and leave a write-back with notes it never took. The early returns cover exactly what only a write-back reads.
  • wrote_regions: the bits are a superset, never cleared, and set before the map insert. A false positive just falls back to the map, and every path that fills gpu_wrote goes through texture_gpu_wrote. Relaxed ordering is fine for a lock-free "maybe" filter.
  • Switch and docs: RX_VK_VB_RANGE is documented and defaults to on. No MKWVK_API_VERSION bump is needed because vk_api.h is untouched.

I did not run anything. The remaining risk is a driver mishandling a partial vertex bind, which the PR already lists, and its "Must be tested" Windows run should still happen. Nothing in the diff blocks the merge. Approving.

Read all three code changes and the tests. - **Vertex bindings:** `vertex_bind_range` looks right. It returns the span of bindings that differ, or all 16 when nothing is valid yet. All 16 are still remembered in `last_vb`, and `last_vb_valid` is still reset wherever the command buffer state is reset. That draw is the only `vkCmdBindVertexBuffers2` call, and the partial bind is legal with dynamic strides. - **Mirror bookkeeping:** `mirror_on()` is a once-computed static, so it can't flip mid-run and leave a write-back with notes it never took. The early returns cover exactly what only a write-back reads. - **`wrote_regions`:** the bits are a superset, never cleared, and set before the map insert. A false positive just falls back to the map, and every path that fills `gpu_wrote` goes through `texture_gpu_wrote`. Relaxed ordering is fine for a lock-free "maybe" filter. - **Switch and docs:** `RX_VK_VB_RANGE` is documented and defaults to on. No `MKWVK_API_VERSION` bump is needed because `vk_api.h` is untouched. I did not run anything. The remaining risk is a driver mishandling a partial vertex bind, which the PR already lists, and its "Must be tested" Windows run should still happen. Nothing in the diff blocks the merge. Approving.
HumanBean deleted branch perf/three-cpu-costs 2026-11-15 19:19:11 +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 5s
beanstalk/approval Test farm not requested
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
2 participants
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!590
No description provided.