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/README.md +360 -84
- package/addons/StatsGLNode.js +201 -0
- package/addons/StatsGLNodeWorker.js +198 -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 +417 -244
- package/dist/main.cjs.map +1 -1
- package/dist/main.js +415 -243
- 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 +335 -72
- 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 +514 -403
- 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 +6 -4
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
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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.
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
33
|
-
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;
|
|
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
|
-
|
|
58
|
-
|
|
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
|
-
|
|
84
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (this.
|
|
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
|
-
|
|
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
|
-
|
|
120
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
this.
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
190
|
-
this.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
202
|
-
this.
|
|
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
|
-
|
|
224
|
+
updateFromInternalData() {
|
|
211
225
|
this.endProfiling("cpu-started", "cpu-finished", "cpu-duration");
|
|
212
|
-
if (
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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 =
|
|
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
|
-
|
|
261
|
-
|
|
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
|
-
|
|
264
|
-
this.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (
|
|
269
|
-
|
|
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
|
-
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
this.
|
|
353
|
-
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;
|
|
354
536
|
}
|
|
355
537
|
const currentValue = averageArray.logs[averageArray.logs.length - 1];
|
|
356
|
-
this.lastMax[
|
|
357
|
-
this.lastMin[
|
|
358
|
-
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;
|
|
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[
|
|
367
|
-
this.lastMax[
|
|
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
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
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
|