@luma.gl/webgpu 9.2.6 → 9.3.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 (39) hide show
  1. package/dist/adapter/resources/webgpu-fence.d.ts +13 -0
  2. package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -0
  3. package/dist/adapter/resources/webgpu-fence.js +25 -0
  4. package/dist/adapter/resources/webgpu-fence.js.map +1 -0
  5. package/dist/adapter/resources/webgpu-texture.d.ts +12 -3
  6. package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
  7. package/dist/adapter/resources/webgpu-texture.js +143 -26
  8. package/dist/adapter/resources/webgpu-texture.js.map +1 -1
  9. package/dist/adapter/webgpu-adapter.d.ts.map +1 -1
  10. package/dist/adapter/webgpu-adapter.js +34 -34
  11. package/dist/adapter/webgpu-adapter.js.map +1 -1
  12. package/dist/adapter/webgpu-canvas-context.d.ts +4 -3
  13. package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
  14. package/dist/adapter/webgpu-canvas-context.js +22 -21
  15. package/dist/adapter/webgpu-canvas-context.js.map +1 -1
  16. package/dist/adapter/webgpu-device.d.ts +3 -1
  17. package/dist/adapter/webgpu-device.d.ts.map +1 -1
  18. package/dist/adapter/webgpu-device.js +14 -3
  19. package/dist/adapter/webgpu-device.js.map +1 -1
  20. package/dist/dist.dev.js +6438 -274
  21. package/dist/dist.min.js +10 -6
  22. package/dist/index.cjs +347 -86
  23. package/dist/index.cjs.map +4 -4
  24. package/dist/index.d.ts +2 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +2 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/wgsl/get-shader-layout-wgsl.d.ts +8 -0
  29. package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -0
  30. package/dist/wgsl/get-shader-layout-wgsl.js +136 -0
  31. package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -0
  32. package/package.json +5 -4
  33. package/src/adapter/resources/webgpu-fence.ts +30 -0
  34. package/src/adapter/resources/webgpu-texture.ts +202 -88
  35. package/src/adapter/webgpu-adapter.ts +40 -42
  36. package/src/adapter/webgpu-canvas-context.ts +25 -23
  37. package/src/adapter/webgpu-device.ts +19 -4
  38. package/src/index.ts +3 -0
  39. package/src/wgsl/get-shader-layout-wgsl.ts +156 -0
@@ -35,62 +35,60 @@ export class WebGPUAdapter extends Adapter {
35
35
  throw new Error('WebGPU not available. Recent Chrome browsers should work.');
36
36
  }
37
37
 
38
- log.groupCollapsed(1, 'WebGPUDevice created')();
39
- try {
40
- const adapter = await navigator.gpu.requestAdapter({
41
- powerPreference: 'high-performance'
42
- // forceSoftware: false
43
- });
38
+ const adapter = await navigator.gpu.requestAdapter({
39
+ powerPreference: 'high-performance'
40
+ // forceSoftware: false
41
+ });
44
42
 
45
- if (!adapter) {
46
- throw new Error('Failed to request WebGPU adapter');
47
- }
43
+ if (!adapter) {
44
+ throw new Error('Failed to request WebGPU adapter');
45
+ }
48
46
 
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
- }
47
+ // Note: adapter.requestAdapterInfo() has been replaced with adapter.info. Fall back in case adapter.info is not available
48
+ const adapterInfo =
49
+ adapter.info ||
50
+ // @ts-ignore
51
+ (await adapter.requestAdapterInfo?.());
52
+ // log.probe(2, 'Adapter available', adapterInfo)();
53
+
54
+ const requiredFeatures: GPUFeatureName[] = [];
55
+ const requiredLimits: Record<string, number> = {};
56
+
57
+ if (props._requestMaxLimits) {
58
+ // Require all features
59
+ requiredFeatures.push(...(Array.from(adapter.features) as GPUFeatureName[]));
60
+
61
+ // Require all limits
62
+ // Filter out chrome specific keys (avoid crash)
63
+ const limits = Object.keys(adapter.limits).filter(
64
+ key => !['minSubgroupSize', 'maxSubgroupSize'].includes(key)
65
+ );
66
+ for (const key of limits) {
67
+ const limit = key as keyof GPUSupportedLimits;
68
+ const value = adapter.limits[limit];
69
+ if (typeof value === 'number') {
70
+ requiredLimits[limit] = value;
74
71
  }
75
72
  }
73
+ }
76
74
 
