@luma.gl/webgpu 9.2.6 → 9.3.0-alpha.11
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 +8 -6
- package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
- package/dist/adapter/helpers/get-bind-group.js +110 -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 +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 +62 -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 +35 -35
- 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 +176 -19
- 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 +8160 -551
- package/dist/dist.min.js +171 -6
- package/dist/index.cjs +2001 -414
- 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 +182 -42
- 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 +90 -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 +41 -43
- package/src/adapter/webgpu-canvas-context.ts +108 -41
- package/src/adapter/webgpu-device.ts +243 -25
- 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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// biome-ignore format: preserve layout
|
|
6
6
|
// / <reference types="@webgpu/types" />
|
|
7
7
|
|
|
8
8
|
import type {TextureFormatDepthStencil, CanvasContextProps} from '@luma.gl/core';
|
|
@@ -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
|
}
|
|
@@ -2,20 +2,26 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// biome-ignore format: preserve layout
|
|
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 {
|
|
@@ -108,10 +128,9 @@ export class WebGPUDevice extends Device {
|
|
|
108
128
|
});
|
|
109
129
|
|
|
110
130
|
// "Context" loss handling
|
|
111
|
-
this.lost =
|
|
112
|
-
const lostInfo = await this.handle.lost;
|
|
131
|
+
this.lost = this.handle.lost.then(lostInfo => {
|
|
113
132
|
this._isLost = true;
|
|
114
|
-
|
|
133
|
+
return {reason: 'destroyed', message: lostInfo.message};
|
|
115
134
|
});
|
|
116
135
|
|
|
117
136
|
// Note: WebGPU devices can be created without a canvas, for compute shader purposes
|
|
@@ -129,6 +148,9 @@ export class WebGPUDevice extends Device {
|
|
|
129
148
|
// this.glslang = glsl && await loadGlslangModule();
|
|
130
149
|
|
|
131
150
|
destroy(): void {
|
|
151
|
+
this.commandEncoder?.destroy();
|
|
152
|
+
this._defaultSampler?.destroy();
|
|
153
|
+
this._defaultSampler = null;
|
|
132
154
|
this.handle.destroy();
|
|
133
155
|
}
|
|
134
156
|
|
|
@@ -136,15 +158,15 @@ export class WebGPUDevice extends Device {
|
|
|
136
158
|
return this._isLost;
|
|
137
159
|
}
|
|
138
160
|
|
|
161
|
+
getShaderLayout(source: string) {
|
|
162
|
+
return getShaderLayoutFromWGSL(source);
|
|
163
|
+
}
|
|
164
|
+
|
|
139
165
|
override isVertexFormatSupported(format: VertexFormat): boolean {
|
|
140
166
|
const info = this.getVertexFormatInfo(format);
|
|
141
167
|
return !info.webglOnly;
|
|
142
168
|
}
|
|
143
169
|
|
|
144
|
-
getTextureByteAlignment(): number {
|
|
145
|
-
return 1;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
170
|
createBuffer(props: BufferProps | ArrayBuffer | ArrayBufferView): WebGPUBuffer {
|
|
149
171
|
const newProps = this._normalizeBufferProps(props);
|
|
150
172
|
return new WebGPUBuffer(this, newProps);
|
|
@@ -166,6 +188,13 @@ export class WebGPUDevice extends Device {
|
|
|
166
188
|
return new WebGPUSampler(this, props);
|
|
167
189
|
}
|
|
168
190
|
|
|
191
|
+
getDefaultSampler(): WebGPUSampler {
|
|
192
|
+
this._defaultSampler ||= new WebGPUSampler(this, {
|
|
193
|
+
id: `${this.id}-default-sampler`
|
|
194
|
+
});
|
|
195
|
+
return this._defaultSampler;
|
|
196
|
+
}
|
|
197
|
+
|
|
169
198
|
createRenderPipeline(props: RenderPipelineProps): WebGPURenderPipeline {
|
|
170
199
|
return new WebGPURenderPipeline(this, props);
|
|
171
200
|
}
|
|
@@ -196,41 +225,189 @@ export class WebGPUDevice extends Device {
|
|
|
196
225
|
return new WebGPUQuerySet(this, props);
|
|
197
226
|
}
|
|
198
227
|
|
|
228
|
+
override createFence(): WebGPUFence {
|
|
229
|
+
return new WebGPUFence(this);
|
|
230
|
+
}
|
|
231
|
+
|
|
199
232
|
createCanvasContext(props: CanvasContextProps): WebGPUCanvasContext {
|
|
200
233
|
return new WebGPUCanvasContext(this, this.adapter, props);
|
|
201
234
|
}
|
|
202
235
|
|
|
236
|
+
createPresentationContext(props?: PresentationContextProps): PresentationContext {
|
|
237
|
+
return new WebGPUPresentationContext(this, props);
|
|
238
|
+
}
|
|
239
|
+
|
|
203
240
|
createPipelineLayout(props: PipelineLayoutProps): WebGPUPipelineLayout {
|
|
204
241
|
return new WebGPUPipelineLayout(this, props);
|
|
205
242
|
}
|
|
206
243
|
|
|
244
|
+
override generateMipmapsWebGPU(texture: Texture): void {
|
|
245
|
+
generateMipmapsWebGPU(this, texture);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
override _createBindGroupLayoutWebGPU(
|
|
249
|
+
pipeline: RenderPipeline | ComputePipeline,
|
|
250
|
+
group: number
|
|
251
|
+
): GPUBindGroupLayout {
|
|
252
|
+
return (pipeline as WebGPURenderPipeline | WebGPUComputePipeline).handle.getBindGroupLayout(
|
|
253
|
+
group
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
override _createBindGroupWebGPU(
|
|
258
|
+
bindGroupLayout: unknown,
|
|
259
|
+
shaderLayout: ShaderLayout | ComputeShaderLayout,
|
|
260
|
+
bindings: Bindings,
|
|
261
|
+
group: number,
|
|
262
|
+
label?: string
|
|
263
|
+
): GPUBindGroup | null {
|
|
264
|
+
if (Object.keys(bindings).length === 0) {
|
|
265
|
+
return this.handle.createBindGroup({
|
|
266
|
+
label,
|
|
267
|
+
layout: bindGroupLayout as GPUBindGroupLayout,
|
|
268
|
+
entries: []
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return getBindGroup(
|
|
273
|
+
this,
|
|
274
|
+
bindGroupLayout as GPUBindGroupLayout,
|
|
275
|
+
shaderLayout,
|
|
276
|
+
bindings,
|
|
277
|
+
group,
|
|
278
|
+
label
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
207
282
|
submit(commandBuffer?: WebGPUCommandBuffer): void {
|
|
283
|
+
let submittedCommandEncoder: WebGPUCommandEncoder | null = null;
|
|
208
284
|
if (!commandBuffer) {
|
|
209
|
-
commandBuffer = this.
|
|
210
|
-
this.commandEncoder.destroy();
|
|
211
|
-
this.commandEncoder = this.createCommandEncoder({id: `${this.id}-default-encoder`});
|
|
285
|
+
({submittedCommandEncoder, commandBuffer} = this._finalizeDefaultCommandEncoderForSubmit());
|
|
212
286
|
}
|
|
213
287
|
|
|
214
|
-
this
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
this.
|
|
288
|
+
const profiler = getWebGPUCpuHotspotProfiler(this);
|
|
289
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
290
|
+
const submitReason = getWebGPUCpuHotspotSubmitReason(this);
|
|
291
|
+
try {
|
|
292
|
+
this.pushErrorScope('validation');
|
|
293
|
+
const queueSubmitStartTime = profiler ? getTimestamp() : 0;
|
|
294
|
+
this.handle.queue.submit([commandBuffer.handle]);
|
|
295
|
+
if (profiler) {
|
|
296
|
+
profiler.queueSubmitCount = (profiler.queueSubmitCount || 0) + 1;
|
|
297
|
+
profiler.queueSubmitTimeMs =
|
|
298
|
+
(profiler.queueSubmitTimeMs || 0) + (getTimestamp() - queueSubmitStartTime);
|
|
299
|
+
}
|
|
300
|
+
this.popErrorScope((error: GPUError) => {
|
|
301
|
+
this.reportError(new Error(`${this} command submission: ${error.message}`), this)();
|
|
302
|
+
this.debug();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
if (submittedCommandEncoder) {
|
|
306
|
+
const submitResolveKickoffStartTime = profiler ? getTimestamp() : 0;
|
|
307
|
+
scheduleMicrotask(() => {
|
|
308
|
+
submittedCommandEncoder
|
|
309
|
+
.resolveTimeProfilingQuerySet()
|
|
310
|
+
.then(() => {
|
|
311
|
+
this.commandEncoder._gpuTimeMs = submittedCommandEncoder._gpuTimeMs;
|
|
312
|
+
})
|
|
313
|
+
.catch(() => {});
|
|
314
|
+
});
|
|
315
|
+
if (profiler) {
|
|
316
|
+
profiler.submitResolveKickoffCount = (profiler.submitResolveKickoffCount || 0) + 1;
|
|
317
|
+
profiler.submitResolveKickoffTimeMs =
|
|
318
|
+
(profiler.submitResolveKickoffTimeMs || 0) +
|
|
319
|
+
(getTimestamp() - submitResolveKickoffStartTime);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
} finally {
|
|
323
|
+
if (profiler) {
|
|
324
|
+
profiler.submitCount = (profiler.submitCount || 0) + 1;
|
|
325
|
+
profiler.submitTimeMs = (profiler.submitTimeMs || 0) + (getTimestamp() - startTime);
|
|
326
|
+
const reasonCountKey =
|
|
327
|
+
submitReason === 'query-readback' ? 'queryReadbackSubmitCount' : 'defaultSubmitCount';
|
|
328
|
+
const reasonTimeKey =
|
|
329
|
+
submitReason === 'query-readback' ? 'queryReadbackSubmitTimeMs' : 'defaultSubmitTimeMs';
|
|
330
|
+
profiler[reasonCountKey] = (profiler[reasonCountKey] || 0) + 1;
|
|
331
|
+
profiler[reasonTimeKey] = (profiler[reasonTimeKey] || 0) + (getTimestamp() - startTime);
|
|
332
|
+
}
|
|
333
|
+
const commandBufferDestroyStartTime = profiler ? getTimestamp() : 0;
|
|
334
|
+
commandBuffer.destroy();
|
|
335
|
+
if (profiler) {
|
|
336
|
+
profiler.commandBufferDestroyCount = (profiler.commandBufferDestroyCount || 0) + 1;
|
|
337
|
+
profiler.commandBufferDestroyTimeMs =
|
|
338
|
+
(profiler.commandBufferDestroyTimeMs || 0) +
|
|
339
|
+
(getTimestamp() - commandBufferDestroyStartTime);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private _finalizeDefaultCommandEncoderForSubmit(): {
|
|
345
|
+
submittedCommandEncoder: WebGPUCommandEncoder;
|
|
346
|
+
commandBuffer: WebGPUCommandBuffer;
|
|
347
|
+
} {
|
|
348
|
+
const submittedCommandEncoder = this.commandEncoder;
|
|
349
|
+
if (
|
|
350
|
+
submittedCommandEncoder.getTimeProfilingSlotCount() > 0 &&
|
|
351
|
+
submittedCommandEncoder.getTimeProfilingQuerySet() instanceof WebGPUQuerySet
|
|
352
|
+
) {
|
|
353
|
+
const querySet = submittedCommandEncoder.getTimeProfilingQuerySet() as WebGPUQuerySet;
|
|
354
|
+
querySet._encodeResolveToReadBuffer(submittedCommandEncoder, {
|
|
355
|
+
firstQuery: 0,
|
|
356
|
+
queryCount: submittedCommandEncoder.getTimeProfilingSlotCount()
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const commandBuffer = submittedCommandEncoder.finish();
|
|
361
|
+
this.commandEncoder.destroy();
|
|
362
|
+
this.commandEncoder = this.createCommandEncoder({
|
|
363
|
+
id: submittedCommandEncoder.props.id,
|
|
364
|
+
timeProfilingQuerySet: submittedCommandEncoder.getTimeProfilingQuerySet()
|
|
219
365
|
});
|
|
366
|
+
|
|
367
|
+
return {submittedCommandEncoder, commandBuffer};
|
|
220
368
|
}
|
|
221
369
|
|
|
222
370
|
// WebGPU specific
|
|
223
371
|
|
|
224
372
|
pushErrorScope(scope: 'validation' | 'out-of-memory'): void {
|
|
373
|
+
if (!this.props.debug) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const profiler = getWebGPUCpuHotspotProfiler(this);
|
|
377
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
225
378
|
this.handle.pushErrorScope(scope);
|
|
379
|
+
if (profiler) {
|
|
380
|
+
profiler.errorScopePushCount = (profiler.errorScopePushCount || 0) + 1;
|
|
381
|
+
profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
|
|
382
|
+
}
|
|
226
383
|
}
|
|
227
384
|
|
|
228
385
|
popErrorScope(handler: (error: GPUError) => void): void {
|
|
229
|
-
this.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
386
|
+
if (!this.props.debug) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const profiler = getWebGPUCpuHotspotProfiler(this);
|
|
390
|
+
const startTime = profiler ? getTimestamp() : 0;
|
|
391
|
+
this.handle
|
|
392
|
+
.popErrorScope()
|
|
393
|
+
.then((error: GPUError | null) => {
|
|
394
|
+
if (error) {
|
|
395
|
+
handler(error);
|
|
396
|
+
}
|
|
397
|
+
})
|
|
398
|
+
.catch((error: unknown) => {
|
|
399
|
+
if (this.shouldIgnoreDroppedInstanceError(error, 'popErrorScope')) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
404
|
+
this.reportError(new Error(`${this} popErrorScope failed: ${errorMessage}`), this)();
|
|
405
|
+
this.debug();
|
|
406
|
+
});
|
|
407
|
+
if (profiler) {
|
|
408
|
+
profiler.errorScopePopCount = (profiler.errorScopePopCount || 0) + 1;
|
|
409
|
+
profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
|
|
410
|
+
}
|
|
234
411
|
}
|
|
235
412
|
|
|
236
413
|
// PRIVATE METHODS
|
|
@@ -242,11 +419,22 @@ export class WebGPUDevice extends Device {
|
|
|
242
419
|
const vendor = this.adapterInfo.vendor || this.adapter.__brand || 'unknown';
|
|
243
420
|
const renderer = driver || '';
|
|
244
421
|
const version = driverVersion || '';
|
|
245
|
-
|
|
246
|
-
|
|
422
|
+
const fallback = Boolean(
|
|
423
|
+
(this.adapterInfo as any).isFallbackAdapter ??
|
|
424
|
+
(this.adapter as any).isFallbackAdapter ??
|
|
425
|
+
false
|
|
426
|
+
);
|
|
427
|
+
const softwareRenderer = /SwiftShader/i.test(
|
|
428
|
+
`${vendor} ${renderer} ${this.adapterInfo.architecture || ''}`
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
const gpu =
|
|
432
|
+
vendor === 'apple' ? 'apple' : softwareRenderer || fallback ? 'software' : 'unknown'; // 'nvidia' | 'amd' | 'intel' | 'apple' | 'unknown',
|
|
247
433
|
const gpuArchitecture = this.adapterInfo.architecture || 'unknown';
|
|
248
434
|
const gpuBackend = (this.adapterInfo as any).backend || 'unknown';
|
|
249
|
-
const gpuType =
|
|
435
|
+
const gpuType =
|
|
436
|
+
((this.adapterInfo as any).type || '').split(' ')[0].toLowerCase() ||
|
|
437
|
+
(softwareRenderer || fallback ? 'cpu' : 'unknown');
|
|
250
438
|
|
|
251
439
|
return {
|
|
252
440
|
type: 'webgpu',
|
|
@@ -257,11 +445,24 @@ export class WebGPUDevice extends Device {
|
|
|
257
445
|
gpuType,
|
|
258
446
|
gpuBackend,
|
|
259
447
|
gpuArchitecture,
|
|
448
|
+
fallback,
|
|
260
449
|
shadingLanguage: 'wgsl',
|
|
261
450
|
shadingLanguageVersion: 100
|
|
262
451
|
};
|
|
263
452
|
}
|
|
264
453
|
|
|
454
|
+
shouldIgnoreDroppedInstanceError(error: unknown, operation?: string): boolean {
|
|
455
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
456
|
+
return (
|
|
457
|
+
errorMessage.includes('Instance dropped') &&
|
|
458
|
+
(!operation || errorMessage.includes(operation)) &&
|
|
459
|
+
(this._isLost ||
|
|
460
|
+
this.info.gpu === 'software' ||
|
|
461
|
+
this.info.gpuType === 'cpu' ||
|
|
462
|
+
Boolean(this.info.fallback))
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
|
|
265
466
|
protected _getFeatures(): DeviceFeatures {
|
|
266
467
|
// Initialize with actual WebGPU Features (note that unknown features may not be in DeviceFeature type)
|
|
267
468
|
const features = new Set<DeviceFeature>(this.handle.features as Set<DeviceFeature>);
|
|
@@ -278,8 +479,15 @@ export class WebGPUDevice extends Device {
|
|
|
278
479
|
features.add('texture-compression-bc5-webgl');
|
|
279
480
|
}
|
|
280
481
|
|
|
482
|
+
if (this.handle.features.has('chromium-experimental-norm16-texture-formats')) {
|
|
483
|
+
features.add('norm16-renderable-webgl');
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (this.handle.features.has('chromium-experimental-snorm16-texture-formats')) {
|
|
487
|
+
features.add('snorm16-renderable-webgl');
|
|
488
|
+
}
|
|
489
|
+
|
|
281
490
|
const WEBGPU_ALWAYS_FEATURES: DeviceFeature[] = [
|
|
282
|
-
'timer-query-webgl',
|
|
283
491
|
'compilation-status-async-webgl',
|
|
284
492
|
'float32-renderable-webgl',
|
|
285
493
|
'float16-renderable-webgl',
|
|
@@ -305,3 +513,13 @@ export class WebGPUDevice extends Device {
|
|
|
305
513
|
return capabilities;
|
|
306
514
|
}
|
|
307
515
|
}
|
|
516
|
+
|
|
517
|
+
function scheduleMicrotask(callback: () => void): void {
|
|
518
|
+
if (globalThis.queueMicrotask) {
|
|
519
|
+
globalThis.queueMicrotask(callback);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
Promise.resolve()
|
|
523
|
+
.then(callback)
|
|
524
|
+
.catch(() => {});
|
|
525
|
+
}
|