@zkmake/three-meter 0.5.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 +26 -2
- package/dist/{mount-perf-hud-CmINOH3T.d.ts → mount-perf-hud-6LeJ93Jg.d.ts} +47 -2
- package/dist/{mount-perf-hud-BLs1jkwM.js → mount-perf-hud-DqY_tP6s.js} +258 -46
- 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 +37 -7
- package/dist/ui.d.ts +3 -3
- package/dist/ui.js +2 -2
- package/package.json +1 -1
- package/dist/mount-perf-hud-BLs1jkwM.js.map +0 -1
|
@@ -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",
|
|
@@ -453,10 +486,26 @@ const dockPanel = (element, options = {}) => {
|
|
|
453
486
|
//#endregion
|
|
454
487
|
//#region src/ui/icons.ts
|
|
455
488
|
const ICON_MARKUP = {
|
|
489
|
+
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\"/>",
|
|
490
|
+
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\"/>",
|
|
491
|
+
circleDot: "<circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"1\"/>",
|
|
492
|
+
cpu: "<rect width=\"16\" height=\"16\" x=\"4\" y=\"4\" rx=\"2\"/><rect width=\"6\" height=\"6\" x=\"9\" y=\"9\" rx=\"1\"/><path d=\"M15 2v2\"/><path d=\"M15 20v2\"/><path d=\"M2 15h2\"/><path d=\"M2 9h2\"/><path d=\"M20 15h2\"/><path d=\"M20 9h2\"/><path d=\"M9 2v2\"/><path d=\"M9 20v2\"/>",
|
|
493
|
+
gauge: "<path d=\"m12 14 4-4\"/><path d=\"M3.34 19a10 10 0 1 1 17.32 0\"/>",
|
|
494
|
+
image: "<rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\"/><circle cx=\"9\" cy=\"9\" r=\"2\"/><path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\"/>",
|
|
495
|
+
info: "<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4\"/><path d=\"M12 8h.01\"/>",
|
|
496
|
+
layers: "<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\"/><path d=\"m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65\"/><path d=\"m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65\"/>",
|
|
497
|
+
palette: "<circle cx=\"13.5\" cy=\"6.5\" r=\".5\" fill=\"currentColor\"/><circle cx=\"17.5\" cy=\"10.5\" r=\".5\" fill=\"currentColor\"/><circle cx=\"8.5\" cy=\"7.5\" r=\".5\" fill=\"currentColor\"/><circle cx=\"6.5\" cy=\"12.5\" r=\".5\" fill=\"currentColor\"/><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\"/>",
|
|
498
|
+
repeat: "<path d=\"m17 2 4 4-4 4\"/><path d=\"M3 11v-1a4 4 0 0 1 4-4h14\"/><path d=\"m7 22-4-4 4-4\"/><path d=\"M21 13v1a4 4 0 0 1-4 4H3\"/>",
|
|
499
|
+
spline: "<circle cx=\"19\" cy=\"5\" r=\"2\"/><circle cx=\"5\" cy=\"19\" r=\"2\"/><path d=\"M5 17A12 12 0 0 1 17 5\"/>",
|
|
500
|
+
timer: "<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\"/>",
|
|
501
|
+
trendingDown: "<polyline points=\"22 17 13.5 8.5 8.5 13.5 2 7\"/><polyline points=\"16 17 22 17 22 11\"/>",
|
|
502
|
+
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\"/>",
|
|
503
|
+
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\"/>",
|
|
456
504
|
blend: "<circle cx=\"9\" cy=\"9\" r=\"7\"/><circle cx=\"15\" cy=\"15\" r=\"7\"/>",
|
|
457
505
|
clipboard: "<rect width=\"8\" height=\"4\" x=\"8\" y=\"2\" rx=\"1\" ry=\"1\"/><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\"/>",
|
|
458
506
|
contrast: "<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 18a6 6 0 0 0 0-12v12z\"/>",
|
|
459
507
|
grip: "<circle cx=\"9\" cy=\"12\" r=\"1\"/><circle cx=\"9\" cy=\"5\" r=\"1\"/><circle cx=\"9\" cy=\"19\" r=\"1\"/><circle cx=\"15\" cy=\"12\" r=\"1\"/><circle cx=\"15\" cy=\"5\" r=\"1\"/><circle cx=\"15\" cy=\"19\" r=\"1\"/>",
|
|
508
|
+
help: "<circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"/><path d=\"M12 17h.01\"/>",
|
|
460
509
|
monitor: "<rect width=\"20\" height=\"14\" x=\"2\" y=\"3\" rx=\"2\"/><line x1=\"8\" x2=\"16\" y1=\"21\" y2=\"21\"/><line x1=\"12\" x2=\"12\" y1=\"17\" y2=\"21\"/>",
|
|
461
510
|
moon: "<path d=\"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z\"/>",
|
|
462
511
|
sun: "<circle cx=\"12\" cy=\"12\" r=\"4\"/><path d=\"M12 2v2\"/><path d=\"M12 20v2\"/><path d=\"m4.93 4.93 1.41 1.41\"/><path d=\"m17.66 17.66 1.41 1.41\"/><path d=\"M2 12h2\"/><path d=\"M20 12h2\"/><path d=\"m6.34 17.66-1.41 1.41\"/><path d=\"m19.07 4.93-1.41 1.41\"/>",
|
|
@@ -477,7 +526,7 @@ const createIcon = (name, className) => {
|
|
|
477
526
|
};
|
|
478
527
|
//#endregion
|
|
479
528
|
//#region package.json
|
|
480
|
-
var version = "0.
|
|
529
|
+
var version = "0.7.0";
|
|
481
530
|
//#endregion
|
|
482
531
|
//#region src/ui/clipboard.ts
|
|
483
532
|
/**
|
|
@@ -529,6 +578,29 @@ const compact = new Intl.NumberFormat("en-US", {
|
|
|
529
578
|
});
|
|
530
579
|
const formatCount = (value) => Math.abs(value) < COMPACT_FROM ? exact.format(value) : compact.format(value);
|
|
531
580
|
//#endregion
|
|
581
|
+
//#region src/ui/metric-help.ts
|
|
582
|
+
/**
|
|
583
|
+
* One plain-language line per metric: what it is, and what a bad reading
|
|
584
|
+
* means. Shown as the label's tooltip and, with "explain metrics" on, under
|
|
585
|
+
* each row. Keyed like the stat rows; the graphs reuse `fps`, `cpu`, `gpu`.
|
|
586
|
+
*/
|
|
587
|
+
const METRIC_HELP = {
|
|
588
|
+
calls: "Separate draw commands sent to the GPU this frame. Fewer is better: merge or instance meshes that share a material.",
|
|
589
|
+
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).",
|
|
590
|
+
fps: "Frames drawn per second. Higher is smoother; most displays top out at 60 or 120.",
|
|
591
|
+
geometries: "Geometries held in GPU memory. Should level off; a steady climb is a leak, so dispose() what you remove.",
|
|
592
|
+
gpu: "Time the graphics card spent drawing the frame. High means shaders, overdraw or resolution are the bottleneck.",
|
|
593
|
+
hitches: "Frames that took over twice as long as usual, in the last 1,000 frames. Each one is a visible stutter.",
|
|
594
|
+
lines: "Line segments drawn this frame.",
|
|
595
|
+
low: "Frame rate during the slowest 1% of frames. Far below FPS means stutter the average hides.",
|
|
596
|
+
p99: "99% of frames finished within this many ms. Above the frame budget (16.7 ms at 60 FPS) means dropped frames.",
|
|
597
|
+
passes: "Times renderer.render() ran this frame. Shadows, post-processing and extra cameras each add passes.",
|
|
598
|
+
points: "Points drawn this frame.",
|
|
599
|
+
shaders: "Compiled shader programs. A jump mid-session usually means a stutter while a new material compiled.",
|
|
600
|
+
textures: "Textures held in GPU memory. Should level off; a steady climb is a leak, so dispose() what you remove.",
|
|
601
|
+
triangles: "Triangles drawn this frame. Mostly GPU cost: use lower-poly models or level of detail to cut it."
|
|
602
|
+
};
|
|
603
|
+
//#endregion
|
|
532
604
|
//#region src/ui/report.ts
|
|
533
605
|
/**
|
|
534
606
|
* A plain-text snapshot of the monitor for bug reports: versions, backend and
|
|
@@ -600,7 +672,7 @@ const formatReport = (monitor) => renderReport({
|
|
|
600
672
|
});
|
|
601
673
|
//#endregion
|
|
602
674
|
//#region src/ui/sparkline.ts
|
|
603
|
-
function drawSparkline(ctx, width, height, values, style) {
|
|
675
|
+
function drawSparkline(ctx, width, height, values, style, guide) {
|
|
604
676
|
ctx.clearRect(0, 0, width, height);
|
|
605
677
|
if (values.length < 2) return;
|
|
606
678
|
let max = 0;
|
|
@@ -619,12 +691,24 @@ function drawSparkline(ctx, width, height, values, style) {
|
|
|
619
691
|
ctx.closePath();
|
|
620
692
|
ctx.fillStyle = style.fill;
|
|
621
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
|
+
}
|
|
622
705
|
}
|
|
623
706
|
//#endregion
|
|
624
707
|
//#region src/ui/performance-view.ts
|
|
625
708
|
const TIMINGS = [
|
|
626
709
|
{
|
|
627
710
|
format: (value) => Math.round(value).toString(),
|
|
711
|
+
icon: "gauge",
|
|
628
712
|
label: "FPS",
|
|
629
713
|
metric: "fps",
|
|
630
714
|
style: {
|
|
@@ -641,6 +725,7 @@ const TIMINGS = [
|
|
|
641
725
|
},
|
|
642
726
|
{
|
|
643
727
|
format: (value) => value.toFixed(1),
|
|
728
|
+
icon: "cpu",
|
|
644
729
|
label: "CPU",
|
|
645
730
|
metric: "cpu",
|
|
646
731
|
style: {
|
|
@@ -657,6 +742,7 @@ const TIMINGS = [
|
|
|
657
742
|
},
|
|
658
743
|
{
|
|
659
744
|
format: (value) => value.toFixed(1),
|
|
745
|
+
icon: "zap",
|
|
660
746
|
label: "GPU",
|
|
661
747
|
metric: "gpu",
|
|
662
748
|
style: {
|
|
@@ -700,78 +786,130 @@ const readTiming = (sample, metric) => {
|
|
|
700
786
|
}
|
|
701
787
|
};
|
|
702
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);
|
|
703
791
|
const NUMBERS = [
|
|
704
792
|
{
|
|
793
|
+
format: whole,
|
|
794
|
+
icon: "gauge",
|
|
705
795
|
key: "fps",
|
|
706
796
|
label: "FPS",
|
|
707
|
-
|
|
797
|
+
value: (sample) => sample.fps || null
|
|
708
798
|
},
|
|
709
799
|
{
|
|
800
|
+
format: formatCount,
|
|
801
|
+
icon: "layers",
|
|
710
802
|
key: "calls",
|
|
711
803
|
label: "Calls",
|
|
712
|
-
|
|
804
|
+
value: (sample) => sample.render.calls
|
|
713
805
|
},
|
|
714
806
|
{
|
|
807
|
+
format: tenths,
|
|
808
|
+
icon: "cpu",
|
|
715
809
|
key: "cpu",
|
|
716
810
|
label: "CPU",
|
|
717
|
-
|
|
811
|
+
unit: "ms",
|
|
812
|
+
value: (sample) => sample.cpu
|
|
718
813
|
},
|
|
719
814
|
{
|
|
815
|
+
format: tenths,
|
|
816
|
+
icon: "zap",
|
|
720
817
|
key: "gpu",
|
|
721
818
|
label: "GPU",
|
|
722
|
-
|
|
819
|
+
unit: "ms",
|
|
820
|
+
value: (sample) => sample.gpu.available ? sample.gpu.ms : null
|
|
723
821
|
},
|
|
724
822
|
{
|
|
823
|
+
format: whole,
|
|
824
|
+
icon: "trendingDown",
|
|
725
825
|
key: "low",
|
|
726
826
|
label: "1% low",
|
|
727
|
-
|
|
827
|
+
value: (_sample, stats) => stats.frames ? stats.lowFps : null
|
|
728
828
|
},
|
|
729
829
|
{
|
|
830
|
+
format: tenths,
|
|
831
|
+
icon: "timer",
|
|
730
832
|
key: "p99",
|
|
731
833
|
label: "Frame p99",
|
|
732
|
-
|
|
834
|
+
unit: "ms",
|
|
835
|
+
value: (_sample, stats) => stats.frames ? stats.p99Ms : null
|
|
733
836
|
},
|
|
734
837
|
{
|
|
838
|
+
format: formatCount,
|
|
839
|
+
icon: "activity",
|
|
735
840
|
key: "hitches",
|
|
736
841
|
label: "Hitches",
|
|
737
|
-
|
|
842
|
+
value: (_sample, stats) => stats.frames ? stats.hitches : null
|
|
738
843
|
},
|
|
739
844
|
{
|
|
845
|
+
format: formatCount,
|
|
846
|
+
icon: "triangle",
|
|
740
847
|
key: "triangles",
|
|
741
848
|
label: "Triangles",
|
|
742
|
-
|
|
849
|
+
value: (sample) => sample.render.triangles
|
|
743
850
|
},
|
|
744
851
|
{
|
|
852
|
+
format: formatCount,
|
|
853
|
+
icon: "spline",
|
|
745
854
|
key: "lines",
|
|
746
855
|
label: "Lines",
|
|
747
|
-
|
|
856
|
+
value: (sample) => sample.render.lines
|
|
748
857
|
},
|
|
749
858
|
{
|
|
859
|
+
format: formatCount,
|
|
860
|
+
icon: "circleDot",
|
|
750
861
|
key: "points",
|
|
751
862
|
label: "Points",
|
|
752
|
-
|
|
863
|
+
value: (sample) => sample.render.points
|
|
753
864
|
},
|
|
754
865
|
{
|
|
866
|
+
format: formatCount,
|
|
867
|
+
icon: "repeat",
|
|
755
868
|
key: "passes",
|
|
756
869
|
label: "Render passes",
|
|
757
|
-
|
|
870
|
+
value: (sample) => sample.render.passes
|
|
758
871
|
},
|
|
759
872
|
{
|
|
873
|
+
format: formatCount,
|
|
874
|
+
icon: "box",
|
|
760
875
|
key: "geometries",
|
|
761
876
|
label: "Geometries",
|
|
762
|
-
|
|
877
|
+
value: (sample) => sample.resources.geometries
|
|
763
878
|
},
|
|
764
879
|
{
|
|
880
|
+
format: formatCount,
|
|
881
|
+
icon: "image",
|
|
765
882
|
key: "textures",
|
|
766
883
|
label: "Textures",
|
|
767
|
-
|
|
884
|
+
value: (sample) => sample.resources.textures
|
|
768
885
|
},
|
|
769
886
|
{
|
|
887
|
+
format: formatCount,
|
|
888
|
+
icon: "palette",
|
|
770
889
|
key: "shaders",
|
|
771
890
|
label: "Shaders",
|
|
772
|
-
|
|
891
|
+
value: (sample) => sample.resources.programs
|
|
773
892
|
}
|
|
774
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
|
+
};
|
|
775
913
|
/**
|
|
776
914
|
* Host-agnostic widget that paints {@link Sample}s. Mount {@link element}
|
|
777
915
|
* anywhere; the view drives its own throttled repaint. Compact is the floating
|
|
@@ -804,6 +942,7 @@ var PerformanceView = class {
|
|
|
804
942
|
hudGridEl;
|
|
805
943
|
hudGraphs = /* @__PURE__ */ new Map();
|
|
806
944
|
hudNumberEls = /* @__PURE__ */ new Map();
|
|
945
|
+
budgets;
|
|
807
946
|
rafId = null;
|
|
808
947
|
lastPaintAt = 0;
|
|
809
948
|
resizeObserver;
|
|
@@ -816,6 +955,7 @@ var PerformanceView = class {
|
|
|
816
955
|
this.theme.setOverride(this.settings.theme);
|
|
817
956
|
this.mode = options.mode ?? "full";
|
|
818
957
|
this.minIntervalMs = 1e3 / (options.refreshHz ?? 10);
|
|
958
|
+
this.budgets = resolveBudgets(options.budgets);
|
|
819
959
|
this.element = document.createElement("div");
|
|
820
960
|
this.element.className = "perf-monitor";
|
|
821
961
|
this.applyModeClass();
|
|
@@ -845,6 +985,11 @@ var PerformanceView = class {
|
|
|
845
985
|
getMode() {
|
|
846
986
|
return this.mode;
|
|
847
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
|
+
}
|
|
848
993
|
dispose() {
|
|
849
994
|
this.stop();
|
|
850
995
|
this.unsubscribe();
|
|
@@ -866,15 +1011,9 @@ var PerformanceView = class {
|
|
|
866
1011
|
if (this.settings.info) this.renderFooter();
|
|
867
1012
|
return;
|
|
868
1013
|
}
|
|
869
|
-
for (const config of TIMINGS)
|
|
870
|
-
const available = isTimingAvailable(sample, config.metric);
|
|
871
|
-
const valueEl = this.graphValueEls.get(config.metric);
|
|
872
|
-
valueEl.textContent = available ? this.formatTiming(config, readTiming(sample, config.metric)) : "unavailable";
|
|
873
|
-
const graph = this.graphCanvases.get(config.metric);
|
|
874
|
-
drawSparkline(graph.ctx, graph.width, graph.height, available ? this.monitor.getHistory(config.metric) : [], config.style[this.theme.resolved]);
|
|
875
|
-
}
|
|
1014
|
+
for (const config of TIMINGS) this.paintGraph(config, sample, this.graphValueEls.get(config.metric), this.graphCanvases.get(config.metric), "unavailable");
|
|
876
1015
|
const stats = this.monitor.getFrameStats();
|
|
877
|
-
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);
|
|
878
1017
|
this.renderFooter();
|
|
879
1018
|
}
|
|
880
1019
|
/** The monitor caches the environment once its backend settles; until then this re-reads. */
|
|
@@ -898,16 +1037,38 @@ var PerformanceView = class {
|
|
|
898
1037
|
renderHud(sample) {
|
|
899
1038
|
const stats = this.hudNumberEls.size > 0 ? this.monitor.getFrameStats() : null;
|
|
900
1039
|
for (const [key, element] of this.hudNumberEls) {
|
|
901
|
-
const config =
|
|
902
|
-
if (config && stats)
|
|
1040
|
+
const config = NUMBER_BY_KEY.get(key);
|
|
1041
|
+
if (config && stats) this.paintNumber(config, element, sample, stats);
|
|
903
1042
|
}
|
|
904
1043
|
for (const [metric, graph] of this.hudGraphs) {
|
|
905
1044
|
const config = TIMINGS.find((timing) => timing.metric === metric);
|
|
906
|
-
|
|
907
|
-
graph.valueEl.textContent = available ? this.formatTiming(config, readTiming(sample, metric)) : "—";
|
|
908
|
-
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, "—");
|
|
909
1046
|
}
|
|
910
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
|
+
}
|
|
911
1072
|
formatTiming(config, value) {
|
|
912
1073
|
return `${config.format(value)}${config.unit ? ` ${config.unit}` : ""}`;
|
|
913
1074
|
}
|
|
@@ -951,7 +1112,14 @@ var PerformanceView = class {
|
|
|
951
1112
|
dimLabel.className = "perf-monitor__label";
|
|
952
1113
|
dimLabel.textContent = "dim on leave";
|
|
953
1114
|
dimRow.append(createIcon("blend", "perf-monitor__icon"), dimLabel, this.dimCheckbox);
|
|
954
|
-
|
|
1115
|
+
const explainRow = document.createElement("label");
|
|
1116
|
+
explainRow.className = "perf-monitor__row";
|
|
1117
|
+
const explainCheckbox = this.buildCheckbox(false, "Explain each metric", (on) => this.element.classList.toggle("perf-monitor--explain", on));
|
|
1118
|
+
const explainLabel = document.createElement("span");
|
|
1119
|
+
explainLabel.className = "perf-monitor__label";
|
|
1120
|
+
explainLabel.textContent = "explain metrics";
|
|
1121
|
+
explainRow.append(createIcon("help", "perf-monitor__icon"), explainLabel, explainCheckbox);
|
|
1122
|
+
options.append(themeRow, dimRow, explainRow, this.buildReportRow());
|
|
955
1123
|
const graphs = document.createElement("div");
|
|
956
1124
|
graphs.className = "perf-monitor__graphs";
|
|
957
1125
|
for (const config of TIMINGS) {
|
|
@@ -959,21 +1127,25 @@ var PerformanceView = class {
|
|
|
959
1127
|
graph.className = "perf-monitor__graph";
|
|
960
1128
|
const canvas = document.createElement("canvas");
|
|
961
1129
|
canvas.className = "perf-monitor__canvas";
|
|
962
|
-
const overlay = document.createElement("
|
|
963
|
-
overlay.className = "perf-monitor__graph-overlay";
|
|
964
|
-
const head = document.createElement("
|
|
1130
|
+
const overlay = document.createElement("label");
|
|
1131
|
+
overlay.className = "perf-monitor__graph-overlay perf-monitor__graph-overlay--toggle";
|
|
1132
|
+
const head = document.createElement("span");
|
|
965
1133
|
head.className = "perf-monitor__graph-head";
|
|
966
1134
|
const checkbox = this.buildCheckbox(this.settings.isGraphEnabled(config.metric), `Show ${config.label} graph in HUD`, (on) => this.settings.setGraphEnabled(config.metric, on));
|
|
967
1135
|
this.graphCheckboxes.set(config.metric, checkbox);
|
|
968
1136
|
const label = document.createElement("span");
|
|
969
1137
|
label.className = "perf-monitor__graph-label";
|
|
970
1138
|
label.textContent = config.label;
|
|
1139
|
+
label.title = METRIC_HELP[config.metric] ?? "";
|
|
971
1140
|
const value = document.createElement("span");
|
|
972
1141
|
value.className = "perf-monitor__graph-value";
|
|
973
1142
|
value.textContent = "—";
|
|
974
1143
|
this.graphValueEls.set(config.metric, value);
|
|
975
|
-
|
|
976
|
-
|
|
1144
|
+
const tail = document.createElement("span");
|
|
1145
|
+
tail.className = "perf-monitor__graph-head";
|
|
1146
|
+
tail.append(value, checkbox);
|
|
1147
|
+
head.append(createIcon(config.icon, "perf-monitor__icon"), label);
|
|
1148
|
+
overlay.append(head, tail);
|
|
977
1149
|
graph.append(canvas, overlay);
|
|
978
1150
|
graphs.append(graph);
|
|
979
1151
|
const ctx = canvas.getContext("2d");
|
|
@@ -994,14 +1166,19 @@ var PerformanceView = class {
|
|
|
994
1166
|
rowEl.className = "perf-monitor__row";
|
|
995
1167
|
const checkbox = this.buildCheckbox(this.settings.isNumberEnabled(config.key), `Show ${config.label} in HUD`, (on) => this.settings.setNumberEnabled(config.key, on));
|
|
996
1168
|
this.statCheckboxes.set(config.key, checkbox);
|
|
1169
|
+
const help = METRIC_HELP[config.key] ?? "";
|
|
997
1170
|
const label = document.createElement("span");
|
|
998
1171
|
label.className = "perf-monitor__label";
|
|
999
1172
|
label.textContent = config.label;
|
|
1173
|
+
label.title = help;
|
|
1000
1174
|
const value = document.createElement("span");
|
|
1001
1175
|
value.className = "perf-monitor__value";
|
|
1002
1176
|
value.textContent = "—";
|
|
1003
1177
|
this.statValueEls.set(config.key, value);
|
|
1004
|
-
|
|
1178
|
+
const hint = document.createElement("span");
|
|
1179
|
+
hint.className = "perf-monitor__hint";
|
|
1180
|
+
hint.textContent = help;
|
|
1181
|
+
rowEl.append(createIcon(config.icon, "perf-monitor__icon"), label, value, checkbox, hint);
|
|
1005
1182
|
stats.append(rowEl);
|
|
1006
1183
|
}
|
|
1007
1184
|
const footer = document.createElement("div");
|
|
@@ -1029,7 +1206,7 @@ var PerformanceView = class {
|
|
|
1029
1206
|
this.footerGpuEl.className = "perf-monitor__footer-gpu";
|
|
1030
1207
|
this.footerHardwareEl.append(this.footerBackendEl, this.footerGpuEl);
|
|
1031
1208
|
footerBody.append(softwareRow, this.footerHardwareEl);
|
|
1032
|
-
footer.append(this.infoCheckbox
|
|
1209
|
+
footer.append(createIcon("info", "perf-monitor__icon"), footerBody, this.infoCheckbox);
|
|
1033
1210
|
this.element.append(hud, options, graphs, stats, footer);
|
|
1034
1211
|
}
|
|
1035
1212
|
rebuildHud() {
|
|
@@ -1050,6 +1227,7 @@ var PerformanceView = class {
|
|
|
1050
1227
|
const label = document.createElement("span");
|
|
1051
1228
|
label.className = "perf-monitor__graph-label";
|
|
1052
1229
|
label.textContent = config.label;
|
|
1230
|
+
label.title = METRIC_HELP[config.metric] ?? "";
|
|
1053
1231
|
const value = document.createElement("span");
|
|
1054
1232
|
value.className = "perf-monitor__graph-value";
|
|
1055
1233
|
value.textContent = "—";
|
|
@@ -1075,6 +1253,7 @@ var PerformanceView = class {
|
|
|
1075
1253
|
const label = document.createElement("span");
|
|
1076
1254
|
label.className = "perf-monitor__hud-label";
|
|
1077
1255
|
label.textContent = config.label;
|
|
1256
|
+
label.title = METRIC_HELP[config.key] ?? "";
|
|
1078
1257
|
const value = document.createElement("span");
|
|
1079
1258
|
value.className = "perf-monitor__hud-value";
|
|
1080
1259
|
value.textContent = "—";
|
|
@@ -1082,6 +1261,7 @@ var PerformanceView = class {
|
|
|
1082
1261
|
this.hudGridEl.append(item);
|
|
1083
1262
|
this.hudNumberEls.set(config.key, value);
|
|
1084
1263
|
}
|
|
1264
|
+
this.applyBudgetTitles();
|
|
1085
1265
|
this.resizeCanvases();
|
|
1086
1266
|
}
|
|
1087
1267
|
onSelectionChanged() {
|
|
@@ -1182,6 +1362,7 @@ const PERF_HUD_STYLES = `
|
|
|
1182
1362
|
--perf-row: rgba(255, 255, 255, 0.04);
|
|
1183
1363
|
--perf-border: rgba(255, 255, 255, 0.12);
|
|
1184
1364
|
--perf-accent: #60a5fa;
|
|
1365
|
+
--perf-warn: #f59e0b;
|
|
1185
1366
|
--perf-shadow: 0 8px 24px rgba(0, 0, 0, 0.32);
|
|
1186
1367
|
|
|
1187
1368
|
color-scheme: dark;
|
|
@@ -1196,6 +1377,7 @@ const PERF_HUD_STYLES = `
|
|
|
1196
1377
|
--perf-row: rgba(0, 0, 0, 0.05);
|
|
1197
1378
|
--perf-border: rgba(0, 0, 0, 0.12);
|
|
1198
1379
|
--perf-accent: #2563eb;
|
|
1380
|
+
--perf-warn: #b45309;
|
|
1199
1381
|
--perf-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
|
|
1200
1382
|
|
|
1201
1383
|
color-scheme: light;
|
|
@@ -1249,8 +1431,9 @@ const PERF_HUD_STYLES = `
|
|
|
1249
1431
|
cursor: pointer;
|
|
1250
1432
|
}
|
|
1251
1433
|
|
|
1252
|
-
.perf-monitor__graph-overlay
|
|
1434
|
+
.perf-monitor__graph-overlay--toggle {
|
|
1253
1435
|
pointer-events: auto;
|
|
1436
|
+
cursor: pointer;
|
|
1254
1437
|
}
|
|
1255
1438
|
|
|
1256
1439
|
.perf-monitor__graph-label {
|
|
@@ -1282,6 +1465,25 @@ const PERF_HUD_STYLES = `
|
|
|
1282
1465
|
cursor: pointer;
|
|
1283
1466
|
}
|
|
1284
1467
|
|
|
1468
|
+
/* Hidden until "explain metrics" is on; then the row wraps it under the label. */
|
|
1469
|
+
.perf-monitor__hint {
|
|
1470
|
+
display: none;
|
|
1471
|
+
flex-basis: 100%;
|
|
1472
|
+
padding: 0 0 2px 18px;
|
|
1473
|
+
color: var(--perf-muted);
|
|
1474
|
+
font-size: 10px;
|
|
1475
|
+
line-height: 1.35;
|
|
1476
|
+
opacity: 0.85;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
.perf-monitor--explain .perf-monitor__hint {
|
|
1480
|
+
display: block;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
.perf-monitor--explain .perf-monitor__section .perf-monitor__row {
|
|
1484
|
+
flex-wrap: wrap;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1285
1487
|
.perf-monitor__row:nth-child(even) {
|
|
1286
1488
|
background: var(--perf-row);
|
|
1287
1489
|
}
|
|
@@ -1487,13 +1689,15 @@ const PERF_HUD_STYLES = `
|
|
|
1487
1689
|
}
|
|
1488
1690
|
|
|
1489
1691
|
.perf-monitor__badge.is-fallback {
|
|
1490
|
-
border-color:
|
|
1491
|
-
color:
|
|
1692
|
+
border-color: var(--perf-warn);
|
|
1693
|
+
color: var(--perf-warn);
|
|
1492
1694
|
}
|
|
1493
1695
|
|
|
1494
|
-
.
|
|
1495
|
-
|
|
1496
|
-
|
|
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);
|
|
1497
1701
|
}
|
|
1498
1702
|
|
|
1499
1703
|
.perf-monitor__link {
|
|
@@ -1514,7 +1718,8 @@ const PERF_HUD_STYLES = `
|
|
|
1514
1718
|
.perf-monitor--compact .perf-monitor__graphs,
|
|
1515
1719
|
.perf-monitor--compact .perf-monitor__section,
|
|
1516
1720
|
.perf-monitor--compact:not(.perf-monitor--info) .perf-monitor__footer,
|
|
1517
|
-
.perf-monitor--compact .perf-monitor__footer > .perf-monitor__checkbox
|
|
1721
|
+
.perf-monitor--compact .perf-monitor__footer > .perf-monitor__checkbox,
|
|
1722
|
+
.perf-monitor--compact .perf-monitor__footer > .perf-monitor__icon {
|
|
1518
1723
|
display: none;
|
|
1519
1724
|
}
|
|
1520
1725
|
|
|
@@ -1545,6 +1750,11 @@ const PERF_HUD_STYLES = `
|
|
|
1545
1750
|
max-width: 100%;
|
|
1546
1751
|
}
|
|
1547
1752
|
|
|
1753
|
+
/* Room for the hints, so most fit on two or three lines. Plus padding, this fills the host's 20rem cap. */
|
|
1754
|
+
.perf-hud--full > .perf-monitor--explain {
|
|
1755
|
+
width: 19rem;
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1548
1758
|
.perf-hud > .perf-monitor {
|
|
1549
1759
|
position: relative;
|
|
1550
1760
|
z-index: 1;
|
|
@@ -1682,6 +1892,7 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
1682
1892
|
const toggle = disc("Toggle full performance metrics", "", "sliders");
|
|
1683
1893
|
tools.append(grip, toggle);
|
|
1684
1894
|
const view = new PerformanceView({
|
|
1895
|
+
budgets: options.budgets,
|
|
1685
1896
|
mode: options.mode ?? "compact",
|
|
1686
1897
|
monitor,
|
|
1687
1898
|
refreshHz: options.refreshHz,
|
|
@@ -1757,6 +1968,7 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
1757
1968
|
},
|
|
1758
1969
|
element: host,
|
|
1759
1970
|
getMode: () => view.getMode(),
|
|
1971
|
+
setBudgets: (budgets) => view.setBudgets(budgets),
|
|
1760
1972
|
setMode,
|
|
1761
1973
|
getTheme: () => theme.mode,
|
|
1762
1974
|
setTheme: (mode) => theme.setMode(mode),
|
|
@@ -1766,6 +1978,6 @@ const mountPerfHud = (monitor, options = {}) => {
|
|
|
1766
1978
|
};
|
|
1767
1979
|
};
|
|
1768
1980
|
//#endregion
|
|
1769
|
-
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 };
|
|
1770
1982
|
|
|
1771
|
-
//# sourceMappingURL=mount-perf-hud-
|
|
1983
|
+
//# sourceMappingURL=mount-perf-hud-DqY_tP6s.js.map
|