@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,11 +2,26 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import type {
|
|
6
|
-
|
|
5
|
+
import type {
|
|
6
|
+
Binding,
|
|
7
|
+
BindingDeclaration,
|
|
8
|
+
Bindings,
|
|
9
|
+
ComputeShaderLayout,
|
|
10
|
+
ShaderLayout
|
|
11
|
+
} from '@luma.gl/core';
|
|
12
|
+
import {Buffer, Sampler, Texture, TextureView, getShaderLayoutBinding, log} from '@luma.gl/core';
|
|
13
|
+
import type {WebGPUDevice} from '../webgpu-device';
|
|
7
14
|
import type {WebGPUBuffer} from '../resources/webgpu-buffer';
|
|
8
15
|
import type {WebGPUSampler} from '../resources/webgpu-sampler';
|
|
9
16
|
import type {WebGPUTexture} from '../resources/webgpu-texture';
|
|
17
|
+
import type {WebGPUTextureView} from '../resources/webgpu-texture-view';
|
|
18
|
+
|
|
19
|
+
type AnyShaderLayout = ShaderLayout | ComputeShaderLayout;
|
|
20
|
+
type BindGroupBindingSummary = {
|
|
21
|
+
name: string;
|
|
22
|
+
location: number;
|
|
23
|
+
type: string;
|
|
24
|
+
};
|
|
10
25
|
|
|
11
26
|
/**
|
|
12
27
|
* Create a WebGPU "bind group layout" from an array of luma.gl bindings
|
|
@@ -15,7 +30,7 @@ import type {WebGPUTexture} from '../resources/webgpu-texture';
|
|
|
15
30
|
export function makeBindGroupLayout(
|
|
16
31
|
device: GPUDevice,
|
|
17
32
|
layout: GPUBindGroupLayout,
|
|
18
|
-
bindings:
|
|
33
|
+
bindings: Bindings
|
|
19
34
|
): GPUBindGroupLayout {
|
|
20
35
|
throw new Error('not implemented');
|
|
21
36
|
// return device.createBindGroupLayout({
|
|
@@ -28,39 +43,82 @@ export function makeBindGroupLayout(
|
|
|
28
43
|
* Create a WebGPU "bind group" from an array of luma.gl bindings
|
|
29
44
|
*/
|
|
30
45
|
export function getBindGroup(
|
|
31
|
-
device:
|
|
46
|
+
device: WebGPUDevice,
|
|
32
47
|
bindGroupLayout: GPUBindGroupLayout,
|
|
33
|
-
shaderLayout: ComputeShaderLayout,
|
|
34
|
-
bindings:
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
shaderLayout: ShaderLayout | ComputeShaderLayout,
|
|
49
|
+
bindings: Bindings,
|
|
50
|
+
group: number,
|
|
51
|
+
label?: string
|
|
52
|
+
): GPUBindGroup | null {
|
|
53
|
+
const entries = getBindGroupEntries(bindings, shaderLayout, group);
|
|
54
|
+
if (entries.length === 0) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
37
57
|
device.pushErrorScope('validation');
|
|
38
|
-
const bindGroup = device.createBindGroup({
|
|
58
|
+
const bindGroup = device.handle.createBindGroup({
|
|
59
|
+
label,
|
|
39
60
|
layout: bindGroupLayout,
|
|
40
61
|
entries
|
|
41
62
|
});
|
|
42
|
-
device.popErrorScope(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
63
|
+
device.popErrorScope((error: GPUError) => {
|
|
64
|
+
const summary = formatBindGroupCreationErrorSummary(shaderLayout, bindings, entries, group);
|
|
65
|
+
log.error(`bindGroup creation: ${summary}\nRaw WebGPU error: ${error.message}`, bindGroup)();
|
|
46
66
|
});
|
|
47
67
|
return bindGroup;
|
|
48
68
|
}
|
|
49
69
|
|
|
50
|
-
export function
|
|
51
|
-
shaderLayout:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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])
|
|
59
79
|
);
|
|
60
|
-
|
|
61
|
-
|
|
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)}`);
|
|
62
105
|
}
|
|
63
|
-
|
|
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}]`;
|
|
64
122
|
}
|
|
65
123
|
|
|
66
124
|
/**
|
|
@@ -68,29 +126,42 @@ export function getShaderLayoutBinding(
|
|
|
68
126
|
* @returns
|
|
69
127
|
*/
|
|
70
128
|
function getBindGroupEntries(
|
|
71
|
-
bindings:
|
|
72
|
-
shaderLayout:
|
|
129
|
+
bindings: Bindings,
|
|
130
|
+
shaderLayout: AnyShaderLayout,
|
|
131
|
+
group: number
|
|
73
132
|
): GPUBindGroupEntry[] {
|
|
74
133
|
const entries: GPUBindGroupEntry[] = [];
|
|
75
134
|
|
|
76
135
|
for (const [bindingName, value] of Object.entries(bindings)) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
136
|
+
const {bindingLayout, isShadowedAlias} = resolveGroupBinding(
|
|
137
|
+
bindingName,
|
|
138
|
+
bindings,
|
|
139
|
+
shaderLayout,
|
|
140
|
+
group
|
|
141
|
+
) || {bindingLayout: null, isShadowedAlias: false};
|
|
142
|
+
|
|
143
|
+
// Mirror the WebGL path: when both `foo` and `fooUniforms` exist in the bindings map,
|
|
144
|
+
// prefer the exact shader binding name and ignore the alias entry.
|
|
145
|
+
if (!isShadowedAlias && bindingLayout) {
|
|
146
|
+
const entry = bindingLayout
|
|
147
|
+
? getBindGroupEntry(value, bindingLayout.location, undefined, bindingName)
|
|
148
|
+
: null;
|
|
80
149
|
if (entry) {
|
|
81
150
|
entries.push(entry);
|
|
82
151
|
}
|
|
83
|
-
}
|
|
84
152
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
153
|
+
// TODO - hack to automatically bind samplers to supplied texture default samplers
|
|
154
|
+
if (value instanceof Texture) {
|
|
155
|
+
const samplerBindingLayout = getShaderLayoutBinding(shaderLayout, `${bindingName}Sampler`, {
|
|
156
|
+
ignoreWarnings: true
|
|
157
|
+
});
|
|
158
|
+
const samplerEntry = samplerBindingLayout
|
|
159
|
+
? samplerBindingLayout.group === group
|
|
160
|
+
? getBindGroupEntry(value, samplerBindingLayout.location, {sampler: true}, bindingName)
|
|
161
|
+
: null
|
|
162
|
+
: null;
|
|
163
|
+
if (samplerEntry) {
|
|
164
|
+
entries.push(samplerEntry);
|
|
94
165
|
}
|
|
95
166
|
}
|
|
96
167
|
}
|
|
@@ -102,7 +173,8 @@ function getBindGroupEntries(
|
|
|
102
173
|
function getBindGroupEntry(
|
|
103
174
|
binding: Binding,
|
|
104
175
|
index: number,
|
|
105
|
-
options?: {sampler?: boolean}
|
|
176
|
+
options?: {sampler?: boolean},
|
|
177
|
+
bindingName: string = 'unknown'
|
|
106
178
|
): GPUBindGroupEntry | null {
|
|
107
179
|
if (binding instanceof Buffer) {
|
|
108
180
|
return {
|
|
@@ -118,6 +190,12 @@ function getBindGroupEntry(
|
|
|
118
190
|
resource: (binding as WebGPUSampler).handle
|
|
119
191
|
};
|
|
120
192
|
}
|
|
193
|
+
if (binding instanceof TextureView) {
|
|
194
|
+
return {
|
|
195
|
+
binding: index,
|
|
196
|
+
resource: (binding as WebGPUTextureView).handle
|
|
197
|
+
};
|
|
198
|
+
}
|
|
121
199
|
if (binding instanceof Texture) {
|
|
122
200
|
if (options?.sampler) {
|
|
123
201
|
return {
|
|
@@ -130,6 +208,68 @@ function getBindGroupEntry(
|
|
|
130
208
|
resource: (binding as WebGPUTexture).view.handle
|
|
131
209
|
};
|
|
132
210
|
}
|
|
133
|
-
log.warn(`invalid binding ${
|
|
211
|
+
log.warn(`invalid binding ${bindingName}`, binding);
|
|
134
212
|
return null;
|
|
135
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
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
5
|
import type {ShaderLayout, BufferLayout, AttributeDeclaration, VertexFormat} from '@luma.gl/core';
|
|
6
|
-
import {log,
|
|
6
|
+
import {log, vertexFormatDecoder} from '@luma.gl/core';
|
|
7
7
|
// import {getAttributeInfosFromLayouts} from '@luma.gl/core';
|
|
8
8
|
|
|
9
9
|
/** Throw error on any WebGL-only vertex formats */
|
|
@@ -23,10 +23,12 @@ function getWebGPUVertexFormat(format: VertexFormat): GPUVertexFormat {
|
|
|
23
23
|
*/
|
|
24
24
|
export function getVertexBufferLayout(
|
|
25
25
|
shaderLayout: ShaderLayout,
|
|
26
|
-
bufferLayout: BufferLayout[]
|
|
26
|
+
bufferLayout: BufferLayout[],
|
|
27
|
+
options?: {pipelineId?: string}
|
|
27
28
|
): GPUVertexBufferLayout[] {
|
|
28
29
|
const vertexBufferLayouts: GPUVertexBufferLayout[] = [];
|
|
29
30
|
const usedAttributes = new Set<string>();
|
|
31
|
+
const shaderAttributes = shaderLayout.attributes || [];
|
|
30
32
|
|
|
31
33
|
// First handle any buffers mentioned in `bufferLayout`
|
|
32
34
|
for (const mapping of bufferLayout) {
|
|
@@ -44,7 +46,12 @@ export function getVertexBufferLayout(
|
|
|
44
46
|
// const arrayStride = mapping.byteStride; TODO
|
|
45
47
|
for (const attributeMapping of mapping.attributes) {
|
|
46
48
|
const attributeName = attributeMapping.attribute;
|
|
47
|
-
const attributeLayout = findAttributeLayout(
|
|
49
|
+
const attributeLayout = findAttributeLayout(
|
|
50
|
+
shaderLayout,
|
|
51
|
+
attributeName,
|
|
52
|
+
usedAttributes,
|
|
53
|
+
options
|
|
54
|
+
);
|
|
48
55
|
|
|
49
56
|
// @ts-ignore
|
|
50
57
|
const location: number = attributeLayout?.location;
|
|
@@ -59,15 +66,20 @@ export function getVertexBufferLayout(
|
|
|
59
66
|
shaderLocation: location
|
|
60
67
|
});
|
|
61
68
|
|
|
62
|
-
byteStride += getVertexFormatInfo(format).byteLength;
|
|
69
|
+
byteStride += vertexFormatDecoder.getVertexFormatInfo(format).byteLength;
|
|
63
70
|
}
|
|
64
71
|
// non-interleaved mapping (just set offset and stride)
|
|
65
72
|
} else {
|
|
66
|
-
const attributeLayout = findAttributeLayout(
|
|
73
|
+
const attributeLayout = findAttributeLayout(
|
|
74
|
+
shaderLayout,
|
|
75
|
+
mapping.name,
|
|
76
|
+
usedAttributes,
|
|
77
|
+
options
|
|
78
|
+
);
|
|
67
79
|
if (!attributeLayout) {
|
|
68
80
|
continue; // eslint-disable-line no-continue
|
|
69
81
|
}
|
|
70
|
-
byteStride = getVertexFormatInfo(format).byteLength;
|
|
82
|
+
byteStride = vertexFormatDecoder.getVertexFormatInfo(format).byteLength;
|
|
71
83
|
|
|
72
84
|
stepMode =
|
|
73
85
|
attributeLayout.stepMode ||
|
|
@@ -89,10 +101,10 @@ export function getVertexBufferLayout(
|
|
|
89
101
|
}
|
|
90
102
|
|
|
91
103
|
// Add any non-mapped attributes - TODO - avoid hardcoded types
|
|
92
|
-
for (const attribute of
|
|
104
|
+
for (const attribute of shaderAttributes) {
|
|
93
105
|
if (!usedAttributes.has(attribute.name)) {
|
|
94
106
|
vertexBufferLayouts.push({
|
|
95
|
-
arrayStride: getVertexFormatInfo('float32x3').byteLength,
|
|
107
|
+
arrayStride: vertexFormatDecoder.getVertexFormatInfo('float32x3').byteLength,
|
|
96
108
|
stepMode:
|
|
97
109
|
attribute.stepMode || (attribute.name.startsWith('instance') ? 'instance' : 'vertex'),
|
|
98
110
|
attributes: [
|
|
@@ -124,6 +136,7 @@ export function getBufferSlots(
|
|
|
124
136
|
bufferLayout: BufferLayout[]
|
|
125
137
|
): Record<string, number> {
|
|
126
138
|
const usedAttributes = new Set<string>();
|
|
139
|
+
const shaderAttributes = shaderLayout.attributes || [];
|
|
127
140
|
let bufferSlot = 0;
|
|
128
141
|
const bufferSlots: Record<string, number> = {};
|
|
129
142
|
|
|
@@ -142,7 +155,7 @@ export function getBufferSlots(
|
|
|
142
155
|
}
|
|
143
156
|
|
|
144
157
|
// Add any non-mapped attributes
|
|
145
|
-
for (const attribute of
|
|
158
|
+
for (const attribute of shaderAttributes) {
|
|
146
159
|
if (!usedAttributes.has(attribute.name)) {
|
|
147
160
|
bufferSlots[attribute.name] = bufferSlot++;
|
|
148
161
|
}
|
|
@@ -159,11 +172,17 @@ export function getBufferSlots(
|
|
|
159
172
|
function findAttributeLayout(
|
|
160
173
|
shaderLayout: ShaderLayout,
|
|
161
174
|
name: string,
|
|
162
|
-
attributeNames?: Set<string
|
|
175
|
+
attributeNames?: Set<string>,
|
|
176
|
+
options?: {pipelineId?: string}
|
|
163
177
|
): AttributeDeclaration | null {
|
|
164
|
-
const attribute = shaderLayout.attributes
|
|
178
|
+
const attribute = shaderLayout.attributes?.find(attribute_ => attribute_.name === name);
|
|
165
179
|
if (!attribute) {
|
|
166
|
-
|
|
180
|
+
const pipelineContext = options?.pipelineId
|
|
181
|
+
? `RenderPipeline(${options.pipelineId})`
|
|
182
|
+
: 'RenderPipeline';
|
|
183
|
+
log.warn(
|
|
184
|
+
`${pipelineContext}: Ignoring "${name}" attribute, since it is not present in shader layout.`
|
|
185
|
+
)();
|
|
167
186
|
return null;
|
|
168
187
|
}
|
|
169
188
|
if (attributeNames) {
|
|
@@ -65,6 +65,8 @@ export const PARAMETER_TABLE: Record<keyof Parameters, Function> = {
|
|
|
65
65
|
depthStencil.format = value;
|
|
66
66
|
},
|
|
67
67
|
|
|
68
|
+
clearDepth: notSupported,
|
|
69
|
+
|
|
68
70
|
depthBias: (_: keyof Parameters, value: any, descriptor: GPURenderPipelineDescriptor) => {
|
|
69
71
|
const depthStencil = addDepthStencil(descriptor);
|
|
70
72
|
depthStencil.depthBias = value;
|
|
@@ -5,20 +5,28 @@
|
|
|
5
5
|
import {log, Buffer, type BufferProps, type BufferMapCallback} from '@luma.gl/core';
|
|
6
6
|
import {type WebGPUDevice} from '../webgpu-device';
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* WebGPU implementation of Buffer
|
|
10
|
+
* For byte alignment requirements see:
|
|
11
|
+
* @see https://www.w3.org/TR/webgpu/#dom-gpubuffer-mapasync
|
|
12
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/mapAsync
|
|
13
|
+
*/
|
|
8
14
|
export class WebGPUBuffer extends Buffer {
|
|
9
15
|
readonly device: WebGPUDevice;
|
|
10
16
|
readonly handle: GPUBuffer;
|
|
11
17
|
readonly byteLength: number;
|
|
18
|
+
readonly paddedByteLength: number;
|
|
12
19
|
|
|
13
20
|
constructor(device: WebGPUDevice, props: BufferProps) {
|
|
14
21
|
super(device, props);
|
|
15
22
|
this.device = device;
|
|
16
23
|
|
|
17
24
|
this.byteLength = props.byteLength || props.data?.byteLength || 0;
|
|
25
|
+
this.paddedByteLength = Math.ceil(this.byteLength / 4) * 4;
|
|
18
26
|
const mappedAtCreation = Boolean(this.props.onMapped || props.data);
|
|
19
27
|
|
|
20
28
|
// WebGPU buffers must be aligned to 4 bytes
|
|
21
|
-
const size =
|
|
29
|
+
const size = this.paddedByteLength;
|
|
22
30
|
|
|
23
31
|
this.device.pushErrorScope('out-of-memory');
|
|
24
32
|
this.device.pushErrorScope('validation');
|
|
@@ -59,12 +67,27 @@ export class WebGPUBuffer extends Buffer {
|
|
|
59
67
|
this.device.reportError(new Error(`${this} creation failed ${error.message}`), this)();
|
|
60
68
|
this.device.debug();
|
|
61
69
|
});
|
|
70
|
+
|
|
71
|
+
if (!this.props.handle) {
|
|
72
|
+
this.trackAllocatedMemory(size);
|
|
73
|
+
} else {
|
|
74
|
+
this.trackReferencedMemory(size, 'Buffer');
|
|
75
|
+
}
|
|
62
76
|
}
|
|
63
77
|
|
|
64
78
|
override destroy(): void {
|
|
65
|
-
this.handle
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
if (!this.destroyed && this.handle) {
|
|
80
|
+
this.removeStats();
|
|
81
|
+
if (!this.props.handle) {
|
|
82
|
+
this.trackDeallocatedMemory();
|
|
83
|
+
this.handle.destroy();
|
|
84
|
+
} else {
|
|
85
|
+
this.trackDeallocatedReferencedMemory('Buffer');
|
|
86
|
+
}
|
|
87
|
+
this.destroyed = true;
|
|
88
|
+
// @ts-expect-error readonly
|
|
89
|
+
this.handle = null;
|
|
90
|
+
}
|
|
68
91
|
}
|
|
69
92
|
|
|
70
93
|
write(data: ArrayBufferLike | ArrayBufferView | SharedArrayBuffer, byteOffset = 0) {
|
|
@@ -92,10 +115,11 @@ export class WebGPUBuffer extends Buffer {
|
|
|
92
115
|
byteOffset: number = 0,
|
|
93
116
|
byteLength: number = this.byteLength - byteOffset
|
|
94
117
|
): Promise<void> {
|
|
118
|
+
const alignedByteLength = Math.ceil(byteLength / 4) * 4;
|
|
95
119
|
// Unless the application created and supplied a mappable buffer, a staging buffer is needed
|
|
96
120
|
const isMappable = (this.usage & Buffer.MAP_WRITE) !== 0;
|
|
97
121
|
const mappableBuffer: WebGPUBuffer | null = !isMappable
|
|
98
|
-
? this._getMappableBuffer(Buffer.MAP_WRITE | Buffer.COPY_SRC, 0, this.
|
|
122
|
+
? this._getMappableBuffer(Buffer.MAP_WRITE | Buffer.COPY_SRC, 0, this.paddedByteLength)
|
|
99
123
|
: null;
|
|
100
124
|
|
|
101
125
|
const writeBuffer = mappableBuffer || this;
|
|
@@ -105,13 +129,15 @@ export class WebGPUBuffer extends Buffer {
|
|
|
105
129
|
this.device.pushErrorScope('validation');
|
|
106
130
|
try {
|
|
107
131
|
await this.device.handle.queue.onSubmittedWorkDone();
|
|
108
|
-
await writeBuffer.handle.mapAsync(GPUMapMode.WRITE, byteOffset,
|
|
109
|
-
const
|
|
132
|
+
await writeBuffer.handle.mapAsync(GPUMapMode.WRITE, byteOffset, alignedByteLength);
|
|
133
|
+
const mappedRange = writeBuffer.handle.getMappedRange(byteOffset, alignedByteLength);
|
|
134
|
+
const arrayBuffer = mappedRange.slice(0, byteLength);
|
|
110
135
|
// eslint-disable-next-line @typescript-eslint/await-thenable
|
|
111
136
|
await callback(arrayBuffer, 'mapped');
|
|
137
|
+
new Uint8Array(mappedRange).set(new Uint8Array(arrayBuffer), 0);
|
|
112
138
|
writeBuffer.handle.unmap();
|
|
113
139
|
if (mappableBuffer) {
|
|
114
|
-
this._copyBuffer(mappableBuffer, byteOffset,
|
|
140
|
+
this._copyBuffer(mappableBuffer, byteOffset, alignedByteLength);
|
|
115
141
|
}
|
|
116
142
|
} finally {
|
|
117
143
|
this.device.popErrorScope((error: GPUError) => {
|
|
@@ -138,17 +164,33 @@ export class WebGPUBuffer extends Buffer {
|
|
|
138
164
|
byteOffset = 0,
|
|
139
165
|
byteLength = this.byteLength - byteOffset
|
|
140
166
|
): Promise<T> {
|
|
167
|
+
const requestedEnd = byteOffset + byteLength;
|
|
168
|
+
if (requestedEnd > this.byteLength) {
|
|
169
|
+
throw new Error('Mapping range exceeds buffer size');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let mappedByteOffset = byteOffset;
|
|
173
|
+
let mappedByteLength = byteLength;
|
|
174
|
+
let sliceByteOffset = 0;
|
|
175
|
+
let lifetime: 'mapped' | 'copied' = 'mapped';
|
|
176
|
+
|
|
177
|
+
// WebGPU mapAsync requires 8-byte offsets and 4-byte lengths.
|
|
141
178
|
if (byteOffset % 8 !== 0 || byteLength % 4 !== 0) {
|
|
142
|
-
|
|
179
|
+
mappedByteOffset = Math.floor(byteOffset / 8) * 8;
|
|
180
|
+
const alignedEnd = Math.ceil(requestedEnd / 4) * 4;
|
|
181
|
+
mappedByteLength = alignedEnd - mappedByteOffset;
|
|
182
|
+
sliceByteOffset = byteOffset - mappedByteOffset;
|
|
183
|
+
lifetime = 'copied';
|
|
143
184
|
}
|
|
144
|
-
|
|
185
|
+
|
|
186
|
+
if (mappedByteOffset + mappedByteLength > this.paddedByteLength) {
|
|
145
187
|
throw new Error('Mapping range exceeds buffer size');
|
|
146
188
|
}
|
|
147
189
|
|
|
148
190
|
// Unless the application created and supplied a mappable buffer, a staging buffer is needed
|
|
149
191
|
const isMappable = (this.usage & Buffer.MAP_READ) !== 0;
|
|
150
192
|
const mappableBuffer: WebGPUBuffer | null = !isMappable
|
|
151
|
-
? this._getMappableBuffer(Buffer.MAP_READ | Buffer.COPY_DST, 0, this.
|
|
193
|
+
? this._getMappableBuffer(Buffer.MAP_READ | Buffer.COPY_DST, 0, this.paddedByteLength)
|
|
152
194
|
: null;
|
|
153
195
|
|
|
154
196
|
const readBuffer = mappableBuffer || this;
|
|
@@ -158,12 +200,16 @@ export class WebGPUBuffer extends Buffer {
|
|
|
158
200
|
try {
|
|
159
201
|
await this.device.handle.queue.onSubmittedWorkDone();
|
|
160
202
|
if (mappableBuffer) {
|
|
161
|
-
mappableBuffer._copyBuffer(this);
|
|
203
|
+
mappableBuffer._copyBuffer(this, mappedByteOffset, mappedByteLength);
|
|
162
204
|
}
|
|
163
|
-
await readBuffer.handle.mapAsync(GPUMapMode.READ,
|
|
164
|
-
const arrayBuffer = readBuffer.handle.getMappedRange(
|
|
205
|
+
await readBuffer.handle.mapAsync(GPUMapMode.READ, mappedByteOffset, mappedByteLength);
|
|
206
|
+
const arrayBuffer = readBuffer.handle.getMappedRange(mappedByteOffset, mappedByteLength);
|
|
207
|
+
const mappedRange =
|
|
208
|
+
lifetime === 'mapped'
|
|
209
|
+
? arrayBuffer
|
|
210
|
+
: arrayBuffer.slice(sliceByteOffset, sliceByteOffset + byteLength);
|
|
165
211
|
// eslint-disable-next-line @typescript-eslint/await-thenable
|
|
166
|
-
const result = await callback(
|
|
212
|
+
const result = await callback(mappedRange, lifetime);
|
|
167
213
|
readBuffer.handle.unmap();
|
|
168
214
|
return result;
|
|
169
215
|
} finally {
|
|
@@ -13,7 +13,7 @@ export class WebGPUCommandBuffer extends CommandBuffer {
|
|
|
13
13
|
readonly handle: GPUCommandBuffer;
|
|
14
14
|
|
|
15
15
|
constructor(commandEncoder: WebGPUCommandEncoder, props: CommandBufferProps) {
|
|
16
|
-
super(commandEncoder.device,
|
|
16
|
+
super(commandEncoder.device, props);
|
|
17
17
|
this.device = commandEncoder.device;
|
|
18
18
|
this.handle =
|
|
19
19
|
this.props.handle ||
|