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

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 (125) 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 +4 -6
  10. package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
  11. package/dist/adapter/helpers/get-bind-group.js +31 -30
  12. package/dist/adapter/helpers/get-bind-group.js.map +1 -1
  13. package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts +3 -1
  14. package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts.map +1 -1
  15. package/dist/adapter/helpers/get-vertex-buffer-layout.js +17 -12
  16. package/dist/adapter/helpers/get-vertex-buffer-layout.js.map +1 -1
  17. package/dist/adapter/helpers/webgpu-parameters.d.ts.map +1 -1
  18. package/dist/adapter/helpers/webgpu-parameters.js +1 -0
  19. package/dist/adapter/helpers/webgpu-parameters.js.map +1 -1
  20. package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -1
  21. package/dist/adapter/resources/webgpu-buffer.js +19 -3
  22. package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
  23. package/dist/adapter/resources/webgpu-command-buffer.js +1 -1
  24. package/dist/adapter/resources/webgpu-command-buffer.js.map +1 -1
  25. package/dist/adapter/resources/webgpu-command-encoder.d.ts +7 -16
  26. package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
  27. package/dist/adapter/resources/webgpu-command-encoder.js +89 -32
  28. package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -1
  29. package/dist/adapter/resources/webgpu-compute-pass.d.ts +3 -3
  30. package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -1
  31. package/dist/adapter/resources/webgpu-compute-pass.js +30 -12
  32. package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -1
  33. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts +7 -9
  34. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
  35. package/dist/adapter/resources/webgpu-compute-pipeline.js +30 -17
  36. package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
  37. package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -1
  38. package/dist/adapter/resources/webgpu-fence.js +9 -1
  39. package/dist/adapter/resources/webgpu-fence.js.map +1 -1
  40. package/dist/adapter/resources/webgpu-framebuffer.d.ts +6 -0
  41. package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
  42. package/dist/adapter/resources/webgpu-framebuffer.js +16 -0
  43. package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -1
  44. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts +1 -1
  45. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
  46. package/dist/adapter/resources/webgpu-pipeline-layout.js +10 -16
  47. package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
  48. package/dist/adapter/resources/webgpu-query-set.d.ts +33 -4
  49. package/dist/adapter/resources/webgpu-query-set.d.ts.map +1 -1
  50. package/dist/adapter/resources/webgpu-query-set.js +145 -4
  51. package/dist/adapter/resources/webgpu-query-set.js.map +1 -1
  52. package/dist/adapter/resources/webgpu-render-pass.d.ts +6 -3
  53. package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
  54. package/dist/adapter/resources/webgpu-render-pass.js +78 -34
  55. package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
  56. package/dist/adapter/resources/webgpu-render-pipeline.d.ts +14 -10
  57. package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
  58. package/dist/adapter/resources/webgpu-render-pipeline.js +56 -35
  59. package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
  60. package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -1
  61. package/dist/adapter/resources/webgpu-sampler.js +4 -0
  62. package/dist/adapter/resources/webgpu-sampler.js.map +1 -1
  63. package/dist/adapter/resources/webgpu-shader.d.ts.map +1 -1
  64. package/dist/adapter/resources/webgpu-shader.js +17 -1
  65. package/dist/adapter/resources/webgpu-shader.js.map +1 -1
  66. package/dist/adapter/resources/webgpu-texture-view.d.ts +6 -0
  67. package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -1
  68. package/dist/adapter/resources/webgpu-texture-view.js +47 -11
  69. package/dist/adapter/resources/webgpu-texture-view.js.map +1 -1
  70. package/dist/adapter/resources/webgpu-texture.d.ts +18 -5
  71. package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
  72. package/dist/adapter/resources/webgpu-texture.js +148 -97
  73. package/dist/adapter/resources/webgpu-texture.js.map +1 -1
  74. package/dist/adapter/resources/webgpu-vertex-array.js +1 -1
  75. package/dist/adapter/resources/webgpu-vertex-array.js.map +1 -1
  76. package/dist/adapter/webgpu-canvas-context.d.ts +2 -0
  77. package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
  78. package/dist/adapter/webgpu-canvas-context.js +78 -19
  79. package/dist/adapter/webgpu-canvas-context.js.map +1 -1
  80. package/dist/adapter/webgpu-device.d.ts +10 -2
  81. package/dist/adapter/webgpu-device.d.ts.map +1 -1
  82. package/dist/adapter/webgpu-device.js +159 -13
  83. package/dist/adapter/webgpu-device.js.map +1 -1
  84. package/dist/adapter/webgpu-presentation-context.d.ts +25 -0
  85. package/dist/adapter/webgpu-presentation-context.d.ts.map +1 -0
  86. package/dist/adapter/webgpu-presentation-context.js +144 -0
  87. package/dist/adapter/webgpu-presentation-context.js.map +1 -0
  88. package/dist/dist.dev.js +3180 -1849
  89. package/dist/dist.min.js +168 -9
  90. package/dist/index.cjs +1640 -405
  91. package/dist/index.cjs.map +4 -4
  92. package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -1
  93. package/dist/wgsl/get-shader-layout-wgsl.js +8 -0
  94. package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -1
  95. package/package.json +5 -5
  96. package/src/adapter/helpers/cpu-hotspot-profiler.ts +70 -0
  97. package/src/adapter/helpers/generate-mipmaps-webgpu.ts +583 -0
  98. package/src/adapter/helpers/get-bind-group.ts +42 -49
  99. package/src/adapter/helpers/get-vertex-buffer-layout.ts +31 -12
  100. package/src/adapter/helpers/webgpu-parameters.ts +2 -0
  101. package/src/adapter/resources/webgpu-buffer.ts +18 -3
  102. package/src/adapter/resources/webgpu-command-buffer.ts +1 -1
  103. package/src/adapter/resources/webgpu-command-encoder.ts +129 -50
  104. package/src/adapter/resources/webgpu-compute-pass.ts +48 -13
  105. package/src/adapter/resources/webgpu-compute-pipeline.ts +49 -18
  106. package/src/adapter/resources/webgpu-fence.ts +11 -3
  107. package/src/adapter/resources/webgpu-framebuffer.ts +21 -0
  108. package/src/adapter/resources/webgpu-pipeline-layout.ts +16 -14
  109. package/src/adapter/resources/webgpu-query-set.ts +185 -9
  110. package/src/adapter/resources/webgpu-render-pass.ts +92 -40
  111. package/src/adapter/resources/webgpu-render-pipeline.ts +83 -44
  112. package/src/adapter/resources/webgpu-sampler.ts +5 -0
  113. package/src/adapter/resources/webgpu-shader.ts +16 -1
  114. package/src/adapter/resources/webgpu-texture-view.ts +51 -11
  115. package/src/adapter/resources/webgpu-texture.ts +198 -132
  116. package/src/adapter/resources/webgpu-vertex-array.ts +1 -1
  117. package/src/adapter/webgpu-canvas-context.ts +91 -26
  118. package/src/adapter/webgpu-device.ts +212 -17
  119. package/src/adapter/webgpu-presentation-context.ts +180 -0
  120. package/src/wgsl/get-shader-layout-wgsl.ts +9 -0
  121. package/dist/adapter/helpers/accessor-to-format.d.ts +0 -1
  122. package/dist/adapter/helpers/accessor-to-format.d.ts.map +0 -1
  123. package/dist/adapter/helpers/accessor-to-format.js +0 -105
  124. package/dist/adapter/helpers/accessor-to-format.js.map +0 -1
  125. package/src/adapter/helpers/accessor-to-format.ts +0 -104
package/dist/index.cjs CHANGED
@@ -74,11 +74,24 @@ var init_webgpu_buffer = __esm({
74
74
  this.device.reportError(new Error(`${this} creation failed ${error.message}`), this)();
75
75
  this.device.debug();
76
76
  });
77
+ if (!this.props.handle) {
78
+ this.trackAllocatedMemory(size);
79
+ } else {
80
+ this.trackReferencedMemory(size, "Buffer");
81
+ }
77
82
  }
78
83
  destroy() {
79
- var _a;
80
- (_a = this.handle) == null ? void 0 : _a.destroy();
81
- this.handle = null;
84
+ if (!this.destroyed && this.handle) {
85
+ this.removeStats();
86
+ if (!this.props.handle) {
87
+ this.trackDeallocatedMemory();
88
+ this.handle.destroy();
89
+ } else {
90
+ this.trackDeallocatedReferencedMemory("Buffer");
91
+ }
92
+ this.destroyed = true;
93
+ this.handle = null;
94
+ }
82
95
  }
83
96
  write(data, byteOffset = 0) {
84
97
  const arrayBuffer = ArrayBuffer.isView(data) ? data.buffer : data;
@@ -226,18 +239,47 @@ var init_webgpu_sampler = __esm({
226
239
  this.handle.label = this.props.id;
227
240
  }
228
241
  destroy() {
242
+ if (this.destroyed) {
243
+ return;
244
+ }
245
+ this.destroyResource();
229
246
  this.handle = null;
230
247
  }
231
248
  };
232
249
  }
233
250
  });
234
251
 
252
+ // dist/adapter/helpers/cpu-hotspot-profiler.js
253
+ function getCpuHotspotProfiler(owner) {
254
+ const profiler = owner.userData[CPU_HOTSPOT_PROFILER_MODULE];
255
+ return (profiler == null ? void 0 : profiler.enabled) ? profiler : null;
256
+ }
257
+ function getCpuHotspotSubmitReason(owner) {
258
+ return owner.userData[CPU_HOTSPOT_SUBMIT_REASON] || null;
259
+ }
260
+ function setCpuHotspotSubmitReason(owner, submitReason) {
261
+ owner.userData[CPU_HOTSPOT_SUBMIT_REASON] = submitReason;
262
+ }
263
+ function getTimestamp() {
264
+ var _a, _b;
265
+ return ((_b = (_a = globalThis.performance) == null ? void 0 : _a.now) == null ? void 0 : _b.call(_a)) ?? Date.now();
266
+ }
267
+ var CPU_HOTSPOT_PROFILER_MODULE, CPU_HOTSPOT_SUBMIT_REASON;
268
+ var init_cpu_hotspot_profiler = __esm({
269
+ "dist/adapter/helpers/cpu-hotspot-profiler.js"() {
270
+ "use strict";
271
+ CPU_HOTSPOT_PROFILER_MODULE = "cpu-hotspot-profiler";
272
+ CPU_HOTSPOT_SUBMIT_REASON = "cpu-hotspot-submit-reason";
273
+ }
274
+ });
275
+
235
276
  // dist/adapter/resources/webgpu-texture-view.js
236
277
  var import_core3, WebGPUTextureView;