77
- const gpuDevice = await adapter.requestDevice({
78
- requiredFeatures,
79
- requiredLimits
80
- });
75
+ const gpuDevice = await adapter.requestDevice({
76
+ requiredFeatures,
77
+ requiredLimits
78
+ });
81
79
 
82
- log.probe(1, 'GPUDevice available')();
80
+ // log.probe(1, 'GPUDevice available')();
83
81
 
84
- const {WebGPUDevice} = await import('./webgpu-device');
82
+ const {WebGPUDevice} = await import('./webgpu-device');
85
83
 
84
+ log.groupCollapsed(1, 'WebGPUDevice created')();
85
+ try {
86
86
  const device = new WebGPUDevice(props, gpuDevice, adapter, adapterInfo);
87
-
88
87
  log.probe(
89
88
  1,
90
89
  'Device created. For more info, set chrome://flags/#enable-webgpu-developer-features'
91
90
  )();
92
91
  log.table(1, device.info)();
93
-
94
92
  return device;
95
93
  } finally {
96
94
  log.groupEnd(1)();
@@ -38,7 +38,7 @@ export class WebGPUCanvasContext extends CanvasContext {
38
38
 
39
39
  // Base class constructor cannot access derived methods/fields, so we need to call these functions in the subclass constructor
40
40
  this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
41
- this._updateDevice();
41
+ this._configureDevice();
42
42
  }
43
43
 
44
44
  /** Destroy any textures produced while configured and remove the context configuration. */
@@ -47,14 +47,34 @@ export class WebGPUCanvasContext extends CanvasContext {
47
47
  super.destroy();
48
48
  }
49
49
 
50
+ // IMPLEMENTATION OF ABSTRACT METHODS
51
+
52
+ /** @see https://www.w3.org/TR/webgpu/#canvas-configuration */
53
+ _configureDevice(): void {
54
+ if (this.depthStencilAttachment) {
55
+ this.depthStencilAttachment.destroy();
56
+ this.depthStencilAttachment = null;
57
+ }
58
+
59
+ // Reconfigure the canvas size.
60
+ this.handle.configure({
61
+ device: this.device.handle,
62
+ format: this.device.preferredColorFormat,
63
+ // Can be used to define e.g. -srgb views
64
+ // viewFormats: [...]
65
+ colorSpace: this.props.colorSpace,
66
+ alphaMode: this.props.alphaMode
67
+ });
68
+ }
69
+
50
70
  /** Update framebuffer with properly resized "swap chain" texture views */
51
- getCurrentFramebuffer(
71
+ _getCurrentFramebuffer(
52
72
  options: {depthStencilFormat?: TextureFormatDepthStencil | false} = {
53
73
  depthStencilFormat: 'depth24plus'
54
74
  }
55
75
  ): WebGPUFramebuffer {
56
76
  // Wrap the current canvas context texture in a luma.gl texture
57
- const currentColorAttachment = this.getCurrentTexture();
77
+ const currentColorAttachment = this._getCurrentTexture();
58
78
  // TODO - temporary debug code
59
79
  if (
60
80
  currentColorAttachment.width !== this.drawingBufferWidth ||
@@ -80,28 +100,10 @@ export class WebGPUCanvasContext extends CanvasContext {
80
100
  });
81
101
  }
82
102
 
83
- // IMPLEMENTATION OF ABSTRACT METHODS
84
-
85
- _updateDevice(): void {
86
- if (this.depthStencilAttachment) {
87
- this.depthStencilAttachment.destroy();
88
- this.depthStencilAttachment = null;
89
- }
90
-
91
- // Reconfigure the canvas size.
92
- // https://www.w3.org/TR/webgpu/#canvas-configuration
93
- this.handle.configure({
94
- device: this.device.handle,
95
- format: this.device.preferredColorFormat,
96
- // Can be used to define e.g. -srgb views
97
- // viewFormats: [...]
98
- colorSpace: this.props.colorSpace,
99
- alphaMode: this.props.alphaMode
100
- });
101
- }
103
+ // PRIMARY METHODS
102
104
 
103
105
  /** Wrap the current canvas context texture in a luma.gl texture */
104
- getCurrentTexture(): WebGPUTexture {
106
+ _getCurrentTexture(): WebGPUTexture {
105
107
  const handle = this.handle.getCurrentTexture();
106
108
  return this.device.createTexture({
107
109
  id: `${this.id}#color-texture`,
@@ -45,6 +45,9 @@ import {WebGPUCommandEncoder} from './resources/webgpu-command-encoder';
45
45
  import {WebGPUCommandBuffer} from './resources/webgpu-command-buffer';
46
46
  import {WebGPUQuerySet} from './resources/webgpu-query-set';
47
47
  import {WebGPUPipelineLayout} from './resources/webgpu-pipeline-layout';
48
+ import {WebGPUFence} from './resources/webgpu-fence';
49
+
50
+ import {getShaderLayoutFromWGSL} from '../wgsl/get-shader-layout-wgsl';
48
51
 
49
52
  /** WebGPU Device implementation */
50
53
  export class WebGPUDevice extends Device {
@@ -136,15 +139,15 @@ export class WebGPUDevice extends Device {
136
139
  return this._isLost;
137
140
  }
138
141
 
142
+ getShaderLayout(source: string) {
143
+ return getShaderLayoutFromWGSL(source);
144
+ }
145
+
139
146
  override isVertexFormatSupported(format: VertexFormat): boolean {
140
147
  const info = this.getVertexFormatInfo(format);
141
148
  return !info.webglOnly;
142
149
  }
143
150
 
144
- getTextureByteAlignment(): number {
145
- return 1;
146
- }
147
-
148
151
  createBuffer(props: BufferProps | ArrayBuffer | ArrayBufferView): WebGPUBuffer {
149
152
  const newProps = this._normalizeBufferProps(props);
150
153
  return new WebGPUBuffer(this, newProps);
@@ -196,6 +199,10 @@ export class WebGPUDevice extends Device {
196
199
  return new WebGPUQuerySet(this, props);
197
200
  }
198
201
 
202
+ override createFence(): WebGPUFence {
203
+ return new WebGPUFence(this);
204
+ }
205
+
199
206
  createCanvasContext(props: CanvasContextProps): WebGPUCanvasContext {
200
207
  return new WebGPUCanvasContext(this, this.adapter, props);
201
208
  }
@@ -278,6 +285,14 @@ export class WebGPUDevice extends Device {
278
285
  features.add('texture-compression-bc5-webgl');
279
286
  }
280
287
 
288
+ if (this.handle.features.has('chromium-experimental-norm16-texture-formats')) {
289
+ features.add('norm16-renderable-webgl');
290
+ }
291
+
292
+ if (this.handle.features.has('chromium-experimental-snorm16-texture-formats')) {
293
+ features.add('snorm16-renderable-webgl');
294
+ }
295
+
281
296
  const WEBGPU_ALWAYS_FEATURES: DeviceFeature[] = [
282
297
  'timer-query-webgl',
283
298
  'compilation-status-async-webgl',
package/src/index.ts CHANGED
@@ -12,3 +12,6 @@ export {WebGPUBuffer} from './adapter/resources/webgpu-buffer';
12
12
  export {WebGPUTexture} from './adapter/resources/webgpu-texture';
13
13
  export {WebGPUSampler} from './adapter/resources/webgpu-sampler';
14
14
  export {WebGPUShader} from './adapter/resources/webgpu-shader';
15
+ export {WebGPUFence} from './adapter/resources/webgpu-fence';
16
+
17
+ export {getShaderLayoutFromWGSL} from './wgsl/get-shader-layout-wgsl';
@@ -0,0 +1,156 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {AttributeShaderType, ShaderLayout, TextureBindingLayout, log} from '@luma.gl/core';
6
+ import {TypeInfo, VariableInfo, WgslReflect, ResourceType} from 'wgsl_reflect';
7
+
8
+ /**
9
+ * Parse a ShaderLayout from WGSL shader source code.
10
+ * @param source WGSL source code (can contain both @vertex and @fragment entry points)
11
+ * @returns
12
+ */
13
+ export function getShaderLayoutFromWGSL(source: string): ShaderLayout {
14
+ const shaderLayout: ShaderLayout = {attributes: [], bindings: []};
15
+
16
+ let parsedWGSL: WgslReflect;
17
+ try {
18
+ parsedWGSL = parseWGSL(source);
19
+ } catch (error: any) {
20
+ log.error(error.message)();
21
+ return shaderLayout;
22
+ }
23
+
24
+ for (const uniform of parsedWGSL.uniforms) {
25
+ const members = [];
26
+ // @ts-expect-error
27
+ for (const attribute of uniform.type?.members || []) {
28
+ members.push({
29
+ name: attribute.name,
30
+ type: getType(attribute.type)
31
+ });
32
+ }
33
+
34
+ shaderLayout.bindings.push({
35
+ type: 'uniform',
36
+ name: uniform.name,
37
+ group: uniform.group,
38
+ location: uniform.binding,
39
+ // @ts-expect-error TODO - unused for now but needs fixing
40
+ members
41
+ });
42
+ }
43
+
44
+ for (const texture of parsedWGSL.textures) {
45
+ const bindingDeclaration: TextureBindingLayout = {
46
+ type: 'texture',
47
+ name: texture.name,
48
+ group: texture.group,
49
+ location: texture.binding,
50
+ ...getTextureBindingFromReflect(texture)
51
+ };
52
+
53
+ shaderLayout.bindings.push(bindingDeclaration);
54
+ }
55
+
56
+ for (const sampler of parsedWGSL.samplers) {
57
+ shaderLayout.bindings.push({
58
+ type: 'sampler',
59
+ name: sampler.name,
60
+ group: sampler.group,
61
+ location: sampler.binding
62
+ });
63
+ }
64
+
65
+ const vertex = parsedWGSL.entry.vertex[0]; // "main"
66
+
67
+ // Vertex shader inputs
68
+ const attributeCount = vertex?.inputs.length || 0; // inputs to "main"
69
+ for (let i = 0; i < attributeCount; i++) {
70
+ const wgslAttribute = vertex.inputs[i];
71
+
72
+ // locationType can be "builtin"
73
+ if (wgslAttribute.locationType === 'location') {
74
+ const type = getType(wgslAttribute.type);
75
+
76
+ shaderLayout.attributes.push({
77
+ name: wgslAttribute.name,
78
+ location: Number(wgslAttribute.location),
79
+ type
80
+ });
81
+ }
82
+ }
83
+ return shaderLayout;
84
+ }
85
+
86
+ /** Get a valid shader attribute type string from a wgsl-reflect type */
87
+ function getType(type: TypeInfo | null): AttributeShaderType {
88
+ // @ts-expect-error WgslReflect type checks needed
89
+ return type?.format ? `${type.name}<${type.format.name}>` : type.name;
90
+ }
91
+
92
+ function parseWGSL(source: string): WgslReflect {
93
+ try {
94
+ return new WgslReflect(source);
95
+ } catch (error: any) {
96
+ if (error instanceof Error) {
97
+ throw error;
98
+ }
99
+ let message = 'WGSL parse error';
100
+ if (typeof error === 'object' && error?.message) {
101
+ message += `: ${error.message} `;
102
+ }
103
+ if (typeof error === 'object' && error?.token) {
104
+ message += error.token.line || '';
105
+ }
106
+ throw new Error(message, {cause: error});
107
+ }
108
+ }
109
+
110
+ function getTextureBindingFromReflect(
111
+ v: VariableInfo, // VariableInfo for a texture
112
+ opts?: {format?: GPUTextureFormat} // optional: if you know the runtime format
113
+ ): {
114
+ viewDimension: GPUTextureViewDimension;
115
+ /** @note sampleType float vs unfilterable-float cannot be determined without checking texture format and features */
116
+ sampleType: GPUTextureSampleType;
117
+ multisampled: boolean;
118
+ } {
119
+ if (v.resourceType !== ResourceType.Texture) {
120
+ throw new Error('Not a texture binding');
121
+ }
122
+
123
+ const typeName = v.type.name; // e.g. "texture_2d", "texture_cube_array", "texture_multisampled_2d"
124
+ // @ts-expect-error v.type.format is not always defined
125
+ const component = v.type.format?.name as 'f32' | 'i32' | 'u32' | undefined;
126
+
127
+ // viewDimension
128
+ const viewDimension: GPUTextureViewDimension = typeName.includes('cube_array')
129
+ ? 'cube-array'
130
+ : typeName.includes('cube')
131
+ ? 'cube'
132
+ : typeName.includes('2d_array')
133
+ ? '2d-array'
134
+ : typeName.includes('3d')
135
+ ? '3d'
136
+ : typeName.includes('1d')
137
+ ? '1d'
138
+ : '2d';
139
+
140
+ // multisampled
141
+ const multisampled = typeName === 'texture_multisampled_2d';
142
+
143
+ // sampleType
144
+ let sampleType: GPUTextureSampleType;
145
+ if (typeName.startsWith('texture_depth')) {
146
+ sampleType = 'depth';
147
+ } else if (component === 'i32') {
148
+ sampleType = 'sint';
149
+ } else if (component === 'u32') {
150
+ sampleType = 'uint';
151
+ } else {
152
+ sampleType = 'float'; // default to float
153
+ }
154
+
155
+ return {viewDimension, sampleType, multisampled};
156
+ }