Documentation
¶
Overview ¶
Package metrics tracks per-frame timing budgets for imzero2 — Go render, Rust interpret, and vsync slack. Exposes a metrics.Current singleton that the metrics-overlay widget reads each frame.
Index ¶
- Constants
- Variables
- type FrameMetrics
- func (inst *FrameMetrics) BeforeSync()
- func (inst *FrameMetrics) BeginFrame()
- func (inst *FrameMetrics) EndFrame()
- func (inst *FrameMetrics) FpsDigest() *tdigest.TDigest
- func (inst *FrameMetrics) RecordBytes(written int, read int)
- func (inst *FrameMetrics) RecordRust(interpretUs uint64, passNr uint64)
- func (inst *FrameMetrics) Snapshot() (s Snapshot)
- type Snapshot
Constants ¶
const SlowFrameThresholdNs int64 = 25_000_000
SlowFrameThresholdNs is the real-work budget at or above which FrameMetrics.RecordBytes emits a structured warning. "Real work" is the Go-side widget build (render) plus the Rust-side interpret — the two slots the app can actually regress, and disjoint ones (the Rust side nets out the time it spent waiting on Go's stream, so this is not render counted twice). Sync wait is deliberately excluded; see [shouldWarnSlowFrame] for why. Set to 1.5 × the 60 Hz frame budget so jitter that stays inside vsync slack stays quiet, but a frame whose work missed its deadline surfaces with its breakdown (render_us / sync_us / interpret_us / written_b / read_b / frame).
The log line is intentionally emitted from RecordBytes — last call in the frame lifecycle — so the timings (set by EndFrame just before) and the wire byte counters (set inside RecordBytes itself) line up against the same frame. Use case is stutter triage: a run of log lines whose render_us or interpret_us is elevated names the slot — Go-render or Rust-interpret — that overran. Zero disables.
Variables ¶
var Current = NewFrameMetrics()
Current is the singleton consumed by the overlay widget and written to from the frame loop. Zero-value usable via NewFrameMetrics.
var PackageProps = packageprops.Props{ WASMWASI: packageprops.WASMCompiles, WASMJS: packageprops.WASMCompiles, WASMFreestanding: packageprops.WASMCompiles, }
PackageProps records this package's curated properties (ADR-0080). Seeded by `boxer code analysis golang wasmsurvey props generate`; curate by hand. The same group's `props verify` reconciles it.
Functions ¶
This section is empty.
Types ¶
type FrameMetrics ¶
type FrameMetrics struct {
LastRenderNs int64
LastSyncNs int64
LastTotalNs int64
LastWritten int64
LastRead int64
LastInterpretNs int64
LastPassNr uint64
EmaRenderNs float64
EmaSyncNs float64
EmaTotalNs float64
EmaWritten float64
EmaRead float64
EmaInterpretNs float64
// contains filtered or unexported fields
}
FrameMetrics holds per-frame counters captured on the Go side. The struct is single-threaded by construction: the imzero2 frame loop runs in one goroutine, so no synchronisation is required.
The lifecycle of a single frame is:
BeginFrame() — at StartServersideFrame, stamps tStart
BeforeSync() — after user widget code, before End/Reset/Sync
EndFrame() — after Sync returns, commits render/sync deltas
RecordBytes() — from the application loop, after RenderLoopHandler
returned, captures wire bytes for the just-finished frame
All "Last*" fields hold the most recently committed frame; the overlay renders these at the *next* frame's MenuBar (one-frame display lag, invisible at 60 Hz).
func NewFrameMetrics ¶
func NewFrameMetrics() *FrameMetrics
func (*FrameMetrics) BeforeSync ¶
func (inst *FrameMetrics) BeforeSync()
func (*FrameMetrics) BeginFrame ¶
func (inst *FrameMetrics) BeginFrame()
func (*FrameMetrics) EndFrame ¶
func (inst *FrameMetrics) EndFrame()
EndFrame commits the timing deltas measured between BeginFrame, BeforeSync, and now. Tolerant of a missing BeforeSync stamp: if BeforeSync was never called for this frame (e.g. an early-return path in the renderer) render time is reported as the full frame and sync time as zero, rather than spuriously spiking the display.
func (*FrameMetrics) FpsDigest ¶
func (inst *FrameMetrics) FpsDigest() *tdigest.TDigest
FpsDigest returns the windowed frame-rate distribution for the overlay to hand to a distsummary widget. The pointer is stable across frames; the digest is rebuilt in place by rebuildFpsDigest. Single-threaded with the frame loop (same goroutine), so no synchronisation is required.
func (*FrameMetrics) RecordBytes ¶
func (inst *FrameMetrics) RecordBytes(written int, read int)
RecordBytes is called from the outer frame loop once the just-finished frame's wire counters can be sampled (after RenderLoopHandler returned and Sync drained the inbound register fetches).
When the frame's real work (render + interpret) crosses SlowFrameThresholdNs — see [shouldWarnSlowFrame] — a structured warning is emitted with the full per-frame breakdown, total_us included so the excluded sync wait stays visible during triage.
func (*FrameMetrics) RecordRust ¶
func (inst *FrameMetrics) RecordRust(interpretUs uint64, passNr uint64)
RecordRust commits the Rust-side per-frame metrics drained from the fetchFrameMetrics fetcher in StateManager.Sync. Reports the previous completed Rust frame's interpret_commands_outer elapsed (one-frame display lag, invisible at 60 Hz). interpretUs is microseconds; 0 means the first frame has not yet completed Rust-side.
func (*FrameMetrics) Snapshot ¶
func (inst *FrameMetrics) Snapshot() (s Snapshot)
type Snapshot ¶
type Snapshot struct {
FrameCounter uint64
RenderNs int64
SyncNs int64
TotalNs int64
RawTotalNs int64
InterpretNs int64
SlackNs int64
WrittenBytes int64
ReadBytes int64
RustPassNr uint64
}
Snapshot is an immutable view of the last completed frame, suitable for rendering. All ns values are smoothed via EMA; LastTotalNs is also included raw for callers that want the unsmoothed value. SlackNs is the vsync residual: TotalNs (Go-side wall clock = render + sync) minus InterpretNs (Rust compute). It captures how much of the 16.6 ms budget is spent waiting on the next vsync rather than on either side's work.