WIP: perf(vk): uniform updates found by the 1 KB of guest memory they touch, not by walking the in-flight list #796

Draft
dyst wants to merge 1 commit from dyst/Longshot:perf/vk-ub-update-index into main
Member

Type: perf · Risk: low · Tested on: Windows 11 (the game-free checks; the game A/B is next) · Needs: a game run on NVIDIA, AMD and Intel

Why

To bind a uniform buffer the replay checks whether the stream has updated any of it (UpdateUniformBuffer), and it did that by walking every update still in flight, for every uniform buffer and handle word of every rebuilt draw; a cached gap went with every update. Found in an audit of the renderer's per-draw path; no issue on the tracker.

What changed

The in-flight updates are indexed by the 1 KB of guest memory they touch, so a lookup reads only the buckets a range touches; each byte still gets the same last writer, and the parallel replay's per-segment limits are honoured by stream number. Updates' bytes live in 256 KB chunks instead of one heap vector each.

  • replay: ub_buckets, ub_odd, ub_find, ub_overlaps, ub_apply, ub_push, ub_truncate, ub_store and the callers ub_try_gap, ub_overlay, ub_read, ub_prune, flush_ub_updates (gpu/vk/vk_replay.cpp)
  • tests: tests/test_ub_index.py (the real code against a walk over random pushes, prunes, limits and ranges, four threads on a frozen list); test_ub_gap, test_ub_memo, test_ub_word_index follow the new shape

Measurements

Verdict: not measured yet: A/B runs in the game (this PR against main on one base, interleaved) are next and will replace this section.

Flags and settings

None.

Testing

Tested: Windows 11, llvm-mingw (the builder's v2.0.14 tools): the new and changed checks; every check that reads the
changed code; -fsyntax-only of the changed renderer files with the build's flags and headers; python -I -B tests/run_all.py on this branch and on main @ aa95d35f, same PC and environment: the same results apart from the new test_ub_index; the failures main has here are named in the first comment.

Not tested: the game yet. This PR is WIP until its A/B is in (next: this PR against main on one dev_packs base of
main @ aa95d35f, on an RTX 3080 with a Ryzen 9 3900X, Windows 11; AMD and Intel need other testers).

Must be tested:

  • Any OS and GPU, defaults and once with RX_VK_PARALLEL=4: Kokiri Forest and Castle Town a few minutes, expecting the same picture and a lower replay time (Test Farm: @Beanstalk any, those scenes, with and without RX_VK_PARALLEL=4)

Risks

What could break: A uniform overlay with the wrong bytes: an object drawn with another frame's constants (wrong position, flicker). The new lookup is checked against the old walk over random streams.

Turning it off: revert the commit

Compatibility: Nothing changes: no caches, settings or vk ABI involved.

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.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • A game-free test added for the fix, failing on main and passing here.

🤖 Generated with Claude Code

**Type:** perf · **Risk:** low · **Tested on:** Windows 11 (the game-free checks; the game A/B is next) · **Needs:** a game run on NVIDIA, AMD and Intel ## Why To bind a uniform buffer the replay checks whether the stream has updated any of it (`UpdateUniformBuffer`), and it did that by walking every update still in flight, for every uniform buffer and handle word of every rebuilt draw; a cached gap went with every update. Found in an audit of the renderer's per-draw path; no issue on the tracker. ## What changed The in-flight updates are indexed by the 1 KB of guest memory they touch, so a lookup reads only the buckets a range touches; each byte still gets the same last writer, and the parallel replay's per-segment limits are honoured by stream number. Updates' bytes live in 256 KB chunks instead of one heap vector each. - replay: `ub_buckets`, `ub_odd`, `ub_find`, `ub_overlaps`, `ub_apply`, `ub_push`, `ub_truncate`, `ub_store` and the callers `ub_try_gap`, `ub_overlay`, `ub_read`, `ub_prune`, `flush_ub_updates` (`gpu/vk/vk_replay.cpp`) - tests: `tests/test_ub_index.py` (the real code against a walk over random pushes, prunes, limits and ranges, four threads on a frozen list); `test_ub_gap`, `test_ub_memo`, `test_ub_word_index` follow the new shape ## Measurements **Verdict:** not measured yet: A/B runs in the game (this PR against main on one base, interleaved) are next and will replace this section. ### Flags and settings None. ## Testing **Tested:** Windows 11, llvm-mingw (the builder's v2.0.14 tools): the new and changed checks; every check that reads the changed code; `-fsyntax-only` of the changed renderer files with the build's flags and headers; python -I -B tests/run_all.py on this branch and on main @ aa95d35f, same PC and environment: the same results apart from the new `test_ub_index`; the failures main has here are named in the first comment. **Not tested:** the game yet. This PR is WIP until its A/B is in (next: this PR against main on one dev_packs base of main @ aa95d35f, on an RTX 3080 with a Ryzen 9 3900X, Windows 11; AMD and Intel need other testers). **Must be tested:** - Any OS and GPU, defaults and once with `RX_VK_PARALLEL=4`: Kokiri Forest and Castle Town a few minutes, expecting the same picture and a lower replay time (Test Farm: @Beanstalk any, those scenes, with and without RX_VK_PARALLEL=4) ## Risks **What could break:** A uniform overlay with the wrong bytes: an object drawn with another frame's constants (wrong position, flicker). The new lookup is checked against the old walk over random streams. **Turning it off:** revert the commit **Compatibility:** Nothing changes: no caches, settings or vk ABI involved. ## 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] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] A game-free test added for the fix, failing on `main` and passing here. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
perf(vk): uniform updates found by the 1 KB of guest memory they touch, not by walking the in-flight list
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 7s
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 7s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 7s
Third-party parts / As their notes say (pull_request) Successful in 9s
Website / Website on Cloudflare (pull_request_target) Has been skipped
2ed73baf91
A uniform buffer's overlay (ub_overlay), a handle word's read (ub_read) and the gap their caches keep (ub_try_gap) looked
for the stream's uniform updates over a range by walking every update still in flight (the list spans the submissions the GPU
has not finished), and the cached gap went with every UPDATE_UB. With descriptors rebuilt on most draws that was a walk per
uniform buffer and handle word.

