@luma.gl/webgpu 9.0.0-alpha.9 → 9.0.0-beta.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.
Files changed (142) hide show
  1. package/LICENSE +3 -1
  2. package/dist/adapter/helpers/accessor-to-format.js +102 -1
  3. package/dist/adapter/helpers/convert-texture-format.d.ts +2 -2
  4. package/dist/adapter/helpers/convert-texture-format.d.ts.map +1 -1
  5. package/dist/adapter/helpers/convert-texture-format.js +8 -6
  6. package/dist/adapter/helpers/get-bind-group.d.ts +4 -4
  7. package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
  8. package/dist/adapter/helpers/get-bind-group.js +61 -52
  9. package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts +5 -5
  10. package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts.map +1 -1
  11. package/dist/adapter/helpers/get-vertex-buffer-layout.js +123 -90
  12. package/dist/adapter/helpers/webgpu-parameters.d.ts +2 -2
  13. package/dist/adapter/helpers/webgpu-parameters.d.ts.map +1 -1
  14. package/dist/adapter/helpers/webgpu-parameters.js +184 -130
  15. package/dist/adapter/resources/webgpu-buffer.d.ts +14 -5
  16. package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -1
  17. package/dist/adapter/resources/webgpu-buffer.js +117 -70
  18. package/dist/adapter/resources/webgpu-command-encoder.d.ts +13 -14
  19. package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
  20. package/dist/adapter/resources/webgpu-command-encoder.js +83 -65
  21. package/dist/adapter/resources/webgpu-compute-pass.d.ts +19 -13
  22. package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -1
  23. package/dist/adapter/resources/webgpu-compute-pass.js +78 -57
  24. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts +16 -6
  25. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
  26. package/dist/adapter/resources/webgpu-compute-pipeline.js +48 -26
  27. package/dist/adapter/resources/webgpu-external-texture.d.ts +5 -5
  28. package/dist/adapter/resources/webgpu-external-texture.d.ts.map +1 -1
  29. package/dist/adapter/resources/webgpu-external-texture.js +38 -29
  30. package/dist/adapter/resources/webgpu-framebuffer.d.ts +4 -21
  31. package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
  32. package/dist/adapter/resources/webgpu-framebuffer.js +15 -109
  33. package/dist/adapter/resources/webgpu-query-set.d.ts +17 -0
  34. package/dist/adapter/resources/webgpu-query-set.d.ts.map +1 -0
  35. package/dist/adapter/resources/webgpu-query-set.js +27 -0
  36. package/dist/adapter/resources/webgpu-render-pass.d.ts +14 -7
  37. package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
  38. package/dist/adapter/resources/webgpu-render-pass.js +136 -91
  39. package/dist/adapter/resources/webgpu-render-pipeline.d.ts +63 -23
  40. package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
  41. package/dist/adapter/resources/webgpu-render-pipeline.js +151 -148
  42. package/dist/adapter/resources/webgpu-sampler.d.ts +5 -5
  43. package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -1
  44. package/dist/adapter/resources/webgpu-sampler.js +23 -23
  45. package/dist/adapter/resources/webgpu-shader.d.ts +9 -12
  46. package/dist/adapter/resources/webgpu-shader.d.ts.map +1 -1
  47. package/dist/adapter/resources/webgpu-shader.js +47 -60
  48. package/dist/adapter/resources/webgpu-texture-view.d.ts +20 -0
  49. package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -0
  50. package/dist/adapter/resources/webgpu-texture-view.js +35 -0
  51. package/dist/adapter/resources/webgpu-texture.d.ts +20 -10
  52. package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
  53. package/dist/adapter/resources/webgpu-texture.js +133 -105
  54. package/dist/adapter/resources/webgpu-vertex-array.d.ts +27 -0
  55. package/dist/adapter/resources/webgpu-vertex-array.d.ts.map +1 -0
  56. package/dist/adapter/resources/webgpu-vertex-array.js +66 -0
  57. package/dist/adapter/webgpu-canvas-context.d.ts +17 -12
  58. package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
  59. package/dist/adapter/webgpu-canvas-context.js +105 -92
  60. package/dist/adapter/webgpu-device.d.ts +45 -30
  61. package/dist/adapter/webgpu-device.d.ts.map +1 -1
  62. package/dist/adapter/webgpu-device.js +266 -241
  63. package/dist/dist.dev.js +1664 -0
  64. package/dist/dist.min.js +9 -0
  65. package/dist/index.cjs +1432 -0
  66. package/dist/index.cjs.map +7 -0
  67. package/dist/index.d.ts +5 -7
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +10 -8
  70. package/dist.min.js +9 -0
  71. package/package.json +22 -11
  72. package/src/adapter/helpers/accessor-to-format.ts +6 -3
  73. package/src/adapter/helpers/convert-texture-format.ts +5 -2
  74. package/src/adapter/helpers/get-bind-group.ts +30 -14
  75. package/src/adapter/helpers/get-vertex-buffer-layout.ts +73 -39
  76. package/src/adapter/helpers/webgpu-parameters.ts +99 -29
  77. package/src/adapter/resources/webgpu-buffer.ts +85 -17
  78. package/src/adapter/resources/webgpu-command-encoder.ts +93 -58
  79. package/src/adapter/resources/webgpu-compute-pass.ts +56 -32
  80. package/src/adapter/resources/webgpu-compute-pipeline.ts +51 -18
  81. package/src/adapter/resources/webgpu-external-texture.ts +19 -11
  82. package/src/adapter/resources/webgpu-framebuffer.ts +11 -108
  83. package/src/adapter/resources/webgpu-query-set.ts +37 -0
  84. package/src/adapter/resources/webgpu-render-pass.ts +95 -19
  85. package/src/adapter/resources/webgpu-render-pipeline.ts +139 -159
  86. package/src/adapter/resources/webgpu-sampler.ts +10 -7
  87. package/src/adapter/resources/webgpu-shader.ts +30 -35
  88. package/src/adapter/resources/webgpu-texture-view.ts +46 -0
  89. package/src/adapter/resources/webgpu-texture.ts +75 -41
  90. package/src/adapter/resources/webgpu-vertex-array.ts +91 -0
  91. package/src/adapter/webgpu-canvas-context.ts +60 -29
  92. package/src/adapter/webgpu-device.ts +219 -120
  93. package/src/index.ts +8 -9
  94. package/dist/adapter/helpers/accessor-to-format.js.map +0 -1
  95. package/dist/adapter/helpers/convert-texture-format.js.map +0 -1
  96. package/dist/adapter/helpers/generate-mipmaps.d.ts +0 -10
  97. package/dist/adapter/helpers/generate-mipmaps.d.ts.map +0 -1
  98. package/dist/adapter/helpers/generate-mipmaps.js +0 -95
  99. package/dist/adapter/helpers/generate-mipmaps.js.map +0 -1
  100. package/dist/adapter/helpers/get-bind-group.js.map +0 -1
  101. package/dist/adapter/helpers/get-vertex-buffer-layout.js.map +0 -1
  102. package/dist/adapter/helpers/webgpu-parameters.js.map +0 -1
  103. package/dist/adapter/resources/webgpu-buffer.js.map +0 -1
  104. package/dist/adapter/resources/webgpu-command-encoder.js.map +0 -1
  105. package/dist/adapter/resources/webgpu-compute-pass.js.map +0 -1
  106. package/dist/adapter/resources/webgpu-compute-pipeline.js.map +0 -1
  107. package/dist/adapter/resources/webgpu-external-texture.js.map +0 -1
  108. package/dist/adapter/resources/webgpu-framebuffer.js.map +0 -1
  109. package/dist/adapter/resources/webgpu-query.d.ts +0 -1
  110. package/dist/adapter/resources/webgpu-query.d.ts.map +0 -1
  111. package/dist/adapter/resources/webgpu-query.js +0 -2
  112. package/dist/adapter/resources/webgpu-query.js.map +0 -1
  113. package/dist/adapter/resources/webgpu-render-pass.js.map +0 -1
  114. package/dist/adapter/resources/webgpu-render-pipeline.js.map +0 -1
  115. package/dist/adapter/resources/webgpu-sampler.js.map +0 -1
  116. package/dist/adapter/resources/webgpu-shader.js.map +0 -1
  117. package/dist/adapter/resources/webgpu-texture.js.map +0 -1
  118. package/dist/adapter/webgpu-canvas-context.js.map +0 -1
  119. package/dist/adapter/webgpu-device.js.map +0 -1
  120. package/dist/adapter/webgpu-types.d.ts +0 -1
  121. package/dist/adapter/webgpu-types.d.ts.map +0 -1
  122. package/dist/adapter/webgpu-types.js +0 -2
  123. package/dist/adapter/webgpu-types.js.map +0 -1
  124. package/dist/bundle.d.ts +0 -2
  125. package/dist/bundle.d.ts.map +0 -1
  126. package/dist/bundle.js +0 -5
  127. package/dist/bundle.js.map +0 -1
  128. package/dist/glsl/glsllang.d.ts +0 -3
  129. package/dist/glsl/glsllang.d.ts.map +0 -1
  130. package/dist/glsl/glsllang.js +0 -10
  131. package/dist/glsl/glsllang.js.map +0 -1
  132. package/dist/index.js.map +0 -1
  133. package/dist/init.d.ts +0 -2
  134. package/dist/init.d.ts.map +0 -1
  135. package/dist/init.js +0 -4
  136. package/dist/init.js.map +0 -1
  137. package/src/adapter/helpers/generate-mipmaps.ts +0 -107
  138. package/src/adapter/resources/webgpu-query.ts +0 -43
  139. package/src/adapter/webgpu-types.ts +0 -0
  140. package/src/bundle.ts +0 -4
  141. package/src/glsl/glsllang.ts +0 -14
  142. package/src/init.ts +0 -4
