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/README.md +360 -86
- package/addons/StatsGLNode.js +201 -0
- package/addons/StatsGLNodeWorker.js +198 -0
- package/dist/Flamingo.glb +0 -0
- package/dist/LittlestTokyo.glb +0 -0
- package/dist/core.cjs +421 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.js +421 -0
- package/dist/core.js.map +1 -0
- package/dist/main.cjs +406 -241
- package/dist/main.cjs.map +1 -1
- package/dist/main.js +404 -240
- package/dist/main.js.map +1 -1
- package/dist/panel.cjs +12 -3
- package/dist/panel.cjs.map +1 -1
- package/dist/panel.js +12 -3
- package/dist/panel.js.map +1 -1
- package/dist/panelTexture.cjs +93 -0
- package/dist/panelTexture.cjs.map +1 -0
- package/dist/panelTexture.js +93 -0
- package/dist/panelTexture.js.map +1 -0
- package/dist/profiler.cjs +95 -0
- package/dist/profiler.cjs.map +1 -0
- package/dist/profiler.js +95 -0
- package/dist/profiler.js.map +1 -0
- package/dist/stats-gl.d.ts +332 -71
- package/dist/statsGLNode.cjs +89 -0
- package/dist/statsGLNode.cjs.map +1 -0
- package/dist/statsGLNode.js +89 -0
- package/dist/statsGLNode.js.map +1 -0
- package/dist/textureCapture.cjs +283 -0
- package/dist/textureCapture.cjs.map +1 -0
- package/dist/textureCapture.js +283 -0
- package/dist/textureCapture.js.map +1 -0
- package/lib/core.ts +579 -0
- package/lib/main.ts +506 -401
- package/lib/panel.ts +18 -4
- package/lib/panelTexture.ts +122 -0
- package/lib/profiler.ts +124 -0
- package/lib/statsGLNode.ts +124 -0
- package/lib/textureCapture.ts +403 -0
- package/lib/webgpu.d.ts +190 -0
- package/package.json +9 -5
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
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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.
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
36
|
-
this.
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
124
|
-
|
|
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
|
-
|
|
128
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
this.
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
198
|
-
this.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
210
|
-
this.
|
|
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
|
-
|
|
224
|
+
updateFromInternalData() {
|
|
219
225
|
this.endProfiling("cpu-started", "cpu-finished", "cpu-duration");
|
|
220
|
-
if (
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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 =
|
|
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
|
-
|
|
269
|
-
|
|
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
|
-
|
|
272
|
-
this.
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (
|
|
277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
359
|
-
|
|
360
|
-
this.
|
|
361
|
-
this.
|
|
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[
|
|
365
|
-
this.lastMin[
|
|
366
|
-
this.lastValue[
|
|
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[
|
|
375
|
-
this.lastMax[
|
|
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
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|