@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.
Files changed (137) hide show
  1. package/dist/adapter/helpers/cpu-hotspot-profiler.d.ts +54 -0
  2. package/dist/adapter/helpers/cpu-hotspot-profiler.d.ts.map +1 -0
  3. package/dist/adapter/helpers/cpu-hotspot-profiler.js +26 -0
  4. package/dist/adapter/helpers/cpu-hotspot-profiler.js.map +1 -0
  5. package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts +7 -0
  6. package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts.map +1 -0
  7. package/dist/adapter/helpers/generate-mipmaps-webgpu.js +490 -0
  8. package/dist/adapter/helpers/generate-mipmaps-webgpu.js.map +1 -0
  9. package/dist/adapter/helpers/get-bind-group.d.ts +8 -6
  10. package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
  11. package/dist/adapter/helpers/get-bind-group.js +110 -30
  12. package/dist/adapter/helpers/get-bind-group.js.map +1 -1
  13. package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts +3 -1
  14. package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts.map +1 -1
  15. package/dist/adapter/helpers/get-vertex-buffer-layout.js +17 -12
  16. package/dist/adapter/helpers/get-vertex-buffer-layout.js.map +1 -1
  17. package/dist/adapter/helpers/webgpu-parameters.d.ts.map +1 -1
  18. package/dist/adapter/helpers/webgpu-parameters.js +1 -0
  19. package/dist/adapter/helpers/webgpu-parameters.js.map +1 -1
  20. package/dist/adapter/resources/webgpu-buffer.d.ts +7 -0
  21. package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -1
  22. package/dist/adapter/resources/webgpu-buffer.js +58 -15
  23. package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
  24. package/dist/adapter/resources/webgpu-command-buffer.js +1 -1
  25. package/dist/adapter/resources/webgpu-command-buffer.js.map +1 -1
  26. package/dist/adapter/resources/webgpu-command-encoder.d.ts +7 -16
  27. package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
  28. package/dist/adapter/resources/webgpu-command-encoder.js +89 -32
  29. package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -1
  30. package/dist/adapter/resources/webgpu-compute-pass.d.ts +3 -3
  31. package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -1
  32. package/dist/adapter/resources/webgpu-compute-pass.js +30 -12
  33. package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -1
  34. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts +7 -9
  35. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
  36. package/dist/adapter/resources/webgpu-compute-pipeline.js +30 -17
  37. package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
  38. package/dist/adapter/resources/webgpu-fence.d.ts +13 -0
  39. package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -0
  40. package/dist/adapter/resources/webgpu-fence.js +33 -0
  41. package/dist/adapter/resources/webgpu-fence.js.map +1 -0
  42. package/dist/adapter/resources/webgpu-framebuffer.d.ts +6 -0
  43. package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
  44. package/dist/adapter/resources/webgpu-framebuffer.js +16 -0
  45. package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -1
  46. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts +1 -1
  47. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
  48. package/dist/adapter/resources/webgpu-pipeline-layout.js +11 -18
  49. package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
  50. package/dist/adapter/resources/webgpu-query-set.d.ts +33 -4
  51. package/dist/adapter/resources/webgpu-query-set.d.ts.map +1 -1
  52. package/dist/adapter/resources/webgpu-query-set.js +145 -4
  53. package/dist/adapter/resources/webgpu-query-set.js.map +1 -1
  54. package/dist/adapter/resources/webgpu-render-pass.d.ts +6 -3
  55. package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
  56. package/dist/adapter/resources/webgpu-render-pass.js +78 -34
  57. package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
  58. package/dist/adapter/resources/webgpu-render-pipeline.d.ts +14 -10
  59. package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
  60. package/dist/adapter/resources/webgpu-render-pipeline.js +62 -35
  61. package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
  62. package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -1
  63. package/dist/adapter/resources/webgpu-sampler.js +4 -0
  64. package/dist/adapter/resources/webgpu-sampler.js.map +1 -1
  65. package/dist/adapter/resources/webgpu-shader.d.ts.map +1 -1
  66. package/dist/adapter/resources/webgpu-shader.js +17 -1
  67. package/dist/adapter/resources/webgpu-shader.js.map +1 -1
  68. package/dist/adapter/resources/webgpu-texture-view.d.ts +6 -0
  69. package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -1
  70. package/dist/adapter/resources/webgpu-texture-view.js +47 -11
  71. package/dist/adapter/resources/webgpu-texture-view.js.map +1 -1
  72. package/dist/adapter/resources/webgpu-texture.d.ts +25 -3
  73. package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
  74. package/dist/adapter/resources/webgpu-texture.js +211 -43
  75. package/dist/adapter/resources/webgpu-texture.js.map +1 -1
  76. package/dist/adapter/resources/webgpu-vertex-array.js +1 -1
  77. package/dist/adapter/resources/webgpu-vertex-array.js.map +1 -1
  78. package/dist/adapter/webgpu-adapter.d.ts.map +1 -1
  79. package/dist/adapter/webgpu-adapter.js +35 -35
  80. package/dist/adapter/webgpu-adapter.js.map +1 -1
  81. package/dist/adapter/webgpu-canvas-context.d.ts +6 -3
  82. package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
  83. package/dist/adapter/webgpu-canvas-context.js +90 -30
  84. package/dist/adapter/webgpu-canvas-context.js.map +1 -1
  85. package/dist/adapter/webgpu-device.d.ts +12 -2
  86. package/dist/adapter/webgpu-device.d.ts.map +1 -1
  87. package/dist/adapter/webgpu-device.js +176 -19
  88. package/dist/adapter/webgpu-device.js.map +1 -1
  89. package/dist/adapter/webgpu-presentation-context.d.ts +25 -0
  90. package/dist/adapter/webgpu-presentation-context.d.ts.map +1 -0
  91. package/dist/adapter/webgpu-presentation-context.js +144 -0
  92. package/dist/adapter/webgpu-presentation-context.js.map +1 -0
  93. package/dist/dist.dev.js +8160 -551
  94. package/dist/dist.min.js +171 -6
  95. package/dist/index.cjs +2001 -414
  96. package/dist/index.cjs.map +4 -4
  97. package/dist/index.d.ts +2 -0
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +2 -0
  100. package/dist/index.js.map +1 -1
  101. package/dist/wgsl/get-shader-layout-wgsl.d.ts +8 -0
  102. package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -0
  103. package/dist/wgsl/get-shader-layout-wgsl.js +144 -0
  104. package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -0
  105. package/package.json +6 -5
  106. package/src/adapter/helpers/cpu-hotspot-profiler.ts +70 -0
  107. package/src/adapter/helpers/generate-mipmaps-webgpu.ts +583 -0
  108. package/src/adapter/helpers/get-bind-group.ts +182 -42
  109. package/src/adapter/helpers/get-vertex-buffer-layout.ts +31 -12
  110. package/src/adapter/helpers/webgpu-parameters.ts +2 -0
  111. package/src/adapter/resources/webgpu-buffer.ts +61 -15
  112. package/src/adapter/resources/webgpu-command-buffer.ts +1 -1
  113. package/src/adapter/resources/webgpu-command-encoder.ts +129 -50
  114. package/src/adapter/resources/webgpu-compute-pass.ts +48 -13
  115. package/src/adapter/resources/webgpu-compute-pipeline.ts +49 -18
  116. package/src/adapter/resources/webgpu-fence.ts +38 -0
  117. package/src/adapter/resources/webgpu-framebuffer.ts +21 -0
  118. package/src/adapter/resources/webgpu-pipeline-layout.ts +18 -17
  119. package/src/adapter/resources/webgpu-query-set.ts +185 -9
  120. package/src/adapter/resources/webgpu-render-pass.ts +92 -40
  121. package/src/adapter/resources/webgpu-render-pipeline.ts +90 -44
  122. package/src/adapter/resources/webgpu-sampler.ts +5 -0
  123. package/src/adapter/resources/webgpu-shader.ts +16 -1
  124. package/src/adapter/resources/webgpu-texture-view.ts +51 -11
  125. package/src/adapter/resources/webgpu-texture.ts +281 -101
  126. package/src/adapter/resources/webgpu-vertex-array.ts +1 -1
  127. package/src/adapter/webgpu-adapter.ts +41 -43
  128. package/src/adapter/webgpu-canvas-context.ts +108 -41
  129. package/src/adapter/webgpu-device.ts +243 -25
  130. package/src/adapter/webgpu-presentation-context.ts +180 -0
  131. package/src/index.ts +3 -0
  132. package/src/wgsl/get-shader-layout-wgsl.ts +165 -0
  133. package/dist/adapter/helpers/accessor-to-format.d.ts +0 -1
  134. package/dist/adapter/helpers/accessor-to-format.d.ts.map +0 -1
  135. package/dist/adapter/helpers/accessor-to-format.js +0 -105
  136. package/dist/adapter/helpers/accessor-to-format.js.map +0 -1
  137. package/src/adapter/helpers/accessor-to-format.ts +0 -104