@@ -1,11 +1,17 @@
1
- import type {RenderPassProps, RenderPassParameters, Binding} from '@luma.gl/api';
2
- import {Buffer, RenderPass, RenderPipeline, cast, log} from '@luma.gl/api';
3
- import WebGPUDevice from '../webgpu-device';
4
- import WebGPUBuffer from './webgpu-buffer';
5
- // import WebGPUCommandEncoder from './webgpu-command-encoder';
6
- import WebGPURenderPipeline from './webgpu-render-pipeline';
7
-
8
- export default class WebGPURenderPass extends RenderPass {
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {RenderPassProps, RenderPassParameters, Binding, Framebuffer} from '@luma.gl/core';
6
+ import {Buffer, RenderPass, RenderPipeline, cast, log} from '@luma.gl/core';
7
+ import {WebGPUDevice} from '../webgpu-device';
8
+ import {WebGPUBuffer} from './webgpu-buffer';
9
+ import {WebGPUTextureView} from './webgpu-texture-view';
10
+ // import {WebGPUCommandEncoder} from './webgpu-command-encoder';
11
+ import {WebGPURenderPipeline} from './webgpu-render-pipeline';
12
+ import {WebGPUQuerySet} from './webgpu-query-set';
13
+
14
+ export class WebGPURenderPass extends RenderPass {
9
15
  readonly device: WebGPUDevice;
10
16
  readonly handle: GPURenderPassEncoder;
11
17
 
@@ -16,16 +22,35 @@ export default class WebGPURenderPass extends RenderPass {
16
22
  super(device, props);
17
23
  this.device = device;
18
24
  const framebuffer = props.framebuffer || device.canvasContext.getCurrentFramebuffer();
19
- // @ts-expect-error
20
- const renderPassDescriptor = framebuffer.renderPassDescriptor;
25
+ const renderPassDescriptor = this.getRenderPassDescriptor(framebuffer);
26
+
27
+ const webgpuQuerySet = props.timestampQuerySet as WebGPUQuerySet;
28
+ if (webgpuQuerySet) {
29
+ renderPassDescriptor.occlusionQuerySet = webgpuQuerySet.handle;
30
+ }
31
+
32
+ if (device.features.has('timestamp-query')) {
33
+ const webgpuQuerySet = props.timestampQuerySet as WebGPUQuerySet;
34
+ renderPassDescriptor.timestampWrites = webgpuQuerySet
35
+ ? ({
36
+ querySet: webgpuQuerySet.handle,
37
+ beginningOfPassWriteIndex: props.beginTimestampIndex,
38
+ endOfPassWriteIndex: props.endTimestampIndex
39
+ } as GPUComputePassTimestampWrites)
40
+ : undefined;
41
+ }
42
+
21
43
  this.handle = this.props.handle || device.commandEncoder.beginRenderPass(renderPassDescriptor);
22
44
  this.handle.label = this.props.id;
45
+ log.groupCollapsed(3, `new WebGPURenderPass(${this.id})`)();
46
+ log.probe(3, JSON.stringify(renderPassDescriptor, null, 2))();
47
+ log.groupEnd(3)();
23
48
  }
24
49
 
25
- destroy() {}
50
+ override destroy(): void {}
26
51
 
27
- endPass(): void {
28
- this.handle.endPass();
52
+ end(): void {
53
+ this.handle.end();
29
54
  }
30
55
 
31
56
  setPipeline(pipeline: RenderPipeline): void {
@@ -75,7 +100,7 @@ export default class WebGPURenderPass extends RenderPass {
75
100
  } else {
76
101
  this.handle.draw(
77
102
  options.vertexCount || 0,
78
- options.instanceCount,
103
+ options.instanceCount || 1,
79
104
  options.firstIndex,
80
105
  options.firstInstance
81
106
  );
@@ -121,11 +146,62 @@ export default class WebGPURenderPass extends RenderPass {
121
146
  this.handle.insertDebugMarker(markerLabel);
122
147
  }
123
148
 
124
- // writeTimestamp(querySet: GPUQuerySet, queryIndex: number): void;
125
- // beginOcclusionQuery(queryIndex: number): void;
126
- // endOcclusionQuery(): void;
127
- // beginPipelineStatisticsQuery(querySet: GPUQuerySet, queryIndex: number): void;
128
- // endPipelineStatisticsQuery(querySet: GPUQuerySet, queryIndex: number): void;
149
+ beginOcclusionQuery(queryIndex: number): void {
150
+ this.handle.beginOcclusionQuery(queryIndex);
151
+ }
152
+ endOcclusionQuery(): void {
153
+ this.handle.endOcclusionQuery();
154
+ }
129
155
 
130
156
  // executeBundles(bundles: Iterable<GPURenderBundle>): void;
157
+
158
+ // INTERNAL
159
+
160
+ /**
161
+ * Partial render pass descriptor. Used by WebGPURenderPass.
162
+ * @returns attachments fields of a renderpass descriptor.
163
+ */
164
+ protected getRenderPassDescriptor(framebuffer: Framebuffer): GPURenderPassDescriptor {
165
+ const renderPassDescriptor: GPURenderPassDescriptor = {
166
+ colorAttachments: []
167
+ };
168
+
169
+ renderPassDescriptor.colorAttachments = framebuffer.colorAttachments.map(colorAttachment => ({
170
+ // clear values
171
+ loadOp: this.props.clearColor !== false ? 'clear' : 'load',
172
+ colorClearValue: this.props.clearColor || [0, 0, 0, 0],
173
+ storeOp: this.props.discard ? 'discard' : 'store',
174
+ // ...colorAttachment,
175
+ view: (colorAttachment as WebGPUTextureView).handle
176
+ }));
177
+
178
+ if (framebuffer.depthStencilAttachment) {
179
+ renderPassDescriptor.depthStencilAttachment = {
180
+ view: (framebuffer.depthStencilAttachment as WebGPUTextureView).handle
181
+ };
182
+ const {depthStencilAttachment} = renderPassDescriptor;
183
+
184
+ // DEPTH
185
+ if (this.props.depthReadOnly) {
186
+ depthStencilAttachment.depthReadOnly = true;
187
+ }
188
+ depthStencilAttachment.depthClearValue = this.props.clearDepth || 0;
189
+
190
+ // WebGPU only wants us to set these parameters if the texture format actually has a depth aspect
191
+ const hasDepthAspect = true;
192
+ if (hasDepthAspect) {
193
+ depthStencilAttachment.depthLoadOp = this.props.clearDepth !== false ? 'clear' : 'load';
194
+ depthStencilAttachment.depthStoreOp = 'store'; // TODO - support 'discard'?
195
+ }
196
+
197
+ // WebGPU only wants us to set these parameters if the texture format actually has a stencil aspect
198
+ const hasStencilAspect = false;
199
+ if (hasStencilAspect) {
200
+ depthStencilAttachment.stencilLoadOp = this.props.clearStencil !== false ? 'clear' : 'load';
201
+ depthStencilAttachment.stencilStoreOp = 'store'; // TODO - support 'discard'?
202
+ }
203
+ }
204
+
205
+ return renderPassDescriptor;
206
+ }
131
207
  }
@@ -1,157 +1,179 @@
1
- import type {Binding, RenderPass, Shader} from '@luma.gl/api';
2
- import {Buffer, RenderPipeline, RenderPipelineProps, cast, log, isObjectEmpty} from '@luma.gl/api';
1
+ // luma.gl MIT license
2
+
3
+ import type {Binding, RenderPass, VertexArray} from '@luma.gl/core';
4
+ import {RenderPipeline, RenderPipelineProps, cast, log} from '@luma.gl/core';
3
5
  import {applyParametersToRenderPipelineDescriptor} from '../helpers/webgpu-parameters';
4
6
  import {getWebGPUTextureFormat} from '../helpers/convert-texture-format';
5
7
  import {getBindGroup} from '../helpers/get-bind-group';
6
- import {getVertexBufferLayout, getBufferSlots} from '../helpers/get-vertex-buffer-layout';
8
+ import {getVertexBufferLayout} from '../helpers/get-vertex-buffer-layout';
7
9
  // import {convertAttributesVertexBufferToLayout} from '../helpers/get-vertex-buffer-layout';
8
10
  // import {mapAccessorToWebGPUFormat} from './helpers/accessor-to-format';
9
11
  // import type {BufferAccessors} from './webgpu-pipeline';
10
12
 
11
- import type WebGPUDevice from '../webgpu-device';
12
- import type WebGPUBuffer from './webgpu-buffer';
13
- import type WebGPUShader from './webgpu-shader';
14
- import type WebGPURenderPass from './webgpu-render-pass';
13
+ import type {WebGPUDevice} from '../webgpu-device';
14
+ // import type {WebGPUBuffer} from './webgpu-buffer';
15
+ import type {WebGPUShader} from './webgpu-shader';
16
+ import type {WebGPURenderPass} from './webgpu-render-pass';
15
17
 
16
18
  // RENDER PIPELINE
17
19
 
18
20
  /** Creates a new render pipeline when parameters change */
19
- export default class WebGPURenderPipeline extends RenderPipeline {
21
+ export class WebGPURenderPipeline extends RenderPipeline {
20
22
  device: WebGPUDevice;
21
23
  handle: GPURenderPipeline;
22
24
 
23
25
  vs: WebGPUShader;
24
26
  fs: WebGPUShader | null = null;
25
27
 
26
- private _bufferSlots: Record<string, number>;
27
- private _buffers: Buffer[];
28
- private _indexBuffer: WebGPUBuffer | null = null;
29
- // private _firstIndex: number;
30
- // private _lastIndex: number;
31
-
32
28
  /** For internal use to create BindGroups */
33
- private _bindGroupLayout: GPUBindGroupLayout;
29
+ private _bindings: Record<string, Binding>;
30
+ private _bindGroupLayout: GPUBindGroupLayout | null = null;
34
31
  private _bindGroup: GPUBindGroup | null = null;
35
32
 
36
33
  constructor(device: WebGPUDevice, props: RenderPipelineProps) {
37
34
  super(device, props);
38
35
  this.device = device;
39
- this.handle = (this.props.handle as GPURenderPipeline) || this.createHandle();
36
+ this.handle = this.props.handle as GPURenderPipeline;
37
+ if (!this.handle) {
38
+ const descriptor = this._getRenderPipelineDescriptor();
39
+ log.groupCollapsed(1, `new WebGPURenderPipeline(${this.id})`)();
40
+ log.probe(1, JSON.stringify(descriptor, null, 2))();
41
+ log.groupEnd(1)();
42
+ this.handle = this.device.handle.createRenderPipeline(descriptor);
43
+ }
40
44
  this.handle.label = this.props.id;
41
45
 
42
46
  this.vs = cast<WebGPUShader>(props.vs);
43
47
  this.fs = cast<WebGPUShader>(props.fs);
44
48
 
45
- this._bufferSlots = getBufferSlots(this.props.layout, this.props.bufferMap);
46
- this._buffers = new Array<Buffer>(Object.keys(this._bufferSlots).length).fill(null);
47
- this._bindGroupLayout = this.handle.getBindGroupLayout(0);
49
+ this._bindings = {...this.props.bindings};
48
50
  }
49
51
 
50
- protected createHandle(): GPURenderPipeline {
51
- const descriptor = this._getRenderPipelineDescriptor();
52
- const renderPipeline = this.device.handle.createRenderPipeline(descriptor);
53
- log.groupCollapsed(1, `new WebGPRenderPipeline(${this.id})`)();
54
- log.log(1, JSON.stringify(descriptor, null, 2))();
55
- log.groupEnd(1)();
56
- return renderPipeline;
57
- }
58
-
59
- destroy() {
52
+ override destroy(): void {
60
53
  // WebGPURenderPipeline has no destroy method.
54
+ this.handle = null;
61
55
  }
62
56
 
63
- setIndexBuffer(indexBuffer: Buffer): void {
64
- this._indexBuffer = cast<WebGPUBuffer>(indexBuffer);
57
+ /**
58
+ * @todo Use renderpass.setBindings() ?
59
+ * @todo Do we want to expose BindGroups in the API and remove this?
60
+ */
61
+ setBindings(bindings: Record<string, Binding>): void {
62
+ Object.assign(this._bindings, bindings);
65
63
  }
66
64
 
67
- setAttributes(attributes: Record<string, Buffer>): void {
68
- for (const [name, buffer] of Object.entries(attributes)) {
69
- const bufferIndex = this._bufferSlots[name];
70
- if (bufferIndex >= 0) {
71
- this._buffers[bufferIndex] = buffer;
72
- } else {
73
- throw new Error(
74
- `Setting attribute '${name}' not listed in shader layout for program ${this.id}`
75
- );
76
- }
65
+ /** @todo - should this be moved to renderpass? */
66
+ draw(options: {
67
+ renderPass: RenderPass;
68
+ vertexArray: VertexArray;
69
+ vertexCount?: number;
70
+ indexCount?: number;
71
+ instanceCount?: number;
72
+ firstVertex?: number;
73
+ firstIndex?: number;
74
+ firstInstance?: number;
75
+ baseVertex?: number;
76
+ }): boolean {
77
+ const webgpuRenderPass = options.renderPass as WebGPURenderPass;
78
+
79
+ // Set pipeline
80
+ webgpuRenderPass.handle.setPipeline(this.handle);
81
+
82
+ // Set bindings (uniform buffers, textures etc)
83
+ const bindGroup = this._getBindGroup();
84
+ if (bindGroup) {
85
+ webgpuRenderPass.handle.setBindGroup(0, bindGroup);
77
86
  }
78
- // for (let i = 0; i < this._bufferSlots.length; ++i) {
79
- // const bufferName = this._bufferSlots[i];
80
- // if (attributes[bufferName]) {
81
- // this.handle
82
- // }
83
- // }
84
- }
85
87
 
86
- /** Set the bindings */
87
- setBindings(bindings: Record<string, Binding>): void {
88
- if (!isObjectEmpty(this.props.bindings)) {
89
- Object.assign(this.props.bindings, bindings);
90
- // Set up the bindings
91
- this._bindGroup = getBindGroup(
92
- this.device.handle,
93
- this._bindGroupLayout,
94
- this.props.layout,
95
- this.props.bindings
88
+ // Set attributes
89
+ // Note: Rebinds constant attributes before each draw call
90
+ options.vertexArray.bindBeforeRender(options.renderPass);
91
+
92
+ // Draw
93
+ if (options.indexCount) {
94
+ webgpuRenderPass.handle.drawIndexed(
95
+ options.indexCount,
96
+ options.instanceCount,
97
+ options.firstIndex,
98
+ options.baseVertex,
99
+ options.firstInstance
100
+ );
101
+ } else {
102
+ webgpuRenderPass.handle.draw(
103
+ options.vertexCount || 0,
104
+ options.instanceCount || 1, // If 0, nothing will be drawn
105
+ options.firstInstance
96
106
  );
97
107
  }
98
- }
99
108
 
100
- setUniforms(uniforms: Record<string, any>): void {
101
- if (!isObjectEmpty(uniforms)) {
102
- throw new Error('WebGPU does not support uniforms');
103
- }
104
- }
109
+ // Note: Rebinds constant attributes before each draw call
110
+ options.vertexArray.unbindAfterRender(options.renderPass);
105
111
 
106
- _getBuffers() {
107
- return this._buffers;
112
+ return true;
108
113
  }
109
114
 
110
115
  /** Return a bind group created by setBindings */
111
116
  _getBindGroup() {
112
- // assert(this._bindGroup);
117
+ if (this.props.shaderLayout.bindings.length === 0) {
118
+ return null;
119
+ }
120
+
121
+ // Get hold of the bind group layout. We don't want to do this unless we know there is at least one bind group
122
+ this._bindGroupLayout = this._bindGroupLayout || this.handle.getBindGroupLayout(0);
123
+
124
+ // Set up the bindings
125
+ // TODO what if bindings change? We need to rebuild the bind group!
126
+ this._bindGroup =
127
+ this._bindGroup ||
128
+ getBindGroup(
129
+ this.device.handle,
130
+ this._bindGroupLayout,
131
+ this.props.shaderLayout,
132
+ this._bindings
133
+ );
134
+
113
135
  return this._bindGroup;
114
136
  }
115
137
 
116
- /** Populate the complex WebGPU GPURenderPipelineDescriptor */
138
+ /**
139
+ * Populate the complex WebGPU GPURenderPipelineDescriptor
140
+ */
117
141
  protected _getRenderPipelineDescriptor() {
118
142
  // Set up the vertex stage
119
143
  const vertex: GPUVertexState = {
120
144
  module: cast<WebGPUShader>(this.props.vs).handle,
121
- entryPoint: this.props.vsEntryPoint || 'main',
122
- buffers: getVertexBufferLayout(this.props.layout, this.props.bufferMap)
145
+ entryPoint: this.props.vertexEntryPoint || 'main',
146
+ buffers: getVertexBufferLayout(this.props.shaderLayout, this.props.bufferLayout)
123
147
  };
124
148
 
125
149
  // Set up the fragment stage
126
- let fragment: GPUFragmentState | undefined;
127
- if (this.props.fs) {
128
- fragment = {
129
- module: cast<WebGPUShader>(this.props.fs).handle,
130
- entryPoint: this.props.fsEntryPoint || 'main',
131
- targets: [
132
- {
133
- // TODO exclamation mark hack!
134
- format: getWebGPUTextureFormat(this.device?.canvasContext?.format!)
135
- }
136
- ]
137
- };
138
- }
150
+ const fragment: GPUFragmentState = {
151
+ module: cast<WebGPUShader>(this.props.fs).handle,
152
+ entryPoint: this.props.fragmentEntryPoint || 'main',
153
+ targets: [
154
+ {
155
+ // TODO exclamation mark hack!
156
+ format: getWebGPUTextureFormat(this.device?.canvasContext?.format)
157
+ }
158
+ ]
159
+ };
139
160
 
140
161
  // WebGPU has more restrictive topology support than WebGL
141
162
  switch (this.props.topology) {
142
- case 'triangle-fan':
143
- case 'line-loop':
163
+ case 'triangle-fan-webgl':
164
+ case 'line-loop-webgl':
144
165
  throw new Error(`WebGPU does not support primitive topology ${this.props.topology}`);
145
166
  default:
146
167
  }
147
168
 
148
169
  // Create a partially populated descriptor
149
- let descriptor: GPURenderPipelineDescriptor = {
170
+ const descriptor: GPURenderPipelineDescriptor = {
150
171
  vertex,
151
172
  fragment,
152
173
  primitive: {
153
174
  topology: this.props.topology
154
- }
175
+ },
176
+ layout: 'auto'
155
177
  };
156
178
 
157
179
  // Set parameters on the descriptor
@@ -159,88 +181,46 @@ export default class WebGPURenderPipeline extends RenderPipeline {
159
181
 
160
182
  return descriptor;
161
183
  }
184
+ }
185
+ /**
186
+ _setAttributeBuffers(webgpuRenderPass: WebGPURenderPass) {
187
+ if (this._indexBuffer) {
188
+ webgpuRenderPass.handle.setIndexBuffer(this._indexBuffer.handle, this._indexBuffer.props.indexType);
189
+ }
162
190
 
163
- draw(options: {
164
- renderPass?: RenderPass;
165
- vertexCount?: number;
166
- indexCount?: number;
167
- instanceCount?: number;
168
- firstVertex?: number;
169
- firstIndex?: number;
170
- firstInstance?: number;
171
- baseVertex?: number;
172
- }): void {
173
- const webgpuRenderPass =
174
- cast<WebGPURenderPass>(options.renderPass) || this.device.getDefaultRenderPass();
175
-
176
- // Set pipeline
177
- webgpuRenderPass.handle.setPipeline(this.handle);
178
-
179
- // Set bindings (uniform buffers, textures etc)
180
- if (this._getBindGroup()) {
181
- webgpuRenderPass.handle.setBindGroup(0, this._getBindGroup());
182
- }
183
-
184
- // Set attributes
185
- this._setAttributeBuffers(webgpuRenderPass);
186
-
187
- // Draw
188
- if (options.indexCount) {
189
- webgpuRenderPass.handle.drawIndexed(
190
- options.indexCount,
191
- options.instanceCount,
192
- options.firstIndex,
193
- options.baseVertex,
194
- options.firstInstance
191
+ const buffers = this._getBuffers();
192
+ for (let i = 0; i < buffers.length; ++i) {
193
+ const buffer = cast<WebGPUBuffer>(buffers[i]);
194
+ if (!buffer) {
195
+ const attribute = this.props.shaderLayout.attributes.find(
196
+ (attribute) => attribute.location === i
195
197
  );
196
- } else {
197
- webgpuRenderPass.handle.draw(
198
- options.vertexCount || 0,
199
- options.instanceCount,
200
- options.firstIndex,
201
- options.firstInstance
198
+ throw new Error(
199
+ `No buffer provided for attribute '${attribute?.name || ''}' in Model '${this.props.id}'`
202
200
  );
203
201
  }
202
+ webgpuRenderPass.handle.setVertexBuffer(i, buffer.handle);
204
203
  }
205
204
 
206
- _setAttributeBuffers(webgpuRenderPass: WebGPURenderPass) {
207
- if (this._indexBuffer) {
208
- webgpuRenderPass.handle.setIndexBuffer(this._indexBuffer.handle, this._indexBuffer.props.indexType);
205
+ // TODO - HANDLE buffer maps
206
+ /*
207
+ for (const [bufferName, attributeMapping] of Object.entries(this.props.bufferLayout)) {
208
+ const buffer = cast<WebGPUBuffer>(this.props.attributes[bufferName]);
209
+ if (!buffer) {
210
+ log.warn(`Missing buffer for buffer map ${bufferName}`)();
211
+ continue;
209
212
  }
210
213
 
211
- const buffers = this._getBuffers();
212
- for (let i = 0; i < buffers.length; ++i) {
213
- const buffer = cast<WebGPUBuffer>(buffers[i]);
214
- if (!buffer) {
215
- const attribute = this.props.layout.attributes.find(
216
- (attribute) => attribute.location === i
217
- );
218
- throw new Error(
219
- `No buffer provided for attribute '${attribute?.name || ''}' in Model '${this.props.id}'`
220
- );
221
- }
222
- webgpuRenderPass.handle.setVertexBuffer(i, buffer.handle);
223
- }
224
-
225
- // TODO - HANDLE buffer maps
226
- /*
227
- for (const [bufferName, attributeMapping] of Object.entries(this.props.bufferMap)) {
228
- const buffer = cast<WebGPUBuffer>(this.props.attributes[bufferName]);
229
- if (!buffer) {
230
- log.warn(`Missing buffer for buffer map ${bufferName}`)();
231
- continue;
232
- }
233
-
234
- if ('location' in attributeMapping) {
214
+ if ('location' in attributeMapping) {
215
+ // @ts-expect-error TODO model must not depend on webgpu
216
+ renderPass.handle.setVertexBuffer(layout.location, buffer.handle);
217
+ } else {
218
+ for (const [bufferName, mapping] of Object.entries(attributeMapping)) {
235
219
  // @ts-expect-error TODO model must not depend on webgpu
236
- renderPass.handle.setVertexBuffer(layout.location, buffer.handle);
237
- } else {
238
- for (const [bufferName, mapping] of Object.entries(attributeMapping)) {
239
- // @ts-expect-error TODO model must not depend on webgpu
240
- renderPass.handle.setVertexBuffer(field.location, buffer.handle);
241
- }
220
+ renderPass.handle.setVertexBuffer(field.location, buffer.handle);
242
221
  }
243
222
  }
244
- */
245
223
  }
224
+ *
246
225
  }
226
+ */
@@ -1,14 +1,14 @@
1
- import {Sampler, SamplerProps} from '@luma.gl/api';
2
- import type WebGPUDevice from '../webgpu-device';
1
+ import {Sampler, SamplerProps} from '@luma.gl/core';
2
+ import type {WebGPUDevice} from '../webgpu-device';
3
3
 
4
4
  export type WebGPUSamplerProps = SamplerProps & {
5
5
  handle?: GPUSampler;
6
- }
6
+ };
7
7
 
8
8
  /**
9
9
  *
10
10
  */
11
- export default class WebGPUSampler extends Sampler {
11
+ export class WebGPUSampler extends Sampler {
12
12
  readonly device: WebGPUDevice;
13
13
  readonly handle: GPUSampler;
14
14
 
@@ -17,16 +17,19 @@ export default class WebGPUSampler extends Sampler {
17
17
  this.device = device;
18
18
 
19
19
  // Prepare sampler props
20
- const samplerProps = this.props;
20
+ const samplerProps: Partial<WebGPUSamplerProps> = {...this.props};
21
21
  if (samplerProps.type !== 'comparison-sampler') {
22
22
  delete samplerProps.compare;
23
23
  }
24
24
 
25
- this.handle = this.handle || this.device.handle.createSampler(this.props);
25
+ this.handle = this.handle || this.device.handle.createSampler(samplerProps);
26
26
  this.handle.label = this.props.id;
27
27
  }
28
28
 
29
- destroy(): void {
29
+ override destroy(): void {
30
+ // GPUSampler does not have a destroy method
30
31
  // this.handle.destroy();
32
+ // @ts-expect-error readonly
33
+ this.handle = null;
31
34
  }
32
35
  }
@@ -1,19 +1,19 @@
1
- import type {ShaderProps, CompilerMessage} from '@luma.gl/api';
2
- import {Shader, log} from '@luma.gl/api';
3
- import type WebGPUDevice from '../webgpu-device';
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
4
 
5
- export type WebGPUShaderProps = ShaderProps & {
6
- handle?: GPUShaderModule;
7
- };
5
+ import type {ShaderProps, CompilerMessage} from '@luma.gl/core';
6
+ import {Shader, log} from '@luma.gl/core';
7
+ import type {WebGPUDevice} from '../webgpu-device';
8
8
 
9
9
  /**
10
10
  * Immutable shader
11
11
  */
12
- export default class WebGPUShader extends Shader {
12
+ export class WebGPUShader extends Shader {
13
13
  readonly device: WebGPUDevice;
14
14
  readonly handle: GPUShaderModule;
15
15
 
16
- constructor(device: WebGPUDevice, props: WebGPUShaderProps) {
16
+ constructor(device: WebGPUDevice, props: ShaderProps) {
17
17
  super(device, props);
18
18
  this.device = device;
19
19
 
@@ -25,10 +25,13 @@ export default class WebGPUShader extends Shader {
25
25
  this._checkCompilationError(this.device.handle.popErrorScope());
26
26
  }
27
27
 
28
- async _checkCompilationError(errorScope: Promise<GPUError>): Promise<void> {
29
- const error = await errorScope as GPUValidationError;
28
+ async _checkCompilationError(errorScope: Promise<GPUError | null>): Promise<void> {
29
+ const error = (await errorScope) as GPUValidationError;
30
30
  if (error) {
31
- const shaderLog = await this.compilationInfo();
31
+ // The `Shader` base class will determine if debug window should be opened based on props
32
+ this.debugShader();
33
+
34
+ const shaderLog = await this.getCompilationInfo();
32
35
  log.error(`Shader compilation error: ${error.message}`, shaderLog)();
33
36
  // Note: Even though this error is asynchronous and thrown after the constructor completes,
34
37
  // it will result in a useful stack trace leading back to the constructor
@@ -36,37 +39,29 @@ export default class WebGPUShader extends Shader {
36
39
  }
37
40
  }
38
41
 
39
- destroy() {
42
+ override destroy(): void {
43
+ // Note: WebGPU does not offer a method to destroy shaders
40
44
  // this.handle.destroy();
45
+ // @ts-expect-error readonly
46
+ this.handle = null;
47
+ }
48
+
49
+ /** Returns compilation info for this shader */
50
+ async getCompilationInfo(): Promise<readonly CompilerMessage[]> {
51
+ const compilationInfo = await this.handle.getCompilationInfo();
52
+ return compilationInfo.messages;
41
53
  }
42
54
 
55
+ // PRIVATE METHODS
56
+
43
57
  protected createHandle(): GPUShaderModule {
44
58
  const {source} = this.props;
45
59
 
46
- let language = this.props.language;
47
- // Compile from src
48
- if (!language) {
49
- // wgsl uses C++ "auto" style arrow notation
50
- language = source.includes('->') ? 'wgsl' : 'glsl';
60
+ const isGLSL = source.includes('#version');
61
+ if (this.props.language === 'glsl' || isGLSL) {
62
+ throw new Error('GLSL shaders are not supported in WebGPU');
51
63
  }
52
64
 
53
- switch(language) {
54
- case 'wgsl':
55
- return this.device.handle.createShaderModule({code: source});
56
- case 'glsl':
57
- return this.device.handle.createShaderModule({
58
- code: source,
59
- // @ts-expect-error
60
- transform: (glsl) => this.device.glslang.compileGLSL(glsl, type)
61
- });
62
- default:
63
- throw new Error(language);
64
- }
65
- }
66
-
67
- /** Returns compilation info for this shader */
68
- async compilationInfo(): Promise<readonly CompilerMessage[]> {
69
- const compilationInfo = await this.handle.compilationInfo();
70
- return compilationInfo.messages;
65
+ return this.device.handle.createShaderModule({code: source});
71
66
  }
72
67
  }