@zkmake/three-meter 0.6.0 → 0.7.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 +19 -1
- package/dist/{mount-perf-hud-CmINOH3T.d.ts → mount-perf-hud-6LeJ93Jg.d.ts} +47 -2
- package/dist/{mount-perf-hud-C5wdjgmM.js → mount-perf-hud-DqY_tP6s.js} +148 -36
- package/dist/mount-perf-hud-DqY_tP6s.js.map +1 -0
- package/dist/react.d.ts +5 -4
- package/dist/react.js +13 -4
- package/dist/react.js.map +1 -1
- package/dist/styles.css +9 -5
- package/dist/ui.d.ts +3 -3
- package/dist/ui.js +2 -2
- package/package.json +1 -1
- package/dist/mount-perf-hud-C5wdjgmM.js.map +0 -1
package/README.md
CHANGED
|
@@ -89,6 +89,7 @@ Canvas is its own React root. To run two canvases on one page, pass the same `st
|
|
|
89
89
|
| `parent` | `document.body` | Where the host element is appended. |
|
|
90
90
|
| `injectStyles` | `true` | Append the stylesheet once per document. |
|
|
91
91
|
| `refreshHz` | `10` | Repaint rate. |
|
|
92
|
+
| `budgets` | timing defaults | Limits past which a value turns amber. See [Budgets](#budgets). |
|
|
92
93
|
|
|
93
94
|
`PerformanceMonitor` and `PerfSampler` take `trackGPU` (default true), `gpuQueryPoolSize` (default 5),
|
|
94
95
|
`historySize` (default 120) and `frameStatsSize` (default 1000).
|
|
@@ -118,7 +119,7 @@ hud.theme.subscribe(() => syncMyPageWith(hud.theme.resolved));
|
|
|
118
119
|
|
|
119
120
|
The resolved theme lands on `data-theme` of `.perf-hud` and `.perf-monitor`. To restyle either
|
|
120
121
|
palette, override the `--perf-*` custom properties (`bg`, `fg`, `fg-dim`, `muted`, `row`,
|
|
121
|
-
`border`, `accent`, `shadow`) on those selectors.
|
|
122
|
+
`border`, `accent`, `warn`, `shadow`) on those selectors. `warn` is the over-budget amber.
|
|
122
123
|
|
|
123
124
|
## GPU timing
|
|
124
125
|
|
|
@@ -149,6 +150,23 @@ Every row has an icon, its value, and a checkbox on the right that puts it in th
|
|
|
149
150
|
a label for a one-line explanation of the metric and what a bad reading means, or tick **explain
|
|
150
151
|
metrics** to show them all under the rows (also works on touch).
|
|
151
152
|
|
|
153
|
+
## Budgets
|
|
154
|
+
|
|
155
|
+
A value past its budget turns amber in the full view and the compact HUD, and its tooltip names the
|
|
156
|
+
budget. The FPS, CPU and GPU graphs draw the budget as a dashed line once the series reaches it; the
|
|
157
|
+
scale never stretches to fit it, so a quiet graph keeps its detail.
|
|
158
|
+
|
|
159
|
+
Timing budgets come from `targetFps` (60 by default): FPS at least 95% of it, 1% low at least half,
|
|
160
|
+
CPU and GPU within one frame (16.7 ms), frame p99 within one and a half. Counts have no default,
|
|
161
|
+
since what's reasonable depends on the scene. Set your own:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
mountPerfHud(monitor, { budgets: { targetFps: 120, calls: 500, triangles: 2_000_000 } });
|
|
165
|
+
hud.setBudgets({ gpu: null }); // null drops a default; false drops them all
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`PerfHud` takes the same `budgets` prop and applies changes live.
|
|
169
|
+
|
|
152
170
|
## Stutter
|
|
153
171
|
|
|
154
172
|
Average FPS hides a hitch every few seconds. Three rows in the full view catch it, and any of them
|
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import { d as TimingMetric, t as PerformanceMonitor } from "./performance-monitor-CBN05_BC.js";
|
|
2
|
+
//#region src/ui/budgets.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Budgets: the value each metric should stay within. A reading past its
|
|
5
|
+
* budget turns amber in the card, and the FPS / CPU / GPU graphs draw the
|
|
6
|
+
* line once the series reaches it. `fps` and `low` are floors (worse below);
|
|
7
|
+
* everything else is a ceiling (worse above).
|
|
8
|
+
*
|
|
9
|
+
* Timing budgets default from `targetFps` (60): FPS at least 95% of it, 1%
|
|
10
|
+
* low at least half, CPU and GPU within one frame, p99 within one and a half.
|
|
11
|
+
* Counts have no default, since what's reasonable depends on the scene. Pass
|
|
12
|
+
* `null` for a key to drop its default, or `false` for no budgets at all.
|
|
13
|
+
*/
|
|
14
|
+
type BudgetKey = "calls" | "cpu" | "fps" | "geometries" | "gpu" | "hitches" | "lines" | "low" | "p99" | "passes" | "points" | "shaders" | "textures" | "triangles";
|
|
15
|
+
type Budgets = Partial<Record<BudgetKey, number | null>> & {
|
|
16
|
+
/** The frame rate the defaults are derived from. Default 60. */
|
|
17
|
+
targetFps?: number;
|
|
18
|
+
};
|
|
19
|
+
type ResolvedBudgets = {
|
|
20
|
+
/** Frame budget in ms, `1000 / targetFps`. */
|
|
21
|
+
frameMs: number;
|
|
22
|
+
limits: Partial<Record<BudgetKey, number>>;
|
|
23
|
+
};
|
|
24
|
+
declare const resolveBudgets: (budgets: Budgets | false | undefined) => ResolvedBudgets;
|
|
25
|
+
//#endregion
|
|
2
26
|
//#region src/ui/dock-panel.d.ts
|
|
3
27
|
declare const EDGES: readonly ["left", "right", "top", "bottom"];
|
|
4
28
|
type ScreenEdge = (typeof EDGES)[number];
|
|
@@ -131,6 +155,11 @@ type PerformanceViewOptions = {
|
|
|
131
155
|
mode?: PerformanceViewMode;
|
|
132
156
|
/** Repaint rate. Default 10. */
|
|
133
157
|
refreshHz?: number;
|
|
158
|
+
/**
|
|
159
|
+
* Limits past which a value turns amber. Timing budgets default from
|
|
160
|
+
* `targetFps` (60); `false` turns them all off. See {@link Budgets}.
|
|
161
|
+
*/
|
|
162
|
+
budgets?: Budgets | false;
|
|
134
163
|
/** Share one between views to keep them in sync; a fresh instance otherwise. */
|
|
135
164
|
settings?: HudSettings;
|
|
136
165
|
/**
|
|
@@ -171,6 +200,7 @@ declare class PerformanceView {
|
|
|
171
200
|
private hudGridEl;
|
|
172
201
|
private readonly hudGraphs;
|
|
173
202
|
private readonly hudNumberEls;
|
|
203
|
+
private budgets;
|
|
174
204
|
private rafId;
|
|
175
205
|
private lastPaintAt;
|
|
176
206
|
private readonly resizeObserver;
|
|
@@ -180,12 +210,19 @@ declare class PerformanceView {
|
|
|
180
210
|
stop(): void;
|
|
181
211
|
setMode(mode: PerformanceViewMode): void;
|
|
182
212
|
getMode(): PerformanceViewMode;
|
|
213
|
+
/** Replace the budgets; `false` turns them all off. Applies on the next paint. */
|
|
214
|
+
setBudgets(budgets: Budgets | false | undefined): void;
|
|
183
215
|
dispose(): void;
|
|
184
216
|
private frame;
|
|
185
217
|
private render;
|
|
186
218
|
/** The monitor caches the environment once its backend settles; until then this re-reads. */
|
|
187
219
|
private renderFooter;
|
|
188
220
|
private renderHud;
|
|
221
|
+
private paintNumber;
|
|
222
|
+
/** Value and sparkline, with the budget line drawn once the series reaches it. */
|
|
223
|
+
private paintGraph;
|
|
224
|
+
/** Each value's tooltip names its budget, so amber explains itself. */
|
|
225
|
+
private applyBudgetTitles;
|
|
189
226
|
private formatTiming;
|
|
190
227
|
private build;
|
|
191
228
|
private rebuildHud;
|
|
@@ -221,6 +258,12 @@ type MountPerfHudOptions = {
|
|
|
221
258
|
injectStyles?: boolean;
|
|
222
259
|
/** Repaint rate. Default 10. */
|
|
223
260
|
refreshHz?: number;
|
|
261
|
+
/**
|
|
262
|
+
* Limits past which a value turns amber, e.g. `{ calls: 500, targetFps: 120 }`.
|
|
263
|
+
* Timing budgets default from `targetFps` (60); `false` turns them all off.
|
|
264
|
+
* Change later with the handle's `setBudgets`.
|
|
265
|
+
*/
|
|
266
|
+
budgets?: Budgets | false;
|
|
224
267
|
/** Accessible name of the panel. Default `Performance`. */
|
|
225
268
|
label?: string;
|
|
226
269
|
};
|
|
@@ -240,9 +283,11 @@ type PerfHudHandle = {
|
|
|
240
283
|
getTheme: () => ThemeMode;
|
|
241
284
|
/** Sets the consumer layer; a pick made in the panel still wins until `settings.setTheme(null)`. */
|
|
242
285
|
setTheme: (mode: ThemeMode) => void;
|
|
286
|
+
/** Replace the budgets; `false` turns them all off. */
|
|
287
|
+
setBudgets: (budgets: Budgets | false | undefined) => void;
|
|
243
288
|
dispose: () => void;
|
|
244
289
|
};
|
|
245
290
|
declare const mountPerfHud: (monitor: PerformanceMonitor, options?: MountPerfHudOptions) => PerfHudHandle;
|
|
246
291
|
//#endregion
|
|
247
|
-
export { dockPanel as C, ScreenEdge as S, DefaultPlacement as _, PerformanceViewMode as a, DockPanelOptions as b, HudDefaults as c, HudSettingsOptions as d, HudTheme as f, isThemeMode as g, ThemeMode as h, PerformanceView as i, HudSelection as l, THEME_MODES as m, PerfHudHandle as n, PerformanceViewOptions as o, ResolvedTheme as p, mountPerfHud as r, DEFAULT_STORAGE_KEY as s, MountPerfHudOptions as t, HudSettings as u, DockAlign as v, Placement as x, DockHandle as y };
|
|
248
|
-
//# sourceMappingURL=mount-perf-hud-
|
|
292
|
+
export { dockPanel as C, resolveBudgets as D, ResolvedBudgets as E, ScreenEdge as S, Budgets as T, DefaultPlacement as _, PerformanceViewMode as a, DockPanelOptions as b, HudDefaults as c, HudSettingsOptions as d, HudTheme as f, isThemeMode as g, ThemeMode as h, PerformanceView as i, HudSelection as l, THEME_MODES as m, PerfHudHandle as n, PerformanceViewOptions as o, ResolvedTheme as p, mountPerfHud as r, DEFAULT_STORAGE_KEY as s, MountPerfHudOptions as t, HudSettings as u, DockAlign as v, BudgetKey as w, Placement as x, DockHandle as y };
|
|
293
|
+
//# sourceMappingURL=mount-perf-hud-6LeJ93Jg.d.ts.map
|
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
//#region src/ui/budgets.ts
|
|
2
|
+
const DEFAULT_TARGET_FPS = 60;
|
|
3
|
+
const FLOORS = /* @__PURE__ */ new Set(["fps", "low"]);
|
|
4
|
+
const resolveBudgets = (budgets) => {
|
|
5
|
+
const targetFps = budgets && budgets.targetFps || DEFAULT_TARGET_FPS;
|
|
6
|
+
const frameMs = 1e3 / targetFps;
|
|
7
|
+
if (budgets === false) return {
|
|
8
|
+
frameMs,
|
|
9
|
+
limits: {}
|
|
10
|
+
};
|
|
11
|
+
const merged = {
|
|
12
|
+
cpu: frameMs,
|
|
13
|
+
fps: targetFps * .95,
|
|
14
|
+
gpu: frameMs,
|
|
15
|
+
low: targetFps / 2,
|
|
16
|
+
p99: frameMs * 1.5,
|
|
17
|
+
...budgets
|
|
18
|
+
};
|
|
19
|
+
const limits = {};
|
|
20
|
+
for (const [key, limit] of Object.entries(merged)) if (key !== "targetFps" && typeof limit === "number" && Number.isFinite(limit)) limits[key] = limit;
|
|
21
|
+
return {
|
|
22
|
+
frameMs,
|
|
23
|
+
limits
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
const isFloor = (key) => FLOORS.has(key);
|
|
27
|
+
/** `null` means the value isn't known yet (no frames, no GPU timer), which is never over. */
|
|
28
|
+
const isOverBudget = (resolved, key, value) => {
|
|
29
|
+
const limit = resolved.limits[key];
|
|
30
|
+
if (limit === void 0 || value === null) return false;
|
|
31
|
+
return isFloor(key) ? value < limit : value > limit;
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
1
34
|
//#region src/ui/theme.ts
|
|
2
35
|
const THEME_MODES = [
|
|
3
36
|
"dark",
|
|
@@ -493,7 +526,7 @@ const createIcon = (name, className) => {
|
|
|
493
526
|
};
|
|
494
527
|
//#endregion
|
|
495
528
|
//#region package.json
|
|
496
|
-
var version = "0.
|
|
529
|
+
var version = "0.7.0";
|
|
497
530
|
//#endregion
|
|
498
531
|
//#region src/ui/clipboard.ts
|
|
499
532
|
/**
|
|
@@ -639,7 +672,7 @@ const formatReport = (monitor) => renderReport({
|
|
|
639
672
|
});
|
|
640
673
|
//#endregion
|
|
641
674
|
//#region src/ui/sparkline.ts
|
|
642
|
-
function drawSparkline(ctx, width, height, values, style) {
|
|
675
|
+
function drawSparkline(ctx, width, height, values, style, guide) {
|
|
643
676
|
ctx.clearRect(0, 0, width, height);
|
|
644
677
|
if (values.length < 2) return;
|
|
645
678
|
let max = 0;
|
|
@@ -658,6 +691,17 @@ function drawSparkline(ctx, width, height, values, style) {
|
|
|
658
691
|
ctx.closePath();
|
|
659
692
|
ctx.fillStyle = style.fill;
|
|
660
693
|
ctx.fill();
|
|
694
|
+
if (guide && guide.value > 0 && guide.value <= range) {
|
|
695
|
+
const y = Math.round(toY(guide.value)) + .5;
|
|
696
|
+
ctx.save();
|
|
697
|
+
ctx.setLineDash([3, 3]);
|
|
698
|
+
ctx.strokeStyle = guide.color;
|
|
699
|
+
ctx.beginPath();
|
|
700
|
+
ctx.moveTo(0, y);
|
|
701
|
+
ctx.lineTo(width, y);
|
|
702
|
+
ctx.stroke();
|
|
703
|
+
ctx.restore();
|
|
704
|
+
}
|
|
661
705
|
}
|
|
662
706
|
//#endregion
|
|
663
707
|
//#region src/ui/performance-view.ts
|
|
@@ -742,92 +786,130 @@ const readTiming = (sample, metric) => {
|
|
|
742
786
|
}
|
|
743
787
|
};
|
|
744
788
|
const isTimingAvailable = (sample, metric) => metric !== "gpu" || sample.gpu.available;
|
|
789
|
+
const whole = (value) => Math.round(value).toString();
|
|
790
|
+
const tenths = (value) => value.toFixed(1);
|
|
745
791
|
const NUMBERS = [
|
|
746
792
|
{
|
|
793
|
+
format: whole,
|
|
747
794
|
icon: "gauge",
|
|
748
795
|
key: "fps",
|
|
749
796
|
label: "FPS",
|
|
750
|
-
|
|
797
|
+
value: (sample) => sample.fps || null
|
|
751
798
|
},
|
|
752
799
|
{
|
|
800
|
+
format: formatCount,
|
|
753
801
|
icon: "layers",
|
|
754
802
|
key: "calls",
|
|
755
803
|
label: "Calls",
|
|
756
|
-
|
|
804
|
+
value: (sample) => sample.render.calls
|
|
757
805
|
},
|
|
758
806
|
{
|
|
807
|
+
format: tenths,
|
|
759
808
|
icon: "cpu",
|
|
760
809
|
key: "cpu",
|
|
761
810
|
label: "CPU",
|
|
762
|
-
|
|
811
|
+
unit: "ms",
|
|
812
|
+
value: (sample) => sample.cpu
|
|
763
813
|
},
|
|
764
814
|
{
|
|
815
|
+
format: tenths,
|
|
765
816
|
icon: "zap",
|
|
766
817
|
key: "gpu",
|
|
767
818
|
label: "GPU",
|
|
768
|
-
|
|
819
|
+
unit: "ms",
|
|
820
|
+
value: (sample) => sample.gpu.available ? sample.gpu.ms : null
|
|
769
821
|
},
|
|
770
822
|
{
|
|
823
|
+
format: whole,
|
|
771
824
|
icon: "trendingDown",
|
|
772
825
|
key: "low",
|
|
773
826
|
label: "1% low",
|
|
774
|
-
|
|
827
|
+
value: (_sample, stats) => stats.frames ? stats.lowFps : null
|
|
775
828
|
},
|
|
776
829
|
{
|
|
830
|
+
format: tenths,
|
|
777
831
|
icon: "timer",
|
|
778
832
|
key: "p99",
|
|
779
833
|
label: "Frame p99",
|
|
780
|
-
|
|
834
|
+
unit: "ms",
|
|
835
|
+
value: (_sample, stats) => stats.frames ? stats.p99Ms : null
|
|
781
836
|
},
|
|
782
837
|
{
|
|
838
|
+
format: formatCount,
|
|
783
839
|
icon: "activity",
|
|
784
840
|
key: "hitches",
|
|
785
841
|
label: "Hitches",
|
|
786
|
-
|
|
842
|
+
value: (_sample, stats) => stats.frames ? stats.hitches : null
|
|
787
843
|
},
|
|
788
844
|
{
|
|
845
|
+
format: formatCount,
|
|
789
846
|
icon: "triangle",
|
|
790
847
|
key: "triangles",
|
|
791
848
|
label: "Triangles",
|
|
792
|
-
|
|
849
|
+
value: (sample) => sample.render.triangles
|
|
793
850
|
},
|
|
794
851
|
{
|
|
852
|
+
format: formatCount,
|
|
795
853
|
icon: "spline",
|
|
796
854
|
key: "lines",
|
|
797
855
|
label: "Lines",
|
|
798
|
-
|
|
856
|
+
value: (sample) => sample.render.lines
|
|
799
857
|
},
|
|
800
858
|
{
|
|
859
|
+
format: formatCount,
|
|
801
860
|
icon: "circleDot",
|
|
802
861
|
key: "points",
|
|
803
862
|
label: "Points",
|
|
804
|
-
|
|
863
|
+
value: (sample) => sample.render.points
|
|
805
864
|
},
|
|
806
865
|
{
|
|
866
|
+
format: formatCount,
|
|
807
867
|
icon: "repeat",
|
|
808
868
|
key: "passes",
|
|
809
869
|
label: "Render passes",
|
|
810
|
-
|
|
870
|
+
value: (sample) => sample.render.passes
|
|
811
871
|
},
|
|
812
872
|
{
|
|
873
|
+
format: formatCount,
|
|
813
874
|
icon: "box",
|
|
814
875
|
key: "geometries",
|
|
815
876
|
label: "Geometries",
|
|
816
|
-
|
|
877
|
+
value: (sample) => sample.resources.geometries
|
|
817
878
|
},
|
|
818
879
|
{
|
|
880
|
+
format: formatCount,
|
|
819
881
|
icon: "image",
|
|
820
882
|
key: "textures",
|
|
821
883
|
label: "Textures",
|
|
822
|
-
|
|
884
|
+
value: (sample) => sample.resources.textures
|
|
823
885
|
},
|
|
824
886
|
{
|
|
887
|
+
format: formatCount,
|
|
825
888
|
icon: "palette",
|
|
826
889
|
key: "shaders",
|
|
827
890
|
label: "Shaders",
|
|
828
|
-
|
|
891
|
+
value: (sample) => sample.resources.programs
|
|
829
892
|
}
|
|
830
893
|
];
|
|
894
|
+
const NUMBER_BY_KEY = new Map(NUMBERS.map((config) => [config.key, config]));
|
|
895
|
+
/** Canvas can't read CSS custom properties; the budget line's amber per theme. */
|
|
896
|
+
const GUIDE_COLOR = {
|
|
897
|
+
dark: "rgba(245, 158, 11, 0.75)",
|
|
898
|
+
light: "rgba(180, 83, 9, 0.7)"
|
|
899
|
+
};
|
|
900
|
+
/** `Budget: at most 16.7 ms` / `at least 57`, or `""` without one. */
|
|
901
|
+
const budgetTitle = (budgets, key) => {
|
|
902
|
+
const limit = budgets.limits[key];
|
|
903
|
+
const config = NUMBER_BY_KEY.get(key);
|
|
904
|
+
if (limit === void 0 || !config) return "";
|
|
905
|
+
return `Budget: ${isFloor(key) ? "at least" : "at most"} ${config.format(limit)}${config.unit ? ` ${config.unit}` : ""}`;
|
|
906
|
+
};
|
|
907
|
+
/** The sample value a timing graph shows, `null` when not known. */
|
|
908
|
+
const timingValue = (sample, metric) => {
|
|
909
|
+
if (!isTimingAvailable(sample, metric)) return null;
|
|
910
|
+
const value = readTiming(sample, metric);
|
|
911
|
+
return metric === "fps" && value === 0 ? null : value;
|
|
912
|
+
};
|
|
831
913
|
/**
|
|
832
914
|
* Host-agnostic widget that paints {@link Sample}s. Mount {@link element}
|
|
833
915
|
* anywhere; the view drives its own throttled repaint. Compact is the floating
|
|
@@ -860,6 +942,7 @@ var PerformanceView = class {
|
|
|
860
942
|
hudGridEl;
|
|
861
943
|
hudGraphs = /* @__PURE__ */ new Map();
|
|
862
944
|
hudNumberEls = /* @__PURE__ */ new Map();
|
|
945
|
+
budgets;
|
|
863
946
|
rafId = null;
|
|
864
947
|
lastPaintAt = 0;
|
|
865
948
|
resizeObserver;
|
|
@@ -872,6 +955,7 @@ var PerformanceView = class {
|
|
|
872
955
|
this.theme.setOverride(this.settings.theme);
|
|
873
956
|
this.mode = options.mode ?? "full";
|
|
874
957
|
this.minIntervalMs = 1e3 / (options.refreshHz ?? 10);
|
|
958
|
+
this.budgets = resolveBudgets(options.budgets);
|
|
875
959
|
this.element = document.createElement("div");
|
|
876
960
|
this.element.className = "perf-monitor";
|
|
877
961
|
this.applyModeClass();
|
|
@@ -901,6 +985,11 @@ var PerformanceView = class {
|
|
|
901
985
|
getMode() {
|
|
902
986
|
return this.mode;
|
|
903
987
|
}
|
|
988
|
+
/** Replace the budgets; `false` turns them all off. Applies on the next paint. */
|
|
989
|
+
setBudgets(budgets) {
|
|
990
|
+
this.budgets = resolveBudgets(budgets);
|
|
991
|
+
this.applyBudgetTitles();
|
|
992
|
+
}
|
|
904
993
|
dispose() {
|
|
905
994
|
this.stop();
|
|
906
995
|
this.unsubscribe();
|
|
@@ -922,15 +1011,9 @@ var PerformanceView = class {
|
|
|
922
1011
|
if (this.settings.info) this.renderFooter();
|
|
923
1012
|
return;
|
|
924
1013
|
}
|
|
925
|
-
for (const config of TIMINGS)
|
|
926
|
-
const available = isTimingAvailable(sample, config.metric);
|
|
927
|
-
const valueEl = this.graphValueEls.get(config.metric);
|
|
928
|
-
valueEl.textContent = available ? this.formatTiming(config, readTiming(sample, config.metric)) : "unavailable";
|
|
929
|
-
const graph = this.graphCanvases.get(config.metric);
|
|
930
|
-
drawSparkline(graph.ctx, graph.width, graph.height, available ? this.monitor.getHistory(config.metric) : [], config.style[this.theme.resolved]);
|
|
931
|
-
}
|
|
1014
|
+
for (const config of TIMINGS) this.paintGraph(config, sample, this.graphValueEls.get(config.metric), this.graphCanvases.get(config.metric), "unavailable");
|
|
932
1015
|
const stats = this.monitor.getFrameStats();
|
|
933
|
-
for (const config of NUMBERS) this.statValueEls.get(config.key)
|
|
1016
|
+
for (const config of NUMBERS) this.paintNumber(config, this.statValueEls.get(config.key), sample, stats);
|
|
934
1017
|
this.renderFooter();
|
|
935
1018
|
}
|
|
936
1019
|
/** The monitor caches the environment once its backend settles; until then this re-reads. */
|
|
@@ -954,16 +1037,38 @@ var PerformanceView = class {
|
|
|
954
1037
|
renderHud(sample) {
|
|
955
1038
|
const stats = this.hudNumberEls.size > 0 ? this.monitor.getFrameStats() : null;
|
|
956
1039
|
for (const [key, element] of this.hudNumberEls) {
|
|
957
|
-
const config =
|
|
958
|
-
if (config && stats)
|
|
1040
|
+
const config = NUMBER_BY_KEY.get(key);
|
|
1041
|
+
if (config && stats) this.paintNumber(config, element, sample, stats);
|
|
959
1042
|
}
|
|
960
1043
|
for (const [metric, graph] of this.hudGraphs) {
|
|
961
1044
|
const config = TIMINGS.find((timing) => timing.metric === metric);
|
|
962
|
-
|
|
963
|
-
graph.valueEl.textContent = available ? this.formatTiming(config, readTiming(sample, metric)) : "—";
|
|
964
|
-
drawSparkline(graph.ctx, graph.width, graph.height, available ? this.monitor.getHistory(metric) : [], config.style[this.theme.resolved]);
|
|
1045
|
+
this.paintGraph(config, sample, graph.valueEl, graph, "—");
|
|
965
1046
|
}
|
|
966
1047
|
}
|
|
1048
|
+
paintNumber(config, element, sample, stats) {
|
|
1049
|
+
const value = config.value(sample, stats);
|
|
1050
|
+
element.textContent = value === null ? "—" : config.format(value);
|
|
1051
|
+
element.classList.toggle("is-over", isOverBudget(this.budgets, config.key, value));
|
|
1052
|
+
}
|
|
1053
|
+
/** Value and sparkline, with the budget line drawn once the series reaches it. */
|
|
1054
|
+
paintGraph(config, sample, valueEl, graph, missing) {
|
|
1055
|
+
const value = timingValue(sample, config.metric);
|
|
1056
|
+
const available = isTimingAvailable(sample, config.metric);
|
|
1057
|
+
const limit = this.budgets.limits[config.metric];
|
|
1058
|
+
valueEl.textContent = available ? this.formatTiming(config, readTiming(sample, config.metric)) : missing;
|
|
1059
|
+
valueEl.classList.toggle("is-over", isOverBudget(this.budgets, config.metric, value));
|
|
1060
|
+
drawSparkline(graph.ctx, graph.width, graph.height, available ? this.monitor.getHistory(config.metric) : [], config.style[this.theme.resolved], limit === void 0 ? void 0 : {
|
|
1061
|
+
color: GUIDE_COLOR[this.theme.resolved],
|
|
1062
|
+
value: limit
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
/** Each value's tooltip names its budget, so amber explains itself. */
|
|
1066
|
+
applyBudgetTitles() {
|
|
1067
|
+
for (const [key, element] of this.statValueEls) element.title = budgetTitle(this.budgets, key);
|
|
1068
|
+
for (const [key, element] of this.hudNumberEls) element.title = budgetTitle(this.budgets, key);
|
|
1069
|
+
for (const [metric, element] of this.graphValueEls) element.title = budgetTitle(this.budgets, metric);
|
|
1070
|
+
for (const [metric, graph] of this.hudGraphs) graph.valueEl.title = budgetTitle(this.budgets, metric);
|
|
1071
|
+
}
|
|
967
1072
|
formatTiming(config, value) {
|
|
968
1073
|
return `${config.format(value)}${config.unit ? ` ${config.unit}` : ""}`;
|
|
969
1074
|
}
|
|
@@ -1156,6 +1261,7 @@ var PerformanceView = class {
|
|
|
1156
1261
|
this.hudGridEl.append(item);
|
|
1157
1262
|
this.hudNumberEls.set(config.key, value);
|
|
1158
1263
|
}
|
|
1264
|
+
this.applyBudgetTitles();
|
|
1159
1265
|
this.resizeCanvases();
|
|
1160
1266
|
}
|
|
1161
1267
|
onSelectionChanged() {
|
|
@@ -1256,6 +1362,7 @@ const PERF_HUD_STYLES = `
|
|
|
1256
1362
|
--perf-row: rgba(255, 255, 255, 0.04);
|
|
1257
1363
|
--perf-border: rgba(255, 255, 255, 0.12);
|
|
1258
1364
|
--perf-accent: #60a5fa;
|
|
1365
|
+
--perf-warn: #f59e0b;
|
|
1259
1366
|
--perf-shadow: 0 8px 24px rgba(0, 0, 0, 0.32);
|
|
1260
1367
|
|
|
1261
1368
|
color-scheme: dark;
|
|
@@ -1270,6 +1377,7 @@ const PERF_HUD_STYLES = `
|
|
|
1270
1377
|
--perf-row: rgba(0, 0, 0, 0.05);
|
|
1271
1378
|
--perf-border: rgba(0, 0, 0, 0.12);
|
|
1272
1379
|
--perf-accent: #2563eb;
|
|
1380
|
+
--perf-warn: #b45309;
|
|
1273
1381
|
--perf-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
|
|
1274
1382
|
|
|
1275
1383
|
color-scheme: light;
|
|
@@ -1581,13 +1689,15 @@ const PERF_HUD_STYLES = `
|
|
|
1581
1689
|
}
|
|
1582
1690
|
|
|
1583
1691
|
.perf-monitor__badge.is-fallback {
|
|
1584
|
-
border-color:
|
|
1585
|
-
color:
|
|
1692
|
+
border-color: var(--perf-warn);
|
|
1693
|
+
color: var(--perf-warn);
|
|
1586
1694
|
}
|
|
1587
1695
|
|
|
1588
|
-
.
|
|
1589
|
-
|
|
1590
|
-
|
|
1696
|
+
/* Past its budget. The value's tooltip names the budget. */
|
|
1697
|
+
.perf-monitor__value.is-over,
|
|
1698
|
+
.perf-monitor__hud-value.is-over,
|
|
1699
|
+
.perf-monitor__graph-value.is-over {
|
|
1700
|
+
color: var(--perf-warn);
|
|
1591
1701
|
}
|
|
1592
1702
|
|
|
1593
1703
|
.perf-monitor__link {
|
|
@@ -1782,6 +1892,7 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
1782
1892
|
const toggle = disc("Toggle full performance metrics", "", "sliders");
|
|
1783
1893
|
tools.append(grip, toggle);
|
|
1784
1894
|
const view = new PerformanceView({
|
|
1895
|
+
budgets: options.budgets,
|
|
1785
1896
|
mode: options.mode ?? "compact",
|
|
1786
1897
|
monitor,
|
|
1787
1898
|
refreshHz: options.refreshHz,
|
|
@@ -1857,6 +1968,7 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
1857
1968
|
},
|
|
1858
1969
|
element: host,
|
|
1859
1970
|
getMode: () => view.getMode(),
|
|
1971
|
+
setBudgets: (budgets) => view.setBudgets(budgets),
|
|
1860
1972
|
setMode,
|
|
1861
1973
|
getTheme: () => theme.mode,
|
|
1862
1974
|
setTheme: (mode) => theme.setMode(mode),
|
|
@@ -1866,6 +1978,6 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
1866
1978
|
};
|
|
1867
1979
|
};
|
|
1868
1980
|
//#endregion
|
|
1869
|
-
export { formatReport as a, HudSettings as c, isThemeMode as d, PerformanceView as i, HudTheme as l, PERF_HUD_STYLES as n, dockPanel as o, injectStyles as r, DEFAULT_STORAGE_KEY as s, mountPerfHud as t, THEME_MODES as u };
|
|
1981
|
+
export { formatReport as a, HudSettings as c, isThemeMode as d, resolveBudgets as f, PerformanceView as i, HudTheme as l, PERF_HUD_STYLES as n, dockPanel as o, injectStyles as r, DEFAULT_STORAGE_KEY as s, mountPerfHud as t, THEME_MODES as u };
|
|
1870
1982
|
|
|
1871
|
-
//# sourceMappingURL=mount-perf-hud-
|
|
1983
|
+
//# sourceMappingURL=mount-perf-hud-DqY_tP6s.js.map
|