stats-gl 3.8.0 → 4.0.1

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/dist/main.js CHANGED
@@ -1,6 +1,11 @@
1
+ import { StatsCore } from "./core.js";
1
2
  import { Panel } from "./panel.js";
2
3
  import { PanelVSync } from "./panelVsync.js";
3
- const _Stats = class _Stats2 {
4
+ import { PanelTexture } from "./panelTexture.js";
5
+ import { DEFAULT_PREVIEW_WIDTH, DEFAULT_PREVIEW_HEIGHT, TextureCaptureWebGL, TextureCaptureWebGPU, extractWebGLSource, extractWebGPUSource } from "./textureCapture.js";
6
+ import { StatsProfiler } from "./profiler.js";
7
+ import { StatsGLCapture } from "./statsGLNode.js";
8
+ const _Stats = class _Stats2 extends StatsCore {
4
9
  constructor({
5
10
  trackGPU = false,
6
11
  trackCPT = false,
@@ -15,25 +20,34 @@ const _Stats = class _Stats2 {
15
20
  horizontal = true,
16
21
  mode = 0
17
22
  } = {}) {
18
- this.gl = null;
19
- this.ext = null;
20
- this.activeQuery = null;
21
- this.gpuQueries = [];
22
- this.threeRendererPatched = false;
23
- this.frameTimes = [];
24
- this.renderCount = 0;
25
- this.totalCpuDuration = 0;
26
- this.totalGpuDuration = 0;
27
- this.totalGpuDurationCompute = 0;
23
+ super({
24
+ trackGPU,
25
+ trackCPT,
26
+ trackHz,
27
+ trackFPS,
28
+ logsPerSecond,
29
+ graphsPerSecond,
30
+ samplesLog,
31
+ samplesGraph,
32
+ precision
33
+ });
28
34
  this.fpsPanel = null;
29
35
  this.msPanel = null;
30
36
  this.gpuPanel = null;
31
37
  this.gpuPanelCompute = null;
32
38
  this.vsyncPanel = null;
33
- this.averageFps = { logs: [], graph: [] };
34
- this.averageCpu = { logs: [], graph: [] };
35
- this.averageGpu = { logs: [], graph: [] };
36
- this.averageGpuCompute = { logs: [], graph: [] };
39
+ this.workerCpuPanel = null;
40
+ this.texturePanels = /* @__PURE__ */ new Map();
41
+ this.texturePanelRow = null;
42
+ this.textureCaptureWebGL = null;
43
+ this.textureCaptureWebGPU = null;
44
+ this.textureSourcesWebGL = /* @__PURE__ */ new Map();
45
+ this.textureSourcesWebGPU = /* @__PURE__ */ new Map();
46
+ this.texturePreviewWidth = DEFAULT_PREVIEW_WIDTH;
47
+ this.texturePreviewHeight = DEFAULT_PREVIEW_HEIGHT;
48
+ this.lastRendererWidth = 0;
49
+ this.lastRendererHeight = 0;
50
+ this.textureUpdatePending = false;
37
51
  this.updateCounter = 0;
38
52
  this.lastMin = {};
39
53
  this.lastMax = {};
@@ -52,6 +66,10 @@ const _Stats = class _Stats2 {
52
66
  this.HISTORY_SIZE = 120;
53
67
  this.VSYNC_THRESHOLD = 0.05;
54
68
  this.lastFrameTime = 0;
69
+ this.externalData = null;
70
+ this.hasNewExternalData = false;
71
+ this.isWorker = false;
72
+ this.averageWorkerCpu = { logs: [], graph: [] };
55
73
  this.handleClick = (event) => {
56
74
  event.preventDefault();
57
75
  this.showPanel(++this.mode % this.dom.children.length);
@@ -61,6 +79,8 @@ const _Stats = class _Stats2 {
61
79
  this.resizePanel(this.fpsPanel);
62
80
  if (this.msPanel)
63
81
  this.resizePanel(this.msPanel);
82
+ if (this.workerCpuPanel)
83
+ this.resizePanel(this.workerCpuPanel);
64
84
  if (this.gpuPanel)
65
85
  this.resizePanel(this.gpuPanel);
66
86
  if (this.gpuPanelCompute)
@@ -69,27 +89,19 @@ const _Stats = class _Stats2 {
69
89
  this.mode = mode;
70
90
  this.horizontal = horizontal;
71
91
  this.minimal = minimal;
72
- this.trackGPU = trackGPU;
73
- this.trackCPT = trackCPT;
74
- this.trackHz = trackHz;
75
- this.trackFPS = trackFPS;
76
- this.samplesLog = samplesLog;
77
- this.samplesGraph = samplesGraph;
78
- this.precision = precision;
79
- this.logsPerSecond = logsPerSecond;
80
- this.graphsPerSecond = graphsPerSecond;
81
- const prevGraphTime = performance.now();
82
- this.prevGraphTime = prevGraphTime;
83
92
  this.dom = document.createElement("div");
84
93
  this.initializeDOM();
85
- this.beginTime = performance.now();
86
- this.prevTextTime = this.beginTime;
87
- this.prevCpuTime = this.beginTime;
88
94
  this._panelId = 0;
89
95
  if (this.trackFPS) {
90
96
  this.fpsPanel = this.addPanel(new _Stats2.Panel("FPS", "#0ff", "#002"));
91
97
  this.msPanel = this.addPanel(new _Stats2.Panel("CPU", "#0f0", "#020"));
92
98
  }
99
+ if (this.trackGPU) {
100
+ this.gpuPanel = this.addPanel(new _Stats2.Panel("GPU", "#ff0", "#220"));
101
+ }
102
+ if (this.trackCPT) {
103
+ this.gpuPanelCompute = this.addPanel(new _Stats2.Panel("CPT", "#e1e1e1", "#212121"));
104
+ }
93
105
  if (this.trackHz === true) {
94
106
  this.vsyncPanel = new PanelVSync("", "#f0f", "#202");
95
107
  this.dom.appendChild(this.vsyncPanel.canvas);
@@ -115,124 +127,163 @@ const _Stats = class _Stats2 {
115
127
  window.addEventListener("resize", this.handleResize);
116
128
  }
117
129
  }
118
- async init(canvasOrGL) {
119
- if (!canvasOrGL) {
120
- console.error('Stats: The "canvas" parameter is undefined.');
130
+ /**
131
+ * Compute and update texture preview dimensions based on renderer aspect ratio
132
+ */
133
+ updateTexturePreviewDimensions() {
134
+ var _a, _b;
135
+ if (!this.renderer)
121
136
  return;
122
- }
123
- if (this.handleThreeRenderer(canvasOrGL))
124
- return;
125
- if (await this.handleWebGPURenderer(canvasOrGL))
137
+ const rendererWidth = ((_a = this.renderer.domElement) == null ? void 0 : _a.width) || 0;
138
+ const rendererHeight = ((_b = this.renderer.domElement) == null ? void 0 : _b.height) || 0;
139
+ if (rendererWidth === this.lastRendererWidth && rendererHeight === this.lastRendererHeight) {
126
140
  return;
127
- if (this.initializeWebGL(canvasOrGL)) {
128
- if (this.trackGPU) {
129
- this.initializeGPUTracking();
130
- }
141
+ }
142
+ if (rendererWidth === 0 || rendererHeight === 0)
131
143
  return;
144
+ this.lastRendererWidth = rendererWidth;
145
+ this.lastRendererHeight = rendererHeight;
146
+ const sourceAspect = rendererWidth / rendererHeight;
147
+ const panelAspect = DEFAULT_PREVIEW_WIDTH / DEFAULT_PREVIEW_HEIGHT;
148
+ let newWidth;
149
+ let newHeight;
150
+ if (sourceAspect > panelAspect) {
151
+ newWidth = DEFAULT_PREVIEW_WIDTH;
152
+ newHeight = Math.round(DEFAULT_PREVIEW_WIDTH / sourceAspect);
132
153
  } else {
133
- console.error("Stats-gl: Failed to initialize WebGL context");
134
- }
135
- }
136
- handleThreeRenderer(renderer) {
137
- if (renderer.isWebGLRenderer && !this.threeRendererPatched) {
138
- this.patchThreeRenderer(renderer);
139
- this.gl = renderer.getContext();
140
- if (this.trackGPU) {
141
- this.initializeGPUTracking();
154
+ newHeight = DEFAULT_PREVIEW_HEIGHT;
155
+ newWidth = Math.round(DEFAULT_PREVIEW_HEIGHT * sourceAspect);
156
+ }
157
+ newWidth = Math.max(newWidth, 16);
158
+ newHeight = Math.max(newHeight, 16);
159
+ if (newWidth !== this.texturePreviewWidth || newHeight !== this.texturePreviewHeight) {
160
+ this.texturePreviewWidth = newWidth;
161
+ this.texturePreviewHeight = newHeight;
162
+ if (this.textureCaptureWebGL) {
163
+ this.textureCaptureWebGL.resize(newWidth, newHeight);
142
164
  }
143
- return true;
144
- }
145
- return false;
146
- }
147
- async handleWebGPURenderer(renderer) {
148
- if (renderer.isWebGPURenderer) {
149
- if (this.trackGPU || this.trackCPT) {
150
- renderer.backend.trackTimestamp = true;
151
- if (!renderer._initialized) {
152
- await renderer.init();
153
- }
154
- if (renderer.hasFeature("timestamp-query")) {
155
- this.initializeWebGPUPanels();
156
- }
165
+ if (this.textureCaptureWebGPU) {
166
+ this.textureCaptureWebGPU.resize(newWidth, newHeight);
167
+ }
168
+ for (const panel of this.texturePanels.values()) {
169
+ panel.setSourceSize(rendererWidth, rendererHeight);
157
170
  }
158
- this.info = renderer.info;
159
- this.patchThreeWebGPU(renderer);
160
- return true;
161
171
  }
162
- return false;
163
172
  }
164
- initializeWebGPUPanels() {
165
- if (this.trackGPU) {
166
- this.gpuPanel = this.addPanel(new _Stats2.Panel("GPU", "#ff0", "#220"));
167
- }
168
- if (this.trackCPT) {
169
- this.gpuPanelCompute = this.addPanel(new _Stats2.Panel("CPT", "#e1e1e1", "#212121"));
170
- }
173
+ onWebGPUTimestampSupported() {
171
174
  }
172
- initializeWebGL(canvasOrGL) {
173
- if (canvasOrGL instanceof WebGL2RenderingContext) {
174
- this.gl = canvasOrGL;
175
- } else if (canvasOrGL instanceof HTMLCanvasElement || canvasOrGL instanceof OffscreenCanvas) {
176
- this.gl = canvasOrGL.getContext("webgl2");
177
- if (!this.gl) {
178
- console.error("Stats: Unable to obtain WebGL2 context.");
179
- return false;
180
- }
181
- } else {
182
- console.error(
183
- "Stats: Invalid input type. Expected WebGL2RenderingContext, HTMLCanvasElement, or OffscreenCanvas."
184
- );
185
- return false;
186
- }
187
- return true;
175
+ onGPUTrackingInitialized() {
188
176
  }
189
- initializeGPUTracking() {
190
- if (this.gl) {
191
- this.ext = this.gl.getExtension("EXT_disjoint_timer_query_webgl2");
192
- if (this.ext) {
193
- this.gpuPanel = this.addPanel(new _Stats2.Panel("GPU", "#ff0", "#220"));
177
+ setData(data) {
178
+ this.externalData = data;
179
+ this.hasNewExternalData = true;
180
+ if (!this.isWorker && this.msPanel) {
181
+ this.isWorker = true;
182
+ this.workerCpuPanel = new _Stats2.Panel("WRK", "#f90", "#220");
183
+ const insertPosition = this.msPanel.id + 1;
184
+ this.workerCpuPanel.id = insertPosition;
185
+ if (this.gpuPanel && this.gpuPanel.id >= insertPosition) {
186
+ this.gpuPanel.id++;
187
+ this.resizePanel(this.gpuPanel);
194
188
  }
189
+ if (this.gpuPanelCompute && this.gpuPanelCompute.id >= insertPosition) {
190
+ this.gpuPanelCompute.id++;
191
+ this.resizePanel(this.gpuPanelCompute);
192
+ }
193
+ const msCanvas = this.msPanel.canvas;
194
+ if (msCanvas.nextSibling) {
195
+ this.dom.insertBefore(this.workerCpuPanel.canvas, msCanvas.nextSibling);
196
+ } else {
197
+ this.dom.appendChild(this.workerCpuPanel.canvas);
198
+ }
199
+ this.resizePanel(this.workerCpuPanel);
200
+ this._panelId++;
195
201
  }
196
202
  }
197
- begin() {
198
- this.beginProfiling("cpu-started");
199
- if (!this.gl || !this.ext)
200
- return;
201
- if (this.activeQuery) {
202
- this.gl.endQuery(this.ext.TIME_ELAPSED_EXT);
203
- }
204
- this.activeQuery = this.gl.createQuery();
205
- if (this.activeQuery) {
206
- this.gl.beginQuery(this.ext.TIME_ELAPSED_EXT, this.activeQuery);
203
+ update() {
204
+ if (this.externalData) {
205
+ this.updateFromExternalData();
206
+ } else {
207
+ this.updateFromInternalData();
207
208
  }
208
209
  }
209
- end() {
210
- this.renderCount++;
211
- if (this.gl && this.ext && this.activeQuery) {
212
- this.gl.endQuery(this.ext.TIME_ELAPSED_EXT);
213
- this.gpuQueries.push({ query: this.activeQuery });
214
- this.activeQuery = null;
215
- }
210
+ updateFromExternalData() {
211
+ const data = this.externalData;
216
212
  this.endProfiling("cpu-started", "cpu-finished", "cpu-duration");
213
+ this.addToAverage(this.totalCpuDuration, this.averageCpu);
214
+ this.totalCpuDuration = 0;
215
+ if (this.hasNewExternalData) {
216
+ this.addToAverage(data.cpu, this.averageWorkerCpu);
217
+ this.addToAverage(data.fps, this.averageFps);
218
+ this.addToAverage(data.gpu, this.averageGpu);
219
+ this.addToAverage(data.gpuCompute, this.averageGpuCompute);
220
+ this.hasNewExternalData = false;
221
+ }
222
+ this.renderPanels();
217
223
  }
218
- update() {
224
+ updateFromInternalData() {
219
225
  this.endProfiling("cpu-started", "cpu-finished", "cpu-duration");
220
- if (!this.info) {
226
+ if (this.webgpuNative) {
227
+ this.resolveTimestampsAsync();
228
+ } else if (!this.info) {
221
229
  this.processGpuQueries();
222
230
  } else {
223
231
  this.processWebGPUTimestamps();
224
232
  }
225
233
  this.updateAverages();
226
234
  this.resetCounters();
235
+ this.renderPanels();
227
236
  }
228
- processWebGPUTimestamps() {
229
- this.totalGpuDuration = this.info.render.timestamp;
230
- this.totalGpuDurationCompute = this.info.compute.timestamp;
237
+ renderPanels() {
238
+ var _a;
239
+ const currentTime = performance.now();
240
+ if (!this.isWorker) {
241
+ this.frameTimes.push(currentTime);
242
+ while (this.frameTimes.length > 0 && this.frameTimes[0] <= currentTime - 1e3) {
243
+ this.frameTimes.shift();
244
+ }
245
+ const fps = Math.round(this.frameTimes.length);
246
+ this.addToAverage(fps, this.averageFps);
247
+ }
248
+ const shouldUpdateText = currentTime >= this.prevTextTime + 1e3 / this.logsPerSecond;
249
+ const shouldUpdateGraph = currentTime >= this.prevGraphTime + 1e3 / this.graphsPerSecond;
250
+ const suffix = this.isWorker ? " ⛭" : "";
251
+ this.updatePanelComponents(this.fpsPanel, this.averageFps, 0, shouldUpdateText, shouldUpdateGraph, suffix);
252
+ this.updatePanelComponents(this.msPanel, this.averageCpu, this.precision, shouldUpdateText, shouldUpdateGraph, "");
253
+ if (this.workerCpuPanel && this.isWorker) {
254
+ this.updatePanelComponents(this.workerCpuPanel, this.averageWorkerCpu, this.precision, shouldUpdateText, shouldUpdateGraph, " ⛭");
255
+ }
256
+ if (this.gpuPanel) {
257
+ this.updatePanelComponents(this.gpuPanel, this.averageGpu, this.precision, shouldUpdateText, shouldUpdateGraph, suffix);
258
+ }
259
+ if (this.trackCPT && this.gpuPanelCompute) {
260
+ this.updatePanelComponents(this.gpuPanelCompute, this.averageGpuCompute, this.precision, shouldUpdateText, shouldUpdateGraph, suffix);
261
+ }
262
+ if (shouldUpdateText) {
263
+ this.prevTextTime = currentTime;
264
+ }
265
+ if (shouldUpdateGraph) {
266
+ this.prevGraphTime = currentTime;
267
+ if (this.texturePanels.size > 0 && !this.textureUpdatePending) {
268
+ this.textureUpdatePending = true;
269
+ this.updateTexturePanels().finally(() => {
270
+ this.textureUpdatePending = false;
271
+ });
272
+ }
273
+ this.captureStatsGLNodes();
274
+ }
275
+ if (this.vsyncPanel !== null) {
276
+ this.detectVSync(currentTime);
277
+ const vsyncValue = ((_a = this.detectedVSync) == null ? void 0 : _a.refreshRate) || 0;
278
+ if (shouldUpdateText && vsyncValue > 0) {
279
+ this.vsyncPanel.update(vsyncValue, vsyncValue);
280
+ }
281
+ }
231
282
  }
232
283
  resetCounters() {
233
284
  this.renderCount = 0;
234
285
  this.totalCpuDuration = 0;
235
- this.beginTime = this.endInternal();
286
+ this.beginTime = performance.now();
236
287
  }
237
288
  resizePanel(panel) {
238
289
  panel.canvas.style.position = "absolute";
@@ -265,23 +316,179 @@ const _Stats = class _Stats2 {
265
316
  }
266
317
  this.mode = id;
267
318
  }
268
- processGpuQueries() {
269
- if (!this.gl || !this.ext)
319
+ // ==========================================================================
320
+ // Texture Panel API
321
+ // ==========================================================================
322
+ /**
323
+ * Add a new texture preview panel
324
+ * @param name - Label for the texture panel
325
+ * @returns The created PanelTexture instance
326
+ */
327
+ addTexturePanel(name) {
328
+ if (!this.texturePanelRow) {
329
+ this.texturePanelRow = document.createElement("div");
330
+ this.texturePanelRow.style.cssText = `
331
+ position: absolute;
332
+ top: 48px;
333
+ left: 0;
334
+ display: flex;
335
+ flex-direction: row;
336
+ `;
337
+ this.dom.appendChild(this.texturePanelRow);
338
+ }
339
+ const panel = new PanelTexture(name);
340
+ panel.canvas.style.position = "relative";
341
+ panel.canvas.style.left = "";
342
+ panel.canvas.style.top = "";
343
+ this.texturePanelRow.appendChild(panel.canvas);
344
+ this.texturePanels.set(name, panel);
345
+ return panel;
346
+ }
347
+ /**
348
+ * Set texture source for a panel (Three.js render target)
349
+ * Auto-detects WebGL/WebGPU and extracts native handles
350
+ * @param name - Panel name
351
+ * @param source - Three.js RenderTarget or native texture
352
+ */
353
+ setTexture(name, source) {
354
+ this.updateTexturePreviewDimensions();
355
+ if (this.gl && !this.textureCaptureWebGL) {
356
+ this.textureCaptureWebGL = new TextureCaptureWebGL(this.gl, this.texturePreviewWidth, this.texturePreviewHeight);
357
+ }
358
+ if (this.gpuDevice && !this.textureCaptureWebGPU) {
359
+ this.textureCaptureWebGPU = new TextureCaptureWebGPU(this.gpuDevice, this.texturePreviewWidth, this.texturePreviewHeight);
360
+ }
361
+ const panel = this.texturePanels.get(name);
362
+ if (source.isWebGLRenderTarget && this.gl) {
363
+ const webglSource = extractWebGLSource(source, this.gl);
364
+ if (webglSource) {
365
+ this.textureSourcesWebGL.set(name, {
366
+ target: source,
367
+ ...webglSource
368
+ });
369
+ if (panel) {
370
+ panel.setSourceSize(webglSource.width, webglSource.height);
371
+ }
372
+ }
270
373
  return;
271
- this.totalGpuDuration = 0;
272
- this.gpuQueries.forEach((queryInfo, index) => {
273
- if (this.gl) {
274
- const available = this.gl.getQueryParameter(queryInfo.query, this.gl.QUERY_RESULT_AVAILABLE);
275
- const disjoint = this.gl.getParameter(this.ext.GPU_DISJOINT_EXT);
276
- if (available && !disjoint) {
277
- const elapsed = this.gl.getQueryParameter(queryInfo.query, this.gl.QUERY_RESULT);
278
- const duration = elapsed * 1e-6;
279
- this.totalGpuDuration += duration;
280
- this.gl.deleteQuery(queryInfo.query);
281
- this.gpuQueries.splice(index, 1);
374
+ }
375
+ if (source.isRenderTarget && this.gpuBackend) {
376
+ const gpuTexture = extractWebGPUSource(source, this.gpuBackend);
377
+ if (gpuTexture) {
378
+ this.textureSourcesWebGPU.set(name, gpuTexture);
379
+ if (panel && source.width && source.height) {
380
+ panel.setSourceSize(source.width, source.height);
282
381
  }
283
382
  }
383
+ return;
384
+ }
385
+ if (source && typeof source.createView === "function") {
386
+ this.textureSourcesWebGPU.set(name, source);
387
+ return;
388
+ }
389
+ }
390
+ /**
391
+ * Set WebGL framebuffer source with explicit dimensions
392
+ * @param name - Panel name
393
+ * @param framebuffer - WebGL framebuffer
394
+ * @param width - Texture width
395
+ * @param height - Texture height
396
+ */
397
+ setTextureWebGL(name, framebuffer, width, height) {
398
+ this.updateTexturePreviewDimensions();
399
+ if (this.gl && !this.textureCaptureWebGL) {
400
+ this.textureCaptureWebGL = new TextureCaptureWebGL(this.gl, this.texturePreviewWidth, this.texturePreviewHeight);
401
+ }
402
+ this.textureSourcesWebGL.set(name, {
403
+ target: { isWebGLRenderTarget: true },
404
+ framebuffer,
405
+ width,
406
+ height
284
407
  });
408
+ const panel = this.texturePanels.get(name);
409
+ if (panel) {
410
+ panel.setSourceSize(width, height);
411
+ }
412
+ }
413
+ /**
414
+ * Set texture from ImageBitmap (for worker mode)
415
+ * @param name - Panel name
416
+ * @param bitmap - ImageBitmap transferred from worker
417
+ * @param sourceWidth - Optional source texture width for aspect ratio
418
+ * @param sourceHeight - Optional source texture height for aspect ratio
419
+ */
420
+ setTextureBitmap(name, bitmap, sourceWidth, sourceHeight) {
421
+ const panel = this.texturePanels.get(name);
422
+ if (panel) {
423
+ if (sourceWidth !== void 0 && sourceHeight !== void 0) {
424
+ panel.setSourceSize(sourceWidth, sourceHeight);
425
+ }
426
+ panel.updateTexture(bitmap);
427
+ }
428
+ }
429
+ /**
430
+ * Remove a texture panel
431
+ * @param name - Panel name to remove
432
+ */
433
+ removeTexturePanel(name) {
434
+ const panel = this.texturePanels.get(name);
435
+ if (panel) {
436
+ panel.dispose();
437
+ panel.canvas.remove();
438
+ this.texturePanels.delete(name);
439
+ this.textureSourcesWebGL.delete(name);
440
+ this.textureSourcesWebGPU.delete(name);
441
+ }
442
+ }
443
+ /**
444
+ * Capture and update all texture panels
445
+ * Called automatically during renderPanels at graphsPerSecond rate
446
+ */
447
+ async updateTexturePanels() {
448
+ this.updateTexturePreviewDimensions();
449
+ if (this.textureCaptureWebGL) {
450
+ for (const [name, source] of this.textureSourcesWebGL) {
451
+ const panel = this.texturePanels.get(name);
452
+ if (panel) {
453
+ let framebuffer = source.framebuffer;
454
+ let width = source.width;
455
+ let height = source.height;
456
+ if (source.target.isWebGLRenderTarget && source.target.__webglFramebuffer) {
457
+ framebuffer = source.target.__webglFramebuffer;
458
+ width = source.target.width || width;
459
+ height = source.target.height || height;
460
+ }
461
+ const bitmap = await this.textureCaptureWebGL.capture(framebuffer, width, height, name);
462
+ if (bitmap) {
463
+ panel.updateTexture(bitmap);
464
+ }
465
+ }
466
+ }
467
+ }
468
+ if (this.textureCaptureWebGPU) {
469
+ for (const [name, gpuTexture] of this.textureSourcesWebGPU) {
470
+ const panel = this.texturePanels.get(name);
471
+ if (panel) {
472
+ const bitmap = await this.textureCaptureWebGPU.capture(gpuTexture);
473
+ if (bitmap) {
474
+ panel.updateTexture(bitmap);
475
+ }
476
+ }
477
+ }
478
+ }
479
+ }
480
+ /**
481
+ * Capture StatsGL nodes registered by the addon
482
+ */
483
+ captureStatsGLNodes() {
484
+ const captures = this._statsGLCaptures;
485
+ if (!captures || captures.size === 0 || !this.renderer)
486
+ return;
487
+ for (const captureData of captures.values()) {
488
+ if (captureData.capture) {
489
+ captureData.capture(this.renderer);
490
+ }
491
+ }
285
492
  }
286
493
  detectVSync(currentTime) {
287
494
  if (this.lastFrameTime === 0) {
@@ -318,52 +525,19 @@ const _Stats = class _Stats2 {
318
525
  this.detectedVSync = null;
319
526
  }
320
527
  }
321
- endInternal() {
322
- var _a;
323
- const currentTime = performance.now();
324
- this.frameTimes.push(currentTime);
325
- while (this.frameTimes.length > 0 && this.frameTimes[0] <= currentTime - 1e3) {
326
- this.frameTimes.shift();
327
- }
328
- const fps = Math.round(this.frameTimes.length);
329
- this.addToAverage(fps, this.averageFps);
330
- const shouldUpdateText = currentTime >= this.prevTextTime + 1e3 / this.logsPerSecond;
331
- const shouldUpdateGraph = currentTime >= this.prevGraphTime + 1e3 / this.graphsPerSecond;
332
- this.updatePanelComponents(this.fpsPanel, this.averageFps, 0, shouldUpdateText, shouldUpdateGraph);
333
- this.updatePanelComponents(this.msPanel, this.averageCpu, this.precision, shouldUpdateText, shouldUpdateGraph);
334
- if (this.gpuPanel) {
335
- this.updatePanelComponents(this.gpuPanel, this.averageGpu, this.precision, shouldUpdateText, shouldUpdateGraph);
336
- }
337
- if (this.trackCPT && this.gpuPanelCompute) {
338
- this.updatePanelComponents(this.gpuPanelCompute, this.averageGpuCompute, this.precision, shouldUpdateText, shouldUpdateGraph);
339
- }
340
- if (shouldUpdateText) {
341
- this.prevTextTime = currentTime;
342
- }
343
- if (shouldUpdateGraph) {
344
- this.prevGraphTime = currentTime;
345
- }
346
- if (this.vsyncPanel !== null) {
347
- this.detectVSync(currentTime);
348
- const vsyncValue = ((_a = this.detectedVSync) == null ? void 0 : _a.refreshRate) || 0;
349
- if (shouldUpdateText && vsyncValue > 0) {
350
- this.vsyncPanel.update(vsyncValue, vsyncValue);
351
- }
352
- }
353
- return currentTime;
354
- }
355
- updatePanelComponents(panel, averageArray, precision, shouldUpdateText, shouldUpdateGraph) {
528
+ updatePanelComponents(panel, averageArray, precision, shouldUpdateText, shouldUpdateGraph, suffix = "") {
356
529
  if (!panel || averageArray.logs.length === 0)
357
530
  return;
358
- if (!(panel.name in this.lastMin)) {
359
- this.lastMin[panel.name] = Infinity;
360
- this.lastMax[panel.name] = 0;
361
- this.lastValue[panel.name] = 0;
531
+ const key = String(panel.id);
532
+ if (!(key in this.lastMin)) {
533
+ this.lastMin[key] = Infinity;
534
+ this.lastMax[key] = 0;
535
+ this.lastValue[key] = 0;
362
536
  }
363
537
  const currentValue = averageArray.logs[averageArray.logs.length - 1];
364
- this.lastMax[panel.name] = Math.max(...averageArray.logs);
365
- this.lastMin[panel.name] = Math.min(this.lastMin[panel.name], currentValue);
366
- this.lastValue[panel.name] = this.lastValue[panel.name] * 0.7 + currentValue * 0.3;
538
+ this.lastMax[key] = Math.max(...averageArray.logs);
539
+ this.lastMin[key] = Math.min(this.lastMin[key], currentValue);
540
+ this.lastValue[key] = this.lastValue[key] * 0.7 + currentValue * 0.3;
367
541
  const graphMax = Math.max(
368
542
  Math.max(...averageArray.logs),
369
543
  ...averageArray.graph.slice(-this.samplesGraph)
@@ -371,9 +545,10 @@ const _Stats = class _Stats2 {
371
545
  this.updateCounter++;
372
546
  if (shouldUpdateText) {
373
547
  panel.update(
374
- this.lastValue[panel.name],
375
- this.lastMax[panel.name],
376
- precision
548
+ this.lastValue[key],
549
+ this.lastMax[key],
550
+ precision,
551
+ suffix
377
552
  );
378
553
  }
379
554
  if (shouldUpdateGraph) {
@@ -383,36 +558,6 @@ const _Stats = class _Stats2 {
383
558
  );
384
559
  }
385
560
  }
386
- beginProfiling(marker) {
387
- if (window.performance) {
388
- try {
389
- window.performance.clearMarks(marker);
390
- window.performance.mark(marker);
391
- } catch (error) {
392
- console.debug("Stats: Performance marking failed:", error);
393
- }
394
- }
395
- }
396
- endProfiling(startMarker, endMarker, measureName) {
397
- if (!window.performance || !endMarker || !startMarker)
398
- return;
399
- try {
400
- const entries = window.performance.getEntriesByName(startMarker, "mark");
401
- if (entries.length === 0) {
402
- this.beginProfiling(startMarker);
403
- }
404
- window.performance.clearMarks(endMarker);
405
- window.performance.mark(endMarker);
406
- window.performance.clearMeasures(measureName);
407
- const cpuMeasure = performance.measure(measureName, startMarker, endMarker);
408
- this.totalCpuDuration += cpuMeasure.duration;
409
- window.performance.clearMarks(startMarker);
410
- window.performance.clearMarks(endMarker);
411
- window.performance.clearMeasures(measureName);
412
- } catch (error) {
413
- console.debug("Stats: Performance measurement failed:", error);
414
- }
415
- }
416
561
  updatePanel(panel, averageArray, precision = 2) {
417
562
  if (!panel || averageArray.logs.length === 0)
418
563
  return;
@@ -452,48 +597,67 @@ const _Stats = class _Stats2 {
452
597
  }
453
598
  }
454
599
  }
455
- updateAverages() {
456
- this.addToAverage(this.totalCpuDuration, this.averageCpu);
457
- this.addToAverage(this.totalGpuDuration, this.averageGpu);
458
- if (this.info && this.totalGpuDurationCompute !== void 0) {
459
- this.addToAverage(this.totalGpuDurationCompute, this.averageGpuCompute);
460
- }
461
- }
462
- addToAverage(value, averageArray) {
463
- averageArray.logs.push(value);
464
- if (averageArray.logs.length > this.samplesLog) {
465
- averageArray.logs = averageArray.logs.slice(-this.samplesLog);
466
- }
467
- averageArray.graph.push(value);
468
- if (averageArray.graph.length > this.samplesGraph) {
469
- averageArray.graph = averageArray.graph.slice(-this.samplesGraph);
470
- }
471
- }
472
600
  get domElement() {
473
601
  return this.dom;
474
602
  }
475
- patchThreeWebGPU(renderer) {
476
- const originalAnimationLoop = renderer.info.reset;
477
- const statsInstance = this;
478
- renderer.info.reset = function() {
479
- statsInstance.beginProfiling("cpu-started");
480
- originalAnimationLoop.call(this);
481
- };
482
- }
483
- patchThreeRenderer(renderer) {
484
- const originalRenderMethod = renderer.render;
485
- const statsInstance = this;
486
- renderer.render = function(scene, camera) {
487
- statsInstance.begin();
488
- originalRenderMethod.call(this, scene, camera);
489
- statsInstance.end();
490
- };
491
- this.threeRendererPatched = true;
603
+ /**
604
+ * Dispose of all resources. Call when done using Stats.
605
+ */
606
+ dispose() {
607
+ if (this.minimal) {
608
+ this.dom.removeEventListener("click", this.handleClick);
609
+ } else {
610
+ window.removeEventListener("resize", this.handleResize);
611
+ }
612
+ if (this.textureCaptureWebGL) {
613
+ this.textureCaptureWebGL.dispose();
614
+ this.textureCaptureWebGL = null;
615
+ }
616
+ if (this.textureCaptureWebGPU) {
617
+ this.textureCaptureWebGPU.dispose();
618
+ this.textureCaptureWebGPU = null;
619
+ }
620
+ for (const panel of this.texturePanels.values()) {
621
+ panel.dispose();
622
+ }
623
+ this.texturePanels.clear();
624
+ this.textureSourcesWebGL.clear();
625
+ this.textureSourcesWebGPU.clear();
626
+ const captures = this._statsGLCaptures;
627
+ if (captures) {
628
+ for (const captureData of captures.values()) {
629
+ if (captureData.dispose) {
630
+ captureData.dispose();
631
+ }
632
+ }
633
+ captures.clear();
634
+ }
635
+ if (this.texturePanelRow) {
636
+ this.texturePanelRow.remove();
637
+ this.texturePanelRow = null;
638
+ }
639
+ this.dom.remove();
640
+ this.fpsPanel = null;
641
+ this.msPanel = null;
642
+ this.gpuPanel = null;
643
+ this.gpuPanelCompute = null;
644
+ this.vsyncPanel = null;
645
+ this.workerCpuPanel = null;
646
+ this.frameTimeHistory.length = 0;
647
+ this.averageWorkerCpu.logs.length = 0;
648
+ this.averageWorkerCpu.graph.length = 0;
649
+ super.dispose();
492
650
  }
493
651
  };
494
652
  _Stats.Panel = Panel;
653
+ _Stats.PanelTexture = PanelTexture;
495
654
  let Stats = _Stats;
496
655
  export {
656
+ PanelTexture,
657
+ StatsGLCapture,
658
+ StatsProfiler,
659
+ TextureCaptureWebGL,
660
+ TextureCaptureWebGPU,
497
661
  Stats as default
498
662
  };
499
663
  //# sourceMappingURL=main.js.map