@luma.gl/webgpu 9.2.6 → 9.3.0-alpha.4

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 (52) hide show
  1. package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
  2. package/dist/adapter/helpers/get-bind-group.js +11 -5
  3. package/dist/adapter/helpers/get-bind-group.js.map +1 -1
  4. package/dist/adapter/resources/webgpu-buffer.d.ts +7 -0
  5. package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -1
  6. package/dist/adapter/resources/webgpu-buffer.js +39 -12
  7. package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
  8. package/dist/adapter/resources/webgpu-fence.d.ts +13 -0
  9. package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -0
  10. package/dist/adapter/resources/webgpu-fence.js +25 -0
  11. package/dist/adapter/resources/webgpu-fence.js.map +1 -0
  12. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
  13. package/dist/adapter/resources/webgpu-pipeline-layout.js +1 -2
  14. package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
  15. package/dist/adapter/resources/webgpu-texture.d.ts +12 -3
  16. package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
  17. package/dist/adapter/resources/webgpu-texture.js +143 -26
  18. package/dist/adapter/resources/webgpu-texture.js.map +1 -1
  19. package/dist/adapter/webgpu-adapter.d.ts.map +1 -1
  20. package/dist/adapter/webgpu-adapter.js +34 -34
  21. package/dist/adapter/webgpu-adapter.js.map +1 -1
  22. package/dist/adapter/webgpu-canvas-context.d.ts +4 -3
  23. package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
  24. package/dist/adapter/webgpu-canvas-context.js +22 -21
  25. package/dist/adapter/webgpu-canvas-context.js.map +1 -1
  26. package/dist/adapter/webgpu-device.d.ts +3 -1
  27. package/dist/adapter/webgpu-device.d.ts.map +1 -1
  28. package/dist/adapter/webgpu-device.js +14 -3
  29. package/dist/adapter/webgpu-device.js.map +1 -1
  30. package/dist/dist.dev.js +6485 -293
  31. package/dist/dist.min.js +10 -6
  32. package/dist/index.cjs +398 -114
  33. package/dist/index.cjs.map +4 -4
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +2 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/wgsl/get-shader-layout-wgsl.d.ts +8 -0
  39. package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -0
  40. package/dist/wgsl/get-shader-layout-wgsl.js +136 -0
  41. package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -0
  42. package/package.json +6 -5
  43. package/src/adapter/helpers/get-bind-group.ts +18 -5
  44. package/src/adapter/resources/webgpu-buffer.ts +43 -12
  45. package/src/adapter/resources/webgpu-fence.ts +30 -0
  46. package/src/adapter/resources/webgpu-pipeline-layout.ts +1 -2
  47. package/src/adapter/resources/webgpu-texture.ts +202 -88
  48. package/src/adapter/webgpu-adapter.ts +40 -42
  49. package/src/adapter/webgpu-canvas-context.ts +25 -23
  50. package/src/adapter/webgpu-device.ts +19 -4
  51. package/src/index.ts +3 -0
  52. package/src/wgsl/get-shader-layout-wgsl.ts +156 -0
package/dist/index.d.ts CHANGED
@@ -5,4 +5,6 @@ export { WebGPUBuffer } from "./adapter/resources/webgpu-buffer.js";
5
5
  export { WebGPUTexture } from "./adapter/resources/webgpu-texture.js";
6
6
  export { WebGPUSampler } from "./adapter/resources/webgpu-sampler.js";
7
7
  export { WebGPUShader } from "./adapter/resources/webgpu-shader.js";
