Skip to content

Vulkan: profiler follow-up: a draw's phases sampled together, slow paths exact - #989

Merged
hckrman101 merged 1 commit into
NotRexed:mainfrom
klimovich008:perf-prof-sampled-draw-scopes
Nov 9, 2026
Merged

hckrman101 merged 1 commit into
NotRexed:mainfrom
klimovich008:perf-prof-sampled-draw-scopes

Conversation

@klimovich008

@klimovich008 klimovich008 commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

What this is now

The first commit of this PR (b70cff4, per-draw phase timers sampled one in eight) was merged into main in 66a0f28. This branch is now rebased on current main and holds only the follow-up commit that answered the review, which the merge did not include.

Change

  • One decision per draw. The outermost PROF_HOT on a thread decides once per draw, and nested hot scopes follow that decision, so a draw's phases are sampled together and their sum stays consistent.
  • Slow paths exact. Rare scopes (pipeline creation, uploads) are always timed, not sampled.
  • RX_VK_PROF_ALL=1 still times every scope.

Tested

  • tests/test_prof_sampling.py (new, in run_all.py): 400,000 synthetic draws. Nested phases bounded, slow paths counted exactly once, phase totals within 0.6% of the true totals, RX_VK_PROF_ALL=1 exact. It reads the sampling code this change adds, so on main it stops at a missing scope rather than showing a wrong total; it passes with the change.
  • tests/test_parallel_rb_refresh.py still passes after the rebase.

Not covered

Not re-run in game after the rebase. Profiling builds only: release timings are unchanged.

🤖 Generated with Claude Code

@klimovich008

Copy link
Copy Markdown
Contributor Author

Thanks, all addressed in 90ec861 (rebased on current main):

  • One decision per draw. The outermost PROF_HOT on a thread picks once per draw, and nested hot scopes follow that pick. A child can't exceed its parent any more, and it's one PRNG step per draw.
  • Slow paths are exact and counted once. Plain PROF scopes already are. A new PROF_EXACT() covers program_pick, the program cache's disk read, a pipeline new to the thread, ensure_layout, the ready-wait, mesh_indirect_convert and texture create(). Each thread keeps a running total of exact time; a hot scope adds (elapsed − exact)·8 + exact when sampled and only exact when not. So a 20 ms pipeline build shows as 20 ms either way, and that also covers PROF_UPLOAD inside an unsampled lookup. I didn't use a time threshold, because knowing a scope ran long needs the clock read the change is skipping.
  • Bench and slow logs. RX_VK_BENCH turns on full timing while it replays, and so does RX_VK_SLOW_LOG. SlowWatch judges a submit only after it ends, so its stall numbers come out exact from the point above and only the cheap per-draw remainder is an estimate. A comment there now says so.
  • Dispatch. PROF_DISPATCH is plain PROF again.
  • Minor points. RX_VK_PROF_ALL is read once into a namespace-scope atomic. Each thread is seeded with splitmix64 of a counter, and the pick uses the top bits (>> 29).

tests/test_prof_sampling.py runs the real scopes on a fake clock over 400k nested draws:

  • nested phases stay bounded;
  • slow paths in unsampled draws count exactly once;
  • long-run totals stay within 2% (0.6% observed);
  • RX_VK_PROF_ALL=1 and bench mode give exact totals.

Three deliberately broken variants (independent nested picks, exact part not subtracted, nested exact scopes counted twice) each fail it.

The fps figure in the description was measured before this rework. The extra bookkeeping is only on the slow paths, but I haven't re-measured it.

@klimovich008
klimovich008 force-pushed the perf-prof-sampled-draw-scopes branch from 26e4596 to 90ec861 Compare November 9, 2026 10:20
@klimovich008
klimovich008 force-pushed the perf-prof-sampled-draw-scopes branch from 90ec861 to f226108 Compare November 9, 2026 15:30
The outermost PROF_HOT on a thread picks one draw in eight and the phases nested
in it follow its pick, so a phase never reads more than the one around it (one
PRNG step per draw). What is slow and rare inside them (program_pick, a program
read from disk, a pipeline new to the thread, a layout made, a ready wait,
mesh_indirect_convert, a texture created, PROF_UPLOAD) is timed exactly by
PROF / PROF_EXACT and counted once as it is: the PROF_HOT scopes around it
weigh only the rest. Every scope is timed with RX_VK_PROF_ALL=1 or
RX_VK_SLOW_LOG (read once, prof_full) and while RX_VK_BENCH replays its frame.
PROF_DISPATCH is exact again. The seed is splitmix64 of a per-thread counter,
the pick the generator's top bits.

tests/test_prof_sampling.py runs the real scopes on a fake clock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@klimovich008 klimovich008 changed the title Vulkan: the per-draw phase timers sampled one in eight Vulkan: profiler follow-up: a draw's phases sampled together, slow paths exact Nov 9, 2026
@hckrman101
hckrman101 merged commit 1c82d6b into NotRexed:main Nov 9, 2026
10 checks passed
NotRexed added a commit that referenced this pull request Nov 9, 2026
…s from-disk path times itself since #989)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant