@luma.gl/webgpu 9.3.0-alpha.6 → 9.3.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/helpers/get-bind-group.d.ts +3 -6
- package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
- package/dist/adapter/helpers/get-bind-group.js +11 -14
- 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-command-encoder.d.ts +3 -13
- package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-command-encoder.js +68 -29
- 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 +17 -7
- 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 +26 -29
- package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
- package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-fence.js +9 -1
- package/dist/adapter/resources/webgpu-fence.js.map +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.d.ts +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-pipeline-layout.js +10 -16
- package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
- package/dist/adapter/resources/webgpu-render-pass.d.ts +4 -4
- package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-render-pass.js +10 -10
- package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
- package/dist/adapter/resources/webgpu-render-pipeline.d.ts +10 -9
- package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-render-pipeline.js +48 -38
- package/dist/adapter/resources/webgpu-render-pipeline.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.d.ts +8 -1
- package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
- package/dist/adapter/resources/webgpu-texture.js +35 -43
- package/dist/adapter/resources/webgpu-texture.js.map +1 -1
- package/dist/adapter/webgpu-device.d.ts +6 -2
- package/dist/adapter/webgpu-device.d.ts.map +1 -1
- package/dist/adapter/webgpu-device.js +60 -18
- package/dist/adapter/webgpu-device.js.map +1 -1
- package/dist/dist.dev.js +508 -311
- package/dist/dist.min.js +13 -13
- package/dist/index.cjs +439 -319
- package/dist/index.cjs.map +4 -4
- package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -1
- package/dist/wgsl/get-shader-layout-wgsl.js +8 -0
- package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -1
- package/package.json +3 -3
- package/src/adapter/helpers/get-bind-group.ts +18 -27
- 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-command-encoder.ts +99 -46
- package/src/adapter/resources/webgpu-compute-pass.ts +35 -8
- package/src/adapter/resources/webgpu-compute-pipeline.ts +43 -30
- package/src/adapter/resources/webgpu-fence.ts +11 -3
- package/src/adapter/resources/webgpu-pipeline-layout.ts +16 -14
- package/src/adapter/resources/webgpu-render-pass.ts +18 -14
- package/src/adapter/resources/webgpu-render-pipeline.ts +68 -46
- package/src/adapter/resources/webgpu-shader.ts +16 -1
- package/src/adapter/resources/webgpu-texture.ts +61 -44
- package/src/adapter/webgpu-device.ts +101 -25
- package/src/wgsl/get-shader-layout-wgsl.ts +9 -0
- package/dist/adapter/helpers/accessor-to-format.d.ts +0 -1
- package/dist/adapter/helpers/accessor-to-format.d.ts.map +0 -1
- package/dist/adapter/helpers/accessor-to-format.js +0 -105
- package/dist/adapter/helpers/accessor-to-format.js.map +0 -1
- package/src/adapter/helpers/accessor-to-format.ts +0 -104
|
@@ -15,9 +15,17 @@ export class WebGPUFence extends Fence {
|
|
|
15
15
|
constructor(device: WebGPUDevice, props: FenceProps = {}) {
|
|
16
16
|
super(device, {});
|
|
17
17
|
this.device = device;
|
|
18
|
-
this.signaled = device.handle.queue
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
this.signaled = device.handle.queue
|
|
19
|
+
.onSubmittedWorkDone()
|
|
20
|
+
.then(() => {
|
|
21
|
+
this._signaled = true;
|
|
22
|
+
})
|
|
23
|
+
.catch(error => {
|
|
24
|
+
if (this.device.shouldIgnoreDroppedInstanceError(error)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
throw error;
|
|
28
|
+
});
|
|
21
29
|
}
|
|
22
30
|
|
|
23
31
|
isSignaled(): boolean {
|
|
@@ -20,19 +20,16 @@ export class WebGPUPipelineLayout extends PipelineLayout {
|
|
|
20
20
|
|
|
21
21
|
this.device = device;
|
|
22
22
|
|
|
23
|
-
const
|
|
23
|
+
const bindGroupEntriesByGroup = this.mapShaderLayoutToBindGroupEntriesByGroup();
|
|
24
24
|
|
|
25
25
|
this.handle = this.device.handle.createPipelineLayout({
|
|
26
26
|
label: props?.id ?? 'unnamed-pipeline-layout',
|
|
27
|
-
bindGroupLayouts:
|
|
28
|
-
// TODO (kaapp): We can cache these to re-use them across
|
|
29
|
-
// layers, particularly if using a separate group for injected
|
|
30
|
-
// bindings (e.g. project/lighting)
|
|
27
|
+
bindGroupLayouts: bindGroupEntriesByGroup.map((entries, group) =>
|
|
31
28
|
this.device.handle.createBindGroupLayout({
|
|
32
|
-
label:
|
|
33
|
-
entries
|
|
29
|
+
label: `bind-group-layout-${group}`,
|
|
30
|
+
entries
|
|
34
31
|
})
|
|
35
|
-
|
|
32
|
+
)
|
|
36
33
|
});
|
|
37
34
|
}
|
|
38
35
|
|
|
@@ -42,10 +39,15 @@ export class WebGPUPipelineLayout extends PipelineLayout {
|
|
|
42
39
|
this.handle = null;
|
|
43
40
|
}
|
|
44
41
|
|
|
45
|
-
protected
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
protected mapShaderLayoutToBindGroupEntriesByGroup(): GPUBindGroupLayoutEntry[][] {
|
|
43
|
+
const maxGroup = this.props.shaderLayout.bindings.reduce(
|
|
44
|
+
(highestGroup, binding) => Math.max(highestGroup, binding.group),
|
|
45
|
+
-1
|
|
46
|
+
);
|
|
47
|
+
const bindGroupEntriesByGroup: GPUBindGroupLayoutEntry[][] = Array.from(
|
|
48
|
+
{length: maxGroup + 1},
|
|
49
|
+
() => []
|
|
50
|
+
);
|
|
49
51
|
|
|
50
52
|
for (const binding of this.props.shaderLayout.bindings) {
|
|
51
53
|
const bindingTypeInfo: Omit<GPUBindGroupLayoutEntry, 'binding' | 'visibility'> = {};
|
|
@@ -112,14 +114,14 @@ export class WebGPUPipelineLayout extends PipelineLayout {
|
|
|
112
114
|
const VISIBILITY_ALL =
|
|
113
115
|
GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE;
|
|
114
116
|
|
|
115
|
-
|
|
117
|
+
bindGroupEntriesByGroup[binding.group].push({
|
|
116
118
|
binding: binding.location,
|
|
117
119
|
visibility: binding.visibility || VISIBILITY_ALL,
|
|
118
120
|
...bindingTypeInfo
|
|
119
121
|
});
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
return
|
|
124
|
+
return bindGroupEntriesByGroup;
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
5
|
import type {TypedArray, NumberArray4} from '@math.gl/types';
|
|
6
|
-
import type {RenderPassProps, RenderPassParameters,
|
|
7
|
-
import {Buffer, RenderPass, RenderPipeline, log} from '@luma.gl/core';
|
|
6
|
+
import type {RenderPassProps, RenderPassParameters, Bindings, BindingsByGroup} from '@luma.gl/core';
|
|
7
|
+
import {Buffer, RenderPass, RenderPipeline, _getDefaultBindGroupFactory, log} from '@luma.gl/core';
|
|
8
8
|
import {WebGPUDevice} from '../webgpu-device';
|
|
9
9
|
import {WebGPUBuffer} from './webgpu-buffer';
|
|
10
10
|
// import {WebGPUCommandEncoder} from './webgpu-command-encoder';
|
|
@@ -22,9 +22,13 @@ export class WebGPURenderPass extends RenderPass {
|
|
|
22
22
|
pipeline: WebGPURenderPipeline | null = null;
|
|
23
23
|
|
|
24
24
|
/** Latest bindings applied to this pass */
|
|
25
|
-
bindings:
|
|
25
|
+
bindings: Bindings | BindingsByGroup = {};
|
|
26
26
|
|
|
27
|
-
constructor(
|
|
27
|
+
constructor(
|
|
28
|
+
device: WebGPUDevice,
|
|
29
|
+
props: RenderPassProps = {},
|
|
30
|
+
commandEncoder: GPUCommandEncoder = device.commandEncoder.handle
|
|
31
|
+
) {
|
|
28
32
|
super(device, props);
|
|
29
33
|
this.device = device;
|
|
30
34
|
const {props: renderPassProps} = this;
|
|
@@ -72,14 +76,9 @@ export class WebGPURenderPass extends RenderPass {
|
|
|
72
76
|
(getTimestamp() - descriptorAssemblyStartTime);
|
|
73
77
|
}
|
|
74
78
|
|
|
75
|
-
if (!device.commandEncoder) {
|
|
76
|
-
throw new Error('commandEncoder not available');
|
|
77
|
-
}
|
|
78
|
-
|
|
79
79
|
this.device.pushErrorScope('validation');
|
|
80
80
|
const beginRenderPassStartTime = profiler ? getTimestamp() : 0;
|
|
81
|
-
this.handle =
|
|
82
|
-
this.props.handle || device.commandEncoder.handle.beginRenderPass(renderPassDescriptor);
|
|
81
|
+
this.handle = this.props.handle || commandEncoder.beginRenderPass(renderPassDescriptor);
|
|
83
82
|
if (profiler) {
|
|
84
83
|
profiler.renderPassBeginCount = (profiler.renderPassBeginCount || 0) + 1;
|
|
85
84
|
profiler.renderPassBeginTimeMs =
|
|
@@ -125,11 +124,16 @@ export class WebGPURenderPass extends RenderPass {
|
|
|
125
124
|
}
|
|
126
125
|
|
|
127
126
|
/** Sets an array of bindings (uniform buffers, samplers, textures, ...) */
|
|
128
|
-
setBindings(bindings:
|
|
127
|
+
setBindings(bindings: Bindings | BindingsByGroup): void {
|
|
129
128
|
this.bindings = bindings;
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
const bindGroups =
|
|
130
|
+
(this.pipeline &&
|
|
131
|
+
_getDefaultBindGroupFactory(this.device).getBindGroups(this.pipeline, bindings)) ||
|
|
132
|
+
{};
|
|
133
|
+
for (const [group, bindGroup] of Object.entries(bindGroups)) {
|
|
134
|
+
if (bindGroup) {
|
|
135
|
+
this.handle.setBindGroup(Number(group), bindGroup as GPUBindGroup);
|
|
136
|
+
}
|
|
133
137
|
}
|
|
134
138
|
}
|
|
135
139
|
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
// luma.gl MIT license
|
|
2
2
|
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
3
|
+
import type {Bindings, BindingsByGroup, RenderPass, VertexArray} from '@luma.gl/core';
|
|
4
|
+
import {
|
|
5
|
+
RenderPipeline,
|
|
6
|
+
RenderPipelineProps,
|
|
7
|
+
_getDefaultBindGroupFactory,
|
|
8
|
+
log,
|
|
9
|
+
normalizeBindingsByGroup
|
|
10
|
+
} from '@luma.gl/core';
|
|
5
11
|
import {applyParametersToRenderPipelineDescriptor} from '../helpers/webgpu-parameters';
|
|
6
12
|
import {getWebGPUTextureFormat} from '../helpers/convert-texture-format';
|
|
7
|
-
import {getBindGroup} from '../helpers/get-bind-group';
|
|
8
13
|
import {getVertexBufferLayout} from '../helpers/get-vertex-buffer-layout';
|
|
9
14
|
// import {convertAttributesVertexBufferToLayout} from '../helpers/get-vertex-buffer-layout';
|
|
10
|
-
// import {mapAccessorToWebGPUFormat} from './helpers/accessor-to-format';
|
|
11
15
|
// import type {BufferAccessors} from './webgpu-pipeline';
|
|
12
16
|
|
|
13
17
|
import type {WebGPUDevice} from '../webgpu-device';
|
|
@@ -15,23 +19,20 @@ import type {WebGPUDevice} from '../webgpu-device';
|
|
|
15
19
|
import type {WebGPUShader} from './webgpu-shader';
|
|
16
20
|
import type {WebGPURenderPass} from './webgpu-render-pass';
|
|
17
21
|
|
|
18
|
-
const EMPTY_BINDINGS: Record<string, Binding> = {};
|
|
19
|
-
|
|
20
22
|
// RENDER PIPELINE
|
|
21
23
|
|
|
22
24
|
/** Creates a new render pipeline when parameters change */
|
|
23
25
|
export class WebGPURenderPipeline extends RenderPipeline {
|
|
24
26
|
readonly device: WebGPUDevice;
|
|
25
27
|
readonly handle: GPURenderPipeline;
|
|
28
|
+
readonly descriptor: GPURenderPipelineDescriptor | null;
|
|
26
29
|
|
|
27
30
|
readonly vs: WebGPUShader;
|
|
28
31
|
readonly fs: WebGPUShader | null = null;
|
|
29
32
|
|
|
30
33
|
/** Compatibility path for direct pipeline.setBindings() usage */
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
private _bindGroupLayout: GPUBindGroupLayout | null = null;
|
|
34
|
-
private _bindGroup: GPUBindGroup | null = null;
|
|
34
|
+
private _bindingsByGroup: BindingsByGroup;
|
|
35
|
+
private _bindGroupCacheKeysByGroup: Partial<Record<number, object>> = {};
|
|
35
36
|
|
|
36
37
|
override get [Symbol.toStringTag]() {
|
|
37
38
|
return 'WebGPURenderPipeline';
|
|
@@ -40,9 +41,14 @@ export class WebGPURenderPipeline extends RenderPipeline {
|
|
|
40
41
|
constructor(device: WebGPUDevice, props: RenderPipelineProps) {
|
|
41
42
|
super(device, props);
|
|
42
43
|
this.device = device;
|
|
44
|
+
this.shaderLayout ||= this.device.getShaderLayout((props.vs as WebGPUShader).source) || {
|
|
45
|
+
attributes: [],
|
|
46
|
+
bindings: []
|
|
47
|
+
};
|
|
43
48
|
this.handle = this.props.handle as GPURenderPipeline;
|
|
49
|
+
let descriptor: GPURenderPipelineDescriptor | null = null;
|
|
44
50
|
if (!this.handle) {
|
|
45
|
-
|
|
51
|
+
descriptor = this._getRenderPipelineDescriptor();
|
|
46
52
|
log.groupCollapsed(1, `new WebGPURenderPipeline(${this.id})`)();
|
|
47
53
|
log.probe(1, JSON.stringify(descriptor, null, 2))();
|
|
48
54
|
log.groupEnd(1)();
|
|
@@ -54,12 +60,15 @@ export class WebGPURenderPipeline extends RenderPipeline {
|
|
|
54
60
|
this.device.debug();
|
|
55
61
|
});
|
|
56
62
|
}
|
|
63
|
+
this.descriptor = descriptor;
|
|
57
64
|
this.handle.label = this.props.id;
|
|
58
65
|
|
|
59
66
|
// Note: Often the same shader in WebGPU
|
|
60
67
|
this.vs = props.vs as WebGPUShader;
|
|
61
68
|
this.fs = props.fs as WebGPUShader;
|
|
62
|
-
this.
|
|
69
|
+
this._bindingsByGroup =
|
|
70
|
+
props.bindGroups || normalizeBindingsByGroup(this.shaderLayout, props.bindings);
|
|
71
|
+
this._bindGroupCacheKeysByGroup = createBindGroupCacheKeys(this._bindingsByGroup);
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
override destroy(): void {
|
|
@@ -72,22 +81,24 @@ export class WebGPURenderPipeline extends RenderPipeline {
|
|
|
72
81
|
* Compatibility shim for code paths that still set bindings on the pipeline.
|
|
73
82
|
* The shared-model path passes bindings per draw and does not rely on this state.
|
|
74
83
|
*/
|
|
75
|
-
setBindings(bindings:
|
|
76
|
-
|
|
77
|
-
for (const [
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
setBindings(bindings: Bindings | BindingsByGroup): void {
|
|
85
|
+
const nextBindingsByGroup = normalizeBindingsByGroup(this.shaderLayout, bindings);
|
|
86
|
+
for (const [groupKey, groupBindings] of Object.entries(nextBindingsByGroup)) {
|
|
87
|
+
const group = Number(groupKey);
|
|
88
|
+
for (const [name, binding] of Object.entries(groupBindings || {})) {
|
|
89
|
+
const currentGroupBindings = this._bindingsByGroup[group] || {};
|
|
90
|
+
if (currentGroupBindings[name] !== binding) {
|
|
91
|
+
if (
|
|
92
|
+
!this._bindingsByGroup[group] ||
|
|
93
|
+
this._bindingsByGroup[group] === currentGroupBindings
|
|
94
|
+
) {
|
|
95
|
+
this._bindingsByGroup[group] = {...currentGroupBindings};
|
|
82
96
|
}
|
|
83
|
-
|
|
97
|
+
this._bindingsByGroup[group][name] = binding;
|
|
98
|
+
this._bindGroupCacheKeysByGroup[group] = {};
|
|
84
99
|
}
|
|
85
|
-
this._bindings[name] = binding;
|
|
86
100
|
}
|
|
87
101
|
}
|
|
88
|
-
if (bindingsChanged) {
|
|
89
|
-
this._bindGroup = null;
|
|
90
|
-
}
|
|
91
102
|
}
|
|
92
103
|
|
|
93
104
|
/** @todo - should this be moved to renderpass? */
|
|
@@ -101,7 +112,9 @@ export class WebGPURenderPipeline extends RenderPipeline {
|
|
|
101
112
|
firstIndex?: number;
|
|
102
113
|
firstInstance?: number;
|
|
103
114
|
baseVertex?: number;
|
|
104
|
-
bindings?:
|
|
115
|
+
bindings?: Bindings;
|
|
116
|
+
bindGroups?: BindingsByGroup;
|
|
117
|
+
_bindGroupCacheKeys?: Partial<Record<number, object>>;
|
|
105
118
|
uniforms?: Record<string, unknown>;
|
|
106
119
|
}): boolean {
|
|
107
120
|
const webgpuRenderPass = options.renderPass as WebGPURenderPass;
|
|
@@ -117,9 +130,16 @@ export class WebGPURenderPipeline extends RenderPipeline {
|
|
|
117
130
|
});
|
|
118
131
|
|
|
119
132
|
// Set bindings (uniform buffers, textures etc)
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
133
|
+
const hasExplicitBindings = Boolean(options.bindGroups || options.bindings);
|
|
134
|
+
const bindGroups = _getDefaultBindGroupFactory(this.device).getBindGroups(
|
|
135
|
+
this,
|
|
136
|
+
hasExplicitBindings ? options.bindGroups || options.bindings : this._bindingsByGroup,
|
|
137
|
+
hasExplicitBindings ? options._bindGroupCacheKeys : this._bindGroupCacheKeysByGroup
|
|
138
|
+
);
|
|
139
|
+
for (const [group, bindGroup] of Object.entries(bindGroups)) {
|
|
140
|
+
if (bindGroup) {
|
|
141
|
+
webgpuRenderPass.handle.setBindGroup(Number(group), bindGroup as GPUBindGroup);
|
|
142
|
+
}
|
|
123
143
|
}
|
|
124
144
|
|
|
125
145
|
// Set attributes
|
|
@@ -150,24 +170,12 @@ export class WebGPURenderPipeline extends RenderPipeline {
|
|
|
150
170
|
return true;
|
|
151
171
|
}
|
|
152
172
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Get hold of the bind group layout. We don't want to do this unless we know there is at least one bind group
|
|
160
|
-
this._bindGroupLayout = this._bindGroupLayout || this.handle.getBindGroupLayout(0);
|
|
161
|
-
|
|
162
|
-
if (bindings) {
|
|
163
|
-
return getBindGroup(this.device, this._bindGroupLayout, this.shaderLayout, bindings);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
this._bindGroup =
|
|
167
|
-
this._bindGroup ||
|
|
168
|
-
getBindGroup(this.device, this._bindGroupLayout, this.shaderLayout, this._bindings);
|
|
173
|
+
_getBindingsByGroupWebGPU(): BindingsByGroup {
|
|
174
|
+
return this._bindingsByGroup;
|
|
175
|
+
}
|
|
169
176
|
|
|
170
|
-
|
|
177
|
+
_getBindGroupCacheKeysWebGPU(): Partial<Record<number, object>> {
|
|
178
|
+
return this._bindGroupCacheKeysByGroup;
|
|
171
179
|
}
|
|
172
180
|
|
|
173
181
|
/**
|
|
@@ -178,7 +186,9 @@ export class WebGPURenderPipeline extends RenderPipeline {
|
|
|
178
186
|
const vertex: GPUVertexState = {
|
|
179
187
|
module: (this.props.vs as WebGPUShader).handle,
|
|
180
188
|
entryPoint: this.props.vertexEntryPoint || 'main',
|
|
181
|
-
buffers: getVertexBufferLayout(this.shaderLayout, this.props.bufferLayout
|
|
189
|
+
buffers: getVertexBufferLayout(this.shaderLayout, this.props.bufferLayout, {
|
|
190
|
+
pipelineId: this.id
|
|
191
|
+
})
|
|
182
192
|
};
|
|
183
193
|
|
|
184
194
|
// Populate color targets
|
|
@@ -228,6 +238,18 @@ export class WebGPURenderPipeline extends RenderPipeline {
|
|
|
228
238
|
return descriptor;
|
|
229
239
|
}
|
|
230
240
|
}
|
|
241
|
+
|
|
242
|
+
function createBindGroupCacheKeys(
|
|
243
|
+
bindingsByGroup: BindingsByGroup
|
|
244
|
+
): Partial<Record<number, object>> {
|
|
245
|
+
const bindGroupCacheKeys: Partial<Record<number, object>> = {};
|
|
246
|
+
for (const [groupKey, groupBindings] of Object.entries(bindingsByGroup)) {
|
|
247
|
+
if (groupBindings && Object.keys(groupBindings).length > 0) {
|
|
248
|
+
bindGroupCacheKeys[Number(groupKey)] = {};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return bindGroupCacheKeys;
|
|
252
|
+
}
|
|
231
253
|
/**
|
|
232
254
|
_setAttributeBuffers(webgpuRenderPass: WebGPURenderPass) {
|
|
233
255
|
if (this._indexBuffer) {
|
|
@@ -64,7 +64,22 @@ export class WebGPUShader extends Shader {
|
|
|
64
64
|
|
|
65
65
|
/** Returns compilation info for this shader */
|
|
66
66
|
async getCompilationInfo(): Promise<readonly CompilerMessage[]> {
|
|
67
|
-
|
|
67
|
+
// `_checkCompilationError()` runs asynchronously after construction, so the shader can be
|
|
68
|
+
// destroyed before we await compilation info. Snapshot the handle and treat a destroyed shader
|
|
69
|
+
// as having no compiler messages instead of dereferencing `null`.
|
|
70
|
+
const handle = this.handle;
|
|
71
|
+
if (!handle) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
let compilationInfo;
|
|
75
|
+
try {
|
|
76
|
+
compilationInfo = await handle.getCompilationInfo();
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (this.device.shouldIgnoreDroppedInstanceError(error, 'getCompilationInfo')) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
68
83
|
return compilationInfo.messages;
|
|
69
84
|
}
|
|
70
85
|
}
|
|
@@ -16,6 +16,7 @@ import {getWebGPUTextureFormat} from '../helpers/convert-texture-format';
|
|
|
16
16
|
import type {WebGPUDevice} from '../webgpu-device';
|
|
17
17
|
import {WebGPUSampler} from './webgpu-sampler';
|
|
18
18
|
import {WebGPUTextureView} from './webgpu-texture-view';
|
|
19
|
+
import {WebGPUBuffer} from './webgpu-buffer';
|
|
19
20
|
|
|
20
21
|
/** WebGPU implementation of the luma.gl core Texture resource */
|
|
21
22
|
export class WebGPUTexture extends Texture {
|
|
@@ -165,77 +166,93 @@ export class WebGPUTexture extends Texture {
|
|
|
165
166
|
};
|
|
166
167
|
}
|
|
167
168
|
|
|
168
|
-
override readBuffer(
|
|
169
|
+
override readBuffer(
|
|
170
|
+
options: TextureReadOptions & {byteOffset?: number} = {},
|
|
171
|
+
buffer?: Buffer
|
|
172
|
+
): Buffer {
|
|
173
|
+
if (!buffer) {
|
|
174
|
+
throw new Error(`${this} readBuffer requires a destination buffer`);
|
|
175
|
+
}
|
|
169
176
|
const {x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect} =
|
|
170
177
|
this._getSupportedColorReadOptions(options);
|
|
178
|
+
const byteOffset = options.byteOffset ?? 0;
|
|
171
179
|
|
|
172
|
-
const layout = this.computeMemoryLayout({
|
|
173
|
-
|
|
174
|
-
const {bytesPerRow, rowsPerImage, byteLength} = layout;
|
|
180
|
+
const layout = this.computeMemoryLayout({width, height, depthOrArrayLayers, mipLevel});
|
|
175
181
|
|
|
176
|
-
|
|
177
|
-
const readBuffer =
|
|
178
|
-
buffer ||
|
|
179
|
-
this.device.createBuffer({
|
|
180
|
-
byteLength,
|
|
181
|
-
usage: Buffer.COPY_DST | Buffer.MAP_READ
|
|
182
|
-
});
|
|
182
|
+
const {byteLength} = layout;
|
|
183
183
|
|
|
184
|
-
if (
|
|
184
|
+
if (buffer.byteLength < byteOffset + byteLength) {
|
|
185
185
|
throw new Error(
|
|
186
|
-
`${this} readBuffer target is too small (${
|
|
186
|
+
`${this} readBuffer target is too small (${buffer.byteLength} < ${byteOffset + byteLength})`
|
|
187
187
|
);
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
const gpuReadBuffer = readBuffer.handle as GPUBuffer;
|
|
191
|
-
|
|
192
|
-
// Record commands to copy from the texture to the buffer.
|
|
193
190
|
const gpuDevice = this.device.handle;
|
|
194
|
-
|
|
195
191
|
this.device.pushErrorScope('validation');
|
|
196
192
|
const commandEncoder = gpuDevice.createCommandEncoder();
|
|
193
|
+
this.copyToBuffer(
|
|
194
|
+
commandEncoder,
|
|
195
|
+
{x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect, byteOffset},
|
|
196
|
+
buffer
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const commandBuffer = commandEncoder.finish();
|
|
200
|
+
this.device.handle.queue.submit([commandBuffer]);
|
|
201
|
+
this.device.popErrorScope((error: GPUError) => {
|
|
202
|
+
this.device.reportError(new Error(`${this} readBuffer: ${error.message}`), this)();
|
|
203
|
+
this.device.debug();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return buffer;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
override async readDataAsync(options: TextureReadOptions = {}): Promise<ArrayBuffer> {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`${this} readDataAsync is deprecated; use readBuffer() with an explicit destination buffer or DynamicTexture.readAsync()`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
copyToBuffer(
|
|
216
|
+
commandEncoder: GPUCommandEncoder,
|
|
217
|
+
options: TextureReadOptions & {
|
|
218
|
+
byteOffset?: number;
|
|
219
|
+
bytesPerRow?: number;
|
|
220
|
+
rowsPerImage?: number;
|
|
221
|
+
} = {},
|
|
222
|
+
buffer: Buffer
|
|
223
|
+
): void {
|
|
224
|
+
const {
|
|
225
|
+
byteOffset = 0,
|
|
226
|
+
bytesPerRow: requestedBytesPerRow,
|
|
227
|
+
rowsPerImage: requestedRowsPerImage,
|
|
228
|
+
...textureReadOptions
|
|
229
|
+
} = options;
|
|
230
|
+
const {x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect} =
|
|
231
|
+
this._getSupportedColorReadOptions(textureReadOptions);
|
|
232
|
+
const layout = this.computeMemoryLayout({width, height, depthOrArrayLayers, mipLevel});
|
|
233
|
+
const effectiveBytesPerRow = requestedBytesPerRow ?? layout.bytesPerRow;
|
|
234
|
+
const effectiveRowsPerImage = requestedRowsPerImage ?? layout.rowsPerImage;
|
|
235
|
+
const webgpuBuffer = buffer as WebGPUBuffer;
|
|
236
|
+
|
|
197
237
|
commandEncoder.copyTextureToBuffer(
|
|
198
|
-
// source
|
|
199
238
|
{
|
|
200
239
|
texture: this.handle,
|
|
201
240
|
origin: {x, y, z},
|
|
202
|
-
// origin: [options.x, options.y, 0], // options.depth],
|
|
203
241
|
mipLevel,
|
|
204
242
|
aspect
|
|
205
|
-
// colorSpace: options.colorSpace,
|
|
206
|
-
// premultipliedAlpha: options.premultipliedAlpha
|
|
207
243
|
},
|
|
208
|
-
// destination
|
|
209
244
|
{
|
|
210
|
-
buffer:
|
|
211
|
-
offset:
|
|
212
|
-
bytesPerRow,
|
|
213
|
-
rowsPerImage
|
|
245
|
+
buffer: webgpuBuffer.handle,
|
|
246
|
+
offset: byteOffset,
|
|
247
|
+
bytesPerRow: effectiveBytesPerRow,
|
|
248
|
+
rowsPerImage: effectiveRowsPerImage
|
|
214
249
|
},
|
|
215
|
-
// copy size
|
|
216
250
|
{
|
|
217
251
|
width,
|
|
218
252
|
height,
|
|
219
253
|
depthOrArrayLayers
|
|
220
254
|
}
|
|
221
255
|
);
|
|
222
|
-
|
|
223
|
-
// Submit the command.
|
|
224
|
-
const commandBuffer = commandEncoder.finish();
|
|
225
|
-
this.device.handle.queue.submit([commandBuffer]);
|
|
226
|
-
this.device.popErrorScope((error: GPUError) => {
|
|
227
|
-
this.device.reportError(new Error(`${this} readBuffer: ${error.message}`), this)();
|
|
228
|
-
this.device.debug();
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
return readBuffer;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
override async readDataAsync(options: TextureReadOptions = {}): Promise<ArrayBuffer> {
|
|
235
|
-
const buffer = this.readBuffer(options);
|
|
236
|
-
const data = await buffer.readAsync();
|
|
237
|
-
buffer.destroy();
|
|
238
|
-
return data.buffer as ArrayBuffer;
|
|
239
256
|
}
|
|
240
257
|
|
|
241
258
|
override writeBuffer(buffer: Buffer, options_: TextureWriteOptions = {}) {
|