@zkmake/three-meter 0.13.3 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,18 @@ const readThreeRevision = () => {
4
4
  const revision = globalThis.__THREE__;
5
5
  return typeof revision === "string" && revision !== "" ? revision : null;
6
6
  };
7
+ /** React DevTools' hook lists each React renderer loaded, when the extension is there. */
8
+ const readDevtoolsFrameworks = () => {
9
+ const hook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
10
+ const found = {};
11
+ if (!(hook?.renderers instanceof Map)) return found;
12
+ for (const renderer of hook.renderers.values()) {
13
+ const version = typeof renderer.version === "string" ? renderer.version : null;
14
+ if (renderer.rendererPackageName === "@react-three/fiber") found.r3f = version;
15
+ else if (renderer.rendererPackageName === "react-dom") found.react = version;
16
+ }
17
+ return found;
18
+ };
7
19
  /**
8
20
  * Trim a WebGL renderer string to the part a person recognises. Chrome wraps
9
21
  * it in ANGLE, e.g. `ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 (0x00002786)
@@ -45,8 +57,7 @@ const webgpuGpuName = (info) => {
45
57
  };
46
58
  const isWebglContext = (value) => typeof WebGL2RenderingContext !== "undefined" && value instanceof WebGL2RenderingContext || typeof WebGLRenderingContext !== "undefined" && value instanceof WebGLRenderingContext;
47
59
  const webglBackend = (gl) => typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext ? "webgl2" : "webgl";
48
- const readEnvironment = (renderer) => {
49
- const three = readThreeRevision();
60
+ const readGraphics = (renderer) => {
50
61
  const backend = renderer.backend;
51
62
  if (backend) {
52
63
  if (backend.isWebGPUBackend) {
@@ -54,39 +65,42 @@ const readEnvironment = (renderer) => {
54
65
  return device ? {
55
66
  backend: "webgpu",
56
67
  fallback: false,
57
- gpu: webgpuGpuName(device.adapterInfo),
58
- three
68
+ gpu: webgpuGpuName(device.adapterInfo)
59
69
  } : {
60
70
  backend: null,
61
71
  fallback: false,
62
- gpu: null,
63
- three
72
+ gpu: null
64
73
  };
65
74
  }
66
75
  if (backend.isWebGLBackend && isWebglContext(backend.gl)) return {
67
76
  backend: webglBackend(backend.gl),
68
77
  fallback: backend.parameters?.forceWebGL !== true,
69
- gpu: webglGpuName(backend.gl),
70
- three
78
+ gpu: webglGpuName(backend.gl)
71
79
  };
72
80
  return {
73
81
  backend: null,
74
82
  fallback: false,
75
- gpu: null,
76
- three
83
+ gpu: null
77
84
  };
78
85
  }
79
86
  const context = renderer.getContext?.();
80
87
  return isWebglContext(context) ? {
81
88
  backend: webglBackend(context),
82
89
  fallback: false,
83
- gpu: webglGpuName(context),
84
- three
90
+ gpu: webglGpuName(context)
85
91
  } : {
86
92
  backend: null,
87
93
  fallback: false,
88
- gpu: null,
89
- three
94
+ gpu: null
95
+ };
96
+ };
97
+ const readEnvironment = (renderer, frameworks = {}) => {
98
+ const devtools = readDevtoolsFrameworks();
99
+ return {
100
+ ...readGraphics(renderer),
101
+ three: readThreeRevision(),
102
+ react: frameworks.react ?? devtools.react ?? null,
103
+ r3f: frameworks.r3f ?? devtools.r3f ?? null
90
104
  };
91
105
  };
92
106
  /** Fewer frames than this and the refresh rate reads `null`. */