8
+ export { WebGPUFence } from "./adapter/resources/webgpu-fence.js";
9
+ export { getShaderLayoutFromWGSL } from "./wgsl/get-shader-layout-wgsl.js";
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,YAAY,EAAC,aAAa,EAAC,oCAAiC;AAC5D,OAAO,EAAC,aAAa,EAAC,oCAAiC;AAGvD,OAAO,EAAC,YAAY,EAAC,mCAAgC;AACrD,OAAO,EAAC,YAAY,EAAC,6CAA0C;AAC/D,OAAO,EAAC,aAAa,EAAC,8CAA2C;AACjE,OAAO,EAAC,aAAa,EAAC,8CAA2C;AACjE,OAAO,EAAC,YAAY,EAAC,6CAA0C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,YAAY,EAAC,aAAa,EAAC,oCAAiC;AAC5D,OAAO,EAAC,aAAa,EAAC,oCAAiC;AAGvD,OAAO,EAAC,YAAY,EAAC,mCAAgC;AACrD,OAAO,EAAC,YAAY,EAAC,6CAA0C;AAC/D,OAAO,EAAC,aAAa,EAAC,8CAA2C;AACjE,OAAO,EAAC,aAAa,EAAC,8CAA2C;AACjE,OAAO,EAAC,YAAY,EAAC,6CAA0C;AAC/D,OAAO,EAAC,WAAW,EAAC,4CAAyC;AAE7D,OAAO,EAAC,uBAAuB,EAAC,yCAAsC"}
package/dist/index.js CHANGED
@@ -8,4 +8,6 @@ export { WebGPUBuffer } from "./adapter/resources/webgpu-buffer.js";
8
8
  export { WebGPUTexture } from "./adapter/resources/webgpu-texture.js";
9
9
  export { WebGPUSampler } from "./adapter/resources/webgpu-sampler.js";
10
10
  export { WebGPUShader } from "./adapter/resources/webgpu-shader.js";
