@luma.gl/webgpu 9.3.0-alpha.2 → 9.3.0-alpha.6

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 (98) hide show
  1. package/dist/adapter/helpers/cpu-hotspot-profiler.d.ts +54 -0
  2. package/dist/adapter/helpers/cpu-hotspot-profiler.d.ts.map +1 -0
  3. package/dist/adapter/helpers/cpu-hotspot-profiler.js +26 -0
  4. package/dist/adapter/helpers/cpu-hotspot-profiler.js.map +1 -0
  5. package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts +7 -0
  6. package/dist/adapter/helpers/generate-mipmaps-webgpu.d.ts.map +1 -0
  7. package/dist/adapter/helpers/generate-mipmaps-webgpu.js +490 -0
  8. package/dist/adapter/helpers/generate-mipmaps-webgpu.js.map +1 -0
  9. package/dist/adapter/helpers/get-bind-group.d.ts +2 -1
  10. package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
  11. package/dist/adapter/helpers/get-bind-group.js +31 -21
  12. package/dist/adapter/helpers/get-bind-group.js.map +1 -1
  13. package/dist/adapter/resources/webgpu-buffer.d.ts +7 -0
  14. package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -1
  15. package/dist/adapter/resources/webgpu-buffer.js +58 -15
  16. package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
  17. package/dist/adapter/resources/webgpu-command-buffer.js +1 -1
  18. package/dist/adapter/resources/webgpu-command-buffer.js.map +1 -1
  19. package/dist/adapter/resources/webgpu-command-encoder.d.ts +5 -4
  20. package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
  21. package/dist/adapter/resources/webgpu-command-encoder.js +23 -5
  22. package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -1
  23. package/dist/adapter/resources/webgpu-compute-pass.d.ts +1 -1
  24. package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -1
  25. package/dist/adapter/resources/webgpu-compute-pass.js +14 -6
  26. package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -1
  27. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
  28. package/dist/adapter/resources/webgpu-compute-pipeline.js +19 -3
  29. package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
  30. package/dist/adapter/resources/webgpu-framebuffer.d.ts +6 -0
  31. package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
  32. package/dist/adapter/resources/webgpu-framebuffer.js +16 -0
  33. package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -1
  34. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
  35. package/dist/adapter/resources/webgpu-pipeline-layout.js +1 -2
  36. package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
  37. package/dist/adapter/resources/webgpu-query-set.d.ts +33 -4
  38. package/dist/adapter/resources/webgpu-query-set.d.ts.map +1 -1
  39. package/dist/adapter/resources/webgpu-query-set.js +145 -4
  40. package/dist/adapter/resources/webgpu-query-set.js.map +1 -1
  41. package/dist/adapter/resources/webgpu-render-pass.d.ts +3 -0
  42. package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
  43. package/dist/adapter/resources/webgpu-render-pass.js +74 -30
  44. package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
  45. package/dist/adapter/resources/webgpu-render-pipeline.d.ts +7 -4
  46. package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
  47. package/dist/adapter/resources/webgpu-render-pipeline.js +26 -15
  48. package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
  49. package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -1
  50. package/dist/adapter/resources/webgpu-sampler.js +4 -0
  51. package/dist/adapter/resources/webgpu-sampler.js.map +1 -1
  52. package/dist/adapter/resources/webgpu-texture-view.d.ts +6 -0
  53. package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -1
  54. package/dist/adapter/resources/webgpu-texture-view.js +47 -11
  55. package/dist/adapter/resources/webgpu-texture-view.js.map +1 -1
  56. package/dist/adapter/resources/webgpu-texture.d.ts +10 -4
  57. package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
  58. package/dist/adapter/resources/webgpu-texture.js +116 -57
  59. package/dist/adapter/resources/webgpu-texture.js.map +1 -1
  60. package/dist/adapter/resources/webgpu-vertex-array.js +1 -1
  61. package/dist/adapter/resources/webgpu-vertex-array.js.map +1 -1
  62. package/dist/adapter/webgpu-canvas-context.d.ts +2 -0
  63. package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
  64. package/dist/adapter/webgpu-canvas-context.js +78 -19
  65. package/dist/adapter/webgpu-canvas-context.js.map +1 -1
  66. package/dist/adapter/webgpu-device.d.ts +5 -1
  67. package/dist/adapter/webgpu-device.d.ts.map +1 -1
  68. package/dist/adapter/webgpu-device.js +113 -9
  69. package/dist/adapter/webgpu-device.js.map +1 -1
  70. package/dist/adapter/webgpu-presentation-context.d.ts +25 -0
  71. package/dist/adapter/webgpu-presentation-context.d.ts.map +1 -0
  72. package/dist/adapter/webgpu-presentation-context.js +144 -0
  73. package/dist/adapter/webgpu-presentation-context.js.map +1 -0
  74. package/dist/dist.dev.js +2864 -1702
  75. package/dist/dist.min.js +167 -8
  76. package/dist/index.cjs +1363 -225
  77. package/dist/index.cjs.map +4 -4
  78. package/package.json +5 -5
  79. package/src/adapter/helpers/cpu-hotspot-profiler.ts +70 -0
  80. package/src/adapter/helpers/generate-mipmaps-webgpu.ts +583 -0
  81. package/src/adapter/helpers/get-bind-group.ts +37 -22
  82. package/src/adapter/resources/webgpu-buffer.ts +61 -15
  83. package/src/adapter/resources/webgpu-command-buffer.ts +1 -1
  84. package/src/adapter/resources/webgpu-command-encoder.ts +32 -6
  85. package/src/adapter/resources/webgpu-compute-pass.ts +14 -6
  86. package/src/adapter/resources/webgpu-compute-pipeline.ts +21 -3
  87. package/src/adapter/resources/webgpu-framebuffer.ts +21 -0
  88. package/src/adapter/resources/webgpu-pipeline-layout.ts +1 -2
  89. package/src/adapter/resources/webgpu-query-set.ts +185 -9
  90. package/src/adapter/resources/webgpu-render-pass.ts +82 -34
  91. package/src/adapter/resources/webgpu-render-pipeline.ts +36 -19
  92. package/src/adapter/resources/webgpu-sampler.ts +5 -0
  93. package/src/adapter/resources/webgpu-texture-view.ts +51 -11
  94. package/src/adapter/resources/webgpu-texture.ts +142 -93
  95. package/src/adapter/resources/webgpu-vertex-array.ts +1 -1
  96. package/src/adapter/webgpu-canvas-context.ts +91 -26
  97. package/src/adapter/webgpu-device.ts +128 -9
  98. package/src/adapter/webgpu-presentation-context.ts +180 -0
