Per-pass GPU timing via the 'timestamp-query' device feature.
Each render/compute pass in the render graph is bracketed with a pair of
timestamp writes (beginningOfPassWriteIndex/endOfPassWriteIndex). After
the command buffer is submitted the query set is resolved into a buffer,
copied into a mappable readback buffer, and mapped asynchronously; the
begin/end delta for each pass is the GPU-side duration of that pass.
profiler.beginFrame(); // ...for each pass: pass descriptor gets `timestampWrites: profiler.writesFor(name)` profiler.resolve(encoder); // before encoder.finish() queue.submit([encoder.finish()]); profiler.afterSubmit(); // kicks off async readback
Results (results) are EMA-smoothed per pass name across frames, so
the display is stable even though individual frames jitter. Note that Chrome
quantizes timestamp values (100 µs by default, unless the "WebGPU Developer
Features" flag is enabled), so a single pass under ~0.1 ms reads as 0 on many
frames; the EMA over many frames still recovers a useful relative mean.
Per-pass GPU timing via the
'timestamp-query'device feature.Each render/compute pass in the render graph is bracketed with a pair of timestamp writes (
beginningOfPassWriteIndex/endOfPassWriteIndex). After the command buffer is submitted the query set is resolved into a buffer, copied into a mappable readback buffer, and mapped asynchronously; the begin/end delta for each pass is the GPU-side duration of that pass.Usage (driven by RenderGraph.execute):
Results (results) are EMA-smoothed per pass name across frames, so the display is stable even though individual frames jitter. Note that Chrome quantizes timestamp values (100 µs by default, unless the "WebGPU Developer Features" flag is enabled), so a single pass under ~0.1 ms reads as 0 on many frames; the EMA over many frames still recovers a useful relative mean.