import { StatsPayload } from "../debug-protocol.js"; import { BufferCursor } from "./bus-pool.js"; //#region src/debug/StatsCollector.d.ts interface RenderInfo { calls?: number; triangles?: number; lines?: number; points?: number; geometries?: number; textures?: number; timestamp?: number; } interface RendererLike { info?: { render?: RenderInfo; memory?: { geometries: number; textures: number; }; frame?: number; }; backend?: { trackTimestamp?: boolean; constructor?: { name?: string; }; disjoint?: unknown; /** * Public three.js API for reading which frame ids were in the last * resolved timestamp batch. `type` is `'render' | 'compute'`. * Returns `[...frameIds]` where the LAST entry is the frame whose * duration is in `renderer.info[type].timestamp`. */ getTimestampFrames?(type: 'render' | 'compute'): number[]; }; resolveTimestampsAsync?(type: 'render' | 'compute'): Promise; } /** * Per-frame stats collector. * * Writes one sample per `endFrame` into pre-allocated typed-array * buffers (size `STATS_RING_SIZE`). `drainBatch` snaps the valid * prefix of each buffer out as `subarray` views (zero data copy — * views over the same ArrayBuffer) and resets the write cursor to 0. * * `postMessage` / structuredClone copies the view contents *at call * time*, so overwriting the underlying buffer on subsequent frames is * safe. Overflow (>`STATS_RING_SIZE` samples collected between flushes * — ~4 s at 60 Hz) drops the newest sample rather than overwriting, * keeping the oldest-first batch contract. */ declare class StatsCollector { private _latestRenderer; private _frame; get frame(): number; private _renderStartAt; private _lastFrameEndAt; private _callsBefore; private _trianglesBefore; private _linesBefore; private _pointsBefore; private _gpuCapable; private _gpuResolveInFlight; private _gpuMs; private _gpuLastAt; /** * Most-recently-resolved GPU ms, used to forward-fill the ring slot * for frames whose own resolve hasn't landed yet. Without this the * graph zigzags — only ~1/3 of frames get a resolved value per GPU * round-trip, so the remaining 2/3 would ship as `0` and render as * noise spikes. Forward-fill renders as stairsteps between updates * instead, which accurately reflects "we don't have newer data * yet" without fabricating a fake zero. */ private _lastResolvedGpuMs; /** * Three.js frame id → ring slot index. Populated in `endFrame` so * the async timestamp-resolve path can retroactively write the * duration into the sample slot for the frame it actually measured, * not the frame that happened to be current when the promise * landed. `getTimestampFrames()` gives us the frame id of the * resolved duration (last entry of its array); we look up the slot * and write there. * * Cleared on `drainBatch` since slot indices are relative to * `_write` which resets. Late-arriving resolves for already-drained * frames silently drop — acceptable since the consumer has already * rendered the batch. */ private _tjsFrameToIdx; private _perfMemory; /** Heap limit is static — emit once on the first batch, then omit. */ private _heapLimitEmitted; private readonly _fpsBuf; private readonly _cpuMsBuf; private readonly _gpuMsBuf; private readonly _heapUsedBuf; private readonly _drawCallsBuf; private readonly _trianglesBuf; private readonly _primitivesBuf; private readonly _geometriesBuf; private readonly _texturesBuf; private _write; private _startFrame; private _fpsAccum; private _fpsFrames; private _fpsSmoothed; beginFrame(now: number, renderer: RendererLike | undefined): void; endFrame(renderer: RendererLike): void; /** * Schedule a GPU-timestamp resolve (async). Called every frame by the * producer to keep three's query pool drained; no-op when the backend * doesn't support timestamps. */ maybeResolveGpu(): void; /** * One-shot drain of three's timestamp query pool, used when GPU sampling * is about to be turned off (panel collapsed). Resolving flushes the * queries three has already written but not yet resolved — three's resolve * resets the pool's query cursor synchronously — so we don't strand entries * in the pool when we stop. MUST be called while `trackTimestamp` is still * true: the backend's resolve path no-ops (and warns) once tracking is off. * No-op on backends that can't do timestamps. The resolved value is * discarded — we're tearing the sampler down, not recording a sample. */ flushGpu(renderer: RendererLike): void; get gpuCapable(): boolean; /** * Fill `out` with the valid prefix of each ring as typed-array * views. Two modes: * * - `into` provided → views are positioned over the supplied * pool buffer (`copyTypedTo` memcpys our private rings in). * The producer can then transfer that pool buffer to the bus * worker without paying `structuredClone` on the render thread. * - `into` omitted → views over our private rings (legacy path * used by the inline transport, which `structuredClone`s on * `BroadcastChannel.postMessage` — fine when not bus-busy). * * Returns `true` if anything was written. `_write` resets to 0 * either way — next frame starts the next batch. */ drainBatch(out: StatsPayload, into?: BufferCursor): boolean; /** Called when a consumer re-subscribes; forces the next batch to re-send `heapLimitMB`. */ resetDelta(): void; dispose(): void; } //#endregion export { StatsCollector }; //# sourceMappingURL=StatsCollector.d.ts.map