@@ -627,8 +641,10 @@ var PerformanceMonitor = class {
627
641
  smoothedFps = 0;
628
642
  sample;
629
643
  environment = null;
644
+ frameworks;
630
645
  constructor(options) {
631
646
  const { renderer, trackGPU = true, gpuQueryPoolSize = DEFAULT_GPU_POOL_SIZE, historySize = DEFAULT_HISTORY_SIZE, frameStatsSize = DEFAULT_FRAME_STATS_SIZE } = options;
647
+ this.frameworks = options.frameworks;
632
648
  if (ATTACHED.has(renderer)) throw new Error("three-meter: a PerformanceMonitor is already attached to this renderer. dispose() it first.");
633
649
  ATTACHED.add(renderer);
634
650
  this.renderer = renderer;
@@ -736,13 +752,13 @@ var PerformanceMonitor = class {
736
752
  return scene ? objectsForKey(scene, key) : [];
737
753
  }
738
754
  /**
739
- * three revision, backend and GPU name. Cached once the backend is known;
755
+ * three revision, React and React Three Fiber versions, backend and GPU name. Cached once the backend is known;
740
756
  * before `WebGPURenderer.init()` settles it, `backend` is `null` and the
741
757
  * next call reads again.
742
758
  */
743
759
  getEnvironment() {
744
760
  if (this.environment) return this.environment;
745
- const environment = Object.freeze(readEnvironment(this.renderer));
761
+ const environment = Object.freeze(readEnvironment(this.renderer, this.frameworks));
746
762
  if (environment.backend !== null) this.environment = environment;
747
763
  return environment;
748
764
  }
@@ -797,4 +813,4 @@ var PerformanceMonitor = class {
797
813
  };
798
814
  export { RingBuffer as n, PerformanceMonitor as t };
799
815
 
800
- //# sourceMappingURL=performance-monitor-Dj5H2VXT.js.map
816
+ //# sourceMappingURL=performance-monitor-Bz3ukv7t.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performance-monitor-Bz3ukv7t.js","names":[],"sources":["../src/core/environment.ts","../src/core/frame-stats.ts","../src/core/gpu-busy.ts","../src/core/gpu-timer.ts","../src/core/ring-buffer.ts","../src/core/scene-cost.ts","../src/core/performance-monitor.ts"],"sourcesContent":["/**\n * What the monitor is running on: three revision, React and React Three Fiber versions, rendering\n * backend, GPU. Read from the renderer and the page, never from an import of any of them: React's\n * come from the app, or from React DevTools' hook where it's installed.\n *\n * `WebGPURenderer` settles its backend in `init()`, so until then the backend\n * reads `null`; callers poll and the monitor caches once it is known.\n */\nimport type { Environment, FrameworkVersions, PerfRenderer, RenderBackend } from \"./types.ts\";\n\ntype BackendShape = {\n device?: { adapterInfo?: AdapterInfoShape } | null;\n gl?: unknown;\n isWebGLBackend?: boolean;\n isWebGPUBackend?: boolean;\n parameters?: { forceWebGL?: boolean };\n};\n\ntype AdapterInfoShape = {\n architecture?: string;\n description?: string;\n device?: string;\n vendor?: string;\n};\n\ntype DebugRendererInfoExt = { readonly UNMASKED_RENDERER_WEBGL: number };\n\n/** three sets `window.__THREE__` to its revision on import (and warns on a second copy). */\nconst readThreeRevision = (): string | null => {\n const revision = (globalThis as { __THREE__?: unknown }).__THREE__;\n\n return typeof revision === \"string\" && revision !== \"\" ? revision : null;\n};\n\ntype DevtoolsRenderer = { rendererPackageName?: unknown; version?: unknown };\n\n/** React DevTools' hook lists each React renderer loaded, when the extension is there. */\nconst readDevtoolsFrameworks = (): FrameworkVersions => {\n const hook = (globalThis as { __REACT_DEVTOOLS_GLOBAL_HOOK__?: { renderers?: unknown } })\n .__REACT_DEVTOOLS_GLOBAL_HOOK__;\n const found: FrameworkVersions = {};\n\n if (!(hook?.renderers instanceof Map)) {\n return found;\n }\n\n for (const renderer of hook.renderers.values() as Iterable<DevtoolsRenderer>) {\n const version = typeof renderer.version === \"string\" ? renderer.version : null;\n\n if (renderer.rendererPackageName === \"@react-three/fiber\") {\n found.r3f = version;\n } else if (renderer.rendererPackageName === \"react-dom\") {\n found.react = version;\n }\n }\n\n return found;\n};\n\n/**\n * Trim a WebGL renderer string to the part a person recognises. Chrome wraps\n * it in ANGLE, e.g. `ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 (0x00002786)\n * Direct3D11 vs_5_0 ps_5_0, D3D11)` or `ANGLE (Apple, ANGLE Metal Renderer:\n * Apple M3 Pro, Unspecified Version)`.\n */\nconst cleanGpuName = (raw: string): string => {\n let name = raw.trim();\n const angle = /^ANGLE \\((.*)\\)$/.exec(name);\n\n if (angle) {\n const parts = angle[1]!.split(\", \");\n name = parts[1] ?? parts[0]!;\n }\n\n return name\n .replace(/^ANGLE [\\w ]+ Renderer: /, \"\")\n .replace(/ \\(0x[0-9a-f]+\\)/i, \"\")\n .replace(/ (Direct3D\\d*|OpenGL|Vulkan|Metal)\\b.*$/, \"\")\n .trim();\n};\n\nconst webglGpuName = (gl: WebGLRenderingContext | WebGL2RenderingContext): string | null => {\n // Firefox deprecates the extension but already returns the unmasked name from RENDERER.\n const ext = gl.getExtension(\"WEBGL_debug_renderer_info\") as DebugRendererInfoExt | null;\n const raw = gl.getParameter(ext ? ext.UNMASKED_RENDERER_WEBGL : gl.RENDERER) as unknown;\n\n return typeof raw === \"string\" && raw !== \"\" && raw !== \"WebKit WebGL\" ? cleanGpuName(raw) : null;\n};\n\n/** WebGPU reports vendors lowercase (`apple`, `nvidia`). */\nconst VENDOR_NAMES: Record<string, string> = {\n amd: \"AMD\",\n apple: \"Apple\",\n arm: \"Arm\",\n intel: \"Intel\",\n nvidia: \"NVIDIA\",\n qualcomm: \"Qualcomm\",\n};\n\n/**\n * `GPUDevice.adapterInfo`. Chrome leaves `description` empty to limit\n * fingerprinting, so this is usually vendor + architecture: `Apple metal-3`.\n */\nconst webgpuGpuName = (info: AdapterInfoShape | undefined): string | null => {\n if (!info) {\n return null;\n }\n\n if (info.description) {\n return info.description;\n }\n\n const vendor = info.vendor ? (VENDOR_NAMES[info.vendor] ?? info.vendor) : \"\";\n const name = [vendor, info.architecture].filter(Boolean).join(\" \");\n\n return name === \"\" ? null : name;\n};\n\nconst isWebglContext = (value: unknown): value is WebGLRenderingContext | WebGL2RenderingContext =>\n (typeof WebGL2RenderingContext !== \"undefined\" && value instanceof WebGL2RenderingContext) ||\n (typeof WebGLRenderingContext !== \"undefined\" && value instanceof WebGLRenderingContext);\n\nconst webglBackend = (gl: WebGLRenderingContext | WebGL2RenderingContext): RenderBackend =>\n typeof WebGL2RenderingContext !== \"undefined\" && gl instanceof WebGL2RenderingContext\n ? \"webgl2\"\n : \"webgl\";\n\ntype Graphics = Pick<Environment, \"backend\" | \"fallback\" | \"gpu\">;\n\nconst readGraphics = (renderer: PerfRenderer): Graphics => {\n const backend = renderer.backend as BackendShape | undefined;\n\n // WebGPURenderer. Read the backend's own fields: `getContext()` before\n // `init()` is what the GPU timer learned not to touch.\n if (backend) {\n if (backend.isWebGPUBackend) {\n const device = backend.device;\n\n return device\n ? { backend: \"webgpu\", fallback: false, gpu: webgpuGpuName(device.adapterInfo) }\n : { backend: null, fallback: false, gpu: null };\n }\n\n if (backend.isWebGLBackend && isWebglContext(backend.gl)) {\n return {\n backend: webglBackend(backend.gl),\n fallback: backend.parameters?.forceWebGL !== true,\n gpu: webglGpuName(backend.gl),\n };\n }\n\n return { backend: null, fallback: false, gpu: null };\n }\n\n // WebGLRenderer.\n const context = renderer.getContext?.();\n\n return isWebglContext(context)\n ? { backend: webglBackend(context), fallback: false, gpu: webglGpuName(context) }\n : { backend: null, fallback: false, gpu: null };\n};\n\nconst readEnvironment = (\n renderer: PerfRenderer,\n frameworks: FrameworkVersions = {},\n): Environment => {\n const devtools = readDevtoolsFrameworks();\n\n return {\n ...readGraphics(renderer),\n three: readThreeRevision(),\n react: frameworks.react ?? devtools.react ?? null,\n r3f: frameworks.r3f ?? devtools.r3f ?? null,\n };\n};\n\nexport { cleanGpuName, readEnvironment };\n","/**\n * Stutter statistics over a window of frame intervals. Average FPS hides a\n * hitch every few seconds; these don't. Stalls (hidden tab, breakpoint) never\n * reach the window, so they don't count as hitches.\n */\nimport type { FrameStats } from \"./types.ts\";\n\n/** A frame over this multiple of the window's median interval is a hitch. */\nconst HITCH_FACTOR = 2;\n\n/** Fewer frames than this and the refresh rate reads `null`. */\nconst REFRESH_MIN_FRAMES = 60;\n\n/** Common display refresh rates. A reading within 4% of one snaps to it. */\nconst REFRESH_RATES = [30, 48, 50, 60, 72, 75, 90, 100, 120, 144, 165, 180, 240, 360];\n\nconst EMPTY: FrameStats = Object.freeze({\n frames: 0,\n hitches: 0,\n lowFps: 0,\n p99Ms: 0,\n refreshHz: null,\n});\n\n/**\n * The rate the loop runs at when it keeps up: the fastest tenth of frames,\n * not the average, so slow frames don't drag it down. That is the display's\n * refresh rate, or the app's own cap if it throttles itself.\n *\n * Intervals are averaged with their neighbour first. A frame that starts late\n * leaves a short interval after it, and on a real 120 Hz display those short\n * ones alone read as 125–127 Hz; each pair averages back to the vsync.\n */\nconst refreshFrom = (intervals: readonly number[]): number | null => {\n if (intervals.length < REFRESH_MIN_FRAMES) {\n return null;\n }\n\n const pairs = intervals.slice(1).map((interval, index) => (interval + intervals[index]!) / 2);\n pairs.sort((a, b) => a - b);\n const rate = 1000 / pairs[Math.floor(pairs.length * 0.1)]!;\n const nearest = REFRESH_RATES.reduce((best, candidate) =>\n Math.abs(candidate - rate) < Math.abs(best - rate) ? candidate : best,\n );\n\n return Math.abs(nearest - rate) / nearest <= 0.04 ? nearest : Math.round(rate);\n};\n\nconst computeFrameStats = (intervals: readonly number[]): FrameStats => {\n const frames = intervals.length;\n\n if (frames === 0) {\n return EMPTY;\n }\n\n const sorted = [...intervals].sort((a, b) => a - b);\n // Nearest rank: the smallest interval at or above 99% of frames.\n const p99Ms = sorted[Math.ceil(frames * 0.99) - 1]!;\n\n // 1% low: the mean rate across the slowest 1% of frames (at least one frame).\n const slowest = Math.max(1, Math.ceil(frames * 0.01));\n let slowSum = 0;\n\n for (let index = frames - slowest; index < frames; index += 1) {\n slowSum += sorted[index]!;\n }\n\n const middle = frames >> 1;\n const median = frames % 2 === 0 ? (sorted[middle - 1]! + sorted[middle]!) / 2 : sorted[middle]!;\n const threshold = median * HITCH_FACTOR;\n let hitches = 0;\n\n for (const interval of intervals) {\n if (interval > threshold) {\n hitches += 1;\n }\n }\n\n return Object.freeze({\n frames,\n hitches,\n lowFps: 1000 / (slowSum / slowest),\n p99Ms,\n refreshHz: refreshFrom(intervals),\n });\n};\n\nexport { computeFrameStats, HITCH_FACTOR };\n","/**\n * WebGPU frame time as the GPU was busy, not the sum of its passes.\n *\n * three's `resolveTimestampsAsync` adds up `end - start` of every pass in the\n * frame. Tile-based GPUs (Apple, most mobile) run passes overlapped, each\n * pass's span includes waiting on the ones before it, and the sum lands far\n * above the frame: keytar-hero's 15 passes read 14 ms, busy for 1.7 ms. Here\n * the raw begin/end pairs are merged, so overlap counts once.\n *\n * three throws those raw values away, so the tap wraps two members of its\n * timestamp pool (`_resolveQueries` for the uid → query offsets,\n * `resultBuffer.getMappedRange` for the times). Internal API: when the shape\n * isn't there, there's no tap and the caller keeps three's sum.\n */\n\n/** The members of three's `WebGPUTimestampQueryPool` the tap reads. */\ntype TimestampPool = {\n queryOffsets: Map<string, number>;\n _resolveQueries: () => Promise<unknown>;\n resultBuffer: { getMappedRange: (offset?: number, size?: number) => ArrayBuffer } | null;\n};\n\ntype PoolTap = {\n /** Busy ms of the newest frame resolved since the last take, else `null`. */\n take(): number | null;\n /** Puts the pool's own members back. */\n detach(): void;\n};\n\nconst FRAME_UID = /:f(\\d+)$/;\n\n/**\n * The newest frame's busy ms: its passes' `[begin, end]` intervals (ns),\n * merged. `null` when no pass of it has a usable pair.\n */\nconst latestFrameBusyMs = (offsets: Map<string, number>, times: BigUint64Array): number | null => {\n let frame = -1;\n const byFrame = new Map<number, [bigint, bigint][]>();\n\n for (const [uid, offset] of offsets) {\n const match = FRAME_UID.exec(uid);\n const begin = times[offset];\n const end = times[offset + 1];\n\n // An unwritten query reads 0; a pass that didn't run can't count.\n if (!match || begin === undefined || end === undefined || begin === 0n || end < begin) {\n continue;\n }\n\n const id = Number(match[1]);\n const intervals = byFrame.get(id) ?? [];\n intervals.push([begin, end]);\n byFrame.set(id, intervals);\n frame = Math.max(frame, id);\n }\n\n const intervals = byFrame.get(frame);\n\n if (!intervals) {\n return null;\n }\n\n intervals.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));\n\n let busy = 0n;\n let [start, stop] = intervals[0]!;\n\n for (const [begin, end] of intervals.slice(1)) {\n if (begin <= stop) {\n stop = end > stop ? end : stop;\n } else {\n busy += stop - start;\n start = begin;\n stop = end;\n }\n }\n\n busy += stop - start;\n\n return Number(busy) / 1e6;\n};\n\nconst isTimestampPool = (pool: unknown): pool is TimestampPool => {\n const candidate = pool as Partial<TimestampPool> | null | undefined;\n\n return (\n candidate?.queryOffsets instanceof Map &&\n typeof candidate._resolveQueries === \"function\" &&\n typeof candidate.resultBuffer?.getMappedRange === \"function\"\n );\n};\n\n/** Wraps a WebGPU render timestamp pool, or `null` when it isn't one. */\nconst tapTimestampPool = (pool: unknown): PoolTap | null => {\n if (!isTimestampPool(pool) || !pool.resultBuffer) {\n return null;\n }\n\n const buffer = pool.resultBuffer;\n const resolve = pool._resolveQueries;\n const getMappedRange = buffer.getMappedRange;\n let offsets: Map<string, number> | null = null;\n let latest: number | null = null;\n\n // three clears the offsets before it maps the buffer: copy them first. One\n // resolve is in flight at a time, so one copy is enough.\n pool._resolveQueries = function (this: TimestampPool) {\n offsets = new Map(this.queryOffsets);\n\n return resolve.call(this);\n };\n\n buffer.getMappedRange = function (this: unknown, offset?: number, size?: number) {\n const range = getMappedRange.call(this, offset, size);\n\n if (offsets) {\n try {\n latest = latestFrameBusyMs(offsets, new BigUint64Array(range)) ?? latest;\n } catch {\n // A shape this tap doesn't know: three's sum still stands.\n }\n\n offsets = null;\n }\n\n return range;\n };\n\n return {\n take() {\n const ms = latest;\n latest = null;\n\n return ms;\n },\n detach() {\n pool._resolveQueries = resolve;\n buffer.getMappedRange = getMappedRange;\n },\n };\n};\n\nexport { latestFrameBusyMs, tapTimestampPool };\nexport type { PoolTap };\n","/**\n * Real GPU frame timing.\n *\n * WebGPU: Three's `trackTimestamp` + `resolveTimestampsAsync` (ms, a frame or\n * two late), read as busy time through `gpu-busy.ts` since three's own number\n * sums overlapping passes. Never calls `getExtension`: that is what remounted\n * the canvas.\n *\n * WebGL2 fallback (Safari without WebGPU, or a forced GL backend):\n * `EXT_disjoint_timer_query_webgl2`, same pool-of-queries pattern as the\n * platform-typing monitor.\n */\nimport { type PoolTap, tapTimestampPool } from \"./gpu-busy.ts\";\nimport type { PerfRenderer } from \"./types.ts\";\n\ntype DisjointTimerQueryExt = {\n readonly GPU_DISJOINT_EXT: number;\n readonly TIME_ELAPSED_EXT: number;\n};\n\nclass GpuTimer {\n private lastMs = 0;\n private readonly webgpu: PerfRenderer | null;\n private webgpuInFlight = false;\n private tappedPool: unknown = null;\n private tap: PoolTap | null = null;\n private readonly gl: WebGL2RenderingContext | null;\n private readonly ext: DisjointTimerQueryExt | null;\n private readonly free: WebGLQuery[] = [];\n private readonly pending: WebGLQuery[] = [];\n private active: WebGLQuery | null = null;\n\n private constructor(\n webgpu: PerfRenderer | null,\n gl: WebGL2RenderingContext | null,\n ext: DisjointTimerQueryExt | null,\n poolSize: number,\n ) {\n this.webgpu = webgpu;\n this.gl = gl;\n this.ext = ext;\n\n if (!gl) {\n return;\n }\n\n for (let index = 0; index < poolSize; index += 1) {\n const query = gl.createQuery();\n\n if (query) {\n this.free.push(query);\n }\n }\n }\n\n static create(renderer: PerfRenderer, poolSize: number): GpuTimer | null {\n const backend = renderer.backend as { trackTimestamp?: boolean } | undefined;\n\n if (backend?.trackTimestamp && renderer.resolveTimestampsAsync) {\n return new GpuTimer(renderer, null, null, poolSize);\n }\n\n const context = renderer.getContext?.();\n\n if (!(context instanceof WebGL2RenderingContext)) {\n return null;\n }\n\n const ext = context.getExtension(\n \"EXT_disjoint_timer_query_webgl2\",\n ) as DisjointTimerQueryExt | null;\n\n return ext ? new GpuTimer(null, context, ext, poolSize) : null;\n }\n\n begin() {\n if (this.webgpu || !this.gl || !this.ext) {\n return;\n }\n\n const query = this.free.pop();\n\n if (!query) {\n this.active = null;\n\n return;\n }\n\n this.active = query;\n this.gl.beginQuery(this.ext.TIME_ELAPSED_EXT, query);\n }\n\n end() {\n if (this.webgpu || !this.gl || !this.ext || !this.active) {\n return;\n }\n\n this.gl.endQuery(this.ext.TIME_ELAPSED_EXT);\n this.pending.push(this.active);\n this.active = null;\n }\n\n poll() {\n if (this.webgpu) {\n this.pollWebgpu();\n\n return;\n }\n\n this.pollWebgl();\n }\n\n getMs() {\n return this.lastMs;\n }\n\n dispose() {\n if (this.gl && this.ext && this.active) {\n this.gl.endQuery(this.ext.TIME_ELAPSED_EXT);\n this.active = null;\n }\n\n if (this.gl) {\n for (const query of this.free) {\n this.gl.deleteQuery(query);\n }\n\n for (const query of this.pending) {\n this.gl.deleteQuery(query);\n }\n }\n\n this.free.length = 0;\n this.pending.length = 0;\n this.tap?.detach();\n this.tap = null;\n this.tappedPool = null;\n }\n\n private pollWebgpu() {\n const resolve = this.webgpu?.resolveTimestampsAsync;\n\n if (!resolve || this.webgpuInFlight) {\n return;\n }\n\n this.tapPool();\n this.webgpuInFlight = true;\n\n void resolve\n .call(this.webgpu, \"render\")\n .then((ms) => {\n if (this.tap) {\n // No new frame resolved (three handed back its last value): keep ours.\n this.lastMs = this.tap.take() ?? this.lastMs;\n } else if (typeof ms === \"number\" && Number.isFinite(ms)) {\n this.lastMs = ms;\n }\n\n this.webgpuInFlight = false;\n })\n .catch(() => {\n this.webgpuInFlight = false;\n });\n }\n\n /** three makes the render pool on the first timed frame, and anew after a reset. */\n private tapPool() {\n const backend = this.webgpu?.backend as\n | { timestampQueryPool?: { render?: unknown } }\n | undefined;\n const pool = backend?.timestampQueryPool?.render;\n\n if (!pool || pool === this.tappedPool) {\n return;\n }\n\n this.tap?.detach();\n this.tappedPool = pool;\n this.tap = tapTimestampPool(pool);\n }\n\n private pollWebgl() {\n if (!this.gl || !this.ext) {\n return;\n }\n\n const disjoint = this.gl.getParameter(this.ext.GPU_DISJOINT_EXT) as boolean;\n\n if (disjoint) {\n while (this.pending.length > 0) {\n this.free.push(this.pending.shift()!);\n }\n\n return;\n }\n\n while (this.pending.length > 0) {\n const query = this.pending[0]!;\n const ready = this.gl.getQueryParameter(query, this.gl.QUERY_RESULT_AVAILABLE) as boolean;\n\n if (!ready) {\n break;\n }\n\n this.pending.shift();\n const ns = this.gl.getQueryParameter(query, this.gl.QUERY_RESULT) as number;\n this.lastMs = ns / 1e6;\n this.free.push(query);\n }\n }\n}\n\nexport { GpuTimer };\n","/**\n * Fixed-capacity numeric ring buffer. Backs the per-frame timing history the\n * view reads to draw sparklines. Push is O(1); `toArray()` returns values in\n * chronological (oldest → newest) order.\n */\nclass RingBuffer {\n private readonly data: Float32Array;\n private readonly capacity: number;\n private head = 0;\n private count = 0;\n\n constructor(capacity: number) {\n this.capacity = capacity;\n this.data = new Float32Array(capacity);\n }\n\n push(value: number) {\n this.data[this.head] = value;\n this.head = (this.head + 1) % this.capacity;\n\n if (this.count < this.capacity) {\n this.count += 1;\n }\n }\n\n /** Values oldest → newest. Length grows up to capacity as samples accumulate. */\n toArray(): number[] {\n const out = Array.from<number>({ length: this.count });\n const start = (this.head - this.count + this.capacity) % this.capacity;\n\n for (let index = 0; index < this.count; index += 1) {\n out[index] = this.data[(start + index) % this.capacity]!;\n }\n\n return out;\n }\n\n clear() {\n this.head = 0;\n this.count = 0;\n }\n}\n\nexport { RingBuffer };\n","/**\n * Where a frame's draw calls and triangles come from, estimated from the\n * scene graph the way three's renderer walks it: hidden objects, other\n * layers and anything outside the camera's frustum are left out; each\n * visible material (or material group) is one draw call, two for a\n * transparent double-sided material. Main pass only: shadow maps and\n * post-processing passes aren't counted.\n *\n * Structural like the rest of core: no import of three, every field read\n * defensively, since this runs against whatever the app renders.\n */\nimport type { CostEntry, SceneCost } from \"./types.ts\";\n\ntype Elements = ArrayLike<number>;\n\ntype Sphere = { center: { x: number; y: number; z: number }; radius: number };\n\ntype GeometryShape = {\n attributes?: { position?: { count: number } };\n boundingSphere?: Sphere | null;\n drawRange?: { count: number; start: number };\n groups?: { count: number; materialIndex?: number; start: number }[];\n index?: { count: number } | null;\n name?: string;\n uuid?: string;\n};\n\ntype MaterialShape = {\n uuid?: string;\n forceSinglePass?: boolean;\n name?: string;\n side?: number;\n transparent?: boolean;\n type?: string;\n visible?: boolean;\n};\n\ntype ObjectShape = {\n boundingSphere?: Sphere | null;\n count?: number;\n frustumCulled?: boolean;\n geometry?: GeometryShape;\n isInstancedMesh?: boolean;\n isLine?: boolean;\n isMesh?: boolean;\n isPoints?: boolean;\n isSprite?: boolean;\n layers?: { mask: number };\n material?: MaterialShape | MaterialShape[];\n matrixWorld?: { elements: Elements };\n name?: string;\n type?: string;\n};\n\ntype CameraShape = {\n coordinateSystem?: number;\n layers?: { mask: number };\n matrixWorldInverse?: { elements: Elements };\n projectionMatrix?: { elements: Elements };\n reversedDepth?: boolean;\n};\n\ntype SceneShape = { traverseVisible?: (callback: (object: ObjectShape) => void) => void };\n\n/** three's `DoubleSide` and `WebGPUCoordinateSystem`. */\nconst DOUBLE_SIDE = 2;\nconst WEBGPU_COORDINATES = 2001;\n\ntype Plane = [number, number, number, number];\n\n/** Column-major `a × b`, as three stores matrices. */\nconst multiply = (a: Elements, b: Elements) => {\n const out = Array.from<number>({ length: 16 });\n\n for (let column = 0; column < 4; column += 1) {\n for (let row = 0; row < 4; row += 1) {\n let sum = 0;\n\n for (let k = 0; k < 4; k += 1) {\n sum += a[k * 4 + row]! * b[column * 4 + k]!;\n }\n\n out[column * 4 + row] = sum;\n }\n }\n\n return out;\n};\n\nconst normalize = (plane: Plane): Plane => {\n const length = Math.hypot(plane[0], plane[1], plane[2]) || 1;\n\n return [plane[0] / length, plane[1] / length, plane[2] / length, plane[3] / length];\n};\n\n/** Mirrors three's `Frustum.setFromProjectionMatrix`. Near and far only where the depth range is known. */\nconst frustumPlanes = (camera: CameraShape): Plane[] | null => {\n if (!camera.projectionMatrix || !camera.matrixWorldInverse) {\n return null;\n }\n\n const m = multiply(camera.projectionMatrix.elements, camera.matrixWorldInverse.elements);\n const row = (index: number): Plane => [m[index]!, m[index + 4]!, m[index + 8]!, m[index + 12]!];\n const [r0, r1, r2, r3] = [row(0), row(1), row(2), row(3)];\n const add = (a: Plane, b: Plane): Plane => [a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]];\n const sub = (a: Plane, b: Plane): Plane => [a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]];\n\n const planes = [sub(r3, r0), add(r3, r0), add(r3, r1), sub(r3, r1)];\n\n if (!camera.reversedDepth) {\n planes.push(sub(r3, r2), camera.coordinateSystem === WEBGPU_COORDINATES ? r2 : add(r3, r2));\n }\n\n return planes.map(normalize);\n};\n\nconst inFrustum = (object: ObjectShape, planes: Plane[] | null) => {\n const sphere = object.boundingSphere ?? object.geometry?.boundingSphere;\n const e = object.matrixWorld?.elements;\n\n // three computes bounding spheres lazily while rendering; without one, count the object.\n if (!planes || !sphere || !e) {\n return true;\n }\n\n const { x, y, z } = sphere.center;\n const cx = e[0]! * x + e[4]! * y + e[8]! * z + e[12]!;\n const cy = e[1]! * x + e[5]! * y + e[9]! * z + e[13]!;\n const cz = e[2]! * x + e[6]! * y + e[10]! * z + e[14]!;\n const scale = Math.sqrt(\n Math.max(\n e[0]! ** 2 + e[1]! ** 2 + e[2]! ** 2,\n e[4]! ** 2 + e[5]! ** 2 + e[6]! ** 2,\n e[8]! ** 2 + e[9]! ** 2 + e[10]! ** 2,\n ),\n );\n const radius = sphere.radius * scale;\n\n // three's own test, `distance < -radius`. Written this way round a NaN plane (a camera with a\n // zero-size aspect, say) keeps the object, as three does, rather than hiding everything.\n return !planes.some(([a, b, c, d]) => a * cx + b * cy + c * cz + d < -radius);\n};\n\nconst layersMatch = (object: ObjectShape, camera: CameraShape) =>\n !object.layers || !camera.layers || (object.layers.mask & camera.layers.mask) !== 0;\n\nconst passes = (material: MaterialShape) =>\n material.transparent === true &&\n material.side === DOUBLE_SIDE &&\n material.forceSinglePass !== true\n ? 2\n : 1;\n\n/** Vertices a draw covers, clipped to `drawRange` and, when given, a group. */\nconst drawnVertices = (\n geometry: GeometryShape,\n group?: { count: number; start: number },\n): number => {\n const total = geometry.index?.count ?? geometry.attributes?.position?.count ?? 0;\n const rangeStart = geometry.drawRange?.start ?? 0;\n const rangeEnd = Math.min(total, rangeStart + (geometry.drawRange?.count ?? Infinity));\n const start = Math.max(rangeStart, group?.start ?? 0);\n const end = Math.min(rangeEnd, group ? group.start + group.count : Infinity);\n\n return Math.max(0, end - start);\n};\n\ntype Draw = { calls: number; material: MaterialShape; triangles: number };\n\n/** One draw per visible material, or per group when the material is an array. */\nconst drawsOf = (object: ObjectShape): Draw[] => {\n const { geometry, material } = object;\n\n if (!geometry || !material) {\n return [];\n }\n\n const instances = object.isInstancedMesh ? (object.count ?? 1) : 1;\n const trianglesFor = (vertices: number) =>\n object.isMesh ? Math.floor(vertices / 3) * instances : 0;\n\n if (Array.isArray(material)) {\n const draws: Draw[] = [];\n\n for (const group of geometry.groups ?? []) {\n const groupMaterial = material[group.materialIndex ?? 0];\n\n if (groupMaterial && groupMaterial.visible !== false) {\n const count = passes(groupMaterial);\n draws.push({\n calls: count,\n material: groupMaterial,\n triangles: trianglesFor(drawnVertices(geometry, group)) * count,\n });\n }\n }\n\n return draws;\n }\n\n return material.visible === false\n ? []\n : [\n {\n calls: passes(material),\n material,\n // A second pass draws the triangles again, and three's info counts them again.\n triangles: trianglesFor(drawnVertices(geometry)) * passes(material),\n },\n ];\n};\n\nconst objectLabel = (object: ObjectShape) =>\n object.name || object.geometry?.name || object.type || \"Object\";\n\nconst materialLabel = (material: MaterialShape) => material.name || material.type || \"Material\";\n\n/** A stable id for objects without a `uuid`, so keys survive between walks. */\nconst ids = new WeakMap<object, string>();\nlet nextId = 0;\n\nconst idOf = (value: object & { uuid?: string }) => {\n if (value.uuid) {\n return value.uuid;\n }\n\n let id = ids.get(value);\n\n if (!id) {\n nextId += 1;\n id = `#${nextId}`;\n ids.set(value, id);\n }\n\n return id;\n};\n\nconst entryFor = (groups: Map<unknown, CostEntry>, group: unknown, key: string, label: string) => {\n let entry = groups.get(group);\n\n if (!entry) {\n entry = { calls: 0, instances: 0, key, label, objects: [], triangles: 0 };\n groups.set(group, entry);\n }\n\n return entry;\n};\n\nconst byCost = (a: CostEntry, b: CostEntry) => b.calls - a.calls || b.triangles - a.triangles;\n\n/** Same label and geometry is one mesh row. */\nconst meshKeyOf = (object: ObjectShape) =>\n `mesh:${objectLabel(object)}\\u0000${object.geometry?.uuid ?? \"\"}`;\n\nconst materialsOf = (object: ObjectShape) =>\n object.material === undefined\n ? []\n : Array.isArray(object.material)\n ? object.material\n : [object.material];\n\n/**\n * Every drawable object in the scene that belongs to a row's `key`, on screen\n * or not: hiding a row has to catch the copies that orbit into view later.\n * Skips objects that are already hidden, like three does.\n */\nconst objectsForKey = (scene: unknown, key: string): unknown[] => {\n const root = scene as SceneShape;\n const found: unknown[] = [];\n\n if (typeof root?.traverseVisible !== \"function\") {\n return found;\n }\n\n root.traverseVisible((object) => {\n const drawable = object.isMesh || object.isLine || object.isPoints || object.isSprite;\n\n if (!drawable) {\n return;\n }\n\n const matches = key.startsWith(\"mesh:\")\n ? meshKeyOf(object) === key\n : materialsOf(object).some((material) => `material:${idOf(material)}` === key);\n\n if (matches) {\n found.push(object);\n }\n });\n\n return found;\n};\n\nconst computeSceneCost = (scene: unknown, camera: unknown): SceneCost | null => {\n const root = scene as SceneShape;\n const view = camera as CameraShape;\n\n if (typeof root?.traverseVisible !== \"function\") {\n return null;\n }\n\n const planes = frustumPlanes(view);\n // Same label and geometry is one row, so 500 unmerged copies read as \"tree ×500\".\n const meshes = new Map<unknown, CostEntry>();\n const materials = new Map<unknown, CostEntry>();\n let calls = 0;\n let triangles = 0;\n\n root.traverseVisible((object) => {\n const drawable = object.isMesh || object.isLine || object.isPoints || object.isSprite;\n\n if (!drawable || !layersMatch(object, view)) {\n return;\n }\n\n if (object.frustumCulled !== false && !object.isSprite && !inFrustum(object, planes)) {\n return;\n }\n\n const instances = object.isInstancedMesh ? (object.count ?? 1) : 1;\n const label = objectLabel(object);\n const meshKey = meshKeyOf(object);\n\n const draws = drawsOf(object);\n\n if (draws.length === 0) {\n return;\n }\n\n const mesh = entryFor(meshes, meshKey, meshKey, label);\n mesh.objects.push(object);\n mesh.instances += instances;\n // A multi-material object can draw one material in several groups; list it once per material.\n const listed = new Set<MaterialShape>();\n\n for (const draw of draws) {\n calls += draw.calls;\n triangles += draw.triangles;\n mesh.calls += draw.calls;\n mesh.triangles += draw.triangles;\n\n const material = entryFor(\n materials,\n draw.material,\n `material:${idOf(draw.material)}`,\n materialLabel(draw.material),\n );\n material.calls += draw.calls;\n material.triangles += draw.triangles;\n\n if (!listed.has(draw.material)) {\n listed.add(draw.material);\n material.objects.push(object);\n material.instances += instances;\n }\n }\n });\n\n return {\n calls,\n materials: [...materials.values()].sort(byCost),\n meshes: [...meshes.values()].sort(byCost),\n triangles,\n };\n};\n\nexport { computeSceneCost, objectsForKey };\n","import { readEnvironment } from \"./environment.ts\";\nimport { computeFrameStats } from \"./frame-stats.ts\";\nimport { GpuTimer } from \"./gpu-timer.ts\";\nimport { RingBuffer } from \"./ring-buffer.ts\";\nimport { computeSceneCost, objectsForKey } from \"./scene-cost.ts\";\nimport type {\n CostEntry,\n Environment,\n FrameworkVersions,\n FrameStats,\n PerfRenderer,\n PerformanceMonitorOptions,\n Sample,\n SceneCost,\n TimingMetric,\n} from \"./types.ts\";\n\nconst DEFAULT_GPU_POOL_SIZE = 5;\n/** One monitor per renderer: a second would stack the `render` patch and double-count passes. */\nconst ATTACHED = new WeakSet<PerfRenderer>();\nconst DEFAULT_HISTORY_SIZE = 120;\nconst DEFAULT_FRAME_STATS_SIZE = 1000;\nconst FPS_SMOOTHING = 0.1;\n/**\n * A frame interval past this is a stall (hidden tab, breakpoint, laptop lid),\n * not a slow frame. It is left out of FPS so one gap doesn't flatten the\n * graph's scale for the whole history window.\n */\nconst STALL_MS = 1000;\n\nconst frameCalls = (info: PerfRenderer[\"info\"]) => info.render.drawCalls ?? info.render.calls;\n\nconst programCount = (info: PerfRenderer[\"info\"]) =>\n info.memory.programs ?? info.programs?.length ?? 0;\n\n/**\n * Collector of three.js frame metrics. Bracket each frame with {@link begin}/\n * {@link end}, or let {@link wrapAnimationLoop} or the R3F `PerfSampler` do it.\n * Works on WebGPURenderer (drawCalls, memory.programs, timestamp queries) and\n * on a WebGL renderer (calls, programs[], timer-query ext).\n *\n * While attached, `renderer.info.autoReset` is off and `info` is reset in\n * {@link begin}; host code that reads `renderer.info` mid-frame sees this\n * frame's counts only. {@link dispose} restores both.\n *\n * CPU time runs from {@link begin} to the return of the frame's last\n * `render()` call, which the `render` patch stamps. So it holds even when\n * {@link end} is only reached on the next tick, as in the R3F sampler: it is\n * the JS work of the frame, not the interval to the next one.\n */\nclass PerformanceMonitor {\n private readonly renderer: PerfRenderer;\n private readonly gpuTimer: GpuTimer | null;\n private readonly originalRender: PerfRenderer[\"render\"];\n private renderPasses = 0;\n private lastRenderEndAt: number | null = null;\n private readonly fpsHistory: RingBuffer;\n private readonly cpuHistory: RingBuffer;\n private readonly gpuHistory: RingBuffer;\n private readonly frameIntervals: RingBuffer;\n private frameStats: FrameStats | null = null;\n /** The render call with the most draw calls this frame, and the scene and camera it drew. */\n private mainPassCalls = -1;\n private mainPass: { camera: WeakRef<object>; scene: WeakRef<object> } | null = null;\n private cpuStart = 0;\n private lastBeginAt: number | null = null;\n private frameFps = 0;\n private smoothedFps = 0;\n private sample: Sample;\n private environment: Environment | null = null;\n private readonly frameworks: FrameworkVersions | undefined;\n\n constructor(options: PerformanceMonitorOptions) {\n const {\n renderer,\n trackGPU = true,\n gpuQueryPoolSize = DEFAULT_GPU_POOL_SIZE,\n historySize = DEFAULT_HISTORY_SIZE,\n frameStatsSize = DEFAULT_FRAME_STATS_SIZE,\n } = options;\n\n this.frameworks = options.frameworks;\n\n if (ATTACHED.has(renderer)) {\n throw new Error(\n \"three-meter: a PerformanceMonitor is already attached to this renderer. dispose() it first.\",\n );\n }\n\n ATTACHED.add(renderer);\n this.renderer = renderer;\n renderer.info.autoReset = false;\n this.originalRender = renderer.render;\n\n renderer.render = (...args: never[]) => {\n this.renderPasses += 1;\n const callsBefore = frameCalls(renderer.info);\n\n try {\n return this.originalRender.apply(renderer, args);\n } finally {\n this.lastRenderEndAt = performance.now();\n this.notePass(args[0], args[1], frameCalls(renderer.info) - callsBefore);\n }\n };\n\n this.gpuTimer = trackGPU ? GpuTimer.create(renderer, gpuQueryPoolSize) : null;\n this.fpsHistory = new RingBuffer(historySize);\n this.cpuHistory = new RingBuffer(historySize);\n this.gpuHistory = new RingBuffer(historySize);\n this.frameIntervals = new RingBuffer(frameStatsSize);\n this.sample = this.buildEmptySample();\n }\n\n begin() {\n const now = performance.now();\n\n if (this.lastBeginAt !== null) {\n const frameMs = now - this.lastBeginAt;\n\n // A stall keeps the previous reading rather than logging a near-zero frame.\n if (frameMs > 0 && frameMs <= STALL_MS) {\n this.frameFps = 1000 / frameMs;\n this.frameIntervals.push(frameMs);\n this.frameStats = null;\n }\n }\n\n this.lastBeginAt = now;\n this.cpuStart = now;\n this.lastRenderEndAt = null;\n this.renderPasses = 0;\n this.mainPassCalls = -1;\n this.renderer.info.reset();\n this.gpuTimer?.begin();\n }\n\n end() {\n const now = performance.now();\n // Without a render this frame there is nothing better than \"until end()\".\n const cpu = (this.lastRenderEndAt ?? now) - this.cpuStart;\n\n this.gpuTimer?.end();\n this.gpuTimer?.poll();\n\n const gpuMs = this.gpuTimer?.getMs() ?? 0;\n\n this.smoothedFps =\n this.smoothedFps === 0\n ? this.frameFps\n : this.smoothedFps + (this.frameFps - this.smoothedFps) * FPS_SMOOTHING;\n\n this.fpsHistory.push(this.frameFps);\n this.cpuHistory.push(cpu);\n this.gpuHistory.push(gpuMs);\n\n const info = this.renderer.info;\n\n this.sample = Object.freeze({\n cpu,\n fps: this.smoothedFps,\n gpu: { available: this.gpuTimer !== null, ms: gpuMs },\n render: {\n calls: frameCalls(info),\n lines: info.render.lines,\n passes: this.renderPasses,\n points: info.render.points,\n triangles: info.render.triangles,\n },\n resources: {\n geometries: info.memory.geometries,\n programs: programCount(info),\n textures: info.memory.textures,\n },\n });\n }\n\n getSample(): Sample {\n return this.sample;\n }\n\n getHistory(metric: TimingMetric): readonly number[] {\n switch (metric) {\n case \"fps\":\n return this.fpsHistory.toArray();\n case \"cpu\":\n return this.cpuHistory.toArray();\n case \"gpu\":\n return this.gpuHistory.toArray();\n }\n }\n\n /** 1% low, p99 frame time and hitches. Computed on read, cached until the next frame. */\n getFrameStats(): FrameStats {\n this.frameStats ??= computeFrameStats(this.frameIntervals.toArray());\n\n return this.frameStats;\n }\n\n /**\n * Where the main pass's draw calls and triangles come from, by mesh and by\n * material. Walks the scene graph, so read it on demand (the HUD does, at\n * 2 Hz while its section is open), not every frame. `null` before the first\n * render, or once the scene is garbage collected.\n */\n getSceneCost(): SceneCost | null {\n const scene = this.mainPass?.scene.deref();\n const camera = this.mainPass?.camera.deref();\n\n return scene && camera ? computeSceneCost(scene, camera) : null;\n }\n\n /**\n * Every visible object in the main scene behind a {@link CostEntry}'s `key`,\n * including ones outside the camera, which `getSceneCost()` leaves out.\n */\n getObjectsForKey(key: string): unknown[] {\n const scene = this.mainPass?.scene.deref();\n\n return scene ? objectsForKey(scene, key) : [];\n }\n\n /**\n * three revision, React and React Three Fiber versions, backend and GPU name. Cached once the backend is known;\n * before `WebGPURenderer.init()` settles it, `backend` is `null` and the\n * next call reads again.\n */\n getEnvironment(): Environment {\n if (this.environment) {\n return this.environment;\n }\n\n const environment = Object.freeze(readEnvironment(this.renderer, this.frameworks));\n\n if (environment.backend !== null) {\n this.environment = environment;\n }\n\n return environment;\n }\n\n dispose() {\n ATTACHED.delete(this.renderer);\n this.renderer.info.autoReset = true;\n this.renderer.render = this.originalRender;\n this.gpuTimer?.dispose();\n this.fpsHistory.clear();\n this.cpuHistory.clear();\n this.gpuHistory.clear();\n this.frameIntervals.clear();\n this.frameStats = null;\n this.mainPass = null;\n }\n\n /**\n * The main pass is the render call that drew the most, so a post-processing\n * quad rendered last doesn't stand in for the scene. Weak references: the\n * monitor never keeps a scene alive.\n */\n private notePass(scene: unknown, camera: unknown, calls: number) {\n if (\n calls > this.mainPassCalls &&\n typeof scene === \"object\" &&\n scene !== null &&\n typeof camera === \"object\" &&\n camera !== null\n ) {\n this.mainPassCalls = calls;\n this.mainPass = { camera: new WeakRef(camera), scene: new WeakRef(scene) };\n }\n }\n\n private buildEmptySample(): Sample {\n return Object.freeze({\n cpu: 0,\n fps: 0,\n gpu: { available: this.gpuTimer !== null, ms: 0 },\n render: { calls: 0, lines: 0, passes: 0, points: 0, triangles: 0 },\n resources: { geometries: 0, programs: 0, textures: 0 },\n });\n }\n}\n\nexport { PerformanceMonitor };\n"],"mappings":"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"}
@@ -92,6 +92,19 @@ type Environment = {
92
92
  gpu: string | null;
93
93
  /** three's revision from `window.__THREE__`, e.g. `"186"`. */
94
94
  three: string | null;
95
+ /** React's version, when the app says or React DevTools can tell. See {@link FrameworkVersions}. */
96
+ react: string | null;
97
+ /** React Three Fiber's version, likewise. */
98
+ r3f: string | null;
99
+ };
100
+ /**
101
+ * React and React Three Fiber versions, for {@link Environment}. The core imports neither, so these
102
+ * come from the app: `frameworkVersions` in `@zkmake/three-meter/react`, which `PerfSampler` and
103
+ * three-devtools' `<DevTools>` pass for you.
104
+ */
105
+ type FrameworkVersions = {
106
+ react?: string | null;
107
+ r3f?: string | null;
95
108
  };
96
109
  /**
97
110
  * The slice of a three renderer the monitor needs. WebGPU's `info.render` uses
@@ -136,6 +149,8 @@ type PerformanceMonitorOptions = {
136
149
  historySize?: number;
137
150
  /** Frame intervals behind {@link FrameStats}. Default 1000. */
138
151
  frameStatsSize?: number;
152
+ /** What the app runs on besides three. Default: whatever React DevTools' hook can tell. */
153
+ frameworks?: FrameworkVersions;
139
154
  };
140
155
  /**
141
156
  * Collector of three.js frame metrics. Bracket each frame with {@link begin}/
@@ -172,6 +187,7 @@ declare class PerformanceMonitor {
172
187
  private smoothedFps;
173
188
  private sample;
174
189
  private environment;
190
+ private readonly frameworks;
175
191
  constructor(options: PerformanceMonitorOptions);
176
192
  begin(): void;
177
193
  end(): void;
@@ -192,7 +208,7 @@ declare class PerformanceMonitor {
192
208
  */
193
209
  getObjectsForKey(key: string): unknown[];
194
210
  /**
195
- * three revision, backend and GPU name. Cached once the backend is known;
211
+ * three revision, React and React Three Fiber versions, backend and GPU name. Cached once the backend is known;
196
212
  * before `WebGPURenderer.init()` settles it, `backend` is `null` and the
197
213
  * next call reads again.
198
214
  */
@@ -206,5 +222,5 @@ declare class PerformanceMonitor {
206
222
  private notePass;
207
223
  private buildEmptySample;
208
224
  }
209
- export { GpuTiming as a, RenderBackend as c, Sample as d, SceneCost as f, FrameStats as i, RenderCounts as l, CostEntry as n, PerfRenderer as o, TimingMetric as p, Environment as r, PerformanceMonitorOptions as s, PerformanceMonitor as t, ResourceCounts as u };
210
- //# sourceMappingURL=performance-monitor-CrYmNIdv.d.ts.map
225
+ export { FrameworkVersions as a, PerformanceMonitorOptions as c, ResourceCounts as d, Sample as f, FrameStats as i, RenderBackend as l, TimingMetric as m, CostEntry as n, GpuTiming as o, SceneCost as p, Environment as r, PerfRenderer as s, PerformanceMonitor as t, RenderCounts as u };
226
+ //# sourceMappingURL=performance-monitor-CH3GOcIm.d.ts.map
package/dist/react.d.ts CHANGED
@@ -1,5 +1,10 @@
1
- import { s as PerformanceMonitorOptions, t as PerformanceMonitor } from "./performance-monitor-CrYmNIdv.js";
2
- import { t as MountPerfHudOptions } from "./mount-perf-hud-C0yuW5JI.js";
1
+ import { a as FrameworkVersions, c as PerformanceMonitorOptions, t as PerformanceMonitor } from "./performance-monitor-CH3GOcIm.js";
2
+ import { t as MountPerfHudOptions } from "./mount-perf-hud-Be8DNtkg.js";
3
+ /**
4
+ * The React and React Three Fiber this app runs: the HUD's footer and report list them beside
5
+ * three. R3F exports no version, so it comes from its package.json, which every release allows.
6
+ */
7
+ declare const frameworkVersions: FrameworkVersions;
3
8
  type MonitorListener = (monitor: PerformanceMonitor | null) => void;
4
9
  type PerfMonitorStore = {
5
10
  get: () => PerformanceMonitor | null;
@@ -24,7 +29,7 @@ type PerfHudProps = Omit<MountPerfHudOptions, "settings"> & {
24
29
  * `store`) remount the HUD when they change.
25
30
  */
26
31
  declare function PerfHud({ budgets, defaultPlacement, injectStyles, label, mode, parent, quality, refreshHz, storageKey, store, theme }: PerfHudProps): null;
27
- type PerfSamplerProps = Omit<PerformanceMonitorOptions, "renderer"> & {
32
+ type PerfSamplerProps = Omit<PerformanceMonitorOptions, "renderer" | "frameworks"> & {
28
33
  /** Where the monitor is published for `PerfHud`. Default: the shared store. */
29
34
  store?: PerfMonitorStore;
30
35
  };
@@ -35,5 +40,5 @@ type PerfSamplerProps = Omit<PerformanceMonitorOptions, "renderer"> & {
35
40
  * an effect so StrictMode's double mount attaches and detaches cleanly.
36
41
  */
37
42
  declare function PerfSampler({ frameStatsSize, gpuQueryPoolSize, historySize, store, trackGPU }: PerfSamplerProps): null;
38
- export { PerfHud, type PerfHudProps, type PerfMonitorStore, PerfSampler, type PerfSamplerProps, createPerfMonitorStore, defaultPerfMonitorStore };
43
+ export { PerfHud, type PerfHudProps, type PerfMonitorStore, PerfSampler, type PerfSamplerProps, createPerfMonitorStore, defaultPerfMonitorStore, frameworkVersions };
39
44
  //# sourceMappingURL=react.d.ts.map
package/dist/react.js CHANGED
@@ -1,7 +1,17 @@
1
- import { t as PerformanceMonitor } from "./performance-monitor-Dj5H2VXT.js";
2
- import { t as mountPerfHud } from "./mount-perf-hud-BRihOM2d.js";
3
- import { useEffect, useRef } from "react";
1
+ import { t as PerformanceMonitor } from "./performance-monitor-Bz3ukv7t.js";
2
+ import { t as mountPerfHud } from "./mount-perf-hud-BtucU7nZ.js";
3
+ import fiber from "@react-three/fiber/package.json" with { type: "json" };
4
+ import { useEffect, useRef, version } from "react";
4
5
  import { useFrame, useThree } from "@react-three/fiber";
6
+ // src/react/framework-versions.ts
7
+ /**
8
+ * The React and React Three Fiber this app runs: the HUD's footer and report list them beside
9
+ * three. R3F exports no version, so it comes from its package.json, which every release allows.
10
+ */
11
+ const frameworkVersions = {
12
+ react: version,
13
+ r3f: fiber.version
14
+ };
5
15
  // src/react/monitor-store.ts
6
16
  const createPerfMonitorStore = () => {
7
17
  let current = null;
@@ -119,6 +129,7 @@ function PerfSampler({ frameStatsSize, gpuQueryPoolSize, historySize, store, tra
119
129
  useEffect(() => {
120
130
  const monitor = new PerformanceMonitor({
121
131
  frameStatsSize,
132
+ frameworks: frameworkVersions,
122
133
  gpuQueryPoolSize,
123
134
  historySize,
124
135
  renderer,
@@ -149,6 +160,6 @@ function PerfSampler({ frameStatsSize, gpuQueryPoolSize, historySize, store, tra
149
160
  });
150
161
  return null;
151
162
  }
152
- export { PerfHud, PerfSampler, createPerfMonitorStore, defaultPerfMonitorStore };
163
+ export { PerfHud, PerfSampler, createPerfMonitorStore, defaultPerfMonitorStore, frameworkVersions };
153
164
 
154
165
  //# sourceMappingURL=react.js.map
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","names":[],"sources":["../src/react/monitor-store.ts","../src/react/perf-hud.tsx","../src/react/perf-sampler.tsx"],"sourcesContent":["/**\n * Hands the in-canvas monitor to the DOM-side HUD. A React context can't do\n * it: R3F's `<Canvas>` is its own reconciler root, so a provider outside it\n * isn't visible inside without a bridge. A tiny external store crosses that\n * boundary for free. One default store; pass your own to both components to\n * run two canvases on one page.\n */\nimport type { PerformanceMonitor } from \"../core/performance-monitor.ts\";\n\ntype MonitorListener = (monitor: PerformanceMonitor | null) => void;\n\ntype PerfMonitorStore = {\n get: () => PerformanceMonitor | null;\n set: (monitor: PerformanceMonitor | null) => void;\n subscribe: (listener: MonitorListener) => () => void;\n};\n\nconst createPerfMonitorStore = (): PerfMonitorStore => {\n let current: PerformanceMonitor | null = null;\n const listeners = new Set<MonitorListener>();\n\n return {\n get: () => current,\n set: (monitor) => {\n current = monitor;\n\n for (const listener of listeners) {\n listener(monitor);\n }\n },\n subscribe: (listener) => {\n listeners.add(listener);\n\n return () => {\n listeners.delete(listener);\n };\n },\n };\n};\n\nconst defaultPerfMonitorStore = createPerfMonitorStore();\n\nexport { createPerfMonitorStore, defaultPerfMonitorStore };\nexport type { PerfMonitorStore };\n","import { useEffect, useRef } from \"react\";\n\nimport type { BudgetsInput } from \"../ui/budgets.ts\";\nimport {\n mountPerfHud,\n type MountPerfHudOptions,\n type PerfHudHandle,\n} from \"../ui/mount-perf-hud.ts\";\nimport { defaultPerfMonitorStore, type PerfMonitorStore } from \"./monitor-store.ts\";\n\ntype PerfHudProps = Omit<MountPerfHudOptions, \"settings\"> & {\n /** Must match the `PerfSampler`'s. Default: the shared store. */\n store?: PerfMonitorStore;\n};\n\n/**\n * DOM overlay. Render it *outside* `<Canvas>`, or Fiber would treat its markup\n * as three objects. Mounts the dockable HUD whenever a `PerfSampler` has\n * published a monitor and tears it down when that sampler unmounts.\n *\n * `theme`, `mode`, `label`, `budgets` and `quality` apply live through the handle.\n * `defaultPlacement` and `budgets` are compared by value, so inline literals\n * like `{{ edge: \"right\" }}` are fine.\n * The remaining props (`storageKey`, `parent`, `refreshHz`, `injectStyles`,\n * `store`) remount the HUD when they change.\n */\nfunction PerfHud({\n budgets,\n defaultPlacement,\n injectStyles,\n label,\n mode,\n parent,\n quality,\n refreshHz,\n storageKey,\n store,\n theme,\n}: PerfHudProps) {\n const source = store ?? defaultPerfMonitorStore;\n const handleRef = useRef<PerfHudHandle | null>(null);\n // The mount effect reads these through refs so changing them never remounts;\n // the live effects below keep the refs current and push changes to the handle.\n const themeRef = useRef(theme);\n const modeRef = useRef(mode);\n const labelRef = useRef(label);\n const budgetsRef = useRef(budgets);\n const qualityRef = useRef(quality);\n // By value: an inline `{{ calls: 500 }}` is a new object every render.\n const budgetsKey = JSON.stringify(budgets ?? null);\n // Primitives, so an inline `{{ edge: \"right\" }}` literal doesn't remount each render.\n const placementEdge = defaultPlacement?.edge;\n const placementAlign = defaultPlacement?.align;\n\n useEffect(() => {\n const attach = (monitor: ReturnType<PerfMonitorStore[\"get\"]>) => {\n handleRef.current?.dispose();\n handleRef.current = null;\n\n if (!monitor) {\n return;\n }\n\n handleRef.current = mountPerfHud(monitor, {\n budgets: budgetsRef.current,\n defaultPlacement: placementEdge\n ? { align: placementAlign, edge: placementEdge }\n : undefined,\n injectStyles,\n label: labelRef.current,\n mode: modeRef.current,\n parent,\n quality: qualityRef.current,\n refreshHz,\n storageKey,\n theme: themeRef.current,\n });\n };\n\n const unsubscribe = source.subscribe(attach);\n attach(source.get());\n\n return () => {\n unsubscribe();\n handleRef.current?.dispose();\n handleRef.current = null;\n };\n }, [injectStyles, parent, placementAlign, placementEdge, refreshHz, source, storageKey]);\n\n useEffect(() => {\n themeRef.current = theme;\n handleRef.current?.setTheme(theme ?? \"system\");\n }, [theme]);\n\n useEffect(() => {\n modeRef.current = mode;\n handleRef.current?.setMode(mode ?? \"compact\");\n }, [mode]);\n\n useEffect(() => {\n // Read back from the key so the effect depends on the value, not the object.\n const next = (JSON.parse(budgetsKey) as BudgetsInput | false | null) ?? undefined;\n budgetsRef.current = next;\n handleRef.current?.setBudgets(next);\n }, [budgetsKey]);\n\n useEffect(() => {\n qualityRef.current = quality;\n handleRef.current?.setQuality(quality);\n }, [quality]);\n\n useEffect(() => {\n labelRef.current = label;\n handleRef.current?.element.setAttribute(\"aria-label\", label ?? \"Performance\");\n }, [label]);\n\n return null;\n}\n\nexport { PerfHud };\nexport type { PerfHudProps };\n","import { useFrame, useThree } from \"@react-three/fiber\";\nimport { useEffect, useRef } from \"react\";\n\nimport { PerformanceMonitor } from \"../core/performance-monitor.ts\";\nimport type { PerformanceMonitorOptions, PerfRenderer } from \"../core/types.ts\";\nimport { defaultPerfMonitorStore, type PerfMonitorStore } from \"./monitor-store.ts\";\n\ntype PerfSamplerProps = Omit<PerformanceMonitorOptions, \"renderer\"> & {\n /** Where the monitor is published for `PerfHud`. Default: the shared store. */\n store?: PerfMonitorStore;\n};\n\n/**\n * In-canvas collector. Renders nothing. Fiber runs every `useFrame` before\n * `gl.render`, so the previous frame is closed here and the next one opens, and\n * GPU queries then span the render that just ran. The monitor is created in\n * an effect so StrictMode's double mount attaches and detaches cleanly.\n */\nfunction PerfSampler({\n frameStatsSize,\n gpuQueryPoolSize,\n historySize,\n store,\n trackGPU,\n}: PerfSamplerProps) {\n const renderer = useThree((state) => state.gl);\n const monitorRef = useRef<PerformanceMonitor | null>(null);\n const startedRef = useRef(false);\n const target = store ?? defaultPerfMonitorStore;\n\n useEffect(() => {\n const monitor = new PerformanceMonitor({\n frameStatsSize,\n gpuQueryPoolSize,\n historySize,\n renderer: renderer as unknown as PerfRenderer,\n trackGPU,\n });\n monitorRef.current = monitor;\n startedRef.current = false;\n target.set(monitor);\n\n return () => {\n target.set(null);\n monitorRef.current = null;\n monitor.dispose();\n };\n }, [frameStatsSize, gpuQueryPoolSize, historySize, renderer, target, trackGPU]);\n\n useFrame(() => {\n const monitor = monitorRef.current;\n\n if (!monitor) {\n return;\n }\n\n if (startedRef.current) {\n monitor.end();\n }\n\n startedRef.current = true;\n monitor.begin();\n });\n\n return null;\n}\n\nexport { PerfSampler };\nexport type { PerfSamplerProps };\n"],"mappings":"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"}
1
+ {"version":3,"file":"react.js","names":[],"sources":["../src/react/framework-versions.ts","../src/react/monitor-store.ts","../src/react/perf-hud.tsx","../src/react/perf-sampler.tsx"],"sourcesContent":["import fiber from \"@react-three/fiber/package.json\" with { type: \"json\" };\nimport { version as react } from \"react\";\n\nimport type { FrameworkVersions } from \"../core/types.ts\";\n\n/**\n * The React and React Three Fiber this app runs: the HUD's footer and report list them beside\n * three. R3F exports no version, so it comes from its package.json, which every release allows.\n */\nconst frameworkVersions: FrameworkVersions = { react, r3f: fiber.version };\n\nexport { frameworkVersions };\n","/**\n * Hands the in-canvas monitor to the DOM-side HUD. A React context can't do\n * it: R3F's `<Canvas>` is its own reconciler root, so a provider outside it\n * isn't visible inside without a bridge. A tiny external store crosses that\n * boundary for free. One default store; pass your own to both components to\n * run two canvases on one page.\n */\nimport type { PerformanceMonitor } from \"../core/performance-monitor.ts\";\n\ntype MonitorListener = (monitor: PerformanceMonitor | null) => void;\n\ntype PerfMonitorStore = {\n get: () => PerformanceMonitor | null;\n set: (monitor: PerformanceMonitor | null) => void;\n subscribe: (listener: MonitorListener) => () => void;\n};\n\nconst createPerfMonitorStore = (): PerfMonitorStore => {\n let current: PerformanceMonitor | null = null;\n const listeners = new Set<MonitorListener>();\n\n return {\n get: () => current,\n set: (monitor) => {\n current = monitor;\n\n for (const listener of listeners) {\n listener(monitor);\n }\n },\n subscribe: (listener) => {\n listeners.add(listener);\n\n return () => {\n listeners.delete(listener);\n };\n },\n };\n};\n\nconst defaultPerfMonitorStore = createPerfMonitorStore();\n\nexport { createPerfMonitorStore, defaultPerfMonitorStore };\nexport type { PerfMonitorStore };\n","import { useEffect, useRef } from \"react\";\n\nimport type { BudgetsInput } from \"../ui/budgets.ts\";\nimport {\n mountPerfHud,\n type MountPerfHudOptions,\n type PerfHudHandle,\n} from \"../ui/mount-perf-hud.ts\";\nimport { defaultPerfMonitorStore, type PerfMonitorStore } from \"./monitor-store.ts\";\n\ntype PerfHudProps = Omit<MountPerfHudOptions, \"settings\"> & {\n /** Must match the `PerfSampler`'s. Default: the shared store. */\n store?: PerfMonitorStore;\n};\n\n/**\n * DOM overlay. Render it *outside* `<Canvas>`, or Fiber would treat its markup\n * as three objects. Mounts the dockable HUD whenever a `PerfSampler` has\n * published a monitor and tears it down when that sampler unmounts.\n *\n * `theme`, `mode`, `label`, `budgets` and `quality` apply live through the handle.\n * `defaultPlacement` and `budgets` are compared by value, so inline literals\n * like `{{ edge: \"right\" }}` are fine.\n * The remaining props (`storageKey`, `parent`, `refreshHz`, `injectStyles`,\n * `store`) remount the HUD when they change.\n */\nfunction PerfHud({\n budgets,\n defaultPlacement,\n injectStyles,\n label,\n mode,\n parent,\n quality,\n refreshHz,\n storageKey,\n store,\n theme,\n}: PerfHudProps) {\n const source = store ?? defaultPerfMonitorStore;\n const handleRef = useRef<PerfHudHandle | null>(null);\n // The mount effect reads these through refs so changing them never remounts;\n // the live effects below keep the refs current and push changes to the handle.\n const themeRef = useRef(theme);\n const modeRef = useRef(mode);\n const labelRef = useRef(label);\n const budgetsRef = useRef(budgets);\n const qualityRef = useRef(quality);\n // By value: an inline `{{ calls: 500 }}` is a new object every render.\n const budgetsKey = JSON.stringify(budgets ?? null);\n // Primitives, so an inline `{{ edge: \"right\" }}` literal doesn't remount each render.\n const placementEdge = defaultPlacement?.edge;\n const placementAlign = defaultPlacement?.align;\n\n useEffect(() => {\n const attach = (monitor: ReturnType<PerfMonitorStore[\"get\"]>) => {\n handleRef.current?.dispose();\n handleRef.current = null;\n\n if (!monitor) {\n return;\n }\n\n handleRef.current = mountPerfHud(monitor, {\n budgets: budgetsRef.current,\n defaultPlacement: placementEdge\n ? { align: placementAlign, edge: placementEdge }\n : undefined,\n injectStyles,\n label: labelRef.current,\n mode: modeRef.current,\n parent,\n quality: qualityRef.current,\n refreshHz,\n storageKey,\n theme: themeRef.current,\n });\n };\n\n const unsubscribe = source.subscribe(attach);\n attach(source.get());\n\n return () => {\n unsubscribe();\n handleRef.current?.dispose();\n handleRef.current = null;\n };\n }, [injectStyles, parent, placementAlign, placementEdge, refreshHz, source, storageKey]);\n\n useEffect(() => {\n themeRef.current = theme;\n handleRef.current?.setTheme(theme ?? \"system\");\n }, [theme]);\n\n useEffect(() => {\n modeRef.current = mode;\n handleRef.current?.setMode(mode ?? \"compact\");\n }, [mode]);\n\n useEffect(() => {\n // Read back from the key so the effect depends on the value, not the object.\n const next = (JSON.parse(budgetsKey) as BudgetsInput | false | null) ?? undefined;\n budgetsRef.current = next;\n handleRef.current?.setBudgets(next);\n }, [budgetsKey]);\n\n useEffect(() => {\n qualityRef.current = quality;\n handleRef.current?.setQuality(quality);\n }, [quality]);\n\n useEffect(() => {\n labelRef.current = label;\n handleRef.current?.element.setAttribute(\"aria-label\", label ?? \"Performance\");\n }, [label]);\n\n return null;\n}\n\nexport { PerfHud };\nexport type { PerfHudProps };\n","import { useFrame, useThree } from \"@react-three/fiber\";\nimport { useEffect, useRef } from \"react\";\n\nimport { PerformanceMonitor } from \"../core/performance-monitor.ts\";\nimport type { PerformanceMonitorOptions, PerfRenderer } from \"../core/types.ts\";\nimport { frameworkVersions } from \"./framework-versions.ts\";\nimport { defaultPerfMonitorStore, type PerfMonitorStore } from \"./monitor-store.ts\";\n\ntype PerfSamplerProps = Omit<PerformanceMonitorOptions, \"renderer\" | \"frameworks\"> & {\n /** Where the monitor is published for `PerfHud`. Default: the shared store. */\n store?: PerfMonitorStore;\n};\n\n/**\n * In-canvas collector. Renders nothing. Fiber runs every `useFrame` before\n * `gl.render`, so the previous frame is closed here and the next one opens, and\n * GPU queries then span the render that just ran. The monitor is created in\n * an effect so StrictMode's double mount attaches and detaches cleanly.\n */\nfunction PerfSampler({\n frameStatsSize,\n gpuQueryPoolSize,\n historySize,\n store,\n trackGPU,\n}: PerfSamplerProps) {\n const renderer = useThree((state) => state.gl);\n const monitorRef = useRef<PerformanceMonitor | null>(null);\n const startedRef = useRef(false);\n const target = store ?? defaultPerfMonitorStore;\n\n useEffect(() => {\n const monitor = new PerformanceMonitor({\n frameStatsSize,\n frameworks: frameworkVersions,\n gpuQueryPoolSize,\n historySize,\n renderer: renderer as unknown as PerfRenderer,\n trackGPU,\n });\n monitorRef.current = monitor;\n startedRef.current = false;\n target.set(monitor);\n\n return () => {\n target.set(null);\n monitorRef.current = null;\n monitor.dispose();\n };\n }, [frameStatsSize, gpuQueryPoolSize, historySize, renderer, target, trackGPU]);\n\n useFrame(() => {\n const monitor = monitorRef.current;\n\n if (!monitor) {\n return;\n }\n\n if (startedRef.current) {\n monitor.end();\n }\n\n startedRef.current = true;\n monitor.begin();\n });\n\n return null;\n}\n\nexport { PerfSampler };\nexport type { PerfSamplerProps };\n"],"mappings":"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"}
package/dist/ui.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { t as PerformanceMonitor } from "./performance-monitor-CrYmNIdv.js";
2
- import { A as BudgetsInput, C as DockPanelOptions, D as AppBudgets, E as dockPanel, M as resolveBudgets, O as BudgetKey, S as DockHandle, T as ScreenEdge, _ as THEME_MODES, a as PerformanceViewMode, b as DefaultPlacement, c as QualitySource, d as HudDefaults, f as HudSelection, g as ResolvedTheme, h as HudTheme, i as PerformanceView, j as ResolvedBudgets, k as Budgets, l as formatQuality, m as HudSettingsOptions, n as PerfHudHandle, o as PerformanceViewOptions, p as HudSettings, r as mountPerfHud, s as QualityReading, t as MountPerfHudOptions, u as DEFAULT_STORAGE_KEY, v as ThemeMode, w as Placement, x as DockAlign, y as isThemeMode } from "./mount-perf-hud-C0yuW5JI.js";
1
+ import { t as PerformanceMonitor } from "./performance-monitor-CH3GOcIm.js";
2
+ import { A as BudgetsInput, C as DockPanelOptions, D as AppBudgets, E as dockPanel, M as resolveBudgets, O as BudgetKey, S as DockHandle, T as ScreenEdge, _ as THEME_MODES, a as PerformanceViewMode, b as DefaultPlacement, c as QualitySource, d as HudDefaults, f as HudSelection, g as ResolvedTheme, h as HudTheme, i as PerformanceView, j as ResolvedBudgets, k as Budgets, l as formatQuality, m as HudSettingsOptions, n as PerfHudHandle, o as PerformanceViewOptions, p as HudSettings, r as mountPerfHud, s as QualityReading, t as MountPerfHudOptions, u as DEFAULT_STORAGE_KEY, v as ThemeMode, w as Placement, x as DockAlign, y as isThemeMode } from "./mount-perf-hud-Be8DNtkg.js";
3
3
  type DevPanelDim = {
4
4
  get: () => boolean;
5
5
  set: (on: boolean) => void;
package/dist/ui.js CHANGED
@@ -1 +1 @@
1
- export { l as DEFAULT_STORAGE_KEY, u as HudSettings, d as HudTheme, o as PERF_HUD_STYLES, n as PerformanceView, f as THEME_MODES, c as dockPanel, i as formatQuality, r as formatReport, s as injectStyles, p as isThemeMode, a as mountDevPanel, t as mountPerfHud, m as resolveBudgets } from "./mount-perf-hud-BRihOM2d.js";
1
+ export { l as DEFAULT_STORAGE_KEY, u as HudSettings, d as HudTheme, o as PERF_HUD_STYLES, n as PerformanceView, f as THEME_MODES, c as dockPanel, i as formatQuality, r as formatReport, s as injectStyles, p as isThemeMode, a as mountDevPanel, t as mountPerfHud, m as resolveBudgets } from "./mount-perf-hud-BtucU7nZ.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zkmake/three-meter",
3
- "version": "0.13.3",
3
+ "version": "0.14.0",
4
4
  "description": "Zero-dependency three.js frame metrics (FPS, CPU, GPU, draw calls) with a dockable HUD. Vanilla three and React Three Fiber adapters.",
5
5
  "keywords": [
6
6
  "devtools",