@zkmake/three-meter 0.13.2 → 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 CHANGED
@@ -62,11 +62,16 @@ Three Fiber, on `WebGLRenderer` and `WebGPURenderer`.
62
62
 
63
63
  ## Entry points
64
64
 
65
- | Import | What it is |
66
- | --------------------------- | ---------------------------------------------------------------------------- |
67
- | `@zkmake/three-meter` | `PerformanceMonitor`. `begin()` and `end()` bracket a frame. No DOM. |
68
- | `@zkmake/three-meter/ui` | `mountPerfHud(monitor, options)`, the dockable card. Vanilla DOM. |
69
- | `@zkmake/three-meter/react` | `PerfSampler` inside `<Canvas>`, `PerfHud` outside it. Peers: react and r3f. |
65
+ | Import | What it is |
66
+ | --------------------------- | ----------------------------------------------------------------------------------------------------- |
67
+ | `@zkmake/three-meter` | `PerformanceMonitor`. `begin()` and `end()` bracket a frame. No DOM. |
68
+ | `@zkmake/three-meter/ui` | `mountPerfHud(monitor, options)`, the dockable card. Vanilla DOM. |
69
+ | `@zkmake/three-meter/react` | `PerfSampler` inside `<Canvas>`, `PerfHud` outside it, and `frameworkVersions`. Peers: react and r3f. |
70
+
71
+ `/ui` also exports the pieces under the HUD, for a host composing its own panel: `PerformanceView`
72
+ (the card's body), `HudSettings`, `HudTheme`, `mountDevPanel`, `dockPanel`, `injectStyles` and
73
+ `PERF_HUD_STYLES`, `resolveBudgets`, `formatReport` and `formatQuality`. The core also exports
74
+ `RingBuffer`, and `/react` `createPerfMonitorStore`.
70
75
 
71
76
  ## Vanilla three
72
77
 
@@ -92,6 +97,10 @@ monitor.dispose();
92
97
  If you run your own loop, call `monitor.begin()` before the frame's work and `monitor.end()` after
93
98
  the render.
94
99
 
100
+ Without the HUD, read the monitor: `monitor.getSample()` is the last frame (`fps`, `cpu`,
101
+ `gpu: { ms, available }`, `render` counts, `resources`), and `monitor.getHistory("fps" | "cpu" |
102
+ "gpu")` the last `historySize` values the graphs draw.
103
+
95
104
  ## React Three Fiber
96
105
 
97
106
  ```tsx
@@ -109,7 +118,11 @@ import { PerfHud, PerfSampler } from "@zkmake/three-meter/react";
109
118
 
110
119
  `PerfHud` must sit outside `<Canvas>`, because Fiber treats HTML under it as three objects. The two
111
120
  components find each other through a small shared store. A React context can't do this, since the
112
- Canvas is its own React root. To run two canvases on one page, pass the same `store` prop to both.
121
+ Canvas is its own React root. To run two canvases on one page, give each canvas's `PerfSampler` and
122
+ its `PerfHud` the same `store` prop, one `createPerfMonitorStore()` per pair.
123
+
124
+ `theme`, `mode`, `label`, `budgets` (by value) and `quality` apply live. `defaultPlacement` (by
125
+ value), `storageKey`, `parent`, `refreshHz`, `injectStyles` and `store` remount the HUD.
113
126
 
114
127
  ## Options
115
128
 
@@ -124,9 +137,10 @@ Canvas is its own React root. To run two canvases on one page, pass the same `st
124
137
  | `parent` | `document.body` | Where the host element is appended. |
125
138
  | `injectStyles` | `true` | Append the stylesheet once per document. |
126
139
  | `refreshHz` | `10` | Repaint rate. |
127
- | `budgets` | timing defaults | Limits past which a value turns amber. See [Budgets](#budgets). |
140
+ | `budgets` | timing defaults | Limits past which a value turns amber; `false` for none. See [Budgets](#budgets). |
128
141
  | `quality` | none | Adaptive quality to show. See [Adaptive quality](#adaptive-quality). |
129
142
  | `label` | `"Performance"` | Accessible name of the panel. |
143
+ | `settings` | from `storageKey` | Your own `HudSettings`, e.g. with `defaults` for what a first visit shows. Not on `PerfHud`. |
130
144
 
131
145
  `PerformanceMonitor` and `PerfSampler` take `trackGPU` (default true), `gpuQueryPoolSize` (default 5),
132
146
  `historySize` (default 120) and `frameStatsSize` (default 1000).
@@ -177,10 +191,16 @@ they follow the detected refresh rate, so a 120 Hz display gets 8.3 ms budgets (
177
191
  detected). Counts have no default, since what's reasonable depends on the scene. Set your own:
178
192
 
179
193
  ```ts
180
- mountPerfHud(monitor, { budgets: { targetFps: 120, calls: 500, triangles: 2_000_000 } });
194
+ const hud = mountPerfHud(monitor, {
195
+ budgets: { targetFps: 120, calls: 500, triangles: 2_000_000 },
196
+ });
181
197
  hud.setBudgets({ gpu: null }); // null drops a default; false drops them all
182
198
  ```
183
199
 
200
+ A budget goes on any row by its key: `fps`, `cpu`, `gpu`, `headroom`, `refresh`, `low` (1% low),
201
+ `p99`, `hitches`, `calls`, `triangles`, `lines`, `points`, `passes`, `geometries`, `textures`,
202
+ `shaders`. `fps`, `low` and `headroom` are floors (amber below); the rest are ceilings.
203
+
184
204
  `PerfHud` takes the same `budgets` prop and applies changes live.
185
205
 
186
206
  An app that keeps its budgets in one file with [three-audit](../three-audit)'s `defineBudgets`
@@ -261,7 +281,7 @@ the current metrics, the stutter stats, the top three meshes, the viewport and t
261
281
  `formatReport(monitor)` from `@zkmake/three-meter/ui` returns the same text.
262
282
 
263
283
  ```text
264
- three-meter v0.10.0 · three r186 · WebGL2 · Apple M4 Max
284
+ three-meter v0.10.0 · three r186 · React 19.3.1 · R3F 9.8.2 · WebGL2 · Apple M4 Max
265
285
  FPS 120 · 1% low 98 · p99 10.2 ms · hitches 3 / 1,000 frames
266
286
  CPU 0.2 ms · GPU 0.7 ms · refresh 120 Hz · headroom 7.6 ms
267
287
  Calls 1 · passes 1 · triangles 24,000 · lines 0 · points 0
@@ -273,16 +293,22 @@ Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) …
273
293
 
274
294
  ## Environment
275
295
 
276
- The full view's footer has two rows: the three-meter version (linked to its release notes) and the
277
- three revision, then the backend and the GPU name. An amber `WebGL2 fallback` badge means
296
+ The full view's footer has the three-meter version (linked to its release notes) and the three
297
+ revision, then the React and React Three Fiber versions when the app has them, then the backend and
298
+ the GPU name. An amber `WebGL2 fallback` badge means
278
299
  `WebGPURenderer` couldn't get WebGPU. Its checkbox (off by default) shows the footer in the compact
279
300
  HUD too. The same data is on the monitor:
280
301
 
281
302
  ```ts
282
303
  monitor.getEnvironment();
283
- // { three: "186", backend: "webgpu", fallback: false, gpu: "Apple metal-3" }
304
+ // { three: "186", react: "19.3.1", r3f: "9.8.2", backend: "webgpu", fallback: false, gpu: "Apple metal-3" }
284
305
  ```
285
306
 
307
+ `react` and `r3f` come from `PerfSampler`, which reads them from `frameworkVersions` in
308
+ `@zkmake/three-meter/react`, and three-devtools' `<DevTools>` does the same. The core imports neither, so a
309
+ monitor you make yourself takes them as an option, `new PerformanceMonitor({ renderer, frameworks })`, and
310
+ otherwise reports what React DevTools can tell, or `null`: on a vanilla app, that's the right answer.
311
+
286
312
  `backend` is `null` until `WebGPURenderer.init()` settles. `gpu` comes from WebGPU's adapter info or
287
313
  WebGL's unmasked renderer string, and is `null` where the browser hides it. Chrome's WebGPU gives
288
314
  vendor and architecture, not the model.
@@ -293,6 +319,10 @@ On WebGL2 the monitor uses `EXT_disjoint_timer_query_webgl2`, which Chrome and E
293
319
  and Firefox don't, so the GPU row shows `—` and `sample.gpu.available` is `false`.
294
320
 
295
321
  On WebGPU, construct the renderer with `trackTimestamp: true`. Timings resolve a frame or two late.
322
+ The number is how long the GPU was busy in the frame, with overlapping passes counted once. three's
323
+ own `resolveTimestampsAsync` sums every pass, which on tile-based GPUs (Apple, most mobile) reads
324
+ a post-processing stack at several times its cost; the meter falls back to that sum when three's
325
+ timestamp pool isn't the shape it knows.
296
326
 
297
327
  ## Theme
298
328
 
@@ -325,7 +355,8 @@ palette, override the `--perf-*` custom properties (`bg`, `fg`, `fg-dim`, `muted
325
355
 
326
356
  The HUD's frame is `mountDevPanel` from `@zkmake/three-meter/ui`: the docked host, a card with a
327
357
  brand label on top, and the drag / expand-compact / dim-on-leave discs. Other zkmake dev panels
328
- (`@zkmake/three-textures`) mount through it too, so they all dock, wake, dim and theme the same way.
358
+ (`@zkmake/three-textures`, `@zkmake/three-cameras`) mount through it too, so they all dock, wake,
359
+ dim and theme the same way.
329
360
  A tool of your own can use it:
330
361
 
331
362
  ```ts
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as GpuTiming, c as RenderBackend, d as Sample, f as SceneCost, i as FrameStats, l as RenderCounts, n as CostEntry, o as PerfRenderer, p as TimingMetric, r as Environment, s as PerformanceMonitorOptions, t as PerformanceMonitor, u as ResourceCounts } from "./performance-monitor-CrYmNIdv.js";
1
+ import { a as FrameworkVersions, c as PerformanceMonitorOptions, d as ResourceCounts, f as Sample, i as FrameStats, l as RenderBackend, m as TimingMetric, n as CostEntry, o as GpuTiming, p as SceneCost, r as Environment, s as PerfRenderer, t as PerformanceMonitor, u as RenderCounts } from "./performance-monitor-CH3GOcIm.js";
2
2
  /**
3
3
  * Fixed-capacity numeric ring buffer. Backs the per-frame timing history the
4
4
  * view reads to draw sparklines. Push is O(1); `toArray()` returns values in
@@ -28,5 +28,5 @@ type AnimationLoop<Args extends unknown[]> = (...args: Args) => void;
28
28
  * ```
29
29
  */
30
30
  declare function wrapAnimationLoop<Args extends unknown[]>(monitor: PerformanceMonitor, loop: AnimationLoop<Args>): AnimationLoop<Args>;
31
- export { type CostEntry, type Environment, type FrameStats, type GpuTiming, type PerfRenderer, PerformanceMonitor, type PerformanceMonitorOptions, type RenderBackend, type RenderCounts, type ResourceCounts, RingBuffer, type Sample, type SceneCost, type TimingMetric, wrapAnimationLoop };
31
+ export { type CostEntry, type Environment, type FrameStats, type FrameworkVersions, type GpuTiming, type PerfRenderer, PerformanceMonitor, type PerformanceMonitorOptions, type RenderBackend, type RenderCounts, type ResourceCounts, RingBuffer, type Sample, type SceneCost, type TimingMetric, wrapAnimationLoop };
32
32
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as RingBuffer, t as PerformanceMonitor } from "./performance-monitor-Dj5H2VXT.js";
1
+ import { n as RingBuffer, t as PerformanceMonitor } from "./performance-monitor-Bz3ukv7t.js";
2
2
  // src/core/wrap-animation-loop.ts
3
3
  /**
4
4
  * Wrap a `renderer.setAnimationLoop` callback so each tick is one frame:
@@ -1,4 +1,4 @@
1
- import { p as TimingMetric, t as PerformanceMonitor } from "./performance-monitor-CrYmNIdv.js";
1
+ import { m as TimingMetric, t as PerformanceMonitor } from "./performance-monitor-CH3GOcIm.js";
2
2
  /**
3
3
  * Budgets: the value each metric should stay within. A reading past its
4
4
  * budget turns amber in the card, and the FPS / CPU / GPU graphs draw the
@@ -234,6 +234,7 @@ declare class PerformanceView {
234
234
  private infoCheckbox;
235
235
  private readonly themeRadios;
236
236
  private footerThreeEl;
237
+ private footerFrameworksEl;
237
238
  private footerHardwareEl;
238
239
  private footerBackendEl;
239
240
  private footerGpuEl;
@@ -375,4 +376,4 @@ type PerfHudHandle = {
375
376
  };
376
377
  declare const mountPerfHud: (monitor: PerformanceMonitor, options?: MountPerfHudOptions) => PerfHudHandle;
377
378
  export { BudgetsInput as A, DockPanelOptions as C, AppBudgets as D, dockPanel as E, resolveBudgets as M, BudgetKey as O, DockHandle as S, ScreenEdge as T, THEME_MODES as _, PerformanceViewMode as a, DefaultPlacement as b, QualitySource as c, HudDefaults as d, HudSelection as f, ResolvedTheme as g, HudTheme as h, PerformanceView as i, ResolvedBudgets as j, Budgets as k, formatQuality as l, HudSettingsOptions as m, PerfHudHandle as n, PerformanceViewOptions as o, HudSettings as p, mountPerfHud as r, QualityReading as s, MountPerfHudOptions as t, DEFAULT_STORAGE_KEY as u, ThemeMode as v, Placement as w, DockAlign as x, isThemeMode as y };
378
- //# sourceMappingURL=mount-perf-hud-C0yuW5JI.d.ts.map
379
+ //# sourceMappingURL=mount-perf-hud-Be8DNtkg.d.ts.map
@@ -1428,7 +1428,7 @@ const mountDevPanel = (options) => {
1428
1428
  };
1429
1429
  // package.json
1430
1430
  var name = "@zkmake/three-meter";
1431
- var version = "0.13.2";
1431
+ var version = "0.14.0";
1432
1432
  // src/ui/clipboard.ts
1433
1433
  /**
1434
1434
  * Copy text from a click handler. The async Clipboard API needs a secure
@@ -1568,6 +1568,9 @@ const BACKEND_LABELS = {
1568
1568
  webgpu: "WebGPU"
1569
1569
  };
1570
1570
  /** `WebGPU`, `WebGL2`, `WebGL2 fallback`, or `""` before the backend settles. */
1571
+ const threeLabel = ({ three }) => three ? `three r${three}` : "";
1572
+ /** `React 19.3.1 · R3F 9.8.2`, as far as the monitor can tell. */
1573
+ const frameworksLabel = ({ react, r3f }) => [react ? `React ${react}` : "", r3f ? `R3F ${r3f}` : ""].filter(Boolean).join(" · ");
1571
1574
  const backendLabel = (environment) => {
1572
1575
  if (!environment.backend) return "";
1573
1576
  const label = BACKEND_LABELS[environment.backend];
@@ -1584,7 +1587,8 @@ const renderReport = ({ cost, environment, page, sample, stats }) => {
1584
1587
  const { render, resources } = sample;
1585
1588
  const heading = [
1586
1589
  `three-meter v${version}`,
1587
- environment.three ? `three r${environment.three}` : "",
1590
+ threeLabel(environment),
1591
+ frameworksLabel(environment),
1588
1592
  backendLabel(environment),
1589
1593
  environment.gpu ?? ""
1590
1594
  ].filter(Boolean);
@@ -1942,6 +1946,7 @@ var PerformanceView = class {
1942
1946
  infoCheckbox;
1943
1947
  themeRadios = /* @__PURE__ */ new Map();
1944
1948
  footerThreeEl;
1949
+ footerFrameworksEl;
1945
1950
  footerHardwareEl;
1946
1951
  footerBackendEl;
1947
1952
  footerGpuEl;
@@ -2164,13 +2169,16 @@ var PerformanceView = class {
2164
2169
  /** The monitor caches the environment once its backend settles; until then this re-reads. */
2165
2170
  renderFooter() {
2166
2171
  const environment = this.monitor.getEnvironment();
2167
- const three = environment.three ? `three r${environment.three}` : "";
2172
+ const three = threeLabel(environment);
2173
+ const frameworks = frameworksLabel(environment);
2168
2174
  const backend = backendLabel(environment);
2169
2175
  const gpu = environment.gpu ?? "";
2170
- const key = `${three}\n${backend}\n${gpu}`;
2176
+ const key = `${three}\n${frameworks}\n${backend}\n${gpu}`;
2171
2177
  if (key === this.lastEnvironmentKey) return;
2172
2178
  this.lastEnvironmentKey = key;
2173
2179
  this.footerThreeEl.textContent = three;
2180
+ this.footerFrameworksEl.textContent = frameworks;
2181
+ this.footerFrameworksEl.hidden = frameworks === "";
2174
2182
  this.footerBackendEl.textContent = backend;
2175
2183
  this.footerBackendEl.hidden = backend === "";
2176
2184
  this.footerBackendEl.title = environment.fallback ? "WebGPURenderer couldn't get WebGPU and fell back to WebGL2" : "";
@@ -2375,6 +2383,9 @@ var PerformanceView = class {
2375
2383
  release.textContent = `three-meter v${version}`;
2376
2384
  this.footerThreeEl = document.createElement("span");
2377
2385
  softwareRow.append(release, this.footerThreeEl);
2386
+ this.footerFrameworksEl = document.createElement("div");
2387
+ this.footerFrameworksEl.className = "perf-monitor__footer-row";
2388
+ this.footerFrameworksEl.hidden = true;
2378
2389
  this.footerHardwareEl = document.createElement("div");
2379
2390
  this.footerHardwareEl.className = "perf-monitor__footer-row";
2380
2391
  this.footerHardwareEl.hidden = true;
@@ -2383,7 +2394,7 @@ var PerformanceView = class {
2383
2394
  this.footerGpuEl = document.createElement("span");
2384
2395
  this.footerGpuEl.className = "perf-monitor__footer-gpu";
2385
2396
  this.footerHardwareEl.append(this.footerBackendEl, this.footerGpuEl);
2386
- footerBody.append(softwareRow, this.footerHardwareEl);
2397
+ footerBody.append(softwareRow, this.footerFrameworksEl, this.footerHardwareEl);
2387
2398
  footer.append(createIcon("info", "perf-monitor__icon"), footerBody, this.infoCheckbox);
2388
2399
  this.element.append(hud, options, graphs, stats, this.buildCosts(), footer);
2389
2400
  }
@@ -2648,4 +2659,4 @@ const mountPerfHud = (monitor, options = {}) => {
2648
2659
  };
2649
2660
  export { mountDevPanel as a, dockPanel as c, HudTheme as d, THEME_MODES as f, formatQuality as i, DEFAULT_STORAGE_KEY as l, resolveBudgets as m, PerformanceView as n, PERF_HUD_STYLES as o, isThemeMode as p, formatReport as r, injectStyles as s, mountPerfHud as t, HudSettings as u };
2650
2661
 
2651
- //# sourceMappingURL=mount-perf-hud-s8ezVm_5.js.map
2662
+ //# sourceMappingURL=mount-perf-hud-BtucU7nZ.js.map