@zkmake/three-meter 0.12.0 → 0.13.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 +24 -0
- package/dist/{mount-perf-hud-Cg_iAuFQ.d.ts → mount-perf-hud-C0yuW5JI.d.ts} +50 -2
- package/dist/{mount-perf-hud-CJju0oTc.js → mount-perf-hud-DefBWWKu.js} +99 -4
- package/dist/mount-perf-hud-DefBWWKu.js.map +1 -0
- package/dist/react.d.ts +3 -3
- package/dist/react.js +9 -3
- package/dist/react.js.map +1 -1
- package/dist/styles.css +28 -0
- package/dist/ui.d.ts +3 -3
- package/dist/ui.js +1 -1
- package/package.json +1 -1
- package/dist/mount-perf-hud-CJju0oTc.js.map +0 -1
package/README.md
CHANGED
|
@@ -125,6 +125,7 @@ Canvas is its own React root. To run two canvases on one page, pass the same `st
|
|
|
125
125
|
| `injectStyles` | `true` | Append the stylesheet once per document. |
|
|
126
126
|
| `refreshHz` | `10` | Repaint rate. |
|
|
127
127
|
| `budgets` | timing defaults | Limits past which a value turns amber. See [Budgets](#budgets). |
|
|
128
|
+
| `quality` | none | Adaptive quality to show. See [Adaptive quality](#adaptive-quality). |
|
|
128
129
|
| `label` | `"Performance"` | Accessible name of the panel. |
|
|
129
130
|
|
|
130
131
|
`PerformanceMonitor` and `PerfSampler` take `trackGPU` (default true), `gpuQueryPoolSize` (default 5),
|
|
@@ -194,6 +195,29 @@ mountPerfHud(monitor, { budgets });
|
|
|
194
195
|
|
|
195
196
|
three-meter reads the file's shape (`AppBudgets`) and doesn't depend on three-audit.
|
|
196
197
|
|
|
198
|
+
## Adaptive quality
|
|
199
|
+
|
|
200
|
+
A game that adapts its render quality at runtime with [three-quality](../three-quality) can show
|
|
201
|
+
what it's doing: pass the controller, or in React its store, as `quality`. The card gets one line
|
|
202
|
+
under the grid, `dpr 1.25 · adapting · shadows off`, and the full view a **Quality** row; hover
|
|
203
|
+
either for the level and the last window's p90 frame against the budget.
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
const quality = adaptQuality(renderer); // @zkmake/three-quality
|
|
207
|
+
|
|
208
|
+
mountPerfHud(monitor, { quality });
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
import { qualityStore } from "@zkmake/three-quality/react";
|
|
213
|
+
|
|
214
|
+
<PerfHud quality={qualityStore} />;
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
three-meter reads the shape (`QualitySource`: a `state` with `dpr`, `phase`, `level`, `levels`
|
|
218
|
+
and `tiers`), so neither package depends on the other: the game ships three-quality and loads
|
|
219
|
+
three-meter behind `?debug`. `hud.setQuality(source)` swaps it later.
|
|
220
|
+
|
|
197
221
|
## Top costs
|
|
198
222
|
|
|
199
223
|
The HUD tells you there are 400 draw calls; **top costs** tells you which objects they come from.
|
|
@@ -160,6 +160,31 @@ declare class HudSettings {
|
|
|
160
160
|
private load;
|
|
161
161
|
private persistAndNotify;
|
|
162
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Adaptive quality, as the HUD shows it: one line, `dpr 1.25 · adapting · shadows off`. The source
|
|
165
|
+
* is `@zkmake/three-quality`'s controller (`adaptQuality`) or its React store (`qualityStore`),
|
|
166
|
+
* read by shape like `AppBudgets`, so three-meter needs no dependency and the runtime package none
|
|
167
|
+
* on this dev tool. Anything with a `state` of this shape works.
|
|
168
|
+
*/
|
|
169
|
+
type QualityReading = {
|
|
170
|
+
/** The pixel ratio applied. */
|
|
171
|
+
readonly dpr: number;
|
|
172
|
+
/** What the controller is doing: `warming`, `adapting`, `steady`, `cpu-bound`, `pinned`. */
|
|
173
|
+
readonly phase: string;
|
|
174
|
+
/** 0 is the best rung. */
|
|
175
|
+
readonly level: number;
|
|
176
|
+
readonly levels: number;
|
|
177
|
+
/** The app's tiers by name, `true` while on. */
|
|
178
|
+
readonly tiers?: Readonly<Record<string, boolean>>;
|
|
179
|
+
/** The last window's 90th-percentile frame, ms. */
|
|
180
|
+
readonly p90Ms?: number | null;
|
|
181
|
+
readonly targetFps?: number;
|
|
182
|
+
};
|
|
183
|
+
/** What the HUD's `quality` option takes. `state` is read on each repaint; `null` shows `—`. */
|
|
184
|
+
type QualitySource = {
|
|
185
|
+
readonly state: QualityReading | null;
|
|
186
|
+
};
|
|
187
|
+
declare const formatQuality: (reading: QualityReading | null) => string;
|
|
163
188
|
type PerformanceViewMode = "full" | "compact";
|
|
164
189
|
type PerformanceViewOptions = {
|
|
165
190
|
monitor: PerformanceMonitor;
|
|
@@ -173,6 +198,11 @@ type PerformanceViewOptions = {
|
|
|
173
198
|
* budgets file (`AppBudgets`) applies its `frame`.
|
|
174
199
|
*/
|
|
175
200
|
budgets?: BudgetsInput | false;
|
|
201
|
+
/**
|
|
202
|
+
* Adaptive quality to show, one line in the card and a row in the list: three-quality's
|
|
203
|
+
* controller or React store, read by shape. See {@link QualitySource}.
|
|
204
|
+
*/
|
|
205
|
+
quality?: QualitySource;
|
|
176
206
|
/** Share one between views to keep them in sync; a fresh instance otherwise. */
|
|
177
207
|
settings?: HudSettings;
|
|
178
208
|
/**
|
|
@@ -215,6 +245,12 @@ declare class PerformanceView {
|
|
|
215
245
|
private budgets;
|
|
216
246
|
private budgetsInput;
|
|
217
247
|
private detectedHz;
|
|
248
|
+
private quality;
|
|
249
|
+
private lastQualityText;
|
|
250
|
+
private hudQualityEl;
|
|
251
|
+
private hudQualityValueEl;
|
|
252
|
+
private qualityRowEl;
|
|
253
|
+
private qualityValueEl;
|
|
218
254
|
private readonly hider;
|
|
219
255
|
private readonly hiddenRows;
|
|
220
256
|
private rafId;
|
|
@@ -236,6 +272,8 @@ declare class PerformanceView {
|
|
|
236
272
|
getMode(): PerformanceViewMode;
|
|
237
273
|
/** Replace the budgets; `false` turns them all off. Applies on the next paint. */
|
|
238
274
|
setBudgets(budgets: BudgetsInput | false | undefined): void;
|
|
275
|
+
/** Show another quality source, or none. Applies on the next paint. */
|
|
276
|
+
setQuality(quality: QualitySource | undefined): void;
|
|
239
277
|
dispose(): void;
|
|
240
278
|
private frame;
|
|
241
279
|
private render;
|
|
@@ -252,6 +290,9 @@ declare class PerformanceView {
|
|
|
252
290
|
private costsNote;
|
|
253
291
|
/** The monitor caches the environment once its backend settles; until then this re-reads. */
|
|
254
292
|
private renderFooter;
|
|
293
|
+
/** One string for both the card's line and the list's row; the DOM is touched only on change. */
|
|
294
|
+
private renderQuality;
|
|
295
|
+
private applyQuality;
|
|
255
296
|
/** Budgets follow the detected refresh rate unless `targetFps` was given. */
|
|
256
297
|
private followRefresh;
|
|
257
298
|
private renderHud;
|
|
@@ -302,6 +343,11 @@ type MountPerfHudOptions = {
|
|
|
302
343
|
* Change later with the handle's `setBudgets`.
|
|
303
344
|
*/
|
|
304
345
|
budgets?: BudgetsInput | false;
|
|
346
|
+
/**
|
|
347
|
+
* Adaptive quality to show: `@zkmake/three-quality`'s controller (`adaptQuality(…)`) or its React
|
|
348
|
+
* store (`qualityStore`), read by shape, no dependency. Change later with `setQuality`.
|
|
349
|
+
*/
|
|
350
|
+
quality?: QualitySource;
|
|
305
351
|
/** Accessible name of the panel. Default `Performance`. */
|
|
306
352
|
label?: string;
|
|
307
353
|
};
|
|
@@ -323,8 +369,10 @@ type PerfHudHandle = {
|
|
|
323
369
|
setTheme: (mode: ThemeMode) => void;
|
|
324
370
|
/** Replace the budgets; `false` turns them all off. */
|
|
325
371
|
setBudgets: (budgets: BudgetsInput | false | undefined) => void;
|
|
372
|
+
/** Show another quality source, or none. */
|
|
373
|
+
setQuality: (quality: QualitySource | undefined) => void;
|
|
326
374
|
dispose: () => void;
|
|
327
375
|
};
|
|
328
376
|
declare const mountPerfHud: (monitor: PerformanceMonitor, options?: MountPerfHudOptions) => PerfHudHandle;
|
|
329
|
-
export {
|
|
330
|
-
//# sourceMappingURL=mount-perf-hud-
|
|
377
|
+
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
|
|
@@ -969,6 +969,34 @@ const PERF_HUD_STYLES = `
|
|
|
969
969
|
text-align: right;
|
|
970
970
|
}
|
|
971
971
|
|
|
972
|
+
/* Adaptive quality (three-quality), under the grid: one muted line, the reading in the foreground. */
|
|
973
|
+
.perf-monitor__hud-quality {
|
|
974
|
+
display: flex;
|
|
975
|
+
align-items: center;
|
|
976
|
+
gap: 4px;
|
|
977
|
+
min-width: 0;
|
|
978
|
+
color: var(--perf-muted);
|
|
979
|
+
font-size: 10px;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
.perf-monitor__hud-quality[hidden],
|
|
983
|
+
.perf-monitor__row[hidden] {
|
|
984
|
+
display: none;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
.perf-monitor__hud-quality .perf-monitor__icon {
|
|
988
|
+
width: 11px;
|
|
989
|
+
height: 11px;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
.perf-monitor__hud-quality-value {
|
|
993
|
+
overflow: hidden;
|
|
994
|
+
color: var(--perf-fg);
|
|
995
|
+
font-variant-numeric: tabular-nums lining-nums;
|
|
996
|
+
text-overflow: ellipsis;
|
|
997
|
+
white-space: nowrap;
|
|
998
|
+
}
|
|
999
|
+
|
|
972
1000
|
.perf-monitor__footer {
|
|
973
1001
|
display: flex;
|
|
974
1002
|
align-items: flex-start;
|
|
@@ -1400,7 +1428,7 @@ const mountDevPanel = (options) => {
|
|
|
1400
1428
|
};
|
|
1401
1429
|
// package.json
|
|
1402
1430
|
var name = "@zkmake/three-meter";
|
|
1403
|
-
var version = "0.
|
|
1431
|
+
var version = "0.13.0";
|
|
1404
1432
|
// src/ui/clipboard.ts
|
|
1405
1433
|
/**
|
|
1406
1434
|
* Copy text from a click handler. The async Clipboard API needs a secure
|
|
@@ -1511,6 +1539,23 @@ const METRIC_HELP = {
|
|
|
1511
1539
|
textures: "Textures held in GPU memory. Should level off; a steady climb is a leak, so dispose() what you remove.",
|
|
1512
1540
|
triangles: "Triangles drawn this frame. Mostly GPU cost: use lower-poly models or level of detail to cut it."
|
|
1513
1541
|
};
|
|
1542
|
+
// src/ui/quality.ts
|
|
1543
|
+
const ratio = (dpr) => Number(dpr.toFixed(2)).toString();
|
|
1544
|
+
const tiersOff = (reading) => Object.entries(reading.tiers ?? {}).filter(([, on]) => !on).map(([name]) => name);
|
|
1545
|
+
const formatQuality = (reading) => {
|
|
1546
|
+
if (!reading) return "—";
|
|
1547
|
+
const off = tiersOff(reading);
|
|
1548
|
+
return [
|
|
1549
|
+
`dpr ${ratio(reading.dpr)}`,
|
|
1550
|
+
reading.phase,
|
|
1551
|
+
off.length ? `${off.join(", ")} off` : ""
|
|
1552
|
+
].filter(Boolean).join(" · ");
|
|
1553
|
+
};
|
|
1554
|
+
const qualityTitle = (reading) => {
|
|
1555
|
+
if (!reading) return "Adaptive quality: no controller mounted.";
|
|
1556
|
+
const frame = typeof reading.p90Ms === "number" ? `; p90 frame ${reading.p90Ms.toFixed(1)} ms${reading.targetFps ? ` against ${(1e3 / reading.targetFps).toFixed(1)} ms` : ""}` : "";
|
|
1557
|
+
return `Adaptive quality: level ${reading.level + 1} of ${reading.levels} (1 is the best)${frame}.`;
|
|
1558
|
+
};
|
|
1514
1559
|
// src/ui/report.ts
|
|
1515
1560
|
/**
|
|
1516
1561
|
* A plain-text snapshot of the monitor for bug reports: versions, backend and
|
|
@@ -1908,6 +1953,12 @@ var PerformanceView = class {
|
|
|
1908
1953
|
budgets;
|
|
1909
1954
|
budgetsInput;
|
|
1910
1955
|
detectedHz = null;
|
|
1956
|
+
quality;
|
|
1957
|
+
lastQualityText = "";
|
|
1958
|
+
hudQualityEl;
|
|
1959
|
+
hudQualityValueEl;
|
|
1960
|
+
qualityRowEl;
|
|
1961
|
+
qualityValueEl;
|
|
1911
1962
|
hider = new Hider();
|
|
1912
1963
|
hiddenRows = /* @__PURE__ */ new Map();
|
|
1913
1964
|
rafId = null;
|
|
@@ -1932,11 +1983,13 @@ var PerformanceView = class {
|
|
|
1932
1983
|
this.minIntervalMs = 1e3 / (options.refreshHz ?? 10);
|
|
1933
1984
|
this.budgetsInput = options.budgets;
|
|
1934
1985
|
this.budgets = resolveBudgets(options.budgets);
|
|
1986
|
+
this.quality = options.quality;
|
|
1935
1987
|
this.element = document.createElement("div");
|
|
1936
1988
|
this.element.className = "perf-monitor";
|
|
1937
1989
|
this.applyModeClass();
|
|
1938
1990
|
this.resizeObserver = new ResizeObserver(() => this.resizeCanvases());
|
|
1939
1991
|
this.build();
|
|
1992
|
+
this.applyQuality();
|
|
1940
1993
|
this.applyInfoClass();
|
|
1941
1994
|
this.rebuildHud();
|
|
1942
1995
|
this.onThemeChanged();
|
|
@@ -1967,6 +2020,11 @@ var PerformanceView = class {
|
|
|
1967
2020
|
this.budgets = resolveBudgets(budgets, this.detectedHz);
|
|
1968
2021
|
this.applyBudgetTitles();
|
|
1969
2022
|
}
|
|
2023
|
+
/** Show another quality source, or none. Applies on the next paint. */
|
|
2024
|
+
setQuality(quality) {
|
|
2025
|
+
this.quality = quality;
|
|
2026
|
+
this.applyQuality();
|
|
2027
|
+
}
|
|
1970
2028
|
dispose() {
|
|
1971
2029
|
this.stop();
|
|
1972
2030
|
this.showAllHidden();
|
|
@@ -1986,6 +2044,7 @@ var PerformanceView = class {
|
|
|
1986
2044
|
const sample = this.monitor.getSample();
|
|
1987
2045
|
const stats = this.monitor.getFrameStats();
|
|
1988
2046
|
this.followRefresh(stats.refreshHz);
|
|
2047
|
+
this.renderQuality();
|
|
1989
2048
|
if (this.mode === "compact") {
|
|
1990
2049
|
this.renderHud(sample, stats);
|
|
1991
2050
|
if (this.settings.info) this.renderFooter();
|
|
@@ -2120,6 +2179,25 @@ var PerformanceView = class {
|
|
|
2120
2179
|
this.footerGpuEl.title = gpu;
|
|
2121
2180
|
this.footerHardwareEl.hidden = backend === "" && gpu === "";
|
|
2122
2181
|
}
|
|
2182
|
+
/** One string for both the card's line and the list's row; the DOM is touched only on change. */
|
|
2183
|
+
renderQuality() {
|
|
2184
|
+
if (!this.quality) return;
|
|
2185
|
+
const reading = this.quality.state;
|
|
2186
|
+
const text = formatQuality(reading);
|
|
2187
|
+
const title = qualityTitle(reading);
|
|
2188
|
+
if (`${text}\n${title}` === this.lastQualityText) return;
|
|
2189
|
+
this.lastQualityText = `${text}\n${title}`;
|
|
2190
|
+
this.hudQualityValueEl.textContent = text;
|
|
2191
|
+
this.qualityValueEl.textContent = text;
|
|
2192
|
+
this.hudQualityEl.title = title;
|
|
2193
|
+
this.qualityRowEl.title = title;
|
|
2194
|
+
}
|
|
2195
|
+
applyQuality() {
|
|
2196
|
+
this.hudQualityEl.hidden = !this.quality;
|
|
2197
|
+
this.qualityRowEl.hidden = !this.quality;
|
|
2198
|
+
this.lastQualityText = "";
|
|
2199
|
+
this.renderQuality();
|
|
2200
|
+
}
|
|
2123
2201
|
/** Budgets follow the detected refresh rate unless `targetFps` was given. */
|
|
2124
2202
|
followRefresh(hz) {
|
|
2125
2203
|
if (hz === this.detectedHz) return;
|
|
@@ -2171,7 +2249,12 @@ var PerformanceView = class {
|
|
|
2171
2249
|
this.hudGraphsEl.className = "perf-monitor__hud-graphs";
|
|
2172
2250
|
this.hudGridEl = document.createElement("div");
|
|
2173
2251
|
this.hudGridEl.className = "perf-monitor__hud-grid";
|
|
2174
|
-
|
|
2252
|
+
this.hudQualityEl = document.createElement("div");
|
|
2253
|
+
this.hudQualityEl.className = "perf-monitor__hud-quality";
|
|
2254
|
+
this.hudQualityValueEl = document.createElement("span");
|
|
2255
|
+
this.hudQualityValueEl.className = "perf-monitor__hud-quality-value";
|
|
2256
|
+
this.hudQualityEl.append(createIcon("sliders", "perf-monitor__icon"), this.hudQualityValueEl);
|
|
2257
|
+
hud.append(this.hudGraphsEl, this.hudGridEl, this.hudQualityEl);
|
|
2175
2258
|
const options = document.createElement("div");
|
|
2176
2259
|
options.className = "perf-monitor__section perf-monitor__options";
|
|
2177
2260
|
const themeRow = document.createElement("div");
|
|
@@ -2246,6 +2329,15 @@ var PerformanceView = class {
|
|
|
2246
2329
|
}
|
|
2247
2330
|
const stats = document.createElement("div");
|
|
2248
2331
|
stats.className = "perf-monitor__section";
|
|
2332
|
+
this.qualityRowEl = document.createElement("div");
|
|
2333
|
+
this.qualityRowEl.className = "perf-monitor__row perf-monitor__row--static";
|
|
2334
|
+
const qualityLabel = document.createElement("span");
|
|
2335
|
+
qualityLabel.className = "perf-monitor__label";
|
|
2336
|
+
qualityLabel.textContent = "Quality";
|
|
2337
|
+
this.qualityValueEl = document.createElement("span");
|
|
2338
|
+
this.qualityValueEl.className = "perf-monitor__value";
|
|
2339
|
+
this.qualityValueEl.textContent = "—";
|
|
2340
|
+
this.qualityRowEl.append(createIcon("sliders", "perf-monitor__icon"), qualityLabel, this.qualityValueEl);
|
|
2249
2341
|
for (const config of NUMBERS) {
|
|
2250
2342
|
const rowEl = document.createElement("label");
|
|
2251
2343
|
rowEl.className = "perf-monitor__row";
|
|
@@ -2266,6 +2358,7 @@ var PerformanceView = class {
|
|
|
2266
2358
|
rowEl.append(createIcon(config.icon, "perf-monitor__icon"), label, value, checkbox, hint);
|
|
2267
2359
|
stats.append(rowEl);
|
|
2268
2360
|
}
|
|
2361
|
+
stats.append(this.qualityRowEl);
|
|
2269
2362
|
const footer = document.createElement("div");
|
|
2270
2363
|
footer.className = "perf-monitor__footer";
|
|
2271
2364
|
this.infoCheckbox = this.buildCheckbox(this.settings.info, "Show version and environment in HUD", (on) => this.settings.setInfo(on));
|
|
@@ -2502,6 +2595,7 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
2502
2595
|
budgets: options.budgets,
|
|
2503
2596
|
mode: options.mode ?? "compact",
|
|
2504
2597
|
monitor,
|
|
2598
|
+
quality: options.quality,
|
|
2505
2599
|
refreshHz: options.refreshHz,
|
|
2506
2600
|
settings,
|
|
2507
2601
|
theme
|
|
@@ -2544,6 +2638,7 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
2544
2638
|
getMode: () => view.getMode(),
|
|
2545
2639
|
setBudgets: (budgets) => view.setBudgets(budgets),
|
|
2546
2640
|
setMode,
|
|
2641
|
+
setQuality: (quality) => view.setQuality(quality),
|
|
2547
2642
|
getTheme: () => theme.mode,
|
|
2548
2643
|
setTheme: (mode) => theme.setMode(mode),
|
|
2549
2644
|
settings,
|
|
@@ -2551,6 +2646,6 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
2551
2646
|
view
|
|
2552
2647
|
};
|
|
2553
2648
|
};
|
|
2554
|
-
export {
|
|
2649
|
+
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 };
|
|
2555
2650
|
|
|
2556
|
-
//# sourceMappingURL=mount-perf-hud-
|
|
2651
|
+
//# sourceMappingURL=mount-perf-hud-DefBWWKu.js.map
|