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

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 (137) 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 +39 -32
  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 +7 -0
  21. package/dist/adapter/resources/webgpu-buffer.d.ts.map +1 -1
  22. package/dist/adapter/resources/webgpu-buffer.js +58 -15
  23. package/dist/adapter/resources/webgpu-buffer.js.map +1 -1
  24. package/dist/adapter/resources/webgpu-command-buffer.js +1 -1
  25. package/dist/adapter/resources/webgpu-command-buffer.js.map +1 -1
  26. package/dist/adapter/resources/webgpu-command-encoder.d.ts +7 -16
  27. package/dist/adapter/resources/webgpu-command-encoder.d.ts.map +1 -1
  28. package/dist/adapter/resources/webgpu-command-encoder.js +89 -32
  29. package/dist/adapter/resources/webgpu-command-encoder.js.map +1 -1
  30. package/dist/adapter/resources/webgpu-compute-pass.d.ts +3 -3
  31. package/dist/adapter/resources/webgpu-compute-pass.d.ts.map +1 -1
  32. package/dist/adapter/resources/webgpu-compute-pass.js +30 -12
  33. package/dist/adapter/resources/webgpu-compute-pass.js.map +1 -1
  34. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts +7 -9
  35. package/dist/adapter/resources/webgpu-compute-pipeline.d.ts.map +1 -1
  36. package/dist/adapter/resources/webgpu-compute-pipeline.js +30 -17
  37. package/dist/adapter/resources/webgpu-compute-pipeline.js.map +1 -1
  38. package/dist/adapter/resources/webgpu-fence.d.ts +13 -0
  39. package/dist/adapter/resources/webgpu-fence.d.ts.map +1 -0
  40. package/dist/adapter/resources/webgpu-fence.js +33 -0
  41. package/dist/adapter/resources/webgpu-fence.js.map +1 -0
  42. package/dist/adapter/resources/webgpu-framebuffer.d.ts +6 -0
  43. package/dist/adapter/resources/webgpu-framebuffer.d.ts.map +1 -1
  44. package/dist/adapter/resources/webgpu-framebuffer.js +16 -0
  45. package/dist/adapter/resources/webgpu-framebuffer.js.map +1 -1
  46. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts +1 -1
  47. package/dist/adapter/resources/webgpu-pipeline-layout.d.ts.map +1 -1
  48. package/dist/adapter/resources/webgpu-pipeline-layout.js +11 -18
  49. package/dist/adapter/resources/webgpu-pipeline-layout.js.map +1 -1
  50. package/dist/adapter/resources/webgpu-query-set.d.ts +33 -4
  51. package/dist/adapter/resources/webgpu-query-set.d.ts.map +1 -1
  52. package/dist/adapter/resources/webgpu-query-set.js +145 -4
  53. package/dist/adapter/resources/webgpu-query-set.js.map +1 -1
  54. package/dist/adapter/resources/webgpu-render-pass.d.ts +6 -3
  55. package/dist/adapter/resources/webgpu-render-pass.d.ts.map +1 -1
  56. package/dist/adapter/resources/webgpu-render-pass.js +78 -34
  57. package/dist/adapter/resources/webgpu-render-pass.js.map +1 -1
  58. package/dist/adapter/resources/webgpu-render-pipeline.d.ts +14 -10
  59. package/dist/adapter/resources/webgpu-render-pipeline.d.ts.map +1 -1
  60. package/dist/adapter/resources/webgpu-render-pipeline.js +56 -35
  61. package/dist/adapter/resources/webgpu-render-pipeline.js.map +1 -1
  62. package/dist/adapter/resources/webgpu-sampler.d.ts.map +1 -1
  63. package/dist/adapter/resources/webgpu-sampler.js +4 -0
  64. package/dist/adapter/resources/webgpu-sampler.js.map +1 -1
  65. package/dist/adapter/resources/webgpu-shader.d.ts.map +1 -1
  66. package/dist/adapter/resources/webgpu-shader.js +17 -1
  67. package/dist/adapter/resources/webgpu-shader.js.map +1 -1
  68. package/dist/adapter/resources/webgpu-texture-view.d.ts +6 -0
  69. package/dist/adapter/resources/webgpu-texture-view.d.ts.map +1 -1
  70. package/dist/adapter/resources/webgpu-texture-view.js +47 -11
  71. package/dist/adapter/resources/webgpu-texture-view.js.map +1 -1
  72. package/dist/adapter/resources/webgpu-texture.d.ts +25 -3
  73. package/dist/adapter/resources/webgpu-texture.d.ts.map +1 -1
  74. package/dist/adapter/resources/webgpu-texture.js +211 -43
  75. package/dist/adapter/resources/webgpu-texture.js.map +1 -1
  76. package/dist/adapter/resources/webgpu-vertex-array.js +1 -1
  77. package/dist/adapter/resources/webgpu-vertex-array.js.map +1 -1
  78. package/dist/adapter/webgpu-adapter.d.ts.map +1 -1
  79. package/dist/adapter/webgpu-adapter.js +34 -34
  80. package/dist/adapter/webgpu-adapter.js.map +1 -1
  81. package/dist/adapter/webgpu-canvas-context.d.ts +6 -3
  82. package/dist/adapter/webgpu-canvas-context.d.ts.map +1 -1
  83. package/dist/adapter/webgpu-canvas-context.js +90 -30
  84. package/dist/adapter/webgpu-canvas-context.js.map +1 -1
  85. package/dist/adapter/webgpu-device.d.ts +12 -2
  86. package/dist/adapter/webgpu-device.d.ts.map +1 -1
  87. package/dist/adapter/webgpu-device.js +173 -16
  88. package/dist/adapter/webgpu-device.js.map +1 -1
  89. package/dist/adapter/webgpu-presentation-context.d.ts +25 -0
  90. package/dist/adapter/webgpu-presentation-context.d.ts.map +1 -0
  91. package/dist/adapter/webgpu-presentation-context.js +144 -0
  92. package/dist/adapter/webgpu-presentation-context.js.map +1 -0
  93. package/dist/dist.dev.js +8070 -547
  94. package/dist/dist.min.js +169 -6
  95. package/dist/index.cjs +1929 -410
  96. package/dist/index.cjs.map +4 -4
  97. package/dist/index.d.ts +2 -0
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +2 -0
  100. package/dist/index.js.map +1 -1
  101. package/dist/wgsl/get-shader-layout-wgsl.d.ts +8 -0
  102. package/dist/wgsl/get-shader-layout-wgsl.d.ts.map +1 -0
  103. package/dist/wgsl/get-shader-layout-wgsl.js +144 -0
  104. package/dist/wgsl/get-shader-layout-wgsl.js.map +1 -0
  105. package/package.json +6 -5
  106. package/src/adapter/helpers/cpu-hotspot-profiler.ts +70 -0
  107. package/src/adapter/helpers/generate-mipmaps-webgpu.ts +583 -0
  108. package/src/adapter/helpers/get-bind-group.ts +52 -46
  109. package/src/adapter/helpers/get-vertex-buffer-layout.ts +31 -12
  110. package/src/adapter/helpers/webgpu-parameters.ts +2 -0
  111. package/src/adapter/resources/webgpu-buffer.ts +61 -15
  112. package/src/adapter/resources/webgpu-command-buffer.ts +1 -1
  113. package/src/adapter/resources/webgpu-command-encoder.ts +129 -50
  114. package/src/adapter/resources/webgpu-compute-pass.ts +48 -13
  115. package/src/adapter/resources/webgpu-compute-pipeline.ts +49 -18
  116. package/src/adapter/resources/webgpu-fence.ts +38 -0
  117. package/src/adapter/resources/webgpu-framebuffer.ts +21 -0
  118. package/src/adapter/resources/webgpu-pipeline-layout.ts +18 -17
  119. package/src/adapter/resources/webgpu-query-set.ts +185 -9
  120. package/src/adapter/resources/webgpu-render-pass.ts +92 -40
  121. package/src/adapter/resources/webgpu-render-pipeline.ts +83 -44
  122. package/src/adapter/resources/webgpu-sampler.ts +5 -0
  123. package/src/adapter/resources/webgpu-shader.ts +16 -1
  124. package/src/adapter/resources/webgpu-texture-view.ts +51 -11
  125. package/src/adapter/resources/webgpu-texture.ts +281 -101
  126. package/src/adapter/resources/webgpu-vertex-array.ts +1 -1
  127. package/src/adapter/webgpu-adapter.ts +40 -42
  128. package/src/adapter/webgpu-canvas-context.ts +107 -40
  129. package/src/adapter/webgpu-device.ts +231 -21
  130. package/src/adapter/webgpu-presentation-context.ts +180 -0
  131. package/src/index.ts +3 -0
  132. package/src/wgsl/get-shader-layout-wgsl.ts +165 -0
  133. package/dist/adapter/helpers/accessor-to-format.d.ts +0 -1
  134. package/dist/adapter/helpers/accessor-to-format.d.ts.map +0 -1
  135. package/dist/adapter/helpers/accessor-to-format.js +0 -105
  136. package/dist/adapter/helpers/accessor-to-format.js.map +0 -1
  137. package/src/adapter/helpers/accessor-to-format.ts +0 -104
package/dist/index.cjs CHANGED
@@ -7,8 +7,8 @@ var __esm = (fn, res) => function __init() {
7
7
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
8
  };
9
9
  var __export = (target, all) => {
10
- for (var name2 in all)
11
- __defProp(target, name2, { get: all[name2], enumerable: true });
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
12
  };
