@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.
- package/README.md +15 -9
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{mount-perf-hud-C0yuW5JI.d.ts → mount-perf-hud-Be8DNtkg.d.ts} +3 -2
- package/dist/{mount-perf-hud-BRihOM2d.js → mount-perf-hud-BtucU7nZ.js} +17 -6
- package/dist/mount-perf-hud-BtucU7nZ.js.map +1 -0
- package/dist/{performance-monitor-Dj5H2VXT.js → performance-monitor-Bz3ukv7t.js} +33 -17
- package/dist/performance-monitor-Bz3ukv7t.js.map +1 -0
- package/dist/{performance-monitor-CrYmNIdv.d.ts → performance-monitor-CH3GOcIm.d.ts} +19 -3
- package/dist/react.d.ts +9 -4
- package/dist/react.js +15 -4
- package/dist/react.js.map +1 -1
- package/dist/ui.d.ts +2 -2
- package/dist/ui.js +1 -1
- package/package.json +1 -1
- package/dist/mount-perf-hud-BRihOM2d.js.map +0 -1
- package/dist/performance-monitor-Dj5H2VXT.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mount-perf-hud-BRihOM2d.js","names":[],"sources":["../src/ui/budgets.ts","../src/ui/theme.ts","../src/ui/hud-settings.ts","../src/ui/dock-panel.ts","../src/ui/icons.ts","../src/ui/styles.ts","../src/ui/dev-panel.ts","../package.json","../src/ui/clipboard.ts","../src/ui/format.ts","../src/ui/hider.ts","../src/ui/metric-help.ts","../src/ui/quality.ts","../src/ui/report.ts","../src/ui/sparkline.ts","../src/ui/performance-view.ts","../src/ui/mount-perf-hud.ts"],"sourcesContent":["/**\n * Budgets: the value each metric should stay within. A reading past its\n * budget turns amber in the card, and the FPS / CPU / GPU graphs draw the\n * line once the series reaches it. `fps`, `low` and `headroom` are floors\n * (worse below); everything else is a ceiling (worse above).\n *\n * Timing budgets default from `targetFps`: FPS at least 95% of it, 1% low at\n * least half, CPU and GPU within one frame, p99 within one and a half,\n * headroom at least zero. Without a `targetFps`, the detected refresh rate is\n * used, then 60 until one is detected.\n * Counts have no default, since what's reasonable depends on the scene. Pass\n * `null` for a key to drop its default, or `false` for no budgets at all.\n *\n * An app's budgets file (`AppBudgets`, from three-audit's `defineBudgets`) works too: its\n * `frame` sets `targetFps`, `calls` and `triangles`.\n */\ntype BudgetKey =\n | \"calls\"\n | \"cpu\"\n | \"fps\"\n | \"geometries\"\n | \"gpu\"\n | \"headroom\"\n | \"hitches\"\n | \"lines\"\n | \"low\"\n | \"p99\"\n | \"passes\"\n | \"points\"\n | \"refresh\"\n | \"shaders\"\n | \"textures\"\n | \"triangles\";\n\ntype Budgets = Partial<Record<BudgetKey, number | null>> & {\n /** The frame rate the defaults are derived from. Default: the detected refresh rate, else 60. */\n targetFps?: number;\n};\n\n/**\n * An app's budgets file, as `@zkmake/three-audit`'s `defineBudgets` returns it. Structural, so\n * three-meter needs no dependency: only `frame` is read, its `fps` as `targetFps`.\n */\ntype AppBudgets = {\n readonly frame: {\n readonly fps?: number | undefined;\n readonly calls?: number | undefined;\n readonly triangles?: number | undefined;\n };\n};\n\n/** What the HUD's `budgets` option takes: its own budgets, or an app's budgets file. */\ntype BudgetsInput = Budgets | AppBudgets;\n\ntype ResolvedBudgets = {\n /** Frame budget in ms, `1000 / targetFps`. */\n frameMs: number;\n /** The rate the timing budgets came from: `targetFps`, the detected refresh, or 60. */\n targetFps: number;\n limits: Partial<Record<BudgetKey, number>>;\n};\n\nconst DEFAULT_TARGET_FPS = 60;\nconst FLOORS = new Set<BudgetKey>([\"fps\", \"headroom\", \"low\"]);\n\nconst isAppBudgets = (budgets: BudgetsInput): budgets is AppBudgets => \"frame\" in budgets;\n\n/** An app's frame budget as the HUD's: only the limits it sets, so the rest keep their defaults. */\nconst fromApp = ({ frame }: AppBudgets): Budgets => {\n const budgets: Budgets = {};\n\n if (frame.fps !== undefined) {\n budgets.targetFps = frame.fps;\n }\n\n if (frame.calls !== undefined) {\n budgets.calls = frame.calls;\n }\n\n if (frame.triangles !== undefined) {\n budgets.triangles = frame.triangles;\n }\n\n return budgets;\n};\n\n/** `detectedHz` is the monitor's refresh reading; an explicit `targetFps` wins over it. */\nconst resolveBudgets = (\n input: BudgetsInput | false | undefined,\n detectedHz: number | null = null,\n): ResolvedBudgets => {\n const budgets = input && isAppBudgets(input) ? fromApp(input) : input;\n const targetFps = (budgets && budgets.targetFps) || detectedHz || DEFAULT_TARGET_FPS;\n const frameMs = 1000 / targetFps;\n\n if (budgets === false) {\n return { frameMs, limits: {}, targetFps };\n }\n\n const merged: Budgets = {\n cpu: frameMs,\n fps: targetFps * 0.95,\n gpu: frameMs,\n headroom: 0,\n low: targetFps / 2,\n p99: frameMs * 1.5,\n ...budgets,\n };\n const limits: Partial<Record<BudgetKey, number>> = {};\n\n for (const [key, limit] of Object.entries(merged)) {\n if (key !== \"targetFps\" && typeof limit === \"number\" && Number.isFinite(limit)) {\n limits[key as BudgetKey] = limit;\n }\n }\n\n return { frameMs, limits, targetFps };\n};\n\nconst isFloor = (key: BudgetKey) => FLOORS.has(key);\n\n/** `null` means the value isn't known yet (no frames, no GPU timer), which is never over. */\nconst isOverBudget = (resolved: ResolvedBudgets, key: BudgetKey, value: number | null) => {\n const limit = resolved.limits[key];\n\n if (limit === undefined || value === null) {\n return false;\n }\n\n return isFloor(key) ? value < limit : value > limit;\n};\n\nexport { isFloor, isOverBudget, resolveBudgets };\nexport type { AppBudgets, BudgetKey, Budgets, BudgetsInput, ResolvedBudgets };\n","/**\n * Dark, light, or follow the OS. One instance per HUD: the floating host and\n * the card inside it both paint `resolved` onto `data-theme`, and the\n * stylesheet picks the palette from that attribute.\n *\n * Two layers. `mode` is what the consumer asked for (the `theme` option,\n * `setTheme`). `override` is what the person using the HUD picked in the\n * panel, kept in `HudSettings`; it wins while set. `effective` is whichever\n * applies, and `system` there tracks `prefers-color-scheme` for as long as\n * the instance lives.\n */\ntype ThemeMode = \"dark\" | \"light\" | \"system\";\ntype ResolvedTheme = Exclude<ThemeMode, \"system\">;\n\nconst THEME_MODES: readonly ThemeMode[] = [\"dark\", \"light\", \"system\"];\nconst DARK_SCHEME_QUERY = \"(prefers-color-scheme: dark)\";\n\nconst isThemeMode = (value: unknown): value is ThemeMode =>\n typeof value === \"string\" && (THEME_MODES as readonly string[]).includes(value);\n\nclass HudTheme {\n private current: ThemeMode;\n private userPick: ThemeMode | null = null;\n private readonly listeners = new Set<() => void>();\n private media: MediaQueryList | null = null;\n private readonly onSchemeChange = () => this.notify();\n\n constructor(mode: ThemeMode = \"system\") {\n this.current = mode;\n this.watch();\n }\n\n /** What the consumer asked for. */\n get mode(): ThemeMode {\n return this.current;\n }\n\n /** What the person using the HUD picked in the panel, or `null` for no pick. */\n get override(): ThemeMode | null {\n return this.userPick;\n }\n\n /** The layer that applies: the user's pick if there is one, else the consumer's mode. */\n get effective(): ThemeMode {\n return this.userPick ?? this.current;\n }\n\n /**\n * `dark` or `light`, with `system` resolved against the OS preference.\n * Where `matchMedia` is missing, `system` falls back to `dark`, the HUD's\n * original palette.\n */\n get resolved(): ResolvedTheme {\n const effective = this.effective;\n\n if (effective !== \"system\") {\n return effective;\n }\n\n if (!this.media) {\n return \"dark\";\n }\n\n return this.media.matches ? \"dark\" : \"light\";\n }\n\n setMode(mode: ThemeMode) {\n if (mode === this.current) {\n return;\n }\n\n this.current = mode;\n this.watch();\n this.notify();\n }\n\n setOverride(mode: ThemeMode | null) {\n if (mode === this.userPick) {\n return;\n }\n\n this.userPick = mode;\n this.watch();\n this.notify();\n }\n\n /** Fires on `setMode`, `setOverride` and, in `system`, whenever the OS preference flips. */\n subscribe(listener: () => void) {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /** Stop tracking the OS preference and drop every subscriber. */\n dispose() {\n this.unwatch();\n this.listeners.clear();\n }\n\n private watch() {\n if (this.effective !== \"system\") {\n this.unwatch();\n\n return;\n }\n\n if (this.media || typeof matchMedia !== \"function\") {\n return;\n }\n\n this.media = matchMedia(DARK_SCHEME_QUERY);\n this.media.addEventListener(\"change\", this.onSchemeChange);\n }\n\n private unwatch() {\n this.media?.removeEventListener(\"change\", this.onSchemeChange);\n this.media = null;\n }\n\n private notify() {\n for (const listener of this.listeners) {\n listener();\n }\n }\n}\n\n/** Paint the resolved theme onto an element for the stylesheet to key off. */\nconst applyTheme = (element: HTMLElement, theme: HudTheme) => {\n element.dataset.theme = theme.resolved;\n};\n\nexport { applyTheme, HudTheme, isThemeMode, THEME_MODES };\nexport type { ResolvedTheme, ThemeMode };\n","/**\n * Which metrics the compact HUD shows, whether it shows the environment\n * footer, the dim-on-leave toggle, and the theme\n * the person using the HUD picked in the panel (`null` until they pick one,\n * so the consumer's `theme` option applies). One instance per HUD; the\n * full-mode controls and the compact card stay in sync through it. Persists\n * to `localStorage` under `storageKey` (pass `null` to keep it in memory only).\n */\nimport type { TimingMetric } from \"../core/types.ts\";\nimport { isThemeMode, type ThemeMode } from \"./theme.ts\";\n\ntype HudSelection = {\n dim: boolean;\n graphs: ReadonlySet<TimingMetric>;\n /** Version, three revision, backend and GPU footer in the compact HUD. */\n info: boolean;\n numbers: ReadonlySet<string>;\n theme: ThemeMode | null;\n};\n\ntype HudDefaults = {\n dim?: boolean;\n graphs?: readonly TimingMetric[];\n info?: boolean;\n numbers?: readonly string[];\n};\n\ntype HudSettingsOptions = {\n /** localStorage key. Default `three-meter`; `null` disables persistence. */\n storageKey?: string | null;\n /** What a first visit shows. */\n defaults?: HudDefaults;\n};\n\ntype MutableSelection = {\n dim: boolean;\n graphs: Set<TimingMetric>;\n info: boolean;\n numbers: Set<string>;\n theme: ThemeMode | null;\n};\n\nconst DEFAULT_STORAGE_KEY = \"three-meter\";\nconst DEFAULT_DIM = false;\nconst DEFAULT_INFO = false;\nconst DEFAULT_NUMBERS: readonly string[] = [\"fps\", \"calls\", \"cpu\", \"gpu\"];\nconst DEFAULT_GRAPHS: readonly TimingMetric[] = [];\nconst TIMING_METRICS = new Set<TimingMetric>([\"fps\", \"cpu\", \"gpu\"]);\n\nconst isTimingMetric = (value: string): value is TimingMetric =>\n TIMING_METRICS.has(value as TimingMetric);\n\nclass HudSettings {\n private readonly storageKey: string | null;\n private readonly defaults: Required<HudDefaults>;\n private readonly listeners = new Set<() => void>();\n private readonly state: MutableSelection;\n\n constructor(options: HudSettingsOptions = {}) {\n this.storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n this.defaults = {\n dim: options.defaults?.dim ?? DEFAULT_DIM,\n graphs: options.defaults?.graphs ?? DEFAULT_GRAPHS,\n info: options.defaults?.info ?? DEFAULT_INFO,\n numbers: options.defaults?.numbers ?? DEFAULT_NUMBERS,\n };\n this.state = this.load();\n }\n\n get selection(): HudSelection {\n return this.state;\n }\n\n get dim() {\n return this.state.dim;\n }\n\n setDim(enabled: boolean) {\n this.state.dim = enabled;\n this.persistAndNotify();\n }\n\n get info() {\n return this.state.info;\n }\n\n setInfo(enabled: boolean) {\n this.state.info = enabled;\n this.persistAndNotify();\n }\n\n /** The user's pick in the panel; `null` means none, so the consumer's option applies. */\n get theme() {\n return this.state.theme;\n }\n\n setTheme(mode: ThemeMode | null) {\n this.state.theme = mode;\n this.persistAndNotify();\n }\n\n isNumberEnabled(key: string) {\n return this.state.numbers.has(key);\n }\n\n isGraphEnabled(metric: TimingMetric) {\n return this.state.graphs.has(metric);\n }\n\n setNumberEnabled(key: string, enabled: boolean) {\n if (enabled) {\n this.state.numbers.add(key);\n } else {\n this.state.numbers.delete(key);\n }\n\n this.persistAndNotify();\n }\n\n setGraphEnabled(metric: TimingMetric, enabled: boolean) {\n if (enabled) {\n this.state.graphs.add(metric);\n } else {\n this.state.graphs.delete(metric);\n }\n\n this.persistAndNotify();\n }\n\n subscribe(listener: () => void) {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private load(): MutableSelection {\n const fallback = (): MutableSelection => ({\n dim: this.defaults.dim,\n graphs: new Set(this.defaults.graphs),\n info: this.defaults.info,\n numbers: new Set(this.defaults.numbers),\n theme: null,\n });\n\n if (this.storageKey === null) {\n return fallback();\n }\n\n try {\n const raw = localStorage.getItem(this.storageKey);\n\n if (!raw) {\n return fallback();\n }\n\n const parsed = JSON.parse(raw) as {\n dim?: boolean;\n graphs?: string[];\n info?: boolean;\n numbers?: string[];\n theme?: unknown;\n };\n\n return {\n dim: parsed.dim ?? this.defaults.dim,\n graphs: new Set((parsed.graphs ?? this.defaults.graphs).filter(isTimingMetric)),\n info: parsed.info ?? this.defaults.info,\n numbers: new Set(parsed.numbers ?? this.defaults.numbers),\n theme: isThemeMode(parsed.theme) ? parsed.theme : null,\n };\n } catch {\n // Malformed or unavailable storage → defaults.\n return fallback();\n }\n }\n\n private persistAndNotify() {\n if (this.storageKey !== null) {\n try {\n localStorage.setItem(\n this.storageKey,\n JSON.stringify({\n dim: this.state.dim,\n graphs: [...this.state.graphs],\n info: this.state.info,\n numbers: [...this.state.numbers],\n // Omitted while null: JSON.stringify drops undefined.\n theme: this.state.theme ?? undefined,\n }),\n );\n } catch {\n // In-memory selection still drives the live views.\n }\n }\n\n for (const listener of this.listeners) {\n listener();\n }\n }\n}\n\nexport { DEFAULT_STORAGE_KEY, HudSettings };\nexport type { HudDefaults, HudSelection, HudSettingsOptions };\n","/**\n * Dock a fixed-position panel to a screen edge. Dragging the handle snaps it\n * to the nearest edge on drop; the edge and the offset along it persist under\n * `<storageKey>:placement`. On a resize (the window's or the panel's own) it\n * keeps its edge and slides along it to stay on screen; only a drop changes the\n * edge. (Re-docking an overflowing box to its nearest edge flipped a panel that\n * grew in a corner onto the side edge.)\n */\nimport { DEFAULT_STORAGE_KEY } from \"./hud-settings.ts\";\n\nconst INSET = 10;\n/** Room for the stacked control discs on the inward side of the panel. */\nconst SIDE_CLEARANCE = 26;\nconst DRAG_THRESHOLD = 4;\nconst EDGES = [\"left\", \"right\", \"top\", \"bottom\"] as const;\n\ntype ScreenEdge = (typeof EDGES)[number];\n\ntype Placement = {\n /** Top (left/right) or left (top/bottom) of the host, in CSS pixels. */\n along: number;\n edge: ScreenEdge;\n};\n\ntype Box = {\n height: number;\n left: number;\n top: number;\n width: number;\n};\n\ntype DockAlign = \"start\" | \"center\" | \"end\";\n\ntype DefaultPlacement = {\n edge: ScreenEdge;\n /** Where along that edge on a first visit. Default `center`. */\n align?: DockAlign;\n};\n\ntype DockPanelOptions = {\n /** The drag grip. Without one the panel still docks and re-docks on resize. */\n handle?: HTMLElement | null;\n /** First-visit position, before anything is stored. Default left edge, centred. */\n defaultPlacement?: DefaultPlacement;\n /** Shares the HUD's key; `null` disables persistence. Default `three-meter`. */\n storageKey?: string | null;\n};\n\ntype DockHandle = {\n /** Re-resolve against the current viewport and panel size (after a mode change, say). */\n refresh: () => Placement;\n dispose: () => void;\n};\n\nconst viewportBox = (): Box => ({\n height: window.innerHeight,\n left: 0,\n top: 0,\n width: window.innerWidth,\n});\n\nconst clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\nconst panelBox = (element: HTMLElement): Box => {\n const rect = element.getBoundingClientRect();\n\n return {\n height: rect.height || element.offsetHeight,\n left: rect.left,\n top: rect.top,\n width: rect.width || element.offsetWidth,\n };\n};\n\nconst clampAlong = (edge: ScreenEdge, along: number, view: Box, panel: Box) => {\n if (edge === \"left\" || edge === \"right\") {\n return clamp(along, INSET, Math.max(INSET, view.height - panel.height - INSET));\n }\n\n // Tools sit on the right (inward) unless the panel is on the right edge.\n return clamp(along, INSET, Math.max(INSET, view.width - panel.width - INSET - SIDE_CLEARANCE));\n};\n\nconst snapBoxToEdge = (box: Box, view: Box): Placement => {\n const distances: Record<ScreenEdge, number> = {\n bottom: view.height - (box.top + box.height),\n left: box.left,\n right: view.width - (box.left + box.width),\n top: box.top,\n };\n\n let edge: ScreenEdge = \"left\";\n let nearest = Number.POSITIVE_INFINITY;\n\n for (const next of EDGES) {\n if (distances[next] < nearest) {\n nearest = distances[next];\n edge = next;\n }\n }\n\n const along = edge === \"left\" || edge === \"right\" ? box.top : box.left;\n\n return { along: Math.round(clampAlong(edge, along, view, box)), edge };\n};\n\nconst initialPlacement = (preset: DefaultPlacement, view: Box, panel: Box): Placement => {\n const { edge } = preset;\n const vertical = edge === \"left\" || edge === \"right\";\n const span = vertical ? view.height : view.width;\n const size = vertical ? panel.height : panel.width;\n const along =\n preset.align === \"start\" ? 0 : preset.align === \"end\" ? span - size : (span - size) / 2;\n\n return { along: Math.round(clampAlong(edge, along, view, panel)), edge };\n};\n\nconst isEdge = (value: unknown): value is ScreenEdge =>\n typeof value === \"string\" && (EDGES as readonly string[]).includes(value);\n\nconst readPlacement = (key: string | null): Placement | null => {\n if (key === null) {\n return null;\n }\n\n try {\n const raw = localStorage.getItem(key);\n\n if (!raw) {\n return null;\n }\n\n const parsed = JSON.parse(raw) as { along?: unknown; edge?: unknown };\n\n if (\n typeof parsed.along !== \"number\" ||\n !Number.isFinite(parsed.along) ||\n !isEdge(parsed.edge)\n ) {\n return null;\n }\n\n return { along: parsed.along, edge: parsed.edge };\n } catch {\n return null;\n }\n};\n\nconst writePlacement = (key: string | null, placement: Placement) => {\n if (key === null) {\n return;\n }\n\n try {\n localStorage.setItem(key, JSON.stringify(placement));\n } catch {\n // Best-effort; the live dock still applies.\n }\n};\n\nconst resolvePlacement = (\n stored: Placement | null,\n preset: DefaultPlacement,\n panel: Box,\n view: Box,\n): Placement => {\n if (!stored) {\n return initialPlacement(preset, view, panel);\n }\n\n return {\n along: Math.round(clampAlong(stored.edge, stored.along, view, panel)),\n edge: stored.edge,\n };\n};\n\nconst paintPlacement = (element: HTMLElement, placement: Placement) => {\n element.dataset.edge = placement.edge;\n element.style.left = \"\";\n element.style.right = \"\";\n element.style.top = \"\";\n element.style.bottom = \"\";\n element.style.transform = \"\";\n\n switch (placement.edge) {\n case \"left\":\n element.style.left = `${INSET}px`;\n element.style.top = `${placement.along}px`;\n break;\n case \"right\":\n element.style.right = `${INSET}px`;\n element.style.top = `${placement.along}px`;\n break;\n case \"top\":\n element.style.top = `${INSET}px`;\n element.style.left = `${placement.along}px`;\n break;\n case \"bottom\":\n element.style.bottom = `${INSET}px`;\n element.style.left = `${placement.along}px`;\n break;\n }\n};\n\n/**\n * Dock `element` from storage (or the left centre), re-dock on resize, and\n * snap to the nearest edge when the handle drops.\n */\nconst dockPanel = (element: HTMLElement, options: DockPanelOptions = {}): DockHandle => {\n const baseKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n const key = baseKey === null ? null : `${baseKey}:placement`;\n const handle = options.handle ?? null;\n const preset = options.defaultPlacement ?? { edge: \"left\" };\n\n const apply = (): Placement => {\n const stored = readPlacement(key);\n const view = viewportBox();\n\n if (element.classList.contains(\"is-dragging\")) {\n return stored ?? initialPlacement(preset, view, panelBox(element));\n }\n\n const panel = panelBox(element);\n\n if (panel.width === 0 || panel.height === 0) {\n return stored ?? initialPlacement(preset, view, panel);\n }\n\n const next = resolvePlacement(stored, preset, panel, view);\n paintPlacement(element, next);\n\n if (!stored || stored.edge !== next.edge || stored.along !== next.along) {\n writePlacement(key, next);\n }\n\n return next;\n };\n\n apply();\n requestAnimationFrame(apply);\n\n const onResize = () => apply();\n window.addEventListener(\"resize\", onResize);\n\n const resizeObserver = new ResizeObserver(() => apply());\n resizeObserver.observe(element);\n\n const disposeBase = () => {\n window.removeEventListener(\"resize\", onResize);\n resizeObserver.disconnect();\n };\n\n if (!handle) {\n return { dispose: disposeBase, refresh: apply };\n }\n\n let pointerId: number | null = null;\n let originX = 0;\n let originY = 0;\n let grabX = 0;\n let grabY = 0;\n let dragging = false;\n\n const finish = (event: PointerEvent) => {\n if (pointerId !== event.pointerId) {\n return;\n }\n\n if (dragging) {\n const next = snapBoxToEdge(panelBox(element), viewportBox());\n paintPlacement(element, next);\n writePlacement(key, next);\n }\n\n dragging = false;\n pointerId = null;\n element.classList.remove(\"is-dragging\");\n\n try {\n handle.releasePointerCapture(event.pointerId);\n } catch {\n // Capture was never set.\n }\n };\n\n const onPointerDown = (event: PointerEvent) => {\n if (event.button !== 0) {\n return;\n }\n\n const box = panelBox(element);\n pointerId = event.pointerId;\n originX = event.clientX;\n originY = event.clientY;\n grabX = event.clientX - box.left;\n grabY = event.clientY - box.top;\n dragging = false;\n handle.setPointerCapture(event.pointerId);\n };\n\n const onPointerMove = (event: PointerEvent) => {\n if (pointerId !== event.pointerId) {\n return;\n }\n\n const dx = event.clientX - originX;\n const dy = event.clientY - originY;\n\n if (!dragging && dx * dx + dy * dy < DRAG_THRESHOLD * DRAG_THRESHOLD) {\n return;\n }\n\n dragging = true;\n element.classList.add(\"is-dragging\");\n element.style.right = \"\";\n element.style.bottom = \"\";\n element.style.transform = \"\";\n element.style.left = `${event.clientX - grabX}px`;\n element.style.top = `${event.clientY - grabY}px`;\n };\n\n handle.addEventListener(\"pointerdown\", onPointerDown);\n handle.addEventListener(\"pointermove\", onPointerMove);\n handle.addEventListener(\"pointerup\", finish);\n handle.addEventListener(\"pointercancel\", finish);\n\n return {\n dispose: () => {\n disposeBase();\n handle.removeEventListener(\"pointerdown\", onPointerDown);\n handle.removeEventListener(\"pointermove\", onPointerMove);\n handle.removeEventListener(\"pointerup\", finish);\n handle.removeEventListener(\"pointercancel\", finish);\n },\n refresh: apply,\n };\n};\n\nexport { dockPanel };\nexport type { DefaultPlacement, DockAlign, DockHandle, DockPanelOptions, Placement, ScreenEdge };\n","/**\n * The Lucide marks the HUD chrome uses, inlined so the package carries no\n * icon dependency. The markup is static, so nothing user-supplied reaches it.\n */\ntype IconName =\n | \"activity\"\n | \"blend\"\n | \"box\"\n | \"circleDot\"\n | \"clipboard\"\n | \"contrast\"\n | \"cpu\"\n | \"eye\"\n | \"eyeOff\"\n | \"flame\"\n | \"gauge\"\n | \"grip\"\n | \"help\"\n | \"hourglass\"\n | \"image\"\n | \"info\"\n | \"layers\"\n | \"monitor\"\n | \"moon\"\n | \"palette\"\n | \"repeat\"\n | \"sliders\"\n | \"spline\"\n | \"sun\"\n | \"timer\"\n | \"trendingDown\"\n | \"triangle\"\n | \"zap\";\n\nconst ICON_MARKUP: Record<IconName, string> = {\n eye:\n '<path d=\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\"/>' +\n '<circle cx=\"12\" cy=\"12\" r=\"3\"/>',\n eyeOff:\n '<path d=\"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\"/>' +\n '<path d=\"M14.084 14.158a3 3 0 0 1-4.242-4.242\"/>' +\n '<path d=\"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\"/>' +\n '<path d=\"m2 2 20 20\"/>',\n hourglass:\n '<path d=\"M5 22h14\"/><path d=\"M5 2h14\"/>' +\n '<path d=\"M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22\"/>' +\n '<path d=\"M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2\"/>',\n flame:\n '<path d=\"M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z\"/>',\n activity:\n '<path d=\"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2\"/>',\n box:\n '<path d=\"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z\"/>' +\n '<path d=\"m3.3 7 8.7 5 8.7-5\"/><path d=\"M12 22V12\"/>',\n circleDot: '<circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"1\"/>',\n cpu:\n '<rect width=\"16\" height=\"16\" x=\"4\" y=\"4\" rx=\"2\"/><rect width=\"6\" height=\"6\" x=\"9\" y=\"9\" rx=\"1\"/>' +\n '<path d=\"M15 2v2\"/><path d=\"M15 20v2\"/><path d=\"M2 15h2\"/><path d=\"M2 9h2\"/>' +\n '<path d=\"M20 15h2\"/><path d=\"M20 9h2\"/><path d=\"M9 2v2\"/><path d=\"M9 20v2\"/>',\n gauge: '<path d=\"m12 14 4-4\"/><path d=\"M3.34 19a10 10 0 1 1 17.32 0\"/>',\n image:\n '<rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\"/><circle cx=\"9\" cy=\"9\" r=\"2\"/>' +\n '<path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\"/>',\n info: '<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4\"/><path d=\"M12 8h.01\"/>',\n layers:\n '<path d=\"m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z\"/>' +\n '<path d=\"m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65\"/>' +\n '<path d=\"m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65\"/>',\n palette:\n '<circle cx=\"13.5\" cy=\"6.5\" r=\".5\" fill=\"currentColor\"/><circle cx=\"17.5\" cy=\"10.5\" r=\".5\" fill=\"currentColor\"/>' +\n '<circle cx=\"8.5\" cy=\"7.5\" r=\".5\" fill=\"currentColor\"/><circle cx=\"6.5\" cy=\"12.5\" r=\".5\" fill=\"currentColor\"/>' +\n '<path d=\"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z\"/>',\n repeat:\n '<path d=\"m17 2 4 4-4 4\"/><path d=\"M3 11v-1a4 4 0 0 1 4-4h14\"/>' +\n '<path d=\"m7 22-4-4 4-4\"/><path d=\"M21 13v1a4 4 0 0 1-4 4H3\"/>',\n spline:\n '<circle cx=\"19\" cy=\"5\" r=\"2\"/><circle cx=\"5\" cy=\"19\" r=\"2\"/><path d=\"M5 17A12 12 0 0 1 17 5\"/>',\n timer:\n '<line x1=\"10\" x2=\"14\" y1=\"2\" y2=\"2\"/><line x1=\"12\" x2=\"15\" y1=\"14\" y2=\"11\"/><circle cx=\"12\" cy=\"14\" r=\"8\"/>',\n trendingDown:\n '<polyline points=\"22 17 13.5 8.5 8.5 13.5 2 7\"/><polyline points=\"16 17 22 17 22 11\"/>',\n triangle: '<path d=\"M13.73 4a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z\"/>',\n zap: '<path d=\"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z\"/>',\n blend: '<circle cx=\"9\" cy=\"9\" r=\"7\"/><circle cx=\"15\" cy=\"15\" r=\"7\"/>',\n clipboard:\n '<rect width=\"8\" height=\"4\" x=\"8\" y=\"2\" rx=\"1\" ry=\"1\"/>' +\n '<path d=\"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2\"/>',\n contrast: '<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 18a6 6 0 0 0 0-12v12z\"/>',\n grip:\n '<circle cx=\"9\" cy=\"12\" r=\"1\"/><circle cx=\"9\" cy=\"5\" r=\"1\"/><circle cx=\"9\" cy=\"19\" r=\"1\"/>' +\n '<circle cx=\"15\" cy=\"12\" r=\"1\"/><circle cx=\"15\" cy=\"5\" r=\"1\"/><circle cx=\"15\" cy=\"19\" r=\"1\"/>',\n help:\n '<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"/>' +\n '<path d=\"M12 17h.01\"/>',\n monitor:\n '<rect width=\"20\" height=\"14\" x=\"2\" y=\"3\" rx=\"2\"/>' +\n '<line x1=\"8\" x2=\"16\" y1=\"21\" y2=\"21\"/><line x1=\"12\" x2=\"12\" y1=\"17\" y2=\"21\"/>',\n moon: '<path d=\"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z\"/>',\n sun:\n '<circle cx=\"12\" cy=\"12\" r=\"4\"/><path d=\"M12 2v2\"/><path d=\"M12 20v2\"/>' +\n '<path d=\"m4.93 4.93 1.41 1.41\"/><path d=\"m17.66 17.66 1.41 1.41\"/>' +\n '<path d=\"M2 12h2\"/><path d=\"M20 12h2\"/>' +\n '<path d=\"m6.34 17.66-1.41 1.41\"/><path d=\"m19.07 4.93-1.41 1.41\"/>',\n sliders:\n '<line x1=\"21\" x2=\"14\" y1=\"4\" y2=\"4\"/><line x1=\"10\" x2=\"3\" y1=\"4\" y2=\"4\"/>' +\n '<line x1=\"21\" x2=\"12\" y1=\"12\" y2=\"12\"/><line x1=\"8\" x2=\"3\" y1=\"12\" y2=\"12\"/>' +\n '<line x1=\"21\" x2=\"16\" y1=\"20\" y2=\"20\"/><line x1=\"12\" x2=\"3\" y1=\"20\" y2=\"20\"/>' +\n '<line x1=\"14\" x2=\"14\" y1=\"2\" y2=\"6\"/><line x1=\"8\" x2=\"8\" y1=\"10\" y2=\"14\"/>' +\n '<line x1=\"16\" x2=\"16\" y1=\"18\" y2=\"22\"/>',\n};\n\nconst createIcon = (name: IconName, className: string) => {\n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"2\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n svg.setAttribute(\"class\", className);\n svg.innerHTML = ICON_MARKUP[name];\n\n return svg;\n};\n\nexport { createIcon };\nexport type { IconName };\n","/**\n * The HUD stylesheet as a string, so the package works without a bundler and\n * injects itself once per document. `.perf-monitor` is the metrics card,\n * `.perf-hud` the floating host around it. Both namespaced so a host page can\n * override safely; the `--perf-*` custom properties are the theming surface.\n *\n * Both elements carry `data-theme=\"dark\" | \"light\"` (see `HudTheme`) and each\n * resolves its own tokens from it, so the card also themes when mounted on\n * its own, outside a `.perf-hud`.\n */\nconst STYLE_ATTRIBUTE = \"data-three-meter\";\n\nconst PERF_HUD_STYLES = `\n.perf-hud,\n.perf-monitor {\n --perf-bg: rgba(22, 24, 29, 0.85);\n --perf-fg: #e6e8eb;\n --perf-fg-dim: #c5c8ce;\n --perf-muted: #8b909a;\n --perf-row: rgba(255, 255, 255, 0.04);\n --perf-border: rgba(255, 255, 255, 0.12);\n --perf-accent: #60a5fa;\n --perf-warn: #f59e0b;\n --perf-shadow: 0 8px 24px rgba(0, 0, 0, 0.32);\n\n color-scheme: dark;\n}\n\n.perf-hud[data-theme=\"light\"],\n.perf-monitor[data-theme=\"light\"] {\n --perf-bg: rgba(250, 250, 252, 0.88);\n --perf-fg: #1a1c21;\n --perf-fg-dim: #4b5058;\n --perf-muted: #6b7079;\n --perf-row: rgba(0, 0, 0, 0.05);\n --perf-border: rgba(0, 0, 0, 0.12);\n --perf-accent: #2563eb;\n --perf-warn: #b45309;\n --perf-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);\n\n color-scheme: light;\n}\n\n.perf-monitor {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 8px;\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.4;\n color: var(--perf-fg);\n}\n\n.perf-monitor__graphs {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.perf-monitor__graph {\n position: relative;\n height: 40px;\n background: var(--perf-row);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.perf-monitor__canvas {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n.perf-monitor__graph-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 8px;\n pointer-events: none;\n}\n\n.perf-monitor__graph-head {\n display: flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n}\n\n.perf-monitor__graph-overlay--toggle {\n pointer-events: auto;\n cursor: pointer;\n}\n\n.perf-monitor__graph-label {\n color: var(--perf-muted);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n font-size: 10px;\n}\n\n.perf-monitor__graph-value {\n min-width: 6ch;\n font-weight: 600;\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n}\n\n.perf-monitor__section {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.perf-monitor__row {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 2px 6px;\n border-radius: 3px;\n cursor: pointer;\n}\n\n/* Hidden until \"explain metrics\" is on; then the row wraps it under the label. */\n.perf-monitor__hint {\n display: none;\n flex-basis: 100%;\n padding: 0 0 2px 18px;\n color: var(--perf-muted);\n font-size: 10px;\n line-height: 1.35;\n opacity: 0.85;\n}\n\n.perf-monitor--explain .perf-monitor__hint {\n display: block;\n}\n\n.perf-monitor--explain .perf-monitor__section .perf-monitor__row {\n flex-wrap: wrap;\n}\n\n.perf-monitor__row:nth-child(even) {\n background: var(--perf-row);\n}\n\n.perf-monitor__label {\n flex: 1;\n color: var(--perf-muted);\n}\n\n.perf-monitor__icon {\n flex: none;\n width: 12px;\n height: 12px;\n color: var(--perf-muted);\n}\n\n.perf-monitor__options {\n padding-bottom: 8px;\n border-bottom: 1px solid var(--perf-border);\n}\n\n.perf-monitor__row--static {\n cursor: default;\n}\n\n/* Light / system / dark, as a small segmented radio group. */\n.perf-monitor__segment {\n display: inline-flex;\n flex: none;\n gap: 1px;\n padding: 1px;\n border-radius: 4px;\n background: var(--perf-row);\n}\n\n.perf-monitor__segment-option {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 16px;\n padding: 0;\n border: 0;\n border-radius: 3px;\n background: transparent;\n color: var(--perf-muted);\n cursor: pointer;\n}\n\n.perf-monitor__segment-option:hover {\n color: var(--perf-fg);\n}\n\n.perf-monitor__segment-option[aria-checked=\"true\"] {\n color: var(--perf-fg);\n background: var(--perf-border);\n}\n\n.perf-monitor__segment-option:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n.perf-monitor__segment-option .perf-monitor__icon {\n width: 11px;\n height: 11px;\n color: inherit;\n}\n\n.perf-monitor__button {\n flex: none;\n min-width: 7ch;\n padding: 0 6px;\n border: 1px solid var(--perf-border);\n border-radius: 3px;\n background: var(--perf-row);\n color: var(--perf-fg-dim);\n font: inherit;\n cursor: pointer;\n}\n\n.perf-monitor__button:hover {\n color: var(--perf-fg);\n}\n\n.perf-monitor__button:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n/* Top costs: name, calls, triangles. Rows are buttons that log their objects. */\n.perf-monitor__costs-body {\n display: flex;\n flex-direction: column;\n gap: 1px;\n padding-top: 2px;\n}\n\n.perf-monitor__costs-body[hidden] {\n display: none;\n}\n\n.perf-monitor__cost {\n box-sizing: border-box;\n width: 100%;\n display: grid;\n grid-template-columns: minmax(0, 1fr) 5ch 6ch 14px;\n gap: 6px;\n align-items: baseline;\n padding: 2px 6px;\n border: 0;\n border-radius: 3px;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: default;\n}\n\n.perf-monitor__cost:not(.perf-monitor__cost--head):hover {\n background: var(--perf-row);\n}\n\n.perf-monitor__cost.is-hidden .perf-monitor__cost-name {\n opacity: 0.6;\n text-decoration: line-through;\n}\n\n/* What hiding saved, across the calls and tris columns. */\n.perf-monitor__cost-saved {\n grid-column: 2 / span 2;\n color: var(--perf-accent);\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n white-space: nowrap;\n}\n\n.perf-monitor__cost-eye {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n height: 14px;\n padding: 0;\n border: 0;\n border-radius: 3px;\n background: transparent;\n color: var(--perf-muted);\n cursor: pointer;\n align-self: center;\n}\n\n.perf-monitor__cost-eye .perf-monitor__icon {\n width: 12px;\n height: 12px;\n color: inherit;\n}\n\n.perf-monitor__cost-eye:hover,\n.perf-monitor__cost-eye[aria-pressed=\"true\"] {\n color: var(--perf-fg);\n}\n\n.perf-monitor__cost-eye:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n.perf-monitor__cost:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: -1px;\n}\n\n.perf-monitor__cost--head {\n align-items: center;\n cursor: default;\n}\n\n/* The name is a button: a click logs the row's objects. */\n.perf-monitor__cost-name {\n min-width: 0;\n padding: 0;\n border: 0;\n background: transparent;\n font: inherit;\n text-align: left;\n cursor: pointer;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--perf-fg-dim);\n}\n\n.perf-monitor__cost-name:hover {\n color: var(--perf-fg);\n}\n\n.perf-monitor__cost-name:focus-visible {\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n.perf-monitor__cost-copies {\n color: var(--perf-muted);\n}\n\n.perf-monitor__cost-value {\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n}\n\n.perf-monitor__cost-sort {\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--perf-muted);\n font: inherit;\n font-size: 10px;\n text-align: right;\n cursor: pointer;\n}\n\n.perf-monitor__cost-sort[aria-pressed=\"true\"] {\n color: var(--perf-fg);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n\n.perf-monitor__segment-option--text {\n width: auto;\n padding: 0 5px;\n font: inherit;\n font-size: 10px;\n}\n\n.perf-monitor__costs-note {\n padding: 2px 6px;\n color: var(--perf-muted);\n font-size: 10px;\n}\n\n.perf-monitor__checkbox {\n flex: none;\n margin: 0;\n width: 12px;\n height: 12px;\n accent-color: var(--perf-accent);\n cursor: pointer;\n}\n\n.perf-monitor__value {\n min-width: 7ch;\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n}\n\n.perf-monitor__hud {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 2px;\n}\n\n.perf-monitor__hud-graphs {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.perf-monitor__hud-graphs:empty {\n display: none;\n}\n\n.perf-monitor__hud-graph {\n position: relative;\n height: 28px;\n background: var(--perf-row);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.perf-monitor__hud-grid {\n display: grid;\n grid-template-columns: auto 6ch auto 6ch;\n align-items: baseline;\n gap: 2px 8px;\n}\n\n.perf-monitor__hud-grid:empty {\n display: none;\n}\n\n.perf-monitor__hud-item {\n display: contents;\n}\n\n.perf-monitor__hud-label {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--perf-muted);\n font-size: 10px;\n}\n\n.perf-monitor__hud-label .perf-monitor__icon,\n.perf-monitor__hud-graph .perf-monitor__icon {\n width: 11px;\n height: 11px;\n}\n\n.perf-monitor__hud-value {\n font-weight: 600;\n font-variant-numeric: tabular-nums lining-nums;\n text-align: right;\n}\n\n/* Adaptive quality (three-quality), under the grid: one muted line, the reading in the foreground. */\n.perf-monitor__hud-quality {\n display: flex;\n align-items: center;\n gap: 4px;\n min-width: 0;\n color: var(--perf-muted);\n font-size: 10px;\n}\n\n.perf-monitor__hud-quality[hidden],\n.perf-monitor__row[hidden] {\n display: none;\n}\n\n.perf-monitor__hud-quality .perf-monitor__icon {\n width: 11px;\n height: 11px;\n}\n\n.perf-monitor__hud-quality-value {\n overflow: hidden;\n color: var(--perf-fg);\n font-variant-numeric: tabular-nums lining-nums;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.perf-monitor__footer {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n padding: 8px 6px 0;\n border-top: 1px solid var(--perf-border);\n color: var(--perf-muted);\n font-size: 10px;\n}\n\n.perf-monitor__footer-body {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n\n.perf-monitor__footer-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n min-width: 0;\n}\n\n.perf-monitor__footer-row[hidden],\n.perf-monitor__badge[hidden] {\n display: none;\n}\n\n.perf-monitor__footer-gpu {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--perf-fg-dim);\n}\n\n.perf-monitor__badge {\n flex: none;\n padding: 0 5px;\n border: 1px solid var(--perf-border);\n border-radius: 3px;\n color: var(--perf-fg-dim);\n letter-spacing: 0.04em;\n}\n\n.perf-monitor__badge.is-fallback {\n border-color: var(--perf-warn);\n color: var(--perf-warn);\n}\n\n/* Past its budget. The value's tooltip names the budget. */\n.perf-monitor__value.is-over,\n.perf-monitor__hud-value.is-over,\n.perf-monitor__graph-value.is-over {\n color: var(--perf-warn);\n}\n\n.perf-monitor__link {\n color: inherit;\n text-decoration: none;\n}\n\n.perf-monitor__link:hover,\n.perf-monitor__link:focus-visible {\n color: var(--perf-accent);\n text-decoration: underline;\n}\n\n.perf-monitor--full .perf-monitor__hud {\n display: none;\n}\n\n.perf-monitor--compact .perf-monitor__graphs,\n.perf-monitor--compact .perf-monitor__section,\n.perf-monitor--compact:not(.perf-monitor--info) .perf-monitor__footer,\n.perf-monitor--compact .perf-monitor__footer > .perf-monitor__checkbox,\n.perf-monitor--compact .perf-monitor__footer > .perf-monitor__icon {\n display: none;\n}\n\n.perf-monitor--compact .perf-monitor__footer {\n padding: 6px 2px 2px;\n}\n\n.perf-monitor--compact {\n padding: 4px 8px;\n}\n\n.perf-hud {\n position: fixed;\n z-index: 9999;\n /* Safari resolves width:100% canvases against the viewport on a fixed\n host with auto width. Shrink-wrap so expand stays a card, not the window. */\n width: max-content;\n max-width: min(20rem, calc(100vw - 48px));\n overflow: visible;\n background: transparent;\n pointer-events: none;\n user-select: none;\n touch-action: none;\n}\n\n.perf-hud--full .perf-hud__card > .perf-monitor {\n width: 16rem;\n max-width: 100%;\n}\n\n/* Room for the hints, so most fit on two or three lines. Plus padding, this fills the host's 20rem cap. */\n.perf-hud--full .perf-hud__card > .perf-monitor--explain {\n width: 19rem;\n}\n\n/* The card every dev panel sits in (\\`mountDevPanel\\`): the brand label, then the panel's content. */\n.perf-hud__card {\n position: relative;\n z-index: 1;\n display: flex;\n flex-direction: column;\n max-height: 70vh;\n overflow: hidden;\n background: var(--perf-bg);\n border-radius: 6px;\n box-shadow: var(--perf-shadow);\n backdrop-filter: blur(2px);\n pointer-events: auto;\n transition: opacity 0.25s ease;\n}\n\n.perf-hud__card > .perf-monitor {\n min-height: 0;\n overflow: auto;\n}\n\n.perf-hud--dim:not(.is-awake):not(.is-dragging) > .perf-hud__card {\n opacity: 0.32;\n}\n\n.perf-hud__brand {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px 0;\n color: var(--perf-muted);\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace;\n font-size: 9px;\n font-weight: 600;\n letter-spacing: 0.08em;\n line-height: 1;\n text-transform: uppercase;\n}\n\n/* Alone in the card: pad it all round. */\n.perf-hud__brand:last-child {\n padding-bottom: 6px;\n}\n\n.perf-hud__brand-name {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n}\n\n.perf-hud__brand-name::before {\n width: 6px;\n height: 6px;\n border-radius: 2px;\n background: var(--perf-accent);\n content: \"\";\n}\n\n.perf-hud__brand-detail {\n margin-left: auto;\n font-weight: 400;\n letter-spacing: 0.02em;\n text-transform: none;\n}\n\n.perf-hud__brand-detail:empty {\n display: none;\n}\n\n.perf-hud__hotspot {\n position: absolute;\n z-index: 0;\n inset: -20px;\n pointer-events: auto;\n}\n\n.perf-hud:not([data-edge=\"right\"]) .perf-hud__hotspot {\n right: -46px;\n}\n\n.perf-hud[data-edge=\"right\"] .perf-hud__hotspot {\n left: -46px;\n}\n\n.perf-hud__tools {\n position: absolute;\n top: 0;\n z-index: 2;\n display: flex;\n flex-direction: column;\n gap: 4px;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease;\n}\n\n.perf-hud.is-awake .perf-hud__tools,\n.perf-hud.is-dragging .perf-hud__tools {\n opacity: 1;\n pointer-events: auto;\n}\n\n/* Inward side: right by default, left when docked to the right edge. */\n.perf-hud:not([data-edge=\"right\"]) .perf-hud__tools {\n left: calc(100% + 6px);\n}\n\n.perf-hud[data-edge=\"right\"] .perf-hud__tools {\n right: calc(100% + 6px);\n}\n\n.perf-hud__disc {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n padding: 0;\n border: 0;\n border-radius: 999px;\n background: var(--perf-bg);\n box-shadow: var(--perf-shadow);\n color: var(--perf-fg-dim);\n cursor: pointer;\n backdrop-filter: blur(2px);\n}\n\n.perf-hud__icon {\n width: 12px;\n height: 12px;\n}\n\n.perf-hud__drag {\n cursor: grab;\n}\n\n.perf-hud.is-dragging .perf-hud__drag {\n cursor: grabbing;\n}\n\n/* The ring is only ever hover or keyboard focus, never state. */\n.perf-hud__disc:hover,\n.perf-hud__disc:focus-visible {\n color: var(--perf-fg);\n outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);\n outline-offset: 1px;\n}\n\n/* Dim on is a solid accent disc, off a plain one, so the state reads without hovering and\n hover keeps its own look on top of either. After the hover rule, so on stays on under it. */\n.perf-hud__dim[aria-pressed=\"true\"] {\n background: var(--perf-accent);\n color: var(--perf-bg);\n}\n`;\n\n/**\n * Append the stylesheet to `target`'s head, once. Call before mounting a view\n * by hand; {@link mountPerfHud} does it for you unless told not to.\n */\nconst injectStyles = (target: Document = document) => {\n if (target.head.querySelector(`style[${STYLE_ATTRIBUTE}]`)) {\n return;\n }\n\n const style = target.createElement(\"style\");\n style.setAttribute(STYLE_ATTRIBUTE, \"\");\n style.textContent = PERF_HUD_STYLES;\n target.head.append(style);\n};\n\nexport { injectStyles, PERF_HUD_STYLES };\n","/**\n * The frame every zkmake dev panel shares, so they look and handle alike: a fixed host docked to a\n * screen edge, a card with a brand label on top and the panel's content under it, and three discs\n * on the inward side — drag grip, expand/compact, dim on leave — that stay hidden until the pointer\n * is near or someone taps. `mountPerfHud` is one; `@zkmake/three-textures`' panel is another.\n */\nimport { type DefaultPlacement, dockPanel, type ScreenEdge } from \"./dock-panel.ts\";\nimport { createIcon } from \"./icons.ts\";\nimport { injectStyles } from \"./styles.ts\";\nimport { applyTheme, type HudTheme } from \"./theme.ts\";\n\nconst TOUCH_LINGER_MS = 2500;\n\ntype DevPanelDim = {\n get: () => boolean;\n set: (on: boolean) => void;\n subscribe: (listener: () => void) => () => void;\n};\n\ntype MountDevPanelOptions = {\n /** The panel's body, placed in the card under the brand label. */\n content: HTMLElement;\n /** The label on top of the card, naming the tool: `three-meter`, `three-textures`. */\n brand: string;\n /** Accessible name of the panel. */\n label: string;\n theme: HudTheme;\n /** Base key for the dock placement (`<key>:placement`) and the default dim flag. `null`: not kept. */\n storageKey: string | null;\n /** Where the card sits on a first visit. Default left edge, centred. */\n defaultPlacement?: DefaultPlacement;\n /** Where the host goes. Default `document.body`. */\n parent?: HTMLElement;\n /** Append the stylesheet to the document once. Default true. */\n injectStyles?: boolean;\n /** The expand/compact disc: its label, and what a press does. */\n onToggle: () => void;\n toggleLabel?: string;\n /** Where dim-on-leave lives. Default a flag of its own under `<storageKey>:dim`, off. */\n dim?: DevPanelDim;\n /** The panel docked to another edge (a drop or a resize). */\n onEdgeChange?: (edge: ScreenEdge) => void;\n};\n\ntype DevPanelHandle = {\n element: HTMLDivElement;\n card: HTMLDivElement;\n /** The brand row's right-hand slot: a count, a status. */\n detail: HTMLSpanElement;\n /** Re-dock against the current size, after the card grows or shrinks. */\n refresh: () => void;\n dispose: () => void;\n};\n\nconst disc = (label: string, className: string, icon: \"blend\" | \"grip\" | \"sliders\") => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = `perf-hud__disc ${className}`.trim();\n button.setAttribute(\"aria-label\", label);\n button.title = label;\n button.append(createIcon(icon, \"perf-hud__icon\"));\n\n return button;\n};\n\n/** A dim flag in localStorage, for panels with no settings of their own. */\nconst storedDim = (key: string | null): DevPanelDim => {\n const listeners = new Set<() => void>();\n let on = false;\n\n try {\n on = key !== null && localStorage.getItem(key) === \"1\";\n } catch {\n // No storage (private mode): the flag lasts this page.\n }\n\n return {\n get: () => on,\n set: (next) => {\n on = next;\n\n try {\n if (key !== null) {\n localStorage.setItem(key, next ? \"1\" : \"0\");\n }\n } catch {\n // As above.\n }\n\n for (const listener of listeners) {\n listener();\n }\n },\n subscribe: (listener) => {\n listeners.add(listener);\n\n return () => {\n listeners.delete(listener);\n };\n },\n };\n};\n\nconst mountDevPanel = (options: MountDevPanelOptions): DevPanelHandle => {\n const parent = options.parent ?? document.body;\n const dim =\n options.dim ?? storedDim(options.storageKey === null ? null : `${options.storageKey}:dim`);\n\n if (options.injectStyles ?? true) {\n injectStyles(parent.ownerDocument);\n }\n\n const host = document.createElement(\"div\");\n host.className = \"perf-hud\";\n host.dataset.edge = \"left\";\n // A named group: an aria-label on a plain div is dropped by screen readers.\n host.setAttribute(\"role\", \"group\");\n host.setAttribute(\"aria-label\", options.label);\n\n const hotspot = document.createElement(\"div\");\n hotspot.className = \"perf-hud__hotspot\";\n hotspot.setAttribute(\"aria-hidden\", \"true\");\n\n const tools = document.createElement(\"div\");\n tools.className = \"perf-hud__tools\";\n const grip = disc(`Drag the ${options.brand} panel`, \"perf-hud__drag\", \"grip\");\n const toggle = disc(options.toggleLabel ?? \"Expand or compact the panel\", \"\", \"sliders\");\n const dimDisc = disc(\"Dim the panel when the pointer leaves\", \"perf-hud__dim\", \"blend\");\n dimDisc.addEventListener(\"click\", () => dim.set(!dim.get()));\n toggle.addEventListener(\"click\", () => options.onToggle());\n tools.append(grip, toggle, dimDisc);\n\n const card = document.createElement(\"div\");\n card.className = \"perf-hud__card\";\n\n const brand = document.createElement(\"div\");\n brand.className = \"perf-hud__brand\";\n\n const name = document.createElement(\"span\");\n name.className = \"perf-hud__brand-name\";\n name.textContent = options.brand;\n\n const detail = document.createElement(\"span\");\n detail.className = \"perf-hud__brand-detail\";\n brand.append(name, detail);\n card.append(brand, options.content);\n\n host.append(hotspot, tools, card);\n parent.append(host);\n\n const applyDim = () => {\n host.classList.toggle(\"perf-hud--dim\", dim.get());\n dimDisc.setAttribute(\"aria-pressed\", String(dim.get()));\n dimDisc.title = `Dim on leave: ${dim.get() ? \"on\" : \"off\"}`;\n };\n\n applyDim();\n applyTheme(host, options.theme);\n const unsubscribeDim = dim.subscribe(applyDim);\n const unsubscribeTheme = options.theme.subscribe(() => applyTheme(host, options.theme));\n const dock = dockPanel(host, {\n defaultPlacement: options.defaultPlacement,\n handle: grip,\n storageKey: options.storageKey,\n });\n\n // `dockPanel` writes the edge onto `data-edge`; pass changes on.\n let edge = host.dataset.edge as ScreenEdge;\n const edgeWatch = new MutationObserver(() => {\n const next = host.dataset.edge as ScreenEdge;\n\n if (next !== edge) {\n edge = next;\n options.onEdgeChange?.(edge);\n }\n });\n\n edgeWatch.observe(host, { attributes: true, attributeFilter: [\"data-edge\"] });\n options.onEdgeChange?.(edge);\n\n // Wake on approach, sleep on leave; a touch lingers so the discs can be tapped.\n let hideTimer = 0;\n\n const sleep = () => {\n if (host.classList.contains(\"is-dragging\")) {\n return;\n }\n\n host.classList.remove(\"is-awake\");\n };\n\n const wake = (linger: boolean) => {\n host.classList.add(\"is-awake\");\n window.clearTimeout(hideTimer);\n\n if (linger) {\n hideTimer = window.setTimeout(sleep, TOUCH_LINGER_MS);\n }\n };\n\n const onEnter = () => {\n wake(false);\n };\n\n const onLeave = (event: PointerEvent) => {\n if (host.contains(event.relatedTarget as Node | null)) {\n return;\n }\n\n if (event.pointerType === \"touch\" || event.pointerType === \"pen\") {\n wake(true);\n\n return;\n }\n\n sleep();\n };\n\n const onDown = (event: PointerEvent) => {\n wake(event.pointerType !== \"mouse\");\n };\n\n host.addEventListener(\"pointerenter\", onEnter);\n host.addEventListener(\"pointerleave\", onLeave);\n host.addEventListener(\"pointerdown\", onDown);\n\n return {\n element: host,\n card,\n detail,\n refresh: () => {\n requestAnimationFrame(() => dock.refresh());\n },\n dispose: () => {\n window.clearTimeout(hideTimer);\n edgeWatch.disconnect();\n host.removeEventListener(\"pointerenter\", onEnter);\n host.removeEventListener(\"pointerleave\", onLeave);\n host.removeEventListener(\"pointerdown\", onDown);\n unsubscribeDim();\n unsubscribeTheme();\n dock.dispose();\n host.remove();\n },\n };\n};\n\nexport { mountDevPanel };\nexport type { DevPanelDim, DevPanelHandle, MountDevPanelOptions };\n","","/**\n * Copy text from a click handler. The async Clipboard API needs a secure\n * context and the `clipboard-write` permission; a dev server opened over a LAN\n * IP has neither, and some embeds deny the permission. The fallback is the old\n * select-a-textarea `execCommand(\"copy\")`, still honoured inside a user gesture.\n */\nconst copyWithSelection = (text: string) => {\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"\");\n textarea.style.position = \"fixed\";\n textarea.style.opacity = \"0\";\n textarea.style.pointerEvents = \"none\";\n\n const previous = document.activeElement as HTMLElement | null;\n document.body.append(textarea);\n textarea.select();\n\n try {\n return document.execCommand(\"copy\");\n } catch {\n return false;\n } finally {\n textarea.remove();\n previous?.focus?.();\n }\n};\n\nconst copyText = async (text: string): Promise<boolean> => {\n try {\n await navigator.clipboard.writeText(text);\n\n return true;\n } catch {\n return copyWithSelection(text);\n }\n};\n\nexport { copyText };\n","/**\n * Number formatting for the card. Counts stay exact with thousands separators\n * while they fit the compact grid's 6ch value column (`99,999`), then switch\n * to compact notation (`250K`, `1.4M`, `12.3M`, `1.2B`) so a triangle count in\n * the millions can't run into the neighbouring label.\n */\nconst COMPACT_FROM = 100_000;\n\nconst exact = new Intl.NumberFormat(\"en-US\");\nconst compact = new Intl.NumberFormat(\"en-US\", {\n maximumFractionDigits: 1,\n notation: \"compact\",\n});\n\nconst formatCount = (value: number) =>\n Math.abs(value) < COMPACT_FROM ? exact.format(value) : compact.format(value);\n\nexport { formatCount };\n","/**\n * Hides groups of scene objects to measure what they cost, and puts every one\n * back exactly as it was. Objects are three objects, read structurally: only\n * `visible` is touched.\n *\n * An object can sit in two hidden groups at once (a mesh row and its\n * material's row); it stays hidden until the last group that holds it is\n * shown again. Objects that were already invisible stay invisible.\n */\ntype Hideable = { visible?: boolean };\n\nclass Hider {\n private readonly groups = new Map<string, Hideable[]>();\n /** Each hidden object's `visible` from before the first group hid it. */\n private readonly original = new Map<Hideable, boolean>();\n\n hide(key: string, objects: readonly unknown[]) {\n if (this.groups.has(key)) {\n return;\n }\n\n const hideable = objects.filter(\n (object): object is Hideable => typeof object === \"object\" && object !== null,\n );\n this.groups.set(key, hideable);\n\n for (const object of hideable) {\n if (!this.original.has(object)) {\n this.original.set(object, object.visible !== false);\n }\n\n object.visible = false;\n }\n }\n\n show(key: string) {\n const objects = this.groups.get(key);\n\n if (!objects) {\n return;\n }\n\n this.groups.delete(key);\n\n for (const object of objects) {\n if (!this.heldElsewhere(object)) {\n object.visible = this.original.get(object) ?? true;\n this.original.delete(object);\n }\n }\n }\n\n isHidden(key: string) {\n return this.groups.has(key);\n }\n\n /** Hidden group keys, in the order they were hidden. */\n keys() {\n return [...this.groups.keys()];\n }\n\n restoreAll() {\n for (const key of this.keys()) {\n this.show(key);\n }\n }\n\n private heldElsewhere(object: Hideable) {\n for (const objects of this.groups.values()) {\n if (objects.includes(object)) {\n return true;\n }\n }\n\n return false;\n }\n}\n\nexport { Hider };\n","/**\n * One plain-language line per metric: what it is, and what a bad reading\n * means. Shown as the label's tooltip and, with \"explain metrics\" on, under\n * each row. Keyed like the stat rows; the graphs reuse `fps`, `cpu`, `gpu`.\n */\nconst METRIC_HELP: Record<string, string> = {\n calls:\n \"Separate draw commands sent to the GPU this frame. Fewer is better: merge or instance meshes that share a material.\",\n cpu: \"JavaScript time per frame: your update code plus three.js preparing the render. It has to fit the frame budget (16.7 ms at 60 FPS).\",\n fps: \"Frames drawn per second. Higher is smoother; most displays top out at 60 or 120.\",\n geometries:\n \"Geometries held in GPU memory. Should level off; a steady climb is a leak, so dispose() what you remove.\",\n headroom:\n \"Spare time left in each frame: the frame budget minus whichever of CPU and GPU took longer. Below zero, frames miss the display's refresh.\",\n refresh:\n \"The rate the loop runs at when it keeps up: the display's refresh rate, or the app's own cap. Budgets use it unless you set targetFps.\",\n gpu: \"Time the graphics card spent drawing the frame. High means shaders, overdraw or resolution are the bottleneck.\",\n hitches:\n \"Frames that took over twice as long as usual, in the last 1,000 frames. Each one is a visible stutter.\",\n lines: \"Line segments drawn this frame.\",\n low: \"Frame rate during the slowest 1% of frames. Far below FPS means stutter the average hides.\",\n p99: \"99% of frames finished within this many ms. Above the frame budget (16.7 ms at 60 FPS) means dropped frames.\",\n passes:\n \"Times renderer.render() ran this frame. Shadows, post-processing and extra cameras each add passes.\",\n points: \"Points drawn this frame.\",\n shaders:\n \"Compiled shader programs. A jump mid-session usually means a stutter while a new material compiled.\",\n textures:\n \"Textures held in GPU memory. Should level off; a steady climb is a leak, so dispose() what you remove.\",\n triangles:\n \"Triangles drawn this frame. Mostly GPU cost: use lower-poly models or level of detail to cut it.\",\n};\n\nexport { METRIC_HELP };\n","/**\n * Adaptive quality, as the HUD shows it: one line, `dpr 1.25 · adapting · shadows off`. The source\n * is `@zkmake/three-quality`'s controller (`adaptQuality`) or its React store (`qualityStore`),\n * read by shape like `AppBudgets`, so three-meter needs no dependency and the runtime package none\n * on this dev tool. Anything with a `state` of this shape works.\n */\ntype QualityReading = {\n /** The pixel ratio applied. */\n readonly dpr: number;\n /** What the controller is doing: `warming`, `adapting`, `steady`, `cpu-bound`, `pinned`. */\n readonly phase: string;\n /** 0 is the best rung. */\n readonly level: number;\n readonly levels: number;\n /** The app's tiers by name, `true` while on. */\n readonly tiers?: Readonly<Record<string, boolean>>;\n /** The last window's 90th-percentile frame, ms. */\n readonly p90Ms?: number | null;\n readonly targetFps?: number;\n};\n\n/** What the HUD's `quality` option takes. `state` is read on each repaint; `null` shows `—`. */\ntype QualitySource = { readonly state: QualityReading | null };\n\nconst ratio = (dpr: number) => Number(dpr.toFixed(2)).toString();\n\nconst tiersOff = (reading: QualityReading) =>\n Object.entries(reading.tiers ?? {})\n .filter(([, on]) => !on)\n .map(([name]) => name);\n\nconst formatQuality = (reading: QualityReading | null) => {\n if (!reading) {\n return \"—\";\n }\n\n const off = tiersOff(reading);\n\n return [`dpr ${ratio(reading.dpr)}`, reading.phase, off.length ? `${off.join(\", \")} off` : \"\"]\n .filter(Boolean)\n .join(\" · \");\n};\n\nconst qualityTitle = (reading: QualityReading | null) => {\n if (!reading) {\n return \"Adaptive quality: no controller mounted.\";\n }\n\n const frame =\n typeof reading.p90Ms === \"number\"\n ? `; p90 frame ${reading.p90Ms.toFixed(1)} ms${reading.targetFps ? ` against ${(1000 / reading.targetFps).toFixed(1)} ms` : \"\"}`\n : \"\";\n\n return `Adaptive quality: level ${reading.level + 1} of ${reading.levels} (1 is the best)${frame}.`;\n};\n\nexport { formatQuality, qualityTitle };\nexport type { QualityReading, QualitySource };\n","/**\n * A plain-text snapshot of the monitor for bug reports: versions, backend and\n * GPU, the current sample, stutter stats, the scene's top meshes and the page. Fenced as a `text`\n * code block so GitHub and Slack keep the line breaks.\n */\nimport { version } from \"../../package.json\";\nimport type { PerformanceMonitor } from \"../core/performance-monitor.ts\";\nimport type { Environment, FrameStats, Sample, SceneCost } from \"../core/types.ts\";\nimport { formatCount } from \"./format.ts\";\n\ntype ReportPage = {\n dpr: number;\n height: number;\n userAgent: string;\n width: number;\n};\n\ntype ReportData = {\n /** The top meshes go in when given. */\n cost?: SceneCost | null;\n environment: Environment;\n page: ReportPage | null;\n sample: Sample;\n stats: FrameStats;\n};\n\nconst BACKEND_LABELS = { webgl: \"WebGL\", webgl2: \"WebGL2\", webgpu: \"WebGPU\" } as const;\n\n/** `WebGPU`, `WebGL2`, `WebGL2 fallback`, or `\"\"` before the backend settles. */\nconst backendLabel = (environment: Environment) => {\n if (!environment.backend) {\n return \"\";\n }\n\n const label = BACKEND_LABELS[environment.backend];\n\n return environment.fallback ? `${label} fallback` : label;\n};\n\nconst ms = (value: number) => `${value.toFixed(1)} ms`;\n\n/** Top meshes by draw calls, three at most: `Top: cubes ×2,000 (1 call, 24K tris) · …`. */\nconst topMeshes = (cost: SceneCost) =>\n `Top: ${cost.meshes\n .slice(0, 3)\n .map((entry) => {\n const copies = entry.instances > 1 ? ` ×${formatCount(entry.instances)}` : \"\";\n const calls = `${formatCount(entry.calls)} ${entry.calls === 1 ? \"call\" : \"calls\"}`;\n\n return `${entry.label}${copies} (${calls}, ${formatCount(entry.triangles)} tris)`;\n })\n .join(\" · \")}`;\n\nconst renderReport = ({ cost, environment, page, sample, stats }: ReportData): string => {\n const { render, resources } = sample;\n const heading = [\n `three-meter v${version}`,\n environment.three ? `three r${environment.three}` : \"\",\n backendLabel(environment),\n environment.gpu ?? \"\",\n ].filter(Boolean);\n\n const frames =\n stats.frames === 0\n ? `FPS ${Math.round(sample.fps)}`\n : [\n `FPS ${Math.round(sample.fps)}`,\n `1% low ${Math.round(stats.lowFps)}`,\n `p99 ${ms(stats.p99Ms)}`,\n `hitches ${formatCount(stats.hitches)} / ${formatCount(stats.frames)} frames`,\n ].join(\" · \");\n\n const timing = [\n `CPU ${ms(sample.cpu)}`,\n `GPU ${sample.gpu.available ? ms(sample.gpu.ms) : \"unavailable\"}`,\n ];\n\n // Headroom against the detected refresh: spare time before the frame misses its slot.\n if (stats.refreshHz) {\n const busiest = Math.max(sample.cpu, sample.gpu.available ? sample.gpu.ms : 0);\n timing.push(\n `refresh ${stats.refreshHz} Hz`,\n `headroom ${ms(1000 / stats.refreshHz - busiest)}`,\n );\n }\n\n const lines = [\n heading.join(\" · \"),\n frames,\n timing.join(\" · \"),\n [\n `Calls ${formatCount(render.calls)}`,\n `passes ${formatCount(render.passes)}`,\n `triangles ${formatCount(render.triangles)}`,\n `lines ${formatCount(render.lines)}`,\n `points ${formatCount(render.points)}`,\n ].join(\" · \"),\n [\n `Geometries ${formatCount(resources.geometries)}`,\n `textures ${formatCount(resources.textures)}`,\n `shaders ${formatCount(resources.programs)}`,\n ].join(\" · \"),\n ];\n\n if (cost && cost.meshes.length > 0) {\n lines.push(topMeshes(cost));\n }\n\n if (page) {\n lines.push(`Viewport ${page.width}×${page.height} @${page.dpr}x`, page.userAgent);\n }\n\n return [\"```text\", ...lines, \"```\"].join(\"\\n\");\n};\n\nconst readPage = (): ReportPage | null =>\n typeof window === \"undefined\"\n ? null\n : {\n dpr: window.devicePixelRatio || 1,\n height: window.innerHeight,\n userAgent: navigator.userAgent,\n width: window.innerWidth,\n };\n\n/** The report for `monitor` right now, as Markdown ready to paste into an issue. */\nconst formatReport = (monitor: PerformanceMonitor): string =>\n renderReport({\n cost: monitor.getSceneCost(),\n environment: monitor.getEnvironment(),\n page: readPage(),\n sample: monitor.getSample(),\n stats: monitor.getFrameStats(),\n });\n\nexport { backendLabel, formatReport, renderReport };\nexport type { ReportData, ReportPage };\n","/**\n * Minimal canvas sparkline. The caller owns the canvas, its size, and any\n * device-pixel-ratio transform; this just paints a filled line series in\n * logical (CSS) pixel coordinates. An optional guide (a budget) is drawn as a\n * dashed line when it falls inside the series' range. The scale never\n * stretches to fit it, so a CPU series at 0.2 ms keeps its shape under a\n * 16.7 ms budget and the line appears once the series gets near.\n */\ntype SparklineStyle = {\n fill: string;\n stroke: string;\n};\n\ntype SparklineGuide = {\n color: string;\n value: number;\n};\n\nfunction drawSparkline(\n ctx: CanvasRenderingContext2D,\n width: number,\n height: number,\n values: readonly number[],\n style: SparklineStyle,\n guide?: SparklineGuide,\n) {\n ctx.clearRect(0, 0, width, height);\n\n if (values.length < 2) {\n return;\n }\n\n let max = 0;\n\n for (const value of values) {\n if (value > max) {\n max = value;\n }\n }\n\n const range = max * 1.1 || 1;\n const stepX = width / (values.length - 1);\n const toY = (value: number) => height - (value / range) * height;\n\n ctx.beginPath();\n ctx.moveTo(0, toY(values[0]!));\n\n for (let index = 1; index < values.length; index += 1) {\n ctx.lineTo(index * stepX, toY(values[index]!));\n }\n\n ctx.lineWidth = 1;\n ctx.strokeStyle = style.stroke;\n ctx.stroke();\n\n ctx.lineTo(width, height);\n ctx.lineTo(0, height);\n ctx.closePath();\n ctx.fillStyle = style.fill;\n ctx.fill();\n\n if (guide && guide.value > 0 && guide.value <= range) {\n // Half-pixel offset keeps a 1px line crisp.\n const y = Math.round(toY(guide.value)) + 0.5;\n ctx.save();\n ctx.setLineDash([3, 3]);\n ctx.strokeStyle = guide.color;\n ctx.beginPath();\n ctx.moveTo(0, y);\n ctx.lineTo(width, y);\n ctx.stroke();\n ctx.restore();\n }\n}\n\nexport { drawSparkline };\nexport type { SparklineGuide, SparklineStyle };\n","import { name, version } from \"../../package.json\";\nimport type { PerformanceMonitor } from \"../core/performance-monitor.ts\";\nimport type { CostEntry, FrameStats, Sample, TimingMetric } from \"../core/types.ts\";\nimport {\n type BudgetKey,\n type Budgets,\n type BudgetsInput,\n isFloor,\n isOverBudget,\n type ResolvedBudgets,\n resolveBudgets,\n} from \"./budgets.ts\";\nimport { copyText } from \"./clipboard.ts\";\nimport { formatCount } from \"./format.ts\";\nimport { Hider } from \"./hider.ts\";\nimport { HudSettings } from \"./hud-settings.ts\";\nimport { createIcon, type IconName } from \"./icons.ts\";\nimport { METRIC_HELP } from \"./metric-help.ts\";\nimport { formatQuality, qualityTitle, type QualitySource } from \"./quality.ts\";\nimport { backendLabel, formatReport } from \"./report.ts\";\nimport { drawSparkline, type SparklineStyle } from \"./sparkline.ts\";\nimport { applyTheme, HudTheme, type ResolvedTheme, type ThemeMode } from \"./theme.ts\";\n\ntype PerformanceViewMode = \"full\" | \"compact\";\n\ntype PerformanceViewOptions = {\n monitor: PerformanceMonitor;\n /** `full` is the checkbox list; `compact` the card it configures. Default `full`. */\n mode?: PerformanceViewMode;\n /** Repaint rate. Default 10. */\n refreshHz?: number;\n /**\n * Limits past which a value turns amber. Timing budgets default from\n * `targetFps` (60); `false` turns them all off. See {@link Budgets}; an app's\n * budgets file (`AppBudgets`) applies its `frame`.\n */\n budgets?: BudgetsInput | false;\n /**\n * Adaptive quality to show, one line in the card and a row in the list: three-quality's\n * controller or React store, read by shape. See {@link QualitySource}.\n */\n quality?: QualitySource;\n /** Share one between views to keep them in sync; a fresh instance otherwise. */\n settings?: HudSettings;\n /**\n * Share the host's so the card and its chrome switch together. Otherwise a\n * fresh `system` instance is created and disposed with the view.\n */\n theme?: HudTheme;\n};\n\ntype TimingConfig = {\n format: (value: number) => string;\n icon: IconName;\n label: string;\n metric: TimingMetric;\n /** Canvas can't read CSS custom properties, so the sparkline palette lives here per theme. */\n style: Record<ResolvedTheme, SparklineStyle>;\n unit: string;\n};\n\ntype NumberConfig = {\n format: (value: number) => string;\n icon: IconName;\n key: BudgetKey;\n label: string;\n unit?: \"ms\" | \"Hz\";\n /**\n * `null` until known: no frames yet, or no GPU timer. `frameMs` is the frame\n * budget the timing budgets came from (target or detected refresh).\n */\n value: (sample: Sample, stats: FrameStats, frameMs: number) => number | null;\n};\n\nconst TIMINGS: TimingConfig[] = [\n {\n format: (value) => Math.round(value).toString(),\n icon: \"gauge\",\n label: \"FPS\",\n metric: \"fps\",\n style: {\n dark: { fill: \"rgba(74, 222, 128, 0.15)\", stroke: \"#4ade80\" },\n light: { fill: \"rgba(22, 163, 74, 0.12)\", stroke: \"#16a34a\" },\n },\n unit: \"\",\n },\n {\n format: (value) => value.toFixed(1),\n icon: \"cpu\",\n label: \"CPU\",\n metric: \"cpu\",\n style: {\n dark: { fill: \"rgba(96, 165, 250, 0.15)\", stroke: \"#60a5fa\" },\n light: { fill: \"rgba(37, 99, 235, 0.12)\", stroke: \"#2563eb\" },\n },\n unit: \"ms\",\n },\n {\n format: (value) => value.toFixed(1),\n icon: \"zap\",\n label: \"GPU\",\n metric: \"gpu\",\n style: {\n dark: { fill: \"rgba(244, 114, 182, 0.15)\", stroke: \"#f472b6\" },\n light: { fill: \"rgba(219, 39, 119, 0.12)\", stroke: \"#db2777\" },\n },\n unit: \"ms\",\n },\n];\n\nconst THEME_OPTIONS: { icon: \"sun\" | \"monitor\" | \"moon\"; label: string; mode: ThemeMode }[] = [\n { icon: \"sun\", label: \"Light theme\", mode: \"light\" },\n { icon: \"monitor\", label: \"Match the system theme\", mode: \"system\" },\n { icon: \"moon\", label: \"Dark theme\", mode: \"dark\" },\n];\n\n/** The monorepo tags each package's releases `@zkmake/three-meter@0.9.2`. */\nconst RELEASE_URL = `https://github.com/zkmake/three-kit/releases/tag/${encodeURIComponent(`${name}@${version}`)}`;\n\n/** How long the report button says `copied` / `failed` before reading `copy` again. */\nconst COPY_FEEDBACK_MS = 1500;\n\n/** Walking the scene graph isn't free; the top-costs list refreshes at 2 Hz while open. */\nconst COSTS_INTERVAL_MS = 500;\nconst COSTS_ROWS = 5;\n\ntype CostGroup = \"meshes\" | \"materials\";\ntype CostSort = \"calls\" | \"triangles\";\n\nconst COST_GROUPS: { group: CostGroup; label: string }[] = [\n { group: \"meshes\", label: \"meshes\" },\n { group: \"materials\", label: \"materials\" },\n];\n\nconst COSTS_HELP =\n \"The scene's biggest draw costs in the main render pass, estimated from the scene graph. Hidden and off-screen objects are left out; shadow and post-processing passes aren't counted. Click a row to log its objects to the console; use the eye to hide them and measure the time they cost.\";\n\n/** Frames averaged on each side of hiding a row. */\nconst MEASURE_FRAMES = 30;\n/** How long after hiding before the saving is read, so the average holds only frames without the row. */\nconst MEASURE_SETTLE_MS = 700;\n\ntype HiddenRow = {\n /** GPU ms when a GPU timer exists, else CPU ms: whichever the saving is measured in. */\n baseline: number;\n entry: CostEntry;\n hiddenAt: number;\n metric: \"cpu\" | \"gpu\";\n};\n\nconst meanOfLast = (values: readonly number[], count: number) => {\n const tail = values.slice(-count);\n\n return tail.length === 0 ? 0 : tail.reduce((sum, value) => sum + value, 0) / tail.length;\n};\n\n/** `×2,000` for instances or copies; nothing for a single object. */\nconst copiesOf = (entry: CostEntry) =>\n entry.instances > 1 ? `×${formatCount(entry.instances)}` : \"\";\n\nconst costTitle = (entry: CostEntry) => {\n const objects = entry.objects.length;\n const shape =\n entry.instances > objects\n ? `${formatCount(entry.instances)} instances in ${objects === 1 ? \"one object\" : `${objects} objects`}`\n : `${formatCount(objects)} ${objects === 1 ? \"object\" : \"separate objects\"}`;\n\n return `${entry.label}: ${shape}, ${formatCount(entry.calls)} draw ${entry.calls === 1 ? \"call\" : \"calls\"}, ${formatCount(entry.triangles)} triangles. Click to log to the console.`;\n};\n\nconst readTiming = (sample: Sample, metric: TimingMetric) => {\n switch (metric) {\n case \"fps\":\n return sample.fps;\n case \"cpu\":\n return sample.cpu;\n case \"gpu\":\n return sample.gpu.ms;\n }\n};\n\nconst isTimingAvailable = (sample: Sample, metric: TimingMetric) =>\n metric !== \"gpu\" || sample.gpu.available;\n\nconst whole = (value: number) => Math.round(value).toString();\nconst tenths = (value: number) => value.toFixed(1);\n\nconst NUMBERS: NumberConfig[] = [\n { format: whole, icon: \"gauge\", key: \"fps\", label: \"FPS\", value: (sample) => sample.fps || null },\n {\n format: formatCount,\n icon: \"layers\",\n key: \"calls\",\n label: \"Calls\",\n value: (sample) => sample.render.calls,\n },\n {\n format: tenths,\n icon: \"cpu\",\n key: \"cpu\",\n label: \"CPU\",\n unit: \"ms\",\n value: (sample) => sample.cpu,\n },\n {\n format: tenths,\n icon: \"zap\",\n key: \"gpu\",\n label: \"GPU\",\n unit: \"ms\",\n value: (sample) => (sample.gpu.available ? sample.gpu.ms : null),\n },\n {\n format: tenths,\n icon: \"hourglass\",\n key: \"headroom\",\n label: \"Headroom\",\n unit: \"ms\",\n // Spare time in the frame: the budget minus whichever of CPU and GPU took longer.\n value: (sample, _stats, frameMs) =>\n sample.fps ? frameMs - Math.max(sample.cpu, sample.gpu.available ? sample.gpu.ms : 0) : null,\n },\n {\n format: (value) => `${Math.round(value)} Hz`,\n icon: \"monitor\",\n key: \"refresh\",\n label: \"Refresh\",\n unit: \"Hz\",\n value: (_sample, stats) => stats.refreshHz,\n },\n {\n format: whole,\n icon: \"trendingDown\",\n key: \"low\",\n label: \"1% low\",\n value: (_sample, stats) => (stats.frames ? stats.lowFps : null),\n },\n {\n format: tenths,\n icon: \"timer\",\n key: \"p99\",\n label: \"Frame p99\",\n unit: \"ms\",\n value: (_sample, stats) => (stats.frames ? stats.p99Ms : null),\n },\n {\n format: formatCount,\n icon: \"activity\",\n key: \"hitches\",\n label: \"Hitches\",\n value: (_sample, stats) => (stats.frames ? stats.hitches : null),\n },\n {\n format: formatCount,\n icon: \"triangle\",\n key: \"triangles\",\n label: \"Triangles\",\n value: (sample) => sample.render.triangles,\n },\n {\n format: formatCount,\n icon: \"spline\",\n key: \"lines\",\n label: \"Lines\",\n value: (sample) => sample.render.lines,\n },\n {\n format: formatCount,\n icon: \"circleDot\",\n key: \"points\",\n label: \"Points\",\n value: (sample) => sample.render.points,\n },\n {\n format: formatCount,\n icon: \"repeat\",\n key: \"passes\",\n label: \"Render passes\",\n value: (sample) => sample.render.passes,\n },\n {\n format: formatCount,\n icon: \"box\",\n key: \"geometries\",\n label: \"Geometries\",\n value: (sample) => sample.resources.geometries,\n },\n {\n format: formatCount,\n icon: \"image\",\n key: \"textures\",\n label: \"Textures\",\n value: (sample) => sample.resources.textures,\n },\n {\n format: formatCount,\n icon: \"palette\",\n key: \"shaders\",\n label: \"Shaders\",\n value: (sample) => sample.resources.programs,\n },\n];\n\nconst NUMBER_BY_KEY = new Map(NUMBERS.map((config) => [config.key, config]));\n\n/** Canvas can't read CSS custom properties; the budget line's amber per theme. */\nconst GUIDE_COLOR: Record<ResolvedTheme, string> = {\n dark: \"rgba(245, 158, 11, 0.75)\",\n light: \"rgba(180, 83, 9, 0.7)\",\n};\n\n/** `Budget: at most 16.7 ms` / `at least 57`, or `\"\"` without one. */\nconst budgetTitle = (budgets: ResolvedBudgets, key: BudgetKey) => {\n const limit = budgets.limits[key];\n const config = NUMBER_BY_KEY.get(key);\n\n if (limit === undefined || !config) {\n return \"\";\n }\n\n const bound = isFloor(key) ? \"at least\" : \"at most\";\n\n return `Budget: ${bound} ${config.format(limit)}${config.unit ? ` ${config.unit}` : \"\"}`;\n};\n\n/** The sample value a timing graph shows, `null` when not known. */\nconst timingValue = (sample: Sample, metric: TimingMetric) => {\n if (!isTimingAvailable(sample, metric)) {\n return null;\n }\n\n const value = readTiming(sample, metric);\n\n return metric === \"fps\" && value === 0 ? null : value;\n};\n\ntype GraphCanvas = {\n canvas: HTMLCanvasElement;\n ctx: CanvasRenderingContext2D;\n height: number;\n width: number;\n};\n\ntype HudGraph = GraphCanvas & {\n valueEl: HTMLElement;\n};\n\n/**\n * Host-agnostic widget that paints {@link Sample}s. Mount {@link element}\n * anywhere; the view drives its own throttled repaint. Compact is the floating\n * HUD; full is the checkbox list that chooses what compact shows. Styles are\n * not injected here. Call `injectStyles()` or use `mountPerfHud`.\n */\nclass PerformanceView {\n readonly element: HTMLDivElement;\n readonly settings: HudSettings;\n readonly theme: HudTheme;\n\n private readonly ownsTheme: boolean;\n private readonly unsubscribeTheme: () => void;\n private readonly monitor: PerformanceMonitor;\n private readonly minIntervalMs: number;\n private mode: PerformanceViewMode;\n private readonly graphValueEls = new Map<TimingMetric, HTMLElement>();\n private readonly graphCanvases = new Map<TimingMetric, GraphCanvas>();\n private readonly graphCheckboxes = new Map<TimingMetric, HTMLInputElement>();\n private readonly statValueEls = new Map<BudgetKey, HTMLElement>();\n private readonly statCheckboxes = new Map<BudgetKey, HTMLInputElement>();\n private infoCheckbox!: HTMLInputElement;\n private readonly themeRadios = new Map<ThemeMode, HTMLButtonElement>();\n private footerThreeEl!: HTMLElement;\n private footerHardwareEl!: HTMLElement;\n private footerBackendEl!: HTMLElement;\n private footerGpuEl!: HTMLElement;\n private lastEnvironmentKey = \"\";\n private hudGraphsEl!: HTMLElement;\n private hudGridEl!: HTMLElement;\n private readonly hudGraphs = new Map<TimingMetric, HudGraph>();\n private readonly hudNumberEls = new Map<BudgetKey, HTMLElement>();\n private budgets: ResolvedBudgets;\n private budgetsInput: BudgetsInput | false | undefined;\n private detectedHz: number | null = null;\n private quality: QualitySource | undefined;\n private lastQualityText = \"\";\n private hudQualityEl!: HTMLElement;\n private hudQualityValueEl!: HTMLElement;\n private qualityRowEl!: HTMLElement;\n private qualityValueEl!: HTMLElement;\n private readonly hider = new Hider();\n private readonly hiddenRows = new Map<string, HiddenRow>();\n private rafId: number | null = null;\n private lastPaintAt = 0;\n private readonly resizeObserver: ResizeObserver;\n private readonly unsubscribe: () => void;\n private costsOpen = false;\n private costGroup: CostGroup = \"meshes\";\n private costSort: CostSort = \"calls\";\n private lastCostsAt = -Infinity;\n private costsBodyEl!: HTMLElement;\n private costsListEl!: HTMLElement;\n private readonly costGroupRadios = new Map<CostGroup, HTMLButtonElement>();\n private readonly costSortButtons = new Map<CostSort, HTMLButtonElement>();\n\n constructor(options: PerformanceViewOptions) {\n this.monitor = options.monitor;\n this.settings = options.settings ?? new HudSettings();\n this.ownsTheme = !options.theme;\n this.theme = options.theme ?? new HudTheme();\n // The panel's pick, restored from storage, sits on top of the consumer's mode.\n this.theme.setOverride(this.settings.theme);\n this.mode = options.mode ?? \"full\";\n this.minIntervalMs = 1000 / (options.refreshHz ?? 10);\n this.budgetsInput = options.budgets;\n this.budgets = resolveBudgets(options.budgets);\n this.quality = options.quality;\n this.element = document.createElement(\"div\");\n this.element.className = \"perf-monitor\";\n this.applyModeClass();\n this.resizeObserver = new ResizeObserver(() => this.resizeCanvases());\n this.build();\n this.applyQuality();\n this.applyInfoClass();\n this.rebuildHud();\n this.onThemeChanged();\n this.unsubscribe = this.settings.subscribe(() => this.onSelectionChanged());\n this.unsubscribeTheme = this.theme.subscribe(() => this.onThemeChanged());\n }\n\n start() {\n if (this.rafId === null) {\n this.rafId = requestAnimationFrame(this.frame);\n }\n }\n\n stop() {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n }\n\n setMode(mode: PerformanceViewMode) {\n if (mode === this.mode) {\n return;\n }\n\n this.mode = mode;\n this.applyModeClass();\n this.resizeCanvases();\n }\n\n getMode() {\n return this.mode;\n }\n\n /** Replace the budgets; `false` turns them all off. Applies on the next paint. */\n setBudgets(budgets: BudgetsInput | false | undefined) {\n this.budgetsInput = budgets;\n this.budgets = resolveBudgets(budgets, this.detectedHz);\n this.applyBudgetTitles();\n }\n\n /** Show another quality source, or none. Applies on the next paint. */\n setQuality(quality: QualitySource | undefined) {\n this.quality = quality;\n this.applyQuality();\n }\n\n dispose() {\n this.stop();\n this.showAllHidden();\n this.unsubscribe();\n this.unsubscribeTheme();\n\n if (this.ownsTheme) {\n this.theme.dispose();\n }\n\n this.resizeObserver.disconnect();\n this.element.remove();\n }\n\n private frame = (now: number) => {\n this.rafId = requestAnimationFrame(this.frame);\n\n if (now - this.lastPaintAt < this.minIntervalMs) {\n return;\n }\n\n this.lastPaintAt = now;\n this.render();\n };\n\n private render() {\n const sample = this.monitor.getSample();\n const stats = this.monitor.getFrameStats();\n this.followRefresh(stats.refreshHz);\n this.renderQuality();\n\n if (this.mode === \"compact\") {\n this.renderHud(sample, stats);\n\n if (this.settings.info) {\n this.renderFooter();\n }\n\n return;\n }\n\n for (const config of TIMINGS) {\n this.paintGraph(\n config,\n sample,\n this.graphValueEls.get(config.metric)!,\n this.graphCanvases.get(config.metric)!,\n \"unavailable\",\n );\n }\n\n for (const config of NUMBERS) {\n this.paintNumber(config, this.statValueEls.get(config.key)!, sample, stats);\n }\n\n if (this.costsOpen && performance.now() - this.lastCostsAt >= COSTS_INTERVAL_MS) {\n this.renderCosts();\n }\n\n this.renderFooter();\n }\n\n private renderCosts() {\n this.lastCostsAt = performance.now();\n const cost = this.monitor.getSceneCost();\n // Hidden rows stay listed, first, so they can be shown again and read their saving.\n const hidden = [...this.hiddenRows.values()].map((row) => this.hiddenCostRow(row));\n\n if (!cost) {\n this.costsListEl.replaceChildren(...hidden, this.costsNote(\"No scene rendered yet.\"));\n\n return;\n }\n\n const entries = (this.costGroup === \"meshes\" ? cost.meshes : cost.materials).filter(\n (entry) => !this.hiddenRows.has(entry.key),\n );\n\n if (this.costSort === \"triangles\") {\n entries.sort((a, b) => b.triangles - a.triangles || b.calls - a.calls);\n }\n\n if (entries.length === 0 && hidden.length === 0) {\n this.costsListEl.replaceChildren(this.costsNote(\"Nothing drawn.\"));\n\n return;\n }\n\n const rows = entries.slice(0, COSTS_ROWS).map((entry) => {\n const row = this.costRowShell(entry);\n const calls = document.createElement(\"span\");\n calls.className = \"perf-monitor__cost-value\";\n calls.textContent = formatCount(entry.calls);\n\n const triangles = document.createElement(\"span\");\n triangles.className = \"perf-monitor__cost-value\";\n triangles.textContent = formatCount(entry.triangles);\n\n row.append(calls, triangles, this.eyeButton(entry, false));\n\n return row;\n });\n\n this.costsListEl.replaceChildren(...hidden, ...rows);\n }\n\n /** Name button (logs the objects) in a row; the caller appends the value cells and the eye. */\n private costRowShell(entry: CostEntry) {\n const row = document.createElement(\"div\");\n row.className = \"perf-monitor__cost\";\n\n const name = document.createElement(\"button\");\n name.type = \"button\";\n name.className = \"perf-monitor__cost-name\";\n name.title = costTitle(entry);\n name.textContent = entry.label;\n\n const copies = copiesOf(entry);\n\n if (copies) {\n const suffix = document.createElement(\"span\");\n suffix.className = \"perf-monitor__cost-copies\";\n suffix.textContent = ` ${copies}`;\n name.append(suffix);\n }\n\n name.addEventListener(\"click\", () => {\n // oxlint-disable-next-line no-console -- handing the objects to devtools is the feature\n console.log(`three-meter: ${entry.label}`, entry.objects);\n });\n\n row.append(name);\n\n return row;\n }\n\n /** A hidden row: what hiding it saved, measured from the timing history. */\n private hiddenCostRow(hidden: HiddenRow) {\n const row = this.costRowShell(hidden.entry);\n row.classList.add(\"is-hidden\");\n\n const saved = document.createElement(\"span\");\n saved.className = \"perf-monitor__cost-saved\";\n const label = hidden.metric.toUpperCase();\n\n if (performance.now() - hidden.hiddenAt < MEASURE_SETTLE_MS) {\n saved.textContent = \"measuring…\";\n } else {\n const now = meanOfLast(this.monitor.getHistory(hidden.metric), MEASURE_FRAMES);\n const delta = hidden.baseline - now;\n saved.textContent = `${delta >= 0 ? \"−\" : \"+\"}${Math.abs(delta).toFixed(1)} ms ${label}`;\n saved.title = `${label} time went from ${hidden.baseline.toFixed(1)} ms to ${now.toFixed(1)} ms with this hidden, averaged over ${MEASURE_FRAMES} frames each side.`;\n }\n\n row.append(saved, this.eyeButton(hidden.entry, true));\n\n return row;\n }\n\n private eyeButton(entry: CostEntry, hidden: boolean) {\n const eye = document.createElement(\"button\");\n eye.type = \"button\";\n eye.className = \"perf-monitor__cost-eye\";\n eye.setAttribute(\"aria-pressed\", String(hidden));\n const label = hidden ? `Show ${entry.label} again` : `Hide ${entry.label} to measure its cost`;\n eye.title = label;\n eye.setAttribute(\"aria-label\", label);\n eye.append(createIcon(hidden ? \"eyeOff\" : \"eye\", \"perf-monitor__icon\"));\n eye.addEventListener(\"click\", () => (hidden ? this.showRow(entry.key) : this.hideRow(entry)));\n\n return eye;\n }\n\n private hideRow(entry: CostEntry) {\n const sample = this.monitor.getSample();\n const metric = sample.gpu.available ? \"gpu\" : \"cpu\";\n\n this.hiddenRows.set(entry.key, {\n baseline: meanOfLast(this.monitor.getHistory(metric), MEASURE_FRAMES),\n entry,\n hiddenAt: performance.now(),\n metric,\n });\n // The whole scene's matches, not just the on-screen ones in the row, so copies that come\n // into view while hidden stay hidden.\n const all = this.monitor.getObjectsForKey(entry.key);\n this.hider.hide(entry.key, all.length > 0 ? all : entry.objects);\n this.renderCosts();\n }\n\n private showRow(key: string) {\n this.hider.show(key);\n this.hiddenRows.delete(key);\n this.renderCosts();\n }\n\n /** Everything hidden goes back: on closing top costs and on dispose. */\n private showAllHidden() {\n this.hider.restoreAll();\n this.hiddenRows.clear();\n }\n\n private costsNote(text: string) {\n const note = document.createElement(\"div\");\n note.className = \"perf-monitor__costs-note\";\n note.textContent = text;\n\n return note;\n }\n\n /** The monitor caches the environment once its backend settles; until then this re-reads. */\n private renderFooter() {\n const environment = this.monitor.getEnvironment();\n const three = environment.three ? `three r${environment.three}` : \"\";\n const backend = backendLabel(environment);\n const gpu = environment.gpu ?? \"\";\n const key = `${three}\\n${backend}\\n${gpu}`;\n\n if (key === this.lastEnvironmentKey) {\n return;\n }\n\n this.lastEnvironmentKey = key;\n this.footerThreeEl.textContent = three;\n this.footerBackendEl.textContent = backend;\n this.footerBackendEl.hidden = backend === \"\";\n this.footerBackendEl.title = environment.fallback\n ? \"WebGPURenderer couldn't get WebGPU and fell back to WebGL2\"\n : \"\";\n this.footerBackendEl.classList.toggle(\"is-fallback\", environment.fallback);\n this.footerGpuEl.textContent = gpu;\n this.footerGpuEl.title = gpu;\n this.footerHardwareEl.hidden = backend === \"\" && gpu === \"\";\n }\n\n /** One string for both the card's line and the list's row; the DOM is touched only on change. */\n private renderQuality() {\n if (!this.quality) {\n return;\n }\n\n const reading = this.quality.state;\n const text = formatQuality(reading);\n const title = qualityTitle(reading);\n\n if (`${text}\\n${title}` === this.lastQualityText) {\n return;\n }\n\n this.lastQualityText = `${text}\\n${title}`;\n this.hudQualityValueEl.textContent = text;\n this.qualityValueEl.textContent = text;\n this.hudQualityEl.title = title;\n this.qualityRowEl.title = title;\n }\n\n private applyQuality() {\n this.hudQualityEl.hidden = !this.quality;\n this.qualityRowEl.hidden = !this.quality;\n this.lastQualityText = \"\";\n this.renderQuality();\n }\n\n /** Budgets follow the detected refresh rate unless `targetFps` was given. */\n private followRefresh(hz: number | null) {\n if (hz === this.detectedHz) {\n return;\n }\n\n this.detectedHz = hz;\n this.budgets = resolveBudgets(this.budgetsInput, hz);\n this.applyBudgetTitles();\n }\n\n private renderHud(sample: Sample, stats: FrameStats) {\n for (const [key, element] of this.hudNumberEls) {\n const config = NUMBER_BY_KEY.get(key);\n\n if (config) {\n this.paintNumber(config, element, sample, stats);\n }\n }\n\n for (const [metric, graph] of this.hudGraphs) {\n const config = TIMINGS.find((timing) => timing.metric === metric)!;\n this.paintGraph(config, sample, graph.valueEl, graph, \"—\");\n }\n }\n\n private paintNumber(\n config: NumberConfig,\n element: HTMLElement,\n sample: Sample,\n stats: FrameStats,\n ) {\n const value = config.value(sample, stats, this.budgets.frameMs);\n element.textContent = value === null ? \"—\" : config.format(value);\n element.classList.toggle(\"is-over\", isOverBudget(this.budgets, config.key, value));\n }\n\n /** Value and sparkline, with the budget line drawn once the series reaches it. */\n private paintGraph(\n config: TimingConfig,\n sample: Sample,\n valueEl: HTMLElement,\n graph: GraphCanvas,\n missing: string,\n ) {\n const value = timingValue(sample, config.metric);\n const available = isTimingAvailable(sample, config.metric);\n const limit = this.budgets.limits[config.metric];\n\n valueEl.textContent = available\n ? this.formatTiming(config, readTiming(sample, config.metric))\n : missing;\n valueEl.classList.toggle(\"is-over\", isOverBudget(this.budgets, config.metric, value));\n\n drawSparkline(\n graph.ctx,\n graph.width,\n graph.height,\n available ? this.monitor.getHistory(config.metric) : [],\n config.style[this.theme.resolved],\n limit === undefined ? undefined : { color: GUIDE_COLOR[this.theme.resolved], value: limit },\n );\n }\n\n /** Each value's tooltip names its budget, so amber explains itself. */\n private applyBudgetTitles() {\n for (const [key, element] of this.statValueEls) {\n element.title = budgetTitle(this.budgets, key);\n }\n\n for (const [key, element] of this.hudNumberEls) {\n element.title = budgetTitle(this.budgets, key);\n }\n\n for (const [metric, element] of this.graphValueEls) {\n element.title = budgetTitle(this.budgets, metric);\n }\n\n for (const [metric, graph] of this.hudGraphs) {\n graph.valueEl.title = budgetTitle(this.budgets, metric);\n }\n }\n\n private formatTiming(config: TimingConfig, value: number) {\n return `${config.format(value)}${config.unit ? ` ${config.unit}` : \"\"}`;\n }\n\n private build() {\n const hud = document.createElement(\"div\");\n hud.className = \"perf-monitor__hud\";\n this.hudGraphsEl = document.createElement(\"div\");\n this.hudGraphsEl.className = \"perf-monitor__hud-graphs\";\n this.hudGridEl = document.createElement(\"div\");\n this.hudGridEl.className = \"perf-monitor__hud-grid\";\n this.hudQualityEl = document.createElement(\"div\");\n this.hudQualityEl.className = \"perf-monitor__hud-quality\";\n this.hudQualityValueEl = document.createElement(\"span\");\n this.hudQualityValueEl.className = \"perf-monitor__hud-quality-value\";\n this.hudQualityEl.append(createIcon(\"sliders\", \"perf-monitor__icon\"), this.hudQualityValueEl);\n hud.append(this.hudGraphsEl, this.hudGridEl, this.hudQualityEl);\n\n // Option rows read icon, label, control; the stat rows below keep the\n // checkbox first, so the two groups scan differently on purpose.\n const options = document.createElement(\"div\");\n options.className = \"perf-monitor__section perf-monitor__options\";\n\n const themeRow = document.createElement(\"div\");\n themeRow.className = \"perf-monitor__row perf-monitor__row--static\";\n\n const themeLabel = document.createElement(\"span\");\n themeLabel.className = \"perf-monitor__label\";\n themeLabel.textContent = \"theme\";\n\n const segment = document.createElement(\"div\");\n segment.className = \"perf-monitor__segment\";\n segment.setAttribute(\"role\", \"radiogroup\");\n segment.setAttribute(\"aria-label\", \"Panel theme\");\n\n for (const option of THEME_OPTIONS) {\n const radio = document.createElement(\"button\");\n radio.type = \"button\";\n radio.className = \"perf-monitor__segment-option\";\n radio.setAttribute(\"role\", \"radio\");\n radio.setAttribute(\"aria-checked\", \"false\");\n radio.setAttribute(\"aria-label\", option.label);\n radio.title = option.label;\n radio.append(createIcon(option.icon, \"perf-monitor__icon\"));\n radio.addEventListener(\"click\", () => this.settings.setTheme(option.mode));\n this.themeRadios.set(option.mode, radio);\n segment.append(radio);\n }\n\n themeRow.append(createIcon(\"contrast\", \"perf-monitor__icon\"), themeLabel, segment);\n\n // View-local, not a setting: it's a way to learn the panel, not a preference.\n const explainRow = document.createElement(\"label\");\n explainRow.className = \"perf-monitor__row\";\n\n const explainCheckbox = this.buildCheckbox(false, \"Explain each metric\", (on) =>\n this.element.classList.toggle(\"perf-monitor--explain\", on),\n );\n\n const explainLabel = document.createElement(\"span\");\n explainLabel.className = \"perf-monitor__label\";\n explainLabel.textContent = \"explain metrics\";\n\n explainRow.append(createIcon(\"help\", \"perf-monitor__icon\"), explainLabel, explainCheckbox);\n // Dim on leave is a disc on the floating HUD (`mountPerfHud`), beside drag and expand.\n options.append(themeRow, explainRow, this.buildReportRow());\n\n const graphs = document.createElement(\"div\");\n graphs.className = \"perf-monitor__graphs\";\n\n for (const config of TIMINGS) {\n const graph = document.createElement(\"div\");\n graph.className = \"perf-monitor__graph\";\n\n const canvas = document.createElement(\"canvas\");\n canvas.className = \"perf-monitor__canvas\";\n\n // The whole overlay is the label, so a click anywhere on the graph toggles it.\n const overlay = document.createElement(\"label\");\n overlay.className = \"perf-monitor__graph-overlay perf-monitor__graph-overlay--toggle\";\n\n const head = document.createElement(\"span\");\n head.className = \"perf-monitor__graph-head\";\n\n const checkbox = this.buildCheckbox(\n this.settings.isGraphEnabled(config.metric),\n `Show ${config.label} graph in HUD`,\n (on) => this.settings.setGraphEnabled(config.metric, on),\n );\n this.graphCheckboxes.set(config.metric, checkbox);\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__graph-label\";\n label.textContent = config.label;\n label.title = METRIC_HELP[config.metric] ?? \"\";\n\n const value = document.createElement(\"span\");\n value.className = \"perf-monitor__graph-value\";\n value.textContent = \"—\";\n this.graphValueEls.set(config.metric, value);\n\n const tail = document.createElement(\"span\");\n tail.className = \"perf-monitor__graph-head\";\n tail.append(value, checkbox);\n\n head.append(createIcon(config.icon, \"perf-monitor__icon\"), label);\n overlay.append(head, tail);\n graph.append(canvas, overlay);\n graphs.append(graph);\n\n const ctx = canvas.getContext(\"2d\");\n\n if (ctx) {\n this.graphCanvases.set(config.metric, { canvas, ctx, height: 0, width: 0 });\n this.resizeObserver.observe(graph);\n }\n }\n\n const stats = document.createElement(\"div\");\n stats.className = \"perf-monitor__section\";\n\n // Read-only, so no checkbox: the card shows it whenever there's a source. Last, so hiding it\n // leaves the rows' stripes alone.\n this.qualityRowEl = document.createElement(\"div\");\n this.qualityRowEl.className = \"perf-monitor__row perf-monitor__row--static\";\n\n const qualityLabel = document.createElement(\"span\");\n qualityLabel.className = \"perf-monitor__label\";\n qualityLabel.textContent = \"Quality\";\n\n this.qualityValueEl = document.createElement(\"span\");\n this.qualityValueEl.className = \"perf-monitor__value\";\n this.qualityValueEl.textContent = \"—\";\n\n this.qualityRowEl.append(\n createIcon(\"sliders\", \"perf-monitor__icon\"),\n qualityLabel,\n this.qualityValueEl,\n );\n\n for (const config of NUMBERS) {\n const rowEl = document.createElement(\"label\");\n rowEl.className = \"perf-monitor__row\";\n\n const checkbox = this.buildCheckbox(\n this.settings.isNumberEnabled(config.key),\n `Show ${config.label} in HUD`,\n (on) => this.settings.setNumberEnabled(config.key, on),\n );\n this.statCheckboxes.set(config.key, checkbox);\n\n const help = METRIC_HELP[config.key] ?? \"\";\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__label\";\n label.textContent = config.label;\n label.title = help;\n\n const value = document.createElement(\"span\");\n value.className = \"perf-monitor__value\";\n value.textContent = \"—\";\n this.statValueEls.set(config.key, value);\n\n const hint = document.createElement(\"span\");\n hint.className = \"perf-monitor__hint\";\n hint.textContent = help;\n\n rowEl.append(createIcon(config.icon, \"perf-monitor__icon\"), label, value, checkbox, hint);\n stats.append(rowEl);\n }\n\n stats.append(this.qualityRowEl);\n\n // Icon, rows, checkbox like the stat rows; icon and checkbox hide in compact, the rows don't.\n const footer = document.createElement(\"div\");\n footer.className = \"perf-monitor__footer\";\n\n this.infoCheckbox = this.buildCheckbox(\n this.settings.info,\n \"Show version and environment in HUD\",\n (on) => this.settings.setInfo(on),\n );\n\n const footerBody = document.createElement(\"div\");\n footerBody.className = \"perf-monitor__footer-body\";\n\n // Software on the first row, hardware on the second.\n const softwareRow = document.createElement(\"div\");\n softwareRow.className = \"perf-monitor__footer-row\";\n\n const release = document.createElement(\"a\");\n release.className = \"perf-monitor__link\";\n release.href = RELEASE_URL;\n release.target = \"_blank\";\n release.rel = \"noopener noreferrer\";\n release.title = `Release notes for v${version}`;\n release.textContent = `three-meter v${version}`;\n\n this.footerThreeEl = document.createElement(\"span\");\n softwareRow.append(release, this.footerThreeEl);\n\n this.footerHardwareEl = document.createElement(\"div\");\n this.footerHardwareEl.className = \"perf-monitor__footer-row\";\n this.footerHardwareEl.hidden = true;\n\n this.footerBackendEl = document.createElement(\"span\");\n this.footerBackendEl.className = \"perf-monitor__badge\";\n\n this.footerGpuEl = document.createElement(\"span\");\n this.footerGpuEl.className = \"perf-monitor__footer-gpu\";\n\n this.footerHardwareEl.append(this.footerBackendEl, this.footerGpuEl);\n footerBody.append(softwareRow, this.footerHardwareEl);\n footer.append(createIcon(\"info\", \"perf-monitor__icon\"), footerBody, this.infoCheckbox);\n\n this.element.append(hud, options, graphs, stats, this.buildCosts(), footer);\n }\n\n private rebuildHud() {\n const { selection } = this.settings;\n\n for (const graph of this.hudGraphs.values()) {\n this.resizeObserver.unobserve(graph.canvas.parentElement ?? graph.canvas);\n }\n\n this.hudGraphs.clear();\n this.hudNumberEls.clear();\n this.hudGraphsEl.replaceChildren();\n this.hudGridEl.replaceChildren();\n\n for (const config of TIMINGS) {\n if (!selection.graphs.has(config.metric)) {\n continue;\n }\n\n const row = document.createElement(\"div\");\n row.className = \"perf-monitor__hud-graph\";\n\n const canvas = document.createElement(\"canvas\");\n canvas.className = \"perf-monitor__canvas\";\n\n const overlay = document.createElement(\"div\");\n overlay.className = \"perf-monitor__graph-overlay\";\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__graph-label\";\n label.textContent = config.label;\n label.title = METRIC_HELP[config.metric] ?? \"\";\n\n const head = document.createElement(\"span\");\n head.className = \"perf-monitor__graph-head\";\n head.append(createIcon(config.icon, \"perf-monitor__icon\"), label);\n\n const value = document.createElement(\"span\");\n value.className = \"perf-monitor__graph-value\";\n value.textContent = \"—\";\n\n overlay.append(head, value);\n row.append(canvas, overlay);\n this.hudGraphsEl.append(row);\n\n const ctx = canvas.getContext(\"2d\");\n\n if (ctx) {\n this.hudGraphs.set(config.metric, { canvas, ctx, height: 0, valueEl: value, width: 0 });\n this.resizeObserver.observe(row);\n }\n }\n\n for (const config of NUMBERS) {\n if (!selection.numbers.has(config.key)) {\n continue;\n }\n\n const item = document.createElement(\"div\");\n item.className = \"perf-monitor__hud-item\";\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__hud-label\";\n label.title = METRIC_HELP[config.key] ?? \"\";\n label.append(createIcon(config.icon, \"perf-monitor__icon\"), config.label);\n\n const value = document.createElement(\"span\");\n value.className = \"perf-monitor__hud-value\";\n value.textContent = \"—\";\n\n item.append(label, value);\n this.hudGridEl.append(item);\n this.hudNumberEls.set(config.key, value);\n }\n\n this.applyBudgetTitles();\n this.resizeCanvases();\n }\n\n private onSelectionChanged() {\n for (const [metric, checkbox] of this.graphCheckboxes) {\n checkbox.checked = this.settings.isGraphEnabled(metric);\n }\n\n for (const [key, checkbox] of this.statCheckboxes) {\n checkbox.checked = this.settings.isNumberEnabled(key);\n }\n\n this.infoCheckbox.checked = this.settings.info;\n this.applyInfoClass();\n this.theme.setOverride(this.settings.theme);\n this.rebuildHud();\n }\n\n /** Repaint the palette and mark the radio matching what applies (pick, else consumer mode). */\n private onThemeChanged() {\n applyTheme(this.element, this.theme);\n\n for (const [mode, radio] of this.themeRadios) {\n radio.setAttribute(\"aria-checked\", String(mode === this.theme.effective));\n }\n }\n\n /** \"top costs\": a toggle row, then meshes / materials and a sortable top five. */\n private buildCosts() {\n const section = document.createElement(\"div\");\n section.className = \"perf-monitor__section perf-monitor__costs\";\n\n const toggleRow = document.createElement(\"label\");\n toggleRow.className = \"perf-monitor__row\";\n toggleRow.title = COSTS_HELP;\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__label\";\n label.textContent = \"top costs\";\n\n const checkbox = this.buildCheckbox(false, \"Show the scene's biggest draw costs\", (on) => {\n this.costsOpen = on;\n this.costsBodyEl.hidden = !on;\n\n if (!on) {\n this.showAllHidden();\n }\n\n if (on) {\n this.renderCosts();\n }\n });\n\n toggleRow.append(createIcon(\"flame\", \"perf-monitor__icon\"), label, checkbox);\n\n this.costsBodyEl = document.createElement(\"div\");\n this.costsBodyEl.className = \"perf-monitor__costs-body\";\n this.costsBodyEl.hidden = true;\n\n const head = document.createElement(\"div\");\n head.className = \"perf-monitor__cost perf-monitor__cost--head\";\n\n const segment = document.createElement(\"div\");\n segment.className = \"perf-monitor__segment\";\n segment.setAttribute(\"role\", \"radiogroup\");\n segment.setAttribute(\"aria-label\", \"Group costs by\");\n\n for (const option of COST_GROUPS) {\n const radio = document.createElement(\"button\");\n radio.type = \"button\";\n radio.className = \"perf-monitor__segment-option perf-monitor__segment-option--text\";\n radio.setAttribute(\"role\", \"radio\");\n radio.setAttribute(\"aria-checked\", String(option.group === this.costGroup));\n radio.textContent = option.label;\n radio.addEventListener(\"click\", () => {\n this.costGroup = option.group;\n\n for (const [group, button] of this.costGroupRadios) {\n button.setAttribute(\"aria-checked\", String(group === option.group));\n }\n\n this.renderCosts();\n });\n this.costGroupRadios.set(option.group, radio);\n segment.append(radio);\n }\n\n head.append(segment);\n\n for (const sort of [\"calls\", \"triangles\"] as const) {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"perf-monitor__cost-sort\";\n button.textContent = sort === \"calls\" ? \"calls\" : \"tris\";\n button.title = `Sort by ${sort === \"calls\" ? \"draw calls\" : \"triangles\"}`;\n button.setAttribute(\"aria-pressed\", String(sort === this.costSort));\n button.addEventListener(\"click\", () => {\n this.costSort = sort;\n\n for (const [key, other] of this.costSortButtons) {\n other.setAttribute(\"aria-pressed\", String(key === sort));\n }\n\n this.renderCosts();\n });\n this.costSortButtons.set(sort, button);\n head.append(button);\n }\n\n // Keeps the header's columns over the rows', which end in an eye button.\n head.append(document.createElement(\"span\"));\n\n this.costsListEl = document.createElement(\"div\");\n this.costsListEl.className = \"perf-monitor__costs-list\";\n\n const hint = document.createElement(\"span\");\n hint.className = \"perf-monitor__hint\";\n hint.textContent = COSTS_HELP;\n\n this.costsBodyEl.append(head, this.costsListEl, hint);\n section.append(toggleRow, this.costsBodyEl);\n\n return section;\n }\n\n private buildReportRow() {\n const row = document.createElement(\"div\");\n row.className = \"perf-monitor__row perf-monitor__row--static\";\n\n const label = document.createElement(\"span\");\n label.className = \"perf-monitor__label\";\n label.textContent = \"report\";\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"perf-monitor__button\";\n button.title = \"Copy versions, GPU and current metrics as Markdown for a bug report\";\n button.textContent = \"copy\";\n\n let resetTimer = 0;\n\n const flash = (text: string) => {\n button.textContent = text;\n window.clearTimeout(resetTimer);\n resetTimer = window.setTimeout(() => {\n button.textContent = \"copy\";\n }, COPY_FEEDBACK_MS);\n };\n\n button.addEventListener(\"click\", () => {\n void copyText(formatReport(this.monitor)).then((copied) =>\n flash(copied ? \"copied\" : \"failed\"),\n );\n });\n\n row.append(createIcon(\"clipboard\", \"perf-monitor__icon\"), label, button);\n\n return row;\n }\n\n private buildCheckbox(checked: boolean, title: string, onChange: (enabled: boolean) => void) {\n const checkbox = document.createElement(\"input\");\n checkbox.type = \"checkbox\";\n checkbox.className = \"perf-monitor__checkbox\";\n checkbox.checked = checked;\n checkbox.title = title;\n checkbox.setAttribute(\"aria-label\", title);\n checkbox.addEventListener(\"change\", () => onChange(checkbox.checked));\n\n return checkbox;\n }\n\n private applyInfoClass() {\n this.element.classList.toggle(\"perf-monitor--info\", this.settings.info);\n }\n\n private applyModeClass() {\n this.element.classList.toggle(\"perf-monitor--compact\", this.mode === \"compact\");\n this.element.classList.toggle(\"perf-monitor--full\", this.mode === \"full\");\n }\n\n private resizeCanvases() {\n const dpr = window.devicePixelRatio || 1;\n\n const sizeAll = (graphs: Iterable<GraphCanvas>) => {\n for (const graph of graphs) {\n const { canvas, ctx } = graph;\n const width = canvas.clientWidth;\n const height = canvas.clientHeight;\n\n if (width === 0 || height === 0) {\n continue;\n }\n\n canvas.width = Math.round(width * dpr);\n canvas.height = Math.round(height * dpr);\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n graph.width = width;\n graph.height = height;\n }\n };\n\n sizeAll(this.graphCanvases.values());\n sizeAll(this.hudGraphs.values());\n }\n}\n\nexport { PerformanceView };\nexport type { PerformanceViewMode, PerformanceViewOptions };\n","/**\n * The floating HUD: the metrics card in the shared dev-panel frame\n * (`mountDevPanel`: docked host, brand label, drag / compact-full / dim discs).\n * The dim disc flips `settings.dim`; the palette comes from `theme`. This is what the React\n * `PerfHud` wraps; a vanilla three app calls it directly.\n */\nimport type { PerformanceMonitor } from \"../core/performance-monitor.ts\";\nimport type { BudgetsInput } from \"./budgets.ts\";\nimport { mountDevPanel } from \"./dev-panel.ts\";\nimport type { DefaultPlacement } from \"./dock-panel.ts\";\nimport { DEFAULT_STORAGE_KEY, HudSettings } from \"./hud-settings.ts\";\nimport { PerformanceView, type PerformanceViewMode } from \"./performance-view.ts\";\nimport type { QualitySource } from \"./quality.ts\";\nimport { HudTheme, type ThemeMode } from \"./theme.ts\";\n\ntype MountPerfHudOptions = {\n /** Start compact (the card) or full (the checkbox list). Default `compact`. */\n mode?: PerformanceViewMode;\n /**\n * `dark`, `light`, or `system` to follow the OS preference. Default\n * `system`. Change it later with the handle's `setTheme`. A pick made in\n * the panel's own theme row (kept in `settings`) sits on top of this.\n */\n theme?: ThemeMode;\n /** localStorage key for the selection and the dock. `null` disables persistence. */\n storageKey?: string | null;\n /** Bring your own; otherwise one is created from `storageKey`. */\n settings?: HudSettings;\n /** Where the card sits on a first visit. Default left edge, centred. */\n defaultPlacement?: DefaultPlacement;\n /** Where the host element goes. Default `document.body`. */\n parent?: HTMLElement;\n /** Append the stylesheet to the document once. Default true. */\n injectStyles?: boolean;\n /** Repaint rate. Default 10. */\n refreshHz?: number;\n /**\n * Limits past which a value turns amber, e.g. `{ calls: 500, targetFps: 120 }`,\n * or the app's budgets file (three-audit's `defineBudgets`), whose `frame` applies.\n * Timing budgets default from `targetFps` (60); `false` turns them all off.\n * Change later with the handle's `setBudgets`.\n */\n budgets?: BudgetsInput | false;\n /**\n * Adaptive quality to show: `@zkmake/three-quality`'s controller (`adaptQuality(…)`) or its React\n * store (`qualityStore`), read by shape, no dependency. Change later with `setQuality`.\n */\n quality?: QualitySource;\n /** Accessible name of the panel. Default `Performance`. */\n label?: string;\n};\n\ntype PerfHudHandle = {\n element: HTMLDivElement;\n view: PerformanceView;\n settings: HudSettings;\n /**\n * `mode` is the consumer layer, `override` the panel's pick, `effective`\n * whichever applies, `resolved` the `dark` / `light` on screen. Subscribe\n * for changes, including the OS preference moving under `system`.\n */\n theme: HudTheme;\n getMode: () => PerformanceViewMode;\n setMode: (mode: PerformanceViewMode) => void;\n /** The consumer layer. The panel's pick, if any, is `settings.theme`. */\n getTheme: () => ThemeMode;\n /** Sets the consumer layer; a pick made in the panel still wins until `settings.setTheme(null)`. */\n setTheme: (mode: ThemeMode) => void;\n /** Replace the budgets; `false` turns them all off. */\n setBudgets: (budgets: BudgetsInput | false | undefined) => void;\n /** Show another quality source, or none. */\n setQuality: (quality: QualitySource | undefined) => void;\n dispose: () => void;\n};\n\nconst mountPerfHud = (\n monitor: PerformanceMonitor,\n options: MountPerfHudOptions = {},\n): PerfHudHandle => {\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n const settings = options.settings ?? new HudSettings({ storageKey });\n const theme = new HudTheme(options.theme ?? \"system\");\n\n const view = new PerformanceView({\n budgets: options.budgets,\n mode: options.mode ?? \"compact\",\n monitor,\n quality: options.quality,\n refreshHz: options.refreshHz,\n settings,\n theme,\n });\n\n // After the view: constructing it restores the panel's stored theme pick.\n const panel = mountDevPanel({\n brand: \"three-meter\",\n content: view.element,\n defaultPlacement: options.defaultPlacement,\n dim: {\n get: () => settings.dim,\n set: (on) => settings.setDim(on),\n subscribe: (listener) => settings.subscribe(listener),\n },\n injectStyles: options.injectStyles,\n label: options.label ?? \"Performance\",\n onToggle: () => setMode(view.getMode() === \"compact\" ? \"full\" : \"compact\"),\n parent: options.parent,\n storageKey,\n theme,\n toggleLabel: \"Toggle full performance metrics\",\n });\n const host = panel.element;\n\n const applyMode = () => {\n host.classList.toggle(\"perf-hud--full\", view.getMode() === \"full\");\n };\n\n const setMode = (mode: PerformanceViewMode) => {\n view.setMode(mode);\n applyMode();\n panel.refresh();\n };\n\n applyMode();\n view.start();\n\n return {\n dispose: () => {\n panel.dispose();\n view.dispose();\n theme.dispose();\n },\n element: host,\n getMode: () => view.getMode(),\n setBudgets: (budgets) => view.setBudgets(budgets),\n setMode,\n setQuality: (quality) => view.setQuality(quality),\n getTheme: () => theme.mode,\n setTheme: (mode: ThemeMode) => theme.setMode(mode),\n settings,\n theme,\n view,\n };\n};\n\nexport { mountPerfHud };\nexport type { MountPerfHudOptions, PerfHudHandle };\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"performance-monitor-Dj5H2VXT.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, rendering backend, GPU.\n * Read from the renderer and the page, never from an import of three.\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, 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\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\nconst readEnvironment = (renderer: PerfRenderer): Environment => {\n const three = readThreeRevision();\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), three }\n : { backend: null, fallback: false, gpu: null, three };\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 three,\n };\n }\n\n return { backend: null, fallback: false, gpu: null, three };\n }\n\n // WebGLRenderer.\n const context = renderer.getContext?.();\n\n return isWebglContext(context)\n ? { backend: webglBackend(context), fallback: false, gpu: webglGpuName(context), three }\n : { backend: null, fallback: false, gpu: null, three };\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 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\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 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, 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));\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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"}
|