@luma.gl/webgpu 9.1.9 → 9.2.0-alpha.2

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 (100) hide show
  1. package/dist/adapter/helpers/accessor-to-format.js +1 -0
  2. package/dist/adapter/helpers/accessor-to-format.js.map +1 -1
  3. package/dist/adapter/helpers/get-bind-group.d.ts +3 -1
  4. package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
  5. package/dist/adapter/helpers/get-bind-group.js +28 -10
  6. package/dist/adapter/helpers/get-bind-group.js.map +1 -1
  7. package/dist/adapter/helpers/get-vertex-buffer-layout.js +5 -5
  8. package/dist/adapter/helpers/get-vertex-buffer-layout.js.map +1 -1
  9. package/dist/adapter/helpers/webgpu-parameters.d.ts.map +1 -1
  10. package/dist/adapter/helpers/webgpu-parameters.js +89 -75
  11. package/dist/adapter/helpers/webgpu-parameters.js.map +1 -1
  12. package/dist/adapter/resources/webgpu-buffer.d.ts +13 -16
  13. package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -1
  14. package/dist/adapter/resources/webgpu-buffer.js +132 -93
  15. package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
  16. package/dist/adapter/resources/webgpu-command-buffer.d.ts +10 -0
  17. package/dist/adapter/resources/webgpu-command-buffer.d.ts.map +1 -0
  18. package/dist/adapter/resources/webgpu-command-buffer.js +18 -0
  19. package/dist/adapter/resources/webgpu-command-buffer.js.map +1 -0
  20. package/dist/adapter/resources/webgpu-command-encoder.d.ts +12 -5
  21. package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
  22. package/dist/adapter/resources/webgpu-command-encoder.js +28 -5
  23. package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -1
  24. package/dist/adapter/resources/webgpu-compute-pass.js +1 -1
  25. package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -1
  26. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts +2 -2
  27. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
  28. package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
  29. package/dist/adapter/resources/webgpu-external-texture.d.ts +2 -2
  30. package/dist/adapter/resources/webgpu-external-texture.d.ts.map +1 -1
  31. package/dist/adapter/resources/webgpu-external-texture.js.map +1 -1
  32. package/dist/adapter/resources/webgpu-framebuffer.d.ts +3 -2
  33. package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
  34. package/dist/adapter/resources/webgpu-framebuffer.js +1 -0
  35. package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -1
  36. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts +2 -2
  37. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
  38. package/dist/adapter/resources/webgpu-pipeline-layout.js +5 -2
  39. package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
  40. package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
  41. package/dist/adapter/resources/webgpu-render-pass.js +11 -6
  42. package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
  43. package/dist/adapter/resources/webgpu-render-pipeline.d.ts +5 -4
  44. package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
  45. package/dist/adapter/resources/webgpu-render-pipeline.js +33 -20
  46. package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
  47. package/dist/adapter/resources/webgpu-sampler.js +1 -1
  48. package/dist/adapter/resources/webgpu-sampler.js.map +1 -1
  49. package/dist/adapter/resources/webgpu-shader.d.ts.map +1 -1
  50. package/dist/adapter/resources/webgpu-shader.js +7 -8
  51. package/dist/adapter/resources/webgpu-shader.js.map +1 -1
  52. package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -1
  53. package/dist/adapter/resources/webgpu-texture-view.js +15 -10
  54. package/dist/adapter/resources/webgpu-texture-view.js.map +1 -1
  55. package/dist/adapter/resources/webgpu-texture.d.ts +4 -37
  56. package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
  57. package/dist/adapter/resources/webgpu-texture.js +97 -126
  58. package/dist/adapter/resources/webgpu-texture.js.map +1 -1
  59. package/dist/adapter/resources/webgpu-vertex-array.d.ts +2 -2
  60. package/dist/adapter/resources/webgpu-vertex-array.d.ts.map +1 -1
  61. package/dist/adapter/resources/webgpu-vertex-array.js +2 -2
  62. package/dist/adapter/resources/webgpu-vertex-array.js.map +1 -1
  63. package/dist/adapter/webgpu-adapter.d.ts +2 -3
  64. package/dist/adapter/webgpu-adapter.d.ts.map +1 -1
  65. package/dist/adapter/webgpu-adapter.js +54 -43
  66. package/dist/adapter/webgpu-adapter.js.map +1 -1
  67. package/dist/adapter/webgpu-canvas-context.d.ts +7 -15
  68. package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
  69. package/dist/adapter/webgpu-canvas-context.js +50 -72
  70. package/dist/adapter/webgpu-canvas-context.js.map +1 -1
  71. package/dist/adapter/webgpu-device.d.ts +16 -27
  72. package/dist/adapter/webgpu-device.d.ts.map +1 -1
  73. package/dist/adapter/webgpu-device.js +48 -65
  74. package/dist/adapter/webgpu-device.js.map +1 -1
  75. package/dist/dist.dev.js +1919 -1520
  76. package/dist/dist.min.js +6 -5
  77. package/dist/index.cjs +1749 -1397
  78. package/dist/index.cjs.map +4 -4
  79. package/package.json +3 -3
  80. package/src/adapter/helpers/get-bind-group.ts +31 -11
  81. package/src/adapter/helpers/get-vertex-buffer-layout.ts +5 -5
  82. package/src/adapter/helpers/webgpu-parameters.ts +114 -102
  83. package/src/adapter/resources/webgpu-buffer.ts +163 -102
  84. package/src/adapter/resources/webgpu-command-buffer.ts +24 -0
  85. package/src/adapter/resources/webgpu-command-encoder.ts +34 -4
  86. package/src/adapter/resources/webgpu-compute-pass.ts +1 -1
  87. package/src/adapter/resources/webgpu-compute-pipeline.ts +2 -2
  88. package/src/adapter/resources/webgpu-external-texture.ts +2 -2
  89. package/src/adapter/resources/webgpu-framebuffer.ts +3 -2
  90. package/src/adapter/resources/webgpu-pipeline-layout.ts +8 -3
  91. package/src/adapter/resources/webgpu-render-pass.ts +11 -6
  92. package/src/adapter/resources/webgpu-render-pipeline.ts +39 -24
  93. package/src/adapter/resources/webgpu-sampler.ts +1 -1
  94. package/src/adapter/resources/webgpu-shader.ts +11 -8
  95. package/src/adapter/resources/webgpu-texture-view.ts +14 -8
  96. package/src/adapter/resources/webgpu-texture.ts +106 -186
  97. package/src/adapter/resources/webgpu-vertex-array.ts +2 -2
  98. package/src/adapter/webgpu-adapter.ts +72 -58
  99. package/src/adapter/webgpu-canvas-context.ts +62 -82
  100. package/src/adapter/webgpu-device.ts +66 -105
