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.js CHANGED
@@ -1,10 +1,16 @@
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,
7
12
  trackHz = false,
13
+ trackFPS = true,
8
14
  logsPerSecond = 4,
9
15
  graphsPerSecond = 30,
10
16
  samplesLog = 40,
@@ -14,23 +20,34 @@ const _Stats = class _Stats2 {
14
20
  horizontal = true,
15
21
  mode = 0
16
22
  } = {}) {
17
- this.gl = null;
18
- this.ext = null;
19
- this.activeQuery = null;
20
- this.gpuQueries = [];
21
- this.threeRendererPatched = false;
22
- this.frameTimes = [];
23
- this.renderCount = 0;
24
- this.totalCpuDuration = 0;
25
- this.totalGpuDuration = 0;
26
- this.totalGpuDurationCompute = 0;
23
+ super({
24
+ trackGPU,
25
+ trackCPT,
26
+ trackHz,
27
+ trackFPS,
28
+ logsPerSecond,
29
+ graphsPerSecond,
30
+ samplesLog,
31
+ samplesGraph,
32
+ precision
33
+ });
34
+ this.fpsPanel = null;
35
+ this.msPanel = null;
27
36
  this.gpuPanel = null;
28
37
  this.gpuPanelCompute = null;
29
38
  this.vsyncPanel = null;
30
- this.averageFps = { logs: [], graph: [] };
31
- this.averageCpu = { logs: [], graph: [] };
32
- this.averageGpu = { logs: [], graph: [] };
33
- 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;
34
51
  this.updateCounter = 0;
35
52
  this.lastMin = {};
36
53
  this.lastMax = {};
@@ -49,13 +66,21 @@ const _Stats = class _Stats2 {
49
66
  this.HISTORY_SIZE = 120;
50
67
  this.VSYNC_THRESHOLD = 0.05;
51
68
  this.lastFrameTime = 0;
69
+ this.externalData = null;
70
+ this.hasNewExternalData = false;
71
+ this.isWorker = false;
72
+ this.averageWorkerCpu = { logs: [], graph: [] };
52
73
  this.handleClick = (event) => {
53
74
  event.preventDefault();
54
75
  this.showPanel(++this.mode % this.dom.children.length);
55
76
  };
56
77
  this.handleResize = () => {
57
- this.resizePanel(this.fpsPanel);
58
- this.resizePanel(this.msPanel);
78
+ if (this.fpsPanel)
79
+ this.resizePanel(this.fpsPanel);
80
+ if (this.msPanel)
81
+ this.resizePanel(this.msPanel);
82
+ if (this.workerCpuPanel)
83
+ this.resizePanel(this.workerCpuPanel);
59
84
  if (this.gpuPanel)
60
85
  this.resizePanel(this.gpuPanel);
61
86
  if (this.gpuPanelCompute)
@@ -64,24 +89,19 @@ const _Stats = class _Stats2 {
64
89
  this.mode = mode;
65
90
  this.horizontal = horizontal;
66
91
  this.minimal = minimal;
67
- this.trackGPU = trackGPU;
68
- this.trackCPT = trackCPT;
69
- this.trackHz = trackHz;
70
- this.samplesLog = samplesLog;
71
- this.samplesGraph = samplesGraph;
72
- this.precision = precision;
73
- this.logsPerSecond = logsPerSecond;
74
- this.graphsPerSecond = graphsPerSecond;
75
- const prevGraphTime = performance.now();
76
- this.prevGraphTime = prevGraphTime;
77
92
  this.dom = document.createElement("div");
78
93
  this.initializeDOM();
79
- this.beginTime = performance.now();
80
- this.prevTextTime = this.beginTime;
81
- this.prevCpuTime = this.beginTime;
82
94
  this._panelId = 0;
83
- this.fpsPanel = this.addPanel(new _Stats2.Panel("FPS", "#0ff", "#002"));
84
- this.msPanel = this.addPanel(new _Stats2.Panel("CPU", "#0f0", "#020"));
95
+ if (this.trackFPS) {
96
+ this.fpsPanel = this.addPanel(new _Stats2.Panel("FPS", "#0ff", "#002"));
97
+ this.msPanel = this.addPanel(new _Stats2.Panel("CPU", "#0f0", "#020"));
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
+ }
85
105
  if (this.trackHz === true) {
86
106
  this.vsyncPanel = new PanelVSync("", "#f0f", "#202");
87
107
  this.dom.appendChild(this.vsyncPanel.canvas);
@@ -107,124 +127,163 @@ const _Stats = class _Stats2 {
107
127
  window.addEventListener("resize", this.handleResize);
108
128
  }
109
129
  }
110
- async init(canvasOrGL) {
111
- if (!canvasOrGL) {
112
- console.error('Stats: The "canvas" parameter is undefined.');
113
- return;
114
- }
115
- if (this.handleThreeRenderer(canvasOrGL))
130
+ /**
131
+ * Compute and update texture preview dimensions based on renderer aspect ratio
132
+ */
133
+ updateTexturePreviewDimensions() {
134
+ var _a, _b;
135
+ if (!this.renderer)
116
136
  return;
117
- 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) {
118
140
  return;
119
- if (this.initializeWebGL(canvasOrGL)) {
120
- if (this.trackGPU) {
121
- this.initializeGPUTracking();
122
- }
141
+ }
142
+ if (rendererWidth === 0 || rendererHeight === 0)
123
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);
124
153
  } else {
125
- console.error("Stats-gl: Failed to initialize WebGL context");
126
- }
127
- }
128
- handleThreeRenderer(renderer) {
129
- if (renderer.isWebGLRenderer && !this.threeRendererPatched) {
130
- this.patchThreeRenderer(renderer);
131
- this.gl = renderer.getContext();
132
- if (this.trackGPU) {
133
- 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);
134
164
  }
135
- return true;
136
- }
137
- return false;
138
- }
139
- async handleWebGPURenderer(renderer) {
140
- if (renderer.isWebGPURenderer) {
141
- if (this.trackGPU || this.trackCPT) {
142
- renderer.backend.trackTimestamp = true;
143
- if (!renderer._initialized) {
144
- await renderer.init();
145
- }
146
- if (renderer.hasFeature("timestamp-query")) {
147
- this.initializeWebGPUPanels();
148
- }
165
+ if (this.textureCaptureWebGPU) {
166
+ this.textureCaptureWebGPU.resize(newWidth, newHeight);
167
+ }
168
+ for (const panel of this.texturePanels.values()) {
169
+ panel.setSourceSize(rendererWidth, rendererHeight);
149
170
  }
150
- this.info = renderer.info;
151
- this.patchThreeWebGPU(renderer);
152
- return true;
153
171
  }
154
- return false;
155
172
  }
156
- initializeWebGPUPanels() {
157
- if (this.trackGPU) {
158
- this.gpuPanel = this.addPanel(new _Stats2.Panel("GPU", "#ff0", "#220"));
159
- }
160
- if (this.trackCPT) {
161
- this.gpuPanelCompute = this.addPanel(new _Stats2.Panel("CPT", "#e1e1e1", "#212121"));
162
- }
173
+ onWebGPUTimestampSupported() {
163
174
  }
164
- initializeWebGL(canvasOrGL) {
165
- if (canvasOrGL instanceof WebGL2RenderingContext) {
166
- this.gl = canvasOrGL;
167
- } else if (canvasOrGL instanceof HTMLCanvasElement || canvasOrGL instanceof OffscreenCanvas) {
168
- this.gl = canvasOrGL.getContext("webgl2");
169
- if (!this.gl) {
170
- console.error("Stats: Unable to obtain WebGL2 context.");
171
- return false;
172
- }
173
- } else {
174
- console.error(
175
- "Stats: Invalid input type. Expected WebGL2RenderingContext, HTMLCanvasElement, or OffscreenCanvas."
176
- );
177
- return false;
178
- }
179
- return true;
175
+ onGPUTrackingInitialized() {
180
176
  }
181
- initializeGPUTracking() {
182
- if (this.gl) {
183
- this.ext = this.gl.getExtension("EXT_disjoint_timer_query_webgl2");
184
- if (this.ext) {
185
- 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);
188
+ }
189
+ if (this.gpuPanelCompute && this.gpuPanelCompute.id >= insertPosition) {
190
+ this.gpuPanelCompute.id++;
191
+ this.resizePanel(this.gpuPanelCompute);
186
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++;
187
201
  }
188
202
  }
189
- begin() {
190
- this.beginProfiling("cpu-started");
191
- if (!this.gl || !this.ext)
192
- return;
193
- if (this.activeQuery) {
194
- this.gl.endQuery(this.ext.TIME_ELAPSED_EXT);
195
- }
196
- this.activeQuery = this.gl.createQuery();
197
- if (this.activeQuery) {
198
- this.gl.beginQuery(this.ext.TIME_ELAPSED_EXT, this.activeQuery);
203
+ update() {
204
+ if (this.externalData) {
205
+ this.updateFromExternalData();
206
+ } else {
207
+ this.updateFromInternalData();
199
208
  }
200
209
  }
201
- end() {
202
- this.renderCount++;
203
- if (this.gl && this.ext && this.activeQuery) {
204
- this.gl.endQuery(this.ext.TIME_ELAPSED_EXT);
205
- this.gpuQueries.push({ query: this.activeQuery });
206
- this.activeQuery = null;
207
- }
210
+ updateFromExternalData() {
211
+ const data = this.externalData;
208
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();
209
223
  }
210
- update() {
224
+ updateFromInternalData() {
211
225
  this.endProfiling("cpu-started", "cpu-finished", "cpu-duration");
212
- if (!this.info) {
226
+ if (this.webgpuNative) {
227
+ this.resolveTimestampsAsync();
228
+ } else if (!this.info) {
213
229
  this.processGpuQueries();
214
230
  } else {
215
231
  this.processWebGPUTimestamps();
216
232
  }
217
233
  this.updateAverages();
218
234
  this.resetCounters();
235
+ this.renderPanels();
219
236
  }
220
- processWebGPUTimestamps() {
221
- this.totalGpuDuration = this.info.render.timestamp;
222
- 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
+ }
223
282
  }
224
283
  resetCounters() {
225
284
  this.renderCount = 0;
226
285
  this.totalCpuDuration = 0;
227
- this.beginTime = this.endInternal();
286
+ this.beginTime = performance.now();
228
287
  }
229
288
  resizePanel(panel) {
230
289
  panel.canvas.style.position = "absolute";
@@ -257,23 +316,179 @@ const _Stats = class _Stats2 {
257
316
  }
258
317
  this.mode = id;
259
318
  }
260
- processGpuQueries() {
261
- 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
+ }
262
373
  return;
263
- this.totalGpuDuration = 0;
264
- this.gpuQueries.forEach((queryInfo, index) => {
265
- if (this.gl) {
266
- const available = this.gl.getQueryParameter(queryInfo.query, this.gl.QUERY_RESULT_AVAILABLE);
267
- const disjoint = this.gl.getParameter(this.ext.GPU_DISJOINT_EXT);
268
- if (available && !disjoint) {
269
- const elapsed = this.gl.getQueryParameter(queryInfo.query, this.gl.QUERY_RESULT);
270
- const duration = elapsed * 1e-6;
271
- this.totalGpuDuration += duration;
272
- this.gl.deleteQuery(queryInfo.query);
273
- 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);
274
381
  }
275
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
276
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
+ }
277
492
  }
278
493
  detectVSync(currentTime) {
279
494
  if (this.lastFrameTime === 0) {
@@ -310,52 +525,19 @@ const _Stats = class _Stats2 {
310
525
  this.detectedVSync = null;
311
526
  }
312
527
  }
313
- endInternal() {
314
- var _a;
315
- const currentTime = performance.now();
316
- this.frameTimes.push(currentTime);
317
- while (this.frameTimes.length > 0 && this.frameTimes[0] <= currentTime - 1e3) {
318
- this.frameTimes.shift();
319
- }
320
- const fps = Math.round(this.frameTimes.length);
321
- this.addToAverage(fps, this.averageFps);
322
- const shouldUpdateText = currentTime >= this.prevTextTime + 1e3 / this.logsPerSecond;
323
- const shouldUpdateGraph = currentTime >= this.prevGraphTime + 1e3 / this.graphsPerSecond;
324
- this.updatePanelComponents(this.fpsPanel, this.averageFps, 0, shouldUpdateText, shouldUpdateGraph);
325
- this.updatePanelComponents(this.msPanel, this.averageCpu, this.precision, shouldUpdateText, shouldUpdateGraph);
326
- if (this.gpuPanel) {
327
- this.updatePanelComponents(this.gpuPanel, this.averageGpu, this.precision, shouldUpdateText, shouldUpdateGraph);
328
- }
329
- if (this.trackCPT && this.gpuPanelCompute) {
330
- this.updatePanelComponents(this.gpuPanelCompute, this.averageGpuCompute, this.precision, shouldUpdateText, shouldUpdateGraph);
331
- }
332
- if (shouldUpdateText) {
333
- this.prevTextTime = currentTime;
334
- }
335
- if (shouldUpdateGraph) {
336
- this.prevGraphTime = currentTime;
337
- }
338
- if (this.vsyncPanel !== null) {
339
- this.detectVSync(currentTime);
340
- const vsyncValue = ((_a = this.detectedVSync) == null ? void 0 : _a.refreshRate) || 0;
341
- if (shouldUpdateText && vsyncValue > 0) {
342
- this.vsyncPanel.update(vsyncValue, vsyncValue);
343
- }
344
- }
345
- return currentTime;
346
- }
347
- updatePanelComponents(panel, averageArray, precision, shouldUpdateText, shouldUpdateGraph) {
528
+ updatePanelComponents(panel, averageArray, precision, shouldUpdateText, shouldUpdateGraph, suffix = "") {
348
529
  if (!panel || averageArray.logs.length === 0)
349
530
  return;
350
- if (!(panel.name in this.lastMin)) {
351
- this.lastMin[panel.name] = Infinity;
352
- this.lastMax[panel.name] = 0;
353
- 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;
354
536
  }
355
537
  const currentValue = averageArray.logs[averageArray.logs.length - 1];
356
- this.lastMax[panel.name] = Math.max(...averageArray.logs);
357
- this.lastMin[panel.name] = Math.min(this.lastMin[panel.name], currentValue);
358
- 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;
359
541
  const graphMax = Math.max(
360
542
  Math.max(...averageArray.logs),
361
543
  ...averageArray.graph.slice(-this.samplesGraph)
@@ -363,9 +545,10 @@ const _Stats = class _Stats2 {
363
545
  this.updateCounter++;
364
546
  if (shouldUpdateText) {
365
547
  panel.update(
366
- this.lastValue[panel.name],
367
- this.lastMax[panel.name],
368
- precision
548
+ this.lastValue[key],
549
+ this.lastMax[key],
550
+ precision,
551
+ suffix
369
552
  );
370
553
  }
371
554
  if (shouldUpdateGraph) {
@@ -375,36 +558,6 @@ const _Stats = class _Stats2 {
375
558
  );
376
559
  }
377
560
  }
378
- beginProfiling(marker) {
379
- if (window.performance) {
380
- try {
381
- window.performance.clearMarks(marker);
382
- window.performance.mark(marker);
383
- } catch (error) {
384
- console.debug("Stats: Performance marking failed:", error);
385
- }
386
- }
387
- }
388
- endProfiling(startMarker, endMarker, measureName) {
389
- if (!window.performance || !endMarker || !startMarker)
390
- return;
391
- try {
392
- const entries = window.performance.getEntriesByName(startMarker, "mark");
393
- if (entries.length === 0) {
394
- this.beginProfiling(startMarker);
395
- }
396
- window.performance.clearMarks(endMarker);
397
- window.performance.mark(endMarker);
398
- window.performance.clearMeasures(measureName);
399
- const cpuMeasure = performance.measure(measureName, startMarker, endMarker);
400
- this.totalCpuDuration += cpuMeasure.duration;
401
- window.performance.clearMarks(startMarker);
402
- window.performance.clearMarks(endMarker);
403
- window.performance.clearMeasures(measureName);
404
- } catch (error) {
405
- console.debug("Stats: Performance measurement failed:", error);
406
- }
407
- }
408
561
  updatePanel(panel, averageArray, precision = 2) {
409
562
  if (!panel || averageArray.logs.length === 0)
410
563
  return;
@@ -444,48 +597,67 @@ const _Stats = class _Stats2 {
444
597
  }
445
598
  }
446
599
  }
447
- updateAverages() {
448
- this.addToAverage(this.totalCpuDuration, this.averageCpu);
449
- this.addToAverage(this.totalGpuDuration, this.averageGpu);
450
- if (this.info && this.totalGpuDurationCompute !== void 0) {
451
- this.addToAverage(this.totalGpuDurationCompute, this.averageGpuCompute);
452
- }
453
- }
454
- addToAverage(value, averageArray) {
455
- averageArray.logs.push(value);
456
- if (averageArray.logs.length > this.samplesLog) {
457
- averageArray.logs = averageArray.logs.slice(-this.samplesLog);
458
- }
459
- averageArray.graph.push(value);
460
- if (averageArray.graph.length > this.samplesGraph) {
461
- averageArray.graph = averageArray.graph.slice(-this.samplesGraph);
462
- }
463
- }
464
600
  get domElement() {
465
601
  return this.dom;
466
602
  }
467
- patchThreeWebGPU(renderer) {
468
- const originalAnimationLoop = renderer.info.reset;
469
- const statsInstance = this;
470
- renderer.info.reset = function() {
471
- statsInstance.beginProfiling("cpu-started");
472
- originalAnimationLoop.call(this);
473
- };
474
- }
475
- patchThreeRenderer(renderer) {
476
- const originalRenderMethod = renderer.render;
477
- const statsInstance = this;
478
- renderer.render = function(scene, camera) {
479
- statsInstance.begin();
480
- originalRenderMethod.call(this, scene, camera);
481
- statsInstance.end();
482
- };
483
- 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();
484
650
  }
485
651
  };
486
652
  _Stats.Panel = Panel;
653
+ _Stats.PanelTexture = PanelTexture;
487
654
  let Stats = _Stats;
488
655
  export {
656
+ PanelTexture,
657
+ StatsGLCapture,
658
+ StatsProfiler,
659
+ TextureCaptureWebGL,
660
+ TextureCaptureWebGPU,
489
661
  Stats as default
490
662
  };
491
663
  //# sourceMappingURL=main.js.map