@@ -3,12 +3,14 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import type {
6
+ CommandBufferProps,
6
7
  RenderPassProps,
7
8
  ComputePassProps,
9
+ CopyBufferToTextureOptions,
8
10
  CopyTextureToTextureOptions,
9
11
  CopyTextureToBufferOptions
10
12
  } from '@luma.gl/core';
11
- import {CommandEncoder, CommandEncoderProps, Buffer, Texture} from '@luma.gl/core';
13
+ import {CommandEncoder, CommandEncoderProps, Buffer} from '@luma.gl/core';
12
14
  import {WebGPUDevice} from '../webgpu-device';
13
15
  import {WebGPUCommandBuffer} from './webgpu-command-buffer';
14
16
  import {WebGPUBuffer} from './webgpu-buffer';
@@ -34,9 +36,11 @@ export class WebGPUCommandEncoder extends CommandEncoder {
34
36
  this.handle.label = this.props.id;
35
37
  }
36
38
 
37
- override destroy(): void {}
39
+ override destroy(): void {
40
+ this.destroyResource();
41
+ }
38
42
 
39
- finish(props?: CommandEncoderProps): WebGPUCommandBuffer {
43
+ finish(props?: CommandBufferProps): WebGPUCommandBuffer {
40
44
  this.device.pushErrorScope('validation');
41
45
  const commandBuffer = new WebGPUCommandBuffer(this, {
42
46
  id: props?.id || 'unnamed-command-buffer'
@@ -46,6 +50,7 @@ export class WebGPUCommandEncoder extends CommandEncoder {
46
50
  this.device.reportError(new Error(message), this)();
47
51
  this.device.debug();
48
52
  });
53
+ this.destroy();
49
54
  return commandBuffer;
50
55
  }
51
56
 
@@ -53,19 +58,26 @@ export class WebGPUCommandEncoder extends CommandEncoder {
53
58
  * Allows a render pass to begin against a canvas context
54
59
  * @todo need to support a "Framebuffer" equivalent (aka preconfigured RenderPassDescriptors?).
55
60
  */
56
- beginRenderPass(props: RenderPassProps): WebGPURenderPass {
57
- return new WebGPURenderPass(this.device, props);
61
+ beginRenderPass(props: RenderPassProps = {}): WebGPURenderPass {
62
+ return new WebGPURenderPass(
63
+ this.device,
64
+ this._applyTimeProfilingToPassProps(props),
65
+ this.handle
66
+ );
58
67
  }
59
68
 
60
- beginComputePass(props: ComputePassProps): WebGPUComputePass {
61
- return new WebGPUComputePass(this.device, props);
69
+ beginComputePass(props: ComputePassProps = {}): WebGPUComputePass {
70
+ return new WebGPUComputePass(
71
+ this.device,
72
+ this._applyTimeProfilingToPassProps(props),
73
+ this.handle
74
+ );
62
75
  }
63
76
 
64
77
  // beginRenderPass(GPURenderPassDescriptor descriptor): GPURenderPassEncoder;
65
78
  // beginComputePass(optional GPUComputePassDescriptor descriptor = {}): GPUComputePassEncoder;
66
79
 
67
- copyBufferToBuffer(options: // CopyBufferToBufferOptions
68
- {
80
+ copyBufferToBuffer(options: {
69
81
  sourceBuffer: Buffer;
70
82
  sourceOffset?: number;
71
83
  destinationBuffer: Buffer;
@@ -73,74 +85,119 @@ export class WebGPUCommandEncoder extends CommandEncoder {
73
85
  size?: number;
74
86
  }): void {
75
87
  const webgpuSourceBuffer = options.sourceBuffer as WebGPUBuffer;
76
- const WebGPUDestinationBuffer = options.destinationBuffer as WebGPUBuffer;
88
+ const webgpuDestinationBuffer = options.destinationBuffer as WebGPUBuffer;
77
89
  this.handle.copyBufferToBuffer(
78
90
  webgpuSourceBuffer.handle,
79
91
  options.sourceOffset ?? 0,
80
- WebGPUDestinationBuffer.handle,
92
+ webgpuDestinationBuffer.handle,
81
93
  options.destinationOffset ?? 0,
82
94
  options.size ?? 0
83
95
  );
84
96
  }
85
97
 
86
- copyBufferToTexture(options: // CopyBufferToTextureOptions
87
- {
88
- sourceBuffer: Buffer;
89
- offset?: number;
90
- bytesPerRow: number;
91
- rowsPerImage: number;
92
-
93
- destinationTexture: Texture;
94
- mipLevel?: number;
95
- aspect?: 'all' | 'stencil-only' | 'depth-only';
96
-
97
- origin?: number[] | [number, number, number];
98
- extent?: number[] | [number, number, number];
99
- }): void {
98
+ copyBufferToTexture(options: CopyBufferToTextureOptions): void {
100
99
  const webgpuSourceBuffer = options.sourceBuffer as WebGPUBuffer;
101
- const WebGPUDestinationTexture = options.destinationTexture as WebGPUTexture;
100
+ const webgpuDestinationTexture = options.destinationTexture as WebGPUTexture;
101
+ const copyOrigin = options.origin ?? [0, 0, 0];
102
+ const copySize = options.size;
102
103
  this.handle.copyBufferToTexture(
103
104
  {
104
105
  buffer: webgpuSourceBuffer.handle,
105
- offset: options.offset ?? 0,
106
+ offset: options.byteOffset ?? 0,
106
107
  bytesPerRow: options.bytesPerRow,
107
108
  rowsPerImage: options.rowsPerImage
108
109
  },
109
110
  {
110
- texture: WebGPUDestinationTexture.handle,
111
+ texture: webgpuDestinationTexture.handle,
111
112
  mipLevel: options.mipLevel ?? 0,
112
- origin: options.origin ?? {}
113
- // aspect: options.aspect
113
+ origin: {
114
+ x: copyOrigin[0] ?? 0,
115
+ y: copyOrigin[1] ?? 0,
116
+ z: copyOrigin[2] ?? 0
117
+ },
118
+ aspect: options.aspect
114
119
  },
115
120
  {
116
- // @ts-ignore
117
- width: options.extent?.[0],
118
- height: options.extent?.[1],
119
- depthOrArrayLayers: options.extent?.[2]
121
+ width: copySize[0],
122
+ height: copySize[1],
123
+ depthOrArrayLayers: copySize[2]
120
124
  }
121
125
  );
122
126
  }
123
127
 
124
128
  copyTextureToBuffer(options: CopyTextureToBufferOptions): void {
125
- // this.handle.copyTextureToBuffer(
126
- // // source
127
- // {},
128
- // // destination
129
- // {},
130
- // // copySize
131
- // {}
132
- // );
129
+ const {
130
+ sourceTexture,
131
+ destinationBuffer,
132
+ origin = [0, 0, 0],
133
+ byteOffset = 0,
134
+ width,
135
+ height,
136
+ depthOrArrayLayers,
137
+ mipLevel,
138
+ aspect
139
+ } = options;
140
+ const webgpuSourceTexture = sourceTexture as WebGPUTexture;
141
+ webgpuSourceTexture.copyToBuffer(
142
+ this.handle,
143
+ {
144
+ x: origin[0] ?? 0,
145
+ y: origin[1] ?? 0,
146
+ z: origin[2] ?? 0,
147
+ width,
148
+ height,
149
+ depthOrArrayLayers,
150
+ mipLevel,
151
+ aspect,
152
+ byteOffset,
153
+ bytesPerRow: options.bytesPerRow,
154
+ rowsPerImage: options.rowsPerImage
155
+ },
156
+ destinationBuffer
157
+ );
133
158
  }
134
159
 
135
160
  copyTextureToTexture(options: CopyTextureToTextureOptions): void {
136
- // this.handle.copyTextureToTexture(
137
- // // source
138
- // {},
139
- // // destination
140
- // {},
141
- // // copySize
142
- // {}
143
- // );
161
+ const webgpuSourceTexture = options.sourceTexture as WebGPUTexture;
162
+ const webgpuDestinationTexture = options.destinationTexture as WebGPUTexture;
163
+ const sourceRegion = webgpuSourceTexture._normalizeTextureReadOptions({
164
+ x: options.origin?.[0] ?? 0,
165
+ y: options.origin?.[1] ?? 0,
166
+ z: options.origin?.[2] ?? 0,
167
+ width: options.width,
168
+ height: options.height,
169
+ depthOrArrayLayers: options.depthOrArrayLayers,
170
+ mipLevel: options.mipLevel ?? 0,
171
+ aspect: options.aspect ?? 'all'
172
+ });
173
+
174
+ this.handle.copyTextureToTexture(
175
+ {
176
+ texture: webgpuSourceTexture.handle,
177
+ mipLevel: sourceRegion.mipLevel,
178
+ origin: {
179
+ x: sourceRegion.x,
180
+ y: sourceRegion.y,
181
+ z: sourceRegion.z
182
+ },
183
+ aspect: sourceRegion.aspect
184
+ },
185
+ {
186
+ texture: webgpuDestinationTexture.handle,
187
+ mipLevel: options.destinationMipLevel ?? 0,
188
+ origin: {
189
+ x: options.destinationOrigin?.[0] ?? 0,
190
+ y: options.destinationOrigin?.[1] ?? 0,
191
+ z: options.destinationOrigin?.[2] ?? 0
192
+ },
193
+ aspect: options.destinationAspect ?? sourceRegion.aspect
194
+ },
195
+ {
196
+ width: sourceRegion.width,
197
+ height: sourceRegion.height,
198
+ depthOrArrayLayers: sourceRegion.depthOrArrayLayers
199
+ }
200
+ );
144
201
  }
145
202
 
146
203
  override pushDebugGroup(groupLabel: string): void {
@@ -174,6 +231,28 @@ export class WebGPUCommandEncoder extends CommandEncoder {
174
231
  options?.destinationOffset || 0
175
232
  );
176
233
  }
234
+
235
+ writeTimestamp(querySet: WebGPUQuerySet, queryIndex: number): void {
236
+ querySet._invalidateResults();
237
+ const writeTimestamp = (
238
+ this.handle as GPUCommandEncoder & {
239
+ writeTimestamp?: (querySet: GPUQuerySet, queryIndex: number) => void;
240
+ }
241
+ ).writeTimestamp;
242
+
243
+ if (writeTimestamp) {
244
+ writeTimestamp.call(this.handle, querySet.handle, queryIndex);
245
+ return;
246
+ }
247
+
248
+ const computePass = this.handle.beginComputePass({
249
+ timestampWrites: {
250
+ querySet: querySet.handle,
251
+ beginningOfPassWriteIndex: queryIndex
252
+ }
253
+ });
254
+ computePass.end();
255
+ }
177
256
  }
178
257
 
179
258
  /*
@@ -2,7 +2,15 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import {ComputePass, ComputePassProps, ComputePipeline, Buffer, Binding} from '@luma.gl/core';
5
+ import {
6
+ ComputePass,
7
+ ComputePassProps,
8
+ ComputePipeline,
9
+ Buffer,
10
+ Bindings,
11
+ BindingsByGroup,
12
+ _getDefaultBindGroupFactory
13
+ } from '@luma.gl/core';
6
14
  import {WebGPUDevice} from '../webgpu-device';
7
15
  import {WebGPUBuffer} from './webgpu-buffer';
8
16
  import {WebGPUComputePipeline} from './webgpu-compute-pipeline';
@@ -14,53 +22,80 @@ export class WebGPUComputePass extends ComputePass {
14
22
 
15
23
  _webgpuPipeline: WebGPUComputePipeline | null = null;
16
24
 
17
- constructor(device: WebGPUDevice, props: ComputePassProps) {
25
+ constructor(
26
+ device: WebGPUDevice,
27
+ props: ComputePassProps = {},
28
+ commandEncoder: GPUCommandEncoder = device.commandEncoder.handle
29
+ ) {
18
30
  super(device, props);
19
31
  this.device = device;
32
+ const {props: computePassProps} = this;
20
33
 
21
34
  // Set up queries
22
35
  let timestampWrites: GPUComputePassTimestampWrites | undefined;
23
- if (device.features.has('timestamp-query')) {
24
- const webgpuQuerySet = props.timestampQuerySet as WebGPUQuerySet;
36
+ if (computePassProps.timestampQuerySet) {
37
+ const webgpuQuerySet = computePassProps.timestampQuerySet as WebGPUQuerySet;
25
38
  if (webgpuQuerySet) {
39
+ webgpuQuerySet._invalidateResults();
26
40
  timestampWrites = {
27
41
  querySet: webgpuQuerySet.handle,
28
- beginningOfPassWriteIndex: props.beginTimestampIndex,
29
- endOfPassWriteIndex: props.endTimestampIndex
42
+ beginningOfPassWriteIndex: computePassProps.beginTimestampIndex,
43
+ endOfPassWriteIndex: computePassProps.endTimestampIndex
30
44
  };
31
45
  }
32
46
  }
33
47
 
34
48
  this.handle =
35
49
  this.props.handle ||
36
- device.commandEncoder.handle.beginComputePass({
50
+ commandEncoder.beginComputePass({
37
51
  label: this.props.id,
38
52
  timestampWrites
39
53
  });
40
54
  }
41
55
 
42
56
  /** @note no WebGPU destroy method, just gc */
43
- override destroy(): void {}
57
+ override destroy(): void {
58
+ this.destroyResource();
59
+ }
44
60
 
45
61
  end(): void {
62
+ if (this.destroyed) {
63
+ return;
64
+ }
46
65
  this.handle.end();
66
+ this.destroy();
47
67
  }
48
68
 
49
69
  setPipeline(pipeline: ComputePipeline): void {
50
70
  const wgpuPipeline = pipeline as WebGPUComputePipeline;
51
71
  this.handle.setPipeline(wgpuPipeline.handle);
52
72
  this._webgpuPipeline = wgpuPipeline;
53
- this.setBindings([]);
73
+ const bindGroups = _getDefaultBindGroupFactory(this.device).getBindGroups(
74
+ this._webgpuPipeline,
75
+ this._webgpuPipeline._getBindingsByGroupWebGPU(),
76
+ this._webgpuPipeline._getBindGroupCacheKeysWebGPU()
77
+ );
78
+ for (const [group, bindGroup] of Object.entries(bindGroups)) {
79
+ if (bindGroup) {
80
+ this.handle.setBindGroup(Number(group), bindGroup as GPUBindGroup);
81
+ }
82
+ }
54
83
  }
55
84
 
56
85
  /**
57
86
  * Sets an array of bindings (uniform buffers, samplers, textures, ...)
58
87
  * TODO - still some API confusion - does this method go here or on the pipeline?
59
88
  */
60
- setBindings(bindings: Binding[]): void {
61
- // @ts-expect-error
62
- const bindGroup = this._webgpuPipeline._getBindGroup();
63
- this.handle.setBindGroup(0, bindGroup);
89
+ setBindings(bindings: Bindings | BindingsByGroup): void {
90
+ const bindGroups =
91
+ (this._webgpuPipeline &&
92
+ _getDefaultBindGroupFactory(this.device).getBindGroups(this._webgpuPipeline, bindings)) ||
93
+ {};
94
+ for (const [group, bindGroup] of Object.entries(bindGroups)) {
95
+ if (bindGroup) {
96
+ this.handle.setBindGroup(Number(group), bindGroup as GPUBindGroup);
97
+ }
98
+ }
64
99
  }
65
100
 
66
101
  /**
@@ -2,11 +2,19 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import {ComputePipeline, ComputePipelineProps, Binding} from '@luma.gl/core';
6
- import {getBindGroup} from '../helpers/get-bind-group';
5
+ import {
6
+ ComputePipeline,
7
+ ComputePipelineProps,
8
+ Bindings,
9
+ BindingsByGroup,
10
+ _getDefaultBindGroupFactory,
11
+ normalizeBindingsByGroup
12
+ } from '@luma.gl/core';
7
13
  import {WebGPUDevice} from '../webgpu-device';
8
14
  import {WebGPUShader} from './webgpu-shader';
9
15
 
16
+ const EMPTY_BIND_GROUPS: BindingsByGroup = {};
17
+
10
18
  // COMPUTE PIPELINE
11
19
 
12
20
  /** Creates a new compute pipeline when parameters change */
@@ -14,11 +22,8 @@ export class WebGPUComputePipeline extends ComputePipeline {
14
22
  readonly device: WebGPUDevice;
15
23
  readonly handle: GPUComputePipeline;
16
24
 
17
- /** For internal use to create BindGroups */
18
- private _bindGroupLayout: GPUBindGroupLayout | null = null;
19
- private _bindGroup: GPUBindGroup | null = null;
20
- /** For internal use to create BindGroups */
21
- private _bindings: Record<string, Binding> = {};
25
+ private _bindingsByGroup: BindingsByGroup;
26
+ private _bindGroupCacheKeysByGroup: Partial<Record<number, object>>;
22
27
 
23
28
  constructor(device: WebGPUDevice, props: ComputePipelineProps) {
24
29
  super(device, props);
@@ -37,26 +42,52 @@ export class WebGPUComputePipeline extends ComputePipeline {
37
42
  },
38
43
  layout: 'auto'
39
44
  });
45
+
46
+ this._bindingsByGroup = EMPTY_BIND_GROUPS;
47
+ this._bindGroupCacheKeysByGroup = {};
40
48
  }
41
49
 
42
50
  /**
43
51
  * @todo Use renderpass.setBindings() ?
44
52
  * @todo Do we want to expose BindGroups in the API and remove this?
45
53
  */
46
- setBindings(bindings: Record<string, Binding>): void {
47
- Object.assign(this._bindings, bindings);
54
+ setBindings(bindings: Bindings | BindingsByGroup): void {
55
+ const nextBindingsByGroup = normalizeBindingsByGroup(this.shaderLayout, bindings);
56
+ for (const [groupKey, groupBindings] of Object.entries(nextBindingsByGroup)) {
57
+ const group = Number(groupKey);
58
+ for (const [name, binding] of Object.entries(groupBindings || {})) {
59
+ const currentGroupBindings = this._bindingsByGroup[group] || {};
60
+ if (currentGroupBindings[name] !== binding) {
61
+ if (
62
+ !this._bindingsByGroup[group] ||
63
+ this._bindingsByGroup[group] === currentGroupBindings
64
+ ) {
65
+ this._bindingsByGroup[group] = {...currentGroupBindings};
66
+ }
67
+ this._bindingsByGroup[group][name] = binding;
68
+ this._bindGroupCacheKeysByGroup[group] = {};
69
+ }
70
+ }
71
+ }
48
72
  }
49
73
 
50
- /** Return a bind group created by setBindings */
51
- _getBindGroup() {
52
- // Get hold of the bind group layout. We don't want to do this unless we know there is at least one bind group
53
- this._bindGroupLayout = this._bindGroupLayout || this.handle.getBindGroupLayout(0);
74
+ _getBindGroups(
75
+ bindings?: Bindings | BindingsByGroup,
76
+ bindGroupCacheKeys?: Partial<Record<number, object>>
77
+ ): Partial<Record<number, unknown>> {
78
+ const hasExplicitBindings = Boolean(bindings);
79
+ return _getDefaultBindGroupFactory(this.device).getBindGroups(
80
+ this,
81
+ hasExplicitBindings ? bindings : this._bindingsByGroup,
82
+ hasExplicitBindings ? bindGroupCacheKeys : this._bindGroupCacheKeysByGroup
83
+ );
84
+ }
54
85
 
55
- // Set up the bindings
56
- this._bindGroup =
57
- this._bindGroup ||
58
- getBindGroup(this.device.handle, this._bindGroupLayout, this.shaderLayout, this._bindings);
86
+ _getBindingsByGroupWebGPU(): BindingsByGroup {
87
+ return this._bindingsByGroup;
88
+ }
59
89
 
60
- return this._bindGroup;
90
+ _getBindGroupCacheKeysWebGPU(): Partial<Record<number, object>> {
91
+ return this._bindGroupCacheKeysByGroup;
61
92
  }
62
93
  }
@@ -0,0 +1,38 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {Fence, type FenceProps} from '@luma.gl/core';
6
+ import {WebGPUDevice} from '../webgpu-device';
7
+
8
+ /** WebGPU fence implemented by waiting for submitted work */
9
+ export class WebGPUFence extends Fence {
10
+ readonly device: WebGPUDevice;
11
+ readonly handle: null = null;
12
+ readonly signaled: Promise<void>;
13
+ private _signaled = false;
14
+
15
+ constructor(device: WebGPUDevice, props: FenceProps = {}) {
16
+ super(device, {});
17
+ this.device = device;
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
+ });
29
+ }
30
+
31
+ isSignaled(): boolean {
32
+ return this._signaled;
33
+ }
34
+
35
+ override destroy(): void {
36
+ // Nothing to release for WebGPU fence
37
+ }
38
+ }
@@ -29,4 +29,25 @@ export class WebGPUFramebuffer extends Framebuffer {
29
29
  protected updateAttachments(): void {
30
30
  // WebGPU framebuffers are JS only objects, nothing to update
31
31
  }
32
+
33
+ /**
34
+ * Internal-only hook for the cached CanvasContext/PresentationContext swapchain path.
35
+ * Rebinds the long-lived default framebuffer wrapper to the current per-frame color view
36
+ * and optional depth attachment without allocating a new luma.gl Framebuffer object.
37
+ */
38
+ _reinitialize(
39
+ colorAttachment: WebGPUTextureView,
40
+ depthStencilAttachment: WebGPUTextureView | null
41
+ ): void {
42
+ this.colorAttachments[0] = colorAttachment;
43
+ // @ts-expect-error Internal-only canvas wrapper reuse mutates this otherwise-readonly attachment.
44
+ this.depthStencilAttachment = depthStencilAttachment;
45
+ this.width = colorAttachment.texture.width;
46
+ this.height = colorAttachment.texture.height;
47
+
48
+ this.props.width = this.width;
49
+ this.props.height = this.height;
50
+ this.props.colorAttachments = [colorAttachment.texture];
51
+ this.props.depthStencilAttachment = depthStencilAttachment?.texture || null;
52
+ }
32
53
  }
@@ -20,19 +20,16 @@ export class WebGPUPipelineLayout extends PipelineLayout {
20
20
 
21
21
  this.device = device;
22
22
 
23
- const bindGroupEntries = this.mapShaderLayoutToBindGroupEntries();
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: 'bind-group-layout',
33
- entries: bindGroupEntries
29
+ label: `bind-group-layout-${group}`,
30
+ entries
34
31
  })
35
- ]
32
+ )
36
33
  });
37
34
  }
38
35
 
@@ -42,13 +39,17 @@ export class WebGPUPipelineLayout extends PipelineLayout {
42
39
  this.handle = null;
43
40
  }
44
41
 
45
- protected mapShaderLayoutToBindGroupEntries(): GPUBindGroupLayoutEntry[] {
46
- // Set up the pipeline layout
47
- // TODO (kaapp): This only supports the first group, but so does the rest of the code
48
- const bindGroupEntries: GPUBindGroupLayoutEntry[] = [];
49
-
50
- for (let i = 0; i < this.props.shaderLayout.bindings.length; i++) {
51
- const binding = this.props.shaderLayout.bindings[i];
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
+ );
51
+
52
+ for (const binding of this.props.shaderLayout.bindings) {
52
53
  const bindingTypeInfo: Omit<GPUBindGroupLayoutEntry, 'binding' | 'visibility'> = {};
53
54
 
54
55
  switch (binding.type) {
@@ -113,14 +114,14 @@ export class WebGPUPipelineLayout extends PipelineLayout {
113
114
  const VISIBILITY_ALL =
114
115
  GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE;
115
116
 
116
- bindGroupEntries.push({
117
+ bindGroupEntriesByGroup[binding.group].push({
117
118
  binding: binding.location,
118
119
  visibility: binding.visibility || VISIBILITY_ALL,
119
120
  ...bindingTypeInfo
120
121
  });
121
122
  }
122
123
 
123
- return bindGroupEntries;
124
+ return bindGroupEntriesByGroup;
124
125
  }
125
126
  }
126
127