WIP: perf(vk): a direct indexed draw keeps its pool's index buffer binding, its offset as the first index #798
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!798
Loading…
Reference in a new issue
No description provided.
Delete branch "dyst/Longshot:perf/vk-index-buffer-bind"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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
Each submesh has its own index offset, and the replay bound the index buffer at it: nearly every indexed draw recorded a
vkCmdBindIndexBuffer. Found in an audit of the renderer's per-draw path; no issue on the tracker.What changed
A direct indexed draw binds its pool's buffer at the start and draws from
first = offset / index size, so the submeshes of one pool keep one binding. The same indices are read, and robust access's bounds see the same reads in and out of bounds. Indirect draws keep their binding at NVN's address.index_bind_baseand the direct indexed draw indraw()(gpu/vk/vk_draw.cpp);RX_VK_PROF_FINE=1counts the bind commands recorded (index buffer bind commands)tests/test_draw_binds.pychecks the read addresses and bounds against the old schemeMeasurements
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
One switch for an A/B.
RX_VK_INDEX_AT_OFFSET=1=1for an A/BTesting
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-onlyof 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 extendedtest_draw_binds; 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:
RX_VK_PROF_FINE=1, far fewerindex buffer bind commandsa draw than withRX_VK_INDEX_AT_OFFSET=1(Test Farm: @Beanstalk amd, intel: those scenes, look for missing or spiky geometry)Risks
What could break: A driver that mishandles large
firstIndexvalues (up to the pool offset over the index size, ~2^27): missing or spiky geometry on that driver.Turning it off:
RX_VK_INDEX_AT_OFFSET=1Compatibility: Nothing changes: no caches, settings or vk ABI involved.
Checklist
Everyone
type(scope): description) of the status line's type.main.python -I -B tests/run_all.pypasses (any failure that also fails onmainis named).third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.mainand passing here.🤖 Generated with Claude Code
Each submesh has an index offset of its own, and the draw bound the index buffer at it: nearly every indexed draw recorded a vkCmdBindIndexBuffer. A direct indexed draw now binds the buffer at its start and draws from first = offset / index size (index_bind_base), so the submeshes of one pool keep one binding. The same indices are read (start + first * size is the offset); robust access's bounds run from the bound offset to the buffer's end and no read lies below the draw's offset, so nothing read in or out of bounds changes. An offset that is no multiple of the index size, or a first index and count past 32 bits, binds at the offset as before. Indirect indexed draws keep their binding at NVN's address: the GPU-written first indices count from there. RX_VK_INDEX_AT_OFFSET=1: every direct draw bound at its offset, as before. RX_VK_PROF_FINE=1 counts the bind commands recorded ("index buffer bind commands"), not only the bind calls. tests/test_draw_binds.py checks index_bind_base's results and fallbacks against the old scheme's read addresses and bounds. Assisted-by: Claude Opus 5.5 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>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.pyon main @aa95d35ffails 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.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.