Skip to content

Add previous-frame GPU time and acquire stages to log_frame_times - #123

Open
nicoburns wants to merge 3 commits into
mainfrom
devin/1792538931-gpu-frame-timer
Open

nicoburns wants to merge 3 commits into
mainfrom
devin/1792538931-gpu-frame-timer

Conversation

@nicoburns

@nicoburns nicoburns commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Summary

Follow-up to #92 (whose commit is included here, since its branch lives on a fork). Once the post-present poll is nonblocking the log_frame_times timer loses its only GPU figure (the old wait stage was effectively "submit → GPU done"), and present lumps the swapchain acquire wait in with the actual present. This restores both, for anyrender_vello and anyrender_vello_hybrid:

  • acquire stage. With an intermediate texture the surface is acquired explicitly (ensure_current_surface_texture) between vello's submit and the blit, so acquire is the drawable wait and present is just the blit submit + present. Without one (SurfaceRenderer::has_intermediate_texture() == false) target_texture_view() already acquired, so acquire is recorded right after it and no blit stage is timed. The poll stage is dropped: with PollType::Poll it measures 5–30µs.

  • gpu render / gpu blit (previous frame). New wgpu_context::GpuFrameTimer, compiled only with the new wgpu_context/log_frame_times feature (enabled by both renderers' log_frame_times); a no-op struct otherwise.

    self.gpu_timer.start();
    renderer.render_to_texture(..);              // or queue.submit() in hybrid
    self.gpu_timer.submitted("render", device, queue);
    ...
    self.gpu_timer.start();
    render_surface.maybe_blit_and_present()?;
    self.gpu_timer.submitted("blit", device, queue);
    ...
    self.gpu_timer.print_completed("vello: ");    // prints whatever completed since last frame

    submitted registers a Queue::on_submitted_work_done callback that records start.elapsed(). Because wgpu only runs those callbacks from submit/poll, a plain callback would read "time until the next frame's poll" (measured 15–19ms for an empty hybrid frame). So on native the timer spawns one gpu-frame-timer thread that device.poll(PollType::Poll)s every 250µs while anything is in flight and parks otherwise. It deliberately never uses PollType::Wait: wgpu-core's wait holds the snatch read lock for the duration while queue.present needs the write lock, so a blocking poll on another thread would stall present on the main thread. On wasm callbacks are driven by the browser, no thread.

    The figure is wall-clock submit→completion observed by wgpu (+ up to 250µs polling granularity), i.e. an upper bound on GPU time, not a timestamp query. Exact GPU timings would need TIMESTAMP_QUERY (vello's wgpu-profiler integration uses encoder-level queries, which Apple GPUs don't support).

Measurements

bunnymark, release build, macOS/Metal, 60 Hz. Medians over ~2k+ frames per cell; frame total is vsync-bound at ~17ms, almost all of it in acquire (13–19ms waiting for a drawable).

gpu render gpu blit present (CPU)
vello, 0 bunnies 2.97ms 1.29ms 109µs
vello, 500 bunnies 3.83ms 1.34ms 135µs
vello_hybrid, 0 bunnies 1.95ms 2.05ms 12µs
vello_hybrid, 500 bunnies 2.40ms 2.28ms 10µs

Sample output:

vello: 15ms (cmd: 2.2us, render: 410us, acquire: 15ms, present: 83us)
vello: gpu blit: 1.15ms (previous frame)
vello: gpu render: 2.42ms (previous frame)

Checks

cargo fmt --check, cargo clippy --workspace -D warnings, and clippy with anyrender_vello/log_frame_times,anyrender_vello_hybrid/log_frame_times pass locally. wasm not built locally (no target installed); the wasm path is the no-op wake_poller.

Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/c025c1dce5e8468bb851c75a4d4f9112
Open in Devin Desktop: https://dioxus.staging.devinenterprise.com/desktop/session/c025c1dce5e8468bb851c75a4d4f9112?variant=devin-insiders
Requested by: @nicoburns

@staging-devin-ai-integration

Copy link
Copy Markdown

I'll fix CI failures and address comments from users with write access that start with 'Devin'.

  • Disable automatic comment, CI, and merge conflict monitoring

VladimirKras and others added 3 commits October 20, 2026 16:48
Use nonblocking device maintenance after presenting in both GPU Vello window renderers. Surface acquisition still supplies bounded presentation backpressure while the CPU can prepare the next frame before the previous submission completes.

Assisted-by: Codex/gpt-5
…t; skip acquire/blit stages without an intermediate texture; drop the poll stage
@staging-devin-ai-integration
staging-devin-ai-integration Bot force-pushed the devin/1792538931-gpu-frame-timer branch from ddd2a3b to b3fee79 Compare October 20, 2026 23:49
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.

2 participants