13
13
  var __copyProps = (to, from, except, desc) => {
14
14
  if (from && typeof from === "object" || typeof from === "function") {
@@ -30,13 +30,15 @@ var init_webgpu_buffer = __esm({
30
30
  device;
31
31
  handle;
32
32
  byteLength;
33
+ paddedByteLength;
33
34
  constructor(device, props) {
34
35
  var _a, _b;
35
36
  super(device, props);
36
37
  this.device = device;
37
38
  this.byteLength = props.byteLength || ((_a = props.data) == null ? void 0 : _a.byteLength) || 0;
39
+ this.paddedByteLength = Math.ceil(this.byteLength / 4) * 4;
38
40
  const mappedAtCreation = Boolean(this.props.onMapped || props.data);
39
- const size = Math.ceil(this.byteLength / 4) * 4;
41
+ const size = this.paddedByteLength;
40
42
  this.device.pushErrorScope("out-of-memory");
41
43
  this.device.pushErrorScope("validation");
42
44
  this.handle = this.props.handle || this.device.handle.createBuffer({
@@ -72,11 +74,24 @@ var init_webgpu_buffer = __esm({
72
74
  this.device.reportError(new Error(`${this} creation failed ${error.message}`), this)();
73
75
  this.device.debug();
74
76
  });
77
+ if (!this.props.handle) {
78
+ this.trackAllocatedMemory(size);
79
+ } else {
80
+ this.trackReferencedMemory(size, "Buffer");
81
+ }
75
82
  }
76
83
  destroy() {
77
- var _a;
78
- (_a = this.handle) == null ? void 0 : _a.destroy();
79
- 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
+ }
80
95
  }
81
96
  write(data, byteOffset = 0) {
82
97
  const arrayBuffer = ArrayBuffer.isView(data) ? data.buffer : data;
@@ -89,18 +104,21 @@ var init_webgpu_buffer = __esm({
89
104
  });
90
105
  }
91
106
  async mapAndWriteAsync(callback, byteOffset = 0, byteLength = this.byteLength - byteOffset) {
107
+ const alignedByteLength = Math.ceil(byteLength / 4) * 4;
92
108
  const isMappable = (this.usage & import_core.Buffer.MAP_WRITE) !== 0;
93
- const mappableBuffer = !isMappable ? this._getMappableBuffer(import_core.Buffer.MAP_WRITE | import_core.Buffer.COPY_SRC, 0, this.byteLength) : null;
109
+ const mappableBuffer = !isMappable ? this._getMappableBuffer(import_core.Buffer.MAP_WRITE | import_core.Buffer.COPY_SRC, 0, this.paddedByteLength) : null;
94
110
  const writeBuffer = mappableBuffer || this;
95
111
  this.device.pushErrorScope("validation");
96
112
  try {
97
113
  await this.device.handle.queue.onSubmittedWorkDone();
98
- await writeBuffer.handle.mapAsync(GPUMapMode.WRITE, byteOffset, byteLength);
99
- const arrayBuffer = writeBuffer.handle.getMappedRange(byteOffset, byteLength);
114
+ await writeBuffer.handle.mapAsync(GPUMapMode.WRITE, byteOffset, alignedByteLength);
115
+ const mappedRange = writeBuffer.handle.getMappedRange(byteOffset, alignedByteLength);
116
+ const arrayBuffer = mappedRange.slice(0, byteLength);
100
117
  await callback(arrayBuffer, "mapped");
118
+ new Uint8Array(mappedRange).set(new Uint8Array(arrayBuffer), 0);
101
119
  writeBuffer.handle.unmap();
102
120
  if (mappableBuffer) {
103
- this._copyBuffer(mappableBuffer, byteOffset, byteLength);
121
+ this._copyBuffer(mappableBuffer, byteOffset, alignedByteLength);
104
122
  }
105
123
  } finally {
106
124
  this.device.popErrorScope((error) => {
@@ -114,24 +132,37 @@ var init_webgpu_buffer = __esm({
114
132
  return this.mapAndReadAsync((arrayBuffer) => new Uint8Array(arrayBuffer.slice(0)), byteOffset, byteLength);
115
133
  }
116
134
  async mapAndReadAsync(callback, byteOffset = 0, byteLength = this.byteLength - byteOffset) {
135
+ const requestedEnd = byteOffset + byteLength;
136
+ if (requestedEnd > this.byteLength) {
137
+ throw new Error("Mapping range exceeds buffer size");
138
+ }
139
+ let mappedByteOffset = byteOffset;
140
+ let mappedByteLength = byteLength;
141
+ let sliceByteOffset = 0;
142
+ let lifetime = "mapped";
117
143
  if (byteOffset % 8 !== 0 || byteLength % 4 !== 0) {
118
- throw new Error("byteOffset must be multiple of 8 and byteLength multiple of 4");
144
+ mappedByteOffset = Math.floor(byteOffset / 8) * 8;
145
+ const alignedEnd = Math.ceil(requestedEnd / 4) * 4;
146
+ mappedByteLength = alignedEnd - mappedByteOffset;
147
+ sliceByteOffset = byteOffset - mappedByteOffset;
148
+ lifetime = "copied";
119
149
  }
120
- if (byteOffset + byteLength > this.handle.size) {
150
+ if (mappedByteOffset + mappedByteLength > this.paddedByteLength) {
121
151
  throw new Error("Mapping range exceeds buffer size");
122
152
  }
123
153
  const isMappable = (this.usage & import_core.Buffer.MAP_READ) !== 0;
124
- const mappableBuffer = !isMappable ? this._getMappableBuffer(import_core.Buffer.MAP_READ | import_core.Buffer.COPY_DST, 0, this.byteLength) : null;
154
+ const mappableBuffer = !isMappable ? this._getMappableBuffer(import_core.Buffer.MAP_READ | import_core.Buffer.COPY_DST, 0, this.paddedByteLength) : null;
125
155
  const readBuffer = mappableBuffer || this;
126
156
  this.device.pushErrorScope("validation");
127
157
  try {
128
158
  await this.device.handle.queue.onSubmittedWorkDone();
129
159
  if (mappableBuffer) {
130
- mappableBuffer._copyBuffer(this);
160
+ mappableBuffer._copyBuffer(this, mappedByteOffset, mappedByteLength);
131
161
  }
132
- await readBuffer.handle.mapAsync(GPUMapMode.READ, byteOffset, byteLength);
133
- const arrayBuffer = readBuffer.handle.getMappedRange(byteOffset, byteLength);
134
- const result = await callback(arrayBuffer, "mapped");
162
+ await readBuffer.handle.mapAsync(GPUMapMode.READ, mappedByteOffset, mappedByteLength);
163
+ const arrayBuffer = readBuffer.handle.getMappedRange(mappedByteOffset, mappedByteLength);
164
+ const mappedRange = lifetime === "mapped" ? arrayBuffer : arrayBuffer.slice(sliceByteOffset, sliceByteOffset + byteLength);
165
+ const result = await callback(mappedRange, lifetime);
135
166
  readBuffer.handle.unmap();
136
167
  return result;
137
168
  } finally {
@@ -208,18 +239,47 @@ var init_webgpu_sampler = __esm({
208
239
  this.handle.label = this.props.id;
209
240
  }
210
241
  destroy() {
242
+ if (this.destroyed) {
243
+ return;
244
+ }
245
+ this.destroyResource();
211
246
  this.handle = null;
212
247
  }
213
248
  };
214
249
  }
215
250
  });
216
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
+
217
276
  // dist/adapter/resources/webgpu-texture-view.js
218
277
  var import_core3, WebGPUTextureView;
219
278
  var init_webgpu_texture_view = __esm({
220
279
  "dist/adapter/resources/webgpu-texture-view.js"() {
221
280
  "use strict";
222
281
  import_core3 = require("@luma.gl/core");
282
+ init_cpu_hotspot_profiler();
223
283
  WebGPUTextureView = class extends import_core3.TextureView {
224
284
  device;
225
285
  handle;
@@ -229,8 +289,7 @@ var init_webgpu_texture_view = __esm({
229
289
  this.device = device;
230
290
  this.texture = props.texture;
231
291
  this.device.pushErrorScope("validation");
232
- this.handle = // props.handle ||
233
- this.texture.handle.createView({
292
+ this.handle = this.texture.handle.createView({
234
293
  format: this.props.format || this.texture.format,
235
294
  dimension: this.props.dimension || this.texture.dimension,
236
295
  aspect: this.props.aspect,
@@ -246,8 +305,42 @@ var init_webgpu_texture_view = __esm({
246
305
  this.handle.label = this.props.id;
247
306
  }
248
307
  destroy() {
308
+ if (this.destroyed) {
309
+ return;
310
+ }
311
+ this.destroyResource();
249
312
  this.handle = null;
250
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
+ }
251
344
  };
252
345
  }
253
346
  });
@@ -266,11 +359,17 @@ var init_webgpu_texture = __esm({
266
359
  handle;
267
360
  sampler;
268
361
  view;
362
+ _allocatedByteLength = 0;
269
363
  constructor(device, props) {
270
- super(device, props);
364
+ super(device, props, { byteAlignment: 256 });
271
365
  this.device = device;
272
- if (this.dimension === "cube") {
273
- this.depth = 6;
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);
274
373
  }
275
374
  this.device.pushErrorScope("out-of-memory");
276
375
  this.device.pushErrorScope("validation");
@@ -300,7 +399,6 @@ var init_webgpu_texture = __esm({
300
399
  this.width = this.handle.width;
301
400
  this.height = this.handle.height;
302
401
  }
303
- this.sampler = props.sampler instanceof WebGPUSampler ? props.sampler : new WebGPUSampler(this.device, props.sampler || {});
304
402
  this.view = new WebGPUTextureView(this.device, {
305
403
  ...this.props,
306
404
  texture: this,
@@ -308,46 +406,31 @@ var init_webgpu_texture = __esm({
308
406
  // Note: arrayLayerCount controls the view of array textures, but does not apply to 3d texture depths
309
407
  arrayLayerCount: this.dimension !== "3d" ? this.depth : 1
310
408
  });
409
+ this.attachResource(this.view);
311
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
+ }
312
417
  }
313
418
  destroy() {
314
- var _a;
315
- (_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();
316
429
  this.handle = null;
317
430
  }
318
431
  createView(props) {
319
432
  return new WebGPUTextureView(this.device, { ...props, texture: this });
320
433
  }
321
- copyImageData(options_) {
322
- const { width, height, depth } = this;
323
- const options = this._normalizeCopyImageDataOptions(options_);
324
- this.device.pushErrorScope("validation");
325
- this.device.handle.queue.writeTexture(
326
- // destination: GPUImageCopyTexture
327
- {
328
- // texture subresource
329
- texture: this.handle,
330
- mipLevel: options.mipLevel,
331
- aspect: options.aspect,
332
- // origin to write to
333
- origin: [options.x, options.y, options.z]
334
- },
335
- // data
336
- options.data,
337
- // dataLayout: GPUImageDataLayout
338
- {
339
- offset: options.byteOffset,
340
- bytesPerRow: options.bytesPerRow,
341
- rowsPerImage: options.rowsPerImage
342
- },
343
- // size: GPUExtent3D - extents of the content to write
344
- [width, height, depth]
345
- );
346
- this.device.popErrorScope((error) => {
347
- this.device.reportError(new Error(`copyImageData: ${error.message}`), this)();
348
- this.device.debug();
349
- });
350
- }
351
434
  copyExternalImage(options_) {
352
435
  const options = this._normalizeCopyExternalImageOptions(options_);
353
436
  this.device.pushErrorScope("validation");
@@ -356,20 +439,21 @@ var init_webgpu_texture = __esm({
356
439
  {
357
440
  source: options.image,
358
441
  origin: [options.sourceX, options.sourceY],
359
- flipY: options.flipY
442
+ flipY: false
443
+ // options.flipY
360
444
  },
361
445
  // destination: GPUImageCopyTextureTagged
362
446
  {
363
447
  texture: this.handle,
364
- origin: [options.x, options.y, 0],
365
- // options.depth],
448
+ origin: [options.x, options.y, options.z],
366
449
  mipLevel: options.mipLevel,
367
450
  aspect: options.aspect,
368
451
  colorSpace: options.colorSpace,
369
452
  premultipliedAlpha: options.premultipliedAlpha
370
453
  },
371
454
  // copySize: GPUExtent3D
372
- [options.width, options.height, 1]
455
+ [options.width, options.height, options.depth]
456
+ // depth is always 1 for 2D textures
373
457
  );
374
458
  this.device.popErrorScope((error) => {
375
459
  this.device.reportError(new Error(`copyExternalImage: ${error.message}`), this)();
@@ -380,6 +464,168 @@ var init_webgpu_texture = __esm({
380
464
  generateMipmapsWebGL() {
381
465
  import_core4.log.warn(`${this}: generateMipmaps not supported in WebGPU`)();
382
466
  }
467
+ getImageDataLayout(options) {
468
+ return {
469
+ byteLength: 0,
470
+ bytesPerRow: 0,
471
+ rowsPerImage: 0
472
+ };
473
+ }
474
+ readBuffer(options = {}, buffer) {
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
+ }
485
+ const gpuDevice = this.device.handle;
486
+ this.device.pushErrorScope("validation");
487
+ const commandEncoder = gpuDevice.createCommandEncoder();
488
+ this.copyToBuffer(commandEncoder, { x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect, byteOffset }, buffer);
489
+ const commandBuffer = commandEncoder.finish();
490
+ this.device.handle.queue.submit([commandBuffer]);
491
+ this.device.popErrorScope((error) => {
492
+ this.device.reportError(new Error(`${this} readBuffer: ${error.message}`), this)();
493
+ this.device.debug();
494
+ });
495
+ return buffer;
496
+ }
497
+ async readDataAsync(options = {}) {
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;
526
+ const gpuDevice = this.device.handle;
527
+ this.device.pushErrorScope("validation");
528
+ const commandEncoder = gpuDevice.createCommandEncoder();
529
+ commandEncoder.copyBufferToTexture({
530
+ buffer: buffer.handle,
531
+ offset: byteOffset,
532
+ bytesPerRow,
533
+ rowsPerImage
534
+ }, {
535
+ texture: this.handle,
536
+ origin: { x, y, z },
537
+ mipLevel,
538
+ aspect
539
+ }, { width, height, depthOrArrayLayers });
540
+ const commandBuffer = commandEncoder.finish();
541
+ this.device.handle.queue.submit([commandBuffer]);
542
+ this.device.popErrorScope((error) => {
543
+ this.device.reportError(new Error(`${this} writeBuffer: ${error.message}`), this)();
544
+ this.device.debug();
545
+ });
546
+ }
547
+ writeData(data, options_ = {}) {
548
+ const device = this.device;
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({
554
+ format: this.format,
555
+ width,
556
+ height,
557
+ depth: depthOrArrayLayers,
558
+ byteAlignment: 1
559
+ });
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
+ }
570
+ this.device.pushErrorScope("validation");
571
+ device.handle.queue.writeTexture({
572
+ texture: this.handle,
573
+ mipLevel,
574
+ aspect,
575
+ origin: { x, y, z }
576
+ }, source, {
577
+ offset: byteOffset,
578
+ bytesPerRow,
579
+ rowsPerImage
580
+ }, { width: copyWidth, height: copyHeight, depthOrArrayLayers });
581
+ this.device.popErrorScope((error) => {
582
+ this.device.reportError(new Error(`${this} writeData: ${error.message}`), this)();
583
+ this.device.debug();
584
+ });
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
+ }
383
629
  };
384
630
  }
385
631
  });
@@ -460,7 +706,19 @@ var init_webgpu_shader = __esm({
460
706
  }
461
707
  /** Returns compilation info for this shader */
462
708
  async getCompilationInfo() {
463
- 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
+ }
464
722
  return compilationInfo.messages;
465
723
  }
466
724
  };
@@ -551,6 +809,7 @@ var init_webgpu_parameters = __esm({
551
809
  const depthStencil = addDepthStencil(descriptor);
552
810
  depthStencil.format = value;
553
811
  },
812
+ clearDepth: notSupported,
554
813
  depthBias: (_, value, descriptor) => {
555
814
  const depthStencil = addDepthStencil(descriptor);
556
815
  depthStencil.depthBias = value;
@@ -689,90 +948,6 @@ var init_webgpu_parameters = __esm({
689
948
  }
690
949
  });
691
950
 
692
- // dist/adapter/helpers/get-bind-group.js
693
- function getBindGroup(device, bindGroupLayout, shaderLayout, bindings) {
694
- const entries = getBindGroupEntries(bindings, shaderLayout);
695
- device.pushErrorScope("validation");
696
- const bindGroup = device.createBindGroup({
697
- layout: bindGroupLayout,
698
- entries
699
- });
700
- device.popErrorScope().then((error) => {
701
- if (error) {
702
- import_core8.log.error(`bindGroup creation: ${error.message}`, bindGroup)();
703
- }
704
- });
705
- return bindGroup;
706
- }
707
- function getShaderLayoutBinding(shaderLayout, bindingName, options) {
708
- const bindingLayout = shaderLayout.bindings.find((binding) => binding.name === bindingName || `${binding.name.toLocaleLowerCase()}uniforms` === bindingName.toLocaleLowerCase());
709
- if (!bindingLayout && !(options == null ? void 0 : options.ignoreWarnings)) {
710
- import_core8.log.warn(`Binding ${bindingName} not set: Not found in shader layout.`)();
711
- }
712
- return bindingLayout || null;
713
- }
714
- function getBindGroupEntries(bindings, shaderLayout) {
715
- const entries = [];
716
- for (const [bindingName, value] of Object.entries(bindings)) {
717
- let bindingLayout = getShaderLayoutBinding(shaderLayout, bindingName);
718
- if (bindingLayout) {
719
- const entry = getBindGroupEntry(value, bindingLayout.location);
720
- if (entry) {
721
- entries.push(entry);
722
- }
723
- }
724
- if (value instanceof import_core8.Texture) {
725
- bindingLayout = getShaderLayoutBinding(shaderLayout, `${bindingName}Sampler`, {
726
- ignoreWarnings: true
727
- });
728
- if (bindingLayout) {
729
- const entry = getBindGroupEntry(value, bindingLayout.location, { sampler: true });
730
- if (entry) {
731
- entries.push(entry);
732
- }
733
- }
734
- }
735
- }
736
- return entries;
737
- }
738
- function getBindGroupEntry(binding, index, options) {
739
- if (binding instanceof import_core8.Buffer) {
740
- return {
741
- binding: index,
742
- resource: {
743
- buffer: binding.handle
744
- }
745
- };
746
- }
747
- if (binding instanceof import_core8.Sampler) {
748
- return {
749
- binding: index,
750
- resource: binding.handle
751
- };
752
- }
753
- if (binding instanceof import_core8.Texture) {
754
- if (options == null ? void 0 : options.sampler) {
755
- return {
756
- binding: index,
757
- resource: binding.sampler.handle
758
- };
759
- }
760
- return {
761
- binding: index,
762
- resource: binding.view.handle
763
- };
764
- }
765
- import_core8.log.warn(`invalid binding ${name}`, binding);
766
- return null;
767
- }
768
- var import_core8;
769
- var init_get_bind_group = __esm({
770
- "dist/adapter/helpers/get-bind-group.js"() {
771
- "use strict";
772
- import_core8 = require("@luma.gl/core");
773
- }
774
- });
775
-
776
951
  // dist/adapter/helpers/get-vertex-buffer-layout.js
777
952
  function getWebGPUVertexFormat(format) {
778
953
  if (format.endsWith("-webgl")) {
@@ -780,9 +955,10 @@ function getWebGPUVertexFormat(format) {
780
955
  }
781
956
  return format;
782
957
  }
783
- function getVertexBufferLayout(shaderLayout, bufferLayout) {
958
+ function getVertexBufferLayout(shaderLayout, bufferLayout, options) {
784
959
  const vertexBufferLayouts = [];
785
960
  const usedAttributes = /* @__PURE__ */ new Set();
961
+ const shaderAttributes = shaderLayout.attributes || [];
786
962
  for (const mapping of bufferLayout) {
787
963
  const vertexAttributes = [];
788
964
  let stepMode = "vertex";
@@ -791,7 +967,7 @@ function getVertexBufferLayout(shaderLayout, bufferLayout) {
791
967
  if (mapping.attributes) {
792
968
  for (const attributeMapping of mapping.attributes) {
793
969
  const attributeName = attributeMapping.attribute;
794
- const attributeLayout = findAttributeLayout(shaderLayout, attributeName, usedAttributes);
970
+ const attributeLayout = findAttributeLayout(shaderLayout, attributeName, usedAttributes, options);
795
971
  const location = attributeLayout == null ? void 0 : attributeLayout.location;
796
972
  format = attributeMapping.format || mapping.format;
797
973
  stepMode = (attributeLayout == null ? void 0 : attributeLayout.stepMode) || ((attributeLayout == null ? void 0 : attributeLayout.name.startsWith("instance")) ? "instance" : "vertex");
@@ -800,14 +976,14 @@ function getVertexBufferLayout(shaderLayout, bufferLayout) {
800
976
  offset: attributeMapping.byteOffset,
801
977
  shaderLocation: location
802
978
  });
803
- byteStride += (0, import_core9.getVertexFormatInfo)(format).byteLength;
979
+ byteStride += import_core8.vertexFormatDecoder.getVertexFormatInfo(format).byteLength;
804
980
  }
805
981
  } else {
806
- const attributeLayout = findAttributeLayout(shaderLayout, mapping.name, usedAttributes);
982
+ const attributeLayout = findAttributeLayout(shaderLayout, mapping.name, usedAttributes, options);
807
983
  if (!attributeLayout) {
808
984
  continue;
809
985
  }
810
- byteStride = (0, import_core9.getVertexFormatInfo)(format).byteLength;
986
+ byteStride = import_core8.vertexFormatDecoder.getVertexFormatInfo(format).byteLength;
811
987
  stepMode = attributeLayout.stepMode || (attributeLayout.name.startsWith("instance") ? "instance" : "vertex");
812
988
  vertexAttributes.push({
813
989
  format: getWebGPUVertexFormat(format),
@@ -822,10 +998,10 @@ function getVertexBufferLayout(shaderLayout, bufferLayout) {
822
998
  attributes: vertexAttributes
823
999
  });
824
1000
  }
825
- for (const attribute of shaderLayout.attributes) {
1001
+ for (const attribute of shaderAttributes) {
826
1002
  if (!usedAttributes.has(attribute.name)) {
827
1003
  vertexBufferLayouts.push({
828
- arrayStride: (0, import_core9.getVertexFormatInfo)("float32x3").byteLength,
1004
+ arrayStride: import_core8.vertexFormatDecoder.getVertexFormatInfo("float32x3").byteLength,
829
1005
  stepMode: attribute.stepMode || (attribute.name.startsWith("instance") ? "instance" : "vertex"),
830
1006
  attributes: [
831
1007
  {
@@ -844,59 +1020,74 @@ function getVertexBufferLayout(shaderLayout, bufferLayout) {
844
1020
  });
845
1021
  return vertexBufferLayouts;
846
1022
  }
847
- function findAttributeLayout(shaderLayout, name2, attributeNames) {
848
- const attribute = shaderLayout.attributes.find((attribute_) => attribute_.name === name2);
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);
849
1026
  if (!attribute) {
850
- import_core9.log.warn(`Supplied attribute not present in shader layout: ${name2}`)();
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.`)();
851
1029
  return null;
852
1030
  }
853
1031
  if (attributeNames) {
854
- if (attributeNames.has(name2)) {
855
- throw new Error(`Found multiple entries for attribute: ${name2}`);
1032
+ if (attributeNames.has(name)) {
1033
+ throw new Error(`Found multiple entries for attribute: ${name}`);
856
1034
  }
857
- attributeNames.add(name2);
1035
+ attributeNames.add(name);
858
1036
  }
859
1037
  return attribute;
860
1038
  }
861
- var import_core9;
1039
+ var import_core8;
862
1040
  var init_get_vertex_buffer_layout = __esm({
863
1041
  "dist/adapter/helpers/get-vertex-buffer-layout.js"() {
864
1042
  "use strict";
865
- import_core9 = require("@luma.gl/core");
1043
+ import_core8 = require("@luma.gl/core");
866
1044
  }
867
1045
  });
868
1046
 
869
1047
  // dist/adapter/resources/webgpu-render-pipeline.js
870
- 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;
871
1058
  var init_webgpu_render_pipeline = __esm({
872
1059
  "dist/adapter/resources/webgpu-render-pipeline.js"() {
873
1060
  "use strict";
874
- import_core10 = require("@luma.gl/core");
1061
+ import_core9 = require("@luma.gl/core");
875
1062
  init_webgpu_parameters();
876
1063
  init_convert_texture_format();
877
- init_get_bind_group();
878
1064
  init_get_vertex_buffer_layout();
879
- WebGPURenderPipeline = class extends import_core10.RenderPipeline {
1065
+ WebGPURenderPipeline = class extends import_core9.RenderPipeline {
880
1066
  device;
881
1067
  handle;
1068
+ descriptor;
882
1069
  vs;
883
1070
  fs = null;
884
- /** For internal use to create BindGroups */
885
- _bindings;
886
- _bindGroupLayout = null;
887
- _bindGroup = null;
1071
+ /** Compatibility path for direct pipeline.setBindings() usage */
1072
+ _bindingsByGroup;
1073
+ _bindGroupCacheKeysByGroup = {};
888
1074
  get [Symbol.toStringTag]() {
889
1075
  return "WebGPURenderPipeline";
890
1076
  }
891
1077
  constructor(device, props) {
892
1078
  super(device, props);
893
1079
  this.device = device;
1080
+ this.shaderLayout ||= this.device.getShaderLayout(props.vs.source) || {
1081
+ attributes: [],
1082
+ bindings: []
1083
+ };
894
1084
  this.handle = this.props.handle;
1085
+ let descriptor = null;
895
1086
  if (!this.handle) {
896
- const descriptor = this._getRenderPipelineDescriptor();
897
- import_core10.log.groupCollapsed(1, `new WebGPURenderPipeline(${this.id})`)();
898
- import_core10.log.probe(1, JSON.stringify(descriptor, null, 2))();
899
- 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)();
900
1091
  this.device.pushErrorScope("validation");
901
1092
  this.handle = this.device.handle.createRenderPipeline(descriptor);
902
1093
  this.device.popErrorScope((error) => {
@@ -905,29 +1096,40 @@ var init_webgpu_render_pipeline = __esm({
905
1096
  this.device.debug();
906
1097
  });
907
1098
  }
1099
+ this.descriptor = descriptor;
908
1100
  this.handle.label = this.props.id;
909
1101
  this.vs = props.vs;
910
1102
  this.fs = props.fs;
911
- this._bindings = { ...this.props.bindings };
1103
+ this._bindingsByGroup = props.bindGroups || (0, import_core9.normalizeBindingsByGroup)(this.shaderLayout, props.bindings);
1104
+ this._bindGroupCacheKeysByGroup = createBindGroupCacheKeys(this._bindingsByGroup);
912
1105
  }
913
1106
  destroy() {
914
1107
  this.handle = null;
915
1108
  }
916
1109
  /**
917
- * @todo Use renderpass.setBindings() ?
918
- * @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.
919
1112
  */
920
1113
  setBindings(bindings) {
921
- for (const [name2, binding] of Object.entries(bindings)) {
922
- if (this._bindings[name2] !== binding) {
923
- 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
+ }
924
1126
  }
925
1127
  }
926
- Object.assign(this._bindings, bindings);
927
1128
  }
928
1129
  /** @todo - should this be moved to renderpass? */
929
1130
  draw(options) {
930
1131
  const webgpuRenderPass = options.renderPass;
1132
+ const instanceCount = options.instanceCount && options.instanceCount > 0 ? options.instanceCount : 1;
931
1133
  this.device.pushErrorScope("validation");
932
1134
  webgpuRenderPass.handle.setPipeline(this.handle);
933
1135
  this.device.popErrorScope((error) => {
@@ -935,32 +1137,27 @@ var init_webgpu_render_pipeline = __esm({
935
1137
  "${error.message}"`), this)();
936
1138
  this.device.debug();
937
1139
  });
938
- const bindGroup = this._getBindGroup();
939
- if (bindGroup) {
940
- 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
+ }
941
1146
  }
942
1147
  options.vertexArray.bindBeforeRender(options.renderPass);
943
1148
  if (options.indexCount) {
944
- 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);
945
1150
  } else {
946
- webgpuRenderPass.handle.draw(
947
- options.vertexCount || 0,
948
- options.instanceCount || 1,
949
- // If 0, nothing will be drawn
950
- options.firstInstance
951
- );
1151
+ webgpuRenderPass.handle.draw(options.vertexCount || 0, instanceCount, options.firstVertex || 0, options.firstInstance || 0);
952
1152
  }
953
1153
  options.vertexArray.unbindAfterRender(options.renderPass);
954
1154
  return true;
955
1155
  }
956
- /** Return a bind group created by setBindings */
957
- _getBindGroup() {
958
- if (this.shaderLayout.bindings.length === 0) {
959
- return null;
960
- }
961
- this._bindGroupLayout = this._bindGroupLayout || this.handle.getBindGroupLayout(0);
962
- this._bindGroup = this._bindGroup || getBindGroup(this.device.handle, this._bindGroupLayout, this.shaderLayout, this._bindings);
963
- return this._bindGroup;
1156
+ _getBindingsByGroupWebGPU() {
1157
+ return this._bindingsByGroup;
1158
+ }
1159
+ _getBindGroupCacheKeysWebGPU() {
1160
+ return this._bindGroupCacheKeysByGroup;
964
1161
  }
965
1162
  /**
966
1163
  * Populate the complex WebGPU GPURenderPipelineDescriptor
@@ -969,7 +1166,9 @@ var init_webgpu_render_pipeline = __esm({
969
1166
  const vertex = {
970
1167
  module: this.props.vs.handle,
971
1168
  entryPoint: this.props.vertexEntryPoint || "main",
972
- buffers: getVertexBufferLayout(this.shaderLayout, this.props.bufferLayout)
1169
+ buffers: getVertexBufferLayout(this.shaderLayout, this.props.bufferLayout, {
1170
+ pipelineId: this.id
1171
+ })
973
1172
  };
974
1173
  const targets = [];
975
1174
  if (this.props.colorAttachmentFormats) {
@@ -1009,12 +1208,12 @@ var init_webgpu_render_pipeline = __esm({
1009
1208
  });
1010
1209
 
1011
1210
  // dist/adapter/resources/webgpu-framebuffer.js
1012
- var import_core11, WebGPUFramebuffer;
1211
+ var import_core10, WebGPUFramebuffer;
1013
1212
  var init_webgpu_framebuffer = __esm({
1014
1213
  "dist/adapter/resources/webgpu-framebuffer.js"() {
1015
1214
  "use strict";
1016
- import_core11 = require("@luma.gl/core");
1017
- WebGPUFramebuffer = class extends import_core11.Framebuffer {
1215
+ import_core10 = require("@luma.gl/core");
1216
+ WebGPUFramebuffer = class extends import_core10.Framebuffer {
1018
1217
  device;
1019
1218
  handle = null;
1020
1219
  colorAttachments = [];
@@ -1026,25 +1225,37 @@ var init_webgpu_framebuffer = __esm({
1026
1225
  }
1027
1226
  updateAttachments() {
1028
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
+ }
1029
1243
  };
1030
1244
  }
1031
1245
  });
1032
1246
 
1033
1247
  // dist/adapter/resources/webgpu-compute-pipeline.js
1034
- var import_core12, WebGPUComputePipeline;
1248
+ var import_core11, EMPTY_BIND_GROUPS, WebGPUComputePipeline;
1035
1249
  var init_webgpu_compute_pipeline = __esm({
1036
1250
  "dist/adapter/resources/webgpu-compute-pipeline.js"() {
1037
1251
  "use strict";
1038
- import_core12 = require("@luma.gl/core");
1039
- init_get_bind_group();
1040
- WebGPUComputePipeline = class extends import_core12.ComputePipeline {
1252
+ import_core11 = require("@luma.gl/core");
1253
+ EMPTY_BIND_GROUPS = {};
1254
+ WebGPUComputePipeline = class extends import_core11.ComputePipeline {
1041
1255
  device;
1042
1256
  handle;
1043
- /** For internal use to create BindGroups */
1044
- _bindGroupLayout = null;
1045
- _bindGroup = null;
1046
- /** For internal use to create BindGroups */
1047
- _bindings = {};
1257
+ _bindingsByGroup;
1258
+ _bindGroupCacheKeysByGroup;
1048
1259
  constructor(device, props) {
1049
1260
  super(device, props);
1050
1261
  this.device = device;
@@ -1058,34 +1269,53 @@ var init_webgpu_compute_pipeline = __esm({
1058
1269
  },
1059
1270
  layout: "auto"
1060
1271
  });
1272
+ this._bindingsByGroup = EMPTY_BIND_GROUPS;
1273
+ this._bindGroupCacheKeysByGroup = {};
1061
1274
  }
1062
1275
  /**
1063
1276
  * @todo Use renderpass.setBindings() ?
1064
1277
  * @todo Do we want to expose BindGroups in the API and remove this?
1065
1278
  */
1066
1279
  setBindings(bindings) {
1067
- 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
+ }
1294
+ }
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);
1068
1298
  }
1069
- /** Return a bind group created by setBindings */
1070
- _getBindGroup() {
1071
- this._bindGroupLayout = this._bindGroupLayout || this.handle.getBindGroupLayout(0);
1072
- this._bindGroup = this._bindGroup || getBindGroup(this.device.handle, this._bindGroupLayout, this.shaderLayout, this._bindings);
1073
- return this._bindGroup;
1299
+ _getBindingsByGroupWebGPU() {
1300
+ return this._bindingsByGroup;
1301
+ }
1302
+ _getBindGroupCacheKeysWebGPU() {
1303
+ return this._bindGroupCacheKeysByGroup;
1074
1304
  }
1075
1305
  };
1076
1306
  }
1077
1307
  });
1078
1308
 
1079
1309
  // dist/adapter/resources/webgpu-vertex-array.js
1080
- var import_core13, import_env, WebGPUVertexArray;
1310
+ var import_core12, import_env, WebGPUVertexArray;
1081
1311
  var init_webgpu_vertex_array = __esm({
1082
1312
  "dist/adapter/resources/webgpu-vertex-array.js"() {
1083
1313
  "use strict";
1084
- import_core13 = require("@luma.gl/core");
1314
+ import_core12 = require("@luma.gl/core");
1085
1315
  import_env = require("@probe.gl/env");
1086
- WebGPUVertexArray = class extends import_core13.VertexArray {
1316
+ WebGPUVertexArray = class extends import_core12.VertexArray {
1087
1317
  get [Symbol.toStringTag]() {
1088
- return "WebGPUVertexArray";
1318
+ return "VertexArray";
1089
1319
  }
1090
1320
  device;
1091
1321
  /** Vertex Array is just a helper class under WebGPU */
@@ -1112,7 +1342,7 @@ var init_webgpu_vertex_array = __esm({
1112
1342
  const webgpuRenderPass = renderPass;
1113
1343
  const webgpuIndexBuffer = this.indexBuffer;
1114
1344
  if (webgpuIndexBuffer == null ? void 0 : webgpuIndexBuffer.handle) {
1115
- 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)();
1116
1346
  webgpuRenderPass.handle.setIndexBuffer(
1117
1347
  webgpuIndexBuffer == null ? void 0 : webgpuIndexBuffer.handle,
1118
1348
  // @ts-expect-error TODO - we must enforce type
@@ -1122,7 +1352,7 @@ var init_webgpu_vertex_array = __esm({
1122
1352
  for (let location = 0; location < this.maxVertexAttributes; location++) {
1123
1353
  const webgpuBuffer = this.attributes[location];
1124
1354
  if (webgpuBuffer == null ? void 0 : webgpuBuffer.handle) {
1125
- 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)();
1126
1356
  webgpuRenderPass.handle.setVertexBuffer(location, webgpuBuffer == null ? void 0 : webgpuBuffer.handle);
1127
1357
  }
1128
1358
  }
@@ -1142,16 +1372,19 @@ var init_webgpu_vertex_array = __esm({
1142
1372
  });
1143
1373
 
1144
1374
  // dist/adapter/webgpu-canvas-context.js
1145
- var import_core14, WebGPUCanvasContext;
1375
+ var import_core13, WebGPUCanvasContext;
1146
1376
  var init_webgpu_canvas_context = __esm({
1147
1377
  "dist/adapter/webgpu-canvas-context.js"() {
1148
1378
  "use strict";
1149
- import_core14 = require("@luma.gl/core");
1379
+ import_core13 = require("@luma.gl/core");
1150
1380
  init_webgpu_framebuffer();
1151
- WebGPUCanvasContext = class extends import_core14.CanvasContext {
1381
+ init_cpu_hotspot_profiler();
1382
+ WebGPUCanvasContext = class extends import_core13.CanvasContext {
1152
1383
  device;
1153
1384
  handle;
1385
+ colorAttachment = null;
1154
1386
  depthStencilAttachment = null;
1387
+ framebuffer = null;
1155
1388
  get [Symbol.toStringTag]() {
1156
1389
  return "WebGPUCanvasContext";
1157
1390
  }
@@ -1164,34 +1397,29 @@ var init_webgpu_canvas_context = __esm({
1164
1397
  this.device = device;
1165
1398
  this.handle = context;
1166
1399
  this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
1167
- this._updateDevice();
1400
+ this._configureDevice();
1401
+ this._startObservers();
1168
1402
  }
1169
1403
  /** Destroy any textures produced while configured and remove the context configuration. */
1170
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
+ }
1171
1417
  this.handle.unconfigure();
1172
1418
  super.destroy();
1173
1419
  }
1174
- /** Update framebuffer with properly resized "swap chain" texture views */
1175
- getCurrentFramebuffer(options = {
1176
- depthStencilFormat: "depth24plus"
1177
- }) {
1178
- const currentColorAttachment = this.getCurrentTexture();
1179
- if (currentColorAttachment.width !== this.drawingBufferWidth || currentColorAttachment.height !== this.drawingBufferHeight) {
1180
- const [oldWidth, oldHeight] = this.getDrawingBufferSize();
1181
- this.drawingBufferWidth = currentColorAttachment.width;
1182
- this.drawingBufferHeight = currentColorAttachment.height;
1183
- import_core14.log.log(1, `${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`)();
1184
- }
1185
- if (options == null ? void 0 : options.depthStencilFormat) {
1186
- this._createDepthStencilAttachment(options == null ? void 0 : options.depthStencilFormat);
1187
- }
1188
- return new WebGPUFramebuffer(this.device, {
1189
- colorAttachments: [currentColorAttachment],
1190
- depthStencilAttachment: this.depthStencilAttachment
1191
- });
1192
- }
1193
1420
  // IMPLEMENTATION OF ABSTRACT METHODS
1194
- _updateDevice() {
1421
+ /** @see https://www.w3.org/TR/webgpu/#canvas-configuration */
1422
+ _configureDevice() {
1195
1423
  if (this.depthStencilAttachment) {
1196
1424
  this.depthStencilAttachment.destroy();
1197
1425
  this.depthStencilAttachment = null;
@@ -1204,24 +1432,81 @@ var init_webgpu_canvas_context = __esm({
1204
1432
  colorSpace: this.props.colorSpace,
1205
1433
  alphaMode: this.props.alphaMode
1206
1434
  });
1435
+ this._createDepthStencilAttachment(this.device.preferredDepthFormat);
1436
+ }
1437
+ /** Update framebuffer with properly resized "swap chain" texture views */
1438
+ _getCurrentFramebuffer(options = {
1439
+ depthStencilFormat: "depth24plus"
1440
+ }) {
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
+ }
1207
1472
  }
1473
+ // PRIMARY METHODS
1208
1474
  /** Wrap the current canvas context texture in a luma.gl texture */
1209
- getCurrentTexture() {
1475
+ _getCurrentTexture() {
1476
+ const profiler = getCpuHotspotProfiler(this.device);
1477
+ const currentTextureStartTime = profiler ? getTimestamp() : 0;
1210
1478
  const handle = this.handle.getCurrentTexture();
1211
- return this.device.createTexture({
1212
- 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, {
1213
1494
  handle,
1214
1495
  format: this.device.preferredColorFormat,
1215
1496
  width: handle.width,
1216
1497
  height: handle.height
1217
1498
  });
1499
+ return this.colorAttachment;
1218
1500
  }
1219
1501
  /** We build render targets on demand (i.e. not when size changes but when about to render) */
1220
1502
  _createDepthStencilAttachment(depthStencilFormat) {
1221
- 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();
1222
1507
  this.depthStencilAttachment = this.device.createTexture({
1223
1508
  id: `${this.id}#depth-stencil-texture`,
1224
- usage: import_core14.Texture.RENDER_ATTACHMENT,
1509
+ usage: import_core13.Texture.RENDER_ATTACHMENT,
1225
1510
  format: depthStencilFormat,
1226
1511
  width: this.drawingBufferWidth,
1227
1512
  height: this.drawingBufferHeight
@@ -1233,76 +1518,246 @@ var init_webgpu_canvas_context = __esm({
1233
1518
  }
1234
1519
  });
1235
1520
 
1236
- // dist/adapter/resources/webgpu-command-buffer.js
1237
- var import_core15, WebGPUCommandBuffer;
1238
- var init_webgpu_command_buffer = __esm({
1239
- "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"() {
1240
1525
  "use strict";
1241
- import_core15 = require("@luma.gl/core");
1242
- 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 {
1243
1530
  device;
1244
1531
  handle;
1245
- constructor(commandEncoder, props) {
1246
- super(commandEncoder.device, {});
1247
- this.device = commandEncoder.device;
1248
- this.handle = this.props.handle || commandEncoder.handle.finish({
1249
- label: (props == null ? void 0 : props.id) || "unnamed-command-buffer"
1250
- });
1532
+ colorAttachment = null;
1533
+ depthStencilAttachment = null;
1534
+ framebuffer = null;
1535
+ get [Symbol.toStringTag]() {
1536
+ return "WebGPUPresentationContext";
1251
1537
  }
1252
- };
1253
- }
1254
- });
1255
-
1256
- // dist/adapter/resources/webgpu-render-pass.js
1257
- function convertColor(color) {
1258
- return { r: color[0], g: color[1], b: color[2], a: color[3] };
1259
- }
1260
- var import_core16, WebGPURenderPass;
1261
- var init_webgpu_render_pass = __esm({
1262
- "dist/adapter/resources/webgpu-render-pass.js"() {
1263
- "use strict";
1264
- import_core16 = require("@luma.gl/core");
1265
- WebGPURenderPass = class extends import_core16.RenderPass {
1266
- device;
1267
- handle;
1268
- /** Active pipeline */
1269
- pipeline = null;
1270
1538
  constructor(device, props = {}) {
1271
- super(device, props);
1272
- this.device = device;
1273
- const framebuffer = props.framebuffer || device.getCanvasContext().getCurrentFramebuffer();
1274
- const renderPassDescriptor = this.getRenderPassDescriptor(framebuffer);
1275
- const webgpuQuerySet = props.timestampQuerySet;
1276
- if (webgpuQuerySet) {
1277
- renderPassDescriptor.occlusionQuerySet = webgpuQuerySet.handle;
1278
- }
1279
- if (device.features.has("timestamp-query")) {
1280
- const webgpuTSQuerySet = props.timestampQuerySet;
1281
- renderPassDescriptor.timestampWrites = webgpuTSQuerySet ? {
1282
- querySet: webgpuTSQuerySet.handle,
1283
- beginningOfPassWriteIndex: props.beginTimestampIndex,
1284
- endOfPassWriteIndex: props.endTimestampIndex
1285
- } : void 0;
1286
- }
1287
- if (!device.commandEncoder) {
1288
- throw new Error("commandEncoder not available");
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`);
1289
1544
  }
1290
- this.device.pushErrorScope("validation");
1291
- this.handle = this.props.handle || device.commandEncoder.handle.beginRenderPass(renderPassDescriptor);
1292
- this.device.popErrorScope((error) => {
1293
- this.device.reportError(new Error(`${this} creation failed:
1294
- "${error.message}"`), this)();
1295
- this.device.debug();
1545
+ this.device = device;
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;
1555
+ }
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
1296
1580
  });
1297
- this.handle.label = this.props.id;
1298
- import_core16.log.groupCollapsed(3, `new WebGPURenderPass(${this.id})`)();
1299
- import_core16.log.probe(3, JSON.stringify(renderPassDescriptor, null, 2))();
1300
- import_core16.log.groupEnd(3)();
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"
1675
+ });
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
+ }
1301
1751
  }
1302
1752
  destroy() {
1753
+ this.destroyResource();
1303
1754
  }
1304
1755
  end() {
1756
+ if (this.destroyed) {
1757
+ return;
1758
+ }
1305
1759
  this.handle.end();
1760
+ this.destroy();
1306
1761
  }
1307
1762
  setPipeline(pipeline) {
1308
1763
  this.pipeline = pipeline;
@@ -1316,11 +1771,12 @@ var init_webgpu_render_pass = __esm({
1316
1771
  }
1317
1772
  /** Sets an array of bindings (uniform buffers, samplers, textures, ...) */
1318
1773
  setBindings(bindings) {
1319
- var _a, _b;
1320
- (_a = this.pipeline) == null ? void 0 : _a.setBindings(bindings);
1321
- const bindGroup = (_b = this.pipeline) == null ? void 0 : _b._getBindGroup();
1322
- if (bindGroup) {
1323
- 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
+ }
1324
1780
  }
1325
1781
  }
1326
1782
  setIndexBuffer(buffer, indexFormat, offset = 0, size) {
@@ -1427,44 +1883,60 @@ var init_webgpu_compute_pass = __esm({
1427
1883
  device;
1428
1884
  handle;
1429
1885
  _webgpuPipeline = null;
1430
- constructor(device, props) {
1886
+ constructor(device, props = {}, commandEncoder = device.commandEncoder.handle) {
1431
1887
  super(device, props);
1432
1888
  this.device = device;
1889
+ const { props: computePassProps } = this;
1433
1890
  let timestampWrites;
1434
- if (device.features.has("timestamp-query")) {
1435
- const webgpuQuerySet = props.timestampQuerySet;
1891
+ if (computePassProps.timestampQuerySet) {
1892
+ const webgpuQuerySet = computePassProps.timestampQuerySet;
1436
1893
  if (webgpuQuerySet) {
1894
+ webgpuQuerySet._invalidateResults();
1437
1895
  timestampWrites = {
1438
1896
  querySet: webgpuQuerySet.handle,
1439
- beginningOfPassWriteIndex: props.beginTimestampIndex,
1440
- endOfPassWriteIndex: props.endTimestampIndex
1897
+ beginningOfPassWriteIndex: computePassProps.beginTimestampIndex,
1898
+ endOfPassWriteIndex: computePassProps.endTimestampIndex
1441
1899
  };
1442
1900
  }
1443
1901
  }
1444
- this.handle = this.props.handle || device.commandEncoder.handle.beginComputePass({
1902
+ this.handle = this.props.handle || commandEncoder.beginComputePass({
1445
1903
  label: this.props.id,
1446
1904
  timestampWrites
1447
1905
  });
1448
1906
  }
1449
1907
  /** @note no WebGPU destroy method, just gc */
1450
1908
  destroy() {
1909
+ this.destroyResource();
1451
1910
  }
1452
1911
  end() {
1912
+ if (this.destroyed) {
1913
+ return;
1914
+ }
1453
1915
  this.handle.end();
1916
+ this.destroy();
1454
1917
  }
1455
1918
  setPipeline(pipeline) {
1456
1919
  const wgpuPipeline = pipeline;
1457
1920
  this.handle.setPipeline(wgpuPipeline.handle);
1458
1921
  this._webgpuPipeline = wgpuPipeline;
1459
- 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
+ }
1460
1928
  }
1461
1929
  /**
1462
1930
  * Sets an array of bindings (uniform buffers, samplers, textures, ...)
1463
1931
  * TODO - still some API confusion - does this method go here or on the pipeline?
1464
1932
  */
1465
1933
  setBindings(bindings) {
1466
- const bindGroup = this._webgpuPipeline._getBindGroup();
1467
- 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
+ }
1468
1940
  }
1469
1941
  /**
1470
1942
  * Dispatch work to be performed with the current ComputePipeline.
@@ -1522,6 +1994,7 @@ var init_webgpu_command_encoder = __esm({
1522
1994
  this.handle.label = this.props.id;
1523
1995
  }
1524
1996
  destroy() {
1997
+ this.destroyResource();
1525
1998
  }
1526
1999
  finish(props) {
1527
2000
  this.device.pushErrorScope("validation");
@@ -1533,49 +2006,105 @@ var init_webgpu_command_encoder = __esm({
1533
2006
  this.device.reportError(new Error(message), this)();
1534
2007
  this.device.debug();
1535
2008
  });
2009
+ this.destroy();
1536
2010
  return commandBuffer;
1537
2011
  }
1538
2012
  /**
1539
2013
  * Allows a render pass to begin against a canvas context
1540
2014
  * @todo need to support a "Framebuffer" equivalent (aka preconfigured RenderPassDescriptors?).
1541
2015
  */
1542
- beginRenderPass(props) {
1543
- return new WebGPURenderPass(this.device, props);
2016
+ beginRenderPass(props = {}) {
2017
+ return new WebGPURenderPass(this.device, this._applyTimeProfilingToPassProps(props), this.handle);
1544
2018
  }
1545
- beginComputePass(props) {
1546
- return new WebGPUComputePass(this.device, props);
2019
+ beginComputePass(props = {}) {
2020
+ return new WebGPUComputePass(this.device, this._applyTimeProfilingToPassProps(props), this.handle);
1547
2021
  }
1548
2022
  // beginRenderPass(GPURenderPassDescriptor descriptor): GPURenderPassEncoder;
1549
2023
  // beginComputePass(optional GPUComputePassDescriptor descriptor = {}): GPUComputePassEncoder;
1550
2024
  copyBufferToBuffer(options) {
1551
2025
  const webgpuSourceBuffer = options.sourceBuffer;
1552
- const WebGPUDestinationBuffer = options.destinationBuffer;
1553
- 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);
1554
2028
  }
1555
2029
  copyBufferToTexture(options) {
1556
- var _a, _b, _c;
1557
2030
  const webgpuSourceBuffer = options.sourceBuffer;
1558
- const WebGPUDestinationTexture = options.destinationTexture;
2031
+ const webgpuDestinationTexture = options.destinationTexture;
2032
+ const copyOrigin = options.origin ?? [0, 0, 0];
2033
+ const copySize = options.size;
1559
2034
  this.handle.copyBufferToTexture({
1560
2035
  buffer: webgpuSourceBuffer.handle,
1561
- offset: options.offset ?? 0,
2036
+ offset: options.byteOffset ?? 0,
1562
2037
  bytesPerRow: options.bytesPerRow,
1563
2038
  rowsPerImage: options.rowsPerImage
1564
2039
  }, {
1565
- texture: WebGPUDestinationTexture.handle,
2040
+ texture: webgpuDestinationTexture.handle,
1566
2041
  mipLevel: options.mipLevel ?? 0,
1567
- origin: options.origin ?? {}
1568
- // 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
1569
2048
  }, {
1570
- // @ts-ignore
1571
- width: (_a = options.extent) == null ? void 0 : _a[0],
1572
- height: (_b = options.extent) == null ? void 0 : _b[1],
1573
- depthOrArrayLayers: (_c = options.extent) == null ? void 0 : _c[2]
2049
+ width: copySize[0],
2050
+ height: copySize[1],
2051
+ depthOrArrayLayers: copySize[2]
1574
2052
  });
1575
2053
  }
1576
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);
1577
2070
  }
1578
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
+ });
1579
2108
  }
1580
2109
  pushDebugGroup(groupLabel) {
1581
2110
  this.handle.pushDebugGroup(groupLabel);
@@ -1591,6 +2120,21 @@ var init_webgpu_command_encoder = __esm({
1591
2120
  const webgpuBuffer = destination;
1592
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);
1593
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
+ }
1594
2138
  };
1595
2139
  }
1596
2140
  });
@@ -1601,9 +2145,15 @@ var init_webgpu_query_set = __esm({
1601
2145
  "dist/adapter/resources/webgpu-query-set.js"() {
1602
2146
  "use strict";
1603
2147
  import_core19 = require("@luma.gl/core");
2148
+ init_cpu_hotspot_profiler();
1604
2149
  WebGPUQuerySet = class extends import_core19.QuerySet {
1605
2150
  device;
1606
2151
  handle;
2152
+ _resolveBuffer = null;
2153
+ _readBuffer = null;
2154
+ _cachedResults = null;
2155
+ _readResultsPromise = null;
2156
+ _resultsPendingResolution = false;
1607
2157
  constructor(device, props) {
1608
2158
  super(device, props);
1609
2159
  this.device = device;
@@ -1615,8 +2165,133 @@ var init_webgpu_query_set = __esm({
1615
2165
  }
1616
2166
  destroy() {
1617
2167
  var _a;
1618
- (_a = this.handle) == null ? void 0 : _a.destroy();
1619
- 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;
1620
2295
  }
1621
2296
  };
1622
2297
  }
@@ -1634,27 +2309,22 @@ var init_webgpu_pipeline_layout = __esm({
1634
2309
  constructor(device, props) {
1635
2310
  super(device, props);
1636
2311
  this.device = device;
1637
- const bindGroupEntries = this.mapShaderLayoutToBindGroupEntries();
2312
+ const bindGroupEntriesByGroup = this.mapShaderLayoutToBindGroupEntriesByGroup();
1638
2313
  this.handle = this.device.handle.createPipelineLayout({
1639
2314
  label: (props == null ? void 0 : props.id) ?? "unnamed-pipeline-layout",
1640
- bindGroupLayouts: [
1641
- // TODO (kaapp): We can cache these to re-use them across
1642
- // layers, particularly if using a separate group for injected
1643
- // bindings (e.g. project/lighting)
1644
- this.device.handle.createBindGroupLayout({
1645
- label: "bind-group-layout",
1646
- entries: bindGroupEntries
1647
- })
1648
- ]
2315
+ bindGroupLayouts: bindGroupEntriesByGroup.map((entries, group) => this.device.handle.createBindGroupLayout({
2316
+ label: `bind-group-layout-${group}`,
2317
+ entries
2318
+ }))
1649
2319
  });
1650
2320
  }
1651
2321
  destroy() {
1652
2322
  this.handle = null;
1653
2323
  }
1654
- mapShaderLayoutToBindGroupEntries() {
1655
- const bindGroupEntries = [];
1656
- for (let i = 0; i < this.props.shaderLayout.bindings.length; i++) {
1657
- const binding = this.props.shaderLayout.bindings[i];
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 }, () => []);
2327
+ for (const binding of this.props.shaderLayout.bindings) {
1658
2328
  const bindingTypeInfo = {};
1659
2329
  switch (binding.type) {
1660
2330
  case "uniform": {
@@ -1710,13 +2380,13 @@ var init_webgpu_pipeline_layout = __esm({
1710
2380
  }
1711
2381
  }
1712
2382
  const VISIBILITY_ALL = GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE;
1713
- bindGroupEntries.push({
2383
+ bindGroupEntriesByGroup[binding.group].push({
1714
2384
  binding: binding.location,
1715
2385
  visibility: binding.visibility || VISIBILITY_ALL,
1716
2386
  ...bindingTypeInfo
1717
2387
  });
1718
2388
  }
1719
- return bindGroupEntries;
2389
+ return bindGroupEntriesByGroup;
1720
2390
  }
1721
2391
  };
1722
2392
  isStorageTextureBindingLayout = (maybe) => {
@@ -1725,16 +2395,734 @@ var init_webgpu_pipeline_layout = __esm({
1725
2395
  }
1726
2396
  });
1727
2397
 
2398
+ // dist/adapter/resources/webgpu-fence.js
2399
+ var import_core21, WebGPUFence;
2400
+ var init_webgpu_fence = __esm({
2401
+ "dist/adapter/resources/webgpu-fence.js"() {
2402
+ "use strict";
2403
+ import_core21 = require("@luma.gl/core");
2404
+ WebGPUFence = class extends import_core21.Fence {
2405
+ device;
2406
+ handle = null;
2407
+ signaled;
2408
+ _signaled = false;
2409
+ constructor(device, props = {}) {
2410
+ super(device, {});
2411
+ this.device = device;
2412
+ this.signaled = device.handle.queue.onSubmittedWorkDone().then(() => {
2413
+ this._signaled = true;
2414
+ }).catch((error) => {
2415
+ if (this.device.shouldIgnoreDroppedInstanceError(error)) {
2416
+ return;
2417
+ }
2418
+ throw error;
2419
+ });
2420
+ }
2421
+ isSignaled() {
2422
+ return this._signaled;
2423
+ }
2424
+ destroy() {
2425
+ }
2426
+ };
2427
+ }
2428
+ });
2429
+
2430
+ // dist/wgsl/get-shader-layout-wgsl.js
2431
+ function getShaderLayoutFromWGSL(source) {
2432
+ var _a;
2433
+ const shaderLayout = { attributes: [], bindings: [] };
2434
+ let parsedWGSL;
2435
+ try {
2436
+ parsedWGSL = parseWGSL(source);
2437
+ } catch (error) {
2438
+ import_core22.log.error(error.message)();
2439
+ return shaderLayout;
2440
+ }
2441
+ for (const uniform of parsedWGSL.uniforms) {
2442
+ const members = [];
2443
+ for (const attribute of ((_a = uniform.type) == null ? void 0 : _a.members) || []) {
2444
+ members.push({
2445
+ name: attribute.name,
2446
+ type: getType(attribute.type)
2447
+ });
2448
+ }
2449
+ shaderLayout.bindings.push({
2450
+ type: "uniform",
2451
+ name: uniform.name,
2452
+ group: uniform.group,
2453
+ location: uniform.binding,
2454
+ // @ts-expect-error TODO - unused for now but needs fixing
2455
+ members
2456
+ });
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
+ }
2466
+ for (const texture of parsedWGSL.textures) {
2467
+ const bindingDeclaration = {
2468
+ type: "texture",
2469
+ name: texture.name,
2470
+ group: texture.group,
2471
+ location: texture.binding,
2472
+ ...getTextureBindingFromReflect(texture)
2473
+ };
2474
+ shaderLayout.bindings.push(bindingDeclaration);
2475
+ }
2476
+ for (const sampler of parsedWGSL.samplers) {
2477
+ shaderLayout.bindings.push({
2478
+ type: "sampler",
2479
+ name: sampler.name,
2480
+ group: sampler.group,
2481
+ location: sampler.binding
2482
+ });
2483
+ }
2484
+ const vertex = parsedWGSL.entry.vertex[0];
2485
+ const attributeCount = (vertex == null ? void 0 : vertex.inputs.length) || 0;
2486
+ for (let i = 0; i < attributeCount; i++) {
2487
+ const wgslAttribute = vertex.inputs[i];
2488
+ if (wgslAttribute.locationType === "location") {
2489
+ const type = getType(wgslAttribute.type);
2490
+ shaderLayout.attributes.push({
2491
+ name: wgslAttribute.name,
2492
+ location: Number(wgslAttribute.location),
2493
+ type
2494
+ });
2495
+ }
2496
+ }
2497
+ return shaderLayout;
2498
+ }
2499
+ function getType(type) {
2500
+ return (type == null ? void 0 : type.format) ? `${type.name}<${type.format.name}>` : type.name;
2501
+ }
2502
+ function parseWGSL(source) {
2503
+ try {
2504
+ return new import_wgsl_reflect.WgslReflect(source);
2505
+ } catch (error) {
2506
+ if (error instanceof Error) {
2507
+ throw error;
2508
+ }
2509
+ let message = "WGSL parse error";
2510
+ if (typeof error === "object" && (error == null ? void 0 : error.message)) {
2511
+ message += `: ${error.message} `;
2512
+ }
2513
+ if (typeof error === "object" && (error == null ? void 0 : error.token)) {
2514
+ message += error.token.line || "";
2515
+ }
2516
+ throw new Error(message, { cause: error });
2517
+ }
2518
+ }
2519
+ function getTextureBindingFromReflect(v, opts) {
2520
+ var _a;
2521
+ if (v.resourceType !== import_wgsl_reflect.ResourceType.Texture) {
2522
+ throw new Error("Not a texture binding");
2523
+ }
2524
+ const typeName = v.type.name;
2525
+ const component = (_a = v.type.format) == null ? void 0 : _a.name;
2526
+ const viewDimension = typeName.includes("cube_array") ? "cube-array" : typeName.includes("cube") ? "cube" : typeName.includes("2d_array") ? "2d-array" : typeName.includes("3d") ? "3d" : typeName.includes("1d") ? "1d" : "2d";
2527
+ const multisampled = typeName === "texture_multisampled_2d";
2528
+ let sampleType;
2529
+ if (typeName.startsWith("texture_depth")) {
2530
+ sampleType = "depth";
2531
+ } else if (component === "i32") {
2532
+ sampleType = "sint";
2533
+ } else if (component === "u32") {
2534
+ sampleType = "uint";
2535
+ } else {
2536
+ sampleType = "float";
2537
+ }
2538
+ return { viewDimension, sampleType, multisampled };
2539
+ }
2540
+ var import_core22, import_wgsl_reflect;
2541
+ var init_get_shader_layout_wgsl = __esm({
2542
+ "dist/wgsl/get-shader-layout-wgsl.js"() {
2543
+ "use strict";
2544
+ import_core22 = require("@luma.gl/core");
2545
+ import_wgsl_reflect = require("wgsl_reflect");
2546
+ }
2547
+ });
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
+
1728
3108
  // dist/adapter/webgpu-device.js
1729
3109
  var webgpu_device_exports = {};
1730
3110
  __export(webgpu_device_exports, {
1731
3111
  WebGPUDevice: () => WebGPUDevice
1732
3112
  });
1733
- var import_core21, 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;
1734
3122
  var init_webgpu_device = __esm({
1735
3123
  "dist/adapter/webgpu-device.js"() {
1736
3124
  "use strict";
1737
- import_core21 = require("@luma.gl/core");
3125
+ import_core25 = require("@luma.gl/core");
1738
3126
  init_webgpu_buffer();
1739
3127
  init_webgpu_texture();
1740
3128
  init_webgpu_external_texture();
@@ -1745,10 +3133,16 @@ var init_webgpu_device = __esm({
1745
3133
  init_webgpu_compute_pipeline();
1746
3134
  init_webgpu_vertex_array();
1747
3135
  init_webgpu_canvas_context();
3136
+ init_webgpu_presentation_context();
1748
3137
  init_webgpu_command_encoder();
1749
3138
  init_webgpu_query_set();
1750
3139
  init_webgpu_pipeline_layout();
1751
- WebGPUDevice = class extends import_core21.Device {
3140
+ init_webgpu_fence();
3141
+ init_get_shader_layout_wgsl();
3142
+ init_generate_mipmaps_webgpu();
3143
+ init_get_bind_group();
3144
+ init_cpu_hotspot_profiler();
3145
+ WebGPUDevice = class extends import_core25.Device {
1752
3146
  /** The underlying WebGPU device */
1753
3147
  handle;
1754
3148
  /* The underlying WebGPU adapter */
@@ -1765,6 +3159,7 @@ var init_webgpu_device = __esm({
1765
3159
  lost;
1766
3160
  canvasContext = null;
1767
3161
  _isLost = false;
3162
+ _defaultSampler = null;
1768
3163
  commandEncoder;
1769
3164
  get [Symbol.toStringTag]() {
1770
3165
  return "WebGPUDevice";
@@ -1791,7 +3186,7 @@ var init_webgpu_device = __esm({
1791
3186
  this._isLost = true;
1792
3187
  resolve({ reason: "destroyed", message: lostInfo.message });
1793
3188
  });
1794
- const canvasContextProps = import_core21.Device._getCanvasContextProps(props);
3189
+ const canvasContextProps = import_core25.Device._getCanvasContextProps(props);
1795
3190
  if (canvasContextProps) {
1796
3191
  this.canvasContext = new WebGPUCanvasContext(this, this.adapter, canvasContextProps);
1797
3192
  }
@@ -1802,18 +3197,22 @@ var init_webgpu_device = __esm({
1802
3197
  // const {glsl = true} = props;
1803
3198
  // this.glslang = glsl && await loadGlslangModule();
1804
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;
1805
3204
  this.handle.destroy();
1806
3205
  }
1807
3206
  get isLost() {
1808
3207
  return this._isLost;
1809
3208
  }
3209
+ getShaderLayout(source) {
3210
+ return getShaderLayoutFromWGSL(source);
3211
+ }
1810
3212
  isVertexFormatSupported(format) {
1811
3213
  const info = this.getVertexFormatInfo(format);
1812
3214
  return !info.webglOnly;
1813
3215
  }
1814
- getTextureByteAlignment() {
1815
- return 1;
1816
- }
1817
3216
  createBuffer(props) {
1818
3217
  const newProps = this._normalizeBufferProps(props);
1819
3218
  return new WebGPUBuffer(this, newProps);
@@ -1830,6 +3229,12 @@ var init_webgpu_device = __esm({
1830
3229
  createSampler(props) {
1831
3230
  return new WebGPUSampler(this, props);
1832
3231
  }
3232
+ getDefaultSampler() {
3233
+ this._defaultSampler ||= new WebGPUSampler(this, {
3234
+ id: `${this.id}-default-sampler`
3235
+ });
3236
+ return this._defaultSampler;
3237
+ }
1833
3238
  createRenderPipeline(props) {
1834
3239
  return new WebGPURenderPipeline(this, props);
1835
3240
  }
@@ -1852,35 +3257,135 @@ var init_webgpu_device = __esm({
1852
3257
  createQuerySet(props) {
1853
3258
  return new WebGPUQuerySet(this, props);
1854
3259
  }
3260
+ createFence() {
3261
+ return new WebGPUFence(this);
3262
+ }
1855
3263
  createCanvasContext(props) {
1856
3264
  return new WebGPUCanvasContext(this, this.adapter, props);
1857
3265
  }
3266
+ createPresentationContext(props) {
3267
+ return new WebGPUPresentationContext(this, props);
3268
+ }
1858
3269
  createPipelineLayout(props) {
1859
3270
  return new WebGPUPipelineLayout(this, props);
1860
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
+ }
1861
3287
  submit(commandBuffer) {
3288
+ let submittedCommandEncoder = null;
1862
3289
  if (!commandBuffer) {
1863
- commandBuffer = this.commandEncoder.finish();
1864
- this.commandEncoder.destroy();
1865
- this.commandEncoder = this.createCommandEncoder({ id: `${this.id}-default-encoder` });
1866
- }
1867
- this.pushErrorScope("validation");
1868
- this.handle.queue.submit([commandBuffer.handle]);
1869
- this.popErrorScope((error) => {
1870
- this.reportError(new Error(`${this} command submission: ${error.message}`), this)();
1871
- 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()
1872
3351
  });
3352
+ return { submittedCommandEncoder, commandBuffer };
1873
3353
  }
1874
3354
  // WebGPU specific
1875
3355
  pushErrorScope(scope) {
3356
+ if (!this.props.debug) {
3357
+ return;
3358
+ }
3359
+ const profiler = getCpuHotspotProfiler(this);
3360
+ const startTime = profiler ? getTimestamp() : 0;
1876
3361
  this.handle.pushErrorScope(scope);
3362
+ if (profiler) {
3363
+ profiler.errorScopePushCount = (profiler.errorScopePushCount || 0) + 1;
3364
+ profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
3365
+ }
1877
3366
  }
1878
3367
  popErrorScope(handler) {
3368
+ if (!this.props.debug) {
3369
+ return;
3370
+ }
3371
+ const profiler = getCpuHotspotProfiler(this);
3372
+ const startTime = profiler ? getTimestamp() : 0;
1879
3373
  this.handle.popErrorScope().then((error) => {
1880
3374
  if (error) {
1881
3375
  handler(error);
1882
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();
1883
3384
  });
3385
+ if (profiler) {
3386
+ profiler.errorScopePopCount = (profiler.errorScopePopCount || 0) + 1;
3387
+ profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
3388
+ }
1884
3389
  }
1885
3390
  // PRIVATE METHODS
1886
3391
  _getInfo() {
@@ -1888,10 +3393,12 @@ var init_webgpu_device = __esm({
1888
3393
  const vendor = this.adapterInfo.vendor || this.adapter.__brand || "unknown";
1889
3394
  const renderer = driver || "";
1890
3395
  const version = driverVersion || "";
1891
- 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";
1892
3399
  const gpuArchitecture = this.adapterInfo.architecture || "unknown";
1893
3400
  const gpuBackend = this.adapterInfo.backend || "unknown";
1894
- const gpuType = (this.adapterInfo.type || "").split(" ")[0].toLowerCase() || "unknown";
3401
+ const gpuType = (this.adapterInfo.type || "").split(" ")[0].toLowerCase() || (softwareRenderer || fallback ? "cpu" : "unknown");
1895
3402
  return {
1896
3403
  type: "webgpu",
1897
3404
  vendor,
@@ -1901,10 +3408,15 @@ var init_webgpu_device = __esm({
1901
3408
  gpuType,
1902
3409
  gpuBackend,
1903
3410
  gpuArchitecture,
3411
+ fallback,
1904
3412
  shadingLanguage: "wgsl",
1905
3413
  shadingLanguageVersion: 100
1906
3414
  };
1907
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
+ }
1908
3420
  _getFeatures() {
1909
3421
  const features = new Set(this.handle.features);
1910
3422
  if (features.has("depth-clamping")) {
@@ -1914,8 +3426,13 @@ var init_webgpu_device = __esm({
1914
3426
  if (features.has("texture-compression-bc")) {
1915
3427
  features.add("texture-compression-bc5-webgl");
1916
3428
  }
3429
+ if (this.handle.features.has("chromium-experimental-norm16-texture-formats")) {
3430
+ features.add("norm16-renderable-webgl");
3431
+ }
3432
+ if (this.handle.features.has("chromium-experimental-snorm16-texture-formats")) {
3433
+ features.add("snorm16-renderable-webgl");
3434
+ }
1917
3435
  const WEBGPU_ALWAYS_FEATURES = [
1918
- "timer-query-webgl",
1919
3436
  "compilation-status-async-webgl",
1920
3437
  "float32-renderable-webgl",
1921
3438
  "float16-renderable-webgl",
@@ -1926,7 +3443,7 @@ var init_webgpu_device = __esm({
1926
3443
  for (const feature of WEBGPU_ALWAYS_FEATURES) {
1927
3444
  features.add(feature);
1928
3445
  }
1929
- return new import_core21.DeviceFeatures(Array.from(features), this.props._disabledFeatures);
3446
+ return new import_core25.DeviceFeatures(Array.from(features), this.props._disabledFeatures);
1930
3447
  }
1931
3448
  _getDeviceSpecificTextureFormatCapabilities(capabilities) {
1932
3449
  const { format } = capabilities;
@@ -1944,16 +3461,18 @@ var dist_exports = {};
1944
3461
  __export(dist_exports, {
1945
3462
  WebGPUBuffer: () => WebGPUBuffer,
1946
3463
  WebGPUDevice: () => WebGPUDevice,
3464
+ WebGPUFence: () => WebGPUFence,
1947
3465
  WebGPUSampler: () => WebGPUSampler,
1948
3466
  WebGPUShader: () => WebGPUShader,
1949
3467
  WebGPUTexture: () => WebGPUTexture,
3468
+ getShaderLayoutFromWGSL: () => getShaderLayoutFromWGSL,
1950
3469
  webgpuAdapter: () => webgpuAdapter
1951
3470
  });
1952
3471
  module.exports = __toCommonJS(dist_exports);
1953
3472
 
1954
3473
  // dist/adapter/webgpu-adapter.js
1955
- var import_core22 = require("@luma.gl/core");
1956
- var WebGPUAdapter = class extends import_core22.Adapter {
3474
+ var import_core26 = require("@luma.gl/core");
3475
+ var WebGPUAdapter = class extends import_core26.Adapter {
1957
3476
  /** type of device's created by this adapter */
1958
3477
  type = "webgpu";
1959
3478
  isSupported() {
@@ -1973,43 +3492,41 @@ var WebGPUAdapter = class extends import_core22.Adapter {
1973
3492
  if (!navigator.gpu) {
1974
3493
  throw new Error("WebGPU not available. Recent Chrome browsers should work.");
1975
3494
  }
1976
- import_core22.log.groupCollapsed(1, "WebGPUDevice created")();
1977
- try {
1978
- const adapter = await navigator.gpu.requestAdapter({
1979
- powerPreference: "high-performance"
1980
- // forceSoftware: false
1981
- });
1982
- if (!adapter) {
1983
- throw new Error("Failed to request WebGPU adapter");
1984
- }
1985
- const adapterInfo = adapter.info || // @ts-ignore
1986
- await ((_a = adapter.requestAdapterInfo) == null ? void 0 : _a.call(adapter));
1987
- import_core22.log.probe(2, "Adapter available", adapterInfo)();
1988
- const requiredFeatures = [];
1989
- const requiredLimits = {};
1990
- if (props._requestMaxLimits) {
1991
- requiredFeatures.push(...Array.from(adapter.features));
1992
- const limits = Object.keys(adapter.limits).filter((key) => !["minSubgroupSize", "maxSubgroupSize"].includes(key));
1993
- for (const key of limits) {
1994
- const limit = key;
1995
- const value = adapter.limits[limit];
1996
- if (typeof value === "number") {
1997
- requiredLimits[limit] = value;
1998
- }
3495
+ const adapter = await navigator.gpu.requestAdapter({
3496
+ powerPreference: "high-performance"
3497
+ // forceSoftware: false
3498
+ });
3499
+ if (!adapter) {
3500
+ throw new Error("Failed to request WebGPU adapter");
3501
+ }
3502
+ const adapterInfo = adapter.info || // @ts-ignore
3503
+ await ((_a = adapter.requestAdapterInfo) == null ? void 0 : _a.call(adapter));
3504
+ const requiredFeatures = [];
3505
+ const requiredLimits = {};
3506
+ if (props._requestMaxLimits) {
3507
+ requiredFeatures.push(...Array.from(adapter.features));
3508
+ const limits = Object.keys(adapter.limits).filter((key) => !["minSubgroupSize", "maxSubgroupSize"].includes(key));
3509
+ for (const key of limits) {
3510
+ const limit = key;
3511
+ const value = adapter.limits[limit];
3512
+ if (typeof value === "number") {
3513
+ requiredLimits[limit] = value;
1999
3514
  }
2000
3515
  }
2001
- const gpuDevice = await adapter.requestDevice({
2002
- requiredFeatures,
2003
- requiredLimits
2004
- });
2005
- import_core22.log.probe(1, "GPUDevice available")();
2006
- const { WebGPUDevice: WebGPUDevice2 } = await Promise.resolve().then(() => (init_webgpu_device(), webgpu_device_exports));
3516
+ }
3517
+ const gpuDevice = await adapter.requestDevice({
3518
+ requiredFeatures,
3519
+ requiredLimits
3520
+ });
3521
+ const { WebGPUDevice: WebGPUDevice2 } = await Promise.resolve().then(() => (init_webgpu_device(), webgpu_device_exports));
3522
+ import_core26.log.groupCollapsed(1, "WebGPUDevice created")();
3523
+ try {
2007
3524
  const device = new WebGPUDevice2(props, gpuDevice, adapter, adapterInfo);
2008
- import_core22.log.probe(1, "Device created. For more info, set chrome://flags/#enable-webgpu-developer-features")();
2009
- import_core22.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)();
2010
3527
  return device;
2011
3528
  } finally {
2012
- import_core22.log.groupEnd(1)();
3529
+ import_core26.log.groupEnd(1)();
2013
3530
  }
2014
3531
  }
2015
3532
  async attach(handle) {
@@ -2024,4 +3541,6 @@ init_webgpu_buffer();
2024
3541
  init_webgpu_texture();
2025
3542
  init_webgpu_sampler();
2026
3543
  init_webgpu_shader();
3544
+ init_webgpu_fence();
3545
+ init_get_shader_layout_wgsl();
2027
3546
  //# sourceMappingURL=index.cjs.map