@luma.gl/webgpu 9.3.0-alpha.4 → 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.
Files changed (125) 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 +4 -6
  10. package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
  11. package/dist/adapter/helpers/get-bind-group.js +31 -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.map +1 -1
  21. package/dist/adapter/resources/webgpu-buffer.js +19 -3
  22. package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
  23. package/dist/adapter/resources/webgpu-command-buffer.js +1 -1
  24. package/dist/adapter/resources/webgpu-command-buffer.js.map +1 -1
  25. package/dist/adapter/resources/webgpu-command-encoder.d.ts +7 -16
  26. package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
  27. package/dist/adapter/resources/webgpu-command-encoder.js +89 -32
  28. package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -1
  29. package/dist/adapter/resources/webgpu-compute-pass.d.ts +3 -3
  30. package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -1
  31. package/dist/adapter/resources/webgpu-compute-pass.js +30 -12
  32. package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -1
  33. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts +7 -9
  34. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
  35. package/dist/adapter/resources/webgpu-compute-pipeline.js +30 -17
  36. package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
  37. package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -1
  38. package/dist/adapter/resources/webgpu-fence.js +9 -1
  39. package/dist/adapter/resources/webgpu-fence.js.map +1 -1
  40. package/dist/adapter/resources/webgpu-framebuffer.d.ts +6 -0
  41. package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
  42. package/dist/adapter/resources/webgpu-framebuffer.js +16 -0
  43. package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -1
  44. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts +1 -1
  45. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
  46. package/dist/adapter/resources/webgpu-pipeline-layout.js +10 -16
  47. package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
  48. package/dist/adapter/resources/webgpu-query-set.d.ts +33 -4
  49. package/dist/adapter/resources/webgpu-query-set.d.ts.map +1 -1
  50. package/dist/adapter/resources/webgpu-query-set.js +145 -4
  51. package/dist/adapter/resources/webgpu-query-set.js.map +1 -1
  52. package/dist/adapter/resources/webgpu-render-pass.d.ts +6 -3
  53. package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
  54. package/dist/adapter/resources/webgpu-render-pass.js +78 -34
  55. package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
  56. package/dist/adapter/resources/webgpu-render-pipeline.d.ts +14 -10
  57. package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
  58. package/dist/adapter/resources/webgpu-render-pipeline.js +56 -35
  59. package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
  60. package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -1
  61. package/dist/adapter/resources/webgpu-sampler.js +4 -0
  62. package/dist/adapter/resources/webgpu-sampler.js.map +1 -1
  63. package/dist/adapter/resources/webgpu-shader.d.ts.map +1 -1
  64. package/dist/adapter/resources/webgpu-shader.js +17 -1
  65. package/dist/adapter/resources/webgpu-shader.js.map +1 -1
  66. package/dist/adapter/resources/webgpu-texture-view.d.ts +6 -0
  67. package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -1
  68. package/dist/adapter/resources/webgpu-texture-view.js +47 -11
  69. package/dist/adapter/resources/webgpu-texture-view.js.map +1 -1
  70. package/dist/adapter/resources/webgpu-texture.d.ts +18 -5
  71. package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
  72. package/dist/adapter/resources/webgpu-texture.js +148 -97
  73. package/dist/adapter/resources/webgpu-texture.js.map +1 -1
  74. package/dist/adapter/resources/webgpu-vertex-array.js +1 -1
  75. package/dist/adapter/resources/webgpu-vertex-array.js.map +1 -1
  76. package/dist/adapter/webgpu-canvas-context.d.ts +2 -0
  77. package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
  78. package/dist/adapter/webgpu-canvas-context.js +78 -19
  79. package/dist/adapter/webgpu-canvas-context.js.map +1 -1
  80. package/dist/adapter/webgpu-device.d.ts +10 -2
  81. package/dist/adapter/webgpu-device.d.ts.map +1 -1
  82. package/dist/adapter/webgpu-device.js +159 -13
  83. package/dist/adapter/webgpu-device.js.map +1 -1
  84. package/dist/adapter/webgpu-presentation-context.d.ts +25 -0
  85. package/dist/adapter/webgpu-presentation-context.d.ts.map +1 -0
  86. package/dist/adapter/webgpu-presentation-context.js +144 -0
  87. package/dist/adapter/webgpu-presentation-context.js.map +1 -0
  88. package/dist/dist.dev.js +3180 -1849
  89. package/dist/dist.min.js +168 -9
  90. package/dist/index.cjs +1640 -405
  91. package/dist/index.cjs.map +4 -4
  92. package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -1
  93. package/dist/wgsl/get-shader-layout-wgsl.js +8 -0
  94. package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -1
  95. package/package.json +5 -5
  96. package/src/adapter/helpers/cpu-hotspot-profiler.ts +70 -0
  97. package/src/adapter/helpers/generate-mipmaps-webgpu.ts +583 -0
  98. package/src/adapter/helpers/get-bind-group.ts +42 -49
  99. package/src/adapter/helpers/get-vertex-buffer-layout.ts +31 -12
  100. package/src/adapter/helpers/webgpu-parameters.ts +2 -0
  101. package/src/adapter/resources/webgpu-buffer.ts +18 -3
  102. package/src/adapter/resources/webgpu-command-buffer.ts +1 -1
  103. package/src/adapter/resources/webgpu-command-encoder.ts +129 -50
  104. package/src/adapter/resources/webgpu-compute-pass.ts +48 -13
  105. package/src/adapter/resources/webgpu-compute-pipeline.ts +49 -18
  106. package/src/adapter/resources/webgpu-fence.ts +11 -3
  107. package/src/adapter/resources/webgpu-framebuffer.ts +21 -0
  108. package/src/adapter/resources/webgpu-pipeline-layout.ts +16 -14
  109. package/src/adapter/resources/webgpu-query-set.ts +185 -9
  110. package/src/adapter/resources/webgpu-render-pass.ts +92 -40
  111. package/src/adapter/resources/webgpu-render-pipeline.ts +83 -44
  112. package/src/adapter/resources/webgpu-sampler.ts +5 -0
  113. package/src/adapter/resources/webgpu-shader.ts +16 -1
  114. package/src/adapter/resources/webgpu-texture-view.ts +51 -11
  115. package/src/adapter/resources/webgpu-texture.ts +198 -132
  116. package/src/adapter/resources/webgpu-vertex-array.ts +1 -1
  117. package/src/adapter/webgpu-canvas-context.ts +91 -26
  118. package/src/adapter/webgpu-device.ts +212 -17
  119. package/src/adapter/webgpu-presentation-context.ts +180 -0
  120. package/src/wgsl/get-shader-layout-wgsl.ts +9 -0
  121. package/dist/adapter/helpers/accessor-to-format.d.ts +0 -1
  122. package/dist/adapter/helpers/accessor-to-format.d.ts.map +0 -1
  123. package/dist/adapter/helpers/accessor-to-format.js +0 -105
  124. package/dist/adapter/helpers/accessor-to-format.js.map +0 -1
  125. package/src/adapter/helpers/accessor-to-format.ts +0 -104