11
+ export { WebGPUFence } from "./adapter/resources/webgpu-fence.js";
12
+ export { getShaderLayoutFromWGSL } from "./wgsl/get-shader-layout-wgsl.js";
11
13
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,UAAU;AACV,+BAA+B;AAC/B,oCAAoC;AAIpC,OAAO,EAAC,aAAa,EAAC,oCAAiC;AAEvD,mDAAmD;AACnD,OAAO,EAAC,YAAY,EAAC,mCAAgC;AACrD,OAAO,EAAC,YAAY,EAAC,6CAA0C;AAC/D,OAAO,EAAC,aAAa,EAAC,8CAA2C;AACjE,OAAO,EAAC,aAAa,EAAC,8CAA2C;AACjE,OAAO,EAAC,YAAY,EAAC,6CAA0C"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,UAAU;AACV,+BAA+B;AAC/B,oCAAoC;AAIpC,OAAO,EAAC,aAAa,EAAC,oCAAiC;AAEvD,mDAAmD;AACnD,OAAO,EAAC,YAAY,EAAC,mCAAgC;AACrD,OAAO,EAAC,YAAY,EAAC,6CAA0C;AAC/D,OAAO,EAAC,aAAa,EAAC,8CAA2C;AACjE,OAAO,EAAC,aAAa,EAAC,8CAA2C;AACjE,OAAO,EAAC,YAAY,EAAC,6CAA0C;AAC/D,OAAO,EAAC,WAAW,EAAC,4CAAyC;AAE7D,OAAO,EAAC,uBAAuB,EAAC,yCAAsC"}
@@ -0,0 +1,8 @@
1
+ import { ShaderLayout } from '@luma.gl/core';
2
+ /**
3
+ * Parse a ShaderLayout from WGSL shader source code.
4
+ * @param source WGSL source code (can contain both @vertex and @fragment entry points)
5
+ * @returns
6
+ */
7
+ export declare function getShaderLayoutFromWGSL(source: string): ShaderLayout;
8
+ //# sourceMappingURL=get-shader-layout-wgsl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-shader-layout-wgsl.d.ts","sourceRoot":"","sources":["../../src/wgsl/get-shader-layout-wgsl.ts"],"names":[],"mappings":"AAIA,OAAO,EAAsB,YAAY,EAA4B,MAAM,eAAe,CAAC;AAG3F;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAuEpE"}
@@ -0,0 +1,136 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+ import { log } from '@luma.gl/core';
5
+ import { WgslReflect, ResourceType } from 'wgsl_reflect';
6
+ /**
7
+ * Parse a ShaderLayout from WGSL shader source code.
8
+ * @param source WGSL source code (can contain both @vertex and @fragment entry points)
9
+ * @returns
10
+ */
11
+ export function getShaderLayoutFromWGSL(source) {
12
+ const shaderLayout = { attributes: [], bindings: [] };
13
+ let parsedWGSL;
14
+ try {
15
+ parsedWGSL = parseWGSL(source);
16
+ }
17
+ catch (error) {
18
+ log.error(error.message)();
19
+ return shaderLayout;
20
+ }
21
+ for (const uniform of parsedWGSL.uniforms) {
22
+ const members = [];
23
+ // @ts-expect-error
24
+ for (const attribute of uniform.type?.members || []) {
25
+ members.push({
26
+ name: attribute.name,
27
+ type: getType(attribute.type)
28
+ });
29
+ }
30
+ shaderLayout.bindings.push({
31
+ type: 'uniform',
32
+ name: uniform.name,
33
+ group: uniform.group,
34
+ location: uniform.binding,
35
+ // @ts-expect-error TODO - unused for now but needs fixing
36
+ members
37
+ });
38
+ }
39
+ for (const texture of parsedWGSL.textures) {
40
+ const bindingDeclaration = {
41
+ type: 'texture',
42
+ name: texture.name,
43
+ group: texture.group,
44
+ location: texture.binding,
45
+ ...getTextureBindingFromReflect(texture)
46
+ };
47
+ shaderLayout.bindings.push(bindingDeclaration);
48
+ }
49
+ for (const sampler of parsedWGSL.samplers) {
50
+ shaderLayout.bindings.push({
51
+ type: 'sampler',
52
+ name: sampler.name,
53
+ group: sampler.group,
54
+ location: sampler.binding
55
+ });
56
+ }
57
+ const vertex = parsedWGSL.entry.vertex[0]; // "main"
58
+ // Vertex shader inputs
59
+ const attributeCount = vertex?.inputs.length || 0; // inputs to "main"
60
+ for (let i = 0; i < attributeCount; i++) {
61
+ const wgslAttribute = vertex.inputs[i];
62
+ // locationType can be "builtin"
63
+ if (wgslAttribute.locationType === 'location') {
64
+ const type = getType(wgslAttribute.type);
65
+ shaderLayout.attributes.push({
66
+ name: wgslAttribute.name,
67
+ location: Number(wgslAttribute.location),
68
+ type
69
+ });
70
+ }
71
+ }
72
+ return shaderLayout;
73
+ }
74
+ /** Get a valid shader attribute type string from a wgsl-reflect type */
75
+ function getType(type) {
76
+ // @ts-expect-error WgslReflect type checks needed
77
+ return type?.format ? `${type.name}<${type.format.name}>` : type.name;
78
+ }
79
+ function parseWGSL(source) {
80
+ try {
81
+ return new WgslReflect(source);
82
+ }
83
+ catch (error) {
84
+ if (error instanceof Error) {
85
+ throw error;
86
+ }
87
+ let message = 'WGSL parse error';
88
+ if (typeof error === 'object' && error?.message) {
89
+ message += `: ${error.message} `;
90
+ }
91
+ if (typeof error === 'object' && error?.token) {
92
+ message += error.token.line || '';
93
+ }
94
+ throw new Error(message, { cause: error });
95
+ }
96
+ }
97
+ function getTextureBindingFromReflect(v, // VariableInfo for a texture
98
+ opts // optional: if you know the runtime format
99
+ ) {
100
+ if (v.resourceType !== ResourceType.Texture) {
101
+ throw new Error('Not a texture binding');
102
+ }
103
+ const typeName = v.type.name; // e.g. "texture_2d", "texture_cube_array", "texture_multisampled_2d"
104
+ // @ts-expect-error v.type.format is not always defined
105
+ const component = v.type.format?.name;
106
+ // viewDimension
107
+ const viewDimension = typeName.includes('cube_array')
108
+ ? 'cube-array'
109
+ : typeName.includes('cube')
110
+ ? 'cube'
111
+ : typeName.includes('2d_array')
112
+ ? '2d-array'
113
+ : typeName.includes('3d')
114
+ ? '3d'
115
+ : typeName.includes('1d')
116
+ ? '1d'
117
+ : '2d';
118
+ // multisampled
119
+ const multisampled = typeName === 'texture_multisampled_2d';
120
+ // sampleType
121
+ let sampleType;
122
+ if (typeName.startsWith('texture_depth')) {
123
+ sampleType = 'depth';
124
+ }
125
+ else if (component === 'i32') {
126
+ sampleType = 'sint';
127
+ }
128
+ else if (component === 'u32') {
129
+ sampleType = 'uint';
130
+ }
131
+ else {
132
+ sampleType = 'float'; // default to float
133
+ }
134
+ return { viewDimension, sampleType, multisampled };
135
+ }
136
+ //# sourceMappingURL=get-shader-layout-wgsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-shader-layout-wgsl.js","sourceRoot":"","sources":["../../src/wgsl/get-shader-layout-wgsl.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luma.gl/webgpu",
3
- "version": "9.2.6",
3
+ "version": "9.3.0-alpha.4",
4
4
  "description": "WebGPU adapter for the luma.gl core API",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,11 +37,12 @@
