webgpu-profiler 0.1.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/LICENSE +21 -0
- package/README.md +97 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/instrument.d.ts +115 -0
- package/dist/instrument.d.ts.map +1 -0
- package/dist/instrument.js +315 -0
- package/dist/instrument.js.map +1 -0
- package/dist/profiler.d.ts +48 -0
- package/dist/profiler.d.ts.map +1 -0
- package/dist/profiler.js +105 -0
- package/dist/profiler.js.map +1 -0
- package/dist/react/MemoryHUD.d.ts +27 -0
- package/dist/react/MemoryHUD.d.ts.map +1 -0
- package/dist/react/MemoryHUD.js +232 -0
- package/dist/react/MemoryHUD.js.map +1 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +73 -0
- package/src/index.ts +20 -0
- package/src/instrument.ts +377 -0
- package/src/profiler.ts +171 -0
- package/src/react/MemoryHUD.tsx +438 -0
- package/src/react/index.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Redmond Riddell
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# webgpu-profiler
|
|
2
|
+
|
|
3
|
+
Live GPU memory profiler for WebGPU. One call patches the device, then every
|
|
4
|
+
buffer and texture is tracked. Optional React HUD overlay.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install webgpu-profiler
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Use
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { autoInstrument } from "webgpu-profiler";
|
|
14
|
+
|
|
15
|
+
// Once, at boot, before any renderer is created.
|
|
16
|
+
if (process.env.NODE_ENV !== "production") autoInstrument();
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { MemoryHUD } from "webgpu-profiler/react";
|
|
21
|
+
|
|
22
|
+
// Anywhere in your tree, dev-only:
|
|
23
|
+
{process.env.NODE_ENV !== "production" && <MemoryHUD />}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
That's it. The HUD finds the active instrumentation automatically.
|
|
27
|
+
|
|
28
|
+
## What it does
|
|
29
|
+
|
|
30
|
+
`autoInstrument()` patches `GPUAdapter.prototype.requestDevice` so any
|
|
31
|
+
`GPUDevice` returned anywhere in the app gets its `createBuffer` and
|
|
32
|
+
`createTexture` wrapped. Those are the only two WebGPU JS-side allocation
|
|
33
|
+
entry points, so every buffer and texture is captured with its descriptor.
|
|
34
|
+
The HUD polls a snapshot every second and shows a copyable breakdown.
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
|
|
38
|
+
| Symbol | Purpose |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `autoInstrument(options?)` | Patch the adapter prototype to auto-instrument every new device. Returns a disposer. |
|
|
41
|
+
| `instrumentDevice(device)` | Manually instrument a single device. Idempotent. Returns a `DeviceInstrumentation` handle. |
|
|
42
|
+
| `getActiveInstrumentation()` | Read the most recently registered handle (null if none). |
|
|
43
|
+
| `subscribeActiveInstrumentation(cb)` | Subscribe to changes in the active handle. |
|
|
44
|
+
| `profileMemory(inst)` | Build a `MemoryReport` snapshot from a handle. |
|
|
45
|
+
| `reportToText(report)` | Plain-text rendering for clipboard / bug reports. |
|
|
46
|
+
| `textureDescriptorBytes(desc)` | Byte calc for a single `GPUTextureDescriptor`. |
|
|
47
|
+
| `resolveExtent(size)` | Normalize a `GPUExtent3D` to `{ width, height, depth }`. |
|
|
48
|
+
| `MemoryHUD` *(from `/react`)* | Overlay component. All props optional. |
|
|
49
|
+
|
|
50
|
+
`MemoryHUD` props: `instrumentation?`, `refreshMs?` (1000), `corner?`
|
|
51
|
+
(`"top-right"` / `"top-left"` / `"bottom-right"` / `"bottom-left"`),
|
|
52
|
+
`className?`, `style?`.
|
|
53
|
+
|
|
54
|
+
### Building your own UI
|
|
55
|
+
|
|
56
|
+
If you're not using React, `getActiveInstrumentation()` and
|
|
57
|
+
`subscribeActiveInstrumentation()` let you react to instrumentation
|
|
58
|
+
changes from any code:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import {
|
|
62
|
+
getActiveInstrumentation,
|
|
63
|
+
subscribeActiveInstrumentation,
|
|
64
|
+
profileMemory,
|
|
65
|
+
} from "webgpu-profiler";
|
|
66
|
+
|
|
67
|
+
const unsubscribe = subscribeActiveInstrumentation(() => {
|
|
68
|
+
const inst = getActiveInstrumentation();
|
|
69
|
+
if (inst) console.log(profileMemory(inst));
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## What is not counted
|
|
74
|
+
|
|
75
|
+
- Canvas swapchain backbuffer (created by `context.configure()`, not
|
|
76
|
+
`createTexture`).
|
|
77
|
+
- Driver-internal allocations: residency caches, MSAA backing, pipeline
|
|
78
|
+
objects, bind group layouts, staging buffers that bypass the JS API.
|
|
79
|
+
Always opaque to JS by design.
|
|
80
|
+
|
|
81
|
+
Compare against your browser's per-tab GPU memory readout
|
|
82
|
+
(Chrome `Shift+Esc`, "GPU Memory" column) to see the residual gap, which
|
|
83
|
+
is typically 5 to 15 percent.
|
|
84
|
+
|
|
85
|
+
## Caveats
|
|
86
|
+
|
|
87
|
+
- WebGPU only. WebGL's allocation model differs significantly and is not
|
|
88
|
+
supported.
|
|
89
|
+
- Depth-stencil formats (`depth24plus`, `depth32float-stencil8`, etc.) are
|
|
90
|
+
reported as their JS-API minimums. Real driver footprint is often
|
|
91
|
+
larger due to alignment and separate stencil planes.
|
|
92
|
+
- ASTC bytes-per-pixel reflect the format's block size. Some
|
|
93
|
+
implementations may pad differently.
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT (c) Redmond Riddell
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { instrumentDevice, autoInstrument, getActiveInstrumentation, subscribeActiveInstrumentation, textureDescriptorBytes, resolveExtent, type DeviceInstrumentation, type TrackedBuffer, type TrackedTexture, type AutoInstrumentOptions, } from "./instrument.js";
|
|
2
|
+
export { profileMemory, reportToText, type MemoryReport, type TextureEntry, type BufferEntry, } from "./profiler.js";
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,wBAAwB,EACxB,8BAA8B,EAC9B,sBAAsB,EACtB,aAAa,EACb,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,qBAAqB,GAC3B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,aAAa,EACb,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,WAAW,GACjB,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,wBAAwB,EACxB,8BAA8B,EAC9B,sBAAsB,EACtB,aAAa,GAKd,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,aAAa,EACb,YAAY,GAIb,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patches `GPUDevice.createBuffer` and `GPUDevice.createTexture` to track
|
|
3
|
+
* every GPU resource the page allocates. Combined with a `.destroy()` patch
|
|
4
|
+
* on each returned object, this gives a live view of all WebGPU memory
|
|
5
|
+
* the JS API can see.
|
|
6
|
+
*
|
|
7
|
+
* Why monkey-patch the device:
|
|
8
|
+
* - WebGPU has *exactly two* JS-visible allocation entry points
|
|
9
|
+
* (`createBuffer`, `createTexture`). Patching both captures everything
|
|
10
|
+
* a renderer, post-processing nodes, and compute services allocate.
|
|
11
|
+
* - No need to instrument production modules. This is the same pattern
|
|
12
|
+
* used by React DevTools, Vue DevTools, and Chrome's GPU inspector.
|
|
13
|
+
*
|
|
14
|
+
* What is NOT captured (driver-internal, not exposed to JS):
|
|
15
|
+
* - Canvas swapchain backbuffer (created by `context.configure()`).
|
|
16
|
+
* - Driver-side staging buffers, residency systems, MSAA backing,
|
|
17
|
+
* pipeline layouts, bind group memory.
|
|
18
|
+
*
|
|
19
|
+
* ## Timing
|
|
20
|
+
*
|
|
21
|
+
* Call `instrumentDevice(device)` *after* the renderer has obtained a
|
|
22
|
+
* `GPUDevice`, but *before* any other code allocates resources. In
|
|
23
|
+
* practice: right after the renderer's `init()` promise resolves. A few
|
|
24
|
+
* allocations (the swapchain, a couple of renderer internals) happen
|
|
25
|
+
* during init itself and will not be captured. Typical overhead: <5 MB.
|
|
26
|
+
*
|
|
27
|
+
* ## Usage
|
|
28
|
+
*
|
|
29
|
+
* import { instrumentDevice } from "webgpu-profiler";
|
|
30
|
+
*
|
|
31
|
+
* const inst = instrumentDevice(device);
|
|
32
|
+
* // ... time passes ...
|
|
33
|
+
* console.log(`GPU bytes: ${inst.bytes()}`);
|
|
34
|
+
* // When done:
|
|
35
|
+
* inst.uninstrument();
|
|
36
|
+
*
|
|
37
|
+
* The returned maps are live views — iterate at query time, do not cache.
|
|
38
|
+
* Entries are removed automatically when `buffer.destroy()` or
|
|
39
|
+
* `texture.destroy()` is called.
|
|
40
|
+
*/
|
|
41
|
+
export interface TrackedBuffer {
|
|
42
|
+
buffer: GPUBuffer;
|
|
43
|
+
descriptor: GPUBufferDescriptor;
|
|
44
|
+
}
|
|
45
|
+
export interface TrackedTexture {
|
|
46
|
+
texture: GPUTexture;
|
|
47
|
+
descriptor: GPUTextureDescriptor;
|
|
48
|
+
}
|
|
49
|
+
export interface DeviceInstrumentation {
|
|
50
|
+
readonly device: GPUDevice;
|
|
51
|
+
readonly buffers: ReadonlyMap<GPUBuffer, TrackedBuffer>;
|
|
52
|
+
readonly textures: ReadonlyMap<GPUTexture, TrackedTexture>;
|
|
53
|
+
/** Sum of all tracked buffer sizes, in bytes. */
|
|
54
|
+
bufferBytes(): number;
|
|
55
|
+
/** Sum of all tracked texture sizes (across formats, mip chains, layers). */
|
|
56
|
+
textureBytes(): number;
|
|
57
|
+
/** Combined total in bytes. */
|
|
58
|
+
bytes(): number;
|
|
59
|
+
/** Remove patches and restore the original device methods. */
|
|
60
|
+
uninstrument(): void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolve a GPUExtent3D to (w, h, d). Per WebGPU spec, `size` is either a
|
|
64
|
+
* `GPUExtent3DDict` (object with `.width`) or any iterable of numbers
|
|
65
|
+
* (Array, Uint32Array, etc). `Array.isArray` returns false for typed
|
|
66
|
+
* arrays, so we discriminate on the dict's `width` property instead.
|
|
67
|
+
*/
|
|
68
|
+
export declare function resolveExtent(size: GPUExtent3D): {
|
|
69
|
+
width: number;
|
|
70
|
+
height: number;
|
|
71
|
+
depth: number;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Bytes occupied on the GPU by a texture with the given descriptor.
|
|
75
|
+
* Accounts for mip chain, array layers / depth, MSAA samples, and the
|
|
76
|
+
* format-specific bits per pixel.
|
|
77
|
+
*
|
|
78
|
+
* For 3D textures, each mip level halves all three dimensions, so the
|
|
79
|
+
* per-level pixel count series is `8^-i`. For 2D and 2D-array textures
|
|
80
|
+
* only x/y halve, giving `4^-i`.
|
|
81
|
+
*/
|
|
82
|
+
export declare function textureDescriptorBytes(desc: GPUTextureDescriptor): number;
|
|
83
|
+
/**
|
|
84
|
+
* Returns the most recently registered active instrumentation (or `null`
|
|
85
|
+
* if none). For React, prefer using `MemoryHUD` without a prop — it reads
|
|
86
|
+
* this via `useSyncExternalStore` and re-renders when it changes.
|
|
87
|
+
*/
|
|
88
|
+
export declare function getActiveInstrumentation(): DeviceInstrumentation | null;
|
|
89
|
+
/**
|
|
90
|
+
* Subscribe to changes in the active instrumentation. Returns an
|
|
91
|
+
* unsubscribe function. Used internally by `MemoryHUD` via React's
|
|
92
|
+
* `useSyncExternalStore`. Exposed for non-React consumers who want to
|
|
93
|
+
* react to active-instrumentation changes.
|
|
94
|
+
*/
|
|
95
|
+
export declare function subscribeActiveInstrumentation(cb: () => void): () => void;
|
|
96
|
+
export declare function instrumentDevice(device: GPUDevice): DeviceInstrumentation;
|
|
97
|
+
export interface AutoInstrumentOptions {
|
|
98
|
+
/** Called for each device the patch instruments. Useful for logging. */
|
|
99
|
+
onInstrument?: (instrumentation: DeviceInstrumentation) => void;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Patches `GPUAdapter.prototype.requestDevice` to auto-instrument every
|
|
103
|
+
* `GPUDevice` it returns. Returns a disposer that restores the original
|
|
104
|
+
* method (existing instrumentations stay live until their own
|
|
105
|
+
* `uninstrument()` is called).
|
|
106
|
+
*
|
|
107
|
+
* Typical usage at the top of your app boot file:
|
|
108
|
+
*
|
|
109
|
+
* import { autoInstrument } from "webgpu-profiler";
|
|
110
|
+
* if (import.meta.env.DEV) autoInstrument();
|
|
111
|
+
*
|
|
112
|
+
* No further wiring needed — render `<MemoryHUD />` anywhere.
|
|
113
|
+
*/
|
|
114
|
+
export declare function autoInstrument(options?: AutoInstrumentOptions): () => void;
|
|
115
|
+
//# sourceMappingURL=instrument.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrument.d.ts","sourceRoot":"","sources":["../src/instrument.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,EAAE,mBAAmB,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,UAAU,CAAC;IACpB,UAAU,EAAE,oBAAoB,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IACxD,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IAC3D,iDAAiD;IACjD,WAAW,IAAI,MAAM,CAAC;IACtB,6EAA6E;IAC7E,YAAY,IAAI,MAAM,CAAC;IACvB,+BAA+B;IAC/B,KAAK,IAAI,MAAM,CAAC;IAChB,8DAA8D;IAC9D,YAAY,IAAI,IAAI,CAAC;CACtB;AA2ED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,WAAW,GAChB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAWlD;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,CAazE;AAmBD;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,qBAAqB,GAAG,IAAI,CAEvE;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAKzE;AAID,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,qBAAqB,CAsFzE;AAeD,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,YAAY,CAAC,EAAE,CAAC,eAAe,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACjE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,MAAM,IAAI,CAiC9E"}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patches `GPUDevice.createBuffer` and `GPUDevice.createTexture` to track
|
|
3
|
+
* every GPU resource the page allocates. Combined with a `.destroy()` patch
|
|
4
|
+
* on each returned object, this gives a live view of all WebGPU memory
|
|
5
|
+
* the JS API can see.
|
|
6
|
+
*
|
|
7
|
+
* Why monkey-patch the device:
|
|
8
|
+
* - WebGPU has *exactly two* JS-visible allocation entry points
|
|
9
|
+
* (`createBuffer`, `createTexture`). Patching both captures everything
|
|
10
|
+
* a renderer, post-processing nodes, and compute services allocate.
|
|
11
|
+
* - No need to instrument production modules. This is the same pattern
|
|
12
|
+
* used by React DevTools, Vue DevTools, and Chrome's GPU inspector.
|
|
13
|
+
*
|
|
14
|
+
* What is NOT captured (driver-internal, not exposed to JS):
|
|
15
|
+
* - Canvas swapchain backbuffer (created by `context.configure()`).
|
|
16
|
+
* - Driver-side staging buffers, residency systems, MSAA backing,
|
|
17
|
+
* pipeline layouts, bind group memory.
|
|
18
|
+
*
|
|
19
|
+
* ## Timing
|
|
20
|
+
*
|
|
21
|
+
* Call `instrumentDevice(device)` *after* the renderer has obtained a
|
|
22
|
+
* `GPUDevice`, but *before* any other code allocates resources. In
|
|
23
|
+
* practice: right after the renderer's `init()` promise resolves. A few
|
|
24
|
+
* allocations (the swapchain, a couple of renderer internals) happen
|
|
25
|
+
* during init itself and will not be captured. Typical overhead: <5 MB.
|
|
26
|
+
*
|
|
27
|
+
* ## Usage
|
|
28
|
+
*
|
|
29
|
+
* import { instrumentDevice } from "webgpu-profiler";
|
|
30
|
+
*
|
|
31
|
+
* const inst = instrumentDevice(device);
|
|
32
|
+
* // ... time passes ...
|
|
33
|
+
* console.log(`GPU bytes: ${inst.bytes()}`);
|
|
34
|
+
* // When done:
|
|
35
|
+
* inst.uninstrument();
|
|
36
|
+
*
|
|
37
|
+
* The returned maps are live views — iterate at query time, do not cache.
|
|
38
|
+
* Entries are removed automatically when `buffer.destroy()` or
|
|
39
|
+
* `texture.destroy()` is called.
|
|
40
|
+
*/
|
|
41
|
+
// ── GPUTextureFormat → bytes per texel ──────────────────────────────────────
|
|
42
|
+
//
|
|
43
|
+
// Comprehensive table per WebGPU spec. For compressed block formats, bpp is
|
|
44
|
+
// the effective rate (BC1 = 0.5, BC7 = 1.0, etc.). A few approximate cases
|
|
45
|
+
// (depth24plus, ASTC variants) are flagged inline.
|
|
46
|
+
const FORMAT_BPP = {
|
|
47
|
+
// 8-bit single channel
|
|
48
|
+
r8unorm: 1, r8snorm: 1, r8uint: 1, r8sint: 1,
|
|
49
|
+
// 8-bit two channel
|
|
50
|
+
rg8unorm: 2, rg8snorm: 2, rg8uint: 2, rg8sint: 2,
|
|
51
|
+
// 8-bit four channel
|
|
52
|
+
rgba8unorm: 4, "rgba8unorm-srgb": 4,
|
|
53
|
+
rgba8snorm: 4, rgba8uint: 4, rgba8sint: 4,
|
|
54
|
+
bgra8unorm: 4, "bgra8unorm-srgb": 4,
|
|
55
|
+
// 16-bit single channel
|
|
56
|
+
r16float: 2, r16uint: 2, r16sint: 2, r16unorm: 2, r16snorm: 2,
|
|
57
|
+
// 16-bit two channel
|
|
58
|
+
rg16float: 4, rg16uint: 4, rg16sint: 4, rg16unorm: 4, rg16snorm: 4,
|
|
59
|
+
// 16-bit four channel
|
|
60
|
+
rgba16float: 8, rgba16uint: 8, rgba16sint: 8,
|
|
61
|
+
rgba16unorm: 8, rgba16snorm: 8,
|
|
62
|
+
// 32-bit single channel
|
|
63
|
+
r32float: 4, r32uint: 4, r32sint: 4,
|
|
64
|
+
// 32-bit two channel
|
|
65
|
+
rg32float: 8, rg32uint: 8, rg32sint: 8,
|
|
66
|
+
// 32-bit four channel
|
|
67
|
+
rgba32float: 16, rgba32uint: 16, rgba32sint: 16,
|
|
68
|
+
// Packed
|
|
69
|
+
rgb9e5ufloat: 4,
|
|
70
|
+
rgb10a2unorm: 4, rgb10a2uint: 4,
|
|
71
|
+
rg11b10ufloat: 4,
|
|
72
|
+
// Depth / stencil
|
|
73
|
+
stencil8: 1,
|
|
74
|
+
depth16unorm: 2,
|
|
75
|
+
depth24plus: 4, // implementation-defined, conservative
|
|
76
|
+
"depth24plus-stencil8": 4,
|
|
77
|
+
depth32float: 4,
|
|
78
|
+
"depth32float-stencil8": 5,
|
|
79
|
+
// Block-compressed (effective bytes per pixel)
|
|
80
|
+
"bc1-rgba-unorm": 0.5, "bc1-rgba-unorm-srgb": 0.5,
|
|
81
|
+
"bc2-rgba-unorm": 1, "bc2-rgba-unorm-srgb": 1,
|
|
82
|
+
"bc3-rgba-unorm": 1, "bc3-rgba-unorm-srgb": 1,
|
|
83
|
+
"bc4-r-unorm": 0.5, "bc4-r-snorm": 0.5,
|
|
84
|
+
"bc5-rg-unorm": 1, "bc5-rg-snorm": 1,
|
|
85
|
+
"bc6h-rgb-ufloat": 1, "bc6h-rgb-float": 1,
|
|
86
|
+
"bc7-rgba-unorm": 1, "bc7-rgba-unorm-srgb": 1,
|
|
87
|
+
"etc2-rgb8unorm": 0.5, "etc2-rgb8unorm-srgb": 0.5,
|
|
88
|
+
"etc2-rgb8a1unorm": 0.5, "etc2-rgb8a1unorm-srgb": 0.5,
|
|
89
|
+
"etc2-rgba8unorm": 1, "etc2-rgba8unorm-srgb": 1,
|
|
90
|
+
"eac-r11unorm": 0.5, "eac-r11snorm": 0.5,
|
|
91
|
+
"eac-rg11unorm": 1, "eac-rg11snorm": 1,
|
|
92
|
+
// ASTC variants are derived dynamically from the format string — see
|
|
93
|
+
// bytesPerTexel(). All ASTC blocks are 16 bytes; bpp is 16 / (W * H).
|
|
94
|
+
};
|
|
95
|
+
// ASTC block-compressed formats name their block dimensions inline, e.g.
|
|
96
|
+
// `astc-4x4-unorm` or `astc-12x12-unorm-srgb`. Every block is 16 bytes,
|
|
97
|
+
// covering W * H texels, so bpp = 16 / (W * H). Covers all 28 variants.
|
|
98
|
+
const ASTC_RE = /^astc-(\d+)x(\d+)-/;
|
|
99
|
+
function bytesPerTexel(format) {
|
|
100
|
+
const known = FORMAT_BPP[format];
|
|
101
|
+
if (known !== undefined)
|
|
102
|
+
return known;
|
|
103
|
+
const astc = ASTC_RE.exec(format);
|
|
104
|
+
if (astc) {
|
|
105
|
+
const blockW = Number(astc[1]);
|
|
106
|
+
const blockH = Number(astc[2]);
|
|
107
|
+
return 16 / (blockW * blockH);
|
|
108
|
+
}
|
|
109
|
+
return 4;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Resolve a GPUExtent3D to (w, h, d). Per WebGPU spec, `size` is either a
|
|
113
|
+
* `GPUExtent3DDict` (object with `.width`) or any iterable of numbers
|
|
114
|
+
* (Array, Uint32Array, etc). `Array.isArray` returns false for typed
|
|
115
|
+
* arrays, so we discriminate on the dict's `width` property instead.
|
|
116
|
+
*/
|
|
117
|
+
export function resolveExtent(size) {
|
|
118
|
+
if (size != null && typeof size.width === "number") {
|
|
119
|
+
const dict = size;
|
|
120
|
+
return {
|
|
121
|
+
width: dict.width,
|
|
122
|
+
height: dict.height ?? 1,
|
|
123
|
+
depth: dict.depthOrArrayLayers ?? 1,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const [width = 0, height = 1, depth = 1] = Array.from(size);
|
|
127
|
+
return { width, height, depth };
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Bytes occupied on the GPU by a texture with the given descriptor.
|
|
131
|
+
* Accounts for mip chain, array layers / depth, MSAA samples, and the
|
|
132
|
+
* format-specific bits per pixel.
|
|
133
|
+
*
|
|
134
|
+
* For 3D textures, each mip level halves all three dimensions, so the
|
|
135
|
+
* per-level pixel count series is `8^-i`. For 2D and 2D-array textures
|
|
136
|
+
* only x/y halve, giving `4^-i`.
|
|
137
|
+
*/
|
|
138
|
+
export function textureDescriptorBytes(desc) {
|
|
139
|
+
const bpp = bytesPerTexel(desc.format);
|
|
140
|
+
const { width, height, depth } = resolveExtent(desc.size);
|
|
141
|
+
const is3D = desc.dimension === "3d";
|
|
142
|
+
const mipLevels = desc.mipLevelCount ?? 1;
|
|
143
|
+
const mipBase = is3D ? 8 : 4;
|
|
144
|
+
let mipFactor = 0;
|
|
145
|
+
for (let i = 0; i < mipLevels; i++)
|
|
146
|
+
mipFactor += Math.pow(mipBase, -i);
|
|
147
|
+
const sampleCount = desc.sampleCount ?? 1;
|
|
148
|
+
return Math.ceil(width * height * depth * bpp * mipFactor * sampleCount);
|
|
149
|
+
}
|
|
150
|
+
// ── Module-level active instrumentation ─────────────────────────────────────
|
|
151
|
+
//
|
|
152
|
+
// For ease of use, `instrumentDevice` sets the returned handle as the
|
|
153
|
+
// "active" instrumentation. The React `MemoryHUD` reads from this state by
|
|
154
|
+
// default, so callers don't need to thread the handle through props or
|
|
155
|
+
// React context. Power users who want to manage multiple instrumentations
|
|
156
|
+
// can still ignore the global and pass handles explicitly.
|
|
157
|
+
let active = null;
|
|
158
|
+
const subscribers = new Set();
|
|
159
|
+
const INSTRUMENTED = Symbol.for("webgpu-profiler.instrumented");
|
|
160
|
+
function notify() {
|
|
161
|
+
for (const cb of subscribers)
|
|
162
|
+
cb();
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Returns the most recently registered active instrumentation (or `null`
|
|
166
|
+
* if none). For React, prefer using `MemoryHUD` without a prop — it reads
|
|
167
|
+
* this via `useSyncExternalStore` and re-renders when it changes.
|
|
168
|
+
*/
|
|
169
|
+
export function getActiveInstrumentation() {
|
|
170
|
+
return active;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Subscribe to changes in the active instrumentation. Returns an
|
|
174
|
+
* unsubscribe function. Used internally by `MemoryHUD` via React's
|
|
175
|
+
* `useSyncExternalStore`. Exposed for non-React consumers who want to
|
|
176
|
+
* react to active-instrumentation changes.
|
|
177
|
+
*/
|
|
178
|
+
export function subscribeActiveInstrumentation(cb) {
|
|
179
|
+
subscribers.add(cb);
|
|
180
|
+
return () => {
|
|
181
|
+
subscribers.delete(cb);
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// ── Patcher ─────────────────────────────────────────────────────────────────
|
|
185
|
+
export function instrumentDevice(device) {
|
|
186
|
+
// Idempotent: calling twice on the same device returns the existing
|
|
187
|
+
// handle rather than double-patching (and double-counting).
|
|
188
|
+
const existing = device[INSTRUMENTED];
|
|
189
|
+
if (existing) {
|
|
190
|
+
active = existing;
|
|
191
|
+
notify();
|
|
192
|
+
return existing;
|
|
193
|
+
}
|
|
194
|
+
const buffers = new Map();
|
|
195
|
+
const textures = new Map();
|
|
196
|
+
// Set to false by `uninstrument()`. Guards against a tracking write
|
|
197
|
+
// landing after teardown (rare, but possible if a `createBuffer` call
|
|
198
|
+
// is in-flight while uninstrument runs).
|
|
199
|
+
let alive = true;
|
|
200
|
+
const origCreateBuffer = device.createBuffer.bind(device);
|
|
201
|
+
const origCreateTexture = device.createTexture.bind(device);
|
|
202
|
+
device.createBuffer = (descriptor) => {
|
|
203
|
+
const buffer = origCreateBuffer(descriptor);
|
|
204
|
+
if (alive)
|
|
205
|
+
buffers.set(buffer, { buffer, descriptor });
|
|
206
|
+
const origDestroy = buffer.destroy.bind(buffer);
|
|
207
|
+
// GPUBuffer.destroy may be called multiple times; the patch handles
|
|
208
|
+
// that idempotently via the no-op Map.delete.
|
|
209
|
+
buffer.destroy = () => {
|
|
210
|
+
buffers.delete(buffer);
|
|
211
|
+
origDestroy();
|
|
212
|
+
};
|
|
213
|
+
return buffer;
|
|
214
|
+
};
|
|
215
|
+
device.createTexture = (descriptor) => {
|
|
216
|
+
const texture = origCreateTexture(descriptor);
|
|
217
|
+
if (alive)
|
|
218
|
+
textures.set(texture, { texture, descriptor });
|
|
219
|
+
const origDestroy = texture.destroy.bind(texture);
|
|
220
|
+
texture.destroy = () => {
|
|
221
|
+
textures.delete(texture);
|
|
222
|
+
origDestroy();
|
|
223
|
+
};
|
|
224
|
+
return texture;
|
|
225
|
+
};
|
|
226
|
+
const inst = {
|
|
227
|
+
device,
|
|
228
|
+
buffers,
|
|
229
|
+
textures,
|
|
230
|
+
bufferBytes() {
|
|
231
|
+
let total = 0;
|
|
232
|
+
for (const { descriptor } of buffers.values())
|
|
233
|
+
total += descriptor.size;
|
|
234
|
+
return total;
|
|
235
|
+
},
|
|
236
|
+
textureBytes() {
|
|
237
|
+
let total = 0;
|
|
238
|
+
for (const { descriptor } of textures.values()) {
|
|
239
|
+
total += textureDescriptorBytes(descriptor);
|
|
240
|
+
}
|
|
241
|
+
return total;
|
|
242
|
+
},
|
|
243
|
+
bytes() {
|
|
244
|
+
return this.bufferBytes() + this.textureBytes();
|
|
245
|
+
},
|
|
246
|
+
uninstrument() {
|
|
247
|
+
alive = false;
|
|
248
|
+
device.createBuffer = origCreateBuffer;
|
|
249
|
+
device.createTexture = origCreateTexture;
|
|
250
|
+
buffers.clear();
|
|
251
|
+
textures.clear();
|
|
252
|
+
delete device[INSTRUMENTED];
|
|
253
|
+
if (active === inst) {
|
|
254
|
+
active = null;
|
|
255
|
+
notify();
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
device[INSTRUMENTED] =
|
|
260
|
+
inst;
|
|
261
|
+
active = inst;
|
|
262
|
+
notify();
|
|
263
|
+
return inst;
|
|
264
|
+
}
|
|
265
|
+
// ── Auto-instrument ─────────────────────────────────────────────────────────
|
|
266
|
+
//
|
|
267
|
+
// Patches `GPUAdapter.prototype.requestDevice` so every device any code
|
|
268
|
+
// requests is automatically instrumented as soon as it's created. Call
|
|
269
|
+
// this once at boot — before any renderer or framework's `init()` runs —
|
|
270
|
+
// and you're done. The React `MemoryHUD` picks up the active
|
|
271
|
+
// instrumentation through `useSyncExternalStore`.
|
|
272
|
+
//
|
|
273
|
+
// Idempotent and safe to call multiple times. No-op if WebGPU is not
|
|
274
|
+
// available in the current environment (e.g. SSR, older browsers).
|
|
275
|
+
const ADAPTER_PATCHED = Symbol.for("webgpu-profiler.adapter-patched");
|
|
276
|
+
/**
|
|
277
|
+
* Patches `GPUAdapter.prototype.requestDevice` to auto-instrument every
|
|
278
|
+
* `GPUDevice` it returns. Returns a disposer that restores the original
|
|
279
|
+
* method (existing instrumentations stay live until their own
|
|
280
|
+
* `uninstrument()` is called).
|
|
281
|
+
*
|
|
282
|
+
* Typical usage at the top of your app boot file:
|
|
283
|
+
*
|
|
284
|
+
* import { autoInstrument } from "webgpu-profiler";
|
|
285
|
+
* if (import.meta.env.DEV) autoInstrument();
|
|
286
|
+
*
|
|
287
|
+
* No further wiring needed — render `<MemoryHUD />` anywhere.
|
|
288
|
+
*/
|
|
289
|
+
export function autoInstrument(options = {}) {
|
|
290
|
+
if (typeof GPUAdapter === "undefined") {
|
|
291
|
+
// WebGPU not available in this environment; nothing to patch.
|
|
292
|
+
return () => undefined;
|
|
293
|
+
}
|
|
294
|
+
const proto = GPUAdapter.prototype;
|
|
295
|
+
if (proto[ADAPTER_PATCHED]) {
|
|
296
|
+
return () => undefined;
|
|
297
|
+
}
|
|
298
|
+
const orig = proto.requestDevice;
|
|
299
|
+
proto.requestDevice = async function (descriptor) {
|
|
300
|
+
const device = await orig.call(this, descriptor);
|
|
301
|
+
if (device) {
|
|
302
|
+
const inst = instrumentDevice(device);
|
|
303
|
+
options.onInstrument?.(inst);
|
|
304
|
+
}
|
|
305
|
+
return device;
|
|
306
|
+
};
|
|
307
|
+
proto[ADAPTER_PATCHED] = true;
|
|
308
|
+
return () => {
|
|
309
|
+
if (proto[ADAPTER_PATCHED]) {
|
|
310
|
+
proto.requestDevice = orig;
|
|
311
|
+
delete proto[ADAPTER_PATCHED];
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=instrument.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrument.js","sourceRoot":"","sources":["../src/instrument.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type DeviceInstrumentation } from "./instrument.js";
|
|
2
|
+
/**
|
|
3
|
+
* Builds a snapshot of GPU memory currently allocated by the page, sourced
|
|
4
|
+
* from a {@link DeviceInstrumentation} handle (see `instrument.ts`).
|
|
5
|
+
*
|
|
6
|
+
* The numbers below are exact for every resource that flows through
|
|
7
|
+
* `GPUDevice.createBuffer` / `createTexture` — those are the only two
|
|
8
|
+
* WebGPU JS-side allocation entry points, so this is exhaustive by
|
|
9
|
+
* construction.
|
|
10
|
+
*
|
|
11
|
+
* Not counted (driver-internal, opaque to JS):
|
|
12
|
+
* - Canvas swapchain backbuffer (created by `context.configure()`).
|
|
13
|
+
* - Residency caches, MSAA backing, pipeline objects, bind group
|
|
14
|
+
* layouts, staging buffers that bypass `createBuffer`.
|
|
15
|
+
*/
|
|
16
|
+
export interface TextureEntry {
|
|
17
|
+
name: string;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
depth: number;
|
|
21
|
+
format: string;
|
|
22
|
+
mipLevels: number;
|
|
23
|
+
sampleCount: number;
|
|
24
|
+
/** True if this texture is a render attachment (post-fx, MRT, depth, etc.). */
|
|
25
|
+
isRenderTarget: boolean;
|
|
26
|
+
bytes: number;
|
|
27
|
+
}
|
|
28
|
+
export interface BufferEntry {
|
|
29
|
+
name: string;
|
|
30
|
+
bytes: number;
|
|
31
|
+
/** Pipe-separated decoded usage flags (e.g. "STORAGE|COPY_DST"). */
|
|
32
|
+
usage: string;
|
|
33
|
+
}
|
|
34
|
+
export interface MemoryReport {
|
|
35
|
+
sampledTextures: TextureEntry[];
|
|
36
|
+
renderTargets: TextureEntry[];
|
|
37
|
+
buffers: BufferEntry[];
|
|
38
|
+
totals: {
|
|
39
|
+
sampledTextures: number;
|
|
40
|
+
renderTargets: number;
|
|
41
|
+
buffers: number;
|
|
42
|
+
all: number;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export declare function profileMemory(instrumentation: DeviceInstrumentation): MemoryReport;
|
|
46
|
+
/** Build a multi-line plain-text report. Feed to a copy button or bug report. */
|
|
47
|
+
export declare function reportToText(report: MemoryReport): string;
|
|
48
|
+
//# sourceMappingURL=profiler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiler.d.ts","sourceRoot":"","sources":["../src/profiler.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,YAAY,EAAE,CAAC;IAChC,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,MAAM,EAAE;QACN,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAsCD,wBAAgB,aAAa,CAC3B,eAAe,EAAE,qBAAqB,GACrC,YAAY,CAkDd;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CA0BzD"}
|