@luma.gl/webgpu 9.3.0-alpha.8 → 9.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/helpers/get-bind-group.d.ts +5 -1
- package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
- package/dist/adapter/helpers/get-bind-group.js +79 -6
- package/dist/adapter/helpers/get-bind-group.js.map +1 -1
- package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-render-pipeline.js +6 -0
- package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
- package/dist/adapter/webgpu-adapter.js +1 -1
- package/dist/adapter/webgpu-adapter.js.map +1 -1
- package/dist/adapter/webgpu-device.d.ts +1 -1
- package/dist/adapter/webgpu-device.d.ts.map +1 -1
- package/dist/adapter/webgpu-device.js +5 -5
- package/dist/adapter/webgpu-device.js.map +1 -1
- package/dist/dist.dev.js +97 -11
- package/dist/dist.min.js +13 -11
- package/dist/index.cjs +79 -11
- package/dist/index.cjs.map +2 -2
- package/package.json +2 -2
- package/src/adapter/helpers/get-bind-group.ts +143 -9
- package/src/adapter/resources/webgpu-render-pipeline.ts +7 -0
- package/src/adapter/webgpu-adapter.ts +1 -1
- package/src/adapter/webgpu-canvas-context.ts +1 -1
- package/src/adapter/webgpu-device.ts +14 -6
- package/src/adapter/webgpu-presentation-context.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luma.gl/webgpu",
|
|
3
|
-
"version": "9.3.0
|
|
3
|
+
"version": "9.3.0",
|
|
4
4
|
"description": "WebGPU adapter for the luma.gl core API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"@webgpu/types": "^0.1.69",
|
|
45
45
|
"wgsl_reflect": "^1.2.3"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "c5e44faacd07b315a8622b5927e724f15a3d40ae"
|
|
48
48
|
}
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
Binding,
|
|
7
|
+
BindingDeclaration,
|
|
8
|
+
Bindings,
|
|
9
|
+
ComputeShaderLayout,
|
|
10
|
+
ShaderLayout
|
|
11
|
+
} from '@luma.gl/core';
|
|
6
12
|
import {Buffer, Sampler, Texture, TextureView, getShaderLayoutBinding, log} from '@luma.gl/core';
|
|
7
13
|
import type {WebGPUDevice} from '../webgpu-device';
|
|
8
14
|
import type {WebGPUBuffer} from '../resources/webgpu-buffer';
|
|
@@ -10,6 +16,13 @@ import type {WebGPUSampler} from '../resources/webgpu-sampler';
|
|
|
10
16
|
import type {WebGPUTexture} from '../resources/webgpu-texture';
|
|
11
17
|
import type {WebGPUTextureView} from '../resources/webgpu-texture-view';
|
|
12
18
|
|
|
19
|
+
type AnyShaderLayout = ShaderLayout | ComputeShaderLayout;
|
|
20
|
+
type BindGroupBindingSummary = {
|
|
21
|
+
name: string;
|
|
22
|
+
location: number;
|
|
23
|
+
type: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
13
26
|
/**
|
|
14
27
|
* Create a WebGPU "bind group layout" from an array of luma.gl bindings
|
|
15
28
|
* @note bind groups can be automatically generated by WebGPU.
|
|
@@ -34,7 +47,8 @@ export function getBindGroup(
|
|
|
34
47
|
bindGroupLayout: GPUBindGroupLayout,
|
|
35
48
|
shaderLayout: ShaderLayout | ComputeShaderLayout,
|
|
36
49
|
bindings: Bindings,
|
|
37
|
-
group: number
|
|
50
|
+
group: number,
|
|
51
|
+
label?: string
|
|
38
52
|
): GPUBindGroup | null {
|
|
39
53
|
const entries = getBindGroupEntries(bindings, shaderLayout, group);
|
|
40
54
|
if (entries.length === 0) {
|
|
@@ -42,35 +56,93 @@ export function getBindGroup(
|
|
|
42
56
|
}
|
|
43
57
|
device.pushErrorScope('validation');
|
|
44
58
|
const bindGroup = device.handle.createBindGroup({
|
|
59
|
+
label,
|
|
45
60
|
layout: bindGroupLayout,
|
|
46
61
|
entries
|
|
47
62
|
});
|
|
48
63
|
device.popErrorScope((error: GPUError) => {
|
|
49
|
-
|
|
64
|
+
const summary = formatBindGroupCreationErrorSummary(shaderLayout, bindings, entries, group);
|
|
65
|
+
log.error(`bindGroup creation: ${summary}\nRaw WebGPU error: ${error.message}`, bindGroup)();
|
|
50
66
|
});
|
|
51
67
|
return bindGroup;
|
|
52
68
|
}
|
|
53
69
|
|
|
70
|
+
export function formatBindGroupCreationErrorSummary(
|
|
71
|
+
shaderLayout: AnyShaderLayout,
|
|
72
|
+
bindings: Bindings,
|
|
73
|
+
entries: GPUBindGroupEntry[],
|
|
74
|
+
group: number
|
|
75
|
+
): string {
|
|
76
|
+
const expectedBindings = getExpectedBindingsForGroup(shaderLayout, group);
|
|
77
|
+
const expectedByLocation = new Map(
|
|
78
|
+
expectedBindings.map(bindingSummary => [bindingSummary.location, bindingSummary])
|
|
79
|
+
);
|
|
80
|
+
const providedBindings = entries
|
|
81
|
+
.map(entry => expectedByLocation.get(entry.binding) || getUnexpectedEntrySummary(entry))
|
|
82
|
+
.sort(compareBindingSummaries);
|
|
83
|
+
const missingBindings = expectedBindings.filter(
|
|
84
|
+
bindingSummary =>
|
|
85
|
+
!providedBindings.some(provided => provided.location === bindingSummary.location)
|
|
86
|
+
);
|
|
87
|
+
const unexpectedBindings = providedBindings.filter(
|
|
88
|
+
bindingSummary => !expectedByLocation.has(bindingSummary.location)
|
|
89
|
+
);
|
|
90
|
+
const unmatchedLogicalBindings = Object.keys(bindings)
|
|
91
|
+
.filter(bindingName => !resolveGroupBinding(bindingName, bindings, shaderLayout, group))
|
|
92
|
+
.sort();
|
|
93
|
+
|
|
94
|
+
const lines = [
|
|
95
|
+
`bindGroup creation failed for group ${group}: expected ${expectedBindings.length}, provided ${providedBindings.length}`,
|
|
96
|
+
`expected: ${formatBindingSummaryList(expectedBindings)}`,
|
|
97
|
+
`provided: ${formatBindingSummaryList(providedBindings)}`
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
if (missingBindings.length > 0) {
|
|
101
|
+
lines.push(`missing: ${formatBindingSummaryList(missingBindings)}`);
|
|
102
|
+
}
|
|
103
|
+
if (unexpectedBindings.length > 0) {
|
|
104
|
+
lines.push(`unexpected entries: ${formatBindingSummaryList(unexpectedBindings)}`);
|
|
105
|
+
}
|
|
106
|
+
if (unmatchedLogicalBindings.length > 0) {
|
|
107
|
+
lines.push(`unmatched logical bindings: ${unmatchedLogicalBindings.join(', ')}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return lines.join('\n');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function getBindGroupLabel(
|
|
114
|
+
pipelineId: string,
|
|
115
|
+
shaderLayout: AnyShaderLayout,
|
|
116
|
+
group: number
|
|
117
|
+
): string {
|
|
118
|
+
const expectedBindings = getExpectedBindingsForGroup(shaderLayout, group);
|
|
119
|
+
const bindingSuffix =
|
|
120
|
+
expectedBindings.length > 0 ? expectedBindings.map(binding => binding.name).join(',') : 'empty';
|
|
121
|
+
return `${pipelineId}/group${group}[${bindingSuffix}]`;
|
|
122
|
+
}
|
|
123
|
+
|
|
54
124
|
/**
|
|
55
125
|
* @param bindings
|
|
56
126
|
* @returns
|
|
57
127
|
*/
|
|
58
128
|
function getBindGroupEntries(
|
|
59
129
|
bindings: Bindings,
|
|
60
|
-
shaderLayout:
|
|
130
|
+
shaderLayout: AnyShaderLayout,
|
|
61
131
|
group: number
|
|
62
132
|
): GPUBindGroupEntry[] {
|
|
63
133
|
const entries: GPUBindGroupEntry[] = [];
|
|
64
134
|
|
|
65
135
|
for (const [bindingName, value] of Object.entries(bindings)) {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
136
|
+
const {bindingLayout, isShadowedAlias} = resolveGroupBinding(
|
|
137
|
+
bindingName,
|
|
138
|
+
bindings,
|
|
139
|
+
shaderLayout,
|
|
140
|
+
group
|
|
141
|
+
) || {bindingLayout: null, isShadowedAlias: false};
|
|
70
142
|
|
|
71
143
|
// Mirror the WebGL path: when both `foo` and `fooUniforms` exist in the bindings map,
|
|
72
144
|
// prefer the exact shader binding name and ignore the alias entry.
|
|
73
|
-
if (!isShadowedAlias && bindingLayout
|
|
145
|
+
if (!isShadowedAlias && bindingLayout) {
|
|
74
146
|
const entry = bindingLayout
|
|
75
147
|
? getBindGroupEntry(value, bindingLayout.location, undefined, bindingName)
|
|
76
148
|
: null;
|
|
@@ -139,3 +211,65 @@ function getBindGroupEntry(
|
|
|
139
211
|
log.warn(`invalid binding ${bindingName}`, binding);
|
|
140
212
|
return null;
|
|
141
213
|
}
|
|
214
|
+
|
|
215
|
+
function getExpectedBindingsForGroup(
|
|
216
|
+
shaderLayout: AnyShaderLayout,
|
|
217
|
+
group: number
|
|
218
|
+
): BindGroupBindingSummary[] {
|
|
219
|
+
return shaderLayout.bindings
|
|
220
|
+
.filter(bindingLayout => bindingLayout.group === group)
|
|
221
|
+
.map(bindingLayout => toBindingSummary(bindingLayout))
|
|
222
|
+
.sort(compareBindingSummaries);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function resolveGroupBinding(
|
|
226
|
+
bindingName: string,
|
|
227
|
+
bindings: Bindings,
|
|
228
|
+
shaderLayout: AnyShaderLayout,
|
|
229
|
+
group: number
|
|
230
|
+
): {bindingLayout: BindingDeclaration; isShadowedAlias: boolean} | null {
|
|
231
|
+
const exactBindingLayout = shaderLayout.bindings.find(binding => binding.name === bindingName);
|
|
232
|
+
const bindingLayout =
|
|
233
|
+
exactBindingLayout || getShaderLayoutBinding(shaderLayout, bindingName, {ignoreWarnings: true});
|
|
234
|
+
const isShadowedAlias =
|
|
235
|
+
!exactBindingLayout && bindingLayout ? bindingLayout.name in bindings : false;
|
|
236
|
+
|
|
237
|
+
if (isShadowedAlias || !bindingLayout || bindingLayout.group !== group) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return {bindingLayout, isShadowedAlias};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function toBindingSummary(bindingLayout: BindingDeclaration): BindGroupBindingSummary {
|
|
245
|
+
return {
|
|
246
|
+
name: bindingLayout.name,
|
|
247
|
+
location: bindingLayout.location,
|
|
248
|
+
type: bindingLayout.type
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function getUnexpectedEntrySummary(entry: GPUBindGroupEntry): BindGroupBindingSummary {
|
|
253
|
+
return {
|
|
254
|
+
name: '?',
|
|
255
|
+
location: entry.binding,
|
|
256
|
+
type: 'unknown'
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function compareBindingSummaries(
|
|
261
|
+
left: BindGroupBindingSummary,
|
|
262
|
+
right: BindGroupBindingSummary
|
|
263
|
+
): number {
|
|
264
|
+
if (left.location !== right.location) {
|
|
265
|
+
return left.location - right.location;
|
|
266
|
+
}
|
|
267
|
+
return left.name.localeCompare(right.name);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function formatBindingSummaryList(bindings: BindGroupBindingSummary[]): string {
|
|
271
|
+
if (bindings.length === 0) {
|
|
272
|
+
return 'none';
|
|
273
|
+
}
|
|
274
|
+
return bindings.map(binding => `${binding.name}@${binding.location}`).join(', ');
|
|
275
|
+
}
|
|
@@ -56,12 +56,14 @@ export class WebGPURenderPipeline extends RenderPipeline {
|
|
|
56
56
|
this.device.pushErrorScope('validation');
|
|
57
57
|
this.handle = this.device.handle.createRenderPipeline(descriptor);
|
|
58
58
|
this.device.popErrorScope((error: GPUError) => {
|
|
59
|
+
this.linkStatus = 'error';
|
|
59
60
|
this.device.reportError(new Error(`${this} creation failed:\n"${error.message}"`), this)();
|
|
60
61
|
this.device.debug();
|
|
61
62
|
});
|
|
62
63
|
}
|
|
63
64
|
this.descriptor = descriptor;
|
|
64
65
|
this.handle.label = this.props.id;
|
|
66
|
+
this.linkStatus = 'success';
|
|
65
67
|
|
|
66
68
|
// Note: Often the same shader in WebGPU
|
|
67
69
|
this.vs = props.vs as WebGPUShader;
|
|
@@ -117,6 +119,11 @@ export class WebGPURenderPipeline extends RenderPipeline {
|
|
|
117
119
|
_bindGroupCacheKeys?: Partial<Record<number, object>>;
|
|
118
120
|
uniforms?: Record<string, unknown>;
|
|
119
121
|
}): boolean {
|
|
122
|
+
if (this.isErrored) {
|
|
123
|
+
log.info(2, `RenderPipeline:${this.id}.draw() aborted - pipeline initialization failed`)();
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
120
127
|
const webgpuRenderPass = options.renderPass as WebGPURenderPass;
|
|
121
128
|
const instanceCount =
|
|
122
129
|
options.instanceCount && options.instanceCount > 0 ? options.instanceCount : 1;
|
|
@@ -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 {
|
|
@@ -128,10 +128,9 @@ export class WebGPUDevice extends Device {
|
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
// "Context" loss handling
|
|
131
|
-
this.lost =
|
|
132
|
-
const lostInfo = await this.handle.lost;
|
|
131
|
+
this.lost = this.handle.lost.then(lostInfo => {
|
|
133
132
|
this._isLost = true;
|
|
134
|
-
|
|
133
|
+
return {reason: 'destroyed', message: lostInfo.message};
|
|
135
134
|
});
|
|
136
135
|
|
|
137
136
|
// Note: WebGPU devices can be created without a canvas, for compute shader purposes
|
|
@@ -259,16 +258,25 @@ export class WebGPUDevice extends Device {
|
|
|
259
258
|
bindGroupLayout: unknown,
|
|
260
259
|
shaderLayout: ShaderLayout | ComputeShaderLayout,
|
|
261
260
|
bindings: Bindings,
|
|
262
|
-
group: number
|
|
261
|
+
group: number,
|
|
262
|
+
label?: string
|
|
263
263
|
): GPUBindGroup | null {
|
|
264
264
|
if (Object.keys(bindings).length === 0) {
|
|
265
265
|
return this.handle.createBindGroup({
|
|
266
|
+
label,
|
|
266
267
|
layout: bindGroupLayout as GPUBindGroupLayout,
|
|
267
268
|
entries: []
|
|
268
269
|
});
|
|
269
270
|
}
|
|
270
271
|
|
|
271
|
-
return getBindGroup(
|
|
272
|
+
return getBindGroup(
|
|
273
|
+
this,
|
|
274
|
+
bindGroupLayout as GPUBindGroupLayout,
|
|
275
|
+
shaderLayout,
|
|
276
|
+
bindings,
|
|
277
|
+
group,
|
|
278
|
+
label
|
|
279
|
+
);
|
|
272
280
|
}
|
|
273
281
|
|
|
274
282
|
submit(commandBuffer?: WebGPUCommandBuffer): void {
|
|
@@ -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 {PresentationContextProps, TextureFormatDepthStencil} from '@luma.gl/core';
|