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

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 +8 -6
  10. package/dist/adapter/helpers/get-bind-group.d.ts.map +1 -1
  11. package/dist/adapter/helpers/get-bind-group.js +110 -30
  12. package/dist/adapter/helpers/get-bind-group.js.map +1 -1
  13. package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts +3 -1
  14. package/dist/adapter/helpers/get-vertex-buffer-layout.d.ts.map +1 -1
  15. package/dist/adapter/helpers/get-vertex-buffer-layout.js +17 -12
  16. package/dist/adapter/helpers/get-vertex-buffer-layout.js.map +1 -1
  17. package/dist/adapter/helpers/webgpu-parameters.d.ts.map +1 -1
  18. package/dist/adapter/helpers/webgpu-parameters.js +1 -0
  19. package/dist/adapter/helpers/webgpu-parameters.js.map +1 -1
  20. package/dist/adapter/resources/webgpu-buffer.d.ts +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 +62 -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 +35 -35
  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 +176 -19
  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 +8160 -551
  94. package/dist/dist.min.js +171 -6
  95. package/dist/index.cjs +2001 -414
  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 +182 -42
  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 +90 -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 +41 -43
  128. package/src/adapter/webgpu-canvas-context.ts +108 -41
  129. package/src/adapter/webgpu-device.ts +243 -25
  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,90 +1020,122 @@ 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) => {
1094
+ this.linkStatus = "error";
903
1095
  this.device.reportError(new Error(`${this} creation failed:
904
1096
  "${error.message}"`), this)();
905
1097
  this.device.debug();
906
1098
  });
907
1099
  }
1100
+ this.descriptor = descriptor;
908
1101
  this.handle.label = this.props.id;
1102
+ this.linkStatus = "success";
909
1103
  this.vs = props.vs;
910
1104
  this.fs = props.fs;
911
- this._bindings = { ...this.props.bindings };
1105
+ this._bindingsByGroup = props.bindGroups || (0, import_core9.normalizeBindingsByGroup)(this.shaderLayout, props.bindings);
1106
+ this._bindGroupCacheKeysByGroup = createBindGroupCacheKeys(this._bindingsByGroup);
912
1107
  }
913
1108
  destroy() {
914
1109
  this.handle = null;
915
1110
  }
916
1111
  /**
917
- * @todo Use renderpass.setBindings() ?
918
- * @todo Do we want to expose BindGroups in the API and remove this?
1112
+ * Compatibility shim for code paths that still set bindings on the pipeline.
1113
+ * The shared-model path passes bindings per draw and does not rely on this state.
919
1114
  */