@@ -3,7 +3,6 @@ import {
3
3
  type TextureProps,
4
4
  type TextureViewProps,
5
5
  type CopyExternalImageOptions,
6
- type CopyImageDataOptions,
7
6
  type TextureReadOptions,
8
7
  type TextureWriteOptions,
9
8
  type SamplerProps,
@@ -24,11 +23,22 @@ export class WebGPUTexture extends Texture {
24
23
  readonly handle: GPUTexture;
25
24
  sampler: WebGPUSampler;
26
25
  view: WebGPUTextureView;
26
+ private _allocatedByteLength: number = 0;
27
27
 
28
28
  constructor(device: WebGPUDevice, props: TextureProps) {
29
- super(device, props, {byteAlignment: 256}); // WebGPU requires row width to be a multiple of 256 bytes
29
+ // WebGPU buffer copies use 256-byte row alignment. queue.writeTexture() can use tightly packed rows.
30
+ super(device, props, {byteAlignment: 256});
30
31
  this.device = device;
31
32
 
33
+ if (props.sampler instanceof WebGPUSampler) {
34
+ this.sampler = props.sampler;
35
+ } else if (props.sampler === undefined) {
36
+ this.sampler = this.device.getDefaultSampler();
37
+ } else {
38
+ this.sampler = new WebGPUSampler(this.device, (props.sampler as SamplerProps) || {});
39
+ this.attachResource(this.sampler);
40
+ }
41
+
32
42
  this.device.pushErrorScope('out-of-memory');
33
43
  this.device.pushErrorScope('validation');
34
44
 
@@ -56,8 +66,6 @@ export class WebGPUTexture extends Texture {
56
66
  this.device.debug();
57
67
  });
58
68
 
59
- // Update props if external handle was supplied - used mainly by CanvasContext.getDefaultFramebuffer()
60
- // TODO - Read all properties directly from the supplied handle?
61
69
  if (this.props.handle) {
62
70
  this.handle.label ||= this.id;
63
71
  // @ts-expect-error readonly
@@ -66,11 +74,6 @@ export class WebGPUTexture extends Texture {
66
74
  this.height = this.handle.height;
67
75
  }
68
76
 
69
- this.sampler =
70
- props.sampler instanceof WebGPUSampler
71
- ? props.sampler
72
- : new WebGPUSampler(this.device, (props.sampler as SamplerProps) || {});
73
-
74
77
  this.view = new WebGPUTextureView(this.device, {
75
78
  ...this.props,
76
79
  texture: this,
@@ -78,14 +81,34 @@ export class WebGPUTexture extends Texture {
78
81
  // Note: arrayLayerCount controls the view of array textures, but does not apply to 3d texture depths
79
82
  arrayLayerCount: this.dimension !== '3d' ? this.depth : 1
80
83
  });
84
+ this.attachResource(this.view);
81
85
 
82
86
  // Set initial data
83
87
  // Texture base class strips out the data prop from this.props, so we need to handle it here
84
88
  this._initializeData(props.data);
89
+
90
+ this._allocatedByteLength = this.getAllocatedByteLength();
91
+
92
+ if (!this.props.handle) {
93
+ this.trackAllocatedMemory(this._allocatedByteLength, 'Texture');
94
+ } else {
95
+ this.trackReferencedMemory(this._allocatedByteLength, 'Texture');
96
+ }
85
97
  }
86
98
 
87
99
  override destroy(): void {
88
- this.handle?.destroy();
100
+ if (this.destroyed) {
101
+ return;
102
+ }
103
+
104
+ if (!this.props.handle && this.handle) {
105
+ this.trackDeallocatedMemory('Texture');
106
+ this.handle.destroy();
107
+ } else if (this.handle) {
108
+ this.trackDeallocatedReferencedMemory('Texture');
109
+ }
110
+
111
+ this.destroyResource();
89
112
  // @ts-expect-error readonly
90
113
  this.handle = null;
91
114
  }
@@ -126,38 +149,6 @@ export class WebGPUTexture extends Texture {
126
149
  return {width: options.width, height: options.height};
127
150
  }
128
151
 
129
- copyImageData(options_: CopyImageDataOptions): void {
130
- const {width, height, depth} = this;
131
- const options = this._normalizeCopyImageDataOptions(options_);
132
- this.device.pushErrorScope('validation');
133
-
134
- this.device.handle.queue.writeTexture(
135
- // destination: GPUImageCopyTexture
136
- {
137
- // texture subresource
138
- texture: this.handle,
139
- mipLevel: options.mipLevel,
140
- aspect: options.aspect,
141
- // origin to write to
142
- origin: [options.x, options.y, options.z]
143
- },
144
- // data
145
- options.data,
146
- // dataLayout: GPUImageDataLayout
147
- {
148
- offset: options.byteOffset,
149
- bytesPerRow: options.bytesPerRow,
150
- rowsPerImage: options.rowsPerImage
151
- },
152
- // size: GPUExtent3D - extents of the content to write
153
- [width, height, depth]
154
- );
155
- this.device.popErrorScope((error: GPUError) => {
156
- this.device.reportError(new Error(`copyImageData: ${error.message}`), this)();
157
- this.device.debug();
158
- });
159
- }
160
-
161
152
  override generateMipmapsWebGL(): void {
162
153
  log.warn(`${this}: generateMipmaps not supported in WebGPU`)();
163
154
  }
@@ -175,18 +166,10 @@ export class WebGPUTexture extends Texture {
175
166
  }
176
167
 
177
168
  override readBuffer(options: TextureReadOptions = {}, buffer?: Buffer): Buffer {
178
- const {
179
- x = 0,
180
- y = 0,
181
- z = 0,
182
- width = this.width,
183
- height = this.height,
184
- depthOrArrayLayers = this.depth,
185
- mipLevel = 0,
186
- aspect = 'all'
187
- } = options;
169
+ const {x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect} =
170
+ this._getSupportedColorReadOptions(options);
188
171
 
189
- const layout = this.computeMemoryLayout(options);
172
+ const layout = this.computeMemoryLayout({x, y, z, width, height, depthOrArrayLayers, mipLevel});
190
173
 
191
174
  const {bytesPerRow, rowsPerImage, byteLength} = layout;
192
175
 
@@ -197,6 +180,13 @@ export class WebGPUTexture extends Texture {
197
180
  byteLength,
198
181
  usage: Buffer.COPY_DST | Buffer.MAP_READ
199
182
  });
183
+
184
+ if (readBuffer.byteLength < byteLength) {
185
+ throw new Error(
186
+ `${this} readBuffer target is too small (${readBuffer.byteLength} < ${byteLength})`
187
+ );
188
+ }
189
+
200
190
  const gpuReadBuffer = readBuffer.handle as GPUBuffer;
201
191
 
202
192
  // Record commands to copy from the texture to the buffer.
@@ -248,25 +238,22 @@ export class WebGPUTexture extends Texture {
248
238
  return data.buffer as ArrayBuffer;
249
239
  }
250
240
 
251
- override writeBuffer(buffer: Buffer, options: TextureWriteOptions = {}) {
241
+ override writeBuffer(buffer: Buffer, options_: TextureWriteOptions = {}) {
242
+ const options = this._normalizeTextureWriteOptions(options_);
252
243
  const {
253
- x = 0,
254
- y = 0,
255
- z = 0,
256
- width = this.width,
257
- height = this.height,
258
- depthOrArrayLayers = this.depth,
259
- mipLevel = 0,
260
- aspect = 'all'
244
+ x,
245
+ y,
246
+ z,
247
+ width,
248
+ height,
249
+ depthOrArrayLayers,
250
+ mipLevel,
251
+ aspect,
252
+ byteOffset,
253
+ bytesPerRow,
254
+ rowsPerImage
261
255
  } = options;
262
256
 
263
- const layout = this.computeMemoryLayout(options);
264
-
265
- // Get the data on the CPU.
266
- // await buffer.mapAndReadAsync();
267
-
268
- const {bytesPerRow, rowsPerImage} = layout;
269
-
270
257
  const gpuDevice = this.device.handle;
271
258
 
272
259
  this.device.pushErrorScope('validation');
@@ -274,7 +261,7 @@ export class WebGPUTexture extends Texture {
274
261
  commandEncoder.copyBufferToTexture(
275
262
  {
276
263
  buffer: buffer.handle as GPUBuffer,
277
- offset: 0,
264
+ offset: byteOffset,
278
265
  bytesPerRow,
279
266
  rowsPerImage
280
267
  },
@@ -294,29 +281,34 @@ export class WebGPUTexture extends Texture {
294
281
  });
295
282
  }
296
283
 
297
- override writeData(data: ArrayBuffer | ArrayBufferView, options: TextureWriteOptions = {}): void {
284
+ override writeData(
285
+ data: ArrayBuffer | SharedArrayBuffer | ArrayBufferView,
286
+ options_: TextureWriteOptions = {}
287
+ ): void {
298
288
  const device = this.device;
299
-
300
- const {
301
- x = 0,
302
- y = 0,
303
- z = 0,
304
- width = this.width,
305
- height = this.height,
306
- depthOrArrayLayers = this.depth,
307
- mipLevel = 0,
308
- aspect = 'all'
309
- } = options;
310
-
311
- const layout = textureFormatDecoder.computeMemoryLayout({
289
+ const options = this._normalizeTextureWriteOptions(options_);
290
+ const {x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect, byteOffset} = options;
291
+ const source = data as GPUAllowSharedBufferSource;
292
+ const formatInfo = this.device.getTextureFormatInfo(this.format);
293
+ // queue.writeTexture() defaults to tightly packed rows, unlike WebGPU buffer copy paths.
294
+ const packedSourceLayout = textureFormatDecoder.computeMemoryLayout({
312
295
  format: this.format,
313
- width: this.width,
314
- height: this.height,
315
- depth: this.depth,
316
- byteAlignment: this.byteAlignment
296
+ width,
297
+ height,
298
+ depth: depthOrArrayLayers,
299
+ byteAlignment: 1
317
300
  });
318
-
319
- const {bytesPerRow, rowsPerImage} = layout;
301
+ const bytesPerRow = options_.bytesPerRow ?? packedSourceLayout.bytesPerRow;
302
+ const rowsPerImage = options_.rowsPerImage ?? packedSourceLayout.rowsPerImage;
303
+ let copyWidth = width;
304
+ let copyHeight = height;
305
+
306
+ if (formatInfo.compressed) {
307
+ const blockWidth = formatInfo.blockWidth || 1;
308
+ const blockHeight = formatInfo.blockHeight || 1;
309
+ copyWidth = Math.ceil(width / blockWidth) * blockWidth;
310
+ copyHeight = Math.ceil(height / blockHeight) * blockHeight;
311
+ }
320
312
 
321
313
  this.device.pushErrorScope('validation');
322
314
  device.handle.queue.writeTexture(
@@ -326,17 +318,74 @@ export class WebGPUTexture extends Texture {
326
318
  aspect,
327
319
  origin: {x, y, z}
328
320
  },
329
- data,
321
+ source,
330
322
  {
331
- offset: 0,
323
+ offset: byteOffset,
332
324
  bytesPerRow,
333
325
  rowsPerImage
334
326
  },
335
- {width, height, depthOrArrayLayers}
327
+ {width: copyWidth, height: copyHeight, depthOrArrayLayers}
336
328
  );
337
329
  this.device.popErrorScope((error: GPUError) => {
338
330
  this.device.reportError(new Error(`${this} writeData: ${error.message}`), this)();
339
331
  this.device.debug();
340
332
  });
341
333
  }
334
+
335
+ /**
336
+ * Internal-only hook for the cached CanvasContext/PresentationContext swapchain path.
337
+ * Rebinds this handle-backed texture wrapper to the current per-frame canvas texture
338
+ * without allocating a new luma.gl Texture or TextureView wrapper.
339
+ */
340
+ _reinitialize(handle: GPUTexture, props?: Partial<TextureProps>): void {
341
+ const nextWidth = props?.width ?? handle.width ?? this.width;
342
+ const nextHeight = props?.height ?? handle.height ?? this.height;
343
+ const nextDepth = props?.depth ?? this.depth;
344
+ const nextFormat = props?.format ?? this.format;
345
+ const allocationMayHaveChanged =
346
+ nextWidth !== this.width ||
347
+ nextHeight !== this.height ||
348
+ nextDepth !== this.depth ||
349
+ nextFormat !== this.format;
350
+ handle.label ||= this.id;
351
+
352
+ // @ts-expect-error readonly
353
+ this.handle = handle;
354
+ // @ts-expect-error readonly
355
+ this.width = nextWidth;
356
+ // @ts-expect-error readonly
357
+ this.height = nextHeight;
358
+
359
+ if (props?.depth !== undefined) {
360
+ // @ts-expect-error readonly
361
+ this.depth = nextDepth;
362
+ }
363
+ if (props?.format !== undefined) {
364
+ // @ts-expect-error readonly
365
+ this.format = nextFormat;
366
+ }
367
+
368
+ this.props.handle = handle;
369
+ if (props?.width !== undefined) {
370
+ this.props.width = props.width;
371
+ }
372
+ if (props?.height !== undefined) {
373
+ this.props.height = props.height;
374
+ }
375
+ if (props?.depth !== undefined) {
376
+ this.props.depth = props.depth;
377
+ }
378
+ if (props?.format !== undefined) {
379
+ this.props.format = props.format;
380
+ }
381
+
382
+ if (allocationMayHaveChanged) {
383
+ const nextAllocation = this.getAllocatedByteLength();
384
+ if (nextAllocation !== this._allocatedByteLength) {
385
+ this._allocatedByteLength = nextAllocation;
386
+ this.trackReferencedMemory(nextAllocation, 'Texture');
387
+ }
388
+ }
389
+ this.view._reinitialize(this);
390
+ }
342
391
  }
@@ -14,7 +14,7 @@ import {WebGPURenderPass} from './webgpu-render-pass';
14
14
  /** VertexArrayObject wrapper */
15
15
  export class WebGPUVertexArray extends VertexArray {
16
16
  override get [Symbol.toStringTag](): string {
17
- return 'WebGPUVertexArray';
17
+ return 'VertexArray';
18
18
  }
19
19
 
20
20
  readonly device: WebGPUDevice;
@@ -10,6 +10,7 @@ import {CanvasContext, Texture, log} from '@luma.gl/core';
10
10
  import {WebGPUDevice} from './webgpu-device';
11
11
  import {WebGPUFramebuffer} from './resources/webgpu-framebuffer';
12
12
  import {WebGPUTexture} from './resources/webgpu-texture';
13
+ import {getCpuHotspotProfiler, getTimestamp} from './helpers/cpu-hotspot-profiler';
13
14
 
14
15
  /**
15
16
  * Holds a WebGPU Canvas Context
@@ -20,7 +21,9 @@ export class WebGPUCanvasContext extends CanvasContext {
20
21
  readonly device: WebGPUDevice;
21
22
  readonly handle: GPUCanvasContext;
22
23
 
24
+ private colorAttachment: WebGPUTexture | null = null;
23
25
  private depthStencilAttachment: WebGPUTexture | null = null;
26
+ private framebuffer: WebGPUFramebuffer | null = null;
24
27
 
25
28
  get [Symbol.toStringTag](): string {
26
29
  return 'WebGPUCanvasContext';
@@ -39,10 +42,23 @@ export class WebGPUCanvasContext extends CanvasContext {
39
42
  // Base class constructor cannot access derived methods/fields, so we need to call these functions in the subclass constructor
40
43
  this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
41
44
  this._configureDevice();
45
+ this._startObservers();
42
46
  }
43
47
 
44
48
  /** Destroy any textures produced while configured and remove the context configuration. */
45
49
  override destroy(): void {
50
+ if (this.framebuffer) {
51
+ this.framebuffer.destroy();
52
+ this.framebuffer = null;
53
+ }
54
+ if (this.colorAttachment) {
55
+ this.colorAttachment.destroy();
56
+ this.colorAttachment = null;
57
+ }
58
+ if (this.depthStencilAttachment) {
59
+ this.depthStencilAttachment.destroy();
60
+ this.depthStencilAttachment = null;
61
+ }
46
62
  this.handle.unconfigure();
47
63
  super.destroy();
48
64
  }
@@ -65,6 +81,8 @@ export class WebGPUCanvasContext extends CanvasContext {
65
81
  colorSpace: this.props.colorSpace,
66
82
  alphaMode: this.props.alphaMode
67
83
  });
84
+
85
+ this._createDepthStencilAttachment(this.device.preferredDepthFormat);
68
86
  }
69
87
 
70
88
  /** Update framebuffer with properly resized "swap chain" texture views */
@@ -73,50 +91,97 @@ export class WebGPUCanvasContext extends CanvasContext {
73
91
  depthStencilFormat: 'depth24plus'
74
92
  }
75
93
  ): WebGPUFramebuffer {
76
- // Wrap the current canvas context texture in a luma.gl texture
77
- const currentColorAttachment = this._getCurrentTexture();
78
- // TODO - temporary debug code
79
- if (
80
- currentColorAttachment.width !== this.drawingBufferWidth ||
81
- currentColorAttachment.height !== this.drawingBufferHeight
82
- ) {
83
- const [oldWidth, oldHeight] = this.getDrawingBufferSize();
84
- this.drawingBufferWidth = currentColorAttachment.width;
85
- this.drawingBufferHeight = currentColorAttachment.height;
86
- log.log(
87
- 1,
88
- `${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`
89
- )();
94
+ const profiler = getCpuHotspotProfiler(this.device);
95
+ const startTime = profiler ? getTimestamp() : 0;
96
+ if (profiler) {
97
+ profiler.framebufferAcquireCount = (profiler.framebufferAcquireCount || 0) + 1;
98
+ profiler.activeDefaultFramebufferAcquireDepth =
99
+ (profiler.activeDefaultFramebufferAcquireDepth || 0) + 1;
90
100
  }
91
101
 
92
- // Resize the depth stencil attachment
93
- if (options?.depthStencilFormat) {
94
- this._createDepthStencilAttachment(options?.depthStencilFormat);
102
+ try {
103
+ // Wrap the current canvas context texture in a luma.gl texture
104
+ const currentColorAttachment = this._getCurrentTexture();
105
+ // TODO - temporary debug code
106
+ if (
107
+ currentColorAttachment.width !== this.drawingBufferWidth ||
108
+ currentColorAttachment.height !== this.drawingBufferHeight
109
+ ) {
110
+ const [oldWidth, oldHeight] = this.getDrawingBufferSize();
111
+ this.drawingBufferWidth = currentColorAttachment.width;
112
+ this.drawingBufferHeight = currentColorAttachment.height;
113
+ log.log(
114
+ 1,
115
+ `${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`
116
+ )();
117
+ }
118
+
119
+ // Resize the depth stencil attachment
120
+ if (options?.depthStencilFormat) {
121
+ this._createDepthStencilAttachment(options?.depthStencilFormat);
122
+ }
123
+
124
+ this.framebuffer ||= new WebGPUFramebuffer(this.device, {
125
+ id: `${this.id}#framebuffer`,
126
+ colorAttachments: [currentColorAttachment],
127
+ depthStencilAttachment: null
128
+ });
129
+ this.framebuffer._reinitialize(
130
+ currentColorAttachment.view,
131
+ options?.depthStencilFormat ? this.depthStencilAttachment?.view || null : null
132
+ );
133
+ return this.framebuffer;
134
+ } finally {
135
+ if (profiler) {
136
+ profiler.activeDefaultFramebufferAcquireDepth =
137
+ (profiler.activeDefaultFramebufferAcquireDepth || 1) - 1;
138
+ profiler.framebufferAcquireTimeMs =
139
+ (profiler.framebufferAcquireTimeMs || 0) + (getTimestamp() - startTime);
140
+ }
95
141
  }
96
-
97
- return new WebGPUFramebuffer(this.device, {
98
- colorAttachments: [currentColorAttachment],
99
- depthStencilAttachment: this.depthStencilAttachment
100
- });
101
142
  }
102
143
 
103
144
  // PRIMARY METHODS
104
145
 
105
146
  /** Wrap the current canvas context texture in a luma.gl texture */
106
147
  _getCurrentTexture(): WebGPUTexture {
148
+ const profiler = getCpuHotspotProfiler(this.device);
149
+ const currentTextureStartTime = profiler ? getTimestamp() : 0;
107
150
  const handle = this.handle.getCurrentTexture();
108
- return this.device.createTexture({
109
- id: `${this.id}#color-texture`,
151
+ if (profiler) {
152
+ profiler.currentTextureAcquireCount = (profiler.currentTextureAcquireCount || 0) + 1;
153
+ profiler.currentTextureAcquireTimeMs =
154
+ (profiler.currentTextureAcquireTimeMs || 0) + (getTimestamp() - currentTextureStartTime);
155
+ }
156
+ if (!this.colorAttachment) {
157
+ this.colorAttachment = this.device.createTexture({
158
+ id: `${this.id}#color-texture`,
159
+ handle,
160
+ format: this.device.preferredColorFormat,
161
+ width: handle.width,
162
+ height: handle.height
163
+ });
164
+ return this.colorAttachment;
165
+ }
166
+
167
+ this.colorAttachment._reinitialize(handle, {
110
168
  handle,
111
169
  format: this.device.preferredColorFormat,
112
170
  width: handle.width,
113
171
  height: handle.height
114
172
  });
173
+ return this.colorAttachment;
115
174
  }
116
175
 
117
176
  /** We build render targets on demand (i.e. not when size changes but when about to render) */
118
177
  _createDepthStencilAttachment(depthStencilFormat: TextureFormatDepthStencil): WebGPUTexture {
119
- if (!this.depthStencilAttachment) {
178
+ const needsNewDepthStencilAttachment =
179
+ !this.depthStencilAttachment ||
180
+ this.depthStencilAttachment.width !== this.drawingBufferWidth ||
181
+ this.depthStencilAttachment.height !== this.drawingBufferHeight ||
182
+ this.depthStencilAttachment.format !== depthStencilFormat;
183
+ if (needsNewDepthStencilAttachment) {
184
+ this.depthStencilAttachment?.destroy();
120
185
  this.depthStencilAttachment = this.device.createTexture({
121
186
  id: `${this.id}#depth-stencil-texture`,
122
187
  usage: Texture.RENDER_ATTACHMENT,
@@ -125,6 +190,6 @@ export class WebGPUCanvasContext extends CanvasContext {
125
190
  height: this.drawingBufferHeight
126
191
  });
127
192
  }
128
- return this.depthStencilAttachment;
193
+ return this.depthStencilAttachment!;
129
194
  }
130
195
  }