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 CHANGED
@@ -1,111 +1,115 @@
1
- # 📈 stats-gl
1
+ # stats-gl
2
2
  [![Version](https://img.shields.io/npm/v/stats-gl?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/stats-gl)
3
3
  [![Version](https://img.shields.io/npm/dw/stats-gl?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/stats-gl)
4
4
 
5
- WebGL/WebGPU Performance Monitor tool.
5
+ WebGL/WebGPU Performance Monitor with real-time FPS, CPU, and GPU timing. Supports Three.js, native WebGL2/WebGPU, Web Workers, and texture preview panels.
6
6
 
7
- 🔗 [Live Demo](https://stats.renaudrohlinger.com/)
7
+ [Live Demo](https://stats.renaudrohlinger.com/)
8
8
 
9
9
 
10
10
  https://github.com/RenaudRohlinger/stats-gl/assets/15867665/3fdafff4-1357-4872-9baf-0629dbaf9d8c
11
11
 
12
12
 
13
- ### ❗📢 Note: To support GPU monitoring on Safari you need to enable Timer Queries under WebKit Feature Flags > WebGL Timer Queries
13
+ ### Note: To support GPU monitoring on Safari you need to enable Timer Queries under WebKit Feature Flags > WebGL Timer Queries
14
14
 
15
- ## 📚 Description
16
-
17
- `stats-gl` is a comprehensive tool to monitor WebGL performance. The Stats class provides methods to create performance panels, log performance metrics, and manage the display and layout of these panels. The performance metrics logged include FPS, CPU, and GPU. The GPU logging is available only if the user's browser supports the WebGL 2.0 `EXT_disjoint_timer_query_webgl2` extension or WebGPU Timestamp Queries.
18
-
19
- In addition to logging real-time performance data, the class also provides methods to calculate and display average performance metrics over a specified interval.
20
-
21
- ## ⬇️ Installation
22
-
23
- Stats-gl is available as an npm package. You can install it using the following command:
15
+ ## Installation
24
16
 
25
17
  ```bash
26
18
  npm install stats-gl
27
19
  ```
28
20
 
29
- ## 🧑‍💻 Example Usage
30
-
31
- Below is an example of how you can use this class in your code:
32
- ```js
33
- import Stats from "stats-gl";
34
-
35
- // create a new Stats object
36
- const stats = new Stats({
37
- trackGPU: false,
38
- trackHz: false,
39
- trackCPT: false,
40
- logsPerSecond: 4,
41
- graphsPerSecond: 30,
42
- samplesLog: 40,
43
- samplesGraph: 10,
44
- precision: 2,
45
- horizontal: true,
46
- minimal: false,
47
- mode: 0
48
- });
21
+ ## Quick Start
49
22
 
50
- // append the stats container to the body of the document
51
- document.body.appendChild( stats.dom );
23
+ ### Three.js (WebGL or WebGPU)
52
24
 
53
- // begin the performance monitor
54
- stats.begin();
55
- // end the performance monitor
56
- stats.end();
25
+ ```js
26
+ import Stats from 'stats-gl';
27
+ import * as THREE from 'three';
57
28
 
58
- stats.begin();
59
- // gl.draw... second pass
60
- stats.end();
29
+ const stats = new Stats({ trackGPU: true });
30
+ document.body.appendChild(stats.dom);
61
31
 
32
+ const renderer = new THREE.WebGLRenderer(); // or WebGPURenderer
33
+ stats.init(renderer);
62
34
 
63
- // when all the passes are drawn update the logs
64
- stats.update();
35
+ function animate() {
36
+ renderer.render(scene, camera); // or renderAsync for WebGPU
37
+ stats.update();
38
+ }
39
+ renderer.setAnimationLoop(animate);
65
40
  ```
66
41
 
42
+ ### Native WebGL2
67
43
 
68
- Quick start with threejs:
69
44
  ```js
70
- import * as THREE from 'three';
45
+ import Stats from 'stats-gl';
71
46
 
72
- // use esm module instead of cjs
73
- import Stats from 'https://www.unpkg.com/stats-gl?module';
47
+ const stats = new Stats({ trackGPU: true });
48
+ const canvas = document.querySelector('#canvas');
49
+ stats.init(canvas);
50
+ document.body.appendChild(stats.dom);
74
51
 
75
- const container = document.getElementById( 'container' );
52
+ function animate() {
53
+ stats.begin();
54
+ // ... your WebGL draw calls ...
55
+ stats.end();
56
+ stats.update();
57
+ requestAnimationFrame(animate);
58
+ }
59
+ animate();
60
+ ```
76
61
 
77
- const stats = new Stats();
78
- container.appendChild( stats.dom );
62
+ ### Native WebGPU
79
63
 
80
- const renderer = new THREE.WebGLRenderer( { antialias: true } ); // or WebGPURenderer
81
- container.appendChild( renderer.domElement );
64
+ ```js
65
+ import Stats from 'stats-gl';
82
66
 
83
- const scene = new THREE.Scene();
67
+ const adapter = await navigator.gpu.requestAdapter();
68
+ const device = await adapter.requestDevice({ requiredFeatures: ['timestamp-query'] });
69
+ const context = canvas.getContext('webgpu');
84
70
 
85
- stats.init( renderer ); // this will patch the threejs render function so no need to call begin() or end()
71
+ const stats = new Stats({ trackGPU: true });
72
+ stats.init(device); // Pass the GPUDevice
73
+ document.body.appendChild(stats.dom);
86
74
 
87
75
  function animate() {
76
+ stats.begin();
88
77
 
89
- requestAnimationFrame( animate );
78
+ const encoder = device.createCommandEncoder();
79
+ const pass = encoder.beginRenderPass({
80
+ colorAttachments: [...],
81
+ timestampWrites: stats.getTimestampWrites() // Enable GPU timing
82
+ });
83
+ // ... your draw calls ...
84
+ pass.end();
90
85
 
91
- render(); // needs async methods in WebGPU (renderAsync)
92
- stats.update();
86
+ stats.end(encoder); // Pass encoder to resolve timestamps
87
+ device.queue.submit([encoder.finish()]);
93
88
 
89
+ stats.update();
90
+ requestAnimationFrame(animate);
94
91
  }
92
+ animate();
95
93
  ```
96
- Quick start with [@react-three/fiber](https://github.com/pmndrs/fiber). A `<StatsGl />` component is available through [@react-three/drei](https://github.com/pmndrs/drei):
94
+
95
+ ### React Three Fiber
96
+
97
+ A `<StatsGl />` component is available through [@react-three/drei](https://github.com/pmndrs/drei):
98
+
97
99
  ```jsx
98
100
  import { Canvas } from '@react-three/fiber'
99
101
  import { StatsGl } from '@react-three/drei'
100
102
 
101
103
  const Scene = () => (
102
- <Canvas>
103
- <StatsGl />
104
- </Canvas>
104
+ <Canvas>
105
+ <StatsGl />
106
+ </Canvas>
105
107
  )
106
108
  ```
107
109
 
108
- Quick start with [Tresjs](https://tresjs.org/) for Vue developers. A `<StatsGl />` component is available through [cientos](https://cientos.tresjs.org/guide/misc/stats-gl.html):
110
+ ### Tresjs (Vue)
111
+
112
+ A `<StatsGl />` component is available through [cientos](https://cientos.tresjs.org/guide/misc/stats-gl.html):
109
113
 
110
114
  ```vue
111
115
  <script setup lang="ts">
@@ -119,33 +123,305 @@ import { StatsGl } from '@tresjs/cientos'
119
123
  </TresCanvas>
120
124
  </template>
121
125
  ```
122
- ## ⚙️ Parameters
123
- The constructor for the Stats class accepts an options object with the following properties:
124
126
 
125
- - `logsPerSecond`: How often to log performance data, in logs per second.
126
- - `graphsPerSecond`: How often to update the graph, in graphs per second.
127
- - `trackGPU`: A boolean value to enable or disable GPU tracking.
128
- - `trackHz`: A boolean value to enable or disable Hz tracking.
129
- - `trackCPT`: (Threejs specific) A boolean value to enable or disable Threejs Compute Shading tracking.
130
- - `samplesLog`: Number of recent log samples to keep for computing averages.
131
- - `samplesGraph`: Number of recent graph samples to keep for computing averages.
132
- - `precision`: Precision of the data, in number of decimal places (only affects CPU and GPU).
133
- - `minimal`: A boolean value to control the minimalistic mode of the panel display. If set to true, a simple click on the panel will switch between different metrics.
134
- - `mode`: Sets the initial panel to display - 0 for FPS, 1 for CPU, and 2 for GPU (if supported).
135
- - `horizontal`: Display the canvases on the X axis, set to align on vertical axis.
127
+ ## Parameters
128
+
129
+ | Parameter | Type | Default | Description |
130
+ |-----------|------|---------|-------------|
131
+ | `trackFPS` | boolean | `true` | Enable built-in FPS and CPU panels |
132
+ | `trackGPU` | boolean | `false` | Enable GPU timing (requires extension support) |
133
+ | `trackHz` | boolean | `false` | Enable refresh rate detection |
134
+ | `trackCPT` | boolean | `false` | Enable Three.js compute shader timing (WebGPU only) |
135
+ | `logsPerSecond` | number | `4` | How often to update text display |
136
+ | `graphsPerSecond` | number | `30` | How often to update graphs |
137
+ | `samplesLog` | number | `40` | Number of samples for text averaging |
138
+ | `samplesGraph` | number | `10` | Number of samples for graph averaging |
139
+ | `precision` | number | `2` | Decimal places for CPU/GPU values |
140
+ | `minimal` | boolean | `false` | Minimal mode - click to cycle panels |
141
+ | `horizontal` | boolean | `true` | Horizontal panel layout |
142
+ | `mode` | number | `0` | Initial panel (0=FPS, 1=CPU, 2=GPU) |
143
+
144
+ ## Web Worker / OffscreenCanvas
145
+
146
+ stats-gl supports rendering in a Web Worker using OffscreenCanvas. Use `StatsProfiler` in the worker to collect timing data, and send it to the main thread where `Stats` displays it.
147
+
148
+ **Worker (offscreen rendering):**
149
+ ```js
150
+ import { StatsProfiler } from 'stats-gl';
151
+
152
+ const profiler = new StatsProfiler({ trackGPU: true });
153
+
154
+ self.onmessage = async (e) => {
155
+ if (e.data.type === 'init') {
156
+ const canvas = e.data.canvas;
157
+ const gl = canvas.getContext('webgl2');
158
+ await profiler.init(gl);
159
+ requestAnimationFrame(loop);
160
+ }
161
+ };
162
+
163
+ function loop() {
164
+ profiler.begin();
165
+ // ... your rendering code ...
166
+ profiler.end();
167
+ profiler.update();
168
+
169
+ // Send timing data to main thread
170
+ self.postMessage({ type: 'stats', ...profiler.getData() });
171
+ requestAnimationFrame(loop);
172
+ }
173
+ ```
174
+
175
+ **Main thread:**
176
+ ```js
177
+ import Stats from 'stats-gl';
178
+
179
+ const stats = new Stats({ trackGPU: true });
180
+ document.body.appendChild(stats.dom);
181
+
182
+ const canvas = document.getElementById('canvas');
183
+ const offscreen = canvas.transferControlToOffscreen();
184
+
185
+ const worker = new Worker('worker.js', { type: 'module' });
186
+ worker.postMessage({ type: 'init', canvas: offscreen }, [offscreen]);
187
+
188
+ worker.onmessage = (e) => {
189
+ if (e.data.type === 'stats') {
190
+ stats.setData(e.data);
191
+ }
192
+ };
193
+
194
+ function loop() {
195
+ stats.update();
196
+ requestAnimationFrame(loop);
197
+ }
198
+ loop();
199
+ ```
200
+
201
+ ### StatsProfiler API
202
+
203
+ `StatsProfiler` is a headless version of `Stats` designed for workers:
204
+
205
+ - `init(canvas | device)` - Initialize with WebGL context, OffscreenCanvas, or GPUDevice
206
+ - `begin()` / `end(encoder?)` - Wrap your render calls (pass encoder for native WebGPU)
207
+ - `getTimestampWrites()` - Get timestampWrites config for native WebGPU render pass
208
+ - `update()` - Process timing data
209
+ - `getData()` - Returns `{ fps, cpu, gpu, gpuCompute }`
210
+ - `captureTexture(source, sourceId)` - Capture texture to ImageBitmap for transfer
211
+
212
+ ### Stats.setData()
213
+
214
+ Use `stats.setData(data)` to feed external timing data into the Stats UI. When set, `update()` uses this data instead of internal timing.
215
+
216
+ ## Texture Preview Panels
217
+
218
+ Display render target previews alongside performance metrics. Supports both WebGL and WebGPU.
219
+
220
+ ### Three.js Usage
221
+
222
+ ```js
223
+ const stats = new Stats({ trackGPU: true });
224
+ stats.init(renderer);
225
+
226
+ // Create a texture panel
227
+ const panel = stats.addTexturePanel('GBuffer');
228
+
229
+ // Set texture source (WebGLRenderTarget or WebGPU RenderTarget)
230
+ const renderTarget = new THREE.WebGLRenderTarget(width, height);
231
+ stats.setTexture('GBuffer', renderTarget);
232
+
233
+ // In render loop - textures update automatically
234
+ function animate() {
235
+ renderer.setRenderTarget(renderTarget);
236
+ renderer.render(scene, camera);
237
+ renderer.setRenderTarget(null);
238
+ renderer.render(scene, camera);
239
+ stats.update();
240
+ }
241
+ ```
242
+
243
+ ### Worker Texture Transfer
244
+
245
+ ```js
246
+ // Worker - capture and transfer texture
247
+ const bitmap = await profiler.captureTexture(renderTarget, 'gbuffer');
248
+ self.postMessage(
249
+ { type: 'texture', name: 'GBuffer', bitmap, width, height },
250
+ [bitmap]
251
+ );
252
+
253
+ // Main thread - receive and display
254
+ worker.onmessage = (e) => {
255
+ if (e.data.type === 'texture') {
256
+ stats.setTextureBitmap(e.data.name, e.data.bitmap, e.data.width, e.data.height);
257
+ }
258
+ };
259
+ ```
260
+
261
+ ### Texture Panel API
262
+
263
+ - `stats.addTexturePanel(name)` - Create a new texture preview panel
264
+ - `stats.setTexture(name, source)` - Set Three.js RenderTarget source
265
+ - `stats.setTextureWebGL(name, framebuffer, width, height)` - Set raw WebGL framebuffer
266
+ - `stats.setTextureBitmap(name, bitmap, width?, height?)` - Set ImageBitmap (for workers)
267
+ - `stats.removeTexturePanel(name)` - Remove a texture panel
268
+
269
+ ## TSL Node Capture (WebGPU)
270
+
271
+ Capture any Three.js TSL node for live preview. Works with MRT, post-processing, and custom shaders.
272
+
273
+ ### Main Thread Usage
274
+
275
+ ```js
276
+ import Stats from 'stats-gl';
277
+ import { statsGL } from 'stats-gl/addons/StatsGLNode.js';
278
+ import { addMethodChaining } from 'three/tsl';
279
+
280
+ // Enable .toStatsGL() method on TSL nodes
281
+ addMethodChaining('toStatsGL', statsGL);
282
+
283
+ const stats = new Stats({ trackGPU: true });
284
+ stats.init(renderer);
285
+ document.body.appendChild(stats.dom);
286
+
287
+ // In your PostProcessing setup:
288
+ const scenePass = pass(scene, camera);
289
+ scenePass.setMRT(mrt({
290
+ output,
291
+ normal: directionToColor(normalView),
292
+ diffuse: diffuseColor
293
+ }));
294
+
295
+ // Register nodes for capture - panels are created automatically
296
+ scenePass.getTextureNode('diffuse').toStatsGL('Diffuse', stats);
297
+ scenePass.getTextureNode('normal').toStatsGL('Normal', stats);
298
+ scenePass.getLinearDepthNode().toStatsGL('Depth', stats);
299
+ ```
300
+
301
+ ### Web Worker Usage
302
+
303
+ For OffscreenCanvas rendering in workers, use the worker addon:
304
+
305
+ **Worker:**
306
+ ```js
307
+ import { StatsProfiler } from 'stats-gl';
308
+ import { statsGLWorker, flushCaptures } from 'stats-gl/addons/StatsGLNodeWorker.js';
309
+ import { addMethodChaining } from 'three/tsl';
310
+
311
+ addMethodChaining('toStatsGL', statsGLWorker);
312
+
313
+ const profiler = new StatsProfiler({ trackGPU: true });
314
+ await profiler.init(renderer);
315
+
316
+ // Register nodes (no stats instance needed in worker)
317
+ diffuseNode.toStatsGL('Diffuse');
318
+ normalNode.toStatsGL('Normal');
319
+ depthNode.toStatsGL('Depth');
320
+
321
+ async function render() {
322
+ profiler.begin();
323
+ postProcessing.render();
324
+ profiler.end();
325
+ profiler.update();
326
+
327
+ // Send stats to main thread
328
+ self.postMessage({ type: 'stats', data: profiler.getData() });
329
+
330
+ // Capture and transfer TSL nodes
331
+ const captures = await flushCaptures(renderer);
332
+ for (const { name, bitmap } of captures) {
333
+ self.postMessage({ type: 'texture', name, bitmap }, [bitmap]);
334
+ }
335
+ }
336
+ ```
337
+
338
+ **Main Thread:**
339
+ ```js
340
+ import Stats from 'stats-gl';
341
+
342
+ const stats = new Stats({ trackGPU: true });
343
+ document.body.appendChild(stats.dom);
344
+
345
+ // Create panels for worker captures
346
+ stats.addTexturePanel('Diffuse');
347
+ stats.addTexturePanel('Normal');
348
+ stats.addTexturePanel('Depth');
349
+
350
+ worker.onmessage = (e) => {
351
+ if (e.data.type === 'stats') stats.setData(e.data.data);
352
+ if (e.data.type === 'texture') {
353
+ stats.setTextureBitmap(e.data.name, e.data.bitmap);
354
+ }
355
+ };
356
+ ```
357
+
358
+ ### Transform Callback
359
+
360
+ Use a callback to transform the node before capture (e.g., linearize depth):
361
+
362
+ ```js
363
+ depthNode.toStatsGL('Depth', stats, (node) => linearizeDepth(node));
364
+ ```
365
+
366
+ ## Custom Panels
367
+
368
+ Add custom metrics panels:
369
+
370
+ ```js
371
+ const customPanel = stats.addPanel(new Stats.Panel('COUNT', '#ff0', '#220'));
372
+
373
+ function animate() {
374
+ // Update with value and max
375
+ customPanel.update(currentValue, maxValue, 2); // 2 decimal places
376
+ customPanel.updateGraph(currentValue, maxValue);
377
+ stats.update();
378
+ }
379
+ ```
380
+
381
+ ## API Reference
382
+
383
+ ### Default Export: Stats
384
+
385
+ Main class with DOM rendering.
136
386
 
137
- All the parameters are optional and have default values. The class also provides other methods such as begin(), end(), init(canvas), etc. which can be used based on the requirement.
387
+ ```js
388
+ import Stats from 'stats-gl';
389
+
390
+ const stats = new Stats(options);
391
+ stats.init(renderer); // Initialize with Three.js renderer, canvas, or GPUDevice
392
+ stats.begin(); // Start timing (auto-called for Three.js)
393
+ stats.end(encoder?); // End timing (pass encoder for native WebGPU)
394
+ stats.update(); // Update display
395
+ stats.setData(data); // Set external timing data
396
+ stats.getTimestampWrites(); // Get timestampWrites for native WebGPU render pass
397
+ stats.dispose(); // Clean up resources
398
+ ```
399
+
400
+ ### Named Exports
401
+
402
+ ```js
403
+ import Stats, {
404
+ StatsProfiler, // Headless profiler for workers
405
+ PanelTexture, // Texture preview panel class
406
+ TextureCaptureWebGL, // WebGL texture capture utility
407
+ TextureCaptureWebGPU, // WebGPU texture capture utility
408
+ StatsGLCapture // Addon capture helper
409
+ } from 'stats-gl';
410
+
411
+ // TSL Node capture addons (WebGPU only)
412
+ import { statsGL } from 'stats-gl/addons/StatsGLNode.js';
413
+ import { statsGLWorker, flushCaptures } from 'stats-gl/addons/StatsGLNodeWorker.js';
414
+ ```
138
415
 
416
+ ## Contributing
139
417
 
140
- ## 🤝 Contributing
141
- Contributions to Stats-gl are welcome.
418
+ Contributions to stats-gl are welcome. Please report any issues or bugs you encounter.
142
419
 
143
- Please report any issues or bugs you encounter.
420
+ ## License
144
421
 
145
- ## 📜 License
146
422
  This project is licensed under the MIT License.
147
423
 
148
- ## 🧑‍🎨 Maintainers :
424
+ ## Maintainers
149
425
 
150
- - [`twitter 🐈‍⬛ @onirenaud`](https://twitter.com/onirenaud)
151
- - [`twitter @utsuboco`](https://twitter.com/utsuboco)
426
+ - [@onirenaud](https://twitter.com/onirenaud)
427
+ - [@utsuboco](https://twitter.com/utsuboco)