@@ -3,7 +3,7 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import type {ShaderProps, CompilerMessage} from '@luma.gl/core';
6
- import {Shader, log} from '@luma.gl/core';
6
+ import {Shader} from '@luma.gl/core';
7
7
  import type {WebGPUDevice} from '../webgpu-device';
8
8
 
9
9
  /**
@@ -22,12 +22,15 @@ export class WebGPUShader extends Shader {
22
22
  throw new Error('GLSL shaders are not supported in WebGPU');
23
23
  }
24
24
 
25
- this.device.handle.pushErrorScope('validation');
25
+ this.device.pushErrorScope('validation');
26
26
  this.handle = this.props.handle || this.device.handle.createShaderModule({code: props.source});
27
- this.device.handle.popErrorScope().then((error: GPUError | null) => {
28
- if (error) {
29
- log.error(`${this} creation failed:\n"${error.message}"`, this, this.props.source)();
30
- }
27
+ this.device.popErrorScope((error: GPUError) => {
28
+ this.device.reportError(
29
+ new Error(`${this} creation failed:\n"${error.message}"`),
30
+ this,
31
+ this.props.source
32
+ )();
33
+ this.device.debug();
31
34
  });
32
35
 
33
36
  this.handle.label = this.props.id;
@@ -45,10 +48,10 @@ export class WebGPUShader extends Shader {
45
48
  this.debugShader();
46
49
 
47
50
  if (this.compilationStatus === 'error') {
48
- log.error(`Shader compilation error`, shaderLog)();
49
51
  // Note: Even though this error is asynchronous and thrown after the constructor completes,
50
52
  // it will result in a useful stack trace leading back to the constructor
51
- // throw new Error(`Shader compilation error`);
53
+ this.device.reportError(new Error(`Shader compilation error`), this, shaderLog)();
54
+ this.device.debug();
52
55
  }
53
56
  }
54
57
 
@@ -38,17 +38,23 @@ export class WebGPUTextureView extends TextureView {
38
38
  this.device = device;
39
39
  this.texture = props.texture;
40
40
 
41
+ this.device.pushErrorScope('validation');
41
42
  this.handle =
42
- this.handle ||
43
+ // props.handle ||
43
44
  this.texture.handle.createView({
44
- format: (props.format || this.texture.format) as GPUTextureFormat,
45
- dimension: props.dimension || this.texture.dimension,
46
- aspect: props.aspect,
47
- baseMipLevel: props.baseMipLevel,
48
- mipLevelCount: props.mipLevelCount, // GPUIntegerCoordinate;
49
- baseArrayLayer: props.baseArrayLayer, // GPUIntegerCoordinate;
50
- arrayLayerCount: props.arrayLayerCount // GPUIntegerCoordinate;
45
+ format: (this.props.format || this.texture.format) as GPUTextureFormat,
46
+ dimension: this.props.dimension || this.texture.dimension,
47
+ aspect: this.props.aspect,
48
+ baseMipLevel: this.props.baseMipLevel,
49
+ mipLevelCount: this.props.mipLevelCount,
50
+ baseArrayLayer: this.props.baseArrayLayer,
51
+ arrayLayerCount: this.props.arrayLayerCount
51
52
  });
53
+ this.device.popErrorScope((error: GPUError) => {
54
+ this.device.reportError(new Error(`TextureView constructor: ${error.message}`), this)();
55
+ this.device.debug();
56
+ });
57
+
52
58
  this.handle.label = this.props.id;
53
59
  }
54
60
 
@@ -1,42 +1,21 @@
1
1
  // luma.gl, MIT license
2
2
  import type {
3
- // Device,
4
3
  TextureProps,
5
4
  TextureViewProps,
6
- Sampler,
7
- SamplerProps,
8
- // TextureFormat,
9
- // TextureCubeFace,
10
- // ExternalImage,
11
- // TextureLevelData,
12
- Texture1DData,
13
- Texture2DData,
14
- Texture3DData,
15
- TextureCubeData,
16
- TextureArrayData,
17
- TextureCubeArrayData,
18
- ExternalImage
5
+ CopyExternalImageOptions,
6
+ CopyImageDataOptions,
7
+ SamplerProps
19
8
  } from '@luma.gl/core';
20
- import {Texture} from '@luma.gl/core';
9
+ import {Texture, log} from '@luma.gl/core';
21
10
 
22
11
  import {getWebGPUTextureFormat} from '../helpers/convert-texture-format';
23
12
  import type {WebGPUDevice} from '../webgpu-device';
24
13
  import {WebGPUSampler} from './webgpu-sampler';
25
14
  import {WebGPUTextureView} from './webgpu-texture-view';
26
15
 
27
- const BASE_DIMENSIONS: Record<string, '1d' | '2d' | '3d'> = {
28
- '1d': '1d',
29
- '2d': '2d',
30
- '2d-array': '2d',
31
- cube: '2d',
32
- 'cube-array': '2d',
33
- '3d': '3d'
34
- };
35
-
36
16
  export class WebGPUTexture extends Texture {
37
17
  readonly device: WebGPUDevice;
38
18
  readonly handle: GPUTexture;
39
-
40
19
  sampler: WebGPUSampler;
41
20
  view: WebGPUTextureView;
42
21
 
@@ -44,197 +23,138 @@ export class WebGPUTexture extends Texture {
44
23
  super(device, props);
45
24
  this.device = device;
46
25
 
47
- // Texture base class strips out the data prop, so we need to add it back in
48
- const propsWithData = {...this.props};
49
- if (props.data) {
50
- propsWithData.data = props.data;
26
+ if (this.dimension === 'cube') {
27
+ this.depth = 6;
51
28
  }
52
29
 
53
- this.initialize(propsWithData);
54
- }
55
-
56
- override destroy(): void {
57
- this.handle?.destroy();
58
- // @ts-expect-error readonly
59
- this.handle = null;
60
- }
61
-
62
- createView(props: TextureViewProps): WebGPUTextureView {
63
- return new WebGPUTextureView(this.device, {...props, texture: this});
64
- }
65
-
66
- protected initialize(props: TextureProps): void {
67
- // @ts-expect-error
68
- this.handle = this.props.handle || this.createHandle();
69
- this.handle.label ||= this.id;
30
+ this.device.pushErrorScope('out-of-memory');
31
+ this.device.pushErrorScope('validation');
32
+
33
+ this.handle =
34
+ this.props.handle ||
35
+ this.device.handle.createTexture({
36
+ label: this.id,
37
+ size: {
38
+ width: this.width,
39
+ height: this.height,
40
+ depthOrArrayLayers: this.depth
41
+ },
42
+ usage: this.props.usage || Texture.TEXTURE | Texture.COPY_DST,
43
+ dimension: this.baseDimension,
44
+ format: getWebGPUTextureFormat(this.format),
45
+ mipLevelCount: this.mipLevels,
46
+ sampleCount: this.props.samples
47
+ });
48
+ this.device.popErrorScope((error: GPUError) => {
49
+ this.device.reportError(new Error(`${this} constructor: ${error.message}`), this)();
50
+ this.device.debug();
51
+ });
52
+ this.device.popErrorScope((error: GPUError) => {
53
+ this.device.reportError(new Error(`${this} out of memory: ${error.message}`), this)();
54
+ this.device.debug();
55
+ });
70
56
 
71
- if (this.props.data) {
72
- if (Texture.isExternalImage(this.props.data)) {
73
- this.copyExternalImage({image: this.props.data});
74
- } else {
75
- this.setData({data: this.props.data});
76
- }
57
+ // Update props if external handle was supplied - used mainly by CanvasContext.getDefaultFramebuffer()
58
+ // TODO - Read all properties directly from the supplied handle?
59
+ if (this.props.handle) {
60
+ this.handle.label ||= this.id;
61
+ this.width = this.handle.width;
62
+ this.height = this.handle.height;
77
63
  }
78
64
 
79
- this.width = this.handle.width;
80
- this.height = this.handle.height;
81
- // Why not just read all properties directly from the texture
82
- // this.depthOrArrayLayers = this.handle.depthOrArrayLayers;
83
- // this.mipLevelCount = this.handle.mipLevelCount;
84
- // this.sampleCount = this.handle.sampleCount;
85
- // this.dimension = this.handle.dimension;
86
- // this.format = this.handle.format;
87
- // this.usage = this.handle.usage;
88
-
89
- // Create a default sampler. This mimics the WebGL1 API where sampler props are stored on the texture
90
- // this.setSampler(props.sampler);
91
65
  this.sampler =
92
66
  props.sampler instanceof WebGPUSampler
93
67
  ? props.sampler
94
- : new WebGPUSampler(this.device, props.sampler || {});
95
-
96
- // TODO - To support texture arrays we need to create custom views...
97
- // But we are not ready to expose TextureViews to the public API.
98
- // @ts-expect-error
68
+ : new WebGPUSampler(this.device, (props.sampler as SamplerProps) || {});
99
69
 
100
- this.view = new WebGPUTextureView(this.device, {...this.props, texture: this});
101
- // format: this.props.format,
102
- // dimension: this.props.dimension,
103
- // aspect = "all";
104
- // baseMipLevel: 0;
105
- // mipLevelCount;
106
- // baseArrayLayer = 0;
107
- // arrayLayerCount;
108
- }
109
-
110
- protected createHandle(): GPUTexture {
111
- // Deduce size from data - TODO this is a hack
112
- // @ts-expect-error
113
- const width = this.props.width || this.props.data?.width || 1;
114
- // @ts-expect-error
115
- const height = this.props.height || this.props.data?.height || 1;
116
-
117
- return this.device.handle.createTexture({
118
- label: this.id,
119
- size: {
120
- width,
121
- height,
122
- depthOrArrayLayers: this.depth
123
- },
124
- usage: this.props.usage || Texture.TEXTURE | Texture.COPY_DST,
125
- dimension: BASE_DIMENSIONS[this.dimension],
126
- format: getWebGPUTextureFormat(this.format),
70
+ this.view = new WebGPUTextureView(this.device, {
71
+ ...this.props,
72
+ texture: this,
127
73
  mipLevelCount: this.mipLevels,
128
- sampleCount: this.props.samples
74
+ // Note: arrayLayerCount controls the view of array textures, but does not apply to 3d texture depths
75
+ arrayLayerCount: this.dimension !== '3d' ? this.depth : 1
129
76
  });
130
- }
131
-
132
- /** @deprecated - intention is to use the createView public API */
133
- createGPUTextureView(): GPUTextureView {
134
- return this.handle.createView({label: this.id});
135
- }
136
-
137
- /**
138
- * Set default sampler
139
- * Accept a sampler instance or set of props;
140
- */
141
- setSampler(sampler: Sampler | SamplerProps): this {
142
- this.sampler =
143
- sampler instanceof WebGPUSampler ? sampler : new WebGPUSampler(this.device, sampler);
144
- return this;
145
- }
146
-
147
- setTexture1DData(data: Texture1DData): void {
148
- throw new Error('not implemented');
149
- }
150
-
151
- setTexture2DData(lodData: Texture2DData, depth?: number, target?: number): void {
152
- throw new Error('not implemented');
153
- }
154
-
155
- setTexture3DData(lodData: Texture3DData, depth?: number, target?: number): void {
156
- throw new Error('not implemented');
157
- }
158
77
 