37
37
  "prepublishOnly": "npm run build-minified-bundle && npm run build-dev-bundle"
38
38
  },
39
39
  "peerDependencies": {
40
- "@luma.gl/core": "~9.2.0"
40
+ "@luma.gl/core": "9.2.0-alpha.6"
41
41
  },
42
42
  "dependencies": {
43
- "@probe.gl/env": "^4.0.8",
44
- "@webgpu/types": "^0.1.34"
43
+ "@probe.gl/env": "^4.1.1",
44
+ "@webgpu/types": "^0.1.34",
45
+ "wgsl_reflect": "^1.2.1"
45
46
  },
46
- "gitHead": "9c7a3adc09c7b23800df3f916762445ebc1fa924"
47
+ "gitHead": "7486e7b0377fb6ab961b4499828681bede60f3b1"
47
48
  }
@@ -3,10 +3,11 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import type {ComputeShaderLayout, BindingDeclaration, Binding} from '@luma.gl/core';
6
- import {Buffer, Sampler, Texture, log} from '@luma.gl/core';
6
+ import {Buffer, Sampler, Texture, TextureView, log} from '@luma.gl/core';
7
7
  import type {WebGPUBuffer} from '../resources/webgpu-buffer';
8
8
  import type {WebGPUSampler} from '../resources/webgpu-sampler';
9
9
  import type {WebGPUTexture} from '../resources/webgpu-texture';
10
+ import type {WebGPUTextureView} from '../resources/webgpu-texture-view';
10
11
 
