@zkmake/three-meter 0.9.2 → 0.10.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 +32 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{mount-perf-hud-BKpk62Pp.d.ts → mount-perf-hud-MTPYNhG9.d.ts} +29 -9
- package/dist/{mount-perf-hud-BL84ddLm.js → mount-perf-hud-U_Xlj0Od.js} +257 -41
- package/dist/mount-perf-hud-U_Xlj0Od.js.map +1 -0
- package/dist/{performance-monitor-DgHxeQ2X.d.ts → performance-monitor-BoY_-GTt.d.ts} +13 -1
- package/dist/{performance-monitor-DsBp13ts.js → performance-monitor-BqnoiLEs.js} +87 -9
- package/dist/performance-monitor-BqnoiLEs.js.map +1 -0
- package/dist/react.d.ts +2 -2
- package/dist/react.js +2 -2
- package/dist/styles.css +64 -3
- package/dist/ui.d.ts +3 -3
- package/dist/ui.js +1 -1
- package/package.json +14 -9
- package/dist/mount-perf-hud-BL84ddLm.js.map +0 -1
- package/dist/performance-monitor-DsBp13ts.js.map +0 -1
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
//#region src/ui/budgets.ts
|
|
2
2
|
const DEFAULT_TARGET_FPS = 60;
|
|
3
|
-
const FLOORS = /* @__PURE__ */ new Set([
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
const FLOORS = /* @__PURE__ */ new Set([
|
|
4
|
+
"fps",
|
|
5
|
+
"headroom",
|
|
6
|
+
"low"
|
|
7
|
+
]);
|
|
8
|
+
/** `detectedHz` is the monitor's refresh reading; an explicit `targetFps` wins over it. */
|
|
9
|
+
const resolveBudgets = (budgets, detectedHz = null) => {
|
|
10
|
+
const targetFps = budgets && budgets.targetFps || detectedHz || DEFAULT_TARGET_FPS;
|
|
6
11
|
const frameMs = 1e3 / targetFps;
|
|
7
12
|
if (budgets === false) return {
|
|
8
13
|
frameMs,
|
|
9
|
-
limits: {}
|
|
14
|
+
limits: {},
|
|
15
|
+
targetFps
|
|
10
16
|
};
|
|
11
17
|
const merged = {
|
|
12
18
|
cpu: frameMs,
|
|
13
19
|
fps: targetFps * .95,
|
|
14
20
|
gpu: frameMs,
|
|
21
|
+
headroom: 0,
|
|
15
22
|
low: targetFps / 2,
|
|
16
23
|
p99: frameMs * 1.5,
|
|
17
24
|
...budgets
|
|
@@ -20,7 +27,8 @@ const resolveBudgets = (budgets) => {
|
|
|
20
27
|
for (const [key, limit] of Object.entries(merged)) if (key !== "targetFps" && typeof limit === "number" && Number.isFinite(limit)) limits[key] = limit;
|
|
21
28
|
return {
|
|
22
29
|
frameMs,
|
|
23
|
-
limits
|
|
30
|
+
limits,
|
|
31
|
+
targetFps
|
|
24
32
|
};
|
|
25
33
|
};
|
|
26
34
|
const isFloor = (key) => FLOORS.has(key);
|
|
@@ -486,6 +494,9 @@ const dockPanel = (element, options = {}) => {
|
|
|
486
494
|
//#endregion
|
|
487
495
|
//#region src/ui/icons.ts
|
|
488
496
|
const ICON_MARKUP = {
|
|
497
|
+
eye: "<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\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>",
|
|
498
|
+
eyeOff: "<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\"/><path d=\"M14.084 14.158a3 3 0 0 1-4.242-4.242\"/><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\"/><path d=\"m2 2 20 20\"/>",
|
|
499
|
+
hourglass: "<path d=\"M5 22h14\"/><path d=\"M5 2h14\"/><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\"/><path d=\"M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2\"/>",
|
|
489
500
|
flame: "<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\"/>",
|
|
490
501
|
activity: "<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\"/>",
|
|
491
502
|
box: "<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\"/><path d=\"m3.3 7 8.7 5 8.7-5\"/><path d=\"M12 22V12\"/>",
|
|
@@ -528,7 +539,7 @@ const createIcon = (name, className) => {
|
|
|
528
539
|
//#endregion
|
|
529
540
|
//#region package.json
|
|
530
541
|
var name = "@zkmake/three-meter";
|
|
531
|
-
var version = "0.
|
|
542
|
+
var version = "0.10.0";
|
|
532
543
|
//#endregion
|
|
533
544
|
//#region src/ui/clipboard.ts
|
|
534
545
|
/**
|
|
@@ -580,6 +591,45 @@ const compact = new Intl.NumberFormat("en-US", {
|
|
|
580
591
|
});
|
|
581
592
|
const formatCount = (value) => Math.abs(value) < COMPACT_FROM ? exact.format(value) : compact.format(value);
|
|
582
593
|
//#endregion
|
|
594
|
+
//#region src/ui/hider.ts
|
|
595
|
+
var Hider = class {
|
|
596
|
+
groups = /* @__PURE__ */ new Map();
|
|
597
|
+
/** Each hidden object's `visible` from before the first group hid it. */
|
|
598
|
+
original = /* @__PURE__ */ new Map();
|
|
599
|
+
hide(key, objects) {
|
|
600
|
+
if (this.groups.has(key)) return;
|
|
601
|
+
const hideable = objects.filter((object) => typeof object === "object" && object !== null);
|
|
602
|
+
this.groups.set(key, hideable);
|
|
603
|
+
for (const object of hideable) {
|
|
604
|
+
if (!this.original.has(object)) this.original.set(object, object.visible !== false);
|
|
605
|
+
object.visible = false;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
show(key) {
|
|
609
|
+
const objects = this.groups.get(key);
|
|
610
|
+
if (!objects) return;
|
|
611
|
+
this.groups.delete(key);
|
|
612
|
+
for (const object of objects) if (!this.heldElsewhere(object)) {
|
|
613
|
+
object.visible = this.original.get(object) ?? true;
|
|
614
|
+
this.original.delete(object);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
isHidden(key) {
|
|
618
|
+
return this.groups.has(key);
|
|
619
|
+
}
|
|
620
|
+
/** Hidden group keys, in the order they were hidden. */
|
|
621
|
+
keys() {
|
|
622
|
+
return [...this.groups.keys()];
|
|
623
|
+
}
|
|
624
|
+
restoreAll() {
|
|
625
|
+
for (const key of this.keys()) this.show(key);
|
|
626
|
+
}
|
|
627
|
+
heldElsewhere(object) {
|
|
628
|
+
for (const objects of this.groups.values()) if (objects.includes(object)) return true;
|
|
629
|
+
return false;
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
//#endregion
|
|
583
633
|
//#region src/ui/metric-help.ts
|
|
584
634
|
/**
|
|
585
635
|
* One plain-language line per metric: what it is, and what a bad reading
|
|
@@ -591,6 +641,8 @@ const METRIC_HELP = {
|
|
|
591
641
|
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).",
|
|
592
642
|
fps: "Frames drawn per second. Higher is smoother; most displays top out at 60 or 120.",
|
|
593
643
|
geometries: "Geometries held in GPU memory. Should level off; a steady climb is a leak, so dispose() what you remove.",
|
|
644
|
+
headroom: "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.",
|
|
645
|
+
refresh: "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.",
|
|
594
646
|
gpu: "Time the graphics card spent drawing the frame. High means shaders, overdraw or resolution are the bottleneck.",
|
|
595
647
|
hitches: "Frames that took over twice as long as usual, in the last 1,000 frames. Each one is a visible stutter.",
|
|
596
648
|
lines: "Line segments drawn this frame.",
|
|
@@ -641,10 +693,15 @@ const renderReport = ({ cost, environment, page, sample, stats }) => {
|
|
|
641
693
|
`p99 ${ms(stats.p99Ms)}`,
|
|
642
694
|
`hitches ${formatCount(stats.hitches)} / ${formatCount(stats.frames)} frames`
|
|
643
695
|
].join(" · ");
|
|
696
|
+
const timing = [`CPU ${ms(sample.cpu)}`, `GPU ${sample.gpu.available ? ms(sample.gpu.ms) : "unavailable"}`];
|
|
697
|
+
if (stats.refreshHz) {
|
|
698
|
+
const busiest = Math.max(sample.cpu, sample.gpu.available ? sample.gpu.ms : 0);
|
|
699
|
+
timing.push(`refresh ${stats.refreshHz} Hz`, `headroom ${ms(1e3 / stats.refreshHz - busiest)}`);
|
|
700
|
+
}
|
|
644
701
|
const lines = [
|
|
645
702
|
heading.join(" · "),
|
|
646
703
|
frames,
|
|
647
|
-
|
|
704
|
+
timing.join(" · "),
|
|
648
705
|
[
|
|
649
706
|
`Calls ${formatCount(render.calls)}`,
|
|
650
707
|
`passes ${formatCount(render.passes)}`,
|
|
@@ -799,7 +856,15 @@ const COST_GROUPS = [{
|
|
|
799
856
|
group: "materials",
|
|
800
857
|
label: "materials"
|
|
801
858
|
}];
|
|
802
|
-
const COSTS_HELP = "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.";
|
|
859
|
+
const COSTS_HELP = "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.";
|
|
860
|
+
/** Frames averaged on each side of hiding a row. */
|
|
861
|
+
const MEASURE_FRAMES = 30;
|
|
862
|
+
/** How long after hiding before the saving is read, so the average holds only frames without the row. */
|
|
863
|
+
const MEASURE_SETTLE_MS = 700;
|
|
864
|
+
const meanOfLast = (values, count) => {
|
|
865
|
+
const tail = values.slice(-count);
|
|
866
|
+
return tail.length === 0 ? 0 : tail.reduce((sum, value) => sum + value, 0) / tail.length;
|
|
867
|
+
};
|
|
803
868
|
/** `×2,000` for instances or copies; nothing for a single object. */
|
|
804
869
|
const copiesOf = (entry) => entry.instances > 1 ? `×${formatCount(entry.instances)}` : "";
|
|
805
870
|
const costTitle = (entry) => {
|
|
@@ -848,6 +913,22 @@ const NUMBERS = [
|
|
|
848
913
|
unit: "ms",
|
|
849
914
|
value: (sample) => sample.gpu.available ? sample.gpu.ms : null
|
|
850
915
|
},
|
|
916
|
+
{
|
|
917
|
+
format: tenths,
|
|
918
|
+
icon: "hourglass",
|
|
919
|
+
key: "headroom",
|
|
920
|
+
label: "Headroom",
|
|
921
|
+
unit: "ms",
|
|
922
|
+
value: (sample, _stats, frameMs) => sample.fps ? frameMs - Math.max(sample.cpu, sample.gpu.available ? sample.gpu.ms : 0) : null
|
|
923
|
+
},
|
|
924
|
+
{
|
|
925
|
+
format: (value) => `${Math.round(value)} Hz`,
|
|
926
|
+
icon: "monitor",
|
|
927
|
+
key: "refresh",
|
|
928
|
+
label: "Refresh",
|
|
929
|
+
unit: "Hz",
|
|
930
|
+
value: (_sample, stats) => stats.refreshHz
|
|
931
|
+
},
|
|
851
932
|
{
|
|
852
933
|
format: whole,
|
|
853
934
|
icon: "trendingDown",
|
|
@@ -971,6 +1052,10 @@ var PerformanceView = class {
|
|
|
971
1052
|
hudGraphs = /* @__PURE__ */ new Map();
|
|
972
1053
|
hudNumberEls = /* @__PURE__ */ new Map();
|
|
973
1054
|
budgets;
|
|
1055
|
+
budgetsInput;
|
|
1056
|
+
detectedHz = null;
|
|
1057
|
+
hider = new Hider();
|
|
1058
|
+
hiddenRows = /* @__PURE__ */ new Map();
|
|
974
1059
|
rafId = null;
|
|
975
1060
|
lastPaintAt = 0;
|
|
976
1061
|
resizeObserver;
|
|
@@ -991,6 +1076,7 @@ var PerformanceView = class {
|
|
|
991
1076
|
this.theme.setOverride(this.settings.theme);
|
|
992
1077
|
this.mode = options.mode ?? "full";
|
|
993
1078
|
this.minIntervalMs = 1e3 / (options.refreshHz ?? 10);
|
|
1079
|
+
this.budgetsInput = options.budgets;
|
|
994
1080
|
this.budgets = resolveBudgets(options.budgets);
|
|
995
1081
|
this.element = document.createElement("div");
|
|
996
1082
|
this.element.className = "perf-monitor";
|
|
@@ -1023,11 +1109,13 @@ var PerformanceView = class {
|
|
|
1023
1109
|
}
|
|
1024
1110
|
/** Replace the budgets; `false` turns them all off. Applies on the next paint. */
|
|
1025
1111
|
setBudgets(budgets) {
|
|
1026
|
-
this.
|
|
1112
|
+
this.budgetsInput = budgets;
|
|
1113
|
+
this.budgets = resolveBudgets(budgets, this.detectedHz);
|
|
1027
1114
|
this.applyBudgetTitles();
|
|
1028
1115
|
}
|
|
1029
1116
|
dispose() {
|
|
1030
1117
|
this.stop();
|
|
1118
|
+
this.showAllHidden();
|
|
1031
1119
|
this.unsubscribe();
|
|
1032
1120
|
this.unsubscribeTheme();
|
|
1033
1121
|
if (this.ownsTheme) this.theme.dispose();
|
|
@@ -1042,13 +1130,14 @@ var PerformanceView = class {
|
|
|
1042
1130
|
};
|
|
1043
1131
|
render() {
|
|
1044
1132
|
const sample = this.monitor.getSample();
|
|
1133
|
+
const stats = this.monitor.getFrameStats();
|
|
1134
|
+
this.followRefresh(stats.refreshHz);
|
|
1045
1135
|
if (this.mode === "compact") {
|
|
1046
|
-
this.renderHud(sample);
|
|
1136
|
+
this.renderHud(sample, stats);
|
|
1047
1137
|
if (this.settings.info) this.renderFooter();
|
|
1048
1138
|
return;
|
|
1049
1139
|
}
|
|
1050
1140
|
for (const config of TIMINGS) this.paintGraph(config, sample, this.graphValueEls.get(config.metric), this.graphCanvases.get(config.metric), "unavailable");
|
|
1051
|
-
const stats = this.monitor.getFrameStats();
|
|
1052
1141
|
for (const config of NUMBERS) this.paintNumber(config, this.statValueEls.get(config.key), sample, stats);
|
|
1053
1142
|
if (this.costsOpen && performance.now() - this.lastCostsAt >= COSTS_INTERVAL_MS) this.renderCosts();
|
|
1054
1143
|
this.renderFooter();
|
|
@@ -1056,44 +1145,102 @@ var PerformanceView = class {
|
|
|
1056
1145
|
renderCosts() {
|
|
1057
1146
|
this.lastCostsAt = performance.now();
|
|
1058
1147
|
const cost = this.monitor.getSceneCost();
|
|
1148
|
+
const hidden = [...this.hiddenRows.values()].map((row) => this.hiddenCostRow(row));
|
|
1059
1149
|
if (!cost) {
|
|
1060
|
-
this.costsListEl.replaceChildren(this.costsNote("No scene rendered yet."));
|
|
1150
|
+
this.costsListEl.replaceChildren(...hidden, this.costsNote("No scene rendered yet."));
|
|
1061
1151
|
return;
|
|
1062
1152
|
}
|
|
1063
|
-
const entries =
|
|
1153
|
+
const entries = (this.costGroup === "meshes" ? cost.meshes : cost.materials).filter((entry) => !this.hiddenRows.has(entry.key));
|
|
1064
1154
|
if (this.costSort === "triangles") entries.sort((a, b) => b.triangles - a.triangles || b.calls - a.calls);
|
|
1065
|
-
if (entries.length === 0) {
|
|
1155
|
+
if (entries.length === 0 && hidden.length === 0) {
|
|
1066
1156
|
this.costsListEl.replaceChildren(this.costsNote("Nothing drawn."));
|
|
1067
1157
|
return;
|
|
1068
1158
|
}
|
|
1069
1159
|
const rows = entries.slice(0, COSTS_ROWS).map((entry) => {
|
|
1070
|
-
const row =
|
|
1071
|
-
row.type = "button";
|
|
1072
|
-
row.className = "perf-monitor__cost";
|
|
1073
|
-
row.title = costTitle(entry);
|
|
1074
|
-
const name = document.createElement("span");
|
|
1075
|
-
name.className = "perf-monitor__cost-name";
|
|
1076
|
-
name.textContent = entry.label;
|
|
1077
|
-
const copies = copiesOf(entry);
|
|
1078
|
-
if (copies) {
|
|
1079
|
-
const suffix = document.createElement("span");
|
|
1080
|
-
suffix.className = "perf-monitor__cost-copies";
|
|
1081
|
-
suffix.textContent = ` ${copies}`;
|
|
1082
|
-
name.append(suffix);
|
|
1083
|
-
}
|
|
1160
|
+
const row = this.costRowShell(entry);
|
|
1084
1161
|
const calls = document.createElement("span");
|
|
1085
1162
|
calls.className = "perf-monitor__cost-value";
|
|
1086
1163
|
calls.textContent = formatCount(entry.calls);
|
|
1087
1164
|
const triangles = document.createElement("span");
|
|
1088
1165
|
triangles.className = "perf-monitor__cost-value";
|
|
1089
1166
|
triangles.textContent = formatCount(entry.triangles);
|
|
1090
|
-
row.append(
|
|
1091
|
-
row.addEventListener("click", () => {
|
|
1092
|
-
console.log(`three-meter: ${entry.label}`, entry.objects);
|
|
1093
|
-
});
|
|
1167
|
+
row.append(calls, triangles, this.eyeButton(entry, false));
|
|
1094
1168
|
return row;
|
|
1095
1169
|
});
|
|
1096
|
-
this.costsListEl.replaceChildren(...rows);
|
|
1170
|
+
this.costsListEl.replaceChildren(...hidden, ...rows);
|
|
1171
|
+
}
|
|
1172
|
+
/** Name button (logs the objects) in a row; the caller appends the value cells and the eye. */
|
|
1173
|
+
costRowShell(entry) {
|
|
1174
|
+
const row = document.createElement("div");
|
|
1175
|
+
row.className = "perf-monitor__cost";
|
|
1176
|
+
const name = document.createElement("button");
|
|
1177
|
+
name.type = "button";
|
|
1178
|
+
name.className = "perf-monitor__cost-name";
|
|
1179
|
+
name.title = costTitle(entry);
|
|
1180
|
+
name.textContent = entry.label;
|
|
1181
|
+
const copies = copiesOf(entry);
|
|
1182
|
+
if (copies) {
|
|
1183
|
+
const suffix = document.createElement("span");
|
|
1184
|
+
suffix.className = "perf-monitor__cost-copies";
|
|
1185
|
+
suffix.textContent = ` ${copies}`;
|
|
1186
|
+
name.append(suffix);
|
|
1187
|
+
}
|
|
1188
|
+
name.addEventListener("click", () => {
|
|
1189
|
+
console.log(`three-meter: ${entry.label}`, entry.objects);
|
|
1190
|
+
});
|
|
1191
|
+
row.append(name);
|
|
1192
|
+
return row;
|
|
1193
|
+
}
|
|
1194
|
+
/** A hidden row: what hiding it saved, measured from the timing history. */
|
|
1195
|
+
hiddenCostRow(hidden) {
|
|
1196
|
+
const row = this.costRowShell(hidden.entry);
|
|
1197
|
+
row.classList.add("is-hidden");
|
|
1198
|
+
const saved = document.createElement("span");
|
|
1199
|
+
saved.className = "perf-monitor__cost-saved";
|
|
1200
|
+
const label = hidden.metric.toUpperCase();
|
|
1201
|
+
if (performance.now() - hidden.hiddenAt < MEASURE_SETTLE_MS) saved.textContent = "measuring…";
|
|
1202
|
+
else {
|
|
1203
|
+
const now = meanOfLast(this.monitor.getHistory(hidden.metric), MEASURE_FRAMES);
|
|
1204
|
+
const delta = hidden.baseline - now;
|
|
1205
|
+
saved.textContent = `${delta >= 0 ? "−" : "+"}${Math.abs(delta).toFixed(1)} ms ${label}`;
|
|
1206
|
+
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.`;
|
|
1207
|
+
}
|
|
1208
|
+
row.append(saved, this.eyeButton(hidden.entry, true));
|
|
1209
|
+
return row;
|
|
1210
|
+
}
|
|
1211
|
+
eyeButton(entry, hidden) {
|
|
1212
|
+
const eye = document.createElement("button");
|
|
1213
|
+
eye.type = "button";
|
|
1214
|
+
eye.className = "perf-monitor__cost-eye";
|
|
1215
|
+
eye.setAttribute("aria-pressed", String(hidden));
|
|
1216
|
+
const label = hidden ? `Show ${entry.label} again` : `Hide ${entry.label} to measure its cost`;
|
|
1217
|
+
eye.title = label;
|
|
1218
|
+
eye.setAttribute("aria-label", label);
|
|
1219
|
+
eye.append(createIcon(hidden ? "eyeOff" : "eye", "perf-monitor__icon"));
|
|
1220
|
+
eye.addEventListener("click", () => hidden ? this.showRow(entry.key) : this.hideRow(entry));
|
|
1221
|
+
return eye;
|
|
1222
|
+
}
|
|
1223
|
+
hideRow(entry) {
|
|
1224
|
+
const metric = this.monitor.getSample().gpu.available ? "gpu" : "cpu";
|
|
1225
|
+
this.hiddenRows.set(entry.key, {
|
|
1226
|
+
baseline: meanOfLast(this.monitor.getHistory(metric), MEASURE_FRAMES),
|
|
1227
|
+
entry,
|
|
1228
|
+
hiddenAt: performance.now(),
|
|
1229
|
+
metric
|
|
1230
|
+
});
|
|
1231
|
+
const all = this.monitor.getObjectsForKey(entry.key);
|
|
1232
|
+
this.hider.hide(entry.key, all.length > 0 ? all : entry.objects);
|
|
1233
|
+
this.renderCosts();
|
|
1234
|
+
}
|
|
1235
|
+
showRow(key) {
|
|
1236
|
+
this.hider.show(key);
|
|
1237
|
+
this.hiddenRows.delete(key);
|
|
1238
|
+
this.renderCosts();
|
|
1239
|
+
}
|
|
1240
|
+
/** Everything hidden goes back: on closing top costs and on dispose. */
|
|
1241
|
+
showAllHidden() {
|
|
1242
|
+
this.hider.restoreAll();
|
|
1243
|
+
this.hiddenRows.clear();
|
|
1097
1244
|
}
|
|
1098
1245
|
costsNote(text) {
|
|
1099
1246
|
const note = document.createElement("div");
|
|
@@ -1119,11 +1266,17 @@ var PerformanceView = class {
|
|
|
1119
1266
|
this.footerGpuEl.title = gpu;
|
|
1120
1267
|
this.footerHardwareEl.hidden = backend === "" && gpu === "";
|
|
1121
1268
|
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1269
|
+
/** Budgets follow the detected refresh rate unless `targetFps` was given. */
|
|
1270
|
+
followRefresh(hz) {
|
|
1271
|
+
if (hz === this.detectedHz) return;
|
|
1272
|
+
this.detectedHz = hz;
|
|
1273
|
+
this.budgets = resolveBudgets(this.budgetsInput, hz);
|
|
1274
|
+
this.applyBudgetTitles();
|
|
1275
|
+
}
|
|
1276
|
+
renderHud(sample, stats) {
|
|
1124
1277
|
for (const [key, element] of this.hudNumberEls) {
|
|
1125
1278
|
const config = NUMBER_BY_KEY.get(key);
|
|
1126
|
-
if (config
|
|
1279
|
+
if (config) this.paintNumber(config, element, sample, stats);
|
|
1127
1280
|
}
|
|
1128
1281
|
for (const [metric, graph] of this.hudGraphs) {
|
|
1129
1282
|
const config = TIMINGS.find((timing) => timing.metric === metric);
|
|
@@ -1131,7 +1284,7 @@ var PerformanceView = class {
|
|
|
1131
1284
|
}
|
|
1132
1285
|
}
|
|
1133
1286
|
paintNumber(config, element, sample, stats) {
|
|
1134
|
-
const value = config.value(sample, stats);
|
|
1287
|
+
const value = config.value(sample, stats, this.budgets.frameMs);
|
|
1135
1288
|
element.textContent = value === null ? "—" : config.format(value);
|
|
1136
1289
|
element.classList.toggle("is-over", isOverBudget(this.budgets, config.key, value));
|
|
1137
1290
|
}
|
|
@@ -1371,6 +1524,7 @@ var PerformanceView = class {
|
|
|
1371
1524
|
const checkbox = this.buildCheckbox(false, "Show the scene's biggest draw costs", (on) => {
|
|
1372
1525
|
this.costsOpen = on;
|
|
1373
1526
|
this.costsBodyEl.hidden = !on;
|
|
1527
|
+
if (!on) this.showAllHidden();
|
|
1374
1528
|
if (on) this.renderCosts();
|
|
1375
1529
|
});
|
|
1376
1530
|
toggleRow.append(createIcon("flame", "perf-monitor__icon"), label, checkbox);
|
|
@@ -1414,6 +1568,7 @@ var PerformanceView = class {
|
|
|
1414
1568
|
this.costSortButtons.set(sort, button);
|
|
1415
1569
|
head.append(button);
|
|
1416
1570
|
}
|
|
1571
|
+
head.append(document.createElement("span"));
|
|
1417
1572
|
this.costsListEl = document.createElement("div");
|
|
1418
1573
|
this.costsListEl.className = "perf-monitor__costs-list";
|
|
1419
1574
|
const hint = document.createElement("span");
|
|
@@ -1732,11 +1887,10 @@ const PERF_HUD_STYLES = `
|
|
|
1732
1887
|
}
|
|
1733
1888
|
|
|
1734
1889
|
.perf-monitor__cost {
|
|
1735
|
-
/* Rows are buttons, which shrink to their content without a width. */
|
|
1736
1890
|
box-sizing: border-box;
|
|
1737
1891
|
width: 100%;
|
|
1738
1892
|
display: grid;
|
|
1739
|
-
grid-template-columns: minmax(0, 1fr) 5ch 6ch;
|
|
1893
|
+
grid-template-columns: minmax(0, 1fr) 5ch 6ch 14px;
|
|
1740
1894
|
gap: 6px;
|
|
1741
1895
|
align-items: baseline;
|
|
1742
1896
|
padding: 2px 6px;
|
|
@@ -1746,13 +1900,58 @@ const PERF_HUD_STYLES = `
|
|
|
1746
1900
|
color: inherit;
|
|
1747
1901
|
font: inherit;
|
|
1748
1902
|
text-align: left;
|
|
1749
|
-
cursor:
|
|
1903
|
+
cursor: default;
|
|
1750
1904
|
}
|
|
1751
1905
|
|
|
1752
1906
|
.perf-monitor__cost:not(.perf-monitor__cost--head):hover {
|
|
1753
1907
|
background: var(--perf-row);
|
|
1754
1908
|
}
|
|
1755
1909
|
|
|
1910
|
+
.perf-monitor__cost.is-hidden .perf-monitor__cost-name {
|
|
1911
|
+
opacity: 0.6;
|
|
1912
|
+
text-decoration: line-through;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
/* What hiding saved, across the calls and tris columns. */
|
|
1916
|
+
.perf-monitor__cost-saved {
|
|
1917
|
+
grid-column: 2 / span 2;
|
|
1918
|
+
color: var(--perf-accent);
|
|
1919
|
+
font-variant-numeric: tabular-nums lining-nums;
|
|
1920
|
+
text-align: right;
|
|
1921
|
+
white-space: nowrap;
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
.perf-monitor__cost-eye {
|
|
1925
|
+
display: flex;
|
|
1926
|
+
align-items: center;
|
|
1927
|
+
justify-content: center;
|
|
1928
|
+
width: 14px;
|
|
1929
|
+
height: 14px;
|
|
1930
|
+
padding: 0;
|
|
1931
|
+
border: 0;
|
|
1932
|
+
border-radius: 3px;
|
|
1933
|
+
background: transparent;
|
|
1934
|
+
color: var(--perf-muted);
|
|
1935
|
+
cursor: pointer;
|
|
1936
|
+
align-self: center;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
.perf-monitor__cost-eye .perf-monitor__icon {
|
|
1940
|
+
width: 12px;
|
|
1941
|
+
height: 12px;
|
|
1942
|
+
color: inherit;
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
.perf-monitor__cost-eye:hover,
|
|
1946
|
+
.perf-monitor__cost-eye[aria-pressed="true"] {
|
|
1947
|
+
color: var(--perf-fg);
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
.perf-monitor__cost-eye:focus-visible {
|
|
1951
|
+
outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);
|
|
1952
|
+
outline-offset: 1px;
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1756
1955
|
.perf-monitor__cost:focus-visible {
|
|
1757
1956
|
outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);
|
|
1758
1957
|
outline-offset: -1px;
|
|
@@ -1763,13 +1962,30 @@ const PERF_HUD_STYLES = `
|
|
|
1763
1962
|
cursor: default;
|
|
1764
1963
|
}
|
|
1765
1964
|
|
|
1965
|
+
/* The name is a button: a click logs the row's objects. */
|
|
1766
1966
|
.perf-monitor__cost-name {
|
|
1967
|
+
min-width: 0;
|
|
1968
|
+
padding: 0;
|
|
1969
|
+
border: 0;
|
|
1970
|
+
background: transparent;
|
|
1971
|
+
font: inherit;
|
|
1972
|
+
text-align: left;
|
|
1973
|
+
cursor: pointer;
|
|
1767
1974
|
overflow: hidden;
|
|
1768
1975
|
text-overflow: ellipsis;
|
|
1769
1976
|
white-space: nowrap;
|
|
1770
1977
|
color: var(--perf-fg-dim);
|
|
1771
1978
|
}
|
|
1772
1979
|
|
|
1980
|
+
.perf-monitor__cost-name:hover {
|
|
1981
|
+
color: var(--perf-fg);
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
.perf-monitor__cost-name:focus-visible {
|
|
1985
|
+
outline: 1px solid color-mix(in srgb, var(--perf-accent) 60%, transparent);
|
|
1986
|
+
outline-offset: 1px;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1773
1989
|
.perf-monitor__cost-copies {
|
|
1774
1990
|
color: var(--perf-muted);
|
|
1775
1991
|
}
|
|
@@ -2236,4 +2452,4 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
2236
2452
|
//#endregion
|
|
2237
2453
|
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 };
|
|
2238
2454
|
|
|
2239
|
-
//# sourceMappingURL=mount-perf-hud-
|
|
2455
|
+
//# sourceMappingURL=mount-perf-hud-U_Xlj0Od.js.map
|