perf(vk): a program's descriptor counts are kept with it, and a build asks for the thread's writer once #41
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
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/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!41
Loading…
Reference in a new issue
No description provided.
Delete branch "HumanBean/Longshot:perf/desc-counts"
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?
Why
Building a draw's descriptors is the largest single part of the replay's CPU time (about 2.1-2.9 ms a frame in Kokiri Forest). Two things in it were repeated for every draw without depending on the draw. Taking them out saves about 0.3 ms a frame of CPU time on the replay thread there; no frame-rate gain is claimed.
What changed
build_descriptors(gpu/vk/vk_draw.cpp) walked six descriptor lists per stage before every build, only to size threereservecalls. The three numbers are the program's own: they are counted once and kept inProgram::desc_counts(gpu/vk/vk_shader.h; one packed word, written only once the program is ready, since its stages are final then).RX_VK_DESC_COUNTS=0counts them at every build, as before.dw()is a thread-local lookup, which in a library loaded withdlopenis a call into the loader; a build made about 26 of them.build_descriptorsnow takes the reference once.tests/test_desc_counts.py: the packing compiled from the source (every count comes back, counts that do not fit are not kept), and the rules read frombuild_descriptors(kept only for a ready program, the switch, the reserve made from the counts).tests/test_mesh_fallback.py: its check of the writer's program reads the new spelling (out.prog = &p;).Flags and settings
RX_VK_DESC_COUNTS0counts a program's descriptors at every build, as before, instead of keeping the counts with the programThe switch is in the switch reference of
docs/development.md("The GPU" table).Measurements
main:
15d4ae8c(2026-11-12)this PR: perf/desc-counts @
0fa31fa2Hardware: RTX 3090, NVIDIA 620.70.06, Linux (CPU and RAM not recorded in this description)
Scene: Kokiri Forest, the pristine save, standing; 2048x1152 window, default options
Method:
RX_VK_BENCH=3000,300replays one frame 300 times on the replay thread and logs the phases; three runs a side. The frame differs a little between runs, so the figures are per drawHow to reproduce: start the game into Kokiri Forest on the pristine save with default options and
RX_VK_BENCH=3000,300, stand still, and read the phases the benchmark logsThe timed build was on an older base than the branch head, and the numbers are not re-measured yet. Both sides were built on main
15d4ae8c: unchanged main, and this change on top of it. The change then moved onto a newer main as0fa31fa2, and the branch headbaee6044merges the current main (94bbc205) into it and adds the switch's row todocs/development.md. No game was built or timed on the merged tree. How the merged tree differs in the files this PR touches (compared by diff after putting the project's new name in the old files):gpu/vk/vk_shader.hand the two tests differ in nothing else;gpu/vk/vk_draw.cppalso has main's change that lets the Descriptor reuse and Sampler cache rows apply in play, which adds one thread-local store tobuild_descriptorsand is on both sides of any comparison with main. So the saving per draw should hold, but that is an expectation, not a measurement. A clean re-measure (unchanged main beside the change, at least three interleaved runs a side, medians, fps and frame time) is still owed: the PC these numbers come from is swapping heavily (about 24 GB in swap, and the last five timed runs on it were void for memory), so it waits for a PC that is not.15d4ae8c0fa31fa2The PC was swapping during all six runs (other work on it), which moves every phase of a run together: run 3 on main (the PC swapping hard) is 30 % slower in every column. So the first row is the one to read, the descriptor phase against the commands phase of the same run: 0.92-0.96 on main, 0.80-0.83 with the change, in all three runs a side. That is about 0.13-0.15 us a draw, or 0.3 ms a frame of CPU time on the replay thread at 2,300 draws. The absolute figures of the two quiet runs (0.916 and 0.904 against 0.760) say the same. Programs and commands do not move, as expected of a change inside
build_descriptors.No frame-rate gain is claimed. In this scene the frame is bounded by the GPU's time and the present's wait for it, not by this thread's CPU time.
Not measured: fps (average and 1% low) and the median and p99 frame time; a median is not reported, the three runs of each side are. Whether the runs of the two sides were interleaved is not recorded in this description. The log lines themselves are not pasted here, only the figures read from them.
Testing
Tested:
tests/test_desc_counts.py(new) andtests/test_mesh_fallback.py, on Linux. Run again on the branch headbaee6044(the tree merged with main94bbc205): both pass.python -I -B tests/run_all.py: 501 passed, 2 failed, 26 skipped on this PC (Linux, 4 at once), on the tree of the earlier merge with main (e2a4d453); not run again on the merge with main94bbc205, where only this PR's two tests were run. The two that fail need tools this PC lacks (zstandard,libtk) and fail on unchanged main here too.bind_descriptors,texture_view_ofand the pass functions) measured no further gain in two runs.Not tested:
Needs other testers:
Screenshots
Nothing on screen.
Risk and rollback
The counts size the
reservecalls of every draw's descriptor build, and the writer's reference is now taken once a build: a mistake would show as wrong or missing descriptors, on screen as flashing or missing draws, which is what the owed flash check is for. The counts are kept only once a program is ready.RX_VK_DESC_COUNTS=0counts at every build as before (the writer's single lookup stays); reverting the commit removes both.Reviewer notes
Read
build_descriptorsingpu/vk/vk_draw.cppfirst, thenProgram::desc_countsingpu/vk/vk_shader.h. Work in progress: not for review until the flash check and the play session are in.Issues
None.
Checklist
For everyone
type(scope): description).main. (By a merge, not a rebase: main94bbc205is merged into the branch headbaee6044; the one conflict, ingpu/vk/vk_shader.h, was resolved in the earlier merge.)python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above).third_party/untouched (a new folder with aPROVENANCE.mdinstead).If applicable
Assisted-by:/Co-Authored-By:trailers.docs/tested-configurations.mdfor each measured run. (Not added.)MKWVK_API_VERSIONbumped (gpu/vk/vk_api.h'sMkwVkHostorMkwVkRendererchanged). (n/a)mainand passing here. (n/a: not a fix. A game-free test of the counts is added.)tests/that reads changed source updated in this PR, not weakened.docs/development.md.)The description is now in the template's sections. No code changed and no new numbers: the same facts as before, with the commits the measurements were taken on named, and the checklist ticked only where true. It stays work in progress: the flash check and the play session are still owed.
Merged current main (
94bbc205) into the branch (headbaee6044) and added the RX_VK_DESC_COUNTS row to docs/development.md; the PR's two tests pass on the merged tree. No new numbers, and the flash check and play session are still owed: they and the re-measure are waiting for a PC that is not swapping, so this stays work in progress.WIP: perf(vk): a program's descriptor counts are kept with it, and a build asks for the thread's writer onceto perf(vk): a program's descriptor counts are kept with it, and a build asks for the thread's writer once