11
12
  /**
12
13
  * Create a WebGPU "bind group layout" from an array of luma.gl bindings
@@ -76,7 +77,7 @@ function getBindGroupEntries(
76
77
  for (const [bindingName, value] of Object.entries(bindings)) {
77
78
  let bindingLayout = getShaderLayoutBinding(shaderLayout, bindingName);
78
79
  if (bindingLayout) {
79
- const entry = getBindGroupEntry(value, bindingLayout.location);
80
+ const entry = getBindGroupEntry(value, bindingLayout.location, undefined, bindingName);
80
81
  if (entry) {
81
82
  entries.push(entry);
82
83
  }
@@ -88,7 +89,12 @@ function getBindGroupEntries(
88
89
  ignoreWarnings: true
89
90
  });
90
91
  if (bindingLayout) {
91
- const entry = getBindGroupEntry(value, bindingLayout.location, {sampler: true});
92
+ const entry = getBindGroupEntry(
93
+ value,
94
+ bindingLayout.location,
95
+ {sampler: true},
96
+ bindingName
97
+ );
92
98
  if (entry) {
93
99
  entries.push(entry);
94
100
  }
@@ -102,7 +108,8 @@ function getBindGroupEntries(
102
108
  function getBindGroupEntry(
103
109
  binding: Binding,
104
110
  index: number,
105
- options?: {sampler?: boolean}
111
+ options?: {sampler?: boolean},
112
+ bindingName: string = 'unknown'
106
113
  ): GPUBindGroupEntry | null {
107
114
  if (binding instanceof Buffer) {
108
115
  return {
@@ -118,6 +125,12 @@ function getBindGroupEntry(
118
125
  resource: (binding as WebGPUSampler).handle
119
126
  };
120
127
  }
128
+ if (binding instanceof TextureView) {
129
+ return {
130
+ binding: index,
131
+ resource: (binding as WebGPUTextureView).handle
132
+ };
133
+ }
121
134
  if (binding instanceof Texture) {
122
135
  if (options?.sampler) {
123
136
  return {
@@ -130,6 +143,6 @@ function getBindGroupEntry(
130
143
  resource: (binding as WebGPUTexture).view.handle
131
144
  };
132
145
  }
133
- log.warn(`invalid binding ${name}`, binding);
146
+ log.warn(`invalid binding ${bindingName}`, binding);
134
147
  return null;
135
148
  }
@@ -5,20 +5,28 @@
5
5
  import {log, Buffer, type BufferProps, type BufferMapCallback} from '@luma.gl/core';
6
6
  import {type WebGPUDevice} from '../webgpu-device';
7
7
 
8
+ /**
9
+ * WebGPU implementation of Buffer
10
+ * For byte alignment requirements see:
11
+ * @see https://www.w3.org/TR/webgpu/#dom-gpubuffer-mapasync
12
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer/mapAsync
13
+ */
8
14
  export class WebGPUBuffer extends Buffer {
9
15
  readonly device: WebGPUDevice;
10
16
  readonly handle: GPUBuffer;
11
17
  readonly byteLength: number;
18
+ readonly paddedByteLength: number;
12
19
 
13
20
  constructor(device: WebGPUDevice, props: BufferProps) {
14
21
  super(device, props);
15
22
  this.device = device;
16
23
 
17
24
  this.byteLength = props.byteLength || props.data?.byteLength || 0;
25
+ this.paddedByteLength = Math.ceil(this.byteLength / 4) * 4;
18
26
  const mappedAtCreation = Boolean(this.props.onMapped || props.data);
19
27
 
20
28
  // WebGPU buffers must be aligned to 4 bytes
21
- const size = Math.ceil(this.byteLength / 4) * 4;
29
+ const size = this.paddedByteLength;
22
30
 
23
31
  this.device.pushErrorScope('out-of-memory');
24
32
  this.device.pushErrorScope('validation');
@@ -92,10 +100,11 @@ export class WebGPUBuffer extends Buffer {
92
100
  byteOffset: number = 0,
93
101
  byteLength: number = this.byteLength - byteOffset
94
102
  ): Promise<void> {
103
+ const alignedByteLength = Math.ceil(byteLength / 4) * 4;
95
104
  // Unless the application created and supplied a mappable buffer, a staging buffer is needed
96
105
  const isMappable = (this.usage & Buffer.MAP_WRITE) !== 0;
97
106
  const mappableBuffer: WebGPUBuffer | null = !isMappable
98
- ? this._getMappableBuffer(Buffer.MAP_WRITE | Buffer.COPY_SRC, 0, this.byteLength)
107
+ ? this._getMappableBuffer(Buffer.MAP_WRITE | Buffer.COPY_SRC, 0, this.paddedByteLength)
99
108
  : null;
100
109
 
101
110
  const writeBuffer = mappableBuffer || this;
@@ -105,13 +114,15 @@ export class WebGPUBuffer extends Buffer {
105
114
  this.device.pushErrorScope('validation');
106
115
  try {
107
116
  await this.device.handle.queue.onSubmittedWorkDone();
108
- await writeBuffer.handle.mapAsync(GPUMapMode.WRITE, byteOffset, byteLength);
109
- const arrayBuffer = writeBuffer.handle.getMappedRange(byteOffset, byteLength);
117
+ await writeBuffer.handle.mapAsync(GPUMapMode.WRITE, byteOffset, alignedByteLength);
118
+ const mappedRange = writeBuffer.handle.getMappedRange(byteOffset, alignedByteLength);
119
+ const arrayBuffer = mappedRange.slice(0, byteLength);
110
120
  // eslint-disable-next-line @typescript-eslint/await-thenable
111
121
  await callback(arrayBuffer, 'mapped');
122
+ new Uint8Array(mappedRange).set(new Uint8Array(arrayBuffer), 0);
112
123
  writeBuffer.handle.unmap();
113
124
  if (mappableBuffer) {
114
- this._copyBuffer(mappableBuffer, byteOffset, byteLength);
125
+ this._copyBuffer(mappableBuffer, byteOffset, alignedByteLength);
115
126
  }
116
127
  } finally {
117
128
  this.device.popErrorScope((error: GPUError) => {
@@ -138,17 +149,33 @@ export class WebGPUBuffer extends Buffer {
138
149
  byteOffset = 0,
139
150
  byteLength = this.byteLength - byteOffset
140
151
  ): Promise<T> {
152
+ const requestedEnd = byteOffset + byteLength;
153
+ if (requestedEnd > this.byteLength) {
154
+ throw new Error('Mapping range exceeds buffer size');
155
+ }
156
+
157
+ let mappedByteOffset = byteOffset;
158
+ let mappedByteLength = byteLength;
159
+ let sliceByteOffset = 0;
160
+ let lifetime: 'mapped' | 'copied' = 'mapped';
161
+
162
+ // WebGPU mapAsync requires 8-byte offsets and 4-byte lengths.
141
163
  if (byteOffset % 8 !== 0 || byteLength % 4 !== 0) {
142
- throw new Error('byteOffset must be multiple of 8 and byteLength multiple of 4');
164
+ mappedByteOffset = Math.floor(byteOffset / 8) * 8;
165
+ const alignedEnd = Math.ceil(requestedEnd / 4) * 4;
166
+ mappedByteLength = alignedEnd - mappedByteOffset;
167
+ sliceByteOffset = byteOffset - mappedByteOffset;
168
+ lifetime = 'copied';
143
169
  }
144
- if (byteOffset + byteLength > this.handle.size) {
170
+
171
+ if (mappedByteOffset + mappedByteLength > this.paddedByteLength) {
145
172
  throw new Error('Mapping range exceeds buffer size');
146
173
  }
147
174
 
148
175
  // Unless the application created and supplied a mappable buffer, a staging buffer is needed
149
176
  const isMappable = (this.usage & Buffer.MAP_READ) !== 0;
150
177
  const mappableBuffer: WebGPUBuffer | null = !isMappable
151
- ? this._getMappableBuffer(Buffer.MAP_READ | Buffer.COPY_DST, 0, this.byteLength)
178
+ ? this._getMappableBuffer(Buffer.MAP_READ | Buffer.COPY_DST, 0, this.paddedByteLength)
152
179
  : null;
153
180
 
154
181
  const readBuffer = mappableBuffer || this;
@@ -158,12 +185,16 @@ export class WebGPUBuffer extends Buffer {
158
185
  try {
159
186
  await this.device.handle.queue.onSubmittedWorkDone();
160
187
  if (mappableBuffer) {
161
- mappableBuffer._copyBuffer(this);
188
+ mappableBuffer._copyBuffer(this, mappedByteOffset, mappedByteLength);
162
189
  }
163
- await readBuffer.handle.mapAsync(GPUMapMode.READ, byteOffset, byteLength);
164
- const arrayBuffer = readBuffer.handle.getMappedRange(byteOffset, byteLength);
190
+ await readBuffer.handle.mapAsync(GPUMapMode.READ, mappedByteOffset, mappedByteLength);
191
+ const arrayBuffer = readBuffer.handle.getMappedRange(mappedByteOffset, mappedByteLength);
192
+ const mappedRange =
193
+ lifetime === 'mapped'
194
+ ? arrayBuffer
195
+ : arrayBuffer.slice(sliceByteOffset, sliceByteOffset + byteLength);
165
196
  // eslint-disable-next-line @typescript-eslint/await-thenable
166
- const result = await callback(arrayBuffer, 'mapped');
197
+ const result = await callback(mappedRange, lifetime);
167
198
  readBuffer.handle.unmap();
168
199
  return result;
169
200
  } finally {
@@ -0,0 +1,30 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {Fence, type FenceProps} from '@luma.gl/core';
6
+ import {WebGPUDevice} from '../webgpu-device';
7
+
8
+ /** WebGPU fence implemented by waiting for submitted work */
9
+ export class WebGPUFence extends Fence {
10
+ readonly device: WebGPUDevice;
11
+ readonly handle: null = null;
12
+ readonly signaled: Promise<void>;
13
+ private _signaled = false;
14
+
15
+ constructor(device: WebGPUDevice, props: FenceProps = {}) {
16
+ super(device, {});
17
+ this.device = device;
18
+ this.signaled = device.handle.queue.onSubmittedWorkDone().then(() => {
19
+ this._signaled = true;
20
+ });
21
+ }
22
+
23
+ isSignaled(): boolean {
24
+ return this._signaled;
25
+ }
26
+
27
+ override destroy(): void {
28
+ // Nothing to release for WebGPU fence
29
+ }
30
+ }
@@ -47,8 +47,7 @@ export class WebGPUPipelineLayout extends PipelineLayout {
47
47
  // TODO (kaapp): This only supports the first group, but so does the rest of the code
48
48
  const bindGroupEntries: GPUBindGroupLayoutEntry[] = [];
49
49
 
50
- for (let i = 0; i < this.props.shaderLayout.bindings.length; i++) {
51
- const binding = this.props.shaderLayout.bindings[i];
50
+ for (const binding of this.props.shaderLayout.bindings) {
52
51
  const bindingTypeInfo: Omit<GPUBindGroupLayoutEntry, 'binding' | 'visibility'> = {};
53
52
 
54
53
  switch (binding.type) {