Skip to content

core: the profiler counts GPU targets made per frame - #423

Merged
foxnne merged 1 commit into
mainfrom
core/profile-gpu-counts
Oct 14, 2026
Merged

foxnne merged 1 commit into
mainfrom
core/profile-gpu-counts

Conversation

@foxnne

@foxnne foxnne commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

What changes

The profiler now counts what each frame asks the GPU to allocate, which no timed scope sees. The devtools frame line gains textures N /f targets N /f switches N /f. A frame picked on the graph, and a held hitch, show their own counts ("made 6 targets"). Hitch chips say "made N" when their frame allocated anything. devtools.stats / core.profile.report writes .gpu = .{ … } on .frame and on each hitch.

Why: dragging a window-tall float on Safari ran at ~66 fps, but the profiler showed 1.22 ms of work and 0.05 ms of submit. The cost was the frost recreating ~5 render targets a frame (fixed separately in #422), and on ANGLE/Metal that cost lands outside every scope.

How:

  • Counted in the host's backend, which every image's textures pass through (plugins included). The counts are running totals: textures made from pixels, targets made, either destroyed, and target switches. Each is one integer add beside a call into the GPU, so a closed profiler pays nothing that matters.
    • Native: Health.Counts gains textures/targets/destroyed next to its existing target_switches. SDLBackend.gpuTotals() returns Health.total.
    • Web: WebBackend keeps the same four totals. A switch counts only when the target actually changes, as on native.
  • core/profile/Gpu.zig is std-only: the counts, their wrapping difference, and a per-frame average. Entry passes the backend's totals to hostFrameBegin, next to last_submit_ns. The profiler assigns the difference to the last recorded frame, the same way it handles submit, so the counts cover that frame's submit too. It keeps them in the history, the window average (FrameStats.gpu), and the hitch (Lookback.noteGpu).
  • Layout changes bump profile.abi 4→5 and Lookback.abi 1→2. A plugin built against the old layout records nothing until it is rebuilt, which is the existing guard.

SDK impact

  • Core-only or additive: reaches plugins at the next SDK release (test-sdk-version passes; fingerprint unchanged)

Verified

  • macOS: zig build, zig build test (91/91 steps), zig build test-integration (53/53 steps, fizzy-profile-tests 18/18), zig build test-sdk-version. New unit tests cover counts across a wrap, a frame's counts landing in its own slot and the window average (and not before the profiler records), and a hitch keeping its late counts but no other frame's.
  • Windows:
  • Linux:
  • Web: zig build check-web, plus a live run (zig build web, ?worker=0) in the desktop browser pane, Chromium, at 120 fps:
    • Idle reads textures 0.0 /f targets 0.0 /f switches 0.0 /f.
    • Opening the File menu (a frost of a new size) reads targets 0.3 /f over 61 frames (~20 targets). A JS wrapper on gl.createTexture/createFramebuffer counted 30 of each over the same stretch.
    • With the menu held open, it settles back to targets 0.0 /f, with switches 13.0 /f (the frost's passes).

Follow-ups

  • A hitch is still a frame whose work goes over budget. The Safari case (1.2 ms work, slow interval) would not have been kept as one. Keeping frames by interval, or by "made targets", is a separate change.
  • Opening a menu makes ~20–30 targets over its open animation. That's the same capture-size churn core: a window-tall frost keeps its capture size as it moves #422 fixes for floats, and worth checking after it lands.

🤖 Generated with Claude Code

The profiler timed fizzy's work and the backend's submit, but not what a
frame asks the driver to allocate. A window-tall frost on Safari remade
~5 render targets a frame and ran at 66 fps while the profiler said 1.2 ms
of work. Now each backend keeps running totals of textures made, targets
made, both destroyed, and target switches; the profiler takes the
difference each frame and shows it per frame, per history frame, and per
hitch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne marked this pull request as ready for review October 14, 2026 17:45
@foxnne
foxnne merged commit 54da359 into main Oct 14, 2026
15 checks passed
@foxnne
foxnne deleted the core/profile-gpu-counts branch October 14, 2026 17:45
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