159
- setTextureCubeData(data: TextureCubeData, depth?: number): void {
160
- throw new Error('not implemented');
78
+ // Set initial data
79
+ // Texture base class strips out the data prop from this.props, so we need to handle it here
80
+ this._initializeData(props.data);
161
81
  }
162
82
 
163
- setTextureArrayData(data: TextureArrayData): void {
164
- throw new Error('not implemented');
83
+ override destroy(): void {
84
+ this.handle?.destroy();
85
+ // @ts-expect-error readonly
86
+ this.handle = null;
165
87
  }
166
88
 
167
- setTextureCubeArrayData(data: TextureCubeArrayData): void {
168
- throw new Error('not implemented');
89
+ createView(props: TextureViewProps): WebGPUTextureView {
90
+ return new WebGPUTextureView(this.device, {...props, texture: this});
169
91
  }
170
92
 
171
- setData(options: {data: any}): {width: number; height: number} {
172
- if (ArrayBuffer.isView(options.data)) {
173
- const clampedArray = new Uint8ClampedArray(options.data.buffer);
174
- // TODO - pass through src data color space as ImageData Options?
175
- const image = new ImageData(clampedArray, this.width, this.height);
176
- return this.copyExternalImage({image});
177
- }
178
-
179
- throw new Error('Texture.setData: Use CommandEncoder to upload data to texture in WebGPU');
93
+ copyImageData(options_: CopyImageDataOptions): void {
94
+ const {width, height, depth} = this;
95
+ const options = this._normalizeCopyImageDataOptions(options_);
96
+ this.device.pushErrorScope('validation');
97
+ this.device.handle.queue.writeTexture(
98
+ // destination: GPUImageCopyTexture
99
+ {
100
+ // texture subresource
101
+ texture: this.handle,
102
+ mipLevel: options.mipLevel,
103
+ aspect: options.aspect,
104
+ // origin to write to
105
+ origin: [options.x, options.y, options.z]
106
+ },
107
+ // data
108
+ options.data,
109
+ // dataLayout: GPUImageDataLayout
110
+ {
111
+ offset: options.byteOffset,
112
+ bytesPerRow: options.bytesPerRow,
113
+ rowsPerImage: options.rowsPerImage
114
+ },
115
+ // size: GPUExtent3D - extents of the content to write
116
+ [width, height, depth]
117
+ );
118
+ this.device.popErrorScope((error: GPUError) => {
119
+ this.device.reportError(new Error(`copyImageData: ${error.message}`), this)();
120
+ this.device.debug();
121
+ });
180
122
  }
181
123
 
182
- copyExternalImage(options: {
183
- image: ExternalImage;
184
- width?: number;
185
- height?: number;
186
- depth?: number;
187
- sourceX?: number;
188
- sourceY?: number;
189
- mipLevel?: number;
190
- x?: number;
191
- y?: number;
192
- z?: number;
193
- aspect?: 'all' | 'stencil-only' | 'depth-only';
194
- colorSpace?: 'srgb';
195
- premultipliedAlpha?: boolean;
196
- }): {width: number; height: number} {
197
- const size = Texture.getExternalImageSize(options.image);
198
- const opts = {...Texture.defaultCopyExternalImageOptions, ...size, ...options};
199
- const {
200
- image,
201
- sourceX,
202
- sourceY,
203
- width,
204
- height,
205
- depth,
206
- mipLevel,
207
- x,
208
- y,
209
- z,
210
- aspect,
211
- colorSpace,
212
- premultipliedAlpha,
213
- flipY
214
- } = opts;
215
-
216
- // TODO - max out width
124
+ copyExternalImage(options_: CopyExternalImageOptions): {width: number; height: number} {
125
+ const options = this._normalizeCopyExternalImageOptions(options_);
217
126
 
127
+ this.device.pushErrorScope('validation');
218
128
  this.device.handle.queue.copyExternalImageToTexture(
219
129
  // source: GPUImageCopyExternalImage
220
130
  {
221
- source: image,
222
- origin: [sourceX, sourceY],
223
- flipY
131
+ source: options.image,
132
+ origin: [options.sourceX, options.sourceY],
133
+ flipY: options.flipY
224
134
  },
225
135
  // destination: GPUImageCopyTextureTagged
226
136
  {
227
137
  texture: this.handle,
228
- origin: [x, y, z],
229
- mipLevel,
230
- aspect,
231
- colorSpace,
232
- premultipliedAlpha
138
+ origin: [options.x, options.y, 0], // options.depth],
139
+ mipLevel: options.mipLevel,
140
+ aspect: options.aspect,
141
+ colorSpace: options.colorSpace,
142
+ premultipliedAlpha: options.premultipliedAlpha
233
143
  },
234
144
  // copySize: GPUExtent3D
235
- [width, height, depth]
145
+ [options.width, options.height, 1]
236
146
  );
237
- return {width, height};
147
+ this.device.popErrorScope((error: GPUError) => {
148
+ this.device.reportError(new Error(`copyExternalImage: ${error.message}`), this)();
149
+ this.device.debug();
150
+ });
151
+
152
+ // TODO - should these be clipped to the texture size minus x,y,z?
153
+ return {width: options.width, height: options.height};
154
+ }
155
+
156
+ override generateMipmapsWebGL(): void {
157
+ log.warn(`${this}: generateMipmaps not supported in WebGPU`)();
238
158
  }
239
159
 
240
160
  // WebGPU specific
@@ -256,7 +176,7 @@ export class WebGPUTexture extends Texture {
256
176
  device.submit([encoder.finish()]);
257
177
 
258
178
  // Get the data on the CPU.
259
- await buffer.mapAsync(GPUMapMode.READ);
179
+ await buffer.mapAndReadAsync(GPUMapMode.READ);
260
180
  saveScreenshot(buffer.getMappedRange());
261
181
  buffer.unmap();
262
182
  }
@@ -18,8 +18,8 @@ export class WebGPUVertexArray extends VertexArray {
18
18
  }
19
19
 
20
20
  readonly device: WebGPUDevice;
21
- /** Vertex Array is a helper class under WebGPU */
22
- readonly handle: never;
21
+ /** Vertex Array is just a helper class under WebGPU */
22
+ readonly handle = null;
23
23
 
24
24
  // Create a VertexArray
25
25
  constructor(device: WebGPUDevice, props: VertexArrayProps) {
@@ -2,85 +2,99 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import {Adapter, DeviceProps, log} from '@luma.gl/core';
6
- import {WebGPUDevice} from './webgpu-device';
7
-
5
+ // prettier-ignore
8
6
  // / <reference types="@webgpu/types" />
9
7
 
8
+ import {Adapter, DeviceProps, log} from '@luma.gl/core';
9
+ import type {WebGPUDevice} from './webgpu-device';
10
+
10
11
  export class WebGPUAdapter extends Adapter {
11
12
  /** type of device's created by this adapter */
12
13
  readonly type: WebGPUDevice['type'] = 'webgpu';
13
14
 
14
- constructor() {
15
- super();
16
- // @ts-ignore For backwards compatibility luma.registerDevices
17
- WebGPUDevice.adapter = this;
18
- }
19
-
20
- /** Check if WebGPU is available */
21
15
  isSupported(): boolean {
16
+ // Check if WebGPU is available
22
17
  return Boolean(typeof navigator !== 'undefined' && navigator.gpu);
23
18
  }
24
19
 
20
+ isDeviceHandle(handle: unknown): boolean {
21
+ if (typeof GPUDevice !== 'undefined' && handle instanceof GPUDevice) {
22
+ return true;
23
+ }
24
+
25
+ // TODO - WebGPU does not yet seem to have a stable in-browser API, so we "sniff" for members instead
26
+ if ((handle as any)?.queue) {
27
+ return true;
28
+ }
29
+
30
+ return false;
31
+ }
32
+
25
33
  async create(props: DeviceProps): Promise<WebGPUDevice> {
26
34
  if (!navigator.gpu) {
27
- throw new Error(
28
- 'WebGPU not available. Open in Chrome Canary and turn on chrome://flags/#enable-unsafe-webgpu'
29
- );
35
+ throw new Error('WebGPU not available. Recent Chrome browsers should work.');
30
36
  }
31
- log.groupCollapsed(1, 'WebGPUDevice created')();
32
- const adapter = await navigator.gpu.requestAdapter({
33
- powerPreference: 'high-performance'
34
- // forceSoftware: false
35
- });
36
37
 
37
- if (!adapter) {
38
- throw new Error('Failed to request WebGPU adapter');
39
- }
38
+ log.groupCollapsed(1, 'WebGPUDevice created')();
39
+ try {
40
+ const adapter = await navigator.gpu.requestAdapter({
41
+ powerPreference: 'high-performance'
42
+ // forceSoftware: false
43
+ });
44
+
45
+ if (!adapter) {
46
+ throw new Error('Failed to request WebGPU adapter');
47
+ }
40
48
 
41
- const adapterInfo =
42
- adapter.info ||
43
- // @ts-ignore Chrome has removed this function
44
- (await adapter.requestAdapterInfo?.());
45
- log.probe(2, 'Adapter available', adapterInfo)();
46
-
47
- const requiredFeatures: GPUFeatureName[] = [];
48
- const requiredLimits: Record<string, number> = {};
49
-
50
- if (props._requestMaxLimits) {
51
- // Require all features
52
- requiredFeatures.push(...(Array.from(adapter.features) as GPUFeatureName[]));
53
-
54
- // Require all limits
55
- // Filter out chrome specific keys (avoid crash)
56
- const limits = Object.keys(adapter.limits).filter(
57
- key => !['minSubgroupSize', 'maxSubgroupSize'].includes(key)
58
- );
59
- for (const key of limits) {
60
- const limit = key as keyof GPUSupportedLimits;
61
- const value = adapter.limits[limit];
62
- if (typeof value === 'number') {
63
- requiredLimits[limit] = value;
49
+ // Note: adapter.requestAdapterInfo() has been replaced with adapter.info. Fall back in case adapter.info is not available
50
+ const adapterInfo =
51
+ adapter.info ||
52
+ // @ts-ignore
53
+ (await adapter.requestAdapterInfo?.());
54
+ log.probe(2, 'Adapter available', adapterInfo)();
55
+
56
+ const requiredFeatures: GPUFeatureName[] = [];
57
+ const requiredLimits: Record<string, number> = {};
58
+
59
+ if (props._requestMaxLimits) {
60
+ // Require all features
61
+ requiredFeatures.push(...(Array.from(adapter.features) as GPUFeatureName[]));
62
+
63
+ // Require all limits
64
+ // Filter out chrome specific keys (avoid crash)
65
+ const limits = Object.keys(adapter.limits).filter(
66
+ key => !['minSubgroupSize', 'maxSubgroupSize'].includes(key)
67
+ );
68
+ for (const key of limits) {
69
+ const limit = key as keyof GPUSupportedLimits;
70
+ const value = adapter.limits[limit];
71
+ if (typeof value === 'number') {
72
+ requiredLimits[limit] = value;
73
+ }
64
74
  }
65
75
  }
66
- }
67
76
 
68
- const gpuDevice = await adapter.requestDevice({
69
- requiredFeatures,
70
- requiredLimits
71
- });
77
+ const gpuDevice = await adapter.requestDevice({
78
+ requiredFeatures,
79
+ requiredLimits
80
+ });
81
+
82
+ log.probe(1, 'GPUDevice available')();
72
83
 
73
- log.probe(1, 'GPUDevice available')();
84
+ const {WebGPUDevice} = await import('./webgpu-device');
74
85
 
75
- const device = new WebGPUDevice(props, gpuDevice, adapter, adapterInfo);
86
+ const device = new WebGPUDevice(props, gpuDevice, adapter, adapterInfo);
76
87
 
77
- log.probe(
78
- 1,
79
- 'Device created. For more info, set chrome://flags/#enable-webgpu-developer-features'
80
- )();
81
- log.table(1, device.info)();
82
- log.groupEnd(1)();
83
- return device;
88
+ log.probe(
89
+ 1,
90
+ 'Device created. For more info, set chrome://flags/#enable-webgpu-developer-features'
91
+ )();
92
+ log.table(1, device.info)();
93
+
94
+ return device;
95
+ } finally {
96
+ log.groupEnd(1)();
97
+ }
84
98
  }
85
99
 
86
100
  async attach(handle: GPUDevice): Promise<WebGPUDevice> {