920
1115
  setBindings(bindings) {
921
- for (const [name2, binding] of Object.entries(bindings)) {
922
- if (this._bindings[name2] !== binding) {
923
- this._bindGroup = null;
1116
+ const nextBindingsByGroup = (0, import_core9.normalizeBindingsByGroup)(this.shaderLayout, bindings);
1117
+ for (const [groupKey, groupBindings] of Object.entries(nextBindingsByGroup)) {
1118
+ const group = Number(groupKey);
1119
+ for (const [name, binding] of Object.entries(groupBindings || {})) {
1120
+ const currentGroupBindings = this._bindingsByGroup[group] || {};
1121
+ if (currentGroupBindings[name] !== binding) {
1122
+ if (!this._bindingsByGroup[group] || this._bindingsByGroup[group] === currentGroupBindings) {
1123
+ this._bindingsByGroup[group] = { ...currentGroupBindings };
1124
+ }
1125
+ this._bindingsByGroup[group][name] = binding;
1126
+ this._bindGroupCacheKeysByGroup[group] = {};
1127
+ }
924
1128
  }
925
1129
  }
926
- Object.assign(this._bindings, bindings);
927
1130
  }
928
1131
  /** @todo - should this be moved to renderpass? */
929
1132
  draw(options) {
1133
+ if (this.isErrored) {
1134
+ import_core9.log.info(2, `RenderPipeline:${this.id}.draw() aborted - pipeline initialization failed`)();
1135
+ return false;
1136
+ }
930
1137
  const webgpuRenderPass = options.renderPass;
1138
+ const instanceCount = options.instanceCount && options.instanceCount > 0 ? options.instanceCount : 1;
931
1139
  this.device.pushErrorScope("validation");
932
1140
  webgpuRenderPass.handle.setPipeline(this.handle);
933
1141
  this.device.popErrorScope((error) => {
@@ -935,32 +1143,27 @@ var init_webgpu_render_pipeline = __esm({
935
1143
  "${error.message}"`), this)();
936
1144
  this.device.debug();
937
1145
  });
938
- const bindGroup = this._getBindGroup();
939
- if (bindGroup) {
940
- webgpuRenderPass.handle.setBindGroup(0, bindGroup);
1146
+ const hasExplicitBindings = Boolean(options.bindGroups || options.bindings);
1147
+ const bindGroups = (0, import_core9._getDefaultBindGroupFactory)(this.device).getBindGroups(this, hasExplicitBindings ? options.bindGroups || options.bindings : this._bindingsByGroup, hasExplicitBindings ? options._bindGroupCacheKeys : this._bindGroupCacheKeysByGroup);
1148
+ for (const [group, bindGroup] of Object.entries(bindGroups)) {
1149
+ if (bindGroup) {
1150
+ webgpuRenderPass.handle.setBindGroup(Number(group), bindGroup);
1151
+ }
941
1152
  }
942
1153
  options.vertexArray.bindBeforeRender(options.renderPass);
943
1154
  if (options.indexCount) {
944
- webgpuRenderPass.handle.drawIndexed(options.indexCount, options.instanceCount, options.firstIndex, options.baseVertex, options.firstInstance);
1155
+ webgpuRenderPass.handle.drawIndexed(options.indexCount, instanceCount, options.firstIndex || 0, options.baseVertex || 0, options.firstInstance || 0);
945
1156
  } else {
946
- webgpuRenderPass.handle.draw(
947
- options.vertexCount || 0,
948
- options.instanceCount || 1,
949
- // If 0, nothing will be drawn
950
- options.firstInstance
951
- );
1157
+ webgpuRenderPass.handle.draw(options.vertexCount || 0, instanceCount, options.firstVertex || 0, options.firstInstance || 0);
952
1158
  }
953
1159
  options.vertexArray.unbindAfterRender(options.renderPass);
954
1160
  return true;
955
1161
  }
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;
1162
+ _getBindingsByGroupWebGPU() {
1163
+ return this._bindingsByGroup;
1164
+ }
1165
+ _getBindGroupCacheKeysWebGPU() {
1166
+ return this._bindGroupCacheKeysByGroup;
964
1167
  }
965
1168
  /**
966
1169
  * Populate the complex WebGPU GPURenderPipelineDescriptor
@@ -969,7 +1172,9 @@ var init_webgpu_render_pipeline = __esm({
969
1172
  const vertex = {
970
1173
  module: this.props.vs.handle,
971
1174
  entryPoint: this.props.vertexEntryPoint || "main",
972
- buffers: getVertexBufferLayout(this.shaderLayout, this.props.bufferLayout)
1175
+ buffers: getVertexBufferLayout(this.shaderLayout, this.props.bufferLayout, {
1176
+ pipelineId: this.id
1177
+ })
973
1178
  };
974
1179
  const targets = [];
975
1180
  if (this.props.colorAttachmentFormats) {
@@ -1009,12 +1214,12 @@ var init_webgpu_render_pipeline = __esm({
1009
1214
  });
1010
1215
 
1011
1216
  // dist/adapter/resources/webgpu-framebuffer.js
1012
- var import_core11, WebGPUFramebuffer;
1217
+ var import_core10, WebGPUFramebuffer;
1013
1218
  var init_webgpu_framebuffer = __esm({
1014
1219
  "dist/adapter/resources/webgpu-framebuffer.js"() {
1015
1220
  "use strict";
1016
- import_core11 = require("@luma.gl/core");
1017
- WebGPUFramebuffer = class extends import_core11.Framebuffer {
1221
+ import_core10 = require("@luma.gl/core");
1222
+ WebGPUFramebuffer = class extends import_core10.Framebuffer {
1018
1223
  device;
1019
1224
  handle = null;
1020
1225
  colorAttachments = [];
@@ -1026,25 +1231,37 @@ var init_webgpu_framebuffer = __esm({
1026
1231
  }
1027
1232
  updateAttachments() {
1028
1233
  }
1234
+ /**
1235
+ * Internal-only hook for the cached CanvasContext/PresentationContext swapchain path.
1236
+ * Rebinds the long-lived default framebuffer wrapper to the current per-frame color view
1237
+ * and optional depth attachment without allocating a new luma.gl Framebuffer object.
1238
+ */
1239
+ _reinitialize(colorAttachment, depthStencilAttachment) {
1240
+ this.colorAttachments[0] = colorAttachment;
1241
+ this.depthStencilAttachment = depthStencilAttachment;
1242
+ this.width = colorAttachment.texture.width;
1243
+ this.height = colorAttachment.texture.height;
1244
+ this.props.width = this.width;
1245
+ this.props.height = this.height;
1246
+ this.props.colorAttachments = [colorAttachment.texture];
1247
+ this.props.depthStencilAttachment = (depthStencilAttachment == null ? void 0 : depthStencilAttachment.texture) || null;
1248
+ }
1029
1249
  };
1030
1250
  }
1031
1251
  });
1032
1252
 
1033
1253
  // dist/adapter/resources/webgpu-compute-pipeline.js
1034
- var import_core12, WebGPUComputePipeline;
1254
+ var import_core11, EMPTY_BIND_GROUPS, WebGPUComputePipeline;
1035
1255
  var init_webgpu_compute_pipeline = __esm({
1036
1256
  "dist/adapter/resources/webgpu-compute-pipeline.js"() {
1037
1257
  "use strict";
1038
- import_core12 = require("@luma.gl/core");
1039
- init_get_bind_group();
1040
- WebGPUComputePipeline = class extends import_core12.ComputePipeline {
1258
+ import_core11 = require("@luma.gl/core");
1259
+ EMPTY_BIND_GROUPS = {};
1260
+ WebGPUComputePipeline = class extends import_core11.ComputePipeline {
1041
1261
  device;
1042
1262
  handle;
1043
- /** For internal use to create BindGroups */
1044
- _bindGroupLayout = null;
1045
- _bindGroup = null;
1046
- /** For internal use to create BindGroups */
1047
- _bindings = {};
1263
+ _bindingsByGroup;
1264
+ _bindGroupCacheKeysByGroup;
1048
1265
  constructor(device, props) {
1049
1266
  super(device, props);
1050
1267
  this.device = device;
@@ -1058,34 +1275,53 @@ var init_webgpu_compute_pipeline = __esm({
1058
1275
  },
1059
1276
  layout: "auto"
1060
1277
  });
1278
+ this._bindingsByGroup = EMPTY_BIND_GROUPS;
1279
+ this._bindGroupCacheKeysByGroup = {};
1061
1280
  }
1062
1281
  /**
1063
1282
  * @todo Use renderpass.setBindings() ?
1064
1283
  * @todo Do we want to expose BindGroups in the API and remove this?
1065
1284
  */
1066
1285
  setBindings(bindings) {
1067
- Object.assign(this._bindings, bindings);
1286
+ const nextBindingsByGroup = (0, import_core11.normalizeBindingsByGroup)(this.shaderLayout, bindings);
1287
+ for (const [groupKey, groupBindings] of Object.entries(nextBindingsByGroup)) {
1288
+ const group = Number(groupKey);
1289
+ for (const [name, binding] of Object.entries(groupBindings || {})) {
1290
+ const currentGroupBindings = this._bindingsByGroup[group] || {};
1291
+ if (currentGroupBindings[name] !== binding) {
1292
+ if (!this._bindingsByGroup[group] || this._bindingsByGroup[group] === currentGroupBindings) {
1293
+ this._bindingsByGroup[group] = { ...currentGroupBindings };
1294
+ }
1295
+ this._bindingsByGroup[group][name] = binding;
1296
+ this._bindGroupCacheKeysByGroup[group] = {};
1297
+ }
1298
+ }
1299
+ }
1068
1300
  }
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;
1301
+ _getBindGroups(bindings, bindGroupCacheKeys) {
1302
+ const hasExplicitBindings = Boolean(bindings);
1303
+ return (0, import_core11._getDefaultBindGroupFactory)(this.device).getBindGroups(this, hasExplicitBindings ? bindings : this._bindingsByGroup, hasExplicitBindings ? bindGroupCacheKeys : this._bindGroupCacheKeysByGroup);
1304
+ }
1305
+ _getBindingsByGroupWebGPU() {
1306
+ return this._bindingsByGroup;
1307
+ }
1308
+ _getBindGroupCacheKeysWebGPU() {
1309
+ return this._bindGroupCacheKeysByGroup;
1074
1310
  }
1075
1311
  };
1076
1312
  }
1077
1313
  });
1078
1314
 
1079
1315
  // dist/adapter/resources/webgpu-vertex-array.js
1080
- var import_core13, import_env, WebGPUVertexArray;
1316
+ var import_core12, import_env, WebGPUVertexArray;
1081
1317
  var init_webgpu_vertex_array = __esm({
1082
1318
  "dist/adapter/resources/webgpu-vertex-array.js"() {
1083
1319
  "use strict";
1084
- import_core13 = require("@luma.gl/core");
1320
+ import_core12 = require("@luma.gl/core");
1085
1321
  import_env = require("@probe.gl/env");
1086
- WebGPUVertexArray = class extends import_core13.VertexArray {
1322
+ WebGPUVertexArray = class extends import_core12.VertexArray {
1087
1323
  get [Symbol.toStringTag]() {
1088
- return "WebGPUVertexArray";
1324
+ return "VertexArray";
1089
1325
  }
1090
1326
  device;
1091
1327
  /** Vertex Array is just a helper class under WebGPU */
@@ -1112,7 +1348,7 @@ var init_webgpu_vertex_array = __esm({
1112
1348
  const webgpuRenderPass = renderPass;
1113
1349
  const webgpuIndexBuffer = this.indexBuffer;
1114
1350
  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)();
1351
+ import_core12.log.info(3, "setting index buffer", webgpuIndexBuffer == null ? void 0 : webgpuIndexBuffer.handle, webgpuIndexBuffer == null ? void 0 : webgpuIndexBuffer.indexType)();
1116
1352
  webgpuRenderPass.handle.setIndexBuffer(
1117
1353
  webgpuIndexBuffer == null ? void 0 : webgpuIndexBuffer.handle,
1118
1354
  // @ts-expect-error TODO - we must enforce type
@@ -1122,7 +1358,7 @@ var init_webgpu_vertex_array = __esm({
1122
1358
  for (let location = 0; location < this.maxVertexAttributes; location++) {
1123
1359
  const webgpuBuffer = this.attributes[location];
1124
1360
  if (webgpuBuffer == null ? void 0 : webgpuBuffer.handle) {
1125
- import_core13.log.info(3, `setting vertex buffer ${location}`, webgpuBuffer == null ? void 0 : webgpuBuffer.handle)();
1361
+ import_core12.log.info(3, `setting vertex buffer ${location}`, webgpuBuffer == null ? void 0 : webgpuBuffer.handle)();
1126
1362
  webgpuRenderPass.handle.setVertexBuffer(location, webgpuBuffer == null ? void 0 : webgpuBuffer.handle);
1127
1363
  }
1128
1364
  }
@@ -1142,16 +1378,19 @@ var init_webgpu_vertex_array = __esm({
1142
1378
  });
1143
1379
 
1144
1380
  // dist/adapter/webgpu-canvas-context.js
1145
- var import_core14, WebGPUCanvasContext;
1381
+ var import_core13, WebGPUCanvasContext;
1146
1382
  var init_webgpu_canvas_context = __esm({
1147
1383
  "dist/adapter/webgpu-canvas-context.js"() {
1148
1384
  "use strict";
1149
- import_core14 = require("@luma.gl/core");
1385
+ import_core13 = require("@luma.gl/core");
1150
1386
  init_webgpu_framebuffer();
1151
- WebGPUCanvasContext = class extends import_core14.CanvasContext {
1387
+ init_cpu_hotspot_profiler();
1388
+ WebGPUCanvasContext = class extends import_core13.CanvasContext {
1152
1389
  device;
1153
1390
  handle;
1391
+ colorAttachment = null;
1154
1392
  depthStencilAttachment = null;
1393
+ framebuffer = null;
1155
1394
  get [Symbol.toStringTag]() {
1156
1395
  return "WebGPUCanvasContext";
1157
1396
  }
@@ -1164,34 +1403,29 @@ var init_webgpu_canvas_context = __esm({
1164
1403
  this.device = device;
1165
1404
  this.handle = context;
1166
1405
  this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
1167
- this._updateDevice();
1406
+ this._configureDevice();
1407
+ this._startObservers();
1168
1408
  }
1169
1409
  /** Destroy any textures produced while configured and remove the context configuration. */
1170
1410
  destroy() {
1411
+ if (this.framebuffer) {
1412
+ this.framebuffer.destroy();
1413
+ this.framebuffer = null;
1414
+ }
1415
+ if (this.colorAttachment) {
1416
+ this.colorAttachment.destroy();
1417
+ this.colorAttachment = null;
1418
+ }
1419
+ if (this.depthStencilAttachment) {
1420
+ this.depthStencilAttachment.destroy();
1421
+ this.depthStencilAttachment = null;
1422
+ }
1171
1423
  this.handle.unconfigure();
1172
1424
  super.destroy();
1173
1425
  }
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
1426
  // IMPLEMENTATION OF ABSTRACT METHODS
1194
- _updateDevice() {
1427
+ /** @see https://www.w3.org/TR/webgpu/#canvas-configuration */
1428
+ _configureDevice() {
1195
1429
  if (this.depthStencilAttachment) {
1196
1430
  this.depthStencilAttachment.destroy();
1197
1431
  this.depthStencilAttachment = null;
@@ -1204,24 +1438,81 @@ var init_webgpu_canvas_context = __esm({
1204
1438
  colorSpace: this.props.colorSpace,
1205
1439
  alphaMode: this.props.alphaMode
1206
1440
  });
1441
+ this._createDepthStencilAttachment(this.device.preferredDepthFormat);
1442
+ }
1443
+ /** Update framebuffer with properly resized "swap chain" texture views */
1444
+ _getCurrentFramebuffer(options = {
1445
+ depthStencilFormat: "depth24plus"
1446
+ }) {
1447
+ var _a;
1448
+ const profiler = getCpuHotspotProfiler(this.device);
1449
+ const startTime = profiler ? getTimestamp() : 0;
1450
+ if (profiler) {
1451
+ profiler.framebufferAcquireCount = (profiler.framebufferAcquireCount || 0) + 1;
1452
+ profiler.activeDefaultFramebufferAcquireDepth = (profiler.activeDefaultFramebufferAcquireDepth || 0) + 1;
1453
+ }
1454
+ try {
1455
+ const currentColorAttachment = this._getCurrentTexture();
1456
+ if (currentColorAttachment.width !== this.drawingBufferWidth || currentColorAttachment.height !== this.drawingBufferHeight) {
1457
+ const [oldWidth, oldHeight] = this.getDrawingBufferSize();
1458
+ this.drawingBufferWidth = currentColorAttachment.width;
1459
+ this.drawingBufferHeight = currentColorAttachment.height;
1460
+ import_core13.log.log(1, `${this}: Resized to compensate for initial canvas size mismatch ${oldWidth}x${oldHeight} => ${this.drawingBufferWidth}x${this.drawingBufferHeight}px`)();
1461
+ }
1462
+ if (options == null ? void 0 : options.depthStencilFormat) {
1463
+ this._createDepthStencilAttachment(options == null ? void 0 : options.depthStencilFormat);
1464
+ }
1465
+ this.framebuffer ||= new WebGPUFramebuffer(this.device, {
1466
+ id: `${this.id}#framebuffer`,
1467
+ colorAttachments: [currentColorAttachment],
1468
+ depthStencilAttachment: null
1469
+ });
1470
+ this.framebuffer._reinitialize(currentColorAttachment.view, (options == null ? void 0 : options.depthStencilFormat) ? ((_a = this.depthStencilAttachment) == null ? void 0 : _a.view) || null : null);
1471
+ return this.framebuffer;
1472
+ } finally {
1473
+ if (profiler) {
1474
+ profiler.activeDefaultFramebufferAcquireDepth = (profiler.activeDefaultFramebufferAcquireDepth || 1) - 1;
1475
+ profiler.framebufferAcquireTimeMs = (profiler.framebufferAcquireTimeMs || 0) + (getTimestamp() - startTime);
1476
+ }
1477
+ }
1207
1478
  }
1479
+ // PRIMARY METHODS
1208
1480
  /** Wrap the current canvas context texture in a luma.gl texture */
1209
- getCurrentTexture() {
1481
+ _getCurrentTexture() {
1482
+ const profiler = getCpuHotspotProfiler(this.device);
1483
+ const currentTextureStartTime = profiler ? getTimestamp() : 0;
1210
1484
  const handle = this.handle.getCurrentTexture();
1211
- return this.device.createTexture({
1212
- id: `${this.id}#color-texture`,
1485
+ if (profiler) {
1486
+ profiler.currentTextureAcquireCount = (profiler.currentTextureAcquireCount || 0) + 1;
1487
+ profiler.currentTextureAcquireTimeMs = (profiler.currentTextureAcquireTimeMs || 0) + (getTimestamp() - currentTextureStartTime);
1488
+ }
1489
+ if (!this.colorAttachment) {
1490
+ this.colorAttachment = this.device.createTexture({
1491
+ id: `${this.id}#color-texture`,
1492
+ handle,
1493
+ format: this.device.preferredColorFormat,
1494
+ width: handle.width,
1495
+ height: handle.height
1496
+ });
1497
+ return this.colorAttachment;
1498
+ }
1499
+ this.colorAttachment._reinitialize(handle, {
1213
1500
  handle,
1214
1501
  format: this.device.preferredColorFormat,
1215
1502
  width: handle.width,
1216
1503
  height: handle.height
1217
1504
  });
1505
+ return this.colorAttachment;
1218
1506
  }
1219
1507
  /** We build render targets on demand (i.e. not when size changes but when about to render) */
1220
1508
  _createDepthStencilAttachment(depthStencilFormat) {
1221
- if (!this.depthStencilAttachment) {
1509
+ var _a;
1510
+ const needsNewDepthStencilAttachment = !this.depthStencilAttachment || this.depthStencilAttachment.width !== this.drawingBufferWidth || this.depthStencilAttachment.height !== this.drawingBufferHeight || this.depthStencilAttachment.format !== depthStencilFormat;
1511
+ if (needsNewDepthStencilAttachment) {
1512
+ (_a = this.depthStencilAttachment) == null ? void 0 : _a.destroy();
1222
1513
  this.depthStencilAttachment = this.device.createTexture({
1223
1514
  id: `${this.id}#depth-stencil-texture`,
1224
- usage: import_core14.Texture.RENDER_ATTACHMENT,
1515
+ usage: import_core13.Texture.RENDER_ATTACHMENT,
1225
1516
  format: depthStencilFormat,
1226
1517
  width: this.drawingBufferWidth,
1227
1518
  height: this.drawingBufferHeight
@@ -1233,76 +1524,246 @@ var init_webgpu_canvas_context = __esm({
1233
1524
  }
1234
1525
  });
1235
1526
 
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"() {
1527
+ // dist/adapter/webgpu-presentation-context.js
1528
+ var import_core14, WebGPUPresentationContext;
1529
+ var init_webgpu_presentation_context = __esm({
1530
+ "dist/adapter/webgpu-presentation-context.js"() {
1240
1531
  "use strict";
1241
- import_core15 = require("@luma.gl/core");
1242
- WebGPUCommandBuffer = class extends import_core15.CommandBuffer {
1532
+ import_core14 = require("@luma.gl/core");
1533
+ init_webgpu_framebuffer();
1534
+ init_cpu_hotspot_profiler();
1535
+ WebGPUPresentationContext = class extends import_core14.PresentationContext {
1243
1536
  device;
1244
1537
  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
- });
1538
+ colorAttachment = null;
1539
+ depthStencilAttachment = null;
1540
+ framebuffer = null;
1541
+ get [Symbol.toStringTag]() {
1542
+ return "WebGPUPresentationContext";
1251
1543
  }
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
1544
  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");
1289
- }
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
+ super(props);
1546
+ const contextLabel = `${this[Symbol.toStringTag]}(${this.id})`;
1547
+ const context = this.canvas.getContext("webgpu");
1548
+ if (!context) {
1549
+ throw new Error(`${contextLabel}: Failed to create WebGPU presentation context`);
1550
+ }
1551
+ this.device = device;
1552
+ this.handle = context;
1553
+ this._setAutoCreatedCanvasId(`${this.device.id}-presentation-canvas`);
1554
+ this._configureDevice();
1555
+ this._startObservers();
1556
+ }
1557
+ destroy() {
1558
+ if (this.framebuffer) {
1559
+ this.framebuffer.destroy();
1560
+ this.framebuffer = null;
1561
+ }
1562
+ if (this.colorAttachment) {
1563
+ this.colorAttachment.destroy();
1564
+ this.colorAttachment = null;
1565
+ }
1566
+ if (this.depthStencilAttachment) {
1567
+ this.depthStencilAttachment.destroy();
1568
+ this.depthStencilAttachment = null;
1569
+ }
1570
+ this.handle.unconfigure();
1571
+ super.destroy();
1572
+ }
1573
+ present() {
1574
+ this.device.submit();
1575
+ }
1576
+ _configureDevice() {
1577
+ if (this.depthStencilAttachment) {
1578
+ this.depthStencilAttachment.destroy();
1579
+ this.depthStencilAttachment = null;
1580
+ }
1581
+ this.handle.configure({
1582
+ device: this.device.handle,
1583
+ format: this.device.preferredColorFormat,
1584
+ colorSpace: this.props.colorSpace,
1585
+ alphaMode: this.props.alphaMode
1296
1586
  });
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)();
1587
+ this._createDepthStencilAttachment(this.device.preferredDepthFormat);
1588
+ }
1589
+ _getCurrentFramebuffer(options = {
1590
+ depthStencilFormat: "depth24plus"
1591
+ }) {
1592
+ var _a;
1593
+ const profiler = getCpuHotspotProfiler(this.device);
1594
+ const startTime = profiler ? getTimestamp() : 0;
1595
+ if (profiler) {
1596
+ profiler.framebufferAcquireCount = (profiler.framebufferAcquireCount || 0) + 1;
1597
+ }
1598
+ try {
1599
+ const currentColorAttachment = this._getCurrentTexture();
1600
+ if (currentColorAttachment.width !== this.drawingBufferWidth || currentColorAttachment.height !== this.drawingBufferHeight) {
1601
+ const [oldWidth, oldHeight] = this.getDrawingBufferSize();
1602
+ this.drawingBufferWidth = currentColorAttachment.width;
1603
+ this.drawingBufferHeight = currentColorAttachment.height;
1604
+ 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`)();
1605
+ }
1606
+ if (options == null ? void 0 : options.depthStencilFormat) {
1607
+ this._createDepthStencilAttachment(options.depthStencilFormat);
1608
+ }
1609
+ this.framebuffer ||= new WebGPUFramebuffer(this.device, {
1610
+ id: `${this.id}#framebuffer`,
1611
+ colorAttachments: [currentColorAttachment],
1612
+ depthStencilAttachment: null
1613
+ });
1614
+ this.framebuffer._reinitialize(currentColorAttachment.view, (options == null ? void 0 : options.depthStencilFormat) ? ((_a = this.depthStencilAttachment) == null ? void 0 : _a.view) || null : null);
1615
+ return this.framebuffer;
1616
+ } finally {
1617
+ if (profiler) {
1618
+ profiler.framebufferAcquireTimeMs = (profiler.framebufferAcquireTimeMs || 0) + (getTimestamp() - startTime);
1619
+ }
1620
+ }
1621
+ }
1622
+ _getCurrentTexture() {
1623
+ const profiler = getCpuHotspotProfiler(this.device);
1624
+ const currentTextureStartTime = profiler ? getTimestamp() : 0;
1625
+ const handle = this.handle.getCurrentTexture();
1626
+ if (profiler) {
1627
+ profiler.currentTextureAcquireCount = (profiler.currentTextureAcquireCount || 0) + 1;
1628
+ profiler.currentTextureAcquireTimeMs = (profiler.currentTextureAcquireTimeMs || 0) + (getTimestamp() - currentTextureStartTime);
1629
+ }
1630
+ if (!this.colorAttachment) {
1631
+ this.colorAttachment = this.device.createTexture({
1632
+ id: `${this.id}#color-texture`,
1633
+ handle,
1634
+ format: this.device.preferredColorFormat,
1635
+ width: handle.width,
1636
+ height: handle.height
1637
+ });
1638
+ return this.colorAttachment;
1639
+ }
1640
+ this.colorAttachment._reinitialize(handle, {
1641
+ handle,
1642
+ format: this.device.preferredColorFormat,
1643
+ width: handle.width,
1644
+ height: handle.height
1645
+ });
1646
+ return this.colorAttachment;
1647
+ }
1648
+ _createDepthStencilAttachment(depthStencilFormat) {
1649
+ var _a;
1650
+ const needsNewDepthStencilAttachment = !this.depthStencilAttachment || this.depthStencilAttachment.width !== this.drawingBufferWidth || this.depthStencilAttachment.height !== this.drawingBufferHeight || this.depthStencilAttachment.format !== depthStencilFormat;
1651
+ if (needsNewDepthStencilAttachment) {
1652
+ (_a = this.depthStencilAttachment) == null ? void 0 : _a.destroy();
1653
+ this.depthStencilAttachment = this.device.createTexture({
1654
+ id: `${this.id}#depth-stencil-texture`,
1655
+ usage: import_core14.Texture.RENDER_ATTACHMENT,
1656
+ format: depthStencilFormat,
1657
+ width: this.drawingBufferWidth,
1658
+ height: this.drawingBufferHeight
1659
+ });
1660
+ }
1661
+ return this.depthStencilAttachment;
1662
+ }
1663
+ };
1664
+ }
1665
+ });
1666
+
1667
+ // dist/adapter/resources/webgpu-command-buffer.js
1668
+ var import_core15, WebGPUCommandBuffer;
1669
+ var init_webgpu_command_buffer = __esm({
1670
+ "dist/adapter/resources/webgpu-command-buffer.js"() {
1671
+ "use strict";
1672
+ import_core15 = require("@luma.gl/core");
1673
+ WebGPUCommandBuffer = class extends import_core15.CommandBuffer {
1674
+ device;
1675
+ handle;
1676
+ constructor(commandEncoder, props) {
1677
+ super(commandEncoder.device, props);
1678
+ this.device = commandEncoder.device;
1679
+ this.handle = this.props.handle || commandEncoder.handle.finish({
1680
+ label: (props == null ? void 0 : props.id) || "unnamed-command-buffer"
1681
+ });
1682
+ }
1683
+ };
1684
+ }
1685
+ });
1686
+
1687
+ // dist/adapter/resources/webgpu-render-pass.js
1688
+ function convertColor(color) {
1689
+ return { r: color[0], g: color[1], b: color[2], a: color[3] };
1690
+ }
1691
+ var import_core16, WebGPURenderPass;
1692
+ var init_webgpu_render_pass = __esm({
1693
+ "dist/adapter/resources/webgpu-render-pass.js"() {
1694
+ "use strict";
1695
+ import_core16 = require("@luma.gl/core");
1696
+ init_cpu_hotspot_profiler();
1697
+ WebGPURenderPass = class extends import_core16.RenderPass {
1698
+ device;
1699
+ handle;
1700
+ framebuffer;
1701
+ /** Active pipeline */
1702
+ pipeline = null;
1703
+ /** Latest bindings applied to this pass */
1704
+ bindings = {};
1705
+ constructor(device, props = {}, commandEncoder = device.commandEncoder.handle) {
1706
+ super(device, props);
1707
+ this.device = device;
1708
+ const { props: renderPassProps } = this;
1709
+ this.framebuffer = renderPassProps.framebuffer || device.getCanvasContext().getCurrentFramebuffer();
1710
+ const profiler = getCpuHotspotProfiler(this.device);
1711
+ if (profiler) {
1712
+ const counterName = renderPassProps.framebuffer ? "explicitFramebufferRenderPassCount" : "defaultFramebufferRenderPassCount";
1713
+ profiler[counterName] = (profiler[counterName] || 0) + 1;
1714
+ }
1715
+ const startTime = profiler ? getTimestamp() : 0;
1716
+ try {
1717
+ const descriptorAssemblyStartTime = profiler ? getTimestamp() : 0;
1718
+ const renderPassDescriptor = this.getRenderPassDescriptor(this.framebuffer);
1719
+ if (renderPassProps.occlusionQuerySet) {
1720
+ renderPassDescriptor.occlusionQuerySet = renderPassProps.occlusionQuerySet.handle;
1721
+ }
1722
+ if (renderPassProps.timestampQuerySet) {
1723
+ const webgpuTSQuerySet = renderPassProps.timestampQuerySet;
1724
+ webgpuTSQuerySet == null ? void 0 : webgpuTSQuerySet._invalidateResults();
1725
+ renderPassDescriptor.timestampWrites = webgpuTSQuerySet ? {
1726
+ querySet: webgpuTSQuerySet.handle,
1727
+ beginningOfPassWriteIndex: renderPassProps.beginTimestampIndex,
1728
+ endOfPassWriteIndex: renderPassProps.endTimestampIndex
1729
+ } : void 0;
1730
+ }
1731
+ if (profiler) {
1732
+ profiler.renderPassDescriptorAssemblyCount = (profiler.renderPassDescriptorAssemblyCount || 0) + 1;
1733
+ profiler.renderPassDescriptorAssemblyTimeMs = (profiler.renderPassDescriptorAssemblyTimeMs || 0) + (getTimestamp() - descriptorAssemblyStartTime);
1734
+ }
1735
+ this.device.pushErrorScope("validation");
1736
+ const beginRenderPassStartTime = profiler ? getTimestamp() : 0;
1737
+ this.handle = this.props.handle || commandEncoder.beginRenderPass(renderPassDescriptor);
1738
+ if (profiler) {
1739
+ profiler.renderPassBeginCount = (profiler.renderPassBeginCount || 0) + 1;
1740
+ profiler.renderPassBeginTimeMs = (profiler.renderPassBeginTimeMs || 0) + (getTimestamp() - beginRenderPassStartTime);
1741
+ }
1742
+ this.device.popErrorScope((error) => {
1743
+ this.device.reportError(new Error(`${this} creation failed:
1744
+ "${error.message}"`), this)();
1745
+ this.device.debug();
1746
+ });
1747
+ this.handle.label = this.props.id;
1748
+ import_core16.log.groupCollapsed(3, `new WebGPURenderPass(${this.id})`)();
1749
+ import_core16.log.probe(3, JSON.stringify(renderPassDescriptor, null, 2))();
1750
+ import_core16.log.groupEnd(3)();
1751
+ } finally {
1752
+ if (profiler) {
1753
+ profiler.renderPassSetupCount = (profiler.renderPassSetupCount || 0) + 1;
1754
+ profiler.renderPassSetupTimeMs = (profiler.renderPassSetupTimeMs || 0) + (getTimestamp() - startTime);
1755
+ }
1756
+ }
1301
1757
  }
1302
1758
  destroy() {
1759
+ this.destroyResource();
1303
1760
  }
1304
1761
  end() {
1762
+ if (this.destroyed) {
1763
+ return;
1764
+ }
1305
1765
  this.handle.end();
1766
+ this.destroy();
1306
1767
  }
1307
1768
  setPipeline(pipeline) {
1308
1769
  this.pipeline = pipeline;
@@ -1316,11 +1777,12 @@ var init_webgpu_render_pass = __esm({
1316
1777
  }
1317
1778
  /** Sets an array of bindings (uniform buffers, samplers, textures, ...) */
1318
1779
  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);
1780
+ this.bindings = bindings;
1781
+ const bindGroups = this.pipeline && (0, import_core16._getDefaultBindGroupFactory)(this.device).getBindGroups(this.pipeline, bindings) || {};
1782
+ for (const [group, bindGroup] of Object.entries(bindGroups)) {
1783
+ if (bindGroup) {
1784
+ this.handle.setBindGroup(Number(group), bindGroup);
1785
+ }
1324
1786
  }
1325
1787
  }
1326
1788
  setIndexBuffer(buffer, indexFormat, offset = 0, size) {
@@ -1427,44 +1889,60 @@ var init_webgpu_compute_pass = __esm({
1427
1889
  device;
1428
1890
  handle;
1429
1891
  _webgpuPipeline = null;
1430
- constructor(device, props) {
1892
+ constructor(device, props = {}, commandEncoder = device.commandEncoder.handle) {
1431
1893
  super(device, props);
1432
1894
  this.device = device;
1895
+ const { props: computePassProps } = this;
1433
1896
  let timestampWrites;
1434
- if (device.features.has("timestamp-query")) {
1435
- const webgpuQuerySet = props.timestampQuerySet;
1897
+ if (computePassProps.timestampQuerySet) {
1898
+ const webgpuQuerySet = computePassProps.timestampQuerySet;
1436
1899
  if (webgpuQuerySet) {
1900
+ webgpuQuerySet._invalidateResults();
1437
1901
  timestampWrites = {
1438
1902
  querySet: webgpuQuerySet.handle,
1439
- beginningOfPassWriteIndex: props.beginTimestampIndex,
1440
- endOfPassWriteIndex: props.endTimestampIndex
1903
+ beginningOfPassWriteIndex: computePassProps.beginTimestampIndex,
1904
+ endOfPassWriteIndex: computePassProps.endTimestampIndex
1441
1905
  };
1442
1906
  }
1443
1907
  }
1444
- this.handle = this.props.handle || device.commandEncoder.handle.beginComputePass({
1908
+ this.handle = this.props.handle || commandEncoder.beginComputePass({
1445
1909
  label: this.props.id,
1446
1910
  timestampWrites
1447
1911
  });
1448
1912
  }
1449
1913
  /** @note no WebGPU destroy method, just gc */
1450
1914
  destroy() {
1915
+ this.destroyResource();
1451
1916
  }
1452
1917
  end() {
1918
+ if (this.destroyed) {
1919
+ return;
1920
+ }
1453
1921
  this.handle.end();
1922
+ this.destroy();
1454
1923
  }
1455
1924
  setPipeline(pipeline) {
1456
1925
  const wgpuPipeline = pipeline;
1457
1926
  this.handle.setPipeline(wgpuPipeline.handle);
1458
1927
  this._webgpuPipeline = wgpuPipeline;
1459
- this.setBindings([]);
1928
+ const bindGroups = (0, import_core17._getDefaultBindGroupFactory)(this.device).getBindGroups(this._webgpuPipeline, this._webgpuPipeline._getBindingsByGroupWebGPU(), this._webgpuPipeline._getBindGroupCacheKeysWebGPU());
1929
+ for (const [group, bindGroup] of Object.entries(bindGroups)) {
1930
+ if (bindGroup) {
1931
+ this.handle.setBindGroup(Number(group), bindGroup);
1932
+ }
1933
+ }
1460
1934
  }
1461
1935
  /**
1462
1936
  * Sets an array of bindings (uniform buffers, samplers, textures, ...)
1463
1937
  * TODO - still some API confusion - does this method go here or on the pipeline?
1464
1938
  */
1465
1939
  setBindings(bindings) {
1466
- const bindGroup = this._webgpuPipeline._getBindGroup();
1467
- this.handle.setBindGroup(0, bindGroup);
1940
+ const bindGroups = this._webgpuPipeline && (0, import_core17._getDefaultBindGroupFactory)(this.device).getBindGroups(this._webgpuPipeline, bindings) || {};
1941
+ for (const [group, bindGroup] of Object.entries(bindGroups)) {
1942
+ if (bindGroup) {
1943
+ this.handle.setBindGroup(Number(group), bindGroup);
1944
+ }
1945
+ }
1468
1946
  }
1469
1947
  /**
1470
1948
  * Dispatch work to be performed with the current ComputePipeline.
@@ -1522,6 +2000,7 @@ var init_webgpu_command_encoder = __esm({
1522
2000
  this.handle.label = this.props.id;
1523
2001
  }
1524
2002
  destroy() {
2003
+ this.destroyResource();
1525
2004
  }
1526
2005
  finish(props) {
1527
2006
  this.device.pushErrorScope("validation");
@@ -1533,49 +2012,105 @@ var init_webgpu_command_encoder = __esm({
1533
2012
  this.device.reportError(new Error(message), this)();
1534
2013
  this.device.debug();
1535
2014
  });
2015
+ this.destroy();
1536
2016
  return commandBuffer;
1537
2017
  }
1538
2018
  /**
1539
2019
  * Allows a render pass to begin against a canvas context
1540
2020
  * @todo need to support a "Framebuffer" equivalent (aka preconfigured RenderPassDescriptors?).
1541
2021
  */
1542
- beginRenderPass(props) {
1543
- return new WebGPURenderPass(this.device, props);
2022
+ beginRenderPass(props = {}) {
2023
+ return new WebGPURenderPass(this.device, this._applyTimeProfilingToPassProps(props), this.handle);
1544
2024
  }
1545
- beginComputePass(props) {
1546
- return new WebGPUComputePass(this.device, props);
2025
+ beginComputePass(props = {}) {
2026
+ return new WebGPUComputePass(this.device, this._applyTimeProfilingToPassProps(props), this.handle);
1547
2027
  }
1548
2028
  // beginRenderPass(GPURenderPassDescriptor descriptor): GPURenderPassEncoder;
1549
2029
  // beginComputePass(optional GPUComputePassDescriptor descriptor = {}): GPUComputePassEncoder;
1550
2030
  copyBufferToBuffer(options) {
1551
2031
  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);
2032
+ const webgpuDestinationBuffer = options.destinationBuffer;
2033
+ this.handle.copyBufferToBuffer(webgpuSourceBuffer.handle, options.sourceOffset ?? 0, webgpuDestinationBuffer.handle, options.destinationOffset ?? 0, options.size ?? 0);
1554
2034
  }
1555
2035
  copyBufferToTexture(options) {
1556
- var _a, _b, _c;
1557
2036
  const webgpuSourceBuffer = options.sourceBuffer;
1558
- const WebGPUDestinationTexture = options.destinationTexture;
2037
+ const webgpuDestinationTexture = options.destinationTexture;
2038
+ const copyOrigin = options.origin ?? [0, 0, 0];
2039
+ const copySize = options.size;
1559
2040
  this.handle.copyBufferToTexture({
1560
2041
  buffer: webgpuSourceBuffer.handle,
1561
- offset: options.offset ?? 0,
2042
+ offset: options.byteOffset ?? 0,
1562
2043
  bytesPerRow: options.bytesPerRow,
1563
2044
  rowsPerImage: options.rowsPerImage
1564
2045
  }, {
1565
- texture: WebGPUDestinationTexture.handle,
2046
+ texture: webgpuDestinationTexture.handle,
1566
2047
  mipLevel: options.mipLevel ?? 0,
1567
- origin: options.origin ?? {}
1568
- // aspect: options.aspect
2048
+ origin: {
2049
+ x: copyOrigin[0] ?? 0,
2050
+ y: copyOrigin[1] ?? 0,
2051
+ z: copyOrigin[2] ?? 0
2052
+ },
2053
+ aspect: options.aspect
1569
2054
  }, {
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]
2055
+ width: copySize[0],
2056
+ height: copySize[1],
2057
+ depthOrArrayLayers: copySize[2]
1574
2058
  });
1575
2059
  }
1576
2060
  copyTextureToBuffer(options) {
2061
+ const { sourceTexture, destinationBuffer, origin = [0, 0, 0], byteOffset = 0, width, height, depthOrArrayLayers, mipLevel, aspect } = options;
2062
+ const webgpuSourceTexture = sourceTexture;
2063
+ webgpuSourceTexture.copyToBuffer(this.handle, {
2064
+ x: origin[0] ?? 0,
2065
+ y: origin[1] ?? 0,
2066
+ z: origin[2] ?? 0,
2067
+ width,
2068
+ height,
2069
+ depthOrArrayLayers,
2070
+ mipLevel,
2071
+ aspect,
2072
+ byteOffset,
2073
+ bytesPerRow: options.bytesPerRow,
2074
+ rowsPerImage: options.rowsPerImage
2075
+ }, destinationBuffer);
1577
2076
  }
1578
2077
  copyTextureToTexture(options) {
2078
+ var _a, _b, _c, _d, _e, _f;
2079
+ const webgpuSourceTexture = options.sourceTexture;
2080
+ const webgpuDestinationTexture = options.destinationTexture;
2081
+ const sourceRegion = webgpuSourceTexture._normalizeTextureReadOptions({
2082
+ x: ((_a = options.origin) == null ? void 0 : _a[0]) ?? 0,
2083
+ y: ((_b = options.origin) == null ? void 0 : _b[1]) ?? 0,
2084
+ z: ((_c = options.origin) == null ? void 0 : _c[2]) ?? 0,
2085
+ width: options.width,
2086
+ height: options.height,
2087
+ depthOrArrayLayers: options.depthOrArrayLayers,
2088
+ mipLevel: options.mipLevel ?? 0,
2089
+ aspect: options.aspect ?? "all"
2090
+ });
2091
+ this.handle.copyTextureToTexture({
2092
+ texture: webgpuSourceTexture.handle,
2093
+ mipLevel: sourceRegion.mipLevel,
2094
+ origin: {
2095
+ x: sourceRegion.x,
2096
+ y: sourceRegion.y,
2097
+ z: sourceRegion.z
2098
+ },
2099
+ aspect: sourceRegion.aspect
2100
+ }, {
2101
+ texture: webgpuDestinationTexture.handle,
2102
+ mipLevel: options.destinationMipLevel ?? 0,
2103
+ origin: {
2104
+ x: ((_d = options.destinationOrigin) == null ? void 0 : _d[0]) ?? 0,
2105
+ y: ((_e = options.destinationOrigin) == null ? void 0 : _e[1]) ?? 0,
2106
+ z: ((_f = options.destinationOrigin) == null ? void 0 : _f[2]) ?? 0
2107
+ },
2108
+ aspect: options.destinationAspect ?? sourceRegion.aspect
2109
+ }, {
2110
+ width: sourceRegion.width,
2111
+ height: sourceRegion.height,
2112
+ depthOrArrayLayers: sourceRegion.depthOrArrayLayers
2113
+ });
1579
2114
  }
1580
2115
  pushDebugGroup(groupLabel) {
1581
2116
  this.handle.pushDebugGroup(groupLabel);
@@ -1591,6 +2126,21 @@ var init_webgpu_command_encoder = __esm({
1591
2126
  const webgpuBuffer = destination;
1592
2127
  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
2128
  }
2129
+ writeTimestamp(querySet, queryIndex) {
2130
+ querySet._invalidateResults();
2131
+ const writeTimestamp = this.handle.writeTimestamp;
2132
+ if (writeTimestamp) {
2133
+ writeTimestamp.call(this.handle, querySet.handle, queryIndex);
2134
+ return;
2135
+ }
2136
+ const computePass = this.handle.beginComputePass({
2137
+ timestampWrites: {
2138
+ querySet: querySet.handle,
2139
+ beginningOfPassWriteIndex: queryIndex
2140
+ }
2141
+ });
2142
+ computePass.end();
2143
+ }
1594
2144
  };
1595
2145
  }
1596
2146
  });
@@ -1601,9 +2151,15 @@ var init_webgpu_query_set = __esm({
1601
2151
  "dist/adapter/resources/webgpu-query-set.js"() {
1602
2152
  "use strict";
1603
2153
  import_core19 = require("@luma.gl/core");
2154
+ init_cpu_hotspot_profiler();
1604
2155
  WebGPUQuerySet = class extends import_core19.QuerySet {
1605
2156
  device;
1606
2157
  handle;
2158
+ _resolveBuffer = null;
2159
+ _readBuffer = null;
2160
+ _cachedResults = null;
2161
+ _readResultsPromise = null;
2162
+ _resultsPendingResolution = false;
1607
2163
  constructor(device, props) {
1608
2164
  super(device, props);
1609
2165
  this.device = device;
@@ -1615,8 +2171,133 @@ var init_webgpu_query_set = __esm({
1615
2171
  }
1616
2172
  destroy() {
1617
2173
  var _a;
1618
- (_a = this.handle) == null ? void 0 : _a.destroy();
1619
- this.handle = null;
2174
+ if (!this.destroyed) {
2175
+ (_a = this.handle) == null ? void 0 : _a.destroy();
2176
+ this.destroyResource();
2177
+ this.handle = null;
2178
+ }
2179
+ }
2180
+ isResultAvailable(queryIndex) {
2181
+ if (!this._cachedResults) {
2182
+ return false;
2183
+ }
2184
+ return queryIndex === void 0 ? true : queryIndex >= 0 && queryIndex < this._cachedResults.length;
2185
+ }
2186
+ async readResults(options) {
2187
+ const firstQuery = (options == null ? void 0 : options.firstQuery) || 0;
2188
+ const queryCount = (options == null ? void 0 : options.queryCount) || this.props.count - firstQuery;
2189
+ if (firstQuery < 0 || queryCount < 0 || firstQuery + queryCount > this.props.count) {
2190
+ throw new Error("Query read range is out of bounds");
2191
+ }
2192
+ let needsFreshResults = true;
2193
+ while (needsFreshResults) {
2194
+ if (!this._readResultsPromise) {
2195
+ this._readResultsPromise = this._readAllResults();
2196
+ }
2197
+ const readResultsPromise = this._readResultsPromise;
2198
+ const results = await readResultsPromise;
2199
+ needsFreshResults = this._resultsPendingResolution;
2200
+ if (!needsFreshResults) {
2201
+ return results.slice(firstQuery, firstQuery + queryCount);
2202
+ }
2203
+ }
2204
+ throw new Error("Query read unexpectedly failed to resolve");
2205
+ }
2206
+ async readTimestampDuration(beginIndex, endIndex) {
2207
+ if (this.props.type !== "timestamp") {
2208
+ throw new Error("Timestamp durations require a timestamp QuerySet");
2209
+ }
2210
+ if (beginIndex < 0 || endIndex <= beginIndex || endIndex >= this.props.count) {
2211
+ throw new Error("Timestamp duration range is out of bounds");
2212
+ }
2213
+ const results = await this.readResults({
2214
+ firstQuery: beginIndex,
2215
+ queryCount: endIndex - beginIndex + 1
2216
+ });
2217
+ return Number(results[results.length - 1] - results[0]) / 1e6;
2218
+ }
2219
+ /** Marks any cached query results as stale after new writes have been encoded. */
2220
+ _invalidateResults() {
2221
+ this._cachedResults = null;
2222
+ this._resultsPendingResolution = true;
2223
+ }
2224
+ async _readAllResults() {
2225
+ this._ensureBuffers();
2226
+ try {
2227
+ if (this._resultsPendingResolution) {
2228
+ const commandEncoder = this.device.createCommandEncoder({
2229
+ id: `${this.id}-read-results`
2230
+ });
2231
+ commandEncoder.resolveQuerySet(this, this._resolveBuffer);
2232
+ commandEncoder.copyBufferToBuffer({
2233
+ sourceBuffer: this._resolveBuffer,
2234
+ destinationBuffer: this._readBuffer,
2235
+ size: this._resolveBuffer.byteLength
2236
+ });
2237
+ const commandBuffer = commandEncoder.finish({
2238
+ id: `${this.id}-read-results-command-buffer`
2239
+ });
2240
+ const previousSubmitReason = getCpuHotspotSubmitReason(this.device) || void 0;
2241
+ setCpuHotspotSubmitReason(this.device, "query-readback");
2242
+ try {
2243
+ this.device.submit(commandBuffer);
2244
+ } finally {
2245
+ setCpuHotspotSubmitReason(this.device, previousSubmitReason);
2246
+ }
2247
+ }
2248
+ const data = await this._readBuffer.readAsync(0, this._readBuffer.byteLength);
2249
+ const resultView = new BigUint64Array(data.buffer, data.byteOffset, this.props.count);
2250
+ this._cachedResults = Array.from(resultView, (value) => value);
2251
+ this._resultsPendingResolution = false;
2252
+ return this._cachedResults;
2253
+ } finally {
2254
+ this._readResultsPromise = null;
2255
+ }
2256
+ }
2257
+ _ensureBuffers() {
2258
+ if (this._resolveBuffer && this._readBuffer) {
2259
+ return;
2260
+ }
2261
+ const byteLength = this.props.count * 8;
2262
+ this._resolveBuffer = this.device.createBuffer({
2263
+ id: `${this.id}-resolve-buffer`,
2264
+ usage: import_core19.Buffer.QUERY_RESOLVE | import_core19.Buffer.COPY_SRC,
2265
+ byteLength
2266
+ });
2267
+ this.attachResource(this._resolveBuffer);
2268
+ this._readBuffer = this.device.createBuffer({
2269
+ id: `${this.id}-read-buffer`,
2270
+ usage: import_core19.Buffer.COPY_DST | import_core19.Buffer.MAP_READ,
2271
+ byteLength
2272
+ });
2273
+ this.attachResource(this._readBuffer);
2274
+ }
2275
+ _encodeResolveToReadBuffer(commandEncoder, options) {
2276
+ if (!this._resultsPendingResolution) {
2277
+ return false;
2278
+ }
2279
+ if (this._readResultsPromise) {
2280
+ return false;
2281
+ }
2282
+ this._ensureBuffers();
2283
+ const firstQuery = (options == null ? void 0 : options.firstQuery) || 0;
2284
+ const queryCount = (options == null ? void 0 : options.queryCount) || this.props.count - firstQuery;
2285
+ const byteLength = queryCount * BigUint64Array.BYTES_PER_ELEMENT;
2286
+ const byteOffset = firstQuery * BigUint64Array.BYTES_PER_ELEMENT;
2287
+ commandEncoder.resolveQuerySet(this, this._resolveBuffer, {
2288
+ firstQuery,
2289
+ queryCount,
2290
+ destinationOffset: byteOffset
2291
+ });
2292
+ commandEncoder.copyBufferToBuffer({
2293
+ sourceBuffer: this._resolveBuffer,
2294
+ sourceOffset: byteOffset,
2295
+ destinationBuffer: this._readBuffer,
2296
+ destinationOffset: byteOffset,
2297
+ size: byteLength
2298
+ });
2299
+ this._resultsPendingResolution = false;
2300
+ return true;
1620
2301
  }
1621
2302
  };
1622
2303
  }
@@ -1634,27 +2315,22 @@ var init_webgpu_pipeline_layout = __esm({
1634
2315
  constructor(device, props) {
1635
2316
  super(device, props);
1636
2317
  this.device = device;
1637
- const bindGroupEntries = this.mapShaderLayoutToBindGroupEntries();
2318
+ const bindGroupEntriesByGroup = this.mapShaderLayoutToBindGroupEntriesByGroup();
1638
2319
  this.handle = this.device.handle.createPipelineLayout({
1639
2320
  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
- ]
2321
+ bindGroupLayouts: bindGroupEntriesByGroup.map((entries, group) => this.device.handle.createBindGroupLayout({
2322
+ label: `bind-group-layout-${group}`,
2323
+ entries
2324
+ }))
1649
2325
  });
1650
2326
  }
1651
2327
  destroy() {
1652
2328
  this.handle = null;
1653
2329
  }
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];
2330
+ mapShaderLayoutToBindGroupEntriesByGroup() {
2331
+ const maxGroup = this.props.shaderLayout.bindings.reduce((highestGroup, binding) => Math.max(highestGroup, binding.group), -1);
2332
+ const bindGroupEntriesByGroup = Array.from({ length: maxGroup + 1 }, () => []);
2333
+ for (const binding of this.props.shaderLayout.bindings) {
1658
2334
  const bindingTypeInfo = {};
1659
2335
  switch (binding.type) {
1660
2336
  case "uniform": {
@@ -1710,13 +2386,13 @@ var init_webgpu_pipeline_layout = __esm({
1710
2386
  }
1711
2387
  }
1712
2388
  const VISIBILITY_ALL = GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE;
1713
- bindGroupEntries.push({
2389
+ bindGroupEntriesByGroup[binding.group].push({
1714
2390
  binding: binding.location,
1715
2391
  visibility: binding.visibility || VISIBILITY_ALL,
1716
2392
  ...bindingTypeInfo
1717
2393
  });
1718
2394
  }
1719
- return bindGroupEntries;
2395
+ return bindGroupEntriesByGroup;
1720
2396
  }
1721
2397
  };
1722
2398
  isStorageTextureBindingLayout = (maybe) => {
@@ -1725,16 +2401,796 @@ var init_webgpu_pipeline_layout = __esm({
1725
2401
  }
1726
2402
  });
1727
2403
 
2404
+ // dist/adapter/resources/webgpu-fence.js
2405
+ var import_core21, WebGPUFence;
2406
+ var init_webgpu_fence = __esm({
2407
+ "dist/adapter/resources/webgpu-fence.js"() {
2408
+ "use strict";
2409
+ import_core21 = require("@luma.gl/core");
2410
+ WebGPUFence = class extends import_core21.Fence {
2411
+ device;
2412
+ handle = null;
2413
+ signaled;
2414
+ _signaled = false;
2415
+ constructor(device, props = {}) {
2416
+ super(device, {});
2417
+ this.device = device;
2418
+ this.signaled = device.handle.queue.onSubmittedWorkDone().then(() => {
2419
+ this._signaled = true;
2420
+ }).catch((error) => {
2421
+ if (this.device.shouldIgnoreDroppedInstanceError(error)) {
2422
+ return;
2423
+ }
2424
+ throw error;
2425
+ });
2426
+ }
2427
+ isSignaled() {
2428
+ return this._signaled;
2429
+ }
2430
+ destroy() {
2431
+ }
2432
+ };
2433
+ }
2434
+ });
2435
+
2436
+ // dist/wgsl/get-shader-layout-wgsl.js
2437
+ function getShaderLayoutFromWGSL(source) {
2438
+ var _a;
2439
+ const shaderLayout = { attributes: [], bindings: [] };
2440
+ let parsedWGSL;
2441
+ try {
2442
+ parsedWGSL = parseWGSL(source);
2443
+ } catch (error) {
2444
+ import_core22.log.error(error.message)();
2445
+ return shaderLayout;
2446
+ }
2447
+ for (const uniform of parsedWGSL.uniforms) {
2448
+ const members = [];
2449
+ for (const attribute of ((_a = uniform.type) == null ? void 0 : _a.members) || []) {
2450
+ members.push({
2451
+ name: attribute.name,
2452
+ type: getType(attribute.type)
2453
+ });
2454
+ }
2455
+ shaderLayout.bindings.push({
2456
+ type: "uniform",
2457
+ name: uniform.name,
2458
+ group: uniform.group,
2459
+ location: uniform.binding,
2460
+ // @ts-expect-error TODO - unused for now but needs fixing
2461
+ members
2462
+ });
2463
+ }
2464
+ for (const storageBuffer of parsedWGSL.storage) {
2465
+ shaderLayout.bindings.push({
2466
+ type: storageBuffer.access === "read" ? "read-only-storage" : "storage",
2467
+ name: storageBuffer.name,
2468
+ group: storageBuffer.group,
2469
+ location: storageBuffer.binding
2470
+ });
2471
+ }
2472
+ for (const texture of parsedWGSL.textures) {
2473
+ const bindingDeclaration = {
2474
+ type: "texture",
2475
+ name: texture.name,
2476
+ group: texture.group,
2477
+ location: texture.binding,
2478
+ ...getTextureBindingFromReflect(texture)
2479
+ };
2480
+ shaderLayout.bindings.push(bindingDeclaration);
2481
+ }
2482
+ for (const sampler of parsedWGSL.samplers) {
2483
+ shaderLayout.bindings.push({
2484
+ type: "sampler",
2485
+ name: sampler.name,
2486
+ group: sampler.group,
2487
+ location: sampler.binding
2488
+ });
2489
+ }
2490
+ const vertex = parsedWGSL.entry.vertex[0];
2491
+ const attributeCount = (vertex == null ? void 0 : vertex.inputs.length) || 0;
2492
+ for (let i = 0; i < attributeCount; i++) {
2493
+ const wgslAttribute = vertex.inputs[i];
2494
+ if (wgslAttribute.locationType === "location") {
2495
+ const type = getType(wgslAttribute.type);
2496
+ shaderLayout.attributes.push({
2497
+ name: wgslAttribute.name,
2498
+ location: Number(wgslAttribute.location),
2499
+ type
2500
+ });
2501
+ }
2502
+ }
2503
+ return shaderLayout;
2504
+ }
2505
+ function getType(type) {
2506
+ return (type == null ? void 0 : type.format) ? `${type.name}<${type.format.name}>` : type.name;
2507
+ }
2508
+ function parseWGSL(source) {
2509
+ try {
2510
+ return new import_wgsl_reflect.WgslReflect(source);
2511
+ } catch (error) {
2512
+ if (error instanceof Error) {
2513
+ throw error;
2514
+ }
2515
+ let message = "WGSL parse error";
2516
+ if (typeof error === "object" && (error == null ? void 0 : error.message)) {
2517
+ message += `: ${error.message} `;
2518
+ }
2519
+ if (typeof error === "object" && (error == null ? void 0 : error.token)) {
2520
+ message += error.token.line || "";
2521
+ }
2522
+ throw new Error(message, { cause: error });
2523
+ }
2524
+ }
2525
+ function getTextureBindingFromReflect(v, opts) {
2526
+ var _a;
2527
+ if (v.resourceType !== import_wgsl_reflect.ResourceType.Texture) {
2528
+ throw new Error("Not a texture binding");
2529
+ }
2530
+ const typeName = v.type.name;
2531
+ const component = (_a = v.type.format) == null ? void 0 : _a.name;
2532
+ 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";
2533
+ const multisampled = typeName === "texture_multisampled_2d";
2534
+ let sampleType;
2535
+ if (typeName.startsWith("texture_depth")) {
2536
+ sampleType = "depth";
2537
+ } else if (component === "i32") {
2538
+ sampleType = "sint";
2539
+ } else if (component === "u32") {
2540
+ sampleType = "uint";
2541
+ } else {
2542
+ sampleType = "float";
2543
+ }
2544
+ return { viewDimension, sampleType, multisampled };
2545
+ }
2546
+ var import_core22, import_wgsl_reflect;
2547
+ var init_get_shader_layout_wgsl = __esm({
2548
+ "dist/wgsl/get-shader-layout-wgsl.js"() {
2549
+ "use strict";
2550
+ import_core22 = require("@luma.gl/core");
2551
+ import_wgsl_reflect = require("wgsl_reflect");
2552
+ }
2553
+ });
2554
+
2555
+ // dist/adapter/helpers/generate-mipmaps-webgpu.js
2556
+ function generateMipmapsWebGPU(device, texture) {
2557
+ if (texture.mipLevels <= 1) {
2558
+ return;
2559
+ }
2560
+ if (texture.dimension === "3d") {
2561
+ generateMipmaps3D(device, texture);
2562
+ return;
2563
+ }
2564
+ if (RENDER_DIMENSIONS.includes(texture.dimension)) {
2565
+ generateMipmapsRender(device, texture);
2566
+ return;
2567
+ }
2568
+ throw new Error(`Cannot generate mipmaps for texture dimension "${texture.dimension}" with WebGPU.`);
2569
+ }
2570
+ function generateMipmapsRender(device, texture) {
2571
+ validateFormatCapabilities(device, texture, ["render", "filter"], "render");
2572
+ const colorAttachmentFormat = getColorAttachmentFormat(texture.format, "render", texture.dimension);
2573
+ const viewDimension = texture.dimension;
2574
+ const shaderSource = getRenderMipmapWGSL(viewDimension);
2575
+ const sampler = device.createSampler({ minFilter: "linear", magFilter: "linear" });
2576
+ const uniformsBuffer = device.createBuffer({
2577
+ byteLength: 16,
2578
+ usage: import_core23.Buffer.UNIFORM | import_core23.Buffer.COPY_DST
2579
+ });
2580
+ const uniformValues = new Uint32Array(1);
2581
+ const sourceTextureLayout = {
2582
+ type: "texture",
2583
+ name: "sourceTexture",
2584
+ group: 0,
2585
+ location: 1,
2586
+ viewDimension,
2587
+ sampleType: "float"
2588
+ };
2589
+ const uniformsLayout = {
2590
+ type: "uniform",
2591
+ name: "uniforms",
2592
+ group: 0,
2593
+ location: 2
2594
+ };
2595
+ const renderShaderLayout = {
2596
+ attributes: [],
2597
+ bindings: [RENDER_SOURCE_SAMPLER_LAYOUT, sourceTextureLayout, uniformsLayout]
2598
+ };
2599
+ const vertexShader = device.createShader({
2600
+ id: "mipmap-generation-render-vs",
2601
+ source: shaderSource,
2602
+ language: "wgsl",
2603
+ stage: "vertex"
2604
+ });
2605
+ const fragmentShader = device.createShader({
2606
+ id: "mipmap-generation-render-fs",
2607
+ source: shaderSource,
2608
+ language: "wgsl",
2609
+ stage: "fragment"
2610
+ });
2611
+ const renderPipeline = device.createRenderPipeline({
2612
+ id: `mipmap-generation-render:${texture.dimension}:${texture.format}`,
2613
+ vs: vertexShader,
2614
+ fs: fragmentShader,
2615
+ shaderLayout: renderShaderLayout,
2616
+ colorAttachmentFormats: [colorAttachmentFormat],
2617
+ topology: "triangle-list"
2618
+ });
2619
+ let sourceWidth = texture.width;
2620
+ let sourceHeight = texture.height;
2621
+ const layerCount = texture.dimension === "2d" ? 1 : texture.depth;
2622
+ function renderMipmapLayer(sourceView, baseMipLevel, baseArrayLayer, destinationWidth, destinationHeight) {
2623
+ uniformValues[0] = baseArrayLayer;
2624
+ uniformsBuffer.write(uniformValues);
2625
+ const destinationView = texture.createView({
2626
+ dimension: "2d",
2627
+ baseMipLevel,
2628
+ mipLevelCount: 1,
2629
+ baseArrayLayer,
2630
+ arrayLayerCount: 1
2631
+ });
2632
+ const framebuffer = device.createFramebuffer({
2633
+ colorAttachments: [destinationView]
2634
+ });
2635
+ const renderPass = device.beginRenderPass({
2636
+ id: `mipmap-generation:${texture.format}:${baseMipLevel}:${baseArrayLayer}`,
2637
+ framebuffer
2638
+ });
2639
+ try {
2640
+ renderPass.setPipeline(renderPipeline);
2641
+ renderPass.setBindings({
2642
+ sourceSampler: sampler,
2643
+ sourceTexture: sourceView,
2644
+ uniforms: uniformsBuffer
2645
+ });
2646
+ renderPass.setParameters({
2647
+ viewport: [0, 0, destinationWidth, destinationHeight, 0, 1],
2648
+ scissorRect: [0, 0, destinationWidth, destinationHeight]
2649
+ });
2650
+ renderPass.draw({ vertexCount: 3 });
2651
+ renderPass.end();
2652
+ device.submit();
2653
+ } finally {
2654
+ destinationView.destroy();
2655
+ framebuffer.destroy();
2656
+ }
2657
+ }
2658
+ try {
2659
+ for (let baseMipLevel = 1; baseMipLevel < texture.mipLevels; ++baseMipLevel) {
2660
+ validateFormatCapabilities(device, texture, ["render", "filter"], "render");
2661
+ const sourceMipLevel = baseMipLevel - 1;
2662
+ const destinationWidth = Math.max(1, sourceWidth >> 1);
2663
+ const destinationHeight = Math.max(1, sourceHeight >> 1);
2664
+ const sourceView = texture.createView({
2665
+ dimension: viewDimension,
2666
+ baseMipLevel: sourceMipLevel,
2667
+ mipLevelCount: 1,
2668
+ baseArrayLayer: 0,
2669
+ arrayLayerCount: texture.depth
2670
+ });
2671
+ try {
2672
+ for (let baseArrayLayer = 0; baseArrayLayer < layerCount; ++baseArrayLayer) {
2673
+ renderMipmapLayer(sourceView, baseMipLevel, baseArrayLayer, destinationWidth, destinationHeight);
2674
+ }
2675
+ } finally {
2676
+ sourceView.destroy();
2677
+ }
2678
+ sourceWidth = destinationWidth;
2679
+ sourceHeight = destinationHeight;
2680
+ }
2681
+ } finally {
2682
+ renderPipeline.destroy();
2683
+ vertexShader.destroy();
2684
+ fragmentShader.destroy();
2685
+ sampler.destroy();
2686
+ uniformsBuffer.destroy();
2687
+ }
2688
+ }
2689
+ function getColorAttachmentFormat(format, path, dimension) {
2690
+ if (import_core23.textureFormatDecoder.isColor(format)) {
2691
+ return format;
2692
+ }
2693
+ 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.`);
2694
+ }
2695
+ function generateMipmaps3D(device, texture) {
2696
+ validateFormatCapabilities(device, texture, ["filter", "store"], "compute");
2697
+ const format = getColorAttachmentFormat(texture.format, "compute", texture.dimension);
2698
+ const shaderSource = get3DComputeMipmapWGSL(format);
2699
+ const destinationTextureLayout = {
2700
+ type: "storage",
2701
+ name: "destinationTexture",
2702
+ group: 0,
2703
+ location: 1,
2704
+ format,
2705
+ viewDimension: "3d",
2706
+ access: "write-only"
2707
+ };
2708
+ const computeShaderLayout = {
2709
+ bindings: [COMPUTE_SOURCE_TEXTURE_LAYOUT, destinationTextureLayout, COMPUTE_UNIFORMS_LAYOUT]
2710
+ };
2711
+ const computeShader = device.createShader({
2712
+ id: "mipmap-generation-compute",
2713
+ source: shaderSource,
2714
+ language: "wgsl",
2715
+ stage: "compute"
2716
+ });
2717
+ const computePipeline = device.createComputePipeline({
2718
+ id: `mipmap-generation-compute:${texture.format}`,
2719
+ shader: computeShader,
2720
+ shaderLayout: computeShaderLayout
2721
+ });
2722
+ const uniformsBuffer = device.createBuffer({
2723
+ byteLength: 32,
2724
+ usage: import_core23.Buffer.UNIFORM | import_core23.Buffer.COPY_DST
2725
+ });
2726
+ const uniformValues = new Uint32Array(8);
2727
+ let sourceWidth = texture.width;
2728
+ let sourceHeight = texture.height;
2729
+ let sourceDepth = texture.depth;
2730
+ try {
2731
+ for (let destinationMipLevel = 1; destinationMipLevel < texture.mipLevels; ++destinationMipLevel) {
2732
+ validateFormatCapabilities(device, texture, ["filter", "store"], "compute");
2733
+ const destinationWidth = Math.max(1, sourceWidth >> 1);
2734
+ const destinationHeight = Math.max(1, sourceHeight >> 1);
2735
+ const destinationDepth = Math.max(1, sourceDepth >> 1);
2736
+ uniformValues[0] = sourceWidth;
2737
+ uniformValues[1] = sourceHeight;
2738
+ uniformValues[2] = sourceDepth;
2739
+ uniformValues[3] = destinationWidth;
2740
+ uniformValues[4] = destinationHeight;
2741
+ uniformValues[5] = destinationDepth;
2742
+ uniformValues[6] = 0;
2743
+ uniformsBuffer.write(uniformValues);
2744
+ const sourceView = texture.createView({
2745
+ dimension: "3d",
2746
+ baseMipLevel: destinationMipLevel - 1,
2747
+ mipLevelCount: 1,
2748
+ baseArrayLayer: 0,
2749
+ arrayLayerCount: 1
2750
+ });
2751
+ const destinationView = texture.createView({
2752
+ dimension: "3d",
2753
+ baseMipLevel: destinationMipLevel,
2754
+ mipLevelCount: 1,
2755
+ baseArrayLayer: 0,
2756
+ arrayLayerCount: 1
2757
+ });
2758
+ computePipeline.setBindings({
2759
+ sourceTexture: sourceView,
2760
+ destinationTexture: destinationView,
2761
+ uniforms: uniformsBuffer
2762
+ });
2763
+ try {
2764
+ const workgroupsX = Math.ceil(destinationWidth / WORKGROUP_SIZE.x);
2765
+ const workgroupsY = Math.ceil(destinationHeight / WORKGROUP_SIZE.y);
2766
+ const workgroupsZ = Math.ceil(destinationDepth / WORKGROUP_SIZE.z);
2767
+ const computePass = device.beginComputePass({});
2768
+ computePass.setPipeline(computePipeline);
2769
+ computePass.dispatch(workgroupsX, workgroupsY, workgroupsZ);
2770
+ computePass.end();
2771
+ device.submit();
2772
+ } finally {
2773
+ sourceView.destroy();
2774
+ destinationView.destroy();
2775
+ }
2776
+ sourceWidth = destinationWidth;
2777
+ sourceHeight = destinationHeight;
2778
+ sourceDepth = destinationDepth;
2779
+ }
2780
+ } finally {
2781
+ computePipeline.destroy();
2782
+ computeShader.destroy();
2783
+ uniformsBuffer.destroy();
2784
+ }
2785
+ }
2786
+ function validateFormatCapabilities(device, texture, requiredCapabilities, path) {
2787
+ const { format, dimension } = texture;
2788
+ const capabilities = device.getTextureFormatCapabilities(format);
2789
+ const missingCapabilities = requiredCapabilities.filter((capability) => !capabilities[capability]);
2790
+ if (missingCapabilities.length > 0) {
2791
+ const required = requiredCapabilities.join(" + ");
2792
+ const actual = requiredCapabilities.map((capability) => `${capability}=${capabilities[capability]}`).join(", ");
2793
+ throw new Error(`Cannot run ${path} mipmap generation for ${dimension} texture with format "${format}". Required capabilities: ${required}. Actual capabilities: ${actual}.`);
2794
+ }
2795
+ }
2796
+ function getSourceTextureType(dimension) {
2797
+ switch (dimension) {
2798
+ case "2d":
2799
+ return "texture_2d<f32>";
2800
+ case "2d-array":
2801
+ return "texture_2d_array<f32>";
2802
+ case "cube":
2803
+ return "texture_cube<f32>";
2804
+ case "cube-array":
2805
+ return "texture_cube_array<f32>";
2806
+ default:
2807
+ throw new Error(`Unsupported render dimension "${dimension}" for mipmap generation.`);
2808
+ }
2809
+ }
2810
+ function getRenderMipmapWGSL(dimension) {
2811
+ const sourceSnippet = getRenderMipmapSampleSnippet(dimension);
2812
+ return `
2813
+ struct MipmapUniforms {
2814
+ sourceLayer: u32,
2815
+ };
2816
+
2817
+ fn _touchUniform(uniforms: MipmapUniforms) {
2818
+ let unusedSourceLayer = uniforms.sourceLayer;
2819
+ }
2820
+
2821
+ const faceMat = array(
2822
+ mat3x3f(
2823
+ 0.0, 0.0, -2.0,
2824
+ 0.0, -2.0, 0.0,
2825
+ 1.0, 1.0, 1.0
2826
+ ), // pos-x
2827
+ mat3x3f(
2828
+ 0.0, 0.0, 2.0,
2829
+ 0.0, -2.0, 0.0,
2830
+ -1.0, 1.0, -1.0
2831
+ ), // neg-x
2832
+ mat3x3f(
2833
+ 2.0, 0.0, 0.0,
2834
+ 0.0, 0.0, 2.0,
2835
+ -1.0, 1.0, -1.0
2836
+ ), // pos-y
2837
+ mat3x3f(
2838
+ 2.0, 0.0, 0.0,
2839
+ 0.0, 0.0, -2.0,
2840
+ -1.0, -1.0, 1.0
2841
+ ), // neg-y
2842
+ mat3x3f(
2843
+ 2.0, 0.0, 0.0,
2844
+ 0.0, -2.0, 0.0,
2845
+ -1.0, 1.0, 1.0
2846
+ ), // pos-z
2847
+ mat3x3f(
2848
+ -2.0, 0.0, 0.0,
2849
+ 0.0, -2.0, 0.0,
2850
+ 1.0, 1.0, -1.0
2851
+ ) // neg-z
2852
+ );
2853
+
2854
+ struct FragmentInputs {
2855
+ @builtin(position) position: vec4f,
2856
+ @location(0) texcoord: vec2f
2857
+ };
2858
+
2859
+ struct VertexOutput {
2860
+ @builtin(position) position: vec4f,
2861
+ @location(0) texcoord: vec2f
2862
+ };
2863
+
2864
+ @group(0) @binding(0) var sourceSampler: sampler;
2865
+ @group(0) @binding(1) var sourceTexture: ${getSourceTextureType(dimension)};
2866
+ @group(0) @binding(2) var<uniform> uniforms: MipmapUniforms;
2867
+
2868
+ @vertex
2869
+ fn vertexMain(
2870
+ @builtin(vertex_index) vertexIndex: u32
2871
+ ) -> VertexOutput {
2872
+ const positions = array(
2873
+ vec2f(-1.0, -1.0),
2874
+ vec2f(-1.0, 3.0),
2875
+ vec2f( 3.0, -1.0)
2876
+ );
2877
+
2878
+ let xy = positions[vertexIndex];
2879
+ return VertexOutput(
2880
+ vec4f(xy, 0.0, 1.0),
2881
+ xy * vec2f(0.5, -0.5) + vec2f(0.5)
2882
+ );
2883
+ }
2884
+
2885
+ @fragment
2886
+ fn fragmentMain(fsInput: VertexOutput) -> @location(0) vec4f {
2887
+ _touchUniform(uniforms);
2888
+ return ${sourceSnippet};
2889
+ }
2890
+ `;
2891
+ }
2892
+ function getRenderMipmapSampleSnippet(dimension) {
2893
+ const layer = "uniforms.sourceLayer";
2894
+ switch (dimension) {
2895
+ case "2d":
2896
+ return "textureSampleLevel(sourceTexture, sourceSampler, fsInput.texcoord, 0.0)";
2897
+ case "2d-array":
2898
+ return `textureSampleLevel(sourceTexture, sourceSampler, fsInput.texcoord, i32(${layer}), 0.0)`;
2899
+ case "cube":
2900
+ return `textureSampleLevel(sourceTexture, sourceSampler, faceMat[i32(${layer})] * vec3f(fract(fsInput.texcoord), 1.0), 0.0)`;
2901
+ case "cube-array":
2902
+ return `textureSampleLevel(sourceTexture, sourceSampler, faceMat[i32(${layer} % 6u)] * vec3f(fract(fsInput.texcoord), 1.0), i32(${layer} / 6u), 0.0)`;
2903
+ default:
2904
+ throw new Error(`Unsupported render dimension "${dimension}" for mipmap generation.`);
2905
+ }
2906
+ }
2907
+ function get3DComputeMipmapWGSL(format) {
2908
+ return `
2909
+ struct MipmapUniforms {
2910
+ sourceWidth: u32,
2911
+ sourceHeight: u32,
2912
+ sourceDepth: u32,
2913
+ destinationWidth: u32,
2914
+ destinationHeight: u32,
2915
+ destinationDepth: u32,
2916
+ padding: u32,
2917
+ };
2918
+
2919
+ @group(0) @binding(0) var sourceTexture: texture_3d<f32>;
2920
+ @group(0) @binding(1) var destinationTexture: texture_storage_3d<${format}, write>;
2921
+ @group(0) @binding(2) var<uniform> uniforms: MipmapUniforms;
2922
+
2923
+ @compute @workgroup_size(${WORKGROUP_SIZE.x}, ${WORKGROUP_SIZE.y}, ${WORKGROUP_SIZE.z})
2924
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2925
+ if (
2926
+ id.x >= uniforms.destinationWidth ||
2927
+ id.y >= uniforms.destinationHeight ||
2928
+ id.z >= uniforms.destinationDepth
2929
+ ) {
2930
+ return;
2931
+ }
2932
+
2933
+ let sourceBase = id * 2u;
2934
+ let sourceX0 = min(sourceBase.x, uniforms.sourceWidth - 1u);
2935
+ let sourceY0 = min(sourceBase.y, uniforms.sourceHeight - 1u);
2936
+ let sourceZ0 = min(sourceBase.z, uniforms.sourceDepth - 1u);
2937
+
2938
+ let sourceX1 = min(sourceBase.x + 1u, uniforms.sourceWidth - 1u);
2939
+ let sourceY1 = min(sourceBase.y + 1u, uniforms.sourceHeight - 1u);
2940
+ let sourceZ1 = min(sourceBase.z + 1u, uniforms.sourceDepth - 1u);
2941
+
2942
+ var sum = textureLoad(
2943
+ sourceTexture,
2944
+ vec3<i32>(i32(sourceX0), i32(sourceY0), i32(sourceZ0)),
2945
+ 0
2946
+ );
2947
+ sum += textureLoad(
2948
+ sourceTexture,
2949
+ vec3<i32>(i32(sourceX1), i32(sourceY0), i32(sourceZ0)),
2950
+ 0
2951
+ );
2952
+ sum += textureLoad(
2953
+ sourceTexture,
2954
+ vec3<i32>(i32(sourceX0), i32(sourceY1), i32(sourceZ0)),
2955
+ 0
2956
+ );
2957
+ sum += textureLoad(
2958
+ sourceTexture,
2959
+ vec3<i32>(i32(sourceX1), i32(sourceY1), i32(sourceZ0)),
2960
+ 0
2961
+ );
2962
+ sum += textureLoad(
2963
+ sourceTexture,
2964
+ vec3<i32>(i32(sourceX0), i32(sourceY0), i32(sourceZ1)),
2965
+ 0
2966
+ );
2967
+ sum += textureLoad(
2968
+ sourceTexture,
2969
+ vec3<i32>(i32(sourceX1), i32(sourceY0), i32(sourceZ1)),
2970
+ 0
2971
+ );
2972
+ sum += textureLoad(
2973
+ sourceTexture,
2974
+ vec3<i32>(i32(sourceX0), i32(sourceY1), i32(sourceZ1)),
2975
+ 0
2976
+ );
2977
+ sum += textureLoad(
2978
+ sourceTexture,
2979
+ vec3<i32>(i32(sourceX1), i32(sourceY1), i32(sourceZ1)),
2980
+ 0
2981
+ );
2982
+
2983
+ textureStore(
2984
+ destinationTexture,
2985
+ vec3<i32>(i32(id.x), i32(id.y), i32(id.z)),
2986
+ vec4<f32>(sum.xyz / 8.0, sum.w / 8.0)
2987
+ );
2988
+ }
2989
+ `;
2990
+ }
2991
+ var import_core23, RENDER_DIMENSIONS, WORKGROUP_SIZE, RENDER_SOURCE_SAMPLER_LAYOUT, COMPUTE_SOURCE_TEXTURE_LAYOUT, COMPUTE_UNIFORMS_LAYOUT;
2992
+ var init_generate_mipmaps_webgpu = __esm({
2993
+ "dist/adapter/helpers/generate-mipmaps-webgpu.js"() {
2994
+ "use strict";
2995
+ import_core23 = require("@luma.gl/core");
2996
+ RENDER_DIMENSIONS = [
2997
+ "2d",
2998
+ "2d-array",
2999
+ "cube",
3000
+ "cube-array"
3001
+ ];
3002
+ WORKGROUP_SIZE = {
3003
+ x: 4,
3004
+ y: 4,
3005
+ z: 4
3006
+ };
3007
+ RENDER_SOURCE_SAMPLER_LAYOUT = {
3008
+ type: "sampler",
3009
+ name: "sourceSampler",
3010
+ group: 0,
3011
+ location: 0
3012
+ };
3013
+ COMPUTE_SOURCE_TEXTURE_LAYOUT = {
3014
+ type: "texture",
3015
+ name: "sourceTexture",
3016
+ group: 0,
3017
+ location: 0,
3018
+ viewDimension: "3d",
3019
+ sampleType: "float"
3020
+ };
3021
+ COMPUTE_UNIFORMS_LAYOUT = {
3022
+ type: "uniform",
3023
+ name: "uniforms",
3024
+ group: 0,
3025
+ location: 2
3026
+ };
3027
+ }
3028
+ });
3029
+
3030
+ // dist/adapter/helpers/get-bind-group.js
3031
+ function getBindGroup(device, bindGroupLayout, shaderLayout, bindings, group, label) {
3032
+ const entries = getBindGroupEntries(bindings, shaderLayout, group);
3033
+ if (entries.length === 0) {
3034
+ return null;
3035
+ }
3036
+ device.pushErrorScope("validation");
3037
+ const bindGroup = device.handle.createBindGroup({
3038
+ label,
3039
+ layout: bindGroupLayout,
3040
+ entries
3041
+ });
3042
+ device.popErrorScope((error) => {
3043
+ const summary = formatBindGroupCreationErrorSummary(shaderLayout, bindings, entries, group);
3044
+ import_core24.log.error(`bindGroup creation: ${summary}
3045
+ Raw WebGPU error: ${error.message}`, bindGroup)();
3046
+ });
3047
+ return bindGroup;
3048
+ }
3049
+ function formatBindGroupCreationErrorSummary(shaderLayout, bindings, entries, group) {
3050
+ const expectedBindings = getExpectedBindingsForGroup(shaderLayout, group);
3051
+ const expectedByLocation = new Map(expectedBindings.map((bindingSummary) => [bindingSummary.location, bindingSummary]));
3052
+ const providedBindings = entries.map((entry) => expectedByLocation.get(entry.binding) || getUnexpectedEntrySummary(entry)).sort(compareBindingSummaries);
3053
+ const missingBindings = expectedBindings.filter((bindingSummary) => !providedBindings.some((provided) => provided.location === bindingSummary.location));
3054
+ const unexpectedBindings = providedBindings.filter((bindingSummary) => !expectedByLocation.has(bindingSummary.location));
3055
+ const unmatchedLogicalBindings = Object.keys(bindings).filter((bindingName) => !resolveGroupBinding(bindingName, bindings, shaderLayout, group)).sort();
3056
+ const lines = [
3057
+ `bindGroup creation failed for group ${group}: expected ${expectedBindings.length}, provided ${providedBindings.length}`,
3058
+ `expected: ${formatBindingSummaryList(expectedBindings)}`,
3059
+ `provided: ${formatBindingSummaryList(providedBindings)}`
3060
+ ];
3061
+ if (missingBindings.length > 0) {
3062
+ lines.push(`missing: ${formatBindingSummaryList(missingBindings)}`);
3063
+ }
3064
+ if (unexpectedBindings.length > 0) {
3065
+ lines.push(`unexpected entries: ${formatBindingSummaryList(unexpectedBindings)}`);
3066
+ }
3067
+ if (unmatchedLogicalBindings.length > 0) {
3068
+ lines.push(`unmatched logical bindings: ${unmatchedLogicalBindings.join(", ")}`);
3069
+ }
3070
+ return lines.join("\n");
3071
+ }
3072
+ function getBindGroupEntries(bindings, shaderLayout, group) {
3073
+ const entries = [];
3074
+ for (const [bindingName, value] of Object.entries(bindings)) {
3075
+ const { bindingLayout, isShadowedAlias } = resolveGroupBinding(bindingName, bindings, shaderLayout, group) || { bindingLayout: null, isShadowedAlias: false };
3076
+ if (!isShadowedAlias && bindingLayout) {
3077
+ const entry = bindingLayout ? getBindGroupEntry(value, bindingLayout.location, void 0, bindingName) : null;
3078
+ if (entry) {
3079
+ entries.push(entry);
3080
+ }
3081
+ if (value instanceof import_core24.Texture) {
3082
+ const samplerBindingLayout = (0, import_core24.getShaderLayoutBinding)(shaderLayout, `${bindingName}Sampler`, {
3083
+ ignoreWarnings: true
3084
+ });
3085
+ const samplerEntry = samplerBindingLayout ? samplerBindingLayout.group === group ? getBindGroupEntry(value, samplerBindingLayout.location, { sampler: true }, bindingName) : null : null;
3086
+ if (samplerEntry) {
3087
+ entries.push(samplerEntry);
3088
+ }
3089
+ }
3090
+ }
3091
+ }
3092
+ return entries;
3093
+ }
3094
+ function getBindGroupEntry(binding, index, options, bindingName = "unknown") {
3095
+ if (binding instanceof import_core24.Buffer) {
3096
+ return {
3097
+ binding: index,
3098
+ resource: {
3099
+ buffer: binding.handle
3100
+ }
3101
+ };
3102
+ }
3103
+ if (binding instanceof import_core24.Sampler) {
3104
+ return {
3105
+ binding: index,
3106
+ resource: binding.handle
3107
+ };
3108
+ }
3109
+ if (binding instanceof import_core24.TextureView) {
3110
+ return {
3111
+ binding: index,
3112
+ resource: binding.handle
3113
+ };
3114
+ }
3115
+ if (binding instanceof import_core24.Texture) {
3116
+ if (options == null ? void 0 : options.sampler) {
3117
+ return {
3118
+ binding: index,
3119
+ resource: binding.sampler.handle
3120
+ };
3121
+ }
3122
+ return {
3123
+ binding: index,
3124
+ resource: binding.view.handle
3125
+ };
3126
+ }
3127
+ import_core24.log.warn(`invalid binding ${bindingName}`, binding);
3128
+ return null;
3129
+ }
3130
+ function getExpectedBindingsForGroup(shaderLayout, group) {
3131
+ return shaderLayout.bindings.filter((bindingLayout) => bindingLayout.group === group).map((bindingLayout) => toBindingSummary(bindingLayout)).sort(compareBindingSummaries);
3132
+ }
3133
+ function resolveGroupBinding(bindingName, bindings, shaderLayout, group) {
3134
+ const exactBindingLayout = shaderLayout.bindings.find((binding) => binding.name === bindingName);
3135
+ const bindingLayout = exactBindingLayout || (0, import_core24.getShaderLayoutBinding)(shaderLayout, bindingName, { ignoreWarnings: true });
3136
+ const isShadowedAlias = !exactBindingLayout && bindingLayout ? bindingLayout.name in bindings : false;
3137
+ if (isShadowedAlias || !bindingLayout || bindingLayout.group !== group) {
3138
+ return null;
3139
+ }
3140
+ return { bindingLayout, isShadowedAlias };
3141
+ }
3142
+ function toBindingSummary(bindingLayout) {
3143
+ return {
3144
+ name: bindingLayout.name,
3145
+ location: bindingLayout.location,
3146
+ type: bindingLayout.type
3147
+ };
3148
+ }
3149
+ function getUnexpectedEntrySummary(entry) {
3150
+ return {
3151
+ name: "?",
3152
+ location: entry.binding,
3153
+ type: "unknown"
3154
+ };
3155
+ }
3156
+ function compareBindingSummaries(left, right) {
3157
+ if (left.location !== right.location) {
3158
+ return left.location - right.location;
3159
+ }
3160
+ return left.name.localeCompare(right.name);
3161
+ }
3162
+ function formatBindingSummaryList(bindings) {
3163
+ if (bindings.length === 0) {
3164
+ return "none";
3165
+ }
3166
+ return bindings.map((binding) => `${binding.name}@${binding.location}`).join(", ");
3167
+ }
3168
+ var import_core24;
3169
+ var init_get_bind_group = __esm({
3170
+ "dist/adapter/helpers/get-bind-group.js"() {
3171
+ "use strict";
3172
+ import_core24 = require("@luma.gl/core");
3173
+ }
3174
+ });
3175
+
1728
3176
  // dist/adapter/webgpu-device.js
1729
3177
  var webgpu_device_exports = {};
1730
3178
  __export(webgpu_device_exports, {
1731
3179
  WebGPUDevice: () => WebGPUDevice
1732
3180
  });
1733
- var import_core21, WebGPUDevice;
3181
+ function scheduleMicrotask(callback) {
3182
+ if (globalThis.queueMicrotask) {
3183
+ globalThis.queueMicrotask(callback);
3184
+ return;
3185
+ }
3186
+ Promise.resolve().then(callback).catch(() => {
3187
+ });
3188
+ }
3189
+ var import_core25, WebGPUDevice;
1734
3190
  var init_webgpu_device = __esm({
1735
3191
  "dist/adapter/webgpu-device.js"() {
1736
3192
  "use strict";
1737
- import_core21 = require("@luma.gl/core");
3193
+ import_core25 = require("@luma.gl/core");
1738
3194
  init_webgpu_buffer();
1739
3195
  init_webgpu_texture();
1740
3196
  init_webgpu_external_texture();
@@ -1745,10 +3201,16 @@ var init_webgpu_device = __esm({
1745
3201
  init_webgpu_compute_pipeline();
1746
3202
  init_webgpu_vertex_array();
1747
3203
  init_webgpu_canvas_context();
3204
+ init_webgpu_presentation_context();
1748
3205
  init_webgpu_command_encoder();
1749
3206
  init_webgpu_query_set();
1750
3207
  init_webgpu_pipeline_layout();
1751
- WebGPUDevice = class extends import_core21.Device {
3208
+ init_webgpu_fence();
3209
+ init_get_shader_layout_wgsl();
3210
+ init_generate_mipmaps_webgpu();
3211
+ init_get_bind_group();
3212
+ init_cpu_hotspot_profiler();
3213
+ WebGPUDevice = class extends import_core25.Device {
1752
3214
  /** The underlying WebGPU device */
1753
3215
  handle;
1754
3216
  /* The underlying WebGPU adapter */
@@ -1765,6 +3227,7 @@ var init_webgpu_device = __esm({
1765
3227
  lost;
1766
3228
  canvasContext = null;
1767
3229
  _isLost = false;
3230
+ _defaultSampler = null;
1768
3231
  commandEncoder;
1769
3232
  get [Symbol.toStringTag]() {
1770
3233
  return "WebGPUDevice";
@@ -1786,12 +3249,11 @@ var init_webgpu_device = __esm({
1786
3249
  this.reportError(new Error(errorMessage), this)();
1787
3250
  this.debug();
1788
3251
  });
1789
- this.lost = new Promise(async (resolve) => {
1790
- const lostInfo = await this.handle.lost;
3252
+ this.lost = this.handle.lost.then((lostInfo) => {
1791
3253
  this._isLost = true;
1792
- resolve({ reason: "destroyed", message: lostInfo.message });
3254
+ return { reason: "destroyed", message: lostInfo.message };
1793
3255
  });
1794
- const canvasContextProps = import_core21.Device._getCanvasContextProps(props);
3256
+ const canvasContextProps = import_core25.Device._getCanvasContextProps(props);
1795
3257
  if (canvasContextProps) {
1796
3258
  this.canvasContext = new WebGPUCanvasContext(this, this.adapter, canvasContextProps);
1797
3259
  }
@@ -1802,18 +3264,22 @@ var init_webgpu_device = __esm({
1802
3264
  // const {glsl = true} = props;
1803
3265
  // this.glslang = glsl && await loadGlslangModule();
1804
3266
  destroy() {
3267
+ var _a, _b;
3268
+ (_a = this.commandEncoder) == null ? void 0 : _a.destroy();
3269
+ (_b = this._defaultSampler) == null ? void 0 : _b.destroy();
3270
+ this._defaultSampler = null;
1805
3271
  this.handle.destroy();
1806
3272
  }
1807
3273
  get isLost() {
1808
3274
  return this._isLost;
1809
3275
  }
3276
+ getShaderLayout(source) {
3277
+ return getShaderLayoutFromWGSL(source);
3278
+ }
1810
3279
  isVertexFormatSupported(format) {
1811
3280
  const info = this.getVertexFormatInfo(format);
1812
3281
  return !info.webglOnly;
1813
3282
  }
1814
- getTextureByteAlignment() {
1815
- return 1;
1816
- }
1817
3283
  createBuffer(props) {
1818
3284
  const newProps = this._normalizeBufferProps(props);
1819
3285
  return new WebGPUBuffer(this, newProps);
@@ -1830,6 +3296,12 @@ var init_webgpu_device = __esm({
1830
3296
  createSampler(props) {
1831
3297
  return new WebGPUSampler(this, props);
1832
3298
  }
3299
+ getDefaultSampler() {
3300
+ this._defaultSampler ||= new WebGPUSampler(this, {
3301
+ id: `${this.id}-default-sampler`
3302
+ });
3303
+ return this._defaultSampler;
3304
+ }
1833
3305
  createRenderPipeline(props) {
1834
3306
  return new WebGPURenderPipeline(this, props);
1835
3307
  }
@@ -1852,35 +3324,136 @@ var init_webgpu_device = __esm({
1852
3324
  createQuerySet(props) {
1853
3325
  return new WebGPUQuerySet(this, props);
1854
3326
  }
3327
+ createFence() {
3328
+ return new WebGPUFence(this);
3329
+ }
1855
3330
  createCanvasContext(props) {
1856
3331
  return new WebGPUCanvasContext(this, this.adapter, props);
1857
3332
  }
3333
+ createPresentationContext(props) {
3334
+ return new WebGPUPresentationContext(this, props);
3335
+ }
1858
3336
  createPipelineLayout(props) {
1859
3337
  return new WebGPUPipelineLayout(this, props);
1860
3338
  }
3339
+ generateMipmapsWebGPU(texture) {
3340
+ generateMipmapsWebGPU(this, texture);
3341
+ }
3342
+ _createBindGroupLayoutWebGPU(pipeline, group) {
3343
+ return pipeline.handle.getBindGroupLayout(group);
3344
+ }
3345
+ _createBindGroupWebGPU(bindGroupLayout, shaderLayout, bindings, group, label) {
3346
+ if (Object.keys(bindings).length === 0) {
3347
+ return this.handle.createBindGroup({
3348
+ label,
3349
+ layout: bindGroupLayout,
3350
+ entries: []
3351
+ });
3352
+ }
3353
+ return getBindGroup(this, bindGroupLayout, shaderLayout, bindings, group, label);
3354
+ }
1861
3355
  submit(commandBuffer) {
3356
+ let submittedCommandEncoder = null;
1862
3357
  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();
3358
+ ({ submittedCommandEncoder, commandBuffer } = this._finalizeDefaultCommandEncoderForSubmit());
3359
+ }
3360
+ const profiler = getCpuHotspotProfiler(this);
3361
+ const startTime = profiler ? getTimestamp() : 0;
3362
+ const submitReason = getCpuHotspotSubmitReason(this);
3363
+ try {
3364
+ this.pushErrorScope("validation");
3365
+ const queueSubmitStartTime = profiler ? getTimestamp() : 0;
3366
+ this.handle.queue.submit([commandBuffer.handle]);
3367
+ if (profiler) {
3368
+ profiler.queueSubmitCount = (profiler.queueSubmitCount || 0) + 1;
3369
+ profiler.queueSubmitTimeMs = (profiler.queueSubmitTimeMs || 0) + (getTimestamp() - queueSubmitStartTime);
3370
+ }
3371
+ this.popErrorScope((error) => {
3372
+ this.reportError(new Error(`${this} command submission: ${error.message}`), this)();
3373
+ this.debug();
3374
+ });
3375
+ if (submittedCommandEncoder) {
3376
+ const submitResolveKickoffStartTime = profiler ? getTimestamp() : 0;
3377
+ scheduleMicrotask(() => {
3378
+ submittedCommandEncoder.resolveTimeProfilingQuerySet().then(() => {
3379
+ this.commandEncoder._gpuTimeMs = submittedCommandEncoder._gpuTimeMs;
3380
+ }).catch(() => {
3381
+ });
3382
+ });
3383
+ if (profiler) {
3384
+ profiler.submitResolveKickoffCount = (profiler.submitResolveKickoffCount || 0) + 1;
3385
+ profiler.submitResolveKickoffTimeMs = (profiler.submitResolveKickoffTimeMs || 0) + (getTimestamp() - submitResolveKickoffStartTime);
3386
+ }
3387
+ }
3388
+ } finally {
3389
+ if (profiler) {
3390
+ profiler.submitCount = (profiler.submitCount || 0) + 1;
3391
+ profiler.submitTimeMs = (profiler.submitTimeMs || 0) + (getTimestamp() - startTime);
3392
+ const reasonCountKey = submitReason === "query-readback" ? "queryReadbackSubmitCount" : "defaultSubmitCount";
3393
+ const reasonTimeKey = submitReason === "query-readback" ? "queryReadbackSubmitTimeMs" : "defaultSubmitTimeMs";
3394
+ profiler[reasonCountKey] = (profiler[reasonCountKey] || 0) + 1;
3395
+ profiler[reasonTimeKey] = (profiler[reasonTimeKey] || 0) + (getTimestamp() - startTime);
3396
+ }
3397
+ const commandBufferDestroyStartTime = profiler ? getTimestamp() : 0;
3398
+ commandBuffer.destroy();
3399
+ if (profiler) {
3400
+ profiler.commandBufferDestroyCount = (profiler.commandBufferDestroyCount || 0) + 1;
3401
+ profiler.commandBufferDestroyTimeMs = (profiler.commandBufferDestroyTimeMs || 0) + (getTimestamp() - commandBufferDestroyStartTime);
3402
+ }
3403
+ }
3404
+ }
3405
+ _finalizeDefaultCommandEncoderForSubmit() {
3406
+ const submittedCommandEncoder = this.commandEncoder;
3407
+ if (submittedCommandEncoder.getTimeProfilingSlotCount() > 0 && submittedCommandEncoder.getTimeProfilingQuerySet() instanceof WebGPUQuerySet) {
3408
+ const querySet = submittedCommandEncoder.getTimeProfilingQuerySet();
3409
+ querySet._encodeResolveToReadBuffer(submittedCommandEncoder, {
3410
+ firstQuery: 0,
3411
+ queryCount: submittedCommandEncoder.getTimeProfilingSlotCount()
3412
+ });
3413
+ }
3414
+ const commandBuffer = submittedCommandEncoder.finish();
3415
+ this.commandEncoder.destroy();
3416
+ this.commandEncoder = this.createCommandEncoder({
3417
+ id: submittedCommandEncoder.props.id,
3418
+ timeProfilingQuerySet: submittedCommandEncoder.getTimeProfilingQuerySet()
1872
3419
  });
3420
+ return { submittedCommandEncoder, commandBuffer };
1873
3421
  }
1874
3422
  // WebGPU specific
1875
3423
  pushErrorScope(scope) {
3424
+ if (!this.props.debug) {
3425
+ return;
3426
+ }
3427
+ const profiler = getCpuHotspotProfiler(this);
3428
+ const startTime = profiler ? getTimestamp() : 0;
1876
3429
  this.handle.pushErrorScope(scope);
3430
+ if (profiler) {
3431
+ profiler.errorScopePushCount = (profiler.errorScopePushCount || 0) + 1;
3432
+ profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
3433
+ }
1877
3434
  }
1878
3435
  popErrorScope(handler) {
3436
+ if (!this.props.debug) {
3437
+ return;
3438
+ }
3439
+ const profiler = getCpuHotspotProfiler(this);
3440
+ const startTime = profiler ? getTimestamp() : 0;
1879
3441
  this.handle.popErrorScope().then((error) => {
1880
3442
  if (error) {
1881
3443
  handler(error);
1882
3444
  }
3445
+ }).catch((error) => {
3446
+ if (this.shouldIgnoreDroppedInstanceError(error, "popErrorScope")) {
3447
+ return;
3448
+ }
3449
+ const errorMessage = error instanceof Error ? error.message : String(error);
3450
+ this.reportError(new Error(`${this} popErrorScope failed: ${errorMessage}`), this)();
3451
+ this.debug();
1883
3452
  });
3453
+ if (profiler) {
3454
+ profiler.errorScopePopCount = (profiler.errorScopePopCount || 0) + 1;
3455
+ profiler.errorScopeTimeMs = (profiler.errorScopeTimeMs || 0) + (getTimestamp() - startTime);
3456
+ }
1884
3457
  }
1885
3458
  // PRIVATE METHODS
1886
3459
  _getInfo() {
@@ -1888,10 +3461,12 @@ var init_webgpu_device = __esm({
1888
3461
  const vendor = this.adapterInfo.vendor || this.adapter.__brand || "unknown";
1889
3462
  const renderer = driver || "";
1890
3463
  const version = driverVersion || "";
1891
- const gpu = vendor === "apple" ? "apple" : "unknown";
3464
+ const fallback = Boolean(this.adapterInfo.isFallbackAdapter ?? this.adapter.isFallbackAdapter ?? false);
3465
+ const softwareRenderer = /SwiftShader/i.test(`${vendor} ${renderer} ${this.adapterInfo.architecture || ""}`);
3466
+ const gpu = vendor === "apple" ? "apple" : softwareRenderer || fallback ? "software" : "unknown";
1892
3467
  const gpuArchitecture = this.adapterInfo.architecture || "unknown";
1893
3468
  const gpuBackend = this.adapterInfo.backend || "unknown";
1894
- const gpuType = (this.adapterInfo.type || "").split(" ")[0].toLowerCase() || "unknown";
3469
+ const gpuType = (this.adapterInfo.type || "").split(" ")[0].toLowerCase() || (softwareRenderer || fallback ? "cpu" : "unknown");
1895
3470
  return {
1896
3471
  type: "webgpu",
1897
3472
  vendor,
@@ -1901,10 +3476,15 @@ var init_webgpu_device = __esm({
1901
3476
  gpuType,
1902
3477
  gpuBackend,
1903
3478
  gpuArchitecture,
3479
+ fallback,
1904
3480
  shadingLanguage: "wgsl",
1905
3481
  shadingLanguageVersion: 100
1906
3482
  };
1907
3483
  }
3484
+ shouldIgnoreDroppedInstanceError(error, operation) {
3485
+ const errorMessage = error instanceof Error ? error.message : String(error);
3486
+ return errorMessage.includes("Instance dropped") && (!operation || errorMessage.includes(operation)) && (this._isLost || this.info.gpu === "software" || this.info.gpuType === "cpu" || Boolean(this.info.fallback));
3487
+ }
1908
3488
  _getFeatures() {
1909
3489
  const features = new Set(this.handle.features);
1910
3490
  if (features.has("depth-clamping")) {
@@ -1914,8 +3494,13 @@ var init_webgpu_device = __esm({
1914
3494
  if (features.has("texture-compression-bc")) {
1915
3495
  features.add("texture-compression-bc5-webgl");
1916
3496
  }
3497
+ if (this.handle.features.has("chromium-experimental-norm16-texture-formats")) {
3498
+ features.add("norm16-renderable-webgl");
3499
+ }
3500
+ if (this.handle.features.has("chromium-experimental-snorm16-texture-formats")) {
3501
+ features.add("snorm16-renderable-webgl");
3502
+ }
1917
3503
  const WEBGPU_ALWAYS_FEATURES = [
1918
- "timer-query-webgl",
1919
3504
  "compilation-status-async-webgl",
1920
3505
  "float32-renderable-webgl",
1921
3506
  "float16-renderable-webgl",
@@ -1926,7 +3511,7 @@ var init_webgpu_device = __esm({
1926
3511
  for (const feature of WEBGPU_ALWAYS_FEATURES) {
1927
3512
  features.add(feature);
1928
3513
  }
1929
- return new import_core21.DeviceFeatures(Array.from(features), this.props._disabledFeatures);
3514
+ return new import_core25.DeviceFeatures(Array.from(features), this.props._disabledFeatures);
1930
3515
  }
1931
3516
  _getDeviceSpecificTextureFormatCapabilities(capabilities) {
1932
3517
  const { format } = capabilities;
@@ -1944,16 +3529,18 @@ var dist_exports = {};
1944
3529
  __export(dist_exports, {
1945
3530
  WebGPUBuffer: () => WebGPUBuffer,
1946
3531
  WebGPUDevice: () => WebGPUDevice,
3532
+ WebGPUFence: () => WebGPUFence,
1947
3533
  WebGPUSampler: () => WebGPUSampler,
1948
3534
  WebGPUShader: () => WebGPUShader,
1949
3535
  WebGPUTexture: () => WebGPUTexture,
3536
+ getShaderLayoutFromWGSL: () => getShaderLayoutFromWGSL,
1950
3537
  webgpuAdapter: () => webgpuAdapter
1951
3538
  });
1952
3539
  module.exports = __toCommonJS(dist_exports);
1953
3540
 
1954
3541
  // dist/adapter/webgpu-adapter.js
1955
- var import_core22 = require("@luma.gl/core");
1956
- var WebGPUAdapter = class extends import_core22.Adapter {
3542
+ var import_core26 = require("@luma.gl/core");
3543
+ var WebGPUAdapter = class extends import_core26.Adapter {
1957
3544
  /** type of device's created by this adapter */
1958
3545
  type = "webgpu";
1959
3546
  isSupported() {
@@ -1973,43 +3560,41 @@ var WebGPUAdapter = class extends import_core22.Adapter {
1973
3560
  if (!navigator.gpu) {
1974
3561
  throw new Error("WebGPU not available. Recent Chrome browsers should work.");
1975
3562
  }
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
- }
3563
+ const adapter = await navigator.gpu.requestAdapter({
3564
+ powerPreference: "high-performance"
3565
+ // forceSoftware: false
3566
+ });
3567
+ if (!adapter) {
3568
+ throw new Error("Failed to request WebGPU adapter");
3569
+ }
3570
+ const adapterInfo = adapter.info || // @ts-ignore
3571
+ await ((_a = adapter.requestAdapterInfo) == null ? void 0 : _a.call(adapter));
3572
+ const requiredFeatures = [];
3573
+ const requiredLimits = {};
3574
+ if (props._requestMaxLimits) {
3575
+ requiredFeatures.push(...Array.from(adapter.features));
3576
+ const limits = Object.keys(adapter.limits).filter((key) => !["minSubgroupSize", "maxSubgroupSize"].includes(key));
3577
+ for (const key of limits) {
3578
+ const limit = key;
3579
+ const value = adapter.limits[limit];
3580
+ if (typeof value === "number") {
3581
+ requiredLimits[limit] = value;
1999
3582
  }
2000
3583
  }
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));
3584
+ }
3585
+ const gpuDevice = await adapter.requestDevice({
3586
+ requiredFeatures,
3587
+ requiredLimits
3588
+ });
3589
+ const { WebGPUDevice: WebGPUDevice2 } = await Promise.resolve().then(() => (init_webgpu_device(), webgpu_device_exports));
3590
+ import_core26.log.groupCollapsed(1, "WebGPUDevice created")();
3591
+ try {
2007
3592
  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)();
3593
+ import_core26.log.probe(1, "Device created. For more info, set chrome://flags/#enable-webgpu-developer-features")();
3594
+ import_core26.log.table(1, device.info)();
2010
3595
  return device;
2011
3596
  } finally {
2012
- import_core22.log.groupEnd(1)();
3597
+ import_core26.log.groupEnd(1)();
2013
3598
  }
2014
3599
  }
2015
3600
  async attach(handle) {
@@ -2024,4 +3609,6 @@ init_webgpu_buffer();
2024
3609
  init_webgpu_texture();
2025
3610
  init_webgpu_sampler();
2026
3611
  init_webgpu_shader();
3612
+ init_webgpu_fence();
3613
+ init_get_shader_layout_wgsl();
2027
3614
  //# sourceMappingURL=index.cjs.map