@luma.gl/webgpu 9.2.5 → 9.3.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/helpers/cpu-hotspot-profiler.d.ts +54 -0
- package/dist/adapter/helpers/cpu-hotspot-profiler.d.ts.map +1 -0
- package/dist/adapter/helpers/cpu-hotspot-profiler.js +26 -0
- package/dist/adapter/helpers/cpu-hotspot-profiler.js.map +1 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts +7 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts.map +1 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.js +490 -0
- package/dist/adapter/helpers/generate-mipmaps-webgpu.js.map +1 -0
- package/dist/adapter/helpers/get-bind-group.d.ts +4 -6
- package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
- package/dist/adapter/helpers/get-bind-group.js +39 -32
- package/dist/adapter/helpers/get-bind-group.js.map +1 -1
- package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts +3 -1
- package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts.map +1 -1
- package/dist/adapter/helpers/get-vertex-buffer-layout.js +17 -12
- package/dist/adapter/helpers/get-vertex-buffer-layout.js.map +1 -1
- package/dist/adapter/helpers/webgpu-parameters.d.ts.map +1 -1
- package/dist/adapter/helpers/webgpu-parameters.js +1 -0
- package/dist/adapter/helpers/webgpu-parameters.js.map +1 -1
- package/dist/adapter/resources/webgpu-buffer.d.ts +7 -0
- package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-buffer.js +58 -15
- package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
- package/dist/adapter/resources/webgpu-command-buffer.js +1 -1
- package/dist/adapter/resources/webgpu-command-buffer.js.map +1 -1
- package/dist/adapter/resources/webgpu-command-encoder.d.ts +7 -16
- package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-command-encoder.js +89 -32
- package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pass.d.ts +3 -3
- package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pass.js +30 -12
- package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pipeline.d.ts +7 -9
- package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-compute-pipeline.js +30 -17
- package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
- package/dist/adapter/resources/webgpu-fence.d.ts +13 -0
- package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -0
- package/dist/adapter/resources/webgpu-fence.js +33 -0
- package/dist/adapter/resources/webgpu-fence.js.map +1 -0
- package/dist/adapter/resources/webgpu-framebuffer.d.ts +6 -0
- package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-framebuffer.js +16 -0
- package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.d.ts +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.js +11 -18
- package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
- package/dist/adapter/resources/webgpu-query-set.d.ts +33 -4
- package/dist/adapter/resources/webgpu-query-set.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-query-set.js +145 -4
- package/dist/adapter/resources/webgpu-query-set.js.map +1 -1
- package/dist/adapter/resources/webgpu-render-pass.d.ts +6 -3
- package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-render-pass.js +78 -34
- package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
- package/dist/adapter/resources/webgpu-render-pipeline.d.ts +14 -10
- package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-render-pipeline.js +56 -35
- package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
- package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-sampler.js +4 -0
- package/dist/adapter/resources/webgpu-sampler.js.map +1 -1
- package/dist/adapter/resources/webgpu-shader.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-shader.js +17 -1
- package/dist/adapter/resources/webgpu-shader.js.map +1 -1
- package/dist/adapter/resources/webgpu-texture-view.d.ts +6 -0
- package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-texture-view.js +47 -11
- package/dist/adapter/resources/webgpu-texture-view.js.map +1 -1
- package/dist/adapter/resources/webgpu-texture.d.ts +25 -3
- package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-texture.js +211 -43
- package/dist/adapter/resources/webgpu-texture.js.map +1 -1
- package/dist/adapter/resources/webgpu-vertex-array.js +1 -1
- package/dist/adapter/resources/webgpu-vertex-array.js.map +1 -1
- package/dist/adapter/webgpu-adapter.d.ts.map +1 -1
- package/dist/adapter/webgpu-adapter.js +34 -34
- package/dist/adapter/webgpu-adapter.js.map +1 -1
- package/dist/adapter/webgpu-canvas-context.d.ts +6 -3
- package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
- package/dist/adapter/webgpu-canvas-context.js +90 -30
- package/dist/adapter/webgpu-canvas-context.js.map +1 -1
- package/dist/adapter/webgpu-device.d.ts +12 -2
- package/dist/adapter/webgpu-device.d.ts.map +1 -1
- package/dist/adapter/webgpu-device.js +173 -16
- package/dist/adapter/webgpu-device.js.map +1 -1
- package/dist/adapter/webgpu-presentation-context.d.ts +25 -0
- package/dist/adapter/webgpu-presentation-context.d.ts.map +1 -0
- package/dist/adapter/webgpu-presentation-context.js +144 -0
- package/dist/adapter/webgpu-presentation-context.js.map +1 -0
- package/dist/dist.dev.js +8070 -547
- package/dist/dist.min.js +169 -6
- package/dist/index.cjs +1929 -410
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/wgsl/get-shader-layout-wgsl.d.ts +8 -0
- package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -0
- package/dist/wgsl/get-shader-layout-wgsl.js +144 -0
- package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -0
- package/package.json +6 -5
- package/src/adapter/helpers/cpu-hotspot-profiler.ts +70 -0
- package/src/adapter/helpers/generate-mipmaps-webgpu.ts +583 -0
- package/src/adapter/helpers/get-bind-group.ts +52 -46
- package/src/adapter/helpers/get-vertex-buffer-layout.ts +31 -12
- package/src/adapter/helpers/webgpu-parameters.ts +2 -0
- package/src/adapter/resources/webgpu-buffer.ts +61 -15
- package/src/adapter/resources/webgpu-command-buffer.ts +1 -1
- package/src/adapter/resources/webgpu-command-encoder.ts +129 -50
- package/src/adapter/resources/webgpu-compute-pass.ts +48 -13
- package/src/adapter/resources/webgpu-compute-pipeline.ts +49 -18
- package/src/adapter/resources/webgpu-fence.ts +38 -0
- package/src/adapter/resources/webgpu-framebuffer.ts +21 -0
- package/src/adapter/resources/webgpu-pipeline-layout.ts +18 -17
- package/src/adapter/resources/webgpu-query-set.ts +185 -9
- package/src/adapter/resources/webgpu-render-pass.ts +92 -40
- package/src/adapter/resources/webgpu-render-pipeline.ts +83 -44
- package/src/adapter/resources/webgpu-sampler.ts +5 -0
- package/src/adapter/resources/webgpu-shader.ts +16 -1
- package/src/adapter/resources/webgpu-texture-view.ts +51 -11
- package/src/adapter/resources/webgpu-texture.ts +281 -101
- package/src/adapter/resources/webgpu-vertex-array.ts +1 -1
- package/src/adapter/webgpu-adapter.ts +40 -42
- package/src/adapter/webgpu-canvas-context.ts +107 -40
- package/src/adapter/webgpu-device.ts +231 -21
- package/src/adapter/webgpu-presentation-context.ts +180 -0
- package/src/index.ts +3 -0
- package/src/wgsl/get-shader-layout-wgsl.ts +165 -0
- package/dist/adapter/helpers/accessor-to-format.d.ts +0 -1
- package/dist/adapter/helpers/accessor-to-format.d.ts.map +0 -1
- package/dist/adapter/helpers/accessor-to-format.js +0 -105
- package/dist/adapter/helpers/accessor-to-format.js.map +0 -1
- package/src/adapter/helpers/accessor-to-format.ts +0 -104
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import type {
|
|
6
|
-
import {Buffer, Sampler, Texture, log} from '@luma.gl/core';
|
|
5
|
+
import type {Binding, Bindings, ComputeShaderLayout, ShaderLayout} from '@luma.gl/core';
|
|
6
|
+
import {Buffer, Sampler, Texture, TextureView, getShaderLayoutBinding, log} from '@luma.gl/core';
|
|
7
|
+
import type {WebGPUDevice} from '../webgpu-device';
|
|
7
8
|
import type {WebGPUBuffer} from '../resources/webgpu-buffer';
|
|
8
9
|
import type {WebGPUSampler} from '../resources/webgpu-sampler';
|
|
9
10
|
import type {WebGPUTexture} from '../resources/webgpu-texture';
|
|
11
|
+
import type {WebGPUTextureView} from '../resources/webgpu-texture-view';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Create a WebGPU "bind group layout" from an array of luma.gl bindings
|
|
@@ -15,7 +17,7 @@ import type {WebGPUTexture} from '../resources/webgpu-texture';
|
|
|
15
17
|
export function makeBindGroupLayout(
|
|
16
18
|
device: GPUDevice,
|
|
17
19
|
layout: GPUBindGroupLayout,
|
|
18
|
-
bindings:
|
|
20
|
+
bindings: Bindings
|
|
19
21
|
): GPUBindGroupLayout {
|
|
20
22
|
throw new Error('not implemented');
|
|
21
23
|
// return device.createBindGroupLayout({
|
|
@@ -28,69 +30,66 @@ export function makeBindGroupLayout(
|
|
|
28
30
|
* Create a WebGPU "bind group" from an array of luma.gl bindings
|
|
29
31
|
*/
|
|
30
32
|
export function getBindGroup(
|
|
31
|
-
device:
|
|
33
|
+
device: WebGPUDevice,
|
|
32
34
|
bindGroupLayout: GPUBindGroupLayout,
|
|
33
|
-
shaderLayout: ComputeShaderLayout,
|
|
34
|
-
bindings:
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
shaderLayout: ShaderLayout | ComputeShaderLayout,
|
|
36
|
+
bindings: Bindings,
|
|
37
|
+
group: number
|
|
38
|
+
): GPUBindGroup | null {
|
|
39
|
+
const entries = getBindGroupEntries(bindings, shaderLayout, group);
|
|
40
|
+
if (entries.length === 0) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
37
43
|
device.pushErrorScope('validation');
|
|
38
|
-
const bindGroup = device.createBindGroup({
|
|
44
|
+
const bindGroup = device.handle.createBindGroup({
|
|
39
45
|
layout: bindGroupLayout,
|
|
40
46
|
entries
|
|
41
47
|
});
|
|
42
|
-
device.popErrorScope(
|
|
43
|
-
|
|
44
|
-
log.error(`bindGroup creation: ${error.message}`, bindGroup)();
|
|
45
|
-
}
|
|
48
|
+
device.popErrorScope((error: GPUError) => {
|
|
49
|
+
log.error(`bindGroup creation: ${error.message}`, bindGroup)();
|
|
46
50
|
});
|
|
47
51
|
return bindGroup;
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
export function getShaderLayoutBinding(
|
|
51
|
-
shaderLayout: ComputeShaderLayout,
|
|
52
|
-
bindingName: string,
|
|
53
|
-
options?: {ignoreWarnings?: boolean}
|
|
54
|
-
): BindingDeclaration | null {
|
|
55
|
-
const bindingLayout = shaderLayout.bindings.find(
|
|
56
|
-
binding =>
|
|
57
|
-
binding.name === bindingName ||
|
|
58
|
-
`${binding.name.toLocaleLowerCase()}uniforms` === bindingName.toLocaleLowerCase()
|
|
59
|
-
);
|
|
60
|
-
if (!bindingLayout && !options?.ignoreWarnings) {
|
|
61
|
-
log.warn(`Binding ${bindingName} not set: Not found in shader layout.`)();
|
|
62
|
-
}
|
|
63
|
-
return bindingLayout || null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
54
|
/**
|
|
67
55
|
* @param bindings
|
|
68
56
|
* @returns
|
|
69
57
|
*/
|
|
70
58
|
function getBindGroupEntries(
|
|
71
|
-
bindings:
|
|
72
|
-
shaderLayout: ComputeShaderLayout
|
|
59
|
+
bindings: Bindings,
|
|
60
|
+
shaderLayout: ShaderLayout | ComputeShaderLayout,
|
|
61
|
+
group: number
|
|
73
62
|
): GPUBindGroupEntry[] {
|
|
74
63
|
const entries: GPUBindGroupEntry[] = [];
|
|
75
64
|
|
|
76
65
|
for (const [bindingName, value] of Object.entries(bindings)) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
66
|
+
const exactBindingLayout = shaderLayout.bindings.find(binding => binding.name === bindingName);
|
|
67
|
+
const bindingLayout = exactBindingLayout || getShaderLayoutBinding(shaderLayout, bindingName);
|
|
68
|
+
const isShadowedAlias =
|
|
69
|
+
!exactBindingLayout && bindingLayout ? bindingLayout.name in bindings : false;
|
|
70
|
+
|
|
71
|
+
// Mirror the WebGL path: when both `foo` and `fooUniforms` exist in the bindings map,
|
|
72
|
+
// prefer the exact shader binding name and ignore the alias entry.
|
|
73
|
+
if (!isShadowedAlias && bindingLayout?.group === group) {
|
|
74
|
+
const entry = bindingLayout
|
|
75
|
+
? getBindGroupEntry(value, bindingLayout.location, undefined, bindingName)
|
|
76
|
+
: null;
|
|
80
77
|
if (entry) {
|
|
81
78
|
entries.push(entry);
|
|
82
79
|
}
|
|
83
|
-
}
|
|
84
80
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
81
|
+
// TODO - hack to automatically bind samplers to supplied texture default samplers
|
|
82
|
+
if (value instanceof Texture) {
|
|
83
|
+
const samplerBindingLayout = getShaderLayoutBinding(shaderLayout, `${bindingName}Sampler`, {
|
|
84
|
+
ignoreWarnings: true
|
|
85
|
+
});
|
|
86
|
+
const samplerEntry = samplerBindingLayout
|
|
87
|
+
? samplerBindingLayout.group === group
|
|
88
|
+
? getBindGroupEntry(value, samplerBindingLayout.location, {sampler: true}, bindingName)
|
|
89
|
+
: null
|
|
90
|
+
: null;
|
|
91
|
+
if (samplerEntry) {
|
|
92
|
+
entries.push(samplerEntry);
|
|
94
93
|
}
|
|
95
94
|
}
|
|
96
95
|
}
|
|
@@ -102,7 +101,8 @@ function getBindGroupEntries(
|
|
|
102
101
|
function getBindGroupEntry(
|
|
103
102
|
binding: Binding,
|
|
104
103
|
index: number,
|
|
105
|
-
options?: {sampler?: boolean}
|
|
104
|
+
options?: {sampler?: boolean},
|
|
105
|
+
bindingName: string = 'unknown'
|
|
106
106
|
): GPUBindGroupEntry | null {
|
|
107
107
|
if (binding instanceof Buffer) {
|
|
108
108
|
return {
|
|
@@ -118,6 +118,12 @@ function getBindGroupEntry(
|
|
|
118
118
|
resource: (binding as WebGPUSampler).handle
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
|
+
if (binding instanceof TextureView) {
|
|
122
|
+
return {
|
|
123
|
+
binding: index,
|
|
124
|
+
resource: (binding as WebGPUTextureView).handle
|
|
125
|
+
};
|
|
126
|
+
}
|
|
121
127
|
if (binding instanceof Texture) {
|
|
122
128
|
if (options?.sampler) {
|
|
123
129
|
return {
|
|
@@ -130,6 +136,6 @@ function getBindGroupEntry(
|
|
|
130
136
|
resource: (binding as WebGPUTexture).view.handle
|
|
131
137
|
};
|
|
132
138
|
}
|
|
133
|
-
log.warn(`invalid binding ${
|
|
139
|
+
log.warn(`invalid binding ${bindingName}`, binding);
|
|
134
140
|
return null;
|
|
135
141
|
}
|
|
@@ -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 ||
|