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