stats-gl 3.2.0 → 3.4.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 +9 -2
- package/dist/main.cjs +104 -30
- package/dist/main.cjs.map +1 -1
- package/dist/main.js +104 -30
- package/dist/main.js.map +1 -1
- package/dist/panel.cjs +1 -0
- package/dist/panel.cjs.map +1 -1
- package/dist/panel.js +1 -0
- package/dist/panel.js.map +1 -1
- package/dist/panelVsync.cjs +55 -0
- package/dist/panelVsync.cjs.map +1 -0
- package/dist/panelVsync.js +55 -0
- package/dist/panelVsync.js.map +1 -0
- package/dist/stats-gl.d.ts +19 -5
- package/lib/main.ts +152 -36
- package/lib/panel.ts +4 -3
- package/lib/panelVsync.ts +74 -0
- package/package.json +1 -1
package/lib/main.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type * as THREE from 'three';
|
|
2
2
|
import { Panel } from './panel';
|
|
3
|
+
import { PanelVSync } from './panelVsync';
|
|
3
4
|
|
|
4
5
|
interface StatsOptions {
|
|
5
6
|
trackGPU?: boolean;
|
|
7
|
+
trackCPT?: boolean;
|
|
8
|
+
trackHz?: boolean;
|
|
6
9
|
logsPerSecond?: number;
|
|
7
10
|
graphsPerSecond?: number;
|
|
8
11
|
samplesLog?: number;
|
|
@@ -22,6 +25,13 @@ interface AverageData {
|
|
|
22
25
|
graph: number[];
|
|
23
26
|
}
|
|
24
27
|
|
|
28
|
+
|
|
29
|
+
interface VSyncInfo {
|
|
30
|
+
refreshRate: number;
|
|
31
|
+
frameTime: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
25
35
|
interface InfoData {
|
|
26
36
|
render: {
|
|
27
37
|
timestamp: number;
|
|
@@ -37,6 +47,8 @@ class Stats {
|
|
|
37
47
|
public horizontal: boolean;
|
|
38
48
|
public minimal: boolean;
|
|
39
49
|
public trackGPU: boolean;
|
|
50
|
+
public trackHz: boolean;
|
|
51
|
+
public trackCPT: boolean;
|
|
40
52
|
public samplesLog: number;
|
|
41
53
|
public samplesGraph: number;
|
|
42
54
|
public precision: number;
|
|
@@ -55,16 +67,17 @@ class Stats {
|
|
|
55
67
|
private frameTimes: number[] = []; // Store frame timestamps
|
|
56
68
|
|
|
57
69
|
private renderCount = 0;
|
|
58
|
-
private isRunningCPUProfiling = false;
|
|
59
70
|
|
|
60
71
|
private totalCpuDuration = 0;
|
|
61
72
|
private totalGpuDuration = 0;
|
|
62
73
|
private totalGpuDurationCompute = 0;
|
|
63
74
|
|
|
75
|
+
private _panelId: number;
|
|
64
76
|
private fpsPanel: Panel;
|
|
65
77
|
private msPanel: Panel;
|
|
66
78
|
private gpuPanel: Panel | null = null;
|
|
67
79
|
private gpuPanelCompute: Panel | null = null;
|
|
80
|
+
private vsyncPanel: PanelVSync | null = null;
|
|
68
81
|
|
|
69
82
|
public averageFps: AverageData = { logs: [], graph: [] };
|
|
70
83
|
public averageCpu: AverageData = { logs: [], graph: [] };
|
|
@@ -78,11 +91,29 @@ class Stats {
|
|
|
78
91
|
private lastValue: { [key: string]: number } = {};
|
|
79
92
|
private prevTextTime: number;
|
|
80
93
|
|
|
94
|
+
private readonly VSYNC_RATES: VSyncInfo[] = [
|
|
95
|
+
{ refreshRate: 60, frameTime: 16.67 },
|
|
96
|
+
{ refreshRate: 75, frameTime: 13.33 },
|
|
97
|
+
{ refreshRate: 90, frameTime: 11.11 },
|
|
98
|
+
{ refreshRate: 120, frameTime: 8.33 },
|
|
99
|
+
{ refreshRate: 144, frameTime: 6.94 },
|
|
100
|
+
{ refreshRate: 165, frameTime: 6.06 },
|
|
101
|
+
{ refreshRate: 240, frameTime: 4.17 }
|
|
102
|
+
];
|
|
103
|
+
private detectedVSync: VSyncInfo | null = null;
|
|
104
|
+
private frameTimeHistory: number[] = [];
|
|
105
|
+
private readonly HISTORY_SIZE = 120; // 2 seconds worth of frames at 60fps
|
|
106
|
+
private readonly VSYNC_THRESHOLD = 0.05; // 5% tolerance
|
|
107
|
+
private lastFrameTime: number = 0;
|
|
108
|
+
|
|
109
|
+
|
|
81
110
|
|
|
82
111
|
static Panel = Panel;
|
|
83
112
|
|
|
84
113
|
constructor({
|
|
85
114
|
trackGPU = false,
|
|
115
|
+
trackCPT = false,
|
|
116
|
+
trackHz = false,
|
|
86
117
|
logsPerSecond = 4,
|
|
87
118
|
graphsPerSecond = 30,
|
|
88
119
|
samplesLog = 40,
|
|
@@ -96,6 +127,8 @@ class Stats {
|
|
|
96
127
|
this.horizontal = horizontal;
|
|
97
128
|
this.minimal = minimal;
|
|
98
129
|
this.trackGPU = trackGPU;
|
|
130
|
+
this.trackCPT = trackCPT;
|
|
131
|
+
this.trackHz = trackHz;
|
|
99
132
|
this.samplesLog = samplesLog;
|
|
100
133
|
this.samplesGraph = samplesGraph;
|
|
101
134
|
this.precision = precision;
|
|
@@ -114,9 +147,16 @@ class Stats {
|
|
|
114
147
|
|
|
115
148
|
this.prevCpuTime = this.beginTime;
|
|
116
149
|
|
|
150
|
+
this._panelId = 0
|
|
117
151
|
// Create panels
|
|
118
|
-
this.fpsPanel = this.addPanel(new Stats.Panel('FPS', '#0ff', '#002')
|
|
119
|
-
this.msPanel = this.addPanel(new Stats.Panel('CPU', '#0f0', '#020')
|
|
152
|
+
this.fpsPanel = this.addPanel(new Stats.Panel('FPS', '#0ff', '#002'));
|
|
153
|
+
this.msPanel = this.addPanel(new Stats.Panel('CPU', '#0f0', '#020'));
|
|
154
|
+
|
|
155
|
+
if (this.trackHz === true) {
|
|
156
|
+
this.vsyncPanel = new PanelVSync('', '#f0f', '#202');
|
|
157
|
+
this.dom.appendChild(this.vsyncPanel.canvas);
|
|
158
|
+
this.vsyncPanel.setOffset(56, 35);
|
|
159
|
+
}
|
|
120
160
|
|
|
121
161
|
this.setupEventListeners();
|
|
122
162
|
}
|
|
@@ -148,10 +188,10 @@ class Stats {
|
|
|
148
188
|
};
|
|
149
189
|
|
|
150
190
|
private handleResize = (): void => {
|
|
151
|
-
this.resizePanel(this.fpsPanel
|
|
152
|
-
this.resizePanel(this.msPanel
|
|
153
|
-
if (this.gpuPanel) this.resizePanel(this.gpuPanel
|
|
154
|
-
if (this.gpuPanelCompute) this.resizePanel(this.gpuPanelCompute
|
|
191
|
+
this.resizePanel(this.fpsPanel);
|
|
192
|
+
this.resizePanel(this.msPanel);
|
|
193
|
+
if (this.gpuPanel) this.resizePanel(this.gpuPanel);
|
|
194
|
+
if (this.gpuPanelCompute) this.resizePanel(this.gpuPanelCompute);
|
|
155
195
|
};
|
|
156
196
|
|
|
157
197
|
public async init(
|
|
@@ -190,24 +230,26 @@ class Stats {
|
|
|
190
230
|
|
|
191
231
|
private async handleWebGPURenderer(renderer: any): Promise<boolean> {
|
|
192
232
|
if (renderer.isWebGPURenderer) {
|
|
193
|
-
if (this.trackGPU) {
|
|
233
|
+
if (this.trackGPU || this.trackCPT) {
|
|
194
234
|
renderer.backend.trackTimestamp = true;
|
|
195
235
|
if (await renderer.hasFeatureAsync('timestamp-query')) {
|
|
196
236
|
this.initializeWebGPUPanels();
|
|
197
237
|
}
|
|
198
238
|
}
|
|
199
239
|
this.info = renderer.info;
|
|
240
|
+
this.patchThreeWebGPU(renderer);
|
|
200
241
|
return true;
|
|
201
242
|
}
|
|
202
243
|
return false;
|
|
203
244
|
}
|
|
204
245
|
|
|
205
246
|
private initializeWebGPUPanels(): void {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
247
|
+
if (this.trackGPU) {
|
|
248
|
+
this.gpuPanel = this.addPanel(new Stats.Panel('GPU', '#ff0', '#220'));
|
|
249
|
+
}
|
|
250
|
+
if (this.trackCPT) {
|
|
251
|
+
this.gpuPanelCompute = this.addPanel(new Stats.Panel('CPT', '#e1e1e1', '#212121'));
|
|
252
|
+
}
|
|
211
253
|
}
|
|
212
254
|
|
|
213
255
|
private initializeWebGL(
|
|
@@ -237,15 +279,13 @@ class Stats {
|
|
|
237
279
|
if (this.gl) {
|
|
238
280
|
this.ext = this.gl.getExtension('EXT_disjoint_timer_query_webgl2');
|
|
239
281
|
if (this.ext) {
|
|
240
|
-
this.gpuPanel = this.addPanel(new Stats.Panel('GPU', '#ff0', '#220')
|
|
282
|
+
this.gpuPanel = this.addPanel(new Stats.Panel('GPU', '#ff0', '#220'));
|
|
241
283
|
}
|
|
242
284
|
}
|
|
243
285
|
}
|
|
244
286
|
|
|
245
287
|
public begin(): void {
|
|
246
|
-
|
|
247
|
-
this.beginProfiling('cpu-started');
|
|
248
|
-
}
|
|
288
|
+
this.beginProfiling('cpu-started');
|
|
249
289
|
|
|
250
290
|
if (!this.gl || !this.ext) return;
|
|
251
291
|
|
|
@@ -266,15 +306,14 @@ class Stats {
|
|
|
266
306
|
this.gpuQueries.push({ query: this.activeQuery });
|
|
267
307
|
this.activeQuery = null;
|
|
268
308
|
}
|
|
309
|
+
|
|
310
|
+
this.endProfiling('cpu-started', 'cpu-finished', 'cpu-duration');
|
|
269
311
|
}
|
|
270
312
|
|
|
271
313
|
public update(): void {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// Add to averages immediately after getting the duration
|
|
276
|
-
// this.addToAverage(this.totalCpuDuration, this.averageCpu);
|
|
277
|
-
}
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
this.endProfiling('cpu-started', 'cpu-finished', 'cpu-duration');
|
|
278
317
|
|
|
279
318
|
if (!this.info) {
|
|
280
319
|
this.processGpuQueries();
|
|
@@ -294,11 +333,10 @@ class Stats {
|
|
|
294
333
|
private resetCounters(): void {
|
|
295
334
|
this.renderCount = 0;
|
|
296
335
|
this.totalCpuDuration = 0;
|
|
297
|
-
this.beginProfiling('cpu-started');
|
|
298
336
|
this.beginTime = this.endInternal();
|
|
299
337
|
}
|
|
300
338
|
|
|
301
|
-
resizePanel(panel: Panel
|
|
339
|
+
resizePanel(panel: Panel) {
|
|
302
340
|
|
|
303
341
|
panel.canvas.style.position = 'absolute';
|
|
304
342
|
|
|
@@ -311,23 +349,24 @@ class Stats {
|
|
|
311
349
|
panel.canvas.style.display = 'block';
|
|
312
350
|
if (this.horizontal) {
|
|
313
351
|
panel.canvas.style.top = '0px';
|
|
314
|
-
panel.canvas.style.left =
|
|
352
|
+
panel.canvas.style.left = panel.id * panel.WIDTH / panel.PR + 'px';
|
|
315
353
|
} else {
|
|
316
354
|
panel.canvas.style.left = '0px';
|
|
317
|
-
panel.canvas.style.top =
|
|
355
|
+
panel.canvas.style.top = panel.id * panel.HEIGHT / panel.PR + 'px';
|
|
318
356
|
|
|
319
357
|
}
|
|
320
358
|
}
|
|
321
359
|
|
|
322
360
|
}
|
|
323
|
-
addPanel(panel: Panel
|
|
361
|
+
addPanel(panel: Panel) {
|
|
324
362
|
|
|
325
363
|
if (panel.canvas) {
|
|
326
364
|
|
|
327
365
|
this.dom.appendChild(panel.canvas);
|
|
366
|
+
panel.id = this._panelId;
|
|
367
|
+
this.resizePanel(panel);
|
|
328
368
|
|
|
329
|
-
this.
|
|
330
|
-
|
|
369
|
+
this._panelId++;
|
|
331
370
|
}
|
|
332
371
|
|
|
333
372
|
return panel;
|
|
@@ -369,6 +408,58 @@ class Stats {
|
|
|
369
408
|
}
|
|
370
409
|
});
|
|
371
410
|
|
|
411
|
+
}
|
|
412
|
+
private detectVSync(currentTime: number): void {
|
|
413
|
+
if (this.lastFrameTime === 0) {
|
|
414
|
+
this.lastFrameTime = currentTime;
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Calculate frame time
|
|
419
|
+
const frameTime = currentTime - this.lastFrameTime;
|
|
420
|
+
this.lastFrameTime = currentTime;
|
|
421
|
+
|
|
422
|
+
// Add to histories
|
|
423
|
+
this.frameTimeHistory.push(frameTime);
|
|
424
|
+
if (this.frameTimeHistory.length > this.HISTORY_SIZE) {
|
|
425
|
+
this.frameTimeHistory.shift();
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Only start detection when we have enough samples
|
|
429
|
+
if (this.frameTimeHistory.length < 60) return;
|
|
430
|
+
|
|
431
|
+
// Calculate average frame time
|
|
432
|
+
const avgFrameTime = this.frameTimeHistory.reduce((a, b) => a + b) / this.frameTimeHistory.length;
|
|
433
|
+
|
|
434
|
+
// Calculate frame time stability (standard deviation)
|
|
435
|
+
const variance = this.frameTimeHistory.reduce((acc, time) =>
|
|
436
|
+
acc + Math.pow(time - avgFrameTime, 2), 0) / this.frameTimeHistory.length;
|
|
437
|
+
const stability = Math.sqrt(variance);
|
|
438
|
+
|
|
439
|
+
// Only proceed if frame timing is relatively stable
|
|
440
|
+
if (stability > 2) { // 2ms stability threshold
|
|
441
|
+
this.detectedVSync = null;
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Find the closest VSync rate based on frame time
|
|
446
|
+
let closestMatch: VSyncInfo | null = null;
|
|
447
|
+
let smallestDiff = Infinity;
|
|
448
|
+
|
|
449
|
+
for (const rate of this.VSYNC_RATES) {
|
|
450
|
+
const diff = Math.abs(avgFrameTime - rate.frameTime);
|
|
451
|
+
if (diff < smallestDiff) {
|
|
452
|
+
smallestDiff = diff;
|
|
453
|
+
closestMatch = rate;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (closestMatch && (smallestDiff / closestMatch.frameTime <= this.VSYNC_THRESHOLD)) {
|
|
458
|
+
this.detectedVSync = closestMatch;
|
|
459
|
+
} else {
|
|
460
|
+
this.detectedVSync = null;
|
|
461
|
+
}
|
|
462
|
+
|
|
372
463
|
}
|
|
373
464
|
|
|
374
465
|
endInternal() {
|
|
@@ -394,7 +485,7 @@ class Stats {
|
|
|
394
485
|
if (this.gpuPanel) {
|
|
395
486
|
this.updatePanelComponents(this.gpuPanel, this.averageGpu, this.precision, shouldUpdateText, shouldUpdateGraph);
|
|
396
487
|
}
|
|
397
|
-
if (this.gpuPanelCompute) {
|
|
488
|
+
if (this.trackCPT && this.gpuPanelCompute) {
|
|
398
489
|
this.updatePanelComponents(this.gpuPanelCompute, this.averageGpuCompute, this.precision, shouldUpdateText, shouldUpdateGraph);
|
|
399
490
|
}
|
|
400
491
|
|
|
@@ -405,6 +496,16 @@ class Stats {
|
|
|
405
496
|
this.prevGraphTime = currentTime;
|
|
406
497
|
}
|
|
407
498
|
|
|
499
|
+
if (this.vsyncPanel !== null) {
|
|
500
|
+
this.detectVSync(currentTime);
|
|
501
|
+
|
|
502
|
+
const vsyncValue = this.detectedVSync?.refreshRate || 0;
|
|
503
|
+
|
|
504
|
+
if (shouldUpdateText && vsyncValue > 0) {
|
|
505
|
+
this.vsyncPanel.update(vsyncValue, vsyncValue);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
408
509
|
return currentTime;
|
|
409
510
|
}
|
|
410
511
|
|
|
@@ -462,7 +563,6 @@ class Stats {
|
|
|
462
563
|
if (window.performance) {
|
|
463
564
|
|
|
464
565
|
window.performance.mark(marker);
|
|
465
|
-
this.isRunningCPUProfiling = true
|
|
466
566
|
|
|
467
567
|
}
|
|
468
568
|
|
|
@@ -470,12 +570,11 @@ class Stats {
|
|
|
470
570
|
|
|
471
571
|
endProfiling(startMarker: string | PerformanceMeasureOptions | undefined, endMarker: string | undefined, measureName: string) {
|
|
472
572
|
|
|
473
|
-
if (window.performance && endMarker
|
|
573
|
+
if (window.performance && endMarker) {
|
|
474
574
|
|
|
475
575
|
window.performance.mark(endMarker);
|
|
476
576
|
const cpuMeasure = performance.measure(measureName, startMarker, endMarker);
|
|
477
577
|
this.totalCpuDuration += cpuMeasure.duration;
|
|
478
|
-
this.isRunningCPUProfiling = false
|
|
479
578
|
|
|
480
579
|
}
|
|
481
580
|
|
|
@@ -539,6 +638,7 @@ class Stats {
|
|
|
539
638
|
}
|
|
540
639
|
|
|
541
640
|
private updateAverages(): void {
|
|
641
|
+
|
|
542
642
|
this.addToAverage(this.totalCpuDuration, this.averageCpu);
|
|
543
643
|
this.addToAverage(this.totalGpuDuration, this.averageGpu);
|
|
544
644
|
// Add GPU Compute to the main update flow
|
|
@@ -549,7 +649,7 @@ class Stats {
|
|
|
549
649
|
|
|
550
650
|
addToAverage(value: number, averageArray: { logs: any; graph: any; }) {
|
|
551
651
|
// Validate value
|
|
552
|
-
// if (value === undefined || value === null || isNaN(value)) {
|
|
652
|
+
// if (value === undefined || value === null || isNaN(value) || value === 0) {
|
|
553
653
|
// return;
|
|
554
654
|
// }
|
|
555
655
|
|
|
@@ -572,6 +672,22 @@ class Stats {
|
|
|
572
672
|
|
|
573
673
|
}
|
|
574
674
|
|
|
675
|
+
patchThreeWebGPU(renderer: any) {
|
|
676
|
+
|
|
677
|
+
const originalAnimationLoop = renderer.info.reset
|
|
678
|
+
|
|
679
|
+
const statsInstance = this;
|
|
680
|
+
|
|
681
|
+
renderer.info.reset = function () {
|
|
682
|
+
|
|
683
|
+
statsInstance.beginProfiling('cpu-started');
|
|
684
|
+
|
|
685
|
+
originalAnimationLoop.call(this);
|
|
686
|
+
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
}
|
|
690
|
+
|
|
575
691
|
patchThreeRenderer(renderer: any) {
|
|
576
692
|
|
|
577
693
|
// Store the original render method
|
|
@@ -583,13 +699,13 @@ class Stats {
|
|
|
583
699
|
// Override the render method on the prototype
|
|
584
700
|
renderer.render = function (scene: THREE.Scene, camera: THREE.Camera) {
|
|
585
701
|
|
|
586
|
-
|
|
587
702
|
statsInstance.begin(); // Start tracking for this render call
|
|
588
703
|
|
|
589
704
|
// Call the original render method
|
|
590
705
|
originalRenderMethod.call(this, scene, camera);
|
|
591
706
|
|
|
592
707
|
statsInstance.end(); // End tracking for this render call
|
|
708
|
+
|
|
593
709
|
};
|
|
594
710
|
|
|
595
711
|
|
package/lib/panel.ts
CHANGED
|
@@ -5,6 +5,7 @@ class Panel {
|
|
|
5
5
|
fg: string;
|
|
6
6
|
bg: string;
|
|
7
7
|
gradient: CanvasGradient | null;
|
|
8
|
+
id: number = 0;
|
|
8
9
|
PR: number;
|
|
9
10
|
WIDTH: number;
|
|
10
11
|
HEIGHT: number;
|
|
@@ -81,7 +82,7 @@ class Panel {
|
|
|
81
82
|
return gradient;
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
|
|
85
|
+
public initializeCanvas() {
|
|
85
86
|
if (!this.context) return;
|
|
86
87
|
|
|
87
88
|
this.context.imageSmoothingEnabled = false;
|
|
@@ -104,7 +105,7 @@ class Panel {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
// Update only text portion
|
|
107
|
-
update(value: number, maxValue: number, decimals: number = 0) {
|
|
108
|
+
public update(value: number, maxValue: number, decimals: number = 0) {
|
|
108
109
|
if (!this.context || !this.gradient) return;
|
|
109
110
|
|
|
110
111
|
const min = Math.min(Infinity, value);
|
|
@@ -125,7 +126,7 @@ class Panel {
|
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
// Update only graph portion
|
|
128
|
-
updateGraph(valueGraph: number, maxGraph: number) {
|
|
129
|
+
public updateGraph(valueGraph: number, maxGraph: number) {
|
|
129
130
|
if (!this.context || !this.gradient) return;
|
|
130
131
|
|
|
131
132
|
// Handle zero values appropriately
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Panel } from './panel';
|
|
2
|
+
|
|
3
|
+
class PanelVSync extends Panel {
|
|
4
|
+
private readonly SMALL_HEIGHT: number;
|
|
5
|
+
private vsyncValue: number = 0;
|
|
6
|
+
|
|
7
|
+
constructor(name: string, fg: string, bg: string) {
|
|
8
|
+
super(name, fg, bg);
|
|
9
|
+
|
|
10
|
+
// Redefine dimensions for a smaller panel
|
|
11
|
+
this.SMALL_HEIGHT = 9 * this.PR; // Smaller height
|
|
12
|
+
this.HEIGHT = this.SMALL_HEIGHT;
|
|
13
|
+
this.TEXT_Y = 0 * this.PR; // Adjust text position
|
|
14
|
+
|
|
15
|
+
// Resize the canvas
|
|
16
|
+
this.canvas.height = this.HEIGHT;
|
|
17
|
+
this.canvas.style.height = '9px'; // Match the new height
|
|
18
|
+
|
|
19
|
+
// Style for overlay positioning
|
|
20
|
+
this.canvas.style.cssText = `
|
|
21
|
+
width: 90px;
|
|
22
|
+
height: 9px;
|
|
23
|
+
position: absolute;
|
|
24
|
+
top: 0;
|
|
25
|
+
left: 0;
|
|
26
|
+
pointer-events: none; // Allow clicks to pass through
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
// Reinitialize with new dimensions
|
|
30
|
+
this.initializeCanvas();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public initializeCanvas() {
|
|
34
|
+
if (!this.context) return;
|
|
35
|
+
|
|
36
|
+
this.context.imageSmoothingEnabled = false;
|
|
37
|
+
|
|
38
|
+
// Larger font for better visibility
|
|
39
|
+
this.context.font = 'bold ' + (9 * this.PR) + 'px Helvetica,Arial,sans-serif';
|
|
40
|
+
this.context.textBaseline = 'top';
|
|
41
|
+
|
|
42
|
+
this.context.globalAlpha = 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Override update for VSync-specific display
|
|
46
|
+
public update(value: number, _maxValue: number, _decimals: number = 0) {
|
|
47
|
+
if (!this.context) return;
|
|
48
|
+
|
|
49
|
+
this.vsyncValue = value;
|
|
50
|
+
|
|
51
|
+
this.context.clearRect(0, 0, this.WIDTH, this.HEIGHT);
|
|
52
|
+
// Draw VSync text
|
|
53
|
+
this.context.globalAlpha = 1;
|
|
54
|
+
this.context.fillStyle = this.bg;
|
|
55
|
+
this.context.fillText(
|
|
56
|
+
`${value.toFixed(0)}Hz`,
|
|
57
|
+
this.TEXT_X,
|
|
58
|
+
this.TEXT_Y
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Override updateGraph to do nothing (we don't need a graph for VSync)
|
|
63
|
+
public updateGraph(_valueGraph: number, _maxGraph: number) {
|
|
64
|
+
// No graph needed for VSync display
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Method to set the offset position relative to parent panel
|
|
69
|
+
public setOffset(x: number, y: number) {
|
|
70
|
+
this.canvas.style.transform = `translate(${x}px, ${y}px)`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { PanelVSync };
|