The updates are now indexed by the 1 KB buckets of guest memory they touch (ub_buckets, by stream number; one over more than
128 buckets or that wraps goes in ub_odd, and a lookup that would see one walks the list as before). ub_find, ub_overlaps and
ub_apply read only the buckets a range touches, honour ub_limit (a worker sees the updates before its segment) by stream
number, and copy each bucket's part from the newest update that covers it, so every byte has the same last writer as the
walk. Updates' bytes go one after another in 256 KB chunks (ub_store) instead of a heap vector each, freed as they are pruned.
The list changes only in ub_push, ub_prune and ub_truncate (the benchmark's), which keep the index; the parallel replay's
workers only read it while the list holds still, and background threads (ub_limit 0) read nothing of it, as before.

tests/test_ub_index.py runs the real code against a walk of a copy of the list over random pushes, prunes, truncations,
limits and ranges (and four threads reading a frozen list); the same checks pass on main's code. test_ub_gap, test_ub_memo
and test_ub_word_index follow the new shape.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Author
Member

The checks on this PC (Windows 11, RTX 3080, Ryzen 9 3900X, llvm-mingw from the builder's v2.0.14 tools): python -I -B tests/run_all.py on main @ aa95d35f fails these 19 here, and so does this branch: test_appimage_player_build.py, test_bake_portability.py, test_cpm_discovery.py, test_cpp_preflight.py, test_crash_dialog.py, test_env_on.py, test_fpx_sticky.py, test_lift_cleanup_error.py, test_linux_preflight.py, test_make_game_cache.py, test_make_game_prepare.py, test_missing_nup.py, test_oot_state_tap.py, test_options_budget_once.py, test_readback_late.py, test_spawn_emit.py, test_tested_configurations.py, test_vk_pgo.py, test_walk_key.py. Any other failure in a full run on this branch was rerun on its own and passed (the full runs shared the PC with dev_packs builds: timing checks).

Next: an interleaved A/B in the game against main on one dev_packs base of aa95d35f; the numbers will replace the Measurements section.

The checks on this PC (Windows 11, RTX 3080, Ryzen 9 3900X, llvm-mingw from the builder's v2.0.14 tools): `python -I -B tests/run_all.py` on main @ aa95d35f fails these 19 here, and so does this branch: `test_appimage_player_build.py`, `test_bake_portability.py`, `test_cpm_discovery.py`, `test_cpp_preflight.py`, `test_crash_dialog.py`, `test_env_on.py`, `test_fpx_sticky.py`, `test_lift_cleanup_error.py`, `test_linux_preflight.py`, `test_make_game_cache.py`, `test_make_game_prepare.py`, `test_missing_nup.py`, `test_oot_state_tap.py`, `test_options_budget_once.py`, `test_readback_late.py`, `test_spawn_emit.py`, `test_tested_configurations.py`, `test_vk_pgo.py`, `test_walk_key.py`. Any other failure in a full run on this branch was rerun on its own and passed (the full runs shared the PC with dev_packs builds: timing checks). Next: an interleaved A/B in the game against main on one dev_packs base of aa95d35f; the numbers will replace the Measurements section.
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 7s
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 7s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 7s
Third-party parts / As their notes say (pull_request) Successful in 9s
Website / Website on Cloudflare (pull_request_target) Has been skipped
This pull request is marked as a work in progress.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'perf/vk-ub-update-index:dyst-perf/vk-ub-update-index'
git switch 'dyst-perf/vk-ub-update-index'
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!796
No description provided.