@luma.gl/webgpu 9.2.6 → 9.3.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/helpers/cpu-hotspot-profiler.d.ts +54 -0
- package/dist/adapter/helpers/cpu-hotspot-profiler.d.ts.map +1 -0
- package/dist/adapter/helpers/cpu-hotspot-profiler.js +26 -0
- package/dist/adapter/helpers/cpu-hotspot-profiler.js.map +1 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts +7 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts.map +1 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.js +490 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.js.map +1 -0
- package/dist/adapter/helpers/get-bind-group.d.ts +4 -6
- package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
- package/dist/adapter/helpers/get-bind-group.js +39 -32
- package/dist/adapter/helpers/get-bind-group.js.map +1 -1
- package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts +3 -1
- package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts.map +1 -1
- package/dist/adapter/helpers/get-vertex-buffer-layout.js +17 -12
- package/dist/adapter/helpers/get-vertex-buffer-layout.js.map +1 -1
- package/dist/adapter/helpers/webgpu-parameters.d.ts.map +1 -1
- package/dist/adapter/helpers/webgpu-parameters.js +1 -0
- package/dist/adapter/helpers/webgpu-parameters.js.map +1 -1
- package/dist/adapter/resources/webgpu-buffer.d.ts +7 -0
- package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-buffer.js +58 -15
- package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
- package/dist/adapter/resources/webgpu-command-buffer.js +1 -1
- package/dist/adapter/resources/webgpu-command-buffer.js.map +1 -1
- package/dist/adapter/resources/webgpu-command-encoder.d.ts +7 -16
- package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-command-encoder.js +89 -32
- package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pass.d.ts +3 -3
- package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pass.js +30 -12
- package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pipeline.d.ts +7 -9
- package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pipeline.js +30 -17
- package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
- package/dist/adapter/resources/webgpu-fence.d.ts +13 -0
- package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -0
- package/dist/adapter/resources/webgpu-fence.js +33 -0
- package/dist/adapter/resources/webgpu-fence.js.map +1 -0
- package/dist/adapter/resources/webgpu-framebuffer.d.ts +6 -0
- package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-framebuffer.js +16 -0
- package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.d.ts +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.js +11 -18
- package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
- package/dist/adapter/resources/webgpu-query-set.d.ts +33 -4
- package/dist/adapter/resources/webgpu-query-set.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-query-set.js +145 -4
- package/dist/adapter/resources/webgpu-query-set.js.map +1 -1
- package/dist/adapter/resources/webgpu-render-pass.d.ts +6 -3
- package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-render-pass.js +78 -34
- package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
- package/dist/adapter/resources/webgpu-render-pipeline.d.ts +14 -10
- package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-render-pipeline.js +56 -35
- package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
- package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-sampler.js +4 -0
- package/dist/adapter/resources/webgpu-sampler.js.map +1 -1
- package/dist/adapter/resources/webgpu-shader.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-shader.js +17 -1
- package/dist/adapter/resources/webgpu-shader.js.map +1 -1
- package/dist/adapter/resources/webgpu-texture-view.d.ts +6 -0
- package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-texture-view.js +47 -11
- package/dist/adapter/resources/webgpu-texture-view.js.map +1 -1
- package/dist/adapter/resources/webgpu-texture.d.ts +25 -3
- package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-texture.js +211 -43
- package/dist/adapter/resources/webgpu-texture.js.map +1 -1
- package/dist/adapter/resources/webgpu-vertex-array.js +1 -1
- package/dist/adapter/resources/webgpu-vertex-array.js.map +1 -1
- package/dist/adapter/webgpu-adapter.d.ts.map +1 -1
- package/dist/adapter/webgpu-adapter.js +34 -34
- package/dist/adapter/webgpu-adapter.js.map +1 -1
- package/dist/adapter/webgpu-canvas-context.d.ts +6 -3
- package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
- package/dist/adapter/webgpu-canvas-context.js +90 -30
- package/dist/adapter/webgpu-canvas-context.js.map +1 -1
- package/dist/adapter/webgpu-device.d.ts +12 -2
- package/dist/adapter/webgpu-device.d.ts.map +1 -1
- package/dist/adapter/webgpu-device.js +173 -16
- package/dist/adapter/webgpu-device.js.map +1 -1
- package/dist/adapter/webgpu-presentation-context.d.ts +25 -0
- package/dist/adapter/webgpu-presentation-context.d.ts.map +1 -0
- package/dist/adapter/webgpu-presentation-context.js +144 -0
- package/dist/adapter/webgpu-presentation-context.js.map +1 -0
- package/dist/dist.dev.js +8070 -547
- package/dist/dist.min.js +169 -6
- package/dist/index.cjs +1929 -410
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/wgsl/get-shader-layout-wgsl.d.ts +8 -0
- package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -0
- package/dist/wgsl/get-shader-layout-wgsl.js +144 -0
- package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -0
- package/package.json +6 -5
- package/src/adapter/helpers/cpu-hotspot-profiler.ts +70 -0
- package/src/adapter/helpers/generate-mipmaps-webgpu.ts +583 -0
- package/src/adapter/helpers/get-bind-group.ts +52 -46
- package/src/adapter/helpers/get-vertex-buffer-layout.ts +31 -12
- package/src/adapter/helpers/webgpu-parameters.ts +2 -0
- package/src/adapter/resources/webgpu-buffer.ts +61 -15
- package/src/adapter/resources/webgpu-command-buffer.ts +1 -1
- package/src/adapter/resources/webgpu-command-encoder.ts +129 -50
- package/src/adapter/resources/webgpu-compute-pass.ts +48 -13
- package/src/adapter/resources/webgpu-compute-pipeline.ts +49 -18
- package/src/adapter/resources/webgpu-fence.ts +38 -0
- package/src/adapter/resources/webgpu-framebuffer.ts +21 -0
- package/src/adapter/resources/webgpu-pipeline-layout.ts +18 -17
- package/src/adapter/resources/webgpu-query-set.ts +185 -9
- package/src/adapter/resources/webgpu-render-pass.ts +92 -40
- package/src/adapter/resources/webgpu-render-pipeline.ts +83 -44
- package/src/adapter/resources/webgpu-sampler.ts +5 -0
- package/src/adapter/resources/webgpu-shader.ts +16 -1
- package/src/adapter/resources/webgpu-texture-view.ts +51 -11
- package/src/adapter/resources/webgpu-texture.ts +281 -101
- package/src/adapter/resources/webgpu-vertex-array.ts +1 -1
- package/src/adapter/webgpu-adapter.ts +40 -42
- package/src/adapter/webgpu-canvas-context.ts +107 -40
- package/src/adapter/webgpu-device.ts +231 -21
- package/src/adapter/webgpu-presentation-context.ts +180 -0
- package/src/index.ts +3 -0
- package/src/wgsl/get-shader-layout-wgsl.ts +165 -0
- package/dist/adapter/helpers/accessor-to-format.d.ts +0 -1
- package/dist/adapter/helpers/accessor-to-format.d.ts.map +0 -1
- package/dist/adapter/helpers/accessor-to-format.js +0 -105
- package/dist/adapter/helpers/accessor-to-format.js.map +0 -1
- package/src/adapter/helpers/accessor-to-format.ts +0 -104
|
@@ -10,6 +10,7 @@ import {CanvasContext, Texture, log} from '@luma.gl/core';
|
|
|
10
10
|
import {WebGPUDevice} from './webgpu-device';
|
|
11
11
|
import {WebGPUFramebuffer} from './resources/webgpu-framebuffer';
|
|
12
12
|
import {WebGPUTexture} from './resources/webgpu-texture';
|
|
13
|
+
import {getCpuHotspotProfiler, getTimestamp} from './helpers/cpu-hotspot-profiler';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Holds a WebGPU Canvas Context
|
|
@@ -20,7 +21,9 @@ export class WebGPUCanvasContext extends CanvasContext {
|
|
|
20
21
|
readonly device: WebGPUDevice;
|
|
21
22
|
readonly handle: GPUCanvasContext;
|
|
22
23
|
|
|
24
|
+
private colorAttachment: WebGPUTexture | null = null;
|
|
23
25
|
private depthStencilAttachment: WebGPUTexture | null = null;
|
|
26
|
+
private framebuffer: WebGPUFramebuffer | null = null;
|
|
24
27
|
|
|
25
28
|
get [Symbol.toStringTag](): string {
|
|
26
29
|
return 'WebGPUCanvasContext';
|
|
@@ -38,58 +41,38 @@ export class WebGPUCanvasContext extends CanvasContext {
|
|
|
38
41
|
|
|
39
42
|
// Base class constructor cannot access derived methods/fields, so we need to call these functions in the subclass constructor
|
|
40
43
|
this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
|
|
41
|
-
this.
|
|
44
|
+
this._configureDevice();
|
|
45
|
+
this._startObservers();
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
/** Destroy any textures produced while configured and remove the context configuration. */
|
|
45
49
|
override destroy(): void {
|
|
46
|
-
this.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
/** Update framebuffer with properly resized "swap chain" texture views */
|
|
51
|
-
getCurrentFramebuffer(
|
|
52
|
-
options: {depthStencilFormat?: TextureFormatDepthStencil | false} = {
|
|
53
|
-
depthStencilFormat: 'depth24plus'
|
|
50
|
+
if (this.framebuffer) {
|
|
51
|
+
this.framebuffer.destroy();
|
|
52
|
+
this.framebuffer = null;
|
|
54
53
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// TODO - temporary debug code
|
|
59
|
-
if (
|
|
60
|
-
currentColorAttachment.width !== this.drawingBufferWidth ||
|
|
61
|
-
currentColorAttachment.height !== this.drawingBufferHeight
|
|
62
|
-
) {
|
|
63
|
-
const [oldWidth, oldHeight] = this.getDrawingBufferSize();
|
|
64
|
-
this.drawingBufferWidth = currentColorAttachment.width;
|
|
65
|
-
this.drawingBufferHeight = currentColorAttachment.height;
|
|
66
|
-
log.log(
|
|
67
|
-
1,
|
|
68
|
-
`${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`
|
|
69
|
-
)();
|
|
54
|
+
if (this.colorAttachment) {
|
|
55
|
+
this.colorAttachment.destroy();
|
|
56
|
+
this.colorAttachment = null;
|
|
70
57
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
this._createDepthStencilAttachment(options?.depthStencilFormat);
|
|
58
|
+
if (this.depthStencilAttachment) {
|
|
59
|
+
this.depthStencilAttachment.destroy();
|
|
60
|
+
this.depthStencilAttachment = null;
|
|
75
61
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
colorAttachments: [currentColorAttachment],
|
|
79
|
-
depthStencilAttachment: this.depthStencilAttachment
|
|
80
|
-
});
|
|
62
|
+
this.handle.unconfigure();
|
|
63
|
+
super.destroy();
|
|
81
64
|
}
|
|
82
65
|
|
|
83
66
|
// IMPLEMENTATION OF ABSTRACT METHODS
|
|
84
67
|
|
|
85
|
-
|
|
68
|
+
/** @see https://www.w3.org/TR/webgpu/#canvas-configuration */
|
|
69
|
+
_configureDevice(): void {
|
|
86
70
|
if (this.depthStencilAttachment) {
|
|
87
71
|
this.depthStencilAttachment.destroy();
|
|
88
72
|
this.depthStencilAttachment = null;
|
|
89
73
|
}
|
|
90
74
|
|
|
91
75
|
// Reconfigure the canvas size.
|
|
92
|
-
// https://www.w3.org/TR/webgpu/#canvas-configuration
|
|
93
76
|
this.handle.configure({
|
|
94
77
|
device: this.device.handle,
|
|
95
78
|
format: this.device.preferredColorFormat,
|
|
@@ -98,23 +81,107 @@ export class WebGPUCanvasContext extends CanvasContext {
|
|
|
98
81
|
colorSpace: this.props.colorSpace,
|
|
99
82
|
alphaMode: this.props.alphaMode
|
|
100
83
|
});
|
|
84
|
+
|
|
85
|
+
this._createDepthStencilAttachment(this.device.preferredDepthFormat);
|
|
101
86
|
}
|
|
102
87
|
|
|
88
|
+
/** Update framebuffer with properly resized "swap chain" texture views */
|
|
89
|
+
_getCurrentFramebuffer(
|
|
90
|
+
options: {depthStencilFormat?: TextureFormatDepthStencil | false} = {
|
|
91
|
+
depthStencilFormat: 'depth24plus'
|
|
92
|
+
}
|
|
93
|
+
): WebGPUFramebuffer {
|
|
94
|
+
const profiler = getCpuHotspotProfiler(this.device);
|
|
95
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
96
|
+
if (profiler) {
|
|
97
|
+
profiler.framebufferAcquireCount = (profiler.framebufferAcquireCount || 0) + 1;
|
|
98
|
+
profiler.activeDefaultFramebufferAcquireDepth =
|
|
99
|
+
(profiler.activeDefaultFramebufferAcquireDepth || 0) + 1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
// Wrap the current canvas context texture in a luma.gl texture
|
|
104
|
+
const currentColorAttachment = this._getCurrentTexture();
|
|
105
|
+
// TODO - temporary debug code
|
|
106
|
+
if (
|
|
107
|
+
currentColorAttachment.width !== this.drawingBufferWidth ||
|
|
108
|
+
currentColorAttachment.height !== this.drawingBufferHeight
|
|
109
|
+
) {
|
|
110
|
+
const [oldWidth, oldHeight] = this.getDrawingBufferSize();
|
|
111
|
+
this.drawingBufferWidth = currentColorAttachment.width;
|
|
112
|
+
this.drawingBufferHeight = currentColorAttachment.height;
|
|
113
|
+
log.log(
|
|
114
|
+
1,
|
|
115
|
+
`${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`
|
|
116
|
+
)();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Resize the depth stencil attachment
|
|
120
|
+
if (options?.depthStencilFormat) {
|
|
121
|
+
this._createDepthStencilAttachment(options?.depthStencilFormat);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this.framebuffer ||= new WebGPUFramebuffer(this.device, {
|
|
125
|
+
id: `${this.id}#framebuffer`,
|
|
126
|
+
colorAttachments: [currentColorAttachment],
|
|
127
|
+
depthStencilAttachment: null
|
|
128
|
+
});
|
|
129
|
+
this.framebuffer._reinitialize(
|
|
130
|
+
currentColorAttachment.view,
|
|
131
|
+
options?.depthStencilFormat ? this.depthStencilAttachment?.view || null : null
|
|
132
|
+
);
|
|
133
|
+
return this.framebuffer;
|
|
134
|
+
} finally {
|
|
135
|
+
if (profiler) {
|
|
136
|
+
profiler.activeDefaultFramebufferAcquireDepth =
|
|
137
|
+
(profiler.activeDefaultFramebufferAcquireDepth || 1) - 1;
|
|
138
|
+
profiler.framebufferAcquireTimeMs =
|
|
139
|
+
(profiler.framebufferAcquireTimeMs || 0) + (getTimestamp() - startTime);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// PRIMARY METHODS
|
|
145
|
+
|
|
103
146
|
/** Wrap the current canvas context texture in a luma.gl texture */
|
|
104
|
-
|
|
147
|
+
_getCurrentTexture(): WebGPUTexture {
|
|
148
|
+
const profiler = getCpuHotspotProfiler(this.device);
|
|
149
|
+
const currentTextureStartTime = profiler ? getTimestamp() : 0;
|
|
105
150
|
const handle = this.handle.getCurrentTexture();
|
|
106
|
-
|
|
107
|
-
|
|
151
|
+
if (profiler) {
|
|
152
|
+
profiler.currentTextureAcquireCount = (profiler.currentTextureAcquireCount || 0) + 1;
|
|
153
|
+
profiler.currentTextureAcquireTimeMs =
|
|
154
|
+
(profiler.currentTextureAcquireTimeMs || 0) + (getTimestamp() - currentTextureStartTime);
|
|
155
|
+
}
|
|
156
|
+
if (!this.colorAttachment) {
|
|
157
|
+
this.colorAttachment = this.device.createTexture({
|
|
158
|
+
id: `${this.id}#color-texture`,
|
|
159
|
+
handle,
|
|
160
|
+
format: this.device.preferredColorFormat,
|
|
161
|
+
width: handle.width,
|
|
162
|
+
height: handle.height
|
|
163
|
+
});
|
|
164
|
+
return this.colorAttachment;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
this.colorAttachment._reinitialize(handle, {
|
|
108
168
|
handle,
|
|
109
169
|
format: this.device.preferredColorFormat,
|
|
110
170
|
width: handle.width,
|
|
111
171
|
height: handle.height
|
|
112
172
|
});
|
|
173
|
+
return this.colorAttachment;
|
|
113
174
|
}
|
|
114
175
|
|
|
115
176
|
/** We build render targets on demand (i.e. not when size changes but when about to render) */
|
|
116
177
|
_createDepthStencilAttachment(depthStencilFormat: TextureFormatDepthStencil): WebGPUTexture {
|
|
117
|
-
|
|
178
|
+
const needsNewDepthStencilAttachment =
|
|
179
|
+
!this.depthStencilAttachment ||
|
|
180
|
+
this.depthStencilAttachment.width !== this.drawingBufferWidth ||
|
|
181
|
+
this.depthStencilAttachment.height !== this.drawingBufferHeight ||
|
|
182
|
+
this.depthStencilAttachment.format !== depthStencilFormat;
|
|
183
|
+
if (needsNewDepthStencilAttachment) {
|
|
184
|
+
this.depthStencilAttachment?.destroy();
|
|
118
185
|
this.depthStencilAttachment = this.device.createTexture({
|
|
119
186
|
id: `${this.id}#depth-stencil-texture`,
|
|
120
187
|
usage: Texture.RENDER_ATTACHMENT,
|
|
@@ -123,6 +190,6 @@ export class WebGPUCanvasContext extends CanvasContext {
|
|
|
123
190
|
height: this.drawingBufferHeight
|
|
124
191
|
});
|
|
125
192
|
}
|
|
126
|
-
return this.depthStencilAttachment
|
|
193
|
+
return this.depthStencilAttachment!;
|
|
127
194
|
}
|
|
128
195
|
}
|
|
@@ -6,16 +6,22 @@
|
|
|
6
6
|
// / <reference types="@webgpu/types" />
|
|
7
7
|
|
|
8
8
|
import type {
|
|
9
|
+
Bindings,
|
|
10
|
+
ComputePipeline,
|
|
11
|
+
ComputeShaderLayout,
|
|
9
12
|
DeviceInfo,
|
|
10
13
|
DeviceLimits,
|
|
11
14
|
DeviceFeature,
|
|
12
15
|
DeviceTextureFormatCapabilities,
|
|
13
16
|
VertexFormat,
|
|
14
17
|
CanvasContextProps,
|
|
18
|
+
PresentationContextProps,
|
|
19
|
+
PresentationContext,
|
|
15
20
|
BufferProps,
|
|
16
21
|
SamplerProps,
|
|
17
22
|
ShaderProps,
|
|
18
23
|
TextureProps,
|
|
24
|
+
Texture,
|
|
19
25
|
ExternalTextureProps,
|
|
20
26
|
FramebufferProps,
|
|
21
27
|
RenderPipelineProps,
|
|
@@ -28,6 +34,8 @@ import type {
|
|
|
28
34
|
DeviceProps,
|
|
29
35
|
CommandEncoderProps,
|
|
30
36
|
PipelineLayoutProps,
|
|
37
|
+
RenderPipeline,
|
|
38
|
+
ShaderLayout
|
|
31
39
|
} from '@luma.gl/core';
|
|
32
40
|
import {Device, DeviceFeatures} from '@luma.gl/core';
|
|
33
41
|
import {WebGPUBuffer} from './resources/webgpu-buffer';
|
|
@@ -41,10 +49,21 @@ import {WebGPUComputePipeline} from './resources/webgpu-compute-pipeline';
|
|
|
41
49
|
import {WebGPUVertexArray} from './resources/webgpu-vertex-array';
|
|
42
50
|
|
|
43
51
|
import {WebGPUCanvasContext} from './webgpu-canvas-context';
|
|
52
|
+
import {WebGPUPresentationContext} from './webgpu-presentation-context';
|
|
44
53
|
import {WebGPUCommandEncoder} from './resources/webgpu-command-encoder';
|
|
45
54
|
import {WebGPUCommandBuffer} from './resources/webgpu-command-buffer';
|
|
46
55
|
import {WebGPUQuerySet} from './resources/webgpu-query-set';
|
|
47
56
|
import {WebGPUPipelineLayout} from './resources/webgpu-pipeline-layout';
|
|
57
|
+
import {WebGPUFence} from './resources/webgpu-fence';
|
|
58
|
+
|
|
59
|
+
import {getShaderLayoutFromWGSL} from '../wgsl/get-shader-layout-wgsl';
|
|
60
|
+
import {generateMipmapsWebGPU} from './helpers/generate-mipmaps-webgpu';
|
|
61
|
+
import {getBindGroup} from './helpers/get-bind-group';
|
|
62
|
+
import {
|
|
63
|
+
getCpuHotspotProfiler as getWebGPUCpuHotspotProfiler,
|
|
64
|
+
getCpuHotspotSubmitReason as getWebGPUCpuHotspotSubmitReason,
|
|
65
|
+
getTimestamp
|
|
66
|
+
} from './helpers/cpu-hotspot-profiler';
|
|
48
67
|
|
|
49
68
|
/** WebGPU Device implementation */
|
|
50
69
|
export class WebGPUDevice extends Device {
|
|
@@ -72,6 +91,7 @@ export class WebGPUDevice extends Device {
|
|
|
72
91
|
override canvasContext: WebGPUCanvasContext | null = null;
|
|
73
92
|
|
|
74
93
|
private _isLost: boolean = false;
|
|
94
|
+
private _defaultSampler: WebGPUSampler | null = null;
|
|
75
95
|
commandEncoder: WebGPUCommandEncoder;
|
|
76
96
|
|
|
77
97
|
override get [Symbol.toStringTag](): string {
|
|
@@ -129,6 +149,9 @@ export class WebGPUDevice extends Device {
|
|
|
129
149
|
// this.glslang = glsl && await loadGlslangModule();
|
|
130
150
|
|
|
131
151
|
destroy(): void {
|
|
152
|
+
this.commandEncoder?.destroy();
|
|
153
|
+
this._defaultSampler?.destroy();
|
|
154
|
+
this._defaultSampler = null;
|
|
132
155
|
this.handle.destroy();
|
|
133
156
|
}
|
|
134
157
|
|
|
@@ -136,15 +159,15 @@ export class WebGPUDevice extends Device {
|
|
|
136
159
|
return this._isLost;
|
|
137
160
|
}
|
|
138
161
|
|
|
162
|
+
getShaderLayout(source: string) {
|
|
163
|
+
return getShaderLayoutFromWGSL(source);
|
|
164
|
+
}
|
|
165
|
+
|
|
139
166
|
override isVertexFormatSupported(format: VertexFormat): boolean {
|
|
140
167
|
const info = this.getVertexFormatInfo(format);
|
|
141
168
|
return !info.webglOnly;
|
|
142
169
|
}
|
|
143
170
|
|
|
144
|
-
getTextureByteAlignment(): number {
|
|
145
|
-
return 1;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
171
|
createBuffer(props: BufferProps | ArrayBuffer | ArrayBufferView): WebGPUBuffer {
|
|
149
172
|
const newProps = this._normalizeBufferProps(props);
|
|
150
173
|
return new WebGPUBuffer(this, newProps);
|
|
@@ -166,6 +189,13 @@ export class WebGPUDevice extends Device {
|
|
|
166
189
|
return new WebGPUSampler(this, props);
|
|
167
190
|
}
|
|
168
191
|
|
|
192
|
+
getDefaultSampler(): WebGPUSampler {
|
|
193
|
+
this._defaultSampler ||= new WebGPUSampler(this, {
|
|
194
|
+
id: `${this.id}-default-sampler`
|
|
195
|
+
});
|
|
196
|
+
return this._defaultSampler;
|
|
197
|
+
}
|
|
198
|
+
|
|
169
199
|
createRenderPipeline(props: RenderPipelineProps): WebGPURenderPipeline {
|
|
170
200
|
return new WebGPURenderPipeline(this, props);
|
|
171
201
|
}
|
|
@@ -196,41 +226,180 @@ export class WebGPUDevice extends Device {
|
|
|
196
226
|
return new WebGPUQuerySet(this, props);
|
|
197
227
|
}
|
|
198
228
|
|
|
229
|
+
override createFence(): WebGPUFence {
|
|
230
|
+
return new WebGPUFence(this);
|
|
231
|
+
}
|
|
232
|
+
|
|
199
233
|
createCanvasContext(props: CanvasContextProps): WebGPUCanvasContext {
|
|
200
234
|
return new WebGPUCanvasContext(this, this.adapter, props);
|
|
201
235
|
}
|
|
202
236
|
|
|
237
|
+
createPresentationContext(props?: PresentationContextProps): PresentationContext {
|
|
238
|
+
return new WebGPUPresentationContext(this, props);
|
|
239
|
+
}
|
|
240
|
+
|
|
203
241
|
createPipelineLayout(props: PipelineLayoutProps): WebGPUPipelineLayout {
|
|
204
242
|
return new WebGPUPipelineLayout(this, props);
|
|
205
243
|
}
|
|
206
244
|
|
|
245
|
+
override generateMipmapsWebGPU(texture: Texture): void {
|
|
246
|
+
generateMipmapsWebGPU(this, texture);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
override _createBindGroupLayoutWebGPU(
|
|
250
|
+
pipeline: RenderPipeline | ComputePipeline,
|
|
251
|
+
group: number
|
|
252
|
+
): GPUBindGroupLayout {
|
|
253
|
+
return (pipeline as WebGPURenderPipeline | WebGPUComputePipeline).handle.getBindGroupLayout(
|
|
254
|
+
group
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
override _createBindGroupWebGPU(
|
|
259
|
+
bindGroupLayout: unknown,
|
|
260
|
+
shaderLayout: ShaderLayout | ComputeShaderLayout,
|
|
261
|
+
bindings: Bindings,
|
|
262
|
+
group: number
|
|
263
|
+
): GPUBindGroup | null {
|
|
264
|
+
if (Object.keys(bindings).length === 0) {
|
|
265
|
+
return this.handle.createBindGroup({
|
|
266
|
+
layout: bindGroupLayout as GPUBindGroupLayout,
|
|
267
|
+
entries: []
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return getBindGroup(this, bindGroupLayout as GPUBindGroupLayout, shaderLayout, bindings, group);
|
|
272
|
+
}
|
|
273
|
+
|
|
207
274
|
submit(commandBuffer?: WebGPUCommandBuffer): void {
|
|
275
|
+
let submittedCommandEncoder: WebGPUCommandEncoder | null = null;
|
|
208
276
|
if (!commandBuffer) {
|
|
209
|
-
commandBuffer = this.
|
|
210
|
-
this.commandEncoder.destroy();
|
|
211
|
-
this.commandEncoder = this.createCommandEncoder({id: `${this.id}-default-encoder`});
|
|
277
|
+
({submittedCommandEncoder, commandBuffer} = this._finalizeDefaultCommandEncoderForSubmit());
|
|
212
278
|
}
|
|
213
279
|
|
|
214
|
-
this
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
this.
|
|
280
|
+
const profiler = getWebGPUCpuHotspotProfiler(this);
|
|
281
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
282
|
+
const submitReason = getWebGPUCpuHotspotSubmitReason(this);
|
|
283
|
+
try {
|
|
284
|
+
this.pushErrorScope('validation');
|
|
285
|
+
const queueSubmitStartTime = profiler ? getTimestamp() : 0;
|
|
286
|
+
this.handle.queue.submit([commandBuffer.handle]);
|
|
287
|
+
if (profiler) {
|
|
288
|
+
profiler.queueSubmitCount = (profiler.queueSubmitCount || 0) + 1;
|
|
289
|
+
profiler.queueSubmitTimeMs =
|
|
290
|
+
(profiler.queueSubmitTimeMs || 0) + (getTimestamp() - queueSubmitStartTime);
|
|
291
|
+
}
|
|
292
|
+
this.popErrorScope((error: GPUError) => {
|
|
293
|
+
this.reportError(new Error(`${this} command submission: ${error.message}`), this)();
|
|
294
|
+
this.debug();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
if (submittedCommandEncoder) {
|
|
298
|
+
const submitResolveKickoffStartTime = profiler ? getTimestamp() : 0;
|
|
299
|
+
scheduleMicrotask(() => {
|
|
300
|
+
submittedCommandEncoder
|
|
301
|
+
.resolveTimeProfilingQuerySet()
|
|
302
|
+
.then(() => {
|
|
303
|
+
this.commandEncoder._gpuTimeMs = submittedCommandEncoder._gpuTimeMs;
|
|
304
|
+
})
|
|
305
|
+
.catch(() => {});
|
|
306
|
+
});
|
|
307
|
+
if (profiler) {
|
|
308
|
+
profiler.submitResolveKickoffCount = (profiler.submitResolveKickoffCount || 0) + 1;
|
|
309
|
+
profiler.submitResolveKickoffTimeMs =
|
|
310
|
+
(profiler.submitResolveKickoffTimeMs || 0) +
|
|
311
|
+
(getTimestamp() - submitResolveKickoffStartTime);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
} finally {
|
|
315
|
+
if (profiler) {
|
|
316
|
+
profiler.submitCount = (profiler.submitCount || 0) + 1;
|
|
317
|
+
profiler.submitTimeMs = (profiler.submitTimeMs || 0) + (getTimestamp() - startTime);
|
|
318
|
+
const reasonCountKey =
|
|
319
|
+
submitReason === 'query-readback' ? 'queryReadbackSubmitCount' : 'defaultSubmitCount';
|
|
320
|
+
const reasonTimeKey =
|
|
321
|
+
submitReason === 'query-readback' ? 'queryReadbackSubmitTimeMs' : 'defaultSubmitTimeMs';
|
|
322
|
+
profiler[reasonCountKey] = (profiler[reasonCountKey] || 0) + 1;
|
|
323
|
+
profiler[reasonTimeKey] = (profiler[reasonTimeKey] || 0) + (getTimestamp() - startTime);
|
|
324
|
+
}
|
|
325
|
+
const commandBufferDestroyStartTime = profiler ? getTimestamp() : 0;
|
|
326
|
+
commandBuffer.destroy();
|
|
327
|
+
if (profiler) {
|
|
328
|
+
profiler.commandBufferDestroyCount = (profiler.commandBufferDestroyCount || 0) + 1;
|
|
329
|
+
profiler.commandBufferDestroyTimeMs =
|
|
330
|
+
(profiler.commandBufferDestroyTimeMs || 0) +
|
|
331
|
+
(getTimestamp() - commandBufferDestroyStartTime);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
private _finalizeDefaultCommandEncoderForSubmit(): {
|
|
337
|
+
submittedCommandEncoder: WebGPUCommandEncoder;
|
|
338
|
+
commandBuffer: WebGPUCommandBuffer;
|
|
339
|
+
} {
|
|
340
|
+
const submittedCommandEncoder = this.commandEncoder;
|
|
341
|
+
if (
|
|
342
|
+
submittedCommandEncoder.getTimeProfilingSlotCount() > 0 &&
|
|
343
|
+
submittedCommandEncoder.getTimeProfilingQuerySet() instanceof WebGPUQuerySet
|
|
344
|
+
) {
|
|
345
|
+
const querySet = submittedCommandEncoder.getTimeProfilingQuerySet() as WebGPUQuerySet;
|
|
346
|
+
querySet._encodeResolveToReadBuffer(submittedCommandEncoder, {
|
|
347
|
+
firstQuery: 0,
|
|
348
|
+
queryCount: submittedCommandEncoder.getTimeProfilingSlotCount()
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const commandBuffer = submittedCommandEncoder.finish();
|
|
353
|
+
this.commandEncoder.destroy();
|
|
354
|
+
this.commandEncoder = this.createCommandEncoder({
|
|
355
|
+
id: submittedCommandEncoder.props.id,
|
|
356
|
+
timeProfilingQuerySet: submittedCommandEncoder.getTimeProfilingQuerySet()
|
|
219
357
|
});
|
|
358
|
+
|
|
359
|
+
return {submittedCommandEncoder, commandBuffer};
|
|
220
360
|
}
|
|
221
361
|
|
|
222
362
|
// WebGPU specific
|
|
223
363
|
|
|
224
364
|
pushErrorScope(scope: 'validation' | 'out-of-memory'): void {
|
|
365
|
+
if (!this.props.debug) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const profiler = getWebGPUCpuHotspotProfiler(this);
|
|
369
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
225
370
|
this.handle.pushErrorScope(scope);
|
|
371
|
+
if (profiler) {
|
|
372
|
+
profiler.errorScopePushCount = (profiler.errorScopePushCount || 0) + 1;
|
|
373
|
+
profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
|
|
374
|
+
}
|
|
226
375
|
}
|
|
227
376
|
|
|
228
377
|
popErrorScope(handler: (error: GPUError) => void): void {
|
|
229
|
-
this.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
378
|
+
if (!this.props.debug) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const profiler = getWebGPUCpuHotspotProfiler(this);
|
|
382
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
383
|
+
this.handle
|
|
384
|
+
.popErrorScope()
|
|
385
|
+
.then((error: GPUError | null) => {
|
|
386
|
+
if (error) {
|
|
387
|
+
handler(error);
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
|
+
.catch((error: unknown) => {
|
|
391
|
+
if (this.shouldIgnoreDroppedInstanceError(error, 'popErrorScope')) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
396
|
+
this.reportError(new Error(`${this} popErrorScope failed: ${errorMessage}`), this)();
|
|
397
|
+
this.debug();
|
|
398
|
+
});
|
|
399
|
+
if (profiler) {
|
|
400
|
+
profiler.errorScopePopCount = (profiler.errorScopePopCount || 0) + 1;
|
|
401
|
+
profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
|
|
402
|
+
}
|
|
234
403
|
}
|
|
235
404
|
|
|
236
405
|
// PRIVATE METHODS
|
|
@@ -242,11 +411,22 @@ export class WebGPUDevice extends Device {
|
|
|
242
411
|
const vendor = this.adapterInfo.vendor || this.adapter.__brand || 'unknown';
|
|
243
412
|
const renderer = driver || '';
|
|
244
413
|
const version = driverVersion || '';
|
|
245
|
-
|
|
246
|
-
|
|
414
|
+
const fallback = Boolean(
|
|
415
|
+
(this.adapterInfo as any).isFallbackAdapter ??
|
|
416
|
+
(this.adapter as any).isFallbackAdapter ??
|
|
417
|
+
false
|
|
418
|
+
);
|
|
419
|
+
const softwareRenderer = /SwiftShader/i.test(
|
|
420
|
+
`${vendor} ${renderer} ${this.adapterInfo.architecture || ''}`
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
const gpu =
|
|
424
|
+
vendor === 'apple' ? 'apple' : softwareRenderer || fallback ? 'software' : 'unknown'; // 'nvidia' | 'amd' | 'intel' | 'apple' | 'unknown',
|
|
247
425
|
const gpuArchitecture = this.adapterInfo.architecture || 'unknown';
|
|
248
426
|
const gpuBackend = (this.adapterInfo as any).backend || 'unknown';
|
|
249
|
-
const gpuType =
|
|
427
|
+
const gpuType =
|
|
428
|
+
((this.adapterInfo as any).type || '').split(' ')[0].toLowerCase() ||
|
|
429
|
+
(softwareRenderer || fallback ? 'cpu' : 'unknown');
|
|
250
430
|
|
|
251
431
|
return {
|
|
252
432
|
type: 'webgpu',
|
|
@@ -257,11 +437,24 @@ export class WebGPUDevice extends Device {
|
|
|
257
437
|
gpuType,
|
|
258
438
|
gpuBackend,
|
|
259
439
|
gpuArchitecture,
|
|
440
|
+
fallback,
|
|
260
441
|
shadingLanguage: 'wgsl',
|
|
261
442
|
shadingLanguageVersion: 100
|
|
262
443
|
};
|
|
263
444
|
}
|
|
264
445
|
|
|
446
|
+
shouldIgnoreDroppedInstanceError(error: unknown, operation?: string): boolean {
|
|
447
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
448
|
+
return (
|
|
449
|
+
errorMessage.includes('Instance dropped') &&
|
|
450
|
+
(!operation || errorMessage.includes(operation)) &&
|
|
451
|
+
(this._isLost ||
|
|
452
|
+
this.info.gpu === 'software' ||
|
|
453
|
+
this.info.gpuType === 'cpu' ||
|
|
454
|
+
Boolean(this.info.fallback))
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
|
|
265
458
|
protected _getFeatures(): DeviceFeatures {
|
|
266
459
|
// Initialize with actual WebGPU Features (note that unknown features may not be in DeviceFeature type)
|
|
267
460
|
const features = new Set<DeviceFeature>(this.handle.features as Set<DeviceFeature>);
|
|
@@ -278,8 +471,15 @@ export class WebGPUDevice extends Device {
|
|
|
278
471
|
features.add('texture-compression-bc5-webgl');
|
|
279
472
|
}
|
|
280
473
|
|
|
474
|
+
if (this.handle.features.has('chromium-experimental-norm16-texture-formats')) {
|
|
475
|
+
features.add('norm16-renderable-webgl');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (this.handle.features.has('chromium-experimental-snorm16-texture-formats')) {
|
|
479
|
+
features.add('snorm16-renderable-webgl');
|
|
480
|
+
}
|
|
481
|
+
|
|
281
482
|
const WEBGPU_ALWAYS_FEATURES: DeviceFeature[] = [
|
|
282
|
-
'timer-query-webgl',
|
|
283
483
|
'compilation-status-async-webgl',
|
|
284
484
|
'float32-renderable-webgl',
|
|
285
485
|
'float16-renderable-webgl',
|
|
@@ -305,3 +505,13 @@ export class WebGPUDevice extends Device {
|
|
|
305
505
|
return capabilities;
|
|
306
506
|
}
|
|
307
507
|
}
|
|
508
|
+
|
|
509
|
+
function scheduleMicrotask(callback: () => void): void {
|
|
510
|
+
if (globalThis.queueMicrotask) {
|
|
511
|
+
globalThis.queueMicrotask(callback);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
Promise.resolve()
|
|
515
|
+
.then(callback)
|
|
516
|
+
.catch(() => {});
|
|
517
|
+
}
|