@luma.gl/webgpu 9.3.0-alpha.4 → 9.3.0-alpha.8
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 +31 -30
- 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.map +1 -1
- package/dist/adapter/resources/webgpu-buffer.js +19 -3
- 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.map +1 -1
- package/dist/adapter/resources/webgpu-fence.js +9 -1
- package/dist/adapter/resources/webgpu-fence.js.map +1 -1
- 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 +10 -16
- 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 +18 -5
- package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-texture.js +148 -97
- 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-canvas-context.d.ts +2 -0
- package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
- package/dist/adapter/webgpu-canvas-context.js +78 -19
- package/dist/adapter/webgpu-canvas-context.js.map +1 -1
- package/dist/adapter/webgpu-device.d.ts +10 -2
- package/dist/adapter/webgpu-device.d.ts.map +1 -1
- package/dist/adapter/webgpu-device.js +159 -13
- 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 +3180 -1849
- package/dist/dist.min.js +168 -9
- package/dist/index.cjs +1640 -405
- package/dist/index.cjs.map +4 -4
- package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -1
- package/dist/wgsl/get-shader-layout-wgsl.js +8 -0
- package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -1
- package/package.json +5 -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 +42 -49
- 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 +18 -3
- 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 +11 -3
- package/src/adapter/resources/webgpu-framebuffer.ts +21 -0
- package/src/adapter/resources/webgpu-pipeline-layout.ts +16 -14
- 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 +198 -132
- package/src/adapter/resources/webgpu-vertex-array.ts +1 -1
- package/src/adapter/webgpu-canvas-context.ts +91 -26
- package/src/adapter/webgpu-device.ts +212 -17
- package/src/adapter/webgpu-presentation-context.ts +180 -0
- package/src/wgsl/get-shader-layout-wgsl.ts +9 -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';
|
|
@@ -39,10 +42,23 @@ export class WebGPUCanvasContext extends CanvasContext {
|
|
|
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
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 {
|
|
50
|
+
if (this.framebuffer) {
|
|
51
|
+
this.framebuffer.destroy();
|
|
52
|
+
this.framebuffer = null;
|
|
53
|
+
}
|
|
54
|
+
if (this.colorAttachment) {
|
|
55
|
+
this.colorAttachment.destroy();
|
|
56
|
+
this.colorAttachment = null;
|
|
57
|
+
}
|
|
58
|
+
if (this.depthStencilAttachment) {
|
|
59
|
+
this.depthStencilAttachment.destroy();
|
|
60
|
+
this.depthStencilAttachment = null;
|
|
61
|
+
}
|
|
46
62
|
this.handle.unconfigure();
|
|
47
63
|
super.destroy();
|
|
48
64
|
}
|
|
@@ -65,6 +81,8 @@ export class WebGPUCanvasContext extends CanvasContext {
|
|
|
65
81
|
colorSpace: this.props.colorSpace,
|
|
66
82
|
alphaMode: this.props.alphaMode
|
|
67
83
|
});
|
|
84
|
+
|
|
85
|
+
this._createDepthStencilAttachment(this.device.preferredDepthFormat);
|
|
68
86
|
}
|
|
69
87
|
|
|
70
88
|
/** Update framebuffer with properly resized "swap chain" texture views */
|
|
@@ -73,50 +91,97 @@ export class WebGPUCanvasContext extends CanvasContext {
|
|
|
73
91
|
depthStencilFormat: 'depth24plus'
|
|
74
92
|
}
|
|
75
93
|
): WebGPUFramebuffer {
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
) {
|
|
83
|
-
const [oldWidth, oldHeight] = this.getDrawingBufferSize();
|
|
84
|
-
this.drawingBufferWidth = currentColorAttachment.width;
|
|
85
|
-
this.drawingBufferHeight = currentColorAttachment.height;
|
|
86
|
-
log.log(
|
|
87
|
-
1,
|
|
88
|
-
`${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`
|
|
89
|
-
)();
|
|
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;
|
|
90
100
|
}
|
|
91
101
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
this.
|
|
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
|
+
}
|
|
95
141
|
}
|
|
96
|
-
|
|
97
|
-
return new WebGPUFramebuffer(this.device, {
|
|
98
|
-
colorAttachments: [currentColorAttachment],
|
|
99
|
-
depthStencilAttachment: this.depthStencilAttachment
|
|
100
|
-
});
|
|
101
142
|
}
|
|
102
143
|
|
|
103
144
|
// PRIMARY METHODS
|
|
104
145
|
|
|
105
146
|
/** Wrap the current canvas context texture in a luma.gl texture */
|
|
106
147
|
_getCurrentTexture(): WebGPUTexture {
|
|
148
|
+
const profiler = getCpuHotspotProfiler(this.device);
|
|
149
|
+
const currentTextureStartTime = profiler ? getTimestamp() : 0;
|
|
107
150
|
const handle = this.handle.getCurrentTexture();
|
|
108
|
-
|
|
109
|
-
|
|
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, {
|
|
110
168
|
handle,
|
|
111
169
|
format: this.device.preferredColorFormat,
|
|
112
170
|
width: handle.width,
|
|
113
171
|
height: handle.height
|
|
114
172
|
});
|
|
173
|
+
return this.colorAttachment;
|
|
115
174
|
}
|
|
116
175
|
|
|
117
176
|
/** We build render targets on demand (i.e. not when size changes but when about to render) */
|
|
118
177
|
_createDepthStencilAttachment(depthStencilFormat: TextureFormatDepthStencil): WebGPUTexture {
|
|
119
|
-
|
|
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();
|
|
120
185
|
this.depthStencilAttachment = this.device.createTexture({
|
|
121
186
|
id: `${this.id}#depth-stencil-texture`,
|
|
122
187
|
usage: Texture.RENDER_ATTACHMENT,
|
|
@@ -125,6 +190,6 @@ export class WebGPUCanvasContext extends CanvasContext {
|
|
|
125
190
|
height: this.drawingBufferHeight
|
|
126
191
|
});
|
|
127
192
|
}
|
|
128
|
-
return this.depthStencilAttachment
|
|
193
|
+
return this.depthStencilAttachment!;
|
|
129
194
|
}
|
|
130
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,6 +49,7 @@ 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';
|
|
@@ -48,6 +57,13 @@ import {WebGPUPipelineLayout} from './resources/webgpu-pipeline-layout';
|
|
|
48
57
|
import {WebGPUFence} from './resources/webgpu-fence';
|
|
49
58
|
|
|
50
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';
|
|
51
67
|
|
|
52
68
|
/** WebGPU Device implementation */
|
|
53
69
|
export class WebGPUDevice extends Device {
|
|
@@ -75,6 +91,7 @@ export class WebGPUDevice extends Device {
|
|
|
75
91
|
override canvasContext: WebGPUCanvasContext | null = null;
|
|
76
92
|
|
|
77
93
|
private _isLost: boolean = false;
|
|
94
|
+
private _defaultSampler: WebGPUSampler | null = null;
|
|
78
95
|
commandEncoder: WebGPUCommandEncoder;
|
|
79
96
|
|
|
80
97
|
override get [Symbol.toStringTag](): string {
|
|
@@ -132,6 +149,9 @@ export class WebGPUDevice extends Device {
|
|
|
132
149
|
// this.glslang = glsl && await loadGlslangModule();
|
|
133
150
|
|
|
134
151
|
destroy(): void {
|
|
152
|
+
this.commandEncoder?.destroy();
|
|
153
|
+
this._defaultSampler?.destroy();
|
|
154
|
+
this._defaultSampler = null;
|
|
135
155
|
this.handle.destroy();
|
|
136
156
|
}
|
|
137
157
|
|
|
@@ -169,6 +189,13 @@ export class WebGPUDevice extends Device {
|
|
|
169
189
|
return new WebGPUSampler(this, props);
|
|
170
190
|
}
|
|
171
191
|
|
|
192
|
+
getDefaultSampler(): WebGPUSampler {
|
|
193
|
+
this._defaultSampler ||= new WebGPUSampler(this, {
|
|
194
|
+
id: `${this.id}-default-sampler`
|
|
195
|
+
});
|
|
196
|
+
return this._defaultSampler;
|
|
197
|
+
}
|
|
198
|
+
|
|
172
199
|
createRenderPipeline(props: RenderPipelineProps): WebGPURenderPipeline {
|
|
173
200
|
return new WebGPURenderPipeline(this, props);
|
|
174
201
|
}
|
|
@@ -207,37 +234,172 @@ export class WebGPUDevice extends Device {
|
|
|
207
234
|
return new WebGPUCanvasContext(this, this.adapter, props);
|
|
208
235
|
}
|
|
209
236
|
|
|
237
|
+
createPresentationContext(props?: PresentationContextProps): PresentationContext {
|
|
238
|
+
return new WebGPUPresentationContext(this, props);
|
|
239
|
+
}
|
|
240
|
+
|
|
210
241
|
createPipelineLayout(props: PipelineLayoutProps): WebGPUPipelineLayout {
|
|
211
242
|
return new WebGPUPipelineLayout(this, props);
|
|
212
243
|
}
|
|
213
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
|
+
|
|
214
274
|
submit(commandBuffer?: WebGPUCommandBuffer): void {
|
|
275
|
+
let submittedCommandEncoder: WebGPUCommandEncoder | null = null;
|
|
215
276
|
if (!commandBuffer) {
|
|
216
|
-
commandBuffer = this.
|
|
217
|
-
this.commandEncoder.destroy();
|
|
218
|
-
this.commandEncoder = this.createCommandEncoder({id: `${this.id}-default-encoder`});
|
|
277
|
+
({submittedCommandEncoder, commandBuffer} = this._finalizeDefaultCommandEncoderForSubmit());
|
|
219
278
|
}
|
|
220
279
|
|
|
221
|
-
this
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
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()
|
|
226
357
|
});
|
|
358
|
+
|
|
359
|
+
return {submittedCommandEncoder, commandBuffer};
|
|
227
360
|
}
|
|
228
361
|
|
|
229
362
|
// WebGPU specific
|
|
230
363
|
|
|
231
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;
|
|
232
370
|
this.handle.pushErrorScope(scope);
|
|
371
|
+
if (profiler) {
|
|
372
|
+
profiler.errorScopePushCount = (profiler.errorScopePushCount || 0) + 1;
|
|
373
|
+
profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
|
|
374
|
+
}
|
|
233
375
|
}
|
|
234
376
|
|
|
235
377
|
popErrorScope(handler: (error: GPUError) => void): void {
|
|
236
|
-
this.
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
+
}
|
|
241
403
|
}
|
|
242
404
|
|
|
243
405
|
// PRIVATE METHODS
|
|
@@ -249,11 +411,22 @@ export class WebGPUDevice extends Device {
|
|
|
249
411
|
const vendor = this.adapterInfo.vendor || this.adapter.__brand || 'unknown';
|
|
250
412
|
const renderer = driver || '';
|
|
251
413
|
const version = driverVersion || '';
|
|
252
|
-
|
|
253
|
-
|
|
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',
|
|
254
425
|
const gpuArchitecture = this.adapterInfo.architecture || 'unknown';
|
|
255
426
|
const gpuBackend = (this.adapterInfo as any).backend || 'unknown';
|
|
256
|
-
const gpuType =
|
|
427
|
+
const gpuType =
|
|
428
|
+
((this.adapterInfo as any).type || '').split(' ')[0].toLowerCase() ||
|
|
429
|
+
(softwareRenderer || fallback ? 'cpu' : 'unknown');
|
|
257
430
|
|
|
258
431
|
return {
|
|
259
432
|
type: 'webgpu',
|
|
@@ -264,11 +437,24 @@ export class WebGPUDevice extends Device {
|
|
|
264
437
|
gpuType,
|
|
265
438
|
gpuBackend,
|
|
266
439
|
gpuArchitecture,
|
|
440
|
+
fallback,
|
|
267
441
|
shadingLanguage: 'wgsl',
|
|
268
442
|
shadingLanguageVersion: 100
|
|
269
443
|
};
|
|
270
444
|
}
|
|
271
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
|
+
|
|
272
458
|
protected _getFeatures(): DeviceFeatures {
|
|
273
459
|
// Initialize with actual WebGPU Features (note that unknown features may not be in DeviceFeature type)
|
|
274
460
|
const features = new Set<DeviceFeature>(this.handle.features as Set<DeviceFeature>);
|
|
@@ -294,7 +480,6 @@ export class WebGPUDevice extends Device {
|
|
|
294
480
|
}
|
|
295
481
|
|
|
296
482
|
const WEBGPU_ALWAYS_FEATURES: DeviceFeature[] = [
|
|
297
|
-
'timer-query-webgl',
|
|
298
483
|
'compilation-status-async-webgl',
|
|
299
484
|
'float32-renderable-webgl',
|
|
300
485
|
'float16-renderable-webgl',
|
|
@@ -320,3 +505,13 @@ export class WebGPUDevice extends Device {
|
|
|
320
505
|
return capabilities;
|
|
321
506
|
}
|
|
322
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
|
+
}
|