PrivaterootsPrivateactivePrivateactivePrivatenextPrivatecurrentPrivateupdatePrivatepassPrivatesortPrivatedepthPrivatedepthPrivategenerationPrivatelistenersPrivate StaticmakeInternal: current generation counter. Read by SessionImpl to gate
its end() merge — and by the depth-sort coordinator to gate its
recordDepthSortCompletion call — against being abandoned by a
reset() that landed mid-flight. NOT a public API.
Internal: draw the next depth-sort sequence number. Called by a
deferred-seq SessionImpl (a depth-sort pass) at end() so its seq
reflects COMPLETION order, not dispatch order (issue #713). NOT a
public API.
Begin a new update cycle (root session) Call this at the start of each update pipeline
End the current update cycle Call this at the end of each update pipeline
Begin a background LOD-refinement pass. Returns a detached root session that merges into the 'LOD Refinement' persistent tree and does NOT touch the active update session or the ambient context — a refinement pass ending mid-update must never disable the update's own profiling.
Callers pass the returned session explicitly (pass.begin('GSplats (/p)')) down the load → process → commit chain, mirroring the main update's per-node top-level sessions.
Begin a depth-sort round-trip (depth-sorting Phase 3). Returns a detached root session that merges into the 'Depth Sort' persistent tree — same isolation contract as beginPass: it never touches the active update session or the ambient context, so a sort resolving mid-update cannot disable the update's own profiling.
The depth-sort coordinator opens one per SortWorker dispatch, stamps
setMetadata({ elements, info, kernelMs, boundaryMs, queueMs, applyMs })
(element count, ordering bytes, and the worker/boundary/queue/apply
timing split — the latency numbers are last-write on merge), and ends
it when the ordering is APPLIED — i.e. its buffer is flipped in and
drawn — NOT merely when the SortWorker RPC resolves (large orderings
apply CHUNKED over many later frames; issue #713). The session also
ends if the ordering is abandoned/cancelled before it is ever drawn,
or if the RPC fails. Because the pass now spans the chunked apply, the
info byte tag starts SCHEDULED (sched, at resolve) and is upgraded
to uploaded (up) only once the buffer actually reaches the GPU.
Begin a child timing entry directly under the root Use this for top-level parallel operations (Points, Lines, GSplats)
Name of this timing entry
Session that should be passed to nested operations
Begin a child timing entry of the current innermost session This is the context/ambient pattern - no need to pass session around
NOTE: For concurrent async operations at the top level, use beginTopLevel() or timeTopLevel() instead to avoid session stack corruption.
Name of this timing entry
Session
Convenience: run a top-level parallel operation with timing Creates a direct child of root, safe for concurrent use with Promise.all
Name of this timing entry (e.g., 'Points (/path)')
Async function to time
Result of fn()
Convenience: run a function with timing Automatically handles begin/end and preserves return value
NOTE: For top-level parallel operations, use timeTopLevel() instead.
Name of this timing entry
Sync or async function to time
Result of fn()
Convenience: run an async function with timing and metadata Allows setting metadata before the operation completes
Name of this timing entry
Function that receives session for metadata and returns result
Result of fn()
Mark an operation as skipped (for extend_to_all, etc.) Creates a timing entry with 0 duration and skip reason
Name of the skipped operation
Why it was skipped
Check if profiling is currently active Useful for conditional instrumentation
Get the current innermost session (for setting metadata) Returns NoOpSession if no update is active
Get the 'Total Update' timing hierarchy (for UI display)
Get the 'LOD Refinement' timing hierarchy (background passes).
count on this root is the number of refinement passes recorded.
Get the 'Depth Sort' timing hierarchy (gsplat sort round-trips).
count on this root is the number of sorts recorded; lastMs is the
latest sort's dispatch→applied round-trip latency.
Record one applied depth-sort completion on the dedicated MONOTONIC
stream (issue #711). Increments the total, tags the event with its
seq, and pushes it onto the bounded ring (oldest trimmed at
DEPTH_SORT_COMPLETION_CAP). Independent of the aggregate 'Depth
Sort' root, so N applications between two polls remain N latency events —
the exact multi-completion undercount issue #711 found.
Snapshot the depth-sort completion stream (issue #711): the monotonic
total (authoritative for the count even if some events aged out of the
bounded ring) plus a COPY of the currently-buffered events (each tagged
with its seq, for latency sampling). The event objects are cloned too,
so mutating a returned event cannot corrupt later snapshots.
Reset all timing data
Clears active-session state too: if reset() is called mid-update, any stale RootSession / currentSessionContext / per-id sessions are dropped so that subsequent endUpdate() / timeTopLevel() calls don't try to merge into the freshly rebuilt rootEntry under a name they no longer own.
Add a listener for timing updates
Remove a listener
Internal: Merge a completed entry into persistent state Called by SessionImpl.end()
PrivatemergeMerge a completed session entry's values into a persistent entry.
PrivatemarkFlag every entry the update/pass seq did not touch as stale.
PrivatemergeFind parent entry and merge child into it
PrivatemergeMerge a child entry into a children array
PrivateclonePrivatenotifyNotify all listeners of timing update
Main profiler class
Supports two usage patterns:
Context/ambient pattern (recommended):
Manual session management:
NOTE: Supports concurrent async operations. Each time() call tracks its parent context at call time, so parallel operations work correctly.