@@ -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, getVertexFormatInfo} from '@luma.gl/core';
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(shaderLayout, attributeName, usedAttributes);
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(shaderLayout, mapping.name, usedAttributes);
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 shaderLayout.attributes) {
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 shaderLayout.attributes) {
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.find(attribute_ => attribute_.name === name);
178
+ const attribute = shaderLayout.attributes?.find(attribute_ => attribute_.name === name);
165
179
  if (!attribute) {
166
- log.warn(`Supplied attribute not present in shader layout: ${name}`)();
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;
@@ -67,12 +67,27 @@ export class WebGPUBuffer extends Buffer {
67
67
  this.device.reportError(new Error(`${this} creation failed ${error.message}`), this)();
68
68
  this.device.debug();
69
69
  });
70
+
71
+ if (!this.props.handle) {
72
+ this.trackAllocatedMemory(size);
73
+ } else {
74
+ this.trackReferencedMemory(size, 'Buffer');
75
+ }
70
76
  }
71
77
 
72
78
  override destroy(): void {
73
- this.handle?.destroy();
74
- // @ts-expect-error readonly
75
- this.handle = null;
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
+ }
76
91
  }
77
92
 
78
93
  write(data: ArrayBufferLike | ArrayBufferView | SharedArrayBuffer, byteOffset = 0) {
@@ -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 ||
@@ -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
  }
@@ -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.onSubmittedWorkDone().then(() => {
19
- this._signaled = true;
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 {
@@ -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
  }