237
278
  var init_webgpu_texture_view = __esm({
238
279
  "dist/adapter/resources/webgpu-texture-view.js"() {
239
280
  "use strict";
240
281
  import_core3 = require("@luma.gl/core");
282
+ init_cpu_hotspot_profiler();
241
283
  WebGPUTextureView = class extends import_core3.TextureView {
242
284
  device;
243
285
  handle;
@@ -247,8 +289,7 @@ var init_webgpu_texture_view = __esm({
247
289
  this.device = device;
248
290
  this.texture = props.texture;
249
291
  this.device.pushErrorScope("validation");
250
- this.handle = // props.handle ||
251
- this.texture.handle.createView({
292
+ this.handle = this.texture.handle.createView({
252
293
  format: this.props.format || this.texture.format,
253
294
  dimension: this.props.dimension || this.texture.dimension,
254
295
  aspect: this.props.aspect,
@@ -264,8 +305,42 @@ var init_webgpu_texture_view = __esm({
264
305
  this.handle.label = this.props.id;
265
306
  }
266
307
  destroy() {
308
+ if (this.destroyed) {
309
+ return;
310
+ }
311
+ this.destroyResource();
267
312
  this.handle = null;
268
313
  }
314
+ /**
315
+ * Internal-only hook for the cached CanvasContext/PresentationContext swapchain path.
316
+ * Rebuilds the default view when the per-frame canvas texture handle changes, without
317
+ * replacing the long-lived luma.gl wrapper object.
318
+ */
319
+ _reinitialize(texture) {
320
+ this.texture = texture;
321
+ const profiler = getCpuHotspotProfiler(this.device);
322
+ this.device.pushErrorScope("validation");
323
+ const createViewStartTime = profiler ? getTimestamp() : 0;
324
+ const handle = this.texture.handle.createView({
325
+ format: this.props.format || this.texture.format,
326
+ dimension: this.props.dimension || this.texture.dimension,
327
+ aspect: this.props.aspect,
328
+ baseMipLevel: this.props.baseMipLevel,
329
+ mipLevelCount: this.props.mipLevelCount,
330
+ baseArrayLayer: this.props.baseArrayLayer,
331
+ arrayLayerCount: this.props.arrayLayerCount
332
+ });
333
+ if (profiler) {
334
+ profiler.textureViewReinitializeCount = (profiler.textureViewReinitializeCount || 0) + 1;
335
+ profiler.textureViewReinitializeTimeMs = (profiler.textureViewReinitializeTimeMs || 0) + (getTimestamp() - createViewStartTime);
336
+ }
337
+ this.device.popErrorScope((error) => {
338
+ this.device.reportError(new Error(`TextureView constructor: ${error.message}`), this)();
339
+ this.device.debug();
340
+ });
341
+ handle.label = this.props.id;
342
+ this.handle = handle;
343
+ }
269
344
  };
270
345
  }
271
346
  });
@@ -284,9 +359,18 @@ var init_webgpu_texture = __esm({
284
359
  handle;
285
360
  sampler;
286
361
  view;
362
+ _allocatedByteLength = 0;
287
363
  constructor(device, props) {
288
364
  super(device, props, { byteAlignment: 256 });
289
365
  this.device = device;
366
+ if (props.sampler instanceof WebGPUSampler) {
367
+ this.sampler = props.sampler;
368
+ } else if (props.sampler === void 0) {
369
+ this.sampler = this.device.getDefaultSampler();
370
+ } else {
371
+ this.sampler = new WebGPUSampler(this.device, props.sampler || {});
372
+ this.attachResource(this.sampler);
373
+ }
290
374
  this.device.pushErrorScope("out-of-memory");
291
375
  this.device.pushErrorScope("validation");
292
376
  this.handle = this.props.handle || this.device.handle.createTexture({
@@ -315,7 +399,6 @@ var init_webgpu_texture = __esm({
315
399
  this.width = this.handle.width;
316
400
  this.height = this.handle.height;
317
401
  }
318
- this.sampler = props.sampler instanceof WebGPUSampler ? props.sampler : new WebGPUSampler(this.device, props.sampler || {});
319
402
  this.view = new WebGPUTextureView(this.device, {
320
403
  ...this.props,
321
404
  texture: this,
@@ -323,11 +406,26 @@ var init_webgpu_texture = __esm({
323
406
  // Note: arrayLayerCount controls the view of array textures, but does not apply to 3d texture depths
324
407
  arrayLayerCount: this.dimension !== "3d" ? this.depth : 1
325
408
  });
409
+ this.attachResource(this.view);
326
410
  this._initializeData(props.data);
411
+ this._allocatedByteLength = this.getAllocatedByteLength();
412
+ if (!this.props.handle) {
413
+ this.trackAllocatedMemory(this._allocatedByteLength, "Texture");
414
+ } else {
415
+ this.trackReferencedMemory(this._allocatedByteLength, "Texture");
416
+ }
327
417
  }
328
418
  destroy() {
329
- var _a;
330
- (_a = this.handle) == null ? void 0 : _a.destroy();
419
+ if (this.destroyed) {
420
+ return;
421
+ }
422
+ if (!this.props.handle && this.handle) {
423
+ this.trackDeallocatedMemory("Texture");
424
+ this.handle.destroy();
425
+ } else if (this.handle) {
426
+ this.trackDeallocatedReferencedMemory("Texture");
427
+ }
428
+ this.destroyResource();
331
429
  this.handle = null;
332
430
  }
333
431
  createView(props) {
@@ -363,36 +461,6 @@ var init_webgpu_texture = __esm({
363
461
  });
364
462
  return { width: options.width, height: options.height };
365
463
  }
366
- copyImageData(options_) {
367
- const { width, height, depth } = this;
368
- const options = this._normalizeCopyImageDataOptions(options_);
369
- this.device.pushErrorScope("validation");
370
- this.device.handle.queue.writeTexture(
371
- // destination: GPUImageCopyTexture
372
- {
373
- // texture subresource
374
- texture: this.handle,
375
- mipLevel: options.mipLevel,
376
- aspect: options.aspect,
377
- // origin to write to
378
- origin: [options.x, options.y, options.z]
379
- },
380
- // data
381
- options.data,
382
- // dataLayout: GPUImageDataLayout
383
- {
384
- offset: options.byteOffset,
385
- bytesPerRow: options.bytesPerRow,
386
- rowsPerImage: options.rowsPerImage
387
- },
388
- // size: GPUExtent3D - extents of the content to write
389
- [width, height, depth]
390
- );
391
- this.device.popErrorScope((error) => {
392
- this.device.reportError(new Error(`copyImageData: ${error.message}`), this)();
393
- this.device.debug();
394
- });
395
- }
396
464
  generateMipmapsWebGL() {
397
465
  import_core4.log.warn(`${this}: generateMipmaps not supported in WebGPU`)();
398
466
  }
@@ -404,66 +472,63 @@ var init_webgpu_texture = __esm({
404
472
  };
405
473
  }
406
474
  readBuffer(options = {}, buffer) {
407
- const { x = 0, y = 0, z = 0, width = this.width, height = this.height, depthOrArrayLayers = this.depth, mipLevel = 0, aspect = "all" } = options;
408
- const layout = this.computeMemoryLayout(options);
409
- const { bytesPerRow, rowsPerImage, byteLength } = layout;
410
- const readBuffer = buffer || this.device.createBuffer({
411
- byteLength,
412
- usage: import_core4.Buffer.COPY_DST | import_core4.Buffer.MAP_READ
413
- });
414
- const gpuReadBuffer = readBuffer.handle;
475
+ if (!buffer) {
476
+ throw new Error(`${this} readBuffer requires a destination buffer`);
477
+ }
478
+ const { x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect } = this._getSupportedColorReadOptions(options);
479
+ const byteOffset = options.byteOffset ?? 0;
480
+ const layout = this.computeMemoryLayout({ width, height, depthOrArrayLayers, mipLevel });
481
+ const { byteLength } = layout;
482
+ if (buffer.byteLength < byteOffset + byteLength) {
483
+ throw new Error(`${this} readBuffer target is too small (${buffer.byteLength} < ${byteOffset + byteLength})`);
484
+ }
415
485
  const gpuDevice = this.device.handle;
416
486
  this.device.pushErrorScope("validation");
417
487
  const commandEncoder = gpuDevice.createCommandEncoder();
418
- commandEncoder.copyTextureToBuffer(
419
- // source
420
- {
421
- texture: this.handle,
422
- origin: { x, y, z },
423
- // origin: [options.x, options.y, 0], // options.depth],
424
- mipLevel,
425
- aspect
426
- // colorSpace: options.colorSpace,
427
- // premultipliedAlpha: options.premultipliedAlpha
428
- },
429
- // destination
430
- {
431
- buffer: gpuReadBuffer,
432
- offset: 0,
433
- bytesPerRow,
434
- rowsPerImage
435
- },
436
- // copy size
437
- {
438
- width,
439
- height,
440
- depthOrArrayLayers
441
- }
442
- );
488
+ this.copyToBuffer(commandEncoder, { x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect, byteOffset }, buffer);
443
489
  const commandBuffer = commandEncoder.finish();
444
490
  this.device.handle.queue.submit([commandBuffer]);
445
491
  this.device.popErrorScope((error) => {
446
492
  this.device.reportError(new Error(`${this} readBuffer: ${error.message}`), this)();
447
493
  this.device.debug();
448
494
  });
449
- return readBuffer;
495
+ return buffer;
450
496
  }
451
497
  async readDataAsync(options = {}) {
452
- const buffer = this.readBuffer(options);
453
- const data = await buffer.readAsync();
454
- buffer.destroy();
455
- return data.buffer;
456
- }
457
- writeBuffer(buffer, options = {}) {
458
- const { x = 0, y = 0, z = 0, width = this.width, height = this.height, depthOrArrayLayers = this.depth, mipLevel = 0, aspect = "all" } = options;
459
- const layout = this.computeMemoryLayout(options);
460
- const { bytesPerRow, rowsPerImage } = layout;
498
+ throw new Error(`${this} readDataAsync is deprecated; use readBuffer() with an explicit destination buffer or DynamicTexture.readAsync()`);
499
+ }
500
+ copyToBuffer(commandEncoder, options = {}, buffer) {
501
+ const { byteOffset = 0, bytesPerRow: requestedBytesPerRow, rowsPerImage: requestedRowsPerImage, ...textureReadOptions } = options;
502
+ const { x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect } = this._getSupportedColorReadOptions(textureReadOptions);
503
+ const layout = this.computeMemoryLayout({ width, height, depthOrArrayLayers, mipLevel });
504
+ const effectiveBytesPerRow = requestedBytesPerRow ?? layout.bytesPerRow;
505
+ const effectiveRowsPerImage = requestedRowsPerImage ?? layout.rowsPerImage;
506
+ const webgpuBuffer = buffer;
507
+ commandEncoder.copyTextureToBuffer({
508
+ texture: this.handle,
509
+ origin: { x, y, z },
510
+ mipLevel,
511
+ aspect
512
+ }, {
513
+ buffer: webgpuBuffer.handle,
514
+ offset: byteOffset,
515
+ bytesPerRow: effectiveBytesPerRow,
516
+ rowsPerImage: effectiveRowsPerImage
517
+ }, {
518
+ width,
519
+ height,
520
+ depthOrArrayLayers
521
+ });
522
+ }
523
+ writeBuffer(buffer, options_ = {}) {
524
+ const options = this._normalizeTextureWriteOptions(options_);
525
+ const { x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect, byteOffset, bytesPerRow, rowsPerImage } = options;
461
526
  const gpuDevice = this.device.handle;
462
527
  this.device.pushErrorScope("validation");
463
528
  const commandEncoder = gpuDevice.createCommandEncoder();
464
529
  commandEncoder.copyBufferToTexture({
465
530
  buffer: buffer.handle,
466
- offset: 0,
531
+ offset: byteOffset,
467
532
  bytesPerRow,
468
533
  rowsPerImage
469
534
  }, {
@@ -479,33 +544,88 @@ var init_webgpu_texture = __esm({
479
544
  this.device.debug();
480
545
  });
481
546
  }
482
- writeData(data, options = {}) {
547
+ writeData(data, options_ = {}) {
483
548
  const device = this.device;
484
- const { x = 0, y = 0, z = 0, width = this.width, height = this.height, depthOrArrayLayers = this.depth, mipLevel = 0, aspect = "all" } = options;
485
- const layout = import_core4.textureFormatDecoder.computeMemoryLayout({
549
+ const options = this._normalizeTextureWriteOptions(options_);
550
+ const { x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect, byteOffset } = options;
551
+ const source = data;
552
+ const formatInfo = this.device.getTextureFormatInfo(this.format);
553
+ const packedSourceLayout = import_core4.textureFormatDecoder.computeMemoryLayout({
486
554
  format: this.format,
487
- width: this.width,
488
- height: this.height,
489
- depth: this.depth,
490
- byteAlignment: this.byteAlignment
555
+ width,
556
+ height,
557
+ depth: depthOrArrayLayers,
558
+ byteAlignment: 1
491
559
  });
492
- const { bytesPerRow, rowsPerImage } = layout;
560
+ const bytesPerRow = options_.bytesPerRow ?? packedSourceLayout.bytesPerRow;
561
+ const rowsPerImage = options_.rowsPerImage ?? packedSourceLayout.rowsPerImage;
562
+ let copyWidth = width;
563
+ let copyHeight = height;
564
+ if (formatInfo.compressed) {
565
+ const blockWidth = formatInfo.blockWidth || 1;
566
+ const blockHeight = formatInfo.blockHeight || 1;
567
+ copyWidth = Math.ceil(width / blockWidth) * blockWidth;
568
+ copyHeight = Math.ceil(height / blockHeight) * blockHeight;
569
+ }
493
570
  this.device.pushErrorScope("validation");
494
571
  device.handle.queue.writeTexture({
495
572
  texture: this.handle,
496
573
  mipLevel,
497
574
  aspect,
498
575
  origin: { x, y, z }
499
- }, data, {
500
- offset: 0,
576
+ }, source, {
577
+ offset: byteOffset,
501
578
  bytesPerRow,
502
579
  rowsPerImage
503
- }, { width, height, depthOrArrayLayers });
580
+ }, { width: copyWidth, height: copyHeight, depthOrArrayLayers });
504
581
  this.device.popErrorScope((error) => {
505
582
  this.device.reportError(new Error(`${this} writeData: ${error.message}`), this)();
506
583
  this.device.debug();
507
584
  });
508
585
  }
586
+ /**
587
+ * Internal-only hook for the cached CanvasContext/PresentationContext swapchain path.
588
+ * Rebinds this handle-backed texture wrapper to the current per-frame canvas texture
589
+ * without allocating a new luma.gl Texture or TextureView wrapper.
590
+ */
591
+ _reinitialize(handle, props) {
592
+ const nextWidth = (props == null ? void 0 : props.width) ?? handle.width ?? this.width;
593
+ const nextHeight = (props == null ? void 0 : props.height) ?? handle.height ?? this.height;
594
+ const nextDepth = (props == null ? void 0 : props.depth) ?? this.depth;
595
+ const nextFormat = (props == null ? void 0 : props.format) ?? this.format;
596
+ const allocationMayHaveChanged = nextWidth !== this.width || nextHeight !== this.height || nextDepth !== this.depth || nextFormat !== this.format;
597
+ handle.label ||= this.id;
598
+ this.handle = handle;
599
+ this.width = nextWidth;
600
+ this.height = nextHeight;
601
+ if ((props == null ? void 0 : props.depth) !== void 0) {
602
+ this.depth = nextDepth;
603
+ }
604
+ if ((props == null ? void 0 : props.format) !== void 0) {
605
+ this.format = nextFormat;
606
+ }
607
+ this.props.handle = handle;
608
+ if ((props == null ? void 0 : props.width) !== void 0) {
609
+ this.props.width = props.width;
610
+ }
611
+ if ((props == null ? void 0 : props.height) !== void 0) {
612
+ this.props.height = props.height;
613
+ }
614
+ if ((props == null ? void 0 : props.depth) !== void 0) {
615
+ this.props.depth = props.depth;
616
+ }
617
+ if ((props == null ? void 0 : props.format) !== void 0) {
618
+ this.props.format = props.format;
619
+ }
620
+ if (allocationMayHaveChanged) {
621
+ const nextAllocation = this.getAllocatedByteLength();
622
+ if (nextAllocation !== this._allocatedByteLength) {
623
+ this._allocatedByteLength = nextAllocation;
624
+ this.trackReferencedMemory(nextAllocation, "Texture");
625
+ }
626
+ }
627
+ this.view._reinitialize(this);
628
+ }
509
629
  };
510
630
  }
511
631
  });
@@ -586,7 +706,19 @@ var init_webgpu_shader = __esm({
586
706
  }
587
707
  /** Returns compilation info for this shader */
588
708
  async getCompilationInfo() {
589
- const compilationInfo = await this.handle.getCompilationInfo();
709
+ const handle = this.handle;
710
+ if (!handle) {
711
+ return [];
712
+ }
713
+ let compilationInfo;
714
+ try {
715
+ compilationInfo = await handle.getCompilationInfo();
716
+ } catch (error) {
717
+ if (this.device.shouldIgnoreDroppedInstanceError(error, "getCompilationInfo")) {
718
+ return [];
719
+ }
720
+ throw error;
721
+ }
590
722
  return compilationInfo.messages;
591
723
  }
592
724
  };
@@ -677,6 +809,7 @@ var init_webgpu_parameters = __esm({
677
809
  const depthStencil = addDepthStencil(descriptor);
678
810
  depthStencil.format = value;
679
811
  },
812
+ clearDepth: notSupported,
680
813
  depthBias: (_, value, descriptor) => {
681
814
  const depthStencil = addDepthStencil(descriptor);
682
815
  depthStencil.depthBias = value;
@@ -815,96 +948,6 @@ var init_webgpu_parameters = __esm({
815
948
  }
816
949
  });
817
950
 
818
- // dist/adapter/helpers/get-bind-group.js
819
- function getBindGroup(device, bindGroupLayout, shaderLayout, bindings) {
820
- const entries = getBindGroupEntries(bindings, shaderLayout);
821
- device.pushErrorScope("validation");
822
- const bindGroup = device.createBindGroup({
823
- layout: bindGroupLayout,
824
- entries
825
- });
826
- device.popErrorScope().then((error) => {
827
- if (error) {
828
- import_core8.log.error(`bindGroup creation: ${error.message}`, bindGroup)();
829
- }
830
- });
831
- return bindGroup;
832
- }
833
- function getShaderLayoutBinding(shaderLayout, bindingName, options) {
834
- const bindingLayout = shaderLayout.bindings.find((binding) => binding.name === bindingName || `${binding.name.toLocaleLowerCase()}uniforms` === bindingName.toLocaleLowerCase());
835
- if (!bindingLayout && !(options == null ? void 0 : options.ignoreWarnings)) {
836
- import_core8.log.warn(`Binding ${bindingName} not set: Not found in shader layout.`)();
837
- }
838
- return bindingLayout || null;
839
- }
840
- function getBindGroupEntries(bindings, shaderLayout) {
841
- const entries = [];
842
- for (const [bindingName, value] of Object.entries(bindings)) {
843
- let bindingLayout = getShaderLayoutBinding(shaderLayout, bindingName);
844
- if (bindingLayout) {
845
- const entry = getBindGroupEntry(value, bindingLayout.location, void 0, bindingName);
846
- if (entry) {
847
- entries.push(entry);
848
- }
849
- }
850
- if (value instanceof import_core8.Texture) {
851
- bindingLayout = getShaderLayoutBinding(shaderLayout, `${bindingName}Sampler`, {
852
- ignoreWarnings: true
853
- });
854
- if (bindingLayout) {
855
- const entry = getBindGroupEntry(value, bindingLayout.location, { sampler: true }, bindingName);
856
- if (entry) {
857
- entries.push(entry);
858
- }
859
- }
860
- }
861
- }
862
- return entries;
863
- }
864
- function getBindGroupEntry(binding, index, options, bindingName = "unknown") {
865
- if (binding instanceof import_core8.Buffer) {
866
- return {
867
- binding: index,
868
- resource: {
869
- buffer: binding.handle
870
- }
871
- };
872
- }
873
- if (binding instanceof import_core8.Sampler) {
874
- return {
875
- binding: index,
876
- resource: binding.handle
877
- };
878
- }
879
- if (binding instanceof import_core8.TextureView) {
880
- return {
881
- binding: index,
882
- resource: binding.handle
883
- };
884
- }
885
- if (binding instanceof import_core8.Texture) {
886
- if (options == null ? void 0 : options.sampler) {
887
- return {
888
- binding: index,
889
- resource: binding.sampler.handle
890
- };
891
- }
892
- return {
893
- binding: index,
894
- resource: binding.view.handle
895
- };
896
- }
897
- import_core8.log.warn(`invalid binding ${bindingName}`, binding);
898
- return null;
899
- }
900
- var import_core8;
901
- var init_get_bind_group = __esm({
902
- "dist/adapter/helpers/get-bind-group.js"() {
903
- "use strict";
904
- import_core8 = require("@luma.gl/core");
905
- }
906
- });
907
-
908
951
  // dist/adapter/helpers/get-vertex-buffer-layout.js
909
952
  function getWebGPUVertexFormat(format) {
910
953
  if (format.endsWith("-webgl")) {
@@ -912,9 +955,10 @@ function getWebGPUVertexFormat(format) {
912
955
  }
913
956
  return format;
914
957
  }
915
- function getVertexBufferLayout(shaderLayout, bufferLayout) {
958
+ function getVertexBufferLayout(shaderLayout, bufferLayout, options) {
916
959
  const vertexBufferLayouts = [];
917
960
  const usedAttributes = /* @__PURE__ */ new Set();
961
+ const shaderAttributes = shaderLayout.attributes || [];
918
962
  for (const mapping of bufferLayout) {
919
963
  const vertexAttributes = [];
920
964
  let stepMode = "vertex";
@@ -923,7 +967,7 @@ function getVertexBufferLayout(shaderLayout, bufferLayout) {
923
967
  if (mapping.attributes) {
924
968
  for (const attributeMapping of mapping.attributes) {
925
969
  const attributeName = attributeMapping.attribute;
926
- const attributeLayout = findAttributeLayout(shaderLayout, attributeName, usedAttributes);
970
+ const attributeLayout = findAttributeLayout(shaderLayout, attributeName, usedAttributes, options);
927
971
  const location = attributeLayout == null ? void 0 : attributeLayout.location;
928
972
  format = attributeMapping.format || mapping.format;
929
973
  stepMode = (attributeLayout == null ? void 0 : attributeLayout.stepMode) || ((attributeLayout == null ? void 0 : attributeLayout.name.startsWith("instance")) ? "instance" : "vertex");
@@ -932,14 +976,14 @@ function getVertexBufferLayout(shaderLayout, bufferLayout) {
932
976
  offset: attributeMapping.byteOffset,
933
977
  shaderLocation: location
934
978
  });
935
- byteStride += (0, import_core9.getVertexFormatInfo)(format).byteLength;
979
+ byteStride += import_core8.vertexFormatDecoder.getVertexFormatInfo(format).byteLength;
936
980
  }
937
981
  } else {
938
- const attributeLayout = findAttributeLayout(shaderLayout, mapping.name, usedAttributes);
982
+ const attributeLayout = findAttributeLayout(shaderLayout, mapping.name, usedAttributes, options);
939
983
  if (!attributeLayout) {
940
984
  continue;
941
985
  }
942
- byteStride = (0, import_core9.getVertexFormatInfo)(format).byteLength;
986
+ byteStride = import_core8.vertexFormatDecoder.getVertexFormatInfo(format).byteLength;
943
987
  stepMode = attributeLayout.stepMode || (attributeLayout.name.startsWith("instance") ? "instance" : "vertex");
944
988
  vertexAttributes.push({
945
989
  format: getWebGPUVertexFormat(format),
@@ -954,10 +998,10 @@ function getVertexBufferLayout(shaderLayout, bufferLayout) {
954
998
  attributes: vertexAttributes
955
999
  });
956
1000
  }
957
- for (const attribute of shaderLayout.attributes) {
1001
+ for (const attribute of shaderAttributes) {
958
1002
  if (!usedAttributes.has(attribute.name)) {
959
1003
  vertexBufferLayouts.push({
960
- arrayStride: (0, import_core9.getVertexFormatInfo)("float32x3").byteLength,
1004
+ arrayStride: import_core8.vertexFormatDecoder.getVertexFormatInfo("float32x3").byteLength,
961
1005
  stepMode: attribute.stepMode || (attribute.name.startsWith("instance") ? "instance" : "vertex"),
962
1006
  attributes: [
963
1007
  {
@@ -976,10 +1020,12 @@ function getVertexBufferLayout(shaderLayout, bufferLayout) {
976
1020
  });
977
1021
  return vertexBufferLayouts;
978
1022
  }
979
- function findAttributeLayout(shaderLayout, name, attributeNames) {
980
- const attribute = shaderLayout.attributes.find((attribute_) => attribute_.name === name);
1023
+ function findAttributeLayout(shaderLayout, name, attributeNames, options) {
1024
+ var _a;
1025
+ const attribute = (_a = shaderLayout.attributes) == null ? void 0 : _a.find((attribute_) => attribute_.name === name);
981
1026
  if (!attribute) {
982
- import_core9.log.warn(`Supplied attribute not present in shader layout: ${name}`)();
1027
+ const pipelineContext = (options == null ? void 0 : options.pipelineId) ? `RenderPipeline(${options.pipelineId})` : "RenderPipeline";
1028
+ import_core8.log.warn(`${pipelineContext}: Ignoring "${name}" attribute, since it is not present in shader layout.`)();
983
1029
  return null;
984
1030
  }
985
1031
  if (attributeNames) {
@@ -990,45 +1036,58 @@ function findAttributeLayout(shaderLayout, name, attributeNames) {
990
1036
  }
991
1037
  return attribute;
992
1038
  }
993
- var import_core9;
1039
+ var import_core8;
994
1040
  var init_get_vertex_buffer_layout = __esm({
995
1041
  "dist/adapter/helpers/get-vertex-buffer-layout.js"() {
996
1042
  "use strict";
997
- import_core9 = require("@luma.gl/core");
1043
+ import_core8 = require("@luma.gl/core");
998
1044
  }
999
1045
  });
1000
1046
 
1001
1047
  // dist/adapter/resources/webgpu-render-pipeline.js
1002
- var import_core10, WebGPURenderPipeline;
1048
+ function createBindGroupCacheKeys(bindingsByGroup) {
1049
+ const bindGroupCacheKeys = {};
1050
+ for (const [groupKey, groupBindings] of Object.entries(bindingsByGroup)) {
1051
+ if (groupBindings && Object.keys(groupBindings).length > 0) {
1052
+ bindGroupCacheKeys[Number(groupKey)] = {};
1053
+ }
1054
+ }
1055
+ return bindGroupCacheKeys;
1056
+ }
1057
+ var import_core9, WebGPURenderPipeline;
1003
1058
  var init_webgpu_render_pipeline = __esm({
1004
1059
  "dist/adapter/resources/webgpu-render-pipeline.js"() {
1005
1060
  "use strict";
1006
- import_core10 = require("@luma.gl/core");
1061
+ import_core9 = require("@luma.gl/core");
1007
1062
  init_webgpu_parameters();
1008
1063
  init_convert_texture_format();
1009
- init_get_bind_group();
1010
1064
  init_get_vertex_buffer_layout();
1011
- WebGPURenderPipeline = class extends import_core10.RenderPipeline {
1065
+ WebGPURenderPipeline = class extends import_core9.RenderPipeline {
1012
1066
  device;
1013
1067
  handle;
1068
+ descriptor;
1014
1069
  vs;
1015
1070
  fs = null;
1016
- /** For internal use to create BindGroups */
1017
- _bindings;
1018
- _bindGroupLayout = null;
1019
- _bindGroup = null;
1071
+ /** Compatibility path for direct pipeline.setBindings() usage */
1072
+ _bindingsByGroup;
1073
+ _bindGroupCacheKeysByGroup = {};
1020
1074
  get [Symbol.toStringTag]() {
1021
1075
  return "WebGPURenderPipeline";
1022
1076
  }
1023
1077
  constructor(device, props) {
1024
1078
  super(device, props);
1025
1079
  this.device = device;
1080
+ this.shaderLayout ||= this.device.getShaderLayout(props.vs.source) || {
1081
+ attributes: [],
1082
+ bindings: []
1083
+ };
1026
1084
  this.handle = this.props.handle;
1085
+ let descriptor = null;
1027
1086
  if (!this.handle) {
1028
- const descriptor = this._getRenderPipelineDescriptor();
1029
- import_core10.log.groupCollapsed(1, `new WebGPURenderPipeline(${this.id})`)();
1030
- import_core10.log.probe(1, JSON.stringify(descriptor, null, 2))();
1031
- import_core10.log.groupEnd(1)();
1087
+ descriptor = this._getRenderPipelineDescriptor();
1088
+ import_core9.log.groupCollapsed(1, `new WebGPURenderPipeline(${this.id})`)();
1089
+ import_core9.log.probe(1, JSON.stringify(descriptor, null, 2))();
1090
+ import_core9.log.groupEnd(1)();
1032
1091
  this.device.pushErrorScope("validation");
1033
1092
  this.handle = this.device.handle.createRenderPipeline(descriptor);
1034
1093
  this.device.popErrorScope((error) => {
@@ -1037,29 +1096,40 @@ var init_webgpu_render_pipeline = __esm({
1037
1096
  this.device.debug();
1038
1097
  });
1039
1098
  }
1099
+ this.descriptor = descriptor;
1040
1100
  this.handle.label = this.props.id;
1041
1101
  this.vs = props.vs;
1042
1102
  this.fs = props.fs;
1043
- this._bindings = { ...this.props.bindings };
1103
+ this._bindingsByGroup = props.bindGroups || (0, import_core9.normalizeBindingsByGroup)(this.shaderLayout, props.bindings);
1104
+ this._bindGroupCacheKeysByGroup = createBindGroupCacheKeys(this._bindingsByGroup);
1044
1105
  }
1045
1106
  destroy() {
1046
1107
  this.handle = null;
1047
1108
  }
1048
1109
  /**
1049
- * @todo Use renderpass.setBindings() ?
1050
- * @todo Do we want to expose BindGroups in the API and remove this?
1110
+ * Compatibility shim for code paths that still set bindings on the pipeline.
1111
+ * The shared-model path passes bindings per draw and does not rely on this state.
1051
1112
  */
1052
1113
  setBindings(bindings) {
1053
- for (const [name, binding] of Object.entries(bindings)) {
1054
- if (this._bindings[name] !== binding) {
1055
- this._bindGroup = null;
1114
+ const nextBindingsByGroup = (0, import_core9.normalizeBindingsByGroup)(this.shaderLayout, bindings);
1115
+ for (const [groupKey, groupBindings] of Object.entries(nextBindingsByGroup)) {
1116
+ const group = Number(groupKey);
1117
+ for (const [name, binding] of Object.entries(groupBindings || {})) {
1118
+ const currentGroupBindings = this._bindingsByGroup[group] || {};
1119
+ if (currentGroupBindings[name] !== binding) {
1120
+ if (!this._bindingsByGroup[group] || this._bindingsByGroup[group] === currentGroupBindings) {
1121
+ this._bindingsByGroup[group] = { ...currentGroupBindings };
1122
+ }
1123
+ this._bindingsByGroup[group][name] = binding;
1124
+ this._bindGroupCacheKeysByGroup[group] = {};
1125
+ }
1056
1126
  }
1057
1127
  }
1058
- Object.assign(this._bindings, bindings);
1059
1128
  }
1060
1129
  /** @todo - should this be moved to renderpass? */
1061
1130
  draw(options) {
1062
1131
  const webgpuRenderPass = options.renderPass;
1132
+ const instanceCount = options.instanceCount && options.instanceCount > 0 ? options.instanceCount : 1;
1063
1133
  this.device.pushErrorScope("validation");
1064
1134
  webgpuRenderPass.handle.setPipeline(this.handle);
1065
1135
  this.device.popErrorScope((error) => {
@@ -1067,32 +1137,27 @@ var init_webgpu_render_pipeline = __esm({
1067
1137
  "${error.message}"`), this)();
1068
1138
  this.device.debug();
1069
1139
  });
1070
- const bindGroup = this._getBindGroup();
1071
- if (bindGroup) {
1072
- webgpuRenderPass.handle.setBindGroup(0, bindGroup);
1140
+ const hasExplicitBindings = Boolean(options.bindGroups || options.bindings);
1141
+ const bindGroups = (0, import_core9._getDefaultBindGroupFactory)(this.device).getBindGroups(this, hasExplicitBindings ? options.bindGroups || options.bindings : this._bindingsByGroup, hasExplicitBindings ? options._bindGroupCacheKeys : this._bindGroupCacheKeysByGroup);
1142
+ for (const [group, bindGroup] of Object.entries(bindGroups)) {
1143
+ if (bindGroup) {
1144
+ webgpuRenderPass.handle.setBindGroup(Number(group), bindGroup);
1145
+ }
1073
1146
  }
1074
1147
  options.vertexArray.bindBeforeRender(options.renderPass);
1075
1148
  if (options.indexCount) {
1076
- webgpuRenderPass.handle.drawIndexed(options.indexCount, options.instanceCount, options.firstIndex, options.baseVertex, options.firstInstance);
1149
+ webgpuRenderPass.handle.drawIndexed(options.indexCount, instanceCount, options.firstIndex || 0, options.baseVertex || 0, options.firstInstance || 0);
1077
1150
  } else {
1078
- webgpuRenderPass.handle.draw(
1079
- options.vertexCount || 0,
1080
- options.instanceCount || 1,
1081
- // If 0, nothing will be drawn
1082
- options.firstInstance
1083
- );
1151
+ webgpuRenderPass.handle.draw(options.vertexCount || 0, instanceCount, options.firstVertex || 0, options.firstInstance || 0);
1084
1152
  }
1085
1153
  options.vertexArray.unbindAfterRender(options.renderPass);
1086
1154
  return true;
1087
1155
  }
1088
- /** Return a bind group created by setBindings */
1089
- _getBindGroup() {
1090
- if (this.shaderLayout.bindings.length === 0) {
1091
- return null;
1092
- }
1093
- this._bindGroupLayout = this._bindGroupLayout || this.handle.getBindGroupLayout(0);
1094
- this._bindGroup = this._bindGroup || getBindGroup(this.device.handle, this._bindGroupLayout, this.shaderLayout, this._bindings);
1095
- return this._bindGroup;
1156
+ _getBindingsByGroupWebGPU() {
1157
+ return this._bindingsByGroup;
1158
+ }
1159
+ _getBindGroupCacheKeysWebGPU() {
1160
+ return this._bindGroupCacheKeysByGroup;
1096
1161
  }
1097
1162
  /**
1098
1163
  * Populate the complex WebGPU GPURenderPipelineDescriptor
@@ -1101,7 +1166,9 @@ var init_webgpu_render_pipeline = __esm({
1101
1166
  const vertex = {
1102
1167
  module: this.props.vs.handle,
1103
1168
  entryPoint: this.props.vertexEntryPoint || "main",
1104
- buffers: getVertexBufferLayout(this.shaderLayout, this.props.bufferLayout)
1169
+ buffers: getVertexBufferLayout(this.shaderLayout, this.props.bufferLayout, {
1170
+ pipelineId: this.id
1171
+ })
1105
1172
  };
1106
1173
  const targets = [];
1107
1174
  if (this.props.colorAttachmentFormats) {
@@ -1141,12 +1208,12 @@ var init_webgpu_render_pipeline = __esm({
1141
1208
  });
1142
1209
 
1143
1210
  // dist/adapter/resources/webgpu-framebuffer.js
1144
- var import_core11, WebGPUFramebuffer;
1211
+ var import_core10, WebGPUFramebuffer;
1145
1212
  var init_webgpu_framebuffer = __esm({
1146
1213
  "dist/adapter/resources/webgpu-framebuffer.js"() {
1147
1214
  "use strict";
1148
- import_core11 = require("@luma.gl/core");
1149
- WebGPUFramebuffer = class extends import_core11.Framebuffer {
1215
+ import_core10 = require("@luma.gl/core");
1216
+ WebGPUFramebuffer = class extends import_core10.Framebuffer {
1150
1217
  device;
1151
1218
  handle = null;
1152
1219
  colorAttachments = [];
@@ -1158,25 +1225,37 @@ var init_webgpu_framebuffer = __esm({
1158
1225
  }
1159
1226
  updateAttachments() {
1160
1227
  }
1228
+ /**
1229
+ * Internal-only hook for the cached CanvasContext/PresentationContext swapchain path.
1230
+ * Rebinds the long-lived default framebuffer wrapper to the current per-frame color view
1231
+ * and optional depth attachment without allocating a new luma.gl Framebuffer object.
1232
+ */
1233
+ _reinitialize(colorAttachment, depthStencilAttachment) {
1234
+ this.colorAttachments[0] = colorAttachment;
1235
+ this.depthStencilAttachment = depthStencilAttachment;
1236
+ this.width = colorAttachment.texture.width;
1237
+ this.height = colorAttachment.texture.height;
1238
+ this.props.width = this.width;
1239
+ this.props.height = this.height;
1240
+ this.props.colorAttachments = [colorAttachment.texture];
1241
+ this.props.depthStencilAttachment = (depthStencilAttachment == null ? void 0 : depthStencilAttachment.texture) || null;
1242
+ }
1161
1243
  };
1162
1244
  }
1163
1245
  });
1164
1246
 
1165
1247
  // dist/adapter/resources/webgpu-compute-pipeline.js
1166
- var import_core12, WebGPUComputePipeline;
1248
+ var import_core11, EMPTY_BIND_GROUPS, WebGPUComputePipeline;
1167
1249
  var init_webgpu_compute_pipeline = __esm({
1168
1250
  "dist/adapter/resources/webgpu-compute-pipeline.js"() {
1169
1251
  "use strict";
1170
- import_core12 = require("@luma.gl/core");
1171
- init_get_bind_group();
1172
- WebGPUComputePipeline = class extends import_core12.ComputePipeline {
1252
+ import_core11 = require("@luma.gl/core");
1253
+ EMPTY_BIND_GROUPS = {};
1254
+ WebGPUComputePipeline = class extends import_core11.ComputePipeline {
1173
1255
  device;
1174
1256
  handle;
1175
- /** For internal use to create BindGroups */
1176
- _bindGroupLayout = null;
1177
- _bindGroup = null;
1178
- /** For internal use to create BindGroups */
1179
- _bindings = {};
1257
+ _bindingsByGroup;
1258
+ _bindGroupCacheKeysByGroup;
1180
1259
  constructor(device, props) {
1181
1260
  super(device, props);
1182
1261
  this.device = device;
@@ -1190,34 +1269,53 @@ var init_webgpu_compute_pipeline = __esm({
1190
1269
  },
1191
1270
  layout: "auto"
1192
1271
  });
1272
+ this._bindingsByGroup = EMPTY_BIND_GROUPS;
1273
+ this._bindGroupCacheKeysByGroup = {};
1193
1274
  }
1194
1275
  /**
1195
1276
  * @todo Use renderpass.setBindings() ?
1196
1277
  * @todo Do we want to expose BindGroups in the API and remove this?
1197
1278
  */
1198
1279
  setBindings(bindings) {
1199
- Object.assign(this._bindings, bindings);
1280
+ const nextBindingsByGroup = (0, import_core11.normalizeBindingsByGroup)(this.shaderLayout, bindings);
1281
+ for (const [groupKey, groupBindings] of Object.entries(nextBindingsByGroup)) {
1282
+ const group = Number(groupKey);
1283
+ for (const [name, binding] of Object.entries(groupBindings || {})) {
1284
+ const currentGroupBindings = this._bindingsByGroup[group] || {};
1285
+ if (currentGroupBindings[name] !== binding) {
1286
+ if (!this._bindingsByGroup[group] || this._bindingsByGroup[group] === currentGroupBindings) {
1287
+ this._bindingsByGroup[group] = { ...currentGroupBindings };
1288
+ }
1289
+ this._bindingsByGroup[group][name] = binding;
1290
+ this._bindGroupCacheKeysByGroup[group] = {};
1291
+ }
1292
+ }
1293
+ }
1200
1294
  }
1201
- /** Return a bind group created by setBindings */
1202
- _getBindGroup() {
1203
- this._bindGroupLayout = this._bindGroupLayout || this.handle.getBindGroupLayout(0);
1204
- this._bindGroup = this._bindGroup || getBindGroup(this.device.handle, this._bindGroupLayout, this.shaderLayout, this._bindings);
1205
- return this._bindGroup;
1295
+ _getBindGroups(bindings, bindGroupCacheKeys) {
1296
+ const hasExplicitBindings = Boolean(bindings);
1297
+ return (0, import_core11._getDefaultBindGroupFactory)(this.device).getBindGroups(this, hasExplicitBindings ? bindings : this._bindingsByGroup, hasExplicitBindings ? bindGroupCacheKeys : this._bindGroupCacheKeysByGroup);
1298
+ }
1299
+ _getBindingsByGroupWebGPU() {
1300
+ return this._bindingsByGroup;
1301
+ }
1302
+ _getBindGroupCacheKeysWebGPU() {
1303
+ return this._bindGroupCacheKeysByGroup;
1206
1304
  }
1207
1305
  };
1208
1306
  }
1209
1307
  });
1210
1308
 
1211
1309
  // dist/adapter/resources/webgpu-vertex-array.js
1212
- var import_core13, import_env, WebGPUVertexArray;
1310
+ var import_core12, import_env, WebGPUVertexArray;
1213
1311
  var init_webgpu_vertex_array = __esm({
1214
1312
  "dist/adapter/resources/webgpu-vertex-array.js"() {
1215
1313
  "use strict";
1216
- import_core13 = require("@luma.gl/core");
1314
+ import_core12 = require("@luma.gl/core");
1217
1315
  import_env = require("@probe.gl/env");
1218
- WebGPUVertexArray = class extends import_core13.VertexArray {
1316
+ WebGPUVertexArray = class extends import_core12.VertexArray {
1219
1317
  get [Symbol.toStringTag]() {
1220
- return "WebGPUVertexArray";
1318
+ return "VertexArray";
1221
1319
  }
1222
1320
  device;
1223
1321
  /** Vertex Array is just a helper class under WebGPU */
@@ -1244,7 +1342,7 @@ var init_webgpu_vertex_array = __esm({
1244
1342
  const webgpuRenderPass = renderPass;
1245
1343
  const webgpuIndexBuffer = this.indexBuffer;
1246
1344
  if (webgpuIndexBuffer == null ? void 0 : webgpuIndexBuffer.handle) {
1247
- import_core13.log.info(3, "setting index buffer", webgpuIndexBuffer == null ? void 0 : webgpuIndexBuffer.handle, webgpuIndexBuffer == null ? void 0 : webgpuIndexBuffer.indexType)();
1345
+ import_core12.log.info(3, "setting index buffer", webgpuIndexBuffer == null ? void 0 : webgpuIndexBuffer.handle, webgpuIndexBuffer == null ? void 0 : webgpuIndexBuffer.indexType)();
1248
1346
  webgpuRenderPass.handle.setIndexBuffer(
1249
1347
  webgpuIndexBuffer == null ? void 0 : webgpuIndexBuffer.handle,
1250
1348
  // @ts-expect-error TODO - we must enforce type
@@ -1254,7 +1352,7 @@ var init_webgpu_vertex_array = __esm({
1254
1352
  for (let location = 0; location < this.maxVertexAttributes; location++) {
1255
1353
  const webgpuBuffer = this.attributes[location];
1256
1354
  if (webgpuBuffer == null ? void 0 : webgpuBuffer.handle) {
1257
- import_core13.log.info(3, `setting vertex buffer ${location}`, webgpuBuffer == null ? void 0 : webgpuBuffer.handle)();
1355
+ import_core12.log.info(3, `setting vertex buffer ${location}`, webgpuBuffer == null ? void 0 : webgpuBuffer.handle)();
1258
1356
  webgpuRenderPass.handle.setVertexBuffer(location, webgpuBuffer == null ? void 0 : webgpuBuffer.handle);
1259
1357
  }
1260
1358
  }
@@ -1274,16 +1372,19 @@ var init_webgpu_vertex_array = __esm({
1274
1372
  });
1275
1373
 
1276
1374
  // dist/adapter/webgpu-canvas-context.js
1277
- var import_core14, WebGPUCanvasContext;
1375
+ var import_core13, WebGPUCanvasContext;
1278
1376
  var init_webgpu_canvas_context = __esm({
1279
1377
  "dist/adapter/webgpu-canvas-context.js"() {
1280
1378
  "use strict";
1281
- import_core14 = require("@luma.gl/core");
1379
+ import_core13 = require("@luma.gl/core");
1282
1380
  init_webgpu_framebuffer();
1283
- WebGPUCanvasContext = class extends import_core14.CanvasContext {
1381
+ init_cpu_hotspot_profiler();
1382
+ WebGPUCanvasContext = class extends import_core13.CanvasContext {
1284
1383
  device;
1285
1384
  handle;
1385
+ colorAttachment = null;
1286
1386
  depthStencilAttachment = null;
1387
+ framebuffer = null;
1287
1388
  get [Symbol.toStringTag]() {
1288
1389
  return "WebGPUCanvasContext";
1289
1390
  }
@@ -1297,9 +1398,22 @@ var init_webgpu_canvas_context = __esm({
1297
1398
  this.handle = context;
1298
1399
  this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
1299
1400
  this._configureDevice();
1401
+ this._startObservers();
1300
1402
  }
1301
1403
  /** Destroy any textures produced while configured and remove the context configuration. */
1302
1404
  destroy() {
1405
+ if (this.framebuffer) {
1406
+ this.framebuffer.destroy();
1407
+ this.framebuffer = null;
1408
+ }
1409
+ if (this.colorAttachment) {
1410
+ this.colorAttachment.destroy();
1411
+ this.colorAttachment = null;
1412
+ }
1413
+ if (this.depthStencilAttachment) {
1414
+ this.depthStencilAttachment.destroy();
1415
+ this.depthStencilAttachment = null;
1416
+ }
1303
1417
  this.handle.unconfigure();
1304
1418
  super.destroy();
1305
1419
  }
@@ -1318,44 +1432,81 @@ var init_webgpu_canvas_context = __esm({
1318
1432
  colorSpace: this.props.colorSpace,
1319
1433
  alphaMode: this.props.alphaMode
1320
1434
  });
1435
+ this._createDepthStencilAttachment(this.device.preferredDepthFormat);
1321
1436
  }
1322
1437
  /** Update framebuffer with properly resized "swap chain" texture views */
1323
1438
  _getCurrentFramebuffer(options = {
1324
1439
  depthStencilFormat: "depth24plus"
1325
1440
  }) {
1326
- const currentColorAttachment = this._getCurrentTexture();
1327
- if (currentColorAttachment.width !== this.drawingBufferWidth || currentColorAttachment.height !== this.drawingBufferHeight) {
1328
- const [oldWidth, oldHeight] = this.getDrawingBufferSize();
1329
- this.drawingBufferWidth = currentColorAttachment.width;
1330
- this.drawingBufferHeight = currentColorAttachment.height;
1331
- import_core14.log.log(1, `${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`)();
1332
- }
1333
- if (options == null ? void 0 : options.depthStencilFormat) {
1334
- this._createDepthStencilAttachment(options == null ? void 0 : options.depthStencilFormat);
1335
- }
1336
- return new WebGPUFramebuffer(this.device, {
1337
- colorAttachments: [currentColorAttachment],
1338
- depthStencilAttachment: this.depthStencilAttachment
1339
- });
1441
+ var _a;
1442
+ const profiler = getCpuHotspotProfiler(this.device);
1443
+ const startTime = profiler ? getTimestamp() : 0;
1444
+ if (profiler) {
1445
+ profiler.framebufferAcquireCount = (profiler.framebufferAcquireCount || 0) + 1;
1446
+ profiler.activeDefaultFramebufferAcquireDepth = (profiler.activeDefaultFramebufferAcquireDepth || 0) + 1;
1447
+ }
1448
+ try {
1449
+ const currentColorAttachment = this._getCurrentTexture();
1450
+ if (currentColorAttachment.width !== this.drawingBufferWidth || currentColorAttachment.height !== this.drawingBufferHeight) {
1451
+ const [oldWidth, oldHeight] = this.getDrawingBufferSize();
1452
+ this.drawingBufferWidth = currentColorAttachment.width;
1453
+ this.drawingBufferHeight = currentColorAttachment.height;
1454
+ import_core13.log.log(1, `${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`)();
1455
+ }
1456
+ if (options == null ? void 0 : options.depthStencilFormat) {
1457
+ this._createDepthStencilAttachment(options == null ? void 0 : options.depthStencilFormat);
1458
+ }
1459
+ this.framebuffer ||= new WebGPUFramebuffer(this.device, {
1460
+ id: `${this.id}#framebuffer`,
1461
+ colorAttachments: [currentColorAttachment],
1462
+ depthStencilAttachment: null
1463
+ });
1464
+ this.framebuffer._reinitialize(currentColorAttachment.view, (options == null ? void 0 : options.depthStencilFormat) ? ((_a = this.depthStencilAttachment) == null ? void 0 : _a.view) || null : null);
1465
+ return this.framebuffer;
1466
+ } finally {
1467
+ if (profiler) {
1468
+ profiler.activeDefaultFramebufferAcquireDepth = (profiler.activeDefaultFramebufferAcquireDepth || 1) - 1;
1469
+ profiler.framebufferAcquireTimeMs = (profiler.framebufferAcquireTimeMs || 0) + (getTimestamp() - startTime);
1470
+ }
1471
+ }
1340
1472
  }
1341
1473
  // PRIMARY METHODS
1342
1474
  /** Wrap the current canvas context texture in a luma.gl texture */
1343
1475
  _getCurrentTexture() {
1476
+ const profiler = getCpuHotspotProfiler(this.device);
1477
+ const currentTextureStartTime = profiler ? getTimestamp() : 0;
1344
1478
  const handle = this.handle.getCurrentTexture();
1345
- return this.device.createTexture({
1346
- id: `${this.id}#color-texture`,
1479
+ if (profiler) {
1480
+ profiler.currentTextureAcquireCount = (profiler.currentTextureAcquireCount || 0) + 1;
1481
+ profiler.currentTextureAcquireTimeMs = (profiler.currentTextureAcquireTimeMs || 0) + (getTimestamp() - currentTextureStartTime);
1482
+ }
1483
+ if (!this.colorAttachment) {
1484
+ this.colorAttachment = this.device.createTexture({
1485
+ id: `${this.id}#color-texture`,
1486
+ handle,
1487
+ format: this.device.preferredColorFormat,
1488
+ width: handle.width,
1489
+ height: handle.height
1490
+ });
1491
+ return this.colorAttachment;
1492
+ }
1493
+ this.colorAttachment._reinitialize(handle, {
1347
1494
  handle,
1348
1495
  format: this.device.preferredColorFormat,
1349
1496
  width: handle.width,
1350
1497
  height: handle.height
1351
1498
  });
1499
+ return this.colorAttachment;
1352
1500
  }
1353
1501
  /** We build render targets on demand (i.e. not when size changes but when about to render) */
1354
1502
  _createDepthStencilAttachment(depthStencilFormat) {
1355
- if (!this.depthStencilAttachment) {
1503
+ var _a;
1504
+ const needsNewDepthStencilAttachment = !this.depthStencilAttachment || this.depthStencilAttachment.width !== this.drawingBufferWidth || this.depthStencilAttachment.height !== this.drawingBufferHeight || this.depthStencilAttachment.format !== depthStencilFormat;
1505
+ if (needsNewDepthStencilAttachment) {
1506
+ (_a = this.depthStencilAttachment) == null ? void 0 : _a.destroy();
1356
1507
  this.depthStencilAttachment = this.device.createTexture({
1357
1508
  id: `${this.id}#depth-stencil-texture`,
1358
- usage: import_core14.Texture.RENDER_ATTACHMENT,
1509
+ usage: import_core13.Texture.RENDER_ATTACHMENT,
1359
1510
  format: depthStencilFormat,
1360
1511
  width: this.drawingBufferWidth,
1361
1512
  height: this.drawingBufferHeight
@@ -1367,76 +1518,246 @@ var init_webgpu_canvas_context = __esm({
1367
1518
  }
1368
1519
  });
1369
1520
 
1370
- // dist/adapter/resources/webgpu-command-buffer.js
1371
- var import_core15, WebGPUCommandBuffer;
1372
- var init_webgpu_command_buffer = __esm({
1373
- "dist/adapter/resources/webgpu-command-buffer.js"() {
1521
+ // dist/adapter/webgpu-presentation-context.js
1522
+ var import_core14, WebGPUPresentationContext;
1523
+ var init_webgpu_presentation_context = __esm({
1524
+ "dist/adapter/webgpu-presentation-context.js"() {
1374
1525
  "use strict";
1375
- import_core15 = require("@luma.gl/core");
1376
- WebGPUCommandBuffer = class extends import_core15.CommandBuffer {
1526
+ import_core14 = require("@luma.gl/core");
1527
+ init_webgpu_framebuffer();
1528
+ init_cpu_hotspot_profiler();
1529
+ WebGPUPresentationContext = class extends import_core14.PresentationContext {
1377
1530
  device;
1378
1531
  handle;
1379
- constructor(commandEncoder, props) {
1380
- super(commandEncoder.device, {});
1381
- this.device = commandEncoder.device;
1382
- this.handle = this.props.handle || commandEncoder.handle.finish({
1383
- label: (props == null ? void 0 : props.id) || "unnamed-command-buffer"
1384
- });
1532
+ colorAttachment = null;
1533
+ depthStencilAttachment = null;
1534
+ framebuffer = null;
1535
+ get [Symbol.toStringTag]() {
1536
+ return "WebGPUPresentationContext";
1385
1537
  }
1386
- };
1387
- }
1388
- });
1389
-
1390
- // dist/adapter/resources/webgpu-render-pass.js
1391
- function convertColor(color) {
1392
- return { r: color[0], g: color[1], b: color[2], a: color[3] };
1393
- }
1394
- var import_core16, WebGPURenderPass;
1395
- var init_webgpu_render_pass = __esm({
1396
- "dist/adapter/resources/webgpu-render-pass.js"() {
1397
- "use strict";
1398
- import_core16 = require("@luma.gl/core");
1399
- WebGPURenderPass = class extends import_core16.RenderPass {
1400
- device;
1401
- handle;
1402
- /** Active pipeline */
1403
- pipeline = null;
1404
1538
  constructor(device, props = {}) {
1405
- super(device, props);
1539
+ super(props);
1540
+ const contextLabel = `${this[Symbol.toStringTag]}(${this.id})`;
1541
+ const context = this.canvas.getContext("webgpu");
1542
+ if (!context) {
1543
+ throw new Error(`${contextLabel}: Failed to create WebGPU presentation context`);
1544
+ }
1406
1545
  this.device = device;
1407
- const framebuffer = props.framebuffer || device.getCanvasContext().getCurrentFramebuffer();
1408
- const renderPassDescriptor = this.getRenderPassDescriptor(framebuffer);
1409
- const webgpuQuerySet = props.timestampQuerySet;
1410
- if (webgpuQuerySet) {
1411
- renderPassDescriptor.occlusionQuerySet = webgpuQuerySet.handle;
1412
- }
1413
- if (device.features.has("timestamp-query")) {
1414
- const webgpuTSQuerySet = props.timestampQuerySet;
1415
- renderPassDescriptor.timestampWrites = webgpuTSQuerySet ? {
1416
- querySet: webgpuTSQuerySet.handle,
1417
- beginningOfPassWriteIndex: props.beginTimestampIndex,
1418
- endOfPassWriteIndex: props.endTimestampIndex
1419
- } : void 0;
1420
- }
1421
- if (!device.commandEncoder) {
1422
- throw new Error("commandEncoder not available");
1546
+ this.handle = context;
1547
+ this._setAutoCreatedCanvasId(`${this.device.id}-presentation-canvas`);
1548
+ this._configureDevice();
1549
+ this._startObservers();
1550
+ }
1551
+ destroy() {
1552
+ if (this.framebuffer) {
1553
+ this.framebuffer.destroy();
1554
+ this.framebuffer = null;
1423
1555
  }
1424
- this.device.pushErrorScope("validation");
1425
- this.handle = this.props.handle || device.commandEncoder.handle.beginRenderPass(renderPassDescriptor);
1426
- this.device.popErrorScope((error) => {
1427
- this.device.reportError(new Error(`${this} creation failed:
1428
- "${error.message}"`), this)();
1429
- this.device.debug();
1556
+ if (this.colorAttachment) {
1557
+ this.colorAttachment.destroy();
1558
+ this.colorAttachment = null;
1559
+ }
1560
+ if (this.depthStencilAttachment) {
1561
+ this.depthStencilAttachment.destroy();
1562
+ this.depthStencilAttachment = null;
1563
+ }
1564
+ this.handle.unconfigure();
1565
+ super.destroy();
1566
+ }
1567
+ present() {
1568
+ this.device.submit();
1569
+ }
1570
+ _configureDevice() {
1571
+ if (this.depthStencilAttachment) {
1572
+ this.depthStencilAttachment.destroy();
1573
+ this.depthStencilAttachment = null;
1574
+ }
1575
+ this.handle.configure({
1576
+ device: this.device.handle,
1577
+ format: this.device.preferredColorFormat,
1578
+ colorSpace: this.props.colorSpace,
1579
+ alphaMode: this.props.alphaMode
1580
+ });
1581
+ this._createDepthStencilAttachment(this.device.preferredDepthFormat);
1582
+ }
1583
+ _getCurrentFramebuffer(options = {
1584
+ depthStencilFormat: "depth24plus"
1585
+ }) {
1586
+ var _a;
1587
+ const profiler = getCpuHotspotProfiler(this.device);
1588
+ const startTime = profiler ? getTimestamp() : 0;
1589
+ if (profiler) {
1590
+ profiler.framebufferAcquireCount = (profiler.framebufferAcquireCount || 0) + 1;
1591
+ }
1592
+ try {
1593
+ const currentColorAttachment = this._getCurrentTexture();
1594
+ if (currentColorAttachment.width !== this.drawingBufferWidth || currentColorAttachment.height !== this.drawingBufferHeight) {
1595
+ const [oldWidth, oldHeight] = this.getDrawingBufferSize();
1596
+ this.drawingBufferWidth = currentColorAttachment.width;
1597
+ this.drawingBufferHeight = currentColorAttachment.height;
1598
+ import_core14.log.log(1, `${this[Symbol.toStringTag]}(${this.id}): Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`)();
1599
+ }
1600
+ if (options == null ? void 0 : options.depthStencilFormat) {
1601
+ this._createDepthStencilAttachment(options.depthStencilFormat);
1602
+ }
1603
+ this.framebuffer ||= new WebGPUFramebuffer(this.device, {
1604
+ id: `${this.id}#framebuffer`,
1605
+ colorAttachments: [currentColorAttachment],
1606
+ depthStencilAttachment: null
1607
+ });
1608
+ this.framebuffer._reinitialize(currentColorAttachment.view, (options == null ? void 0 : options.depthStencilFormat) ? ((_a = this.depthStencilAttachment) == null ? void 0 : _a.view) || null : null);
1609
+ return this.framebuffer;
1610
+ } finally {
1611
+ if (profiler) {
1612
+ profiler.framebufferAcquireTimeMs = (profiler.framebufferAcquireTimeMs || 0) + (getTimestamp() - startTime);
1613
+ }
1614
+ }
1615
+ }
1616
+ _getCurrentTexture() {
1617
+ const profiler = getCpuHotspotProfiler(this.device);
1618
+ const currentTextureStartTime = profiler ? getTimestamp() : 0;
1619
+ const handle = this.handle.getCurrentTexture();
1620
+ if (profiler) {
1621
+ profiler.currentTextureAcquireCount = (profiler.currentTextureAcquireCount || 0) + 1;
1622
+ profiler.currentTextureAcquireTimeMs = (profiler.currentTextureAcquireTimeMs || 0) + (getTimestamp() - currentTextureStartTime);
1623
+ }
1624
+ if (!this.colorAttachment) {
1625
+ this.colorAttachment = this.device.createTexture({
1626
+ id: `${this.id}#color-texture`,
1627
+ handle,
1628
+ format: this.device.preferredColorFormat,
1629
+ width: handle.width,
1630
+ height: handle.height
1631
+ });
1632
+ return this.colorAttachment;
1633
+ }
1634
+ this.colorAttachment._reinitialize(handle, {
1635
+ handle,
1636
+ format: this.device.preferredColorFormat,
1637
+ width: handle.width,
1638
+ height: handle.height
1639
+ });
1640
+ return this.colorAttachment;
1641
+ }
1642
+ _createDepthStencilAttachment(depthStencilFormat) {
1643
+ var _a;
1644
+ const needsNewDepthStencilAttachment = !this.depthStencilAttachment || this.depthStencilAttachment.width !== this.drawingBufferWidth || this.depthStencilAttachment.height !== this.drawingBufferHeight || this.depthStencilAttachment.format !== depthStencilFormat;
1645
+ if (needsNewDepthStencilAttachment) {
1646
+ (_a = this.depthStencilAttachment) == null ? void 0 : _a.destroy();
1647
+ this.depthStencilAttachment = this.device.createTexture({
1648
+ id: `${this.id}#depth-stencil-texture`,
1649
+ usage: import_core14.Texture.RENDER_ATTACHMENT,
1650
+ format: depthStencilFormat,
1651
+ width: this.drawingBufferWidth,
1652
+ height: this.drawingBufferHeight
1653
+ });
1654
+ }
1655
+ return this.depthStencilAttachment;
1656
+ }
1657
+ };
1658
+ }
1659
+ });
1660
+
1661
+ // dist/adapter/resources/webgpu-command-buffer.js
1662
+ var import_core15, WebGPUCommandBuffer;
1663
+ var init_webgpu_command_buffer = __esm({
1664
+ "dist/adapter/resources/webgpu-command-buffer.js"() {
1665
+ "use strict";
1666
+ import_core15 = require("@luma.gl/core");
1667
+ WebGPUCommandBuffer = class extends import_core15.CommandBuffer {
1668
+ device;
1669
+ handle;
1670
+ constructor(commandEncoder, props) {
1671
+ super(commandEncoder.device, props);
1672
+ this.device = commandEncoder.device;
1673
+ this.handle = this.props.handle || commandEncoder.handle.finish({
1674
+ label: (props == null ? void 0 : props.id) || "unnamed-command-buffer"
1430
1675
  });
1431
- this.handle.label = this.props.id;
1432
- import_core16.log.groupCollapsed(3, `new WebGPURenderPass(${this.id})`)();
1433
- import_core16.log.probe(3, JSON.stringify(renderPassDescriptor, null, 2))();
1434
- import_core16.log.groupEnd(3)();
1676
+ }
1677
+ };
1678
+ }
1679
+ });
1680
+
1681
+ // dist/adapter/resources/webgpu-render-pass.js
1682
+ function convertColor(color) {
1683
+ return { r: color[0], g: color[1], b: color[2], a: color[3] };
1684
+ }
1685
+ var import_core16, WebGPURenderPass;
1686
+ var init_webgpu_render_pass = __esm({
1687
+ "dist/adapter/resources/webgpu-render-pass.js"() {
1688
+ "use strict";
1689
+ import_core16 = require("@luma.gl/core");
1690
+ init_cpu_hotspot_profiler();
1691
+ WebGPURenderPass = class extends import_core16.RenderPass {
1692
+ device;
1693
+ handle;
1694
+ framebuffer;
1695
+ /** Active pipeline */
1696
+ pipeline = null;
1697
+ /** Latest bindings applied to this pass */
1698
+ bindings = {};
1699
+ constructor(device, props = {}, commandEncoder = device.commandEncoder.handle) {
1700
+ super(device, props);
1701
+ this.device = device;
1702
+ const { props: renderPassProps } = this;
1703
+ this.framebuffer = renderPassProps.framebuffer || device.getCanvasContext().getCurrentFramebuffer();
1704
+ const profiler = getCpuHotspotProfiler(this.device);
1705
+ if (profiler) {
1706
+ const counterName = renderPassProps.framebuffer ? "explicitFramebufferRenderPassCount" : "defaultFramebufferRenderPassCount";
1707
+ profiler[counterName] = (profiler[counterName] || 0) + 1;
1708
+ }
1709
+ const startTime = profiler ? getTimestamp() : 0;
1710
+ try {
1711
+ const descriptorAssemblyStartTime = profiler ? getTimestamp() : 0;
1712
+ const renderPassDescriptor = this.getRenderPassDescriptor(this.framebuffer);
1713
+ if (renderPassProps.occlusionQuerySet) {
1714
+ renderPassDescriptor.occlusionQuerySet = renderPassProps.occlusionQuerySet.handle;
1715
+ }
1716
+ if (renderPassProps.timestampQuerySet) {
1717
+ const webgpuTSQuerySet = renderPassProps.timestampQuerySet;
1718
+ webgpuTSQuerySet == null ? void 0 : webgpuTSQuerySet._invalidateResults();
1719
+ renderPassDescriptor.timestampWrites = webgpuTSQuerySet ? {
1720
+ querySet: webgpuTSQuerySet.handle,
1721
+ beginningOfPassWriteIndex: renderPassProps.beginTimestampIndex,
1722
+ endOfPassWriteIndex: renderPassProps.endTimestampIndex
1723
+ } : void 0;
1724
+ }
1725
+ if (profiler) {
1726
+ profiler.renderPassDescriptorAssemblyCount = (profiler.renderPassDescriptorAssemblyCount || 0) + 1;
1727
+ profiler.renderPassDescriptorAssemblyTimeMs = (profiler.renderPassDescriptorAssemblyTimeMs || 0) + (getTimestamp() - descriptorAssemblyStartTime);
1728
+ }
1729
+ this.device.pushErrorScope("validation");
1730
+ const beginRenderPassStartTime = profiler ? getTimestamp() : 0;
1731
+ this.handle = this.props.handle || commandEncoder.beginRenderPass(renderPassDescriptor);
1732
+ if (profiler) {
1733
+ profiler.renderPassBeginCount = (profiler.renderPassBeginCount || 0) + 1;
1734
+ profiler.renderPassBeginTimeMs = (profiler.renderPassBeginTimeMs || 0) + (getTimestamp() - beginRenderPassStartTime);
1735
+ }
1736
+ this.device.popErrorScope((error) => {
1737
+ this.device.reportError(new Error(`${this} creation failed:
1738
+ "${error.message}"`), this)();
1739
+ this.device.debug();
1740
+ });
1741
+ this.handle.label = this.props.id;
1742
+ import_core16.log.groupCollapsed(3, `new WebGPURenderPass(${this.id})`)();
1743
+ import_core16.log.probe(3, JSON.stringify(renderPassDescriptor, null, 2))();
1744
+ import_core16.log.groupEnd(3)();
1745
+ } finally {
1746
+ if (profiler) {
1747
+ profiler.renderPassSetupCount = (profiler.renderPassSetupCount || 0) + 1;
1748
+ profiler.renderPassSetupTimeMs = (profiler.renderPassSetupTimeMs || 0) + (getTimestamp() - startTime);
1749
+ }
1750
+ }
1435
1751
  }
1436
1752
  destroy() {
1753
+ this.destroyResource();
1437
1754
  }
1438
1755
  end() {
1756
+ if (this.destroyed) {
1757
+ return;
1758
+ }
1439
1759
  this.handle.end();
1760
+ this.destroy();
1440
1761
  }
1441
1762
  setPipeline(pipeline) {
1442
1763
  this.pipeline = pipeline;
@@ -1450,11 +1771,12 @@ var init_webgpu_render_pass = __esm({
1450
1771
  }
1451
1772
  /** Sets an array of bindings (uniform buffers, samplers, textures, ...) */
1452
1773
  setBindings(bindings) {
1453
- var _a, _b;
1454
- (_a = this.pipeline) == null ? void 0 : _a.setBindings(bindings);
1455
- const bindGroup = (_b = this.pipeline) == null ? void 0 : _b._getBindGroup();
1456
- if (bindGroup) {
1457
- this.handle.setBindGroup(0, bindGroup);
1774
+ this.bindings = bindings;
1775
+ const bindGroups = this.pipeline && (0, import_core16._getDefaultBindGroupFactory)(this.device).getBindGroups(this.pipeline, bindings) || {};
1776
+ for (const [group, bindGroup] of Object.entries(bindGroups)) {
1777
+ if (bindGroup) {
1778
+ this.handle.setBindGroup(Number(group), bindGroup);
1779
+ }
1458
1780
  }
1459
1781
  }
1460
1782
  setIndexBuffer(buffer, indexFormat, offset = 0, size) {
@@ -1561,44 +1883,60 @@ var init_webgpu_compute_pass = __esm({
1561
1883
  device;
1562
1884
  handle;
1563
1885
  _webgpuPipeline = null;
1564
- constructor(device, props) {
1886
+ constructor(device, props = {}, commandEncoder = device.commandEncoder.handle) {
1565
1887
  super(device, props);
1566
1888
  this.device = device;
1889
+ const { props: computePassProps } = this;
1567
1890
  let timestampWrites;
1568
- if (device.features.has("timestamp-query")) {
1569
- const webgpuQuerySet = props.timestampQuerySet;
1891
+ if (computePassProps.timestampQuerySet) {
1892
+ const webgpuQuerySet = computePassProps.timestampQuerySet;
1570
1893
  if (webgpuQuerySet) {
1894
+ webgpuQuerySet._invalidateResults();
1571
1895
  timestampWrites = {
1572
1896
  querySet: webgpuQuerySet.handle,
1573
- beginningOfPassWriteIndex: props.beginTimestampIndex,
1574
- endOfPassWriteIndex: props.endTimestampIndex
1897
+ beginningOfPassWriteIndex: computePassProps.beginTimestampIndex,
1898
+ endOfPassWriteIndex: computePassProps.endTimestampIndex
1575
1899
  };
1576
1900
  }
1577
1901
  }
1578
- this.handle = this.props.handle || device.commandEncoder.handle.beginComputePass({
1902
+ this.handle = this.props.handle || commandEncoder.beginComputePass({
1579
1903
  label: this.props.id,
1580
1904
  timestampWrites
1581
1905
  });
1582
1906
  }
1583
1907
  /** @note no WebGPU destroy method, just gc */
1584
1908
  destroy() {
1909
+ this.destroyResource();
1585
1910
  }
1586
1911
  end() {
1912
+ if (this.destroyed) {
1913
+ return;
1914
+ }
1587
1915
  this.handle.end();
1916
+ this.destroy();
1588
1917
  }
1589
1918
  setPipeline(pipeline) {
1590
1919
  const wgpuPipeline = pipeline;
1591
1920
  this.handle.setPipeline(wgpuPipeline.handle);
1592
1921
  this._webgpuPipeline = wgpuPipeline;
1593
- this.setBindings([]);
1922
+ const bindGroups = (0, import_core17._getDefaultBindGroupFactory)(this.device).getBindGroups(this._webgpuPipeline, this._webgpuPipeline._getBindingsByGroupWebGPU(), this._webgpuPipeline._getBindGroupCacheKeysWebGPU());
1923
+ for (const [group, bindGroup] of Object.entries(bindGroups)) {
1924
+ if (bindGroup) {
1925
+ this.handle.setBindGroup(Number(group), bindGroup);
1926
+ }
1927
+ }
1594
1928
  }
1595
1929
  /**
1596
1930
  * Sets an array of bindings (uniform buffers, samplers, textures, ...)
1597
1931
  * TODO - still some API confusion - does this method go here or on the pipeline?
1598
1932
  */
1599
1933
  setBindings(bindings) {
1600
- const bindGroup = this._webgpuPipeline._getBindGroup();
1601
- this.handle.setBindGroup(0, bindGroup);
1934
+ const bindGroups = this._webgpuPipeline && (0, import_core17._getDefaultBindGroupFactory)(this.device).getBindGroups(this._webgpuPipeline, bindings) || {};
1935
+ for (const [group, bindGroup] of Object.entries(bindGroups)) {
1936
+ if (bindGroup) {
1937
+ this.handle.setBindGroup(Number(group), bindGroup);
1938
+ }
1939
+ }
1602
1940
  }
1603
1941
  /**
1604
1942
  * Dispatch work to be performed with the current ComputePipeline.
@@ -1656,6 +1994,7 @@ var init_webgpu_command_encoder = __esm({
1656
1994
  this.handle.label = this.props.id;
1657
1995
  }
1658
1996
  destroy() {
1997
+ this.destroyResource();
1659
1998
  }
1660
1999
  finish(props) {
1661
2000
  this.device.pushErrorScope("validation");
@@ -1667,49 +2006,105 @@ var init_webgpu_command_encoder = __esm({
1667
2006
  this.device.reportError(new Error(message), this)();
1668
2007
  this.device.debug();
1669
2008
  });
2009
+ this.destroy();
1670
2010
  return commandBuffer;
1671
2011
  }
1672
2012
  /**
1673
2013
  * Allows a render pass to begin against a canvas context
1674
2014
  * @todo need to support a "Framebuffer" equivalent (aka preconfigured RenderPassDescriptors?).
1675
2015
  */
1676
- beginRenderPass(props) {
1677
- return new WebGPURenderPass(this.device, props);
2016
+ beginRenderPass(props = {}) {
2017
+ return new WebGPURenderPass(this.device, this._applyTimeProfilingToPassProps(props), this.handle);
1678
2018
  }
1679
- beginComputePass(props) {
1680
- return new WebGPUComputePass(this.device, props);
2019
+ beginComputePass(props = {}) {
2020
+ return new WebGPUComputePass(this.device, this._applyTimeProfilingToPassProps(props), this.handle);
1681
2021
  }
1682
2022
  // beginRenderPass(GPURenderPassDescriptor descriptor): GPURenderPassEncoder;
1683
2023
  // beginComputePass(optional GPUComputePassDescriptor descriptor = {}): GPUComputePassEncoder;
1684
2024
  copyBufferToBuffer(options) {
1685
2025
  const webgpuSourceBuffer = options.sourceBuffer;
1686
- const WebGPUDestinationBuffer = options.destinationBuffer;
1687
- this.handle.copyBufferToBuffer(webgpuSourceBuffer.handle, options.sourceOffset ?? 0, WebGPUDestinationBuffer.handle, options.destinationOffset ?? 0, options.size ?? 0);
2026
+ const webgpuDestinationBuffer = options.destinationBuffer;
2027
+ this.handle.copyBufferToBuffer(webgpuSourceBuffer.handle, options.sourceOffset ?? 0, webgpuDestinationBuffer.handle, options.destinationOffset ?? 0, options.size ?? 0);
1688
2028
  }
1689
2029
  copyBufferToTexture(options) {
1690
- var _a, _b, _c;
1691
2030
  const webgpuSourceBuffer = options.sourceBuffer;
1692
- const WebGPUDestinationTexture = options.destinationTexture;
2031
+ const webgpuDestinationTexture = options.destinationTexture;
2032
+ const copyOrigin = options.origin ?? [0, 0, 0];
2033
+ const copySize = options.size;
1693
2034
  this.handle.copyBufferToTexture({
1694
2035
  buffer: webgpuSourceBuffer.handle,
1695
- offset: options.offset ?? 0,
2036
+ offset: options.byteOffset ?? 0,
1696
2037
  bytesPerRow: options.bytesPerRow,
1697
2038
  rowsPerImage: options.rowsPerImage
1698
2039
  }, {
1699
- texture: WebGPUDestinationTexture.handle,
2040
+ texture: webgpuDestinationTexture.handle,
1700
2041
  mipLevel: options.mipLevel ?? 0,
1701
- origin: options.origin ?? {}
1702
- // aspect: options.aspect
2042
+ origin: {
2043
+ x: copyOrigin[0] ?? 0,
2044
+ y: copyOrigin[1] ?? 0,
2045
+ z: copyOrigin[2] ?? 0
2046
+ },
2047
+ aspect: options.aspect
1703
2048
  }, {
1704
- // @ts-ignore
1705
- width: (_a = options.extent) == null ? void 0 : _a[0],
1706
- height: (_b = options.extent) == null ? void 0 : _b[1],
1707
- depthOrArrayLayers: (_c = options.extent) == null ? void 0 : _c[2]
2049
+ width: copySize[0],
2050
+ height: copySize[1],
2051
+ depthOrArrayLayers: copySize[2]
1708
2052
  });
1709
2053
  }
1710
2054
  copyTextureToBuffer(options) {
2055
+ const { sourceTexture, destinationBuffer, origin = [0, 0, 0], byteOffset = 0, width, height, depthOrArrayLayers, mipLevel, aspect } = options;
2056
+ const webgpuSourceTexture = sourceTexture;
2057
+ webgpuSourceTexture.copyToBuffer(this.handle, {
2058
+ x: origin[0] ?? 0,
2059
+ y: origin[1] ?? 0,
2060
+ z: origin[2] ?? 0,
2061
+ width,
2062
+ height,
2063
+ depthOrArrayLayers,
2064
+ mipLevel,
2065
+ aspect,
2066
+ byteOffset,
2067
+ bytesPerRow: options.bytesPerRow,
2068
+ rowsPerImage: options.rowsPerImage
2069
+ }, destinationBuffer);
1711
2070
  }
1712
2071
  copyTextureToTexture(options) {
2072
+ var _a, _b, _c, _d, _e, _f;
2073
+ const webgpuSourceTexture = options.sourceTexture;
2074
+ const webgpuDestinationTexture = options.destinationTexture;
2075
+ const sourceRegion = webgpuSourceTexture._normalizeTextureReadOptions({
2076
+ x: ((_a = options.origin) == null ? void 0 : _a[0]) ?? 0,
2077
+ y: ((_b = options.origin) == null ? void 0 : _b[1]) ?? 0,
2078
+ z: ((_c = options.origin) == null ? void 0 : _c[2]) ?? 0,
2079
+ width: options.width,
2080
+ height: options.height,
2081
+ depthOrArrayLayers: options.depthOrArrayLayers,
2082
+ mipLevel: options.mipLevel ?? 0,
2083
+ aspect: options.aspect ?? "all"
2084
+ });
2085
+ this.handle.copyTextureToTexture({
2086
+ texture: webgpuSourceTexture.handle,
2087
+ mipLevel: sourceRegion.mipLevel,
2088
+ origin: {
2089
+ x: sourceRegion.x,
2090
+ y: sourceRegion.y,
2091
+ z: sourceRegion.z
2092
+ },
2093
+ aspect: sourceRegion.aspect
2094
+ }, {
2095
+ texture: webgpuDestinationTexture.handle,
2096
+ mipLevel: options.destinationMipLevel ?? 0,
2097
+ origin: {
2098
+ x: ((_d = options.destinationOrigin) == null ? void 0 : _d[0]) ?? 0,
2099
+ y: ((_e = options.destinationOrigin) == null ? void 0 : _e[1]) ?? 0,
2100
+ z: ((_f = options.destinationOrigin) == null ? void 0 : _f[2]) ?? 0
2101
+ },
2102
+ aspect: options.destinationAspect ?? sourceRegion.aspect
2103
+ }, {
2104
+ width: sourceRegion.width,
2105
+ height: sourceRegion.height,
2106
+ depthOrArrayLayers: sourceRegion.depthOrArrayLayers
2107
+ });
1713
2108
  }
1714
2109
  pushDebugGroup(groupLabel) {
1715
2110
  this.handle.pushDebugGroup(groupLabel);
@@ -1725,6 +2120,21 @@ var init_webgpu_command_encoder = __esm({
1725
2120
  const webgpuBuffer = destination;
1726
2121
  this.handle.resolveQuerySet(webgpuQuerySet.handle, (options == null ? void 0 : options.firstQuery) || 0, (options == null ? void 0 : options.queryCount) || querySet.props.count - ((options == null ? void 0 : options.firstQuery) || 0), webgpuBuffer.handle, (options == null ? void 0 : options.destinationOffset) || 0);
1727
2122
  }
2123
+ writeTimestamp(querySet, queryIndex) {
2124
+ querySet._invalidateResults();
2125
+ const writeTimestamp = this.handle.writeTimestamp;
2126
+ if (writeTimestamp) {
2127
+ writeTimestamp.call(this.handle, querySet.handle, queryIndex);
2128
+ return;
2129
+ }
2130
+ const computePass = this.handle.beginComputePass({
2131
+ timestampWrites: {
2132
+ querySet: querySet.handle,
2133
+ beginningOfPassWriteIndex: queryIndex
2134
+ }
2135
+ });
2136
+ computePass.end();
2137
+ }
1728
2138
  };
1729
2139
  }
1730
2140
  });
@@ -1735,9 +2145,15 @@ var init_webgpu_query_set = __esm({
1735
2145
  "dist/adapter/resources/webgpu-query-set.js"() {
1736
2146
  "use strict";
1737
2147
  import_core19 = require("@luma.gl/core");
2148
+ init_cpu_hotspot_profiler();
1738
2149
  WebGPUQuerySet = class extends import_core19.QuerySet {
1739
2150
  device;
1740
2151
  handle;
2152
+ _resolveBuffer = null;
2153
+ _readBuffer = null;
2154
+ _cachedResults = null;
2155
+ _readResultsPromise = null;
2156
+ _resultsPendingResolution = false;
1741
2157
  constructor(device, props) {
1742
2158
  super(device, props);
1743
2159
  this.device = device;
@@ -1749,8 +2165,133 @@ var init_webgpu_query_set = __esm({
1749
2165
  }
1750
2166
  destroy() {
1751
2167
  var _a;
1752
- (_a = this.handle) == null ? void 0 : _a.destroy();
1753
- this.handle = null;
2168
+ if (!this.destroyed) {
2169
+ (_a = this.handle) == null ? void 0 : _a.destroy();
2170
+ this.destroyResource();
2171
+ this.handle = null;
2172
+ }
2173
+ }
2174
+ isResultAvailable(queryIndex) {
2175
+ if (!this._cachedResults) {
2176
+ return false;
2177
+ }
2178
+ return queryIndex === void 0 ? true : queryIndex >= 0 && queryIndex < this._cachedResults.length;
2179
+ }
2180
+ async readResults(options) {
2181
+ const firstQuery = (options == null ? void 0 : options.firstQuery) || 0;
2182
+ const queryCount = (options == null ? void 0 : options.queryCount) || this.props.count - firstQuery;
2183
+ if (firstQuery < 0 || queryCount < 0 || firstQuery + queryCount > this.props.count) {
2184
+ throw new Error("Query read range is out of bounds");
2185
+ }
2186
+ let needsFreshResults = true;
2187
+ while (needsFreshResults) {
2188
+ if (!this._readResultsPromise) {
2189
+ this._readResultsPromise = this._readAllResults();
2190
+ }
2191
+ const readResultsPromise = this._readResultsPromise;
2192
+ const results = await readResultsPromise;
2193
+ needsFreshResults = this._resultsPendingResolution;
2194
+ if (!needsFreshResults) {
2195
+ return results.slice(firstQuery, firstQuery + queryCount);
2196
+ }
2197
+ }
2198
+ throw new Error("Query read unexpectedly failed to resolve");
2199
+ }
2200
+ async readTimestampDuration(beginIndex, endIndex) {
2201
+ if (this.props.type !== "timestamp") {
2202
+ throw new Error("Timestamp durations require a timestamp QuerySet");
2203
+ }
2204
+ if (beginIndex < 0 || endIndex <= beginIndex || endIndex >= this.props.count) {
2205
+ throw new Error("Timestamp duration range is out of bounds");
2206
+ }
2207
+ const results = await this.readResults({
2208
+ firstQuery: beginIndex,
2209
+ queryCount: endIndex - beginIndex + 1
2210
+ });
2211
+ return Number(results[results.length - 1] - results[0]) / 1e6;
2212
+ }
2213
+ /** Marks any cached query results as stale after new writes have been encoded. */
2214
+ _invalidateResults() {
2215
+ this._cachedResults = null;
2216
+ this._resultsPendingResolution = true;
2217
+ }
2218
+ async _readAllResults() {
2219
+ this._ensureBuffers();
2220
+ try {
2221
+ if (this._resultsPendingResolution) {
2222
+ const commandEncoder = this.device.createCommandEncoder({
2223
+ id: `${this.id}-read-results`
2224
+ });
2225
+ commandEncoder.resolveQuerySet(this, this._resolveBuffer);
2226
+ commandEncoder.copyBufferToBuffer({
2227
+ sourceBuffer: this._resolveBuffer,
2228
+ destinationBuffer: this._readBuffer,
2229
+ size: this._resolveBuffer.byteLength
2230
+ });
2231
+ const commandBuffer = commandEncoder.finish({
2232
+ id: `${this.id}-read-results-command-buffer`
2233
+ });
2234
+ const previousSubmitReason = getCpuHotspotSubmitReason(this.device) || void 0;
2235
+ setCpuHotspotSubmitReason(this.device, "query-readback");
2236
+ try {
2237
+ this.device.submit(commandBuffer);
2238
+ } finally {
2239
+ setCpuHotspotSubmitReason(this.device, previousSubmitReason);
2240
+ }
2241
+ }
2242
+ const data = await this._readBuffer.readAsync(0, this._readBuffer.byteLength);
2243
+ const resultView = new BigUint64Array(data.buffer, data.byteOffset, this.props.count);
2244
+ this._cachedResults = Array.from(resultView, (value) => value);
2245
+ this._resultsPendingResolution = false;
2246
+ return this._cachedResults;
2247
+ } finally {
2248
+ this._readResultsPromise = null;
2249
+ }
2250
+ }
2251
+ _ensureBuffers() {
2252
+ if (this._resolveBuffer && this._readBuffer) {
2253
+ return;
2254
+ }
2255
+ const byteLength = this.props.count * 8;
2256
+ this._resolveBuffer = this.device.createBuffer({
2257
+ id: `${this.id}-resolve-buffer`,
2258
+ usage: import_core19.Buffer.QUERY_RESOLVE | import_core19.Buffer.COPY_SRC,
2259
+ byteLength
2260
+ });
2261
+ this.attachResource(this._resolveBuffer);
2262
+ this._readBuffer = this.device.createBuffer({
2263
+ id: `${this.id}-read-buffer`,
2264
+ usage: import_core19.Buffer.COPY_DST | import_core19.Buffer.MAP_READ,
2265
+ byteLength
2266
+ });
2267
+ this.attachResource(this._readBuffer);
2268
+ }
2269
+ _encodeResolveToReadBuffer(commandEncoder, options) {
2270
+ if (!this._resultsPendingResolution) {
2271
+ return false;
2272
+ }
2273
+ if (this._readResultsPromise) {
2274
+ return false;
2275
+ }
2276
+ this._ensureBuffers();
2277
+ const firstQuery = (options == null ? void 0 : options.firstQuery) || 0;
2278
+ const queryCount = (options == null ? void 0 : options.queryCount) || this.props.count - firstQuery;
2279
+ const byteLength = queryCount * BigUint64Array.BYTES_PER_ELEMENT;
2280
+ const byteOffset = firstQuery * BigUint64Array.BYTES_PER_ELEMENT;
2281
+ commandEncoder.resolveQuerySet(this, this._resolveBuffer, {
2282
+ firstQuery,
2283
+ queryCount,
2284
+ destinationOffset: byteOffset
2285
+ });
2286
+ commandEncoder.copyBufferToBuffer({
2287
+ sourceBuffer: this._resolveBuffer,
2288
+ sourceOffset: byteOffset,
2289
+ destinationBuffer: this._readBuffer,
2290
+ destinationOffset: byteOffset,
2291
+ size: byteLength
2292
+ });
2293
+ this._resultsPendingResolution = false;
2294
+ return true;
1754
2295
  }
1755
2296
  };
1756
2297
  }
@@ -1768,25 +2309,21 @@ var init_webgpu_pipeline_layout = __esm({
1768
2309
  constructor(device, props) {
1769
2310
  super(device, props);
1770
2311
  this.device = device;
1771
- const bindGroupEntries = this.mapShaderLayoutToBindGroupEntries();
2312
+ const bindGroupEntriesByGroup = this.mapShaderLayoutToBindGroupEntriesByGroup();
1772
2313
  this.handle = this.device.handle.createPipelineLayout({
1773
2314
  label: (props == null ? void 0 : props.id) ?? "unnamed-pipeline-layout",
1774
- bindGroupLayouts: [
1775
- // TODO (kaapp): We can cache these to re-use them across
1776
- // layers, particularly if using a separate group for injected
1777
- // bindings (e.g. project/lighting)
1778
- this.device.handle.createBindGroupLayout({
1779
- label: "bind-group-layout",
1780
- entries: bindGroupEntries
1781
- })
1782
- ]
2315
+ bindGroupLayouts: bindGroupEntriesByGroup.map((entries, group) => this.device.handle.createBindGroupLayout({
2316
+ label: `bind-group-layout-${group}`,
2317
+ entries
2318
+ }))
1783
2319
  });
1784
2320
  }
1785
2321
  destroy() {
1786
2322
  this.handle = null;
1787
2323
  }
1788
- mapShaderLayoutToBindGroupEntries() {
1789
- const bindGroupEntries = [];
2324
+ mapShaderLayoutToBindGroupEntriesByGroup() {
2325
+ const maxGroup = this.props.shaderLayout.bindings.reduce((highestGroup, binding) => Math.max(highestGroup, binding.group), -1);
2326
+ const bindGroupEntriesByGroup = Array.from({ length: maxGroup + 1 }, () => []);
1790
2327
  for (const binding of this.props.shaderLayout.bindings) {
1791
2328
  const bindingTypeInfo = {};
1792
2329
  switch (binding.type) {
@@ -1843,13 +2380,13 @@ var init_webgpu_pipeline_layout = __esm({
1843
2380
  }
1844
2381
  }
1845
2382
  const VISIBILITY_ALL = GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE;
1846
- bindGroupEntries.push({
2383
+ bindGroupEntriesByGroup[binding.group].push({
1847
2384
  binding: binding.location,
1848
2385
  visibility: binding.visibility || VISIBILITY_ALL,
1849
2386
  ...bindingTypeInfo
1850
2387
  });
1851
2388
  }
1852
- return bindGroupEntries;
2389
+ return bindGroupEntriesByGroup;
1853
2390
  }
1854
2391
  };
1855
2392
  isStorageTextureBindingLayout = (maybe) => {
@@ -1874,6 +2411,11 @@ var init_webgpu_fence = __esm({
1874
2411
  this.device = device;
1875
2412
  this.signaled = device.handle.queue.onSubmittedWorkDone().then(() => {
1876
2413
  this._signaled = true;
2414
+ }).catch((error) => {
2415
+ if (this.device.shouldIgnoreDroppedInstanceError(error)) {
2416
+ return;
2417
+ }
2418
+ throw error;
1877
2419
  });
1878
2420
  }
1879
2421
  isSignaled() {
@@ -1913,6 +2455,14 @@ function getShaderLayoutFromWGSL(source) {
1913
2455
  members
1914
2456
  });
1915
2457
  }
2458
+ for (const storageBuffer of parsedWGSL.storage) {
2459
+ shaderLayout.bindings.push({
2460
+ type: storageBuffer.access === "read" ? "read-only-storage" : "storage",
2461
+ name: storageBuffer.name,
2462
+ group: storageBuffer.group,
2463
+ location: storageBuffer.binding
2464
+ });
2465
+ }
1916
2466
  for (const texture of parsedWGSL.textures) {
1917
2467
  const bindingDeclaration = {
1918
2468
  type: "texture",
@@ -1996,16 +2546,583 @@ var init_get_shader_layout_wgsl = __esm({
1996
2546
  }
1997
2547
  });
1998
2548
 
2549
+ // dist/adapter/helpers/generate-mipmaps-webgpu.js
2550
+ function generateMipmapsWebGPU(device, texture) {
2551
+ if (texture.mipLevels <= 1) {
2552
+ return;
2553
+ }
2554
+ if (texture.dimension === "3d") {
2555
+ generateMipmaps3D(device, texture);
2556
+ return;
2557
+ }
2558
+ if (RENDER_DIMENSIONS.includes(texture.dimension)) {
2559
+ generateMipmapsRender(device, texture);
2560
+ return;
2561
+ }
2562
+ throw new Error(`Cannot generate mipmaps for texture dimension "${texture.dimension}" with WebGPU.`);
2563
+ }
2564
+ function generateMipmapsRender(device, texture) {
2565
+ validateFormatCapabilities(device, texture, ["render", "filter"], "render");
2566
+ const colorAttachmentFormat = getColorAttachmentFormat(texture.format, "render", texture.dimension);
2567
+ const viewDimension = texture.dimension;
2568
+ const shaderSource = getRenderMipmapWGSL(viewDimension);
2569
+ const sampler = device.createSampler({ minFilter: "linear", magFilter: "linear" });
2570
+ const uniformsBuffer = device.createBuffer({
2571
+ byteLength: 16,
2572
+ usage: import_core23.Buffer.UNIFORM | import_core23.Buffer.COPY_DST
2573
+ });
2574
+ const uniformValues = new Uint32Array(1);
2575
+ const sourceTextureLayout = {
2576
+ type: "texture",
2577
+ name: "sourceTexture",
2578
+ group: 0,
2579
+ location: 1,
2580
+ viewDimension,
2581
+ sampleType: "float"
2582
+ };
2583
+ const uniformsLayout = {
2584
+ type: "uniform",
2585
+ name: "uniforms",
2586
+ group: 0,
2587
+ location: 2
2588
+ };
2589
+ const renderShaderLayout = {
2590
+ attributes: [],
2591
+ bindings: [RENDER_SOURCE_SAMPLER_LAYOUT, sourceTextureLayout, uniformsLayout]
2592
+ };
2593
+ const vertexShader = device.createShader({
2594
+ id: "mipmap-generation-render-vs",
2595
+ source: shaderSource,
2596
+ language: "wgsl",
2597
+ stage: "vertex"
2598
+ });
2599
+ const fragmentShader = device.createShader({
2600
+ id: "mipmap-generation-render-fs",
2601
+ source: shaderSource,
2602
+ language: "wgsl",
2603
+ stage: "fragment"
2604
+ });
2605
+ const renderPipeline = device.createRenderPipeline({
2606
+ id: `mipmap-generation-render:${texture.dimension}:${texture.format}`,
2607
+ vs: vertexShader,
2608
+ fs: fragmentShader,
2609
+ shaderLayout: renderShaderLayout,
2610
+ colorAttachmentFormats: [colorAttachmentFormat],
2611
+ topology: "triangle-list"
2612
+ });
2613
+ let sourceWidth = texture.width;
2614
+ let sourceHeight = texture.height;
2615
+ const layerCount = texture.dimension === "2d" ? 1 : texture.depth;
2616
+ function renderMipmapLayer(sourceView, baseMipLevel, baseArrayLayer, destinationWidth, destinationHeight) {
2617
+ uniformValues[0] = baseArrayLayer;
2618
+ uniformsBuffer.write(uniformValues);
2619
+ const destinationView = texture.createView({
2620
+ dimension: "2d",
2621
+ baseMipLevel,
2622
+ mipLevelCount: 1,
2623
+ baseArrayLayer,
2624
+ arrayLayerCount: 1
2625
+ });
2626
+ const framebuffer = device.createFramebuffer({
2627
+ colorAttachments: [destinationView]
2628
+ });
2629
+ const renderPass = device.beginRenderPass({
2630
+ id: `mipmap-generation:${texture.format}:${baseMipLevel}:${baseArrayLayer}`,
2631
+ framebuffer
2632
+ });
2633
+ try {
2634
+ renderPass.setPipeline(renderPipeline);
2635
+ renderPass.setBindings({
2636
+ sourceSampler: sampler,
2637
+ sourceTexture: sourceView,
2638
+ uniforms: uniformsBuffer
2639
+ });
2640
+ renderPass.setParameters({
2641
+ viewport: [0, 0, destinationWidth, destinationHeight, 0, 1],
2642
+ scissorRect: [0, 0, destinationWidth, destinationHeight]
2643
+ });
2644
+ renderPass.draw({ vertexCount: 3 });
2645
+ renderPass.end();
2646
+ device.submit();
2647
+ } finally {
2648
+ destinationView.destroy();
2649
+ framebuffer.destroy();
2650
+ }
2651
+ }
2652
+ try {
2653
+ for (let baseMipLevel = 1; baseMipLevel < texture.mipLevels; ++baseMipLevel) {
2654
+ validateFormatCapabilities(device, texture, ["render", "filter"], "render");
2655
+ const sourceMipLevel = baseMipLevel - 1;
2656
+ const destinationWidth = Math.max(1, sourceWidth >> 1);
2657
+ const destinationHeight = Math.max(1, sourceHeight >> 1);
2658
+ const sourceView = texture.createView({
2659
+ dimension: viewDimension,
2660
+ baseMipLevel: sourceMipLevel,
2661
+ mipLevelCount: 1,
2662
+ baseArrayLayer: 0,
2663
+ arrayLayerCount: texture.depth
2664
+ });
2665
+ try {
2666
+ for (let baseArrayLayer = 0; baseArrayLayer < layerCount; ++baseArrayLayer) {
2667
+ renderMipmapLayer(sourceView, baseMipLevel, baseArrayLayer, destinationWidth, destinationHeight);
2668
+ }
2669
+ } finally {
2670
+ sourceView.destroy();
2671
+ }
2672
+ sourceWidth = destinationWidth;
2673
+ sourceHeight = destinationHeight;
2674
+ }
2675
+ } finally {
2676
+ renderPipeline.destroy();
2677
+ vertexShader.destroy();
2678
+ fragmentShader.destroy();
2679
+ sampler.destroy();
2680
+ uniformsBuffer.destroy();
2681
+ }
2682
+ }
2683
+ function getColorAttachmentFormat(format, path, dimension) {
2684
+ if (import_core23.textureFormatDecoder.isColor(format)) {
2685
+ return format;
2686
+ }
2687
+ throw new Error(`Cannot run ${path} mipmap generation for ${dimension} texture with format "${format}". Only color textures can be used for this operation. Required capabilities: color. Actual capabilities: color=false.`);
2688
+ }
2689
+ function generateMipmaps3D(device, texture) {
2690
+ validateFormatCapabilities(device, texture, ["filter", "store"], "compute");
2691
+ const format = getColorAttachmentFormat(texture.format, "compute", texture.dimension);
2692
+ const shaderSource = get3DComputeMipmapWGSL(format);
2693
+ const destinationTextureLayout = {
2694
+ type: "storage",
2695
+ name: "destinationTexture",
2696
+ group: 0,
2697
+ location: 1,
2698
+ format,
2699
+ viewDimension: "3d",
2700
+ access: "write-only"
2701
+ };
2702
+ const computeShaderLayout = {
2703
+ bindings: [COMPUTE_SOURCE_TEXTURE_LAYOUT, destinationTextureLayout, COMPUTE_UNIFORMS_LAYOUT]
2704
+ };
2705
+ const computeShader = device.createShader({
2706
+ id: "mipmap-generation-compute",
2707
+ source: shaderSource,
2708
+ language: "wgsl",
2709
+ stage: "compute"
2710
+ });
2711
+ const computePipeline = device.createComputePipeline({
2712
+ id: `mipmap-generation-compute:${texture.format}`,
2713
+ shader: computeShader,
2714
+ shaderLayout: computeShaderLayout
2715
+ });
2716
+ const uniformsBuffer = device.createBuffer({
2717
+ byteLength: 32,
2718
+ usage: import_core23.Buffer.UNIFORM | import_core23.Buffer.COPY_DST
2719
+ });
2720
+ const uniformValues = new Uint32Array(8);
2721
+ let sourceWidth = texture.width;
2722
+ let sourceHeight = texture.height;
2723
+ let sourceDepth = texture.depth;
2724
+ try {
2725
+ for (let destinationMipLevel = 1; destinationMipLevel < texture.mipLevels; ++destinationMipLevel) {
2726
+ validateFormatCapabilities(device, texture, ["filter", "store"], "compute");
2727
+ const destinationWidth = Math.max(1, sourceWidth >> 1);
2728
+ const destinationHeight = Math.max(1, sourceHeight >> 1);
2729
+ const destinationDepth = Math.max(1, sourceDepth >> 1);
2730
+ uniformValues[0] = sourceWidth;
2731
+ uniformValues[1] = sourceHeight;
2732
+ uniformValues[2] = sourceDepth;
2733
+ uniformValues[3] = destinationWidth;
2734
+ uniformValues[4] = destinationHeight;
2735
+ uniformValues[5] = destinationDepth;
2736
+ uniformValues[6] = 0;
2737
+ uniformsBuffer.write(uniformValues);
2738
+ const sourceView = texture.createView({
2739
+ dimension: "3d",
2740
+ baseMipLevel: destinationMipLevel - 1,
2741
+ mipLevelCount: 1,
2742
+ baseArrayLayer: 0,
2743
+ arrayLayerCount: 1
2744
+ });
2745
+ const destinationView = texture.createView({
2746
+ dimension: "3d",
2747
+ baseMipLevel: destinationMipLevel,
2748
+ mipLevelCount: 1,
2749
+ baseArrayLayer: 0,
2750
+ arrayLayerCount: 1
2751
+ });
2752
+ computePipeline.setBindings({
2753
+ sourceTexture: sourceView,
2754
+ destinationTexture: destinationView,
2755
+ uniforms: uniformsBuffer
2756
+ });
2757
+ try {
2758
+ const workgroupsX = Math.ceil(destinationWidth / WORKGROUP_SIZE.x);
2759
+ const workgroupsY = Math.ceil(destinationHeight / WORKGROUP_SIZE.y);
2760
+ const workgroupsZ = Math.ceil(destinationDepth / WORKGROUP_SIZE.z);
2761
+ const computePass = device.beginComputePass({});
2762
+ computePass.setPipeline(computePipeline);
2763
+ computePass.dispatch(workgroupsX, workgroupsY, workgroupsZ);
2764
+ computePass.end();
2765
+ device.submit();
2766
+ } finally {
2767
+ sourceView.destroy();
2768
+ destinationView.destroy();
2769
+ }
2770
+ sourceWidth = destinationWidth;
2771
+ sourceHeight = destinationHeight;
2772
+ sourceDepth = destinationDepth;
2773
+ }
2774
+ } finally {
2775
+ computePipeline.destroy();
2776
+ computeShader.destroy();
2777
+ uniformsBuffer.destroy();
2778
+ }
2779
+ }
2780
+ function validateFormatCapabilities(device, texture, requiredCapabilities, path) {
2781
+ const { format, dimension } = texture;
2782
+ const capabilities = device.getTextureFormatCapabilities(format);
2783
+ const missingCapabilities = requiredCapabilities.filter((capability) => !capabilities[capability]);
2784
+ if (missingCapabilities.length > 0) {
2785
+ const required = requiredCapabilities.join(" + ");
2786
+ const actual = requiredCapabilities.map((capability) => `${capability}=${capabilities[capability]}`).join(", ");
2787
+ throw new Error(`Cannot run ${path} mipmap generation for ${dimension} texture with format "${format}". Required capabilities: ${required}. Actual capabilities: ${actual}.`);
2788
+ }
2789
+ }
2790
+ function getSourceTextureType(dimension) {
2791
+ switch (dimension) {
2792
+ case "2d":
2793
+ return "texture_2d<f32>";
2794
+ case "2d-array":
2795
+ return "texture_2d_array<f32>";
2796
+ case "cube":
2797
+ return "texture_cube<f32>";
2798
+ case "cube-array":
2799
+ return "texture_cube_array<f32>";
2800
+ default:
2801
+ throw new Error(`Unsupported render dimension "${dimension}" for mipmap generation.`);
2802
+ }
2803
+ }
2804
+ function getRenderMipmapWGSL(dimension) {
2805
+ const sourceSnippet = getRenderMipmapSampleSnippet(dimension);
2806
+ return `
2807
+ struct MipmapUniforms {
2808
+ sourceLayer: u32,
2809
+ };
2810
+
2811
+ fn _touchUniform(uniforms: MipmapUniforms) {
2812
+ let unusedSourceLayer = uniforms.sourceLayer;
2813
+ }
2814
+
2815
+ const faceMat = array(
2816
+ mat3x3f(
2817
+ 0.0, 0.0, -2.0,
2818
+ 0.0, -2.0, 0.0,
2819
+ 1.0, 1.0, 1.0
2820
+ ), // pos-x
2821
+ mat3x3f(
2822
+ 0.0, 0.0, 2.0,
2823
+ 0.0, -2.0, 0.0,
2824
+ -1.0, 1.0, -1.0
2825
+ ), // neg-x
2826
+ mat3x3f(
2827
+ 2.0, 0.0, 0.0,
2828
+ 0.0, 0.0, 2.0,
2829
+ -1.0, 1.0, -1.0
2830
+ ), // pos-y
2831
+ mat3x3f(
2832
+ 2.0, 0.0, 0.0,
2833
+ 0.0, 0.0, -2.0,
2834
+ -1.0, -1.0, 1.0
2835
+ ), // neg-y
2836
+ mat3x3f(
2837
+ 2.0, 0.0, 0.0,
2838
+ 0.0, -2.0, 0.0,
2839
+ -1.0, 1.0, 1.0
2840
+ ), // pos-z
2841
+ mat3x3f(
2842
+ -2.0, 0.0, 0.0,
2843
+ 0.0, -2.0, 0.0,
2844
+ 1.0, 1.0, -1.0
2845
+ ) // neg-z
2846
+ );
2847
+
2848
+ struct FragmentInputs {
2849
+ @builtin(position) position: vec4f,
2850
+ @location(0) texcoord: vec2f
2851
+ };
2852
+
2853
+ struct VertexOutput {
2854
+ @builtin(position) position: vec4f,
2855
+ @location(0) texcoord: vec2f
2856
+ };
2857
+
2858
+ @group(0) @binding(0) var sourceSampler: sampler;
2859
+ @group(0) @binding(1) var sourceTexture: ${getSourceTextureType(dimension)};
2860
+ @group(0) @binding(2) var<uniform> uniforms: MipmapUniforms;
2861
+
2862
+ @vertex
2863
+ fn vertexMain(
2864
+ @builtin(vertex_index) vertexIndex: u32
2865
+ ) -> VertexOutput {
2866
+ const positions = array(
2867
+ vec2f(-1.0, -1.0),
2868
+ vec2f(-1.0, 3.0),
2869
+ vec2f( 3.0, -1.0)
2870
+ );
2871
+
2872
+ let xy = positions[vertexIndex];
2873
+ return VertexOutput(
2874
+ vec4f(xy, 0.0, 1.0),
2875
+ xy * vec2f(0.5, -0.5) + vec2f(0.5)
2876
+ );
2877
+ }
2878
+
2879
+ @fragment
2880
+ fn fragmentMain(fsInput: VertexOutput) -> @location(0) vec4f {
2881
+ _touchUniform(uniforms);
2882
+ return ${sourceSnippet};
2883
+ }
2884
+ `;
2885
+ }
2886
+ function getRenderMipmapSampleSnippet(dimension) {
2887
+ const layer = "uniforms.sourceLayer";
2888
+ switch (dimension) {
2889
+ case "2d":
2890
+ return "textureSampleLevel(sourceTexture, sourceSampler, fsInput.texcoord, 0.0)";
2891
+ case "2d-array":
2892
+ return `textureSampleLevel(sourceTexture, sourceSampler, fsInput.texcoord, i32(${layer}), 0.0)`;
2893
+ case "cube":
2894
+ return `textureSampleLevel(sourceTexture, sourceSampler, faceMat[i32(${layer})] * vec3f(fract(fsInput.texcoord), 1.0), 0.0)`;
2895
+ case "cube-array":
2896
+ return `textureSampleLevel(sourceTexture, sourceSampler, faceMat[i32(${layer} % 6u)] * vec3f(fract(fsInput.texcoord), 1.0), i32(${layer} / 6u), 0.0)`;
2897
+ default:
2898
+ throw new Error(`Unsupported render dimension "${dimension}" for mipmap generation.`);
2899
+ }
2900
+ }
2901
+ function get3DComputeMipmapWGSL(format) {
2902
+ return `
2903
+ struct MipmapUniforms {
2904
+ sourceWidth: u32,
2905
+ sourceHeight: u32,
2906
+ sourceDepth: u32,
2907
+ destinationWidth: u32,
2908
+ destinationHeight: u32,
2909
+ destinationDepth: u32,
2910
+ padding: u32,
2911
+ };
2912
+
2913
+ @group(0) @binding(0) var sourceTexture: texture_3d<f32>;
2914
+ @group(0) @binding(1) var destinationTexture: texture_storage_3d<${format}, write>;
2915
+ @group(0) @binding(2) var<uniform> uniforms: MipmapUniforms;
2916
+
2917
+ @compute @workgroup_size(${WORKGROUP_SIZE.x}, ${WORKGROUP_SIZE.y}, ${WORKGROUP_SIZE.z})
2918
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2919
+ if (
2920
+ id.x >= uniforms.destinationWidth ||
2921
+ id.y >= uniforms.destinationHeight ||
2922
+ id.z >= uniforms.destinationDepth
2923
+ ) {
2924
+ return;
2925
+ }
2926
+
2927
+ let sourceBase = id * 2u;
2928
+ let sourceX0 = min(sourceBase.x, uniforms.sourceWidth - 1u);
2929
+ let sourceY0 = min(sourceBase.y, uniforms.sourceHeight - 1u);
2930
+ let sourceZ0 = min(sourceBase.z, uniforms.sourceDepth - 1u);
2931
+
2932
+ let sourceX1 = min(sourceBase.x + 1u, uniforms.sourceWidth - 1u);
2933
+ let sourceY1 = min(sourceBase.y + 1u, uniforms.sourceHeight - 1u);
2934
+ let sourceZ1 = min(sourceBase.z + 1u, uniforms.sourceDepth - 1u);
2935
+
2936
+ var sum = textureLoad(
2937
+ sourceTexture,
2938
+ vec3<i32>(i32(sourceX0), i32(sourceY0), i32(sourceZ0)),
2939
+ 0
2940
+ );
2941
+ sum += textureLoad(
2942
+ sourceTexture,
2943
+ vec3<i32>(i32(sourceX1), i32(sourceY0), i32(sourceZ0)),
2944
+ 0
2945
+ );
2946
+ sum += textureLoad(
2947
+ sourceTexture,
2948
+ vec3<i32>(i32(sourceX0), i32(sourceY1), i32(sourceZ0)),
2949
+ 0
2950
+ );
2951
+ sum += textureLoad(
2952
+ sourceTexture,
2953
+ vec3<i32>(i32(sourceX1), i32(sourceY1), i32(sourceZ0)),
2954
+ 0
2955
+ );
2956
+ sum += textureLoad(
2957
+ sourceTexture,
2958
+ vec3<i32>(i32(sourceX0), i32(sourceY0), i32(sourceZ1)),
2959
+ 0
2960
+ );
2961
+ sum += textureLoad(
2962
+ sourceTexture,
2963
+ vec3<i32>(i32(sourceX1), i32(sourceY0), i32(sourceZ1)),
2964
+ 0
2965
+ );
2966
+ sum += textureLoad(
2967
+ sourceTexture,
2968
+ vec3<i32>(i32(sourceX0), i32(sourceY1), i32(sourceZ1)),
2969
+ 0
2970
+ );
2971
+ sum += textureLoad(
2972
+ sourceTexture,
2973
+ vec3<i32>(i32(sourceX1), i32(sourceY1), i32(sourceZ1)),
2974
+ 0
2975
+ );
2976
+
2977
+ textureStore(
2978
+ destinationTexture,
2979
+ vec3<i32>(i32(id.x), i32(id.y), i32(id.z)),
2980
+ vec4<f32>(sum.xyz / 8.0, sum.w / 8.0)
2981
+ );
2982
+ }
2983
+ `;
2984
+ }
2985
+ var import_core23, RENDER_DIMENSIONS, WORKGROUP_SIZE, RENDER_SOURCE_SAMPLER_LAYOUT, COMPUTE_SOURCE_TEXTURE_LAYOUT, COMPUTE_UNIFORMS_LAYOUT;
2986
+ var init_generate_mipmaps_webgpu = __esm({
2987
+ "dist/adapter/helpers/generate-mipmaps-webgpu.js"() {
2988
+ "use strict";
2989
+ import_core23 = require("@luma.gl/core");
2990
+ RENDER_DIMENSIONS = [
2991
+ "2d",
2992
+ "2d-array",
2993
+ "cube",
2994
+ "cube-array"
2995
+ ];
2996
+ WORKGROUP_SIZE = {
2997
+ x: 4,
2998
+ y: 4,
2999
+ z: 4
3000
+ };
3001
+ RENDER_SOURCE_SAMPLER_LAYOUT = {
3002
+ type: "sampler",
3003
+ name: "sourceSampler",
3004
+ group: 0,
3005
+ location: 0
3006
+ };
3007
+ COMPUTE_SOURCE_TEXTURE_LAYOUT = {
3008
+ type: "texture",
3009
+ name: "sourceTexture",
3010
+ group: 0,
3011
+ location: 0,
3012
+ viewDimension: "3d",
3013
+ sampleType: "float"
3014
+ };
3015
+ COMPUTE_UNIFORMS_LAYOUT = {
3016
+ type: "uniform",
3017
+ name: "uniforms",
3018
+ group: 0,
3019
+ location: 2
3020
+ };
3021
+ }
3022
+ });
3023
+
3024
+ // dist/adapter/helpers/get-bind-group.js
3025
+ function getBindGroup(device, bindGroupLayout, shaderLayout, bindings, group) {
3026
+ const entries = getBindGroupEntries(bindings, shaderLayout, group);
3027
+ if (entries.length === 0) {
3028
+ return null;
3029
+ }
3030
+ device.pushErrorScope("validation");
3031
+ const bindGroup = device.handle.createBindGroup({
3032
+ layout: bindGroupLayout,
3033
+ entries
3034
+ });
3035
+ device.popErrorScope((error) => {
3036
+ import_core24.log.error(`bindGroup creation: ${error.message}`, bindGroup)();
3037
+ });
3038
+ return bindGroup;
3039
+ }
3040
+ function getBindGroupEntries(bindings, shaderLayout, group) {
3041
+ const entries = [];
3042
+ for (const [bindingName, value] of Object.entries(bindings)) {
3043
+ const exactBindingLayout = shaderLayout.bindings.find((binding) => binding.name === bindingName);
3044
+ const bindingLayout = exactBindingLayout || (0, import_core24.getShaderLayoutBinding)(shaderLayout, bindingName);
3045
+ const isShadowedAlias = !exactBindingLayout && bindingLayout ? bindingLayout.name in bindings : false;
3046
+ if (!isShadowedAlias && (bindingLayout == null ? void 0 : bindingLayout.group) === group) {
3047
+ const entry = bindingLayout ? getBindGroupEntry(value, bindingLayout.location, void 0, bindingName) : null;
3048
+ if (entry) {
3049
+ entries.push(entry);
3050
+ }
3051
+ if (value instanceof import_core24.Texture) {
3052
+ const samplerBindingLayout = (0, import_core24.getShaderLayoutBinding)(shaderLayout, `${bindingName}Sampler`, {
3053
+ ignoreWarnings: true
3054
+ });
3055
+ const samplerEntry = samplerBindingLayout ? samplerBindingLayout.group === group ? getBindGroupEntry(value, samplerBindingLayout.location, { sampler: true }, bindingName) : null : null;
3056
+ if (samplerEntry) {
3057
+ entries.push(samplerEntry);
3058
+ }
3059
+ }
3060
+ }
3061
+ }
3062
+ return entries;
3063
+ }
3064
+ function getBindGroupEntry(binding, index, options, bindingName = "unknown") {
3065
+ if (binding instanceof import_core24.Buffer) {
3066
+ return {
3067
+ binding: index,
3068
+ resource: {
3069
+ buffer: binding.handle
3070
+ }
3071
+ };
3072
+ }
3073
+ if (binding instanceof import_core24.Sampler) {
3074
+ return {
3075
+ binding: index,
3076
+ resource: binding.handle
3077
+ };
3078
+ }
3079
+ if (binding instanceof import_core24.TextureView) {
3080
+ return {
3081
+ binding: index,
3082
+ resource: binding.handle
3083
+ };
3084
+ }
3085
+ if (binding instanceof import_core24.Texture) {
3086
+ if (options == null ? void 0 : options.sampler) {
3087
+ return {
3088
+ binding: index,
3089
+ resource: binding.sampler.handle
3090
+ };
3091
+ }
3092
+ return {
3093
+ binding: index,
3094
+ resource: binding.view.handle
3095
+ };
3096
+ }
3097
+ import_core24.log.warn(`invalid binding ${bindingName}`, binding);
3098
+ return null;
3099
+ }
3100
+ var import_core24;
3101
+ var init_get_bind_group = __esm({
3102
+ "dist/adapter/helpers/get-bind-group.js"() {
3103
+ "use strict";
3104
+ import_core24 = require("@luma.gl/core");
3105
+ }
3106
+ });
3107
+
1999
3108
  // dist/adapter/webgpu-device.js
2000
3109
  var webgpu_device_exports = {};
2001
3110
  __export(webgpu_device_exports, {
2002
3111
  WebGPUDevice: () => WebGPUDevice
2003
3112
  });
2004
- var import_core23, WebGPUDevice;
3113
+ function scheduleMicrotask(callback) {
3114
+ if (globalThis.queueMicrotask) {
3115
+ globalThis.queueMicrotask(callback);
3116
+ return;
3117
+ }
3118
+ Promise.resolve().then(callback).catch(() => {
3119
+ });
3120
+ }
3121
+ var import_core25, WebGPUDevice;
2005
3122
  var init_webgpu_device = __esm({
2006
3123
  "dist/adapter/webgpu-device.js"() {
2007
3124
  "use strict";
2008
- import_core23 = require("@luma.gl/core");
3125
+ import_core25 = require("@luma.gl/core");
2009
3126
  init_webgpu_buffer();
2010
3127
  init_webgpu_texture();
2011
3128
  init_webgpu_external_texture();
@@ -2016,12 +3133,16 @@ var init_webgpu_device = __esm({
2016
3133
  init_webgpu_compute_pipeline();
2017
3134
  init_webgpu_vertex_array();
2018
3135
  init_webgpu_canvas_context();
3136
+ init_webgpu_presentation_context();
2019
3137
  init_webgpu_command_encoder();
2020
3138
  init_webgpu_query_set();
2021
3139
  init_webgpu_pipeline_layout();
2022
3140
  init_webgpu_fence();
2023
3141
  init_get_shader_layout_wgsl();
2024
- WebGPUDevice = class extends import_core23.Device {
3142
+ init_generate_mipmaps_webgpu();
3143
+ init_get_bind_group();
3144
+ init_cpu_hotspot_profiler();
3145
+ WebGPUDevice = class extends import_core25.Device {
2025
3146
  /** The underlying WebGPU device */
2026
3147
  handle;
2027
3148
  /* The underlying WebGPU adapter */
@@ -2038,6 +3159,7 @@ var init_webgpu_device = __esm({
2038
3159
  lost;
2039
3160
  canvasContext = null;
2040
3161
  _isLost = false;
3162
+ _defaultSampler = null;
2041
3163
  commandEncoder;
2042
3164
  get [Symbol.toStringTag]() {
2043
3165
  return "WebGPUDevice";
@@ -2064,7 +3186,7 @@ var init_webgpu_device = __esm({
2064
3186
  this._isLost = true;
2065
3187
  resolve({ reason: "destroyed", message: lostInfo.message });
2066
3188
  });
2067
- const canvasContextProps = import_core23.Device._getCanvasContextProps(props);
3189
+ const canvasContextProps = import_core25.Device._getCanvasContextProps(props);
2068
3190
  if (canvasContextProps) {
2069
3191
  this.canvasContext = new WebGPUCanvasContext(this, this.adapter, canvasContextProps);
2070
3192
  }
@@ -2075,6 +3197,10 @@ var init_webgpu_device = __esm({
2075
3197
  // const {glsl = true} = props;
2076
3198
  // this.glslang = glsl && await loadGlslangModule();
2077
3199
  destroy() {
3200
+ var _a, _b;
3201
+ (_a = this.commandEncoder) == null ? void 0 : _a.destroy();
3202
+ (_b = this._defaultSampler) == null ? void 0 : _b.destroy();
3203
+ this._defaultSampler = null;
2078
3204
  this.handle.destroy();
2079
3205
  }
2080
3206
  get isLost() {
@@ -2103,6 +3229,12 @@ var init_webgpu_device = __esm({
2103
3229
  createSampler(props) {
2104
3230
  return new WebGPUSampler(this, props);
2105
3231
  }
3232
+ getDefaultSampler() {
3233
+ this._defaultSampler ||= new WebGPUSampler(this, {
3234
+ id: `${this.id}-default-sampler`
3235
+ });
3236
+ return this._defaultSampler;
3237
+ }
2106
3238
  createRenderPipeline(props) {
2107
3239
  return new WebGPURenderPipeline(this, props);
2108
3240
  }
@@ -2131,32 +3263,129 @@ var init_webgpu_device = __esm({
2131
3263
  createCanvasContext(props) {
2132
3264
  return new WebGPUCanvasContext(this, this.adapter, props);
2133
3265
  }
3266
+ createPresentationContext(props) {
3267
+ return new WebGPUPresentationContext(this, props);
3268
+ }
2134
3269
  createPipelineLayout(props) {
2135
3270
  return new WebGPUPipelineLayout(this, props);
2136
3271
  }
3272
+ generateMipmapsWebGPU(texture) {
3273
+ generateMipmapsWebGPU(this, texture);
3274
+ }
3275
+ _createBindGroupLayoutWebGPU(pipeline, group) {
3276
+ return pipeline.handle.getBindGroupLayout(group);
3277
+ }
3278
+ _createBindGroupWebGPU(bindGroupLayout, shaderLayout, bindings, group) {
3279
+ if (Object.keys(bindings).length === 0) {
3280
+ return this.handle.createBindGroup({
3281
+ layout: bindGroupLayout,
3282
+ entries: []
3283
+ });
3284
+ }
3285
+ return getBindGroup(this, bindGroupLayout, shaderLayout, bindings, group);
3286
+ }
2137
3287
  submit(commandBuffer) {
3288
+ let submittedCommandEncoder = null;
2138
3289
  if (!commandBuffer) {
2139
- commandBuffer = this.commandEncoder.finish();
2140
- this.commandEncoder.destroy();
2141
- this.commandEncoder = this.createCommandEncoder({ id: `${this.id}-default-encoder` });
2142
- }
2143
- this.pushErrorScope("validation");
2144
- this.handle.queue.submit([commandBuffer.handle]);
2145
- this.popErrorScope((error) => {
2146
- this.reportError(new Error(`${this} command submission: ${error.message}`), this)();
2147
- this.debug();
3290
+ ({ submittedCommandEncoder, commandBuffer } = this._finalizeDefaultCommandEncoderForSubmit());
3291
+ }
3292
+ const profiler = getCpuHotspotProfiler(this);
3293
+ const startTime = profiler ? getTimestamp() : 0;
3294
+ const submitReason = getCpuHotspotSubmitReason(this);
3295
+ try {
3296
+ this.pushErrorScope("validation");
3297
+ const queueSubmitStartTime = profiler ? getTimestamp() : 0;
3298
+ this.handle.queue.submit([commandBuffer.handle]);
3299
+ if (profiler) {
3300
+ profiler.queueSubmitCount = (profiler.queueSubmitCount || 0) + 1;
3301
+ profiler.queueSubmitTimeMs = (profiler.queueSubmitTimeMs || 0) + (getTimestamp() - queueSubmitStartTime);
3302
+ }
3303
+ this.popErrorScope((error) => {
3304
+ this.reportError(new Error(`${this} command submission: ${error.message}`), this)();
3305
+ this.debug();
3306
+ });
3307
+ if (submittedCommandEncoder) {
3308
+ const submitResolveKickoffStartTime = profiler ? getTimestamp() : 0;
3309
+ scheduleMicrotask(() => {
3310
+ submittedCommandEncoder.resolveTimeProfilingQuerySet().then(() => {
3311
+ this.commandEncoder._gpuTimeMs = submittedCommandEncoder._gpuTimeMs;
3312
+ }).catch(() => {
3313
+ });
3314
+ });
3315
+ if (profiler) {
3316
+ profiler.submitResolveKickoffCount = (profiler.submitResolveKickoffCount || 0) + 1;
3317
+ profiler.submitResolveKickoffTimeMs = (profiler.submitResolveKickoffTimeMs || 0) + (getTimestamp() - submitResolveKickoffStartTime);
3318
+ }
3319
+ }
3320
+ } finally {
3321
+ if (profiler) {
3322
+ profiler.submitCount = (profiler.submitCount || 0) + 1;
3323
+ profiler.submitTimeMs = (profiler.submitTimeMs || 0) + (getTimestamp() - startTime);
3324
+ const reasonCountKey = submitReason === "query-readback" ? "queryReadbackSubmitCount" : "defaultSubmitCount";
3325
+ const reasonTimeKey = submitReason === "query-readback" ? "queryReadbackSubmitTimeMs" : "defaultSubmitTimeMs";
3326
+ profiler[reasonCountKey] = (profiler[reasonCountKey] || 0) + 1;
3327
+ profiler[reasonTimeKey] = (profiler[reasonTimeKey] || 0) + (getTimestamp() - startTime);
3328
+ }
3329
+ const commandBufferDestroyStartTime = profiler ? getTimestamp() : 0;
3330
+ commandBuffer.destroy();
3331
+ if (profiler) {
3332
+ profiler.commandBufferDestroyCount = (profiler.commandBufferDestroyCount || 0) + 1;
3333
+ profiler.commandBufferDestroyTimeMs = (profiler.commandBufferDestroyTimeMs || 0) + (getTimestamp() - commandBufferDestroyStartTime);
3334
+ }
3335
+ }
3336
+ }
3337
+ _finalizeDefaultCommandEncoderForSubmit() {
3338
+ const submittedCommandEncoder = this.commandEncoder;
3339
+ if (submittedCommandEncoder.getTimeProfilingSlotCount() > 0 && submittedCommandEncoder.getTimeProfilingQuerySet() instanceof WebGPUQuerySet) {
3340
+ const querySet = submittedCommandEncoder.getTimeProfilingQuerySet();
3341
+ querySet._encodeResolveToReadBuffer(submittedCommandEncoder, {
3342
+ firstQuery: 0,
3343
+ queryCount: submittedCommandEncoder.getTimeProfilingSlotCount()
3344
+ });
3345
+ }
3346
+ const commandBuffer = submittedCommandEncoder.finish();
3347
+ this.commandEncoder.destroy();
3348
+ this.commandEncoder = this.createCommandEncoder({
3349
+ id: submittedCommandEncoder.props.id,
3350
+ timeProfilingQuerySet: submittedCommandEncoder.getTimeProfilingQuerySet()
2148
3351
  });
3352
+ return { submittedCommandEncoder, commandBuffer };
2149
3353
  }
2150
3354
  // WebGPU specific
2151
3355
  pushErrorScope(scope) {
3356
+ if (!this.props.debug) {
3357
+ return;
3358
+ }
3359
+ const profiler = getCpuHotspotProfiler(this);
3360
+ const startTime = profiler ? getTimestamp() : 0;
2152
3361
  this.handle.pushErrorScope(scope);
3362
+ if (profiler) {
3363
+ profiler.errorScopePushCount = (profiler.errorScopePushCount || 0) + 1;
3364
+ profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
3365
+ }
2153
3366
  }
2154
3367
  popErrorScope(handler) {
3368
+ if (!this.props.debug) {
3369
+ return;
3370
+ }
3371
+ const profiler = getCpuHotspotProfiler(this);
3372
+ const startTime = profiler ? getTimestamp() : 0;
2155
3373
  this.handle.popErrorScope().then((error) => {
2156
3374
  if (error) {
2157
3375
  handler(error);
2158
3376
  }
3377
+ }).catch((error) => {
3378
+ if (this.shouldIgnoreDroppedInstanceError(error, "popErrorScope")) {
3379
+ return;
3380
+ }
3381
+ const errorMessage = error instanceof Error ? error.message : String(error);
3382
+ this.reportError(new Error(`${this} popErrorScope failed: ${errorMessage}`), this)();
3383
+ this.debug();
2159
3384
  });
3385
+ if (profiler) {
3386
+ profiler.errorScopePopCount = (profiler.errorScopePopCount || 0) + 1;
3387
+ profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
3388
+ }
2160
3389
  }
2161
3390
  // PRIVATE METHODS
2162
3391
  _getInfo() {
@@ -2164,10 +3393,12 @@ var init_webgpu_device = __esm({
2164
3393
  const vendor = this.adapterInfo.vendor || this.adapter.__brand || "unknown";
2165
3394
  const renderer = driver || "";
2166
3395
  const version = driverVersion || "";
2167
- const gpu = vendor === "apple" ? "apple" : "unknown";
3396
+ const fallback = Boolean(this.adapterInfo.isFallbackAdapter ?? this.adapter.isFallbackAdapter ?? false);
3397
+ const softwareRenderer = /SwiftShader/i.test(`${vendor} ${renderer} ${this.adapterInfo.architecture || ""}`);
3398
+ const gpu = vendor === "apple" ? "apple" : softwareRenderer || fallback ? "software" : "unknown";
2168
3399
  const gpuArchitecture = this.adapterInfo.architecture || "unknown";
2169
3400
  const gpuBackend = this.adapterInfo.backend || "unknown";
2170
- const gpuType = (this.adapterInfo.type || "").split(" ")[0].toLowerCase() || "unknown";
3401
+ const gpuType = (this.adapterInfo.type || "").split(" ")[0].toLowerCase() || (softwareRenderer || fallback ? "cpu" : "unknown");
2171
3402
  return {
2172
3403
  type: "webgpu",
2173
3404
  vendor,
@@ -2177,10 +3408,15 @@ var init_webgpu_device = __esm({
2177
3408
  gpuType,
2178
3409
  gpuBackend,
2179
3410
  gpuArchitecture,
3411
+ fallback,
2180
3412
  shadingLanguage: "wgsl",
2181
3413
  shadingLanguageVersion: 100
2182
3414
  };
2183
3415
  }
3416
+ shouldIgnoreDroppedInstanceError(error, operation) {
3417
+ const errorMessage = error instanceof Error ? error.message : String(error);
3418
+ return errorMessage.includes("Instance dropped") && (!operation || errorMessage.includes(operation)) && (this._isLost || this.info.gpu === "software" || this.info.gpuType === "cpu" || Boolean(this.info.fallback));
3419
+ }
2184
3420
  _getFeatures() {
2185
3421
  const features = new Set(this.handle.features);
2186
3422
  if (features.has("depth-clamping")) {
@@ -2197,7 +3433,6 @@ var init_webgpu_device = __esm({
2197
3433
  features.add("snorm16-renderable-webgl");
2198
3434
  }
2199
3435
  const WEBGPU_ALWAYS_FEATURES = [
2200
- "timer-query-webgl",
2201
3436
  "compilation-status-async-webgl",
2202
3437
  "float32-renderable-webgl",
2203
3438
  "float16-renderable-webgl",
@@ -2208,7 +3443,7 @@ var init_webgpu_device = __esm({
2208
3443
  for (const feature of WEBGPU_ALWAYS_FEATURES) {
2209
3444
  features.add(feature);
2210
3445
  }
2211
- return new import_core23.DeviceFeatures(Array.from(features), this.props._disabledFeatures);
3446
+ return new import_core25.DeviceFeatures(Array.from(features), this.props._disabledFeatures);
2212
3447
  }
2213
3448
  _getDeviceSpecificTextureFormatCapabilities(capabilities) {
2214
3449
  const { format } = capabilities;
@@ -2236,8 +3471,8 @@ __export(dist_exports, {
2236
3471
  module.exports = __toCommonJS(dist_exports);
2237
3472
 
2238
3473
  // dist/adapter/webgpu-adapter.js
2239
- var import_core24 = require("@luma.gl/core");
2240
- var WebGPUAdapter = class extends import_core24.Adapter {
3474
+ var import_core26 = require("@luma.gl/core");
3475
+ var WebGPUAdapter = class extends import_core26.Adapter {
2241
3476
  /** type of device's created by this adapter */
2242
3477
  type = "webgpu";
2243
3478
  isSupported() {
@@ -2284,14 +3519,14 @@ var WebGPUAdapter = class extends import_core24.Adapter {
2284
3519
  requiredLimits
2285
3520
  });
2286
3521
  const { WebGPUDevice: WebGPUDevice2 } = await Promise.resolve().then(() => (init_webgpu_device(), webgpu_device_exports));
2287
- import_core24.log.groupCollapsed(1, "WebGPUDevice created")();
3522
+ import_core26.log.groupCollapsed(1, "WebGPUDevice created")();
2288
3523
  try {
2289
3524
  const device = new WebGPUDevice2(props, gpuDevice, adapter, adapterInfo);
2290
- import_core24.log.probe(1, "Device created. For more info, set chrome://flags/#enable-webgpu-developer-features")();
2291
- import_core24.log.table(1, device.info)();
3525
+ import_core26.log.probe(1, "Device created. For more info, set chrome://flags/#enable-webgpu-developer-features")();
3526
+ import_core26.log.table(1, device.info)();
2292
3527
  return device;
2293
3528
  } finally {
2294
- import_core24.log.groupEnd(1)();
3529
+ import_core26.log.groupEnd(1)();
2295
3530
  }
2296
3531
  }
2297
3532
  async attach(handle) {