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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/adapter/canvas-context.d.ts +6 -181
  2. package/dist/adapter/canvas-context.d.ts.map +1 -1
  3. package/dist/adapter/canvas-context.js +5 -450
  4. package/dist/adapter/canvas-context.js.map +1 -1
  5. package/dist/adapter/canvas-observer.d.ts +32 -0
  6. package/dist/adapter/canvas-observer.d.ts.map +1 -0
  7. package/dist/adapter/canvas-observer.js +90 -0
  8. package/dist/adapter/canvas-observer.js.map +1 -0
  9. package/dist/adapter/canvas-surface.d.ts +150 -0
  10. package/dist/adapter/canvas-surface.d.ts.map +1 -0
  11. package/dist/adapter/canvas-surface.js +392 -0
  12. package/dist/adapter/canvas-surface.js.map +1 -0
  13. package/dist/adapter/device.d.ts +64 -9
  14. package/dist/adapter/device.d.ts.map +1 -1
  15. package/dist/adapter/device.js +108 -4
  16. package/dist/adapter/device.js.map +1 -1
  17. package/dist/adapter/luma.js +1 -1
  18. package/dist/adapter/presentation-context.d.ts +11 -0
  19. package/dist/adapter/presentation-context.d.ts.map +1 -0
  20. package/dist/adapter/presentation-context.js +12 -0
  21. package/dist/adapter/presentation-context.js.map +1 -0
  22. package/dist/adapter/resources/buffer.d.ts +1 -1
  23. package/dist/adapter/resources/buffer.d.ts.map +1 -1
  24. package/dist/adapter/resources/buffer.js +14 -6
  25. package/dist/adapter/resources/buffer.js.map +1 -1
  26. package/dist/adapter/resources/command-encoder.d.ts +27 -6
  27. package/dist/adapter/resources/command-encoder.d.ts.map +1 -1
  28. package/dist/adapter/resources/command-encoder.js +65 -1
  29. package/dist/adapter/resources/command-encoder.js.map +1 -1
  30. package/dist/adapter/resources/fence.d.ts +1 -1
  31. package/dist/adapter/resources/fence.d.ts.map +1 -1
  32. package/dist/adapter/resources/fence.js +3 -1
  33. package/dist/adapter/resources/fence.js.map +1 -1
  34. package/dist/adapter/resources/framebuffer.d.ts.map +1 -1
  35. package/dist/adapter/resources/framebuffer.js +9 -11
  36. package/dist/adapter/resources/framebuffer.js.map +1 -1
  37. package/dist/adapter/resources/query-set.d.ts +17 -1
  38. package/dist/adapter/resources/query-set.d.ts.map +1 -1
  39. package/dist/adapter/resources/query-set.js.map +1 -1
  40. package/dist/adapter/resources/render-pipeline.d.ts +19 -7
  41. package/dist/adapter/resources/render-pipeline.d.ts.map +1 -1
  42. package/dist/adapter/resources/render-pipeline.js +20 -2
  43. package/dist/adapter/resources/render-pipeline.js.map +1 -1
  44. package/dist/adapter/resources/resource.d.ts +8 -0
  45. package/dist/adapter/resources/resource.d.ts.map +1 -1
  46. package/dist/adapter/resources/resource.js +240 -14
  47. package/dist/adapter/resources/resource.js.map +1 -1
  48. package/dist/adapter/resources/shader.js +27 -25
  49. package/dist/adapter/resources/shader.js.map +1 -1
  50. package/dist/adapter/resources/shared-render-pipeline.d.ts +22 -0
  51. package/dist/adapter/resources/shared-render-pipeline.d.ts.map +1 -0
  52. package/dist/adapter/resources/shared-render-pipeline.js +25 -0
  53. package/dist/adapter/resources/shared-render-pipeline.js.map +1 -0
  54. package/dist/adapter/resources/texture.d.ts +78 -12
  55. package/dist/adapter/resources/texture.d.ts.map +1 -1
  56. package/dist/adapter/resources/texture.js +182 -30
  57. package/dist/adapter/resources/texture.js.map +1 -1
  58. package/dist/adapter-utils/format-compiler-log.d.ts.map +1 -1
  59. package/dist/adapter-utils/format-compiler-log.js +23 -15
  60. package/dist/adapter-utils/format-compiler-log.js.map +1 -1
  61. package/dist/dist.dev.js +1265 -362
  62. package/dist/dist.min.js +10 -9
  63. package/dist/index.cjs +1027 -243
  64. package/dist/index.cjs.map +4 -4
  65. package/dist/index.d.ts +5 -1
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +3 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/shadertypes/data-types/decode-shader-types.d.ts +2 -2
  70. package/dist/shadertypes/data-types/decode-shader-types.d.ts.map +1 -1
  71. package/dist/shadertypes/data-types/decode-shader-types.js +11 -2
  72. package/dist/shadertypes/data-types/decode-shader-types.js.map +1 -1
  73. package/dist/shadertypes/textures/pixel-utils.js +4 -4
  74. package/dist/shadertypes/textures/pixel-utils.js.map +1 -1
  75. package/dist/shadertypes/textures/texture-format-decoder.d.ts.map +1 -1
  76. package/dist/shadertypes/textures/texture-format-decoder.js +53 -8
  77. package/dist/shadertypes/textures/texture-format-decoder.js.map +1 -1
  78. package/dist/shadertypes/textures/texture-format-table.d.ts.map +1 -1
  79. package/dist/shadertypes/textures/texture-format-table.js +10 -9
  80. package/dist/shadertypes/textures/texture-format-table.js.map +1 -1
  81. package/dist/shadertypes/textures/texture-formats.d.ts +5 -2
  82. package/dist/shadertypes/textures/texture-formats.d.ts.map +1 -1
  83. package/dist/shadertypes/textures/texture-formats.js.map +1 -1
  84. package/dist/shadertypes/textures/texture-layout.d.ts +1 -1
  85. package/dist/utils/array-equal.d.ts +1 -1
  86. package/dist/utils/array-equal.d.ts.map +1 -1
  87. package/dist/utils/array-equal.js +15 -9
  88. package/dist/utils/array-equal.js.map +1 -1
  89. package/dist/utils/assert.d.ts +5 -0
  90. package/dist/utils/assert.d.ts.map +1 -0
  91. package/dist/utils/assert.js +17 -0
  92. package/dist/utils/assert.js.map +1 -0
  93. package/dist/utils/stats-manager.d.ts.map +1 -1
  94. package/dist/utils/stats-manager.js +61 -1
  95. package/dist/utils/stats-manager.js.map +1 -1
  96. package/package.json +6 -6
  97. package/src/adapter/canvas-context.ts +7 -590
  98. package/src/adapter/canvas-observer.ts +130 -0
  99. package/src/adapter/canvas-surface.ts +521 -0
  100. package/src/adapter/device.ts +174 -13
  101. package/src/adapter/presentation-context.ts +16 -0
  102. package/src/adapter/resources/buffer.ts +13 -5
  103. package/src/adapter/resources/command-encoder.ts +96 -7
  104. package/src/adapter/resources/fence.ts +3 -1
  105. package/src/adapter/resources/framebuffer.ts +9 -11
  106. package/src/adapter/resources/query-set.ts +17 -1
  107. package/src/adapter/resources/render-pipeline.ts +42 -13
  108. package/src/adapter/resources/resource.ts +284 -14
  109. package/src/adapter/resources/shader.ts +28 -28
  110. package/src/adapter/resources/shared-render-pipeline.ts +40 -0
  111. package/src/adapter/resources/texture.ts +269 -40
  112. package/src/adapter-utils/format-compiler-log.ts +23 -15
  113. package/src/index.ts +8 -0
  114. package/src/shadertypes/data-types/decode-shader-types.ts +13 -4
  115. package/src/shadertypes/textures/pixel-utils.ts +4 -4
  116. package/src/shadertypes/textures/texture-format-decoder.ts +73 -8
  117. package/src/shadertypes/textures/texture-format-table.ts +10 -9
  118. package/src/shadertypes/textures/texture-formats.ts +6 -1
  119. package/src/utils/array-equal.ts +21 -9
  120. package/src/utils/assert.ts +18 -0
  121. package/src/utils/stats-manager.ts +76 -2
package/dist/index.cjs CHANGED
@@ -39,12 +39,14 @@ __export(dist_exports, {
39
39
  Fence: () => Fence,
40
40
  Framebuffer: () => Framebuffer,
41
41
  PipelineLayout: () => PipelineLayout,
42
+ PresentationContext: () => PresentationContext,
42
43
  QuerySet: () => QuerySet,
43
44
  RenderPass: () => RenderPass,
44
45
  RenderPipeline: () => RenderPipeline,
45
46
  Resource: () => Resource,
46
47
  Sampler: () => Sampler,
47
48
  Shader: () => Shader,
49
+ SharedRenderPipeline: () => SharedRenderPipeline,
48
50
  Texture: () => Texture,
49
51
  TextureFormatDecoder: () => TextureFormatDecoder,
50
52
  TextureView: () => TextureView,
@@ -55,6 +57,8 @@ __export(dist_exports, {
55
57
  VertexArray: () => VertexArray,
56
58
  _getTextureFormatDefinition: () => getTextureFormatDefinition,
57
59
  _getTextureFormatTable: () => getTextureFormatTable,
60
+ assert: () => assert,
61
+ assertDefined: () => assertDefined,
58
62
  getAttributeInfosFromLayouts: () => getAttributeInfosFromLayouts,
59
63
  getAttributeShaderTypeInfo: () => getAttributeShaderTypeInfo,
60
64
  getDataType: () => getDataType,
@@ -80,6 +84,24 @@ module.exports = __toCommonJS(dist_exports);
80
84
 
81
85
  // dist/utils/stats-manager.js
82
86
  var import_stats = require("@probe.gl/stats");
87
+ var GPU_TIME_AND_MEMORY_STATS = "GPU Time and Memory";
88
+ var GPU_TIME_AND_MEMORY_STAT_ORDER = [
89
+ "Adapter",
90
+ "GPU",
91
+ "GPU Type",
92
+ "GPU Backend",
93
+ "Frame Rate",
94
+ "CPU Time",
95
+ "GPU Time",
96
+ "GPU Memory",
97
+ "Buffer Memory",
98
+ "Texture Memory",
99
+ "Referenced Buffer Memory",
100
+ "Referenced Texture Memory",
101
+ "Swap Chain Texture"
102
+ ];
103
+ var ORDERED_STATS_CACHE = /* @__PURE__ */ new WeakMap();
104
+ var ORDERED_STAT_NAME_SET_CACHE = /* @__PURE__ */ new WeakMap();
83
105
  var StatsManager = class {
84
106
  stats = /* @__PURE__ */ new Map();
85
107
  getStats(name2) {
@@ -89,10 +111,50 @@ var StatsManager = class {
89
111
  if (!this.stats.has(name2)) {
90
112
  this.stats.set(name2, new import_stats.Stats({ id: name2 }));
91
113
  }
92
- return this.stats.get(name2);
114
+ const stats = this.stats.get(name2);
115
+ if (name2 === GPU_TIME_AND_MEMORY_STATS) {
116
+ initializeStats(stats, GPU_TIME_AND_MEMORY_STAT_ORDER);
117
+ }
118
+ return stats;
93
119
  }
94
120
  };
95
121
  var lumaStats = new StatsManager();
122
+ function initializeStats(stats, orderedStatNames) {
123
+ const statsMap = stats.stats;
124
+ let addedOrderedStat = false;
125
+ for (const statName of orderedStatNames) {
126
+ if (!statsMap[statName]) {
127
+ stats.get(statName);
128
+ addedOrderedStat = true;
129
+ }
130
+ }
131
+ const statCount = Object.keys(statsMap).length;
132
+ const cachedStats = ORDERED_STATS_CACHE.get(stats);
133
+ if (!addedOrderedStat && (cachedStats == null ? void 0 : cachedStats.orderedStatNames) === orderedStatNames && cachedStats.statCount === statCount) {
134
+ return;
135
+ }
136
+ const reorderedStats = {};
137
+ let orderedStatNamesSet = ORDERED_STAT_NAME_SET_CACHE.get(orderedStatNames);
138
+ if (!orderedStatNamesSet) {
139
+ orderedStatNamesSet = new Set(orderedStatNames);
140
+ ORDERED_STAT_NAME_SET_CACHE.set(orderedStatNames, orderedStatNamesSet);
141
+ }
142
+ for (const statName of orderedStatNames) {
143
+ if (statsMap[statName]) {
144
+ reorderedStats[statName] = statsMap[statName];
145
+ }
146
+ }
147
+ for (const [statName, stat] of Object.entries(statsMap)) {
148
+ if (!orderedStatNamesSet.has(statName)) {
149
+ reorderedStats[statName] = stat;
150
+ }
151
+ }
152
+ for (const statName of Object.keys(statsMap)) {
153
+ delete statsMap[statName];
154
+ }
155
+ Object.assign(statsMap, reorderedStats);
156
+ ORDERED_STATS_CACHE.set(stats, { orderedStatNames, statCount });
157
+ }
96
158
 
97
159
  // dist/utils/log.js
98
160
  var import_log = require("@probe.gl/log");
@@ -107,6 +169,57 @@ function uid(id = "id") {
107
169
  }
108
170
 
109
171
  // dist/adapter/resources/resource.js
172
+ var CPU_HOTSPOT_PROFILER_MODULE = "cpu-hotspot-profiler";
173
+ var RESOURCE_COUNTS_STATS = "GPU Resource Counts";
174
+ var LEGACY_RESOURCE_COUNTS_STATS = "Resource Counts";
175
+ var GPU_TIME_AND_MEMORY_STATS2 = "GPU Time and Memory";
176
+ var BASE_RESOURCE_COUNT_ORDER = [
177
+ "Resources",
178
+ "Buffers",
179
+ "Textures",
180
+ "Samplers",
181
+ "TextureViews",
182
+ "Framebuffers",
183
+ "QuerySets",
184
+ "Shaders",
185
+ "RenderPipelines",
186
+ "ComputePipelines",
187
+ "PipelineLayouts",
188
+ "VertexArrays",
189
+ "RenderPasss",
190
+ "ComputePasss",
191
+ "CommandEncoders",
192
+ "CommandBuffers"
193
+ ];
194
+ var WEBGL_RESOURCE_COUNT_ORDER = [
195
+ "Resources",
196
+ "Buffers",
197
+ "Textures",
198
+ "Samplers",
199
+ "TextureViews",
200
+ "Framebuffers",
201
+ "QuerySets",
202
+ "Shaders",
203
+ "RenderPipelines",
204
+ "SharedRenderPipelines",
205
+ "ComputePipelines",
206
+ "PipelineLayouts",
207
+ "VertexArrays",
208
+ "RenderPasss",
209
+ "ComputePasss",
210
+ "CommandEncoders",
211
+ "CommandBuffers"
212
+ ];
213
+ var BASE_RESOURCE_COUNT_STAT_ORDER = BASE_RESOURCE_COUNT_ORDER.flatMap((resourceType) => [
214
+ `${resourceType} Created`,
215
+ `${resourceType} Active`
216
+ ]);
217
+ var WEBGL_RESOURCE_COUNT_STAT_ORDER = WEBGL_RESOURCE_COUNT_ORDER.flatMap((resourceType) => [
218
+ `${resourceType} Created`,
219
+ `${resourceType} Active`
220
+ ]);
221
+ var ORDERED_STATS_CACHE2 = /* @__PURE__ */ new WeakMap();
222
+ var ORDERED_STAT_NAME_SET_CACHE2 = /* @__PURE__ */ new WeakMap();
110
223
  var Resource = class {
111
224
  toString() {
112
225
  return `${this[Symbol.toStringTag] || this.constructor.name}:"${this.id}"`;
@@ -123,6 +236,8 @@ var Resource = class {
123
236
  destroyed = false;
124
237
  /** For resources that allocate GPU memory */
125
238
  allocatedBytes = 0;
239
+ /** Stats bucket currently holding the tracked allocation */
240
+ allocatedBytesName = null;
126
241
  /** Attached resources will be destroyed when this resource is destroyed. Tracks auto-created "sub" resources. */
127
242
  _attachedResources = /* @__PURE__ */ new Set();
128
243
  /**
@@ -144,6 +259,9 @@ var Resource = class {
144
259
  * destroy can be called on any resource to release it before it is garbage collected.
145
260
  */
146
261
  destroy() {
262
+ if (this.destroyed) {
263
+ return;
264
+ }
147
265
  this.destroyResource();
148
266
  }
149
267
  /** @deprecated Use destroy() */
@@ -182,7 +300,7 @@ var Resource = class {
182
300
  }
183
301
  /** Destroy all owned resources. Make sure the resources are no longer needed before calling. */
184
302
  destroyAttachedResources() {
185
- for (const resource of Object.values(this._attachedResources)) {
303
+ for (const resource of this._attachedResources) {
186
304
  resource.destroy();
187
305
  }
188
306
  this._attachedResources = /* @__PURE__ */ new Set();
@@ -190,37 +308,107 @@ var Resource = class {
190
308
  // PROTECTED METHODS
191
309
  /** Perform all destroy steps. Can be called by derived resources when overriding destroy() */
192
310
  destroyResource() {
311
+ if (this.destroyed) {
312
+ return;
313
+ }
193
314
  this.destroyAttachedResources();
194
315
  this.removeStats();
195
316
  this.destroyed = true;
196
317
  }
197
318
  /** Called by .destroy() to track object destruction. Subclass must call if overriding destroy() */
198
319
  removeStats() {
199
- const stats = this._device.statsManager.getStats("Resource Counts");
200
- const name2 = this[Symbol.toStringTag];
201
- stats.get(`${name2}s Active`).decrementCount();
320
+ const profiler = getCpuHotspotProfiler(this._device);
321
+ const startTime = profiler ? getTimestamp() : 0;
322
+ const statsObjects = [
323
+ this._device.statsManager.getStats(RESOURCE_COUNTS_STATS),
324
+ this._device.statsManager.getStats(LEGACY_RESOURCE_COUNTS_STATS)
325
+ ];
326
+ const orderedStatNames = getResourceCountStatOrder(this._device);
327
+ for (const stats of statsObjects) {
328
+ initializeStats2(stats, orderedStatNames);
329
+ }
330
+ const name2 = this.getStatsName();
331
+ for (const stats of statsObjects) {
332
+ stats.get("Resources Active").decrementCount();
333
+ stats.get(`${name2}s Active`).decrementCount();
334
+ }
335
+ if (profiler) {
336
+ profiler.statsBookkeepingCalls = (profiler.statsBookkeepingCalls || 0) + 1;
337
+ profiler.statsBookkeepingTimeMs = (profiler.statsBookkeepingTimeMs || 0) + (getTimestamp() - startTime);
338
+ }
202
339
  }
203
340
  /** Called by subclass to track memory allocations */
204
- trackAllocatedMemory(bytes, name2 = this[Symbol.toStringTag]) {
205
- const stats = this._device.statsManager.getStats("Resource Counts");
341
+ trackAllocatedMemory(bytes, name2 = this.getStatsName()) {
342
+ const profiler = getCpuHotspotProfiler(this._device);
343
+ const startTime = profiler ? getTimestamp() : 0;
344
+ const stats = this._device.statsManager.getStats(GPU_TIME_AND_MEMORY_STATS2);
345
+ if (this.allocatedBytes > 0 && this.allocatedBytesName) {
346
+ stats.get("GPU Memory").subtractCount(this.allocatedBytes);
347
+ stats.get(`${this.allocatedBytesName} Memory`).subtractCount(this.allocatedBytes);
348
+ }
206
349
  stats.get("GPU Memory").addCount(bytes);
207
350
  stats.get(`${name2} Memory`).addCount(bytes);
351
+ if (profiler) {
352
+ profiler.statsBookkeepingCalls = (profiler.statsBookkeepingCalls || 0) + 1;
353
+ profiler.statsBookkeepingTimeMs = (profiler.statsBookkeepingTimeMs || 0) + (getTimestamp() - startTime);
354
+ }
208
355
  this.allocatedBytes = bytes;
356
+ this.allocatedBytesName = name2;
357
+ }
358
+ /** Called by subclass to track handle-backed memory allocations separately from owned allocations */
359
+ trackReferencedMemory(bytes, name2 = this.getStatsName()) {
360
+ this.trackAllocatedMemory(bytes, `Referenced ${name2}`);
209
361
  }
210
362
  /** Called by subclass to track memory deallocations */
211
- trackDeallocatedMemory(name2 = this[Symbol.toStringTag]) {
212
- const stats = this._device.statsManager.getStats("Resource Counts");
363
+ trackDeallocatedMemory(name2 = this.getStatsName()) {
364
+ if (this.allocatedBytes === 0) {
365
+ this.allocatedBytesName = null;
366
+ return;
367
+ }
368
+ const profiler = getCpuHotspotProfiler(this._device);
369
+ const startTime = profiler ? getTimestamp() : 0;
370
+ const stats = this._device.statsManager.getStats(GPU_TIME_AND_MEMORY_STATS2);
213
371
  stats.get("GPU Memory").subtractCount(this.allocatedBytes);
214
- stats.get(`${name2} Memory`).subtractCount(this.allocatedBytes);
372
+ stats.get(`${this.allocatedBytesName || name2} Memory`).subtractCount(this.allocatedBytes);
373
+ if (profiler) {
374
+ profiler.statsBookkeepingCalls = (profiler.statsBookkeepingCalls || 0) + 1;
375
+ profiler.statsBookkeepingTimeMs = (profiler.statsBookkeepingTimeMs || 0) + (getTimestamp() - startTime);
376
+ }
215
377
  this.allocatedBytes = 0;
378
+ this.allocatedBytesName = null;
379
+ }
380
+ /** Called by subclass to deallocate handle-backed memory tracked via trackReferencedMemory() */
381
+ trackDeallocatedReferencedMemory(name2 = this.getStatsName()) {
382
+ this.trackDeallocatedMemory(`Referenced ${name2}`);
216
383
  }
217
384
  /** Called by resource constructor to track object creation */
218
385
  addStats() {
219
- const stats = this._device.statsManager.getStats("Resource Counts");
220
- const name2 = this[Symbol.toStringTag];
221
- stats.get("Resources Created").incrementCount();
222
- stats.get(`${name2}s Created`).incrementCount();
223
- stats.get(`${name2}s Active`).incrementCount();
386
+ const name2 = this.getStatsName();
387
+ const profiler = getCpuHotspotProfiler(this._device);
388
+ const startTime = profiler ? getTimestamp() : 0;
389
+ const statsObjects = [
390
+ this._device.statsManager.getStats(RESOURCE_COUNTS_STATS),
391
+ this._device.statsManager.getStats(LEGACY_RESOURCE_COUNTS_STATS)
392
+ ];
393
+ const orderedStatNames = getResourceCountStatOrder(this._device);
394
+ for (const stats of statsObjects) {
395
+ initializeStats2(stats, orderedStatNames);
396
+ }
397
+ for (const stats of statsObjects) {
398
+ stats.get("Resources Created").incrementCount();
399
+ stats.get("Resources Active").incrementCount();
400
+ stats.get(`${name2}s Created`).incrementCount();
401
+ stats.get(`${name2}s Active`).incrementCount();
402
+ }
403
+ if (profiler) {
404
+ profiler.statsBookkeepingCalls = (profiler.statsBookkeepingCalls || 0) + 1;
405
+ profiler.statsBookkeepingTimeMs = (profiler.statsBookkeepingTimeMs || 0) + (getTimestamp() - startTime);
406
+ }
407
+ recordTransientCanvasResourceCreate(this._device, name2);
408
+ }
409
+ /** Canonical resource name used for stats buckets. */
410
+ getStatsName() {
411
+ return getCanonicalResourceName(this);
224
412
  }
225
413
  };
226
414
  /** Default properties for resource */
@@ -238,6 +426,97 @@ function selectivelyMerge(props, defaultProps) {
238
426
  }
239
427
  return mergedProps;
240
428
  }
429
+ function initializeStats2(stats, orderedStatNames) {
430
+ const statsMap = stats.stats;
431
+ let addedOrderedStat = false;
432
+ for (const statName of orderedStatNames) {
433
+ if (!statsMap[statName]) {
434
+ stats.get(statName);
435
+ addedOrderedStat = true;
436
+ }
437
+ }
438
+ const statCount = Object.keys(statsMap).length;
439
+ const cachedStats = ORDERED_STATS_CACHE2.get(stats);
440
+ if (!addedOrderedStat && (cachedStats == null ? void 0 : cachedStats.orderedStatNames) === orderedStatNames && cachedStats.statCount === statCount) {
441
+ return;
442
+ }
443
+ const reorderedStats = {};
444
+ let orderedStatNamesSet = ORDERED_STAT_NAME_SET_CACHE2.get(orderedStatNames);
445
+ if (!orderedStatNamesSet) {
446
+ orderedStatNamesSet = new Set(orderedStatNames);
447
+ ORDERED_STAT_NAME_SET_CACHE2.set(orderedStatNames, orderedStatNamesSet);
448
+ }
449
+ for (const statName of orderedStatNames) {
450
+ if (statsMap[statName]) {
451
+ reorderedStats[statName] = statsMap[statName];
452
+ }
453
+ }
454
+ for (const [statName, stat] of Object.entries(statsMap)) {
455
+ if (!orderedStatNamesSet.has(statName)) {
456
+ reorderedStats[statName] = stat;
457
+ }
458
+ }
459
+ for (const statName of Object.keys(statsMap)) {
460
+ delete statsMap[statName];
461
+ }
462
+ Object.assign(statsMap, reorderedStats);
463
+ ORDERED_STATS_CACHE2.set(stats, { orderedStatNames, statCount });
464
+ }
465
+ function getResourceCountStatOrder(device) {
466
+ return device.type === "webgl" ? WEBGL_RESOURCE_COUNT_STAT_ORDER : BASE_RESOURCE_COUNT_STAT_ORDER;
467
+ }
468
+ function getCpuHotspotProfiler(device) {
469
+ const profiler = device.userData[CPU_HOTSPOT_PROFILER_MODULE];
470
+ return (profiler == null ? void 0 : profiler.enabled) ? profiler : null;
471
+ }
472
+ function getTimestamp() {
473
+ var _a, _b;
474
+ return ((_b = (_a = globalThis.performance) == null ? void 0 : _a.now) == null ? void 0 : _b.call(_a)) ?? Date.now();
475
+ }
476
+ function recordTransientCanvasResourceCreate(device, name2) {
477
+ const profiler = getCpuHotspotProfiler(device);
478
+ if (!profiler || !profiler.activeDefaultFramebufferAcquireDepth) {
479
+ return;
480
+ }
481
+ profiler.transientCanvasResourceCreates = (profiler.transientCanvasResourceCreates || 0) + 1;
482
+ switch (name2) {
483
+ case "Texture":
484
+ profiler.transientCanvasTextureCreates = (profiler.transientCanvasTextureCreates || 0) + 1;
485
+ break;
486
+ case "TextureView":
487
+ profiler.transientCanvasTextureViewCreates = (profiler.transientCanvasTextureViewCreates || 0) + 1;
488
+ break;
489
+ case "Sampler":
490
+ profiler.transientCanvasSamplerCreates = (profiler.transientCanvasSamplerCreates || 0) + 1;
491
+ break;
492
+ case "Framebuffer":
493
+ profiler.transientCanvasFramebufferCreates = (profiler.transientCanvasFramebufferCreates || 0) + 1;
494
+ break;
495
+ default:
496
+ break;
497
+ }
498
+ }
499
+ function getCanonicalResourceName(resource) {
500
+ let prototype = Object.getPrototypeOf(resource);
501
+ while (prototype) {
502
+ const parentPrototype = Object.getPrototypeOf(prototype);
503
+ if (!parentPrototype || parentPrototype === Resource.prototype) {
504
+ return getPrototypeToStringTag(prototype) || resource[Symbol.toStringTag] || resource.constructor.name;
505
+ }
506
+ prototype = parentPrototype;
507
+ }
508
+ return resource[Symbol.toStringTag] || resource.constructor.name;
509
+ }
510
+ function getPrototypeToStringTag(prototype) {
511
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, Symbol.toStringTag);
512
+ if (typeof (descriptor == null ? void 0 : descriptor.get) === "function") {
513
+ return descriptor.get.call(prototype);
514
+ }
515
+ if (typeof (descriptor == null ? void 0 : descriptor.value) === "string") {
516
+ return descriptor.value;
517
+ }
518
+ return null;
519
+ }
241
520
 
242
521
  // dist/adapter/resources/buffer.js
243
522
  var _Buffer = class extends Resource {
@@ -277,15 +556,23 @@ var _Buffer = class extends Resource {
277
556
  /** A partial CPU-side copy of the data in this buffer, for debugging purposes */
278
557
  debugData = new ArrayBuffer(0);
279
558
  /** This doesn't handle partial non-zero offset updates correctly */
280
- _setDebugData(data, byteOffset, byteLength) {
281
- const arrayBuffer2 = ArrayBuffer.isView(data) ? data.buffer : data;
559
+ _setDebugData(data, _byteOffset, byteLength) {
560
+ let arrayBufferView = null;
561
+ let arrayBuffer2;
562
+ if (ArrayBuffer.isView(data)) {
563
+ arrayBufferView = data;
564
+ arrayBuffer2 = data.buffer;
565
+ } else {
566
+ arrayBuffer2 = data;
567
+ }
282
568
  const debugDataLength = Math.min(data ? data.byteLength : byteLength, _Buffer.DEBUG_DATA_MAX_LENGTH);
283
569
  if (arrayBuffer2 === null) {
284
570
  this.debugData = new ArrayBuffer(debugDataLength);
285
- } else if (byteOffset === 0 && byteLength === arrayBuffer2.byteLength) {
286
- this.debugData = arrayBuffer2.slice(0, debugDataLength);
287
571
  } else {
288
- this.debugData = arrayBuffer2.slice(byteOffset, byteOffset + debugDataLength);
572
+ const sourceByteOffset = Math.min((arrayBufferView == null ? void 0 : arrayBufferView.byteOffset) || 0, arrayBuffer2.byteLength);
573
+ const availableByteLength = Math.max(0, arrayBuffer2.byteLength - sourceByteOffset);
574
+ const copyByteLength = Math.min(debugDataLength, availableByteLength);
575
+ this.debugData = new Uint8Array(arrayBuffer2, sourceByteOffset, copyByteLength).slice().buffer;
289
576
  }
290
577
  }
291
578
  };
@@ -481,6 +768,7 @@ var float32_renderable = "float32-renderable-webgl";
481
768
  var float16_renderable = "float16-renderable-webgl";
482
769
  var rgb9e5ufloat_renderable = "rgb9e5ufloat-renderable-webgl";
483
770
  var snorm8_renderable = "snorm8-renderable-webgl";
771
+ var norm16_webgl = "norm16-webgl";
484
772
  var norm16_renderable = "norm16-renderable-webgl";
485
773
  var snorm16_renderable = "snorm16-renderable-webgl";
486
774
  var float32_filterable = "float32-filterable";
@@ -514,16 +802,16 @@ var TEXTURE_FORMAT_COLOR_DEPTH_TABLE = {
514
802
  "rgba8sint": {},
515
803
  "bgra8unorm": {},
516
804
  "bgra8unorm-srgb": {},
517
- "r16unorm": { f: norm16_renderable },
518
- "rg16unorm": { render: norm16_renderable },
519
- "rgb16unorm-webgl": { f: norm16_renderable },
805
+ "r16unorm": { f: norm16_webgl, render: norm16_renderable },
806
+ "rg16unorm": { f: norm16_webgl, render: norm16_renderable },
807
+ "rgb16unorm-webgl": { f: norm16_webgl, render: false },
520
808
  // rgb not renderable
521
- "rgba16unorm": { render: norm16_renderable },
522
- "r16snorm": { f: snorm16_renderable },
523
- "rg16snorm": { render: snorm16_renderable },
524
- "rgb16snorm-webgl": { f: norm16_renderable },
809
+ "rgba16unorm": { f: norm16_webgl, render: norm16_renderable },
810
+ "r16snorm": { f: norm16_webgl, render: snorm16_renderable },
811
+ "rg16snorm": { f: norm16_webgl, render: snorm16_renderable },
812
+ "rgb16snorm-webgl": { f: norm16_webgl, render: false },
525
813
  // rgb not renderable
526
- "rgba16snorm": { render: snorm16_renderable },
814
+ "rgba16snorm": { f: norm16_webgl, render: snorm16_renderable },
527
815
  "r16uint": {},
528
816
  "rg16uint": {},
529
817
  "rgba16uint": {},
@@ -626,7 +914,7 @@ var TEXTURE_FORMAT_COMPRESSED_TABLE = {
626
914
  // WEBGL_compressed_texture_pvrtc
627
915
  "pvrtc-rgb4unorm-webgl": { f: texture_compression_pvrtc_webgl },
628
916
  "pvrtc-rgba4unorm-webgl": { f: texture_compression_pvrtc_webgl },
629
- "pvrtc-rbg2unorm-webgl": { f: texture_compression_pvrtc_webgl },
917
+ "pvrtc-rgb2unorm-webgl": { f: texture_compression_pvrtc_webgl },
630
918
  "pvrtc-rgba2unorm-webgl": { f: texture_compression_pvrtc_webgl },
631
919
  // WEBGL_compressed_texture_etc1
632
920
  "etc1-rbg-unorm-webgl": { f: texture_compression_etc1_webgl },
@@ -687,10 +975,13 @@ var TextureFormatDecoder = class {
687
975
  };
688
976
  var textureFormatDecoder = new TextureFormatDecoder();
689
977
  function computeTextureMemoryLayout({ format, width, height, depth, byteAlignment }) {
690
- const { bytesPerPixel } = textureFormatDecoder.getInfo(format);
691
- const unpaddedBytesPerRow = width * bytesPerPixel;
978
+ const formatInfo = textureFormatDecoder.getInfo(format);
979
+ const { bytesPerPixel, bytesPerBlock = bytesPerPixel, blockWidth = 1, blockHeight = 1, compressed = false } = formatInfo;
980
+ const blockColumns = compressed ? Math.ceil(width / blockWidth) : width;
981
+ const blockRows = compressed ? Math.ceil(height / blockHeight) : height;
982
+ const unpaddedBytesPerRow = blockColumns * bytesPerBlock;
692
983
  const bytesPerRow = Math.ceil(unpaddedBytesPerRow / byteAlignment) * byteAlignment;
693
- const rowsPerImage = height;
984
+ const rowsPerImage = blockRows;
694
985
  const byteLength = bytesPerRow * rowsPerImage * depth;
695
986
  return {
696
987
  bytesPerPixel,
@@ -716,7 +1007,8 @@ function getTextureFormatCapabilities(format) {
716
1007
  const isSigned = formatInfo == null ? void 0 : formatInfo.signed;
717
1008
  const isInteger = formatInfo == null ? void 0 : formatInfo.integer;
718
1009
  const isWebGLSpecific = formatInfo == null ? void 0 : formatInfo.webgl;
719
- formatCapabilities.render &&= !isSigned;
1010
+ const isCompressed = Boolean(formatInfo == null ? void 0 : formatInfo.compressed);
1011
+ formatCapabilities.render &&= !isDepthStencil && !isCompressed;
720
1012
  formatCapabilities.filter &&= !isDepthStencil && !isSigned && !isInteger && !isWebGLSpecific;
721
1013
  return formatCapabilities;
722
1014
  }
@@ -728,6 +1020,7 @@ function getTextureFormatInfo(format) {
728
1020
  formatInfo.bytesPerPixel = 1;
729
1021
  formatInfo.srgb = false;
730
1022
  formatInfo.compressed = true;
1023
+ formatInfo.bytesPerBlock = getCompressedTextureBlockByteLength(format);
731
1024
  const blockSize = getCompressedTextureBlockSize(format);
732
1025
  if (blockSize) {
733
1026
  formatInfo.blockWidth = blockSize.blockWidth;
@@ -740,7 +1033,7 @@ function getTextureFormatInfo(format) {
740
1033
  const dataType = `${type}${length}`;
741
1034
  const decodedType = getDataTypeInfo(dataType);
742
1035
  const bits = decodedType.byteLength * 8;
743
- const components = channels.length;
1036
+ const components = (channels == null ? void 0 : channels.length) ?? 1;
744
1037
  const bitsPerChannel = [
745
1038
  bits,
746
1039
  components >= 2 ? bits : 0,
@@ -757,7 +1050,7 @@ function getTextureFormatInfo(format) {
757
1050
  signed: decodedType.signed,
758
1051
  normalized: decodedType.normalized,
759
1052
  bitsPerChannel,
760
- bytesPerPixel: decodedType.byteLength * channels.length,
1053
+ bytesPerPixel: decodedType.byteLength * components,
761
1054
  packed: formatInfo.packed,
762
1055
  srgb: formatInfo.srgb
763
1056
  };
@@ -814,8 +1107,29 @@ function getCompressedTextureBlockSize(format) {
814
1107
  const [, blockWidth, blockHeight] = matches;
815
1108
  return { blockWidth: Number(blockWidth), blockHeight: Number(blockHeight) };
816
1109
  }
1110
+ if (format.startsWith("bc") || format.startsWith("etc1") || format.startsWith("etc2") || format.startsWith("eac") || format.startsWith("atc")) {
1111
+ return { blockWidth: 4, blockHeight: 4 };
1112
+ }
1113
+ if (format.startsWith("pvrtc-rgb4") || format.startsWith("pvrtc-rgba4")) {
1114
+ return { blockWidth: 4, blockHeight: 4 };
1115
+ }
1116
+ if (format.startsWith("pvrtc-rgb2") || format.startsWith("pvrtc-rgba2")) {
1117
+ return { blockWidth: 8, blockHeight: 4 };
1118
+ }
817
1119
  return null;
818
1120
  }
1121
+ function getCompressedTextureBlockByteLength(format) {
1122
+ if (format.startsWith("bc1") || format.startsWith("bc4") || format.startsWith("etc1") || format.startsWith("etc2-rgb8") || format.startsWith("etc2-rgb8a1") || format.startsWith("eac-r11") || format === "atc-rgb-unorm-webgl") {
1123
+ return 8;
1124
+ }
1125
+ if (format.startsWith("bc2") || format.startsWith("bc3") || format.startsWith("bc5") || format.startsWith("bc6h") || format.startsWith("bc7") || format.startsWith("etc2-rgba8") || format.startsWith("eac-rg11") || format.startsWith("astc") || format === "atc-rgba-unorm-webgl" || format === "atc-rgbai-unorm-webgl") {
1126
+ return 16;
1127
+ }
1128
+ if (format.startsWith("pvrtc")) {
1129
+ return 8;
1130
+ }
1131
+ return 16;
1132
+ }
819
1133
 
820
1134
  // dist/image-utils/image-types.js
821
1135
  function isExternalImage(data) {
@@ -877,6 +1191,8 @@ var _Device = class {
877
1191
  /** Used by other luma.gl modules to store data on the device */
878
1192
  _moduleData = {};
879
1193
  _textureCaps = {};
1194
+ /** Internal timestamp query set used when GPU timing collection is enabled for this device. */
1195
+ _debugGPUTimeQuery = null;
880
1196
  constructor(props) {
881
1197
  this.props = { ..._Device.defaultProps, ...props };
882
1198
  this.id = this.props.id || uid(this[Symbol.toStringTag].toLowerCase());
@@ -930,6 +1246,16 @@ var _Device = class {
930
1246
  isTextureFormatCompressed(format) {
931
1247
  return textureFormatDecoder.isCompressed(format);
932
1248
  }
1249
+ /** Returns the compressed texture formats that can be created and sampled on this device */
1250
+ getSupportedCompressedTextureFormats() {
1251
+ const supportedFormats = [];
1252
+ for (const format of Object.keys(getTextureFormatTable())) {
1253
+ if (this.isTextureFormatCompressed(format) && this.isTextureFormatSupported(format)) {
1254
+ supportedFormats.push(format);
1255
+ }
1256
+ }
1257
+ return supportedFormats;
1258
+ }
933
1259
  // DEBUG METHODS
934
1260
  pushDebugGroup(groupLabel) {
935
1261
  this.commandEncoder.pushDebugGroup(groupLabel);
@@ -1008,6 +1334,70 @@ or create a device with the 'debug: true' prop.`;
1008
1334
  beginComputePass(props) {
1009
1335
  return this.commandEncoder.beginComputePass(props);
1010
1336
  }
1337
+ /**
1338
+ * Generate mipmaps for a WebGPU texture.
1339
+ * WebGPU textures must be created up front with the required mip count, usage flags, and a format that supports the chosen generation path.
1340
+ * WebGL uses `Texture.generateMipmapsWebGL()` directly because the backend manages mip generation on the texture object itself.
1341
+ */
1342
+ generateMipmapsWebGPU(_texture) {
1343
+ throw new Error("not implemented");
1344
+ }
1345
+ /** Internal helper for creating a shareable WebGL render-pipeline implementation. */
1346
+ _createSharedRenderPipelineWebGL(_props) {
1347
+ throw new Error("_createSharedRenderPipelineWebGL() not implemented");
1348
+ }
1349
+ /**
1350
+ * Internal helper that returns `true` when timestamp-query GPU timing should be
1351
+ * collected for this device.
1352
+ */
1353
+ _supportsDebugGPUTime() {
1354
+ return this.features.has("timestamp-query") && Boolean(this.props.debug || this.props.debugGPUTime);
1355
+ }
1356
+ /**
1357
+ * Internal helper that enables device-managed GPU timing collection on the
1358
+ * default command encoder. Reuses the existing query set if timing is already enabled.
1359
+ *
1360
+ * @param queryCount - Number of timestamp slots reserved for profiled passes.
1361
+ * @returns The device-managed timestamp QuerySet, or `null` when timing is not supported or could not be enabled.
1362
+ */
1363
+ _enableDebugGPUTime(queryCount = 256) {
1364
+ if (!this._supportsDebugGPUTime()) {
1365
+ return null;
1366
+ }
1367
+ if (this._debugGPUTimeQuery) {
1368
+ return this._debugGPUTimeQuery;
1369
+ }
1370
+ try {
1371
+ this._debugGPUTimeQuery = this.createQuerySet({ type: "timestamp", count: queryCount });
1372
+ this.commandEncoder = this.createCommandEncoder({
1373
+ id: this.commandEncoder.props.id,
1374
+ timeProfilingQuerySet: this._debugGPUTimeQuery
1375
+ });
1376
+ } catch {
1377
+ this._debugGPUTimeQuery = null;
1378
+ }
1379
+ return this._debugGPUTimeQuery;
1380
+ }
1381
+ /**
1382
+ * Internal helper that disables device-managed GPU timing collection and restores
1383
+ * the default command encoder to an unprofiled state.
1384
+ */
1385
+ _disableDebugGPUTime() {
1386
+ if (!this._debugGPUTimeQuery) {
1387
+ return;
1388
+ }
1389
+ if (this.commandEncoder.getTimeProfilingQuerySet() === this._debugGPUTimeQuery) {
1390
+ this.commandEncoder = this.createCommandEncoder({
1391
+ id: this.commandEncoder.props.id
1392
+ });
1393
+ }
1394
+ this._debugGPUTimeQuery.destroy();
1395
+ this._debugGPUTimeQuery = null;
1396
+ }
1397
+ /** Internal helper that returns `true` when device-managed GPU timing is currently active. */
1398
+ _isDebugGPUTimeEnabled() {
1399
+ return this._debugGPUTimeQuery !== null;
1400
+ }
1011
1401
  // DEPRECATED METHODS
1012
1402
  /** @deprecated Use getDefaultCanvasContext() */
1013
1403
  getCanvasContext() {
@@ -1115,7 +1505,8 @@ __publicField(Device, "defaultProps", {
1115
1505
  onVisibilityChange: (context) => log.log(1, `${context} Visibility changed ${context.isVisible}`)(),
1116
1506
  onDevicePixelRatioChange: (context, info) => log.log(1, `${context} DPR changed ${info.oldRatio} => ${context.devicePixelRatio}`)(),
1117
1507
  // Debug flags
1118
- debug: log.get("debug") || void 0,
1508
+ debug: getDefaultDebugValue(),
1509
+ debugGPUTime: false,
1119
1510
  debugShaders: log.get("debug-shaders") || void 0,
1120
1511
  debugFramebuffers: Boolean(log.get("debug-framebuffers")),
1121
1512
  debugFactories: Boolean(log.get("debug-factories")),
@@ -1126,9 +1517,11 @@ __publicField(Device, "defaultProps", {
1126
1517
  // Experimental
1127
1518
  _reuseDevices: false,
1128
1519
  _requestMaxLimits: true,
1129
- _cacheShaders: false,
1130
- _cachePipelines: false,
1131
- _cacheDestroyPolicy: "unused",
1520
+ _cacheShaders: true,
1521
+ _destroyShaders: false,
1522
+ _cachePipelines: true,
1523
+ _sharePipelines: true,
1524
+ _destroyPipelines: false,
1132
1525
  // TODO - Change these after confirming things work as expected
1133
1526
  _initializeFeatures: true,
1134
1527
  _disabledFeatures: {
@@ -1137,6 +1530,25 @@ __publicField(Device, "defaultProps", {
1137
1530
  // INTERNAL
1138
1531
  _handle: void 0
1139
1532
  });
1533
+ function _getDefaultDebugValue(logDebugValue, nodeEnv) {
1534
+ if (logDebugValue !== void 0 && logDebugValue !== null) {
1535
+ return Boolean(logDebugValue);
1536
+ }
1537
+ if (nodeEnv !== void 0) {
1538
+ return nodeEnv !== "production";
1539
+ }
1540
+ return false;
1541
+ }
1542
+ function getDefaultDebugValue() {
1543
+ return _getDefaultDebugValue(log.get("debug"), getNodeEnv());
1544
+ }
1545
+ function getNodeEnv() {
1546
+ const processObject = globalThis.process;
1547
+ if (!(processObject == null ? void 0 : processObject.env)) {
1548
+ return void 0;
1549
+ }
1550
+ return processObject.env["NODE_ENV"];
1551
+ }
1140
1552
 
1141
1553
  // dist/adapter/luma.js
1142
1554
  var STARTUP_MESSAGE = "set luma.log.level=1 (or higher) to trace rendering";
@@ -1156,7 +1568,7 @@ var _Luma = class {
1156
1568
  VERSION = (
1157
1569
  // Version detection using build plugin
1158
1570
  // @ts-expect-error no-undef
1159
- true ? "9.3.0-alpha.2" : "running from source"
1571
+ true ? "9.3.0-alpha.6" : "running from source"
1160
1572
  );
1161
1573
  spector;
1162
1574
  preregisteredAdapters = /* @__PURE__ */ new Map();
@@ -1321,9 +1733,91 @@ function getPageLoadPromise() {
1321
1733
  return pageLoadPromise;
1322
1734
  }
1323
1735
 
1324
- // dist/adapter/canvas-context.js
1736
+ // dist/adapter/canvas-surface.js
1325
1737
  var import_env2 = require("@probe.gl/env");
1326
1738
 
1739
+ // dist/adapter/canvas-observer.js
1740
+ var CanvasObserver = class {
1741
+ props;
1742
+ _resizeObserver;
1743
+ _intersectionObserver;
1744
+ _observeDevicePixelRatioTimeout = null;
1745
+ _observeDevicePixelRatioMediaQuery = null;
1746
+ _handleDevicePixelRatioChange = () => this._refreshDevicePixelRatio();
1747
+ _trackPositionInterval = null;
1748
+ _started = false;
1749
+ get started() {
1750
+ return this._started;
1751
+ }
1752
+ constructor(props) {
1753
+ this.props = props;
1754
+ }
1755
+ start() {
1756
+ if (this._started || !this.props.canvas) {
1757
+ return;
1758
+ }
1759
+ this._started = true;
1760
+ this._intersectionObserver ||= new IntersectionObserver((entries) => this.props.onIntersection(entries));
1761
+ this._resizeObserver ||= new ResizeObserver((entries) => this.props.onResize(entries));
1762
+ this._intersectionObserver.observe(this.props.canvas);
1763
+ try {
1764
+ this._resizeObserver.observe(this.props.canvas, { box: "device-pixel-content-box" });
1765
+ } catch {
1766
+ this._resizeObserver.observe(this.props.canvas, { box: "content-box" });
1767
+ }
1768
+ this._observeDevicePixelRatioTimeout = setTimeout(() => this._refreshDevicePixelRatio(), 0);
1769
+ if (this.props.trackPosition) {
1770
+ this._trackPosition();
1771
+ }
1772
+ }
1773
+ stop() {
1774
+ var _a, _b;
1775
+ if (!this._started) {
1776
+ return;
1777
+ }
1778
+ this._started = false;
1779
+ if (this._observeDevicePixelRatioTimeout) {
1780
+ clearTimeout(this._observeDevicePixelRatioTimeout);
1781
+ this._observeDevicePixelRatioTimeout = null;
1782
+ }
1783
+ if (this._observeDevicePixelRatioMediaQuery) {
1784
+ this._observeDevicePixelRatioMediaQuery.removeEventListener("change", this._handleDevicePixelRatioChange);
1785
+ this._observeDevicePixelRatioMediaQuery = null;
1786
+ }
1787
+ if (this._trackPositionInterval) {
1788
+ clearInterval(this._trackPositionInterval);
1789
+ this._trackPositionInterval = null;
1790
+ }
1791
+ (_a = this._resizeObserver) == null ? void 0 : _a.disconnect();
1792
+ (_b = this._intersectionObserver) == null ? void 0 : _b.disconnect();
1793
+ }
1794
+ _refreshDevicePixelRatio() {
1795
+ var _a;
1796
+ if (!this._started) {
1797
+ return;
1798
+ }
1799
+ this.props.onDevicePixelRatioChange();
1800
+ (_a = this._observeDevicePixelRatioMediaQuery) == null ? void 0 : _a.removeEventListener("change", this._handleDevicePixelRatioChange);
1801
+ this._observeDevicePixelRatioMediaQuery = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
1802
+ this._observeDevicePixelRatioMediaQuery.addEventListener("change", this._handleDevicePixelRatioChange, { once: true });
1803
+ }
1804
+ _trackPosition(intervalMs = 100) {
1805
+ if (this._trackPositionInterval) {
1806
+ return;
1807
+ }
1808
+ this._trackPositionInterval = setInterval(() => {
1809
+ if (!this._started) {
1810
+ if (this._trackPositionInterval) {
1811
+ clearInterval(this._trackPositionInterval);
1812
+ this._trackPositionInterval = null;
1813
+ }
1814
+ } else {
1815
+ this.props.onPositionChange();
1816
+ }
1817
+ }, intervalMs);
1818
+ }
1819
+ };
1820
+
1327
1821
  // dist/utils/promise-utils.js
1328
1822
  function withResolvers() {
1329
1823
  let resolve;
@@ -1335,8 +1829,22 @@ function withResolvers() {
1335
1829
  return { promise, resolve, reject };
1336
1830
  }
1337
1831
 
1338
- // dist/adapter/canvas-context.js
1339
- var _CanvasContext = class {
1832
+ // dist/utils/assert.js
1833
+ function assert(condition, message) {
1834
+ var _a;
1835
+ if (!condition) {
1836
+ const error = new Error(message ?? "luma.gl assertion failed.");
1837
+ (_a = Error.captureStackTrace) == null ? void 0 : _a.call(Error, error, assert);
1838
+ throw error;
1839
+ }
1840
+ }
1841
+ function assertDefined(value, message) {
1842
+ assert(value, message);
1843
+ return value;
1844
+ }
1845
+
1846
+ // dist/adapter/canvas-surface.js
1847
+ var _CanvasSurface = class {
1340
1848
  static isHTMLCanvas(canvas) {
1341
1849
  return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
1342
1850
  }
@@ -1372,10 +1880,7 @@ var _CanvasContext = class {
1372
1880
  drawingBufferHeight;
1373
1881
  /** Resolves when the canvas is initialized, i.e. when the ResizeObserver has updated the pixel size */
1374
1882
  _initializedResolvers = withResolvers();
1375
- /** ResizeObserver to track canvas size changes */
1376
- _resizeObserver;
1377
- /** IntersectionObserver to track canvas visibility changes */
1378
- _intersectionObserver;
1883
+ _canvasObserver;
1379
1884
  /** Position of the canvas in the document, updated by a timer */
1380
1885
  _position = [0, 0];
1381
1886
  /** Whether this canvas context has been destroyed */
@@ -1387,7 +1892,7 @@ var _CanvasContext = class {
1387
1892
  }
1388
1893
  constructor(props) {
1389
1894
  var _a, _b;
1390
- this.props = { ..._CanvasContext.defaultProps, ...props };
1895
+ this.props = { ..._CanvasSurface.defaultProps, ...props };
1391
1896
  props = this.props;
1392
1897
  this.initialized = this._initializedResolvers.promise;
1393
1898
  if (!(0, import_env2.isBrowser)()) {
@@ -1399,11 +1904,11 @@ var _CanvasContext = class {
1399
1904
  } else {
1400
1905
  this.canvas = props.canvas;
1401
1906
  }
1402
- if (_CanvasContext.isHTMLCanvas(this.canvas)) {
1907
+ if (_CanvasSurface.isHTMLCanvas(this.canvas)) {
1403
1908
  this.id = props.id || this.canvas.id;
1404
1909
  this.type = "html-canvas";
1405
1910
  this.htmlCanvas = this.canvas;
1406
- } else if (_CanvasContext.isOffscreenCanvas(this.canvas)) {
1911
+ } else if (_CanvasSurface.isOffscreenCanvas(this.canvas)) {
1407
1912
  this.id = props.id || "offscreen-canvas";
1408
1913
  this.type = "offscreen-canvas";
1409
1914
  this.offscreenCanvas = this.canvas;
@@ -1419,23 +1924,21 @@ var _CanvasContext = class {
1419
1924
  this.drawingBufferHeight = this.canvas.height;
1420
1925
  this.devicePixelRatio = globalThis.devicePixelRatio || 1;
1421
1926
  this._position = [0, 0];
1422
- if (_CanvasContext.isHTMLCanvas(this.canvas)) {
1423
- this._intersectionObserver = new IntersectionObserver((entries) => this._handleIntersection(entries));
1424
- this._intersectionObserver.observe(this.canvas);
1425
- this._resizeObserver = new ResizeObserver((entries) => this._handleResize(entries));
1426
- try {
1427
- this._resizeObserver.observe(this.canvas, { box: "device-pixel-content-box" });
1428
- } catch {
1429
- this._resizeObserver.observe(this.canvas, { box: "content-box" });
1430
- }
1431
- setTimeout(() => this._observeDevicePixelRatio(), 0);
1432
- if (this.props.trackPosition) {
1433
- this._trackPosition();
1434
- }
1435
- }
1927
+ this._canvasObserver = new CanvasObserver({
1928
+ canvas: this.htmlCanvas,
1929
+ trackPosition: this.props.trackPosition,
1930
+ onResize: (entries) => this._handleResize(entries),
1931
+ onIntersection: (entries) => this._handleIntersection(entries),
1932
+ onDevicePixelRatioChange: () => this._observeDevicePixelRatio(),
1933
+ onPositionChange: () => this.updatePosition()
1934
+ });
1436
1935
  }
1437
1936
  destroy() {
1438
- this.destroyed = true;
1937
+ if (!this.destroyed) {
1938
+ this.destroyed = true;
1939
+ this._stopObservers();
1940
+ this.device = null;
1941
+ }
1439
1942
  }
1440
1943
  setProps(props) {
1441
1944
  if ("useDevicePixels" in props) {
@@ -1449,59 +1952,36 @@ var _CanvasContext = class {
1449
1952
  this._resizeDrawingBufferIfNeeded();
1450
1953
  return this._getCurrentFramebuffer(options);
1451
1954
  }
1452
- // SIZE METHODS
1453
- /**
1454
- * Returns the size covered by the canvas in CSS pixels
1455
- * @note This can be different from the actual device pixel size of a canvas due to DPR scaling, and rounding to integer pixels
1456
- * @note This is independent of the canvas' internal drawing buffer size (.width, .height).
1457
- */
1458
1955
  getCSSSize() {
1459
1956
  return [this.cssWidth, this.cssHeight];
1460
1957
  }
1461
1958
  getPosition() {
1462
1959
  return this._position;
1463
1960
  }
1464
- /**
1465
- * Returns the size covered by the canvas in actual device pixels.
1466
- * @note This can be different from the 'CSS' size of a canvas due to DPR scaling, and rounding to integer pixels
1467
- * @note This is independent of the canvas' internal drawing buffer size (.width, .height).
1468
- */
1469
1961
  getDevicePixelSize() {
1470
1962
  return [this.devicePixelWidth, this.devicePixelHeight];
1471
1963
  }
1472
- /** Get the drawing buffer size (number of pixels GPU is rendering into, can be different from CSS size) */
1473
1964
  getDrawingBufferSize() {
1474
1965
  return [this.drawingBufferWidth, this.drawingBufferHeight];
1475
1966
  }
1476
- /** Returns the biggest allowed framebuffer size. @todo Allow the application to limit this? */
1477
1967
  getMaxDrawingBufferSize() {
1478
1968
  const maxTextureDimension = this.device.limits.maxTextureDimension2D;
1479
1969
  return [maxTextureDimension, maxTextureDimension];
1480
1970
  }
1481
- /**
1482
- * Update the canvas drawing buffer size.
1483
- * @note - Called automatically if props.autoResize is true.
1484
- * @note - Defers update of drawing buffer size until framebuffer is requested to avoid flicker
1485
- * (resizing clears the drawing buffer)!
1486
- */
1487
1971
  setDrawingBufferSize(width, height) {
1488
- this.drawingBufferWidth = Math.floor(width);
1489
- this.drawingBufferHeight = Math.floor(height);
1972
+ width = Math.floor(width);
1973
+ height = Math.floor(height);
1974
+ if (this.drawingBufferWidth === width && this.drawingBufferHeight === height) {
1975
+ return;
1976
+ }
1977
+ this.drawingBufferWidth = width;
1978
+ this.drawingBufferHeight = height;
1490
1979
  this._needsDrawingBufferResize = true;
1491
1980
  }
1492
- /**
1493
- * Returns the current DPR (number of physical pixels per CSS pixel), if props.useDevicePixels is true
1494
- * @note This can be a fractional (non-integer) number, e.g. when the user zooms in the browser.
1495
- * @note This function handles the non-HTML canvas cases
1496
- */
1497
1981
  getDevicePixelRatio() {
1498
- const dpr = typeof window !== "undefined" && window.devicePixelRatio;
1499
- return dpr || 1;
1982
+ const devicePixelRatio2 = typeof window !== "undefined" && window.devicePixelRatio;
1983
+ return devicePixelRatio2 || 1;
1500
1984
  }
1501
- // DEPRECATED METHODS
1502
- /**
1503
- * Maps CSS pixel position to device pixel position
1504
- */
1505
1985
  cssToDevicePixels(cssPixel, yInvert = true) {
1506
1986
  const ratio = this.cssToDeviceRatio();
1507
1987
  const [width, height] = this.getDrawingBufferSize();
@@ -1511,10 +1991,10 @@ var _CanvasContext = class {
1511
1991
  getPixelSize() {
1512
1992
  return this.getDevicePixelSize();
1513
1993
  }
1514
- /** @deprecated - TODO which values should we use for aspect */
1994
+ /** @deprecated Use the current drawing buffer size for projection setup. */
1515
1995
  getAspect() {
1516
- const [width, height] = this.getDevicePixelSize();
1517
- return width / height;
1996
+ const [width, height] = this.getDrawingBufferSize();
1997
+ return width > 0 && height > 0 ? width / height : 1;
1518
1998
  }
1519
1999
  /** @deprecated Returns multiplier need to convert CSS size to Device size */
1520
2000
  cssToDeviceRatio() {
@@ -1530,19 +2010,41 @@ var _CanvasContext = class {
1530
2010
  resize(size) {
1531
2011
  this.setDrawingBufferSize(size.width, size.height);
1532
2012
  }
1533
- // IMPLEMENTATION
1534
- /**
1535
- * Allows subclass constructor to override the canvas id for auto created canvases.
1536
- * This can really help when debugging DOM in apps that create multiple devices
1537
- */
1538
2013
  _setAutoCreatedCanvasId(id) {
1539
2014
  var _a;
1540
2015
  if (((_a = this.htmlCanvas) == null ? void 0 : _a.id) === "lumagl-auto-created-canvas") {
1541
2016
  this.htmlCanvas.id = id;
1542
2017
  }
1543
2018
  }
1544
- /** reacts to an observed intersection */
2019
+ /**
2020
+ * Starts DOM observation after the derived context and its device are fully initialized.
2021
+ *
2022
+ * `CanvasSurface` construction runs before subclasses can assign `this.device`, and the
2023
+ * default WebGL canvas context is created before `WebGLDevice` has initialized `limits`,
2024
+ * `features`, and the rest of its runtime state. Deferring observer startup avoids early
2025
+ * `ResizeObserver` and DPR callbacks running against a partially initialized device.
2026
+ */
2027
+ _startObservers() {
2028
+ if (this.destroyed) {
2029
+ return;
2030
+ }
2031
+ this._canvasObserver.start();
2032
+ }
2033
+ /**
2034
+ * Stops all DOM observation and timers associated with a canvas surface.
2035
+ *
2036
+ * This pairs with `_startObservers()` so teardown uses the same lifecycle whether a context is
2037
+ * explicitly destroyed, abandoned during device reuse, or temporarily has not started observing
2038
+ * yet. Centralizing shutdown here keeps resize/DPR/position watchers from surviving past the
2039
+ * lifetime of the owning device.
2040
+ */
2041
+ _stopObservers() {
2042
+ this._canvasObserver.stop();
2043
+ }
1545
2044
  _handleIntersection(entries) {
2045
+ if (this.destroyed) {
2046
+ return;
2047
+ }
1546
2048
  const entry = entries.find((entry_) => entry_.target === this.canvas);
1547
2049
  if (!entry) {
1548
2050
  return;
@@ -1553,34 +2055,32 @@ var _CanvasContext = class {
1553
2055
  this.device.props.onVisibilityChange(this);
1554
2056
  }
1555
2057
  }
1556
- /**
1557
- * Reacts to an observed resize by using the most accurate pixel size information the browser can provide
1558
- * @see https://web.dev/articles/device-pixel-content-box
1559
- * @see https://webgpufundamentals.org/webgpu/lessons/webgpu-resizing-the-canvas.html
1560
- */
1561
2058
  _handleResize(entries) {
1562
- var _a, _b;
2059
+ var _a, _b, _c, _d, _e;
2060
+ if (this.destroyed) {
2061
+ return;
2062
+ }
1563
2063
  const entry = entries.find((entry_) => entry_.target === this.canvas);
1564
2064
  if (!entry) {
1565
2065
  return;
1566
2066
  }
1567
- this.cssWidth = entry.contentBoxSize[0].inlineSize;
1568
- this.cssHeight = entry.contentBoxSize[0].blockSize;
2067
+ const contentBoxSize = assertDefined((_a = entry.contentBoxSize) == null ? void 0 : _a[0]);
2068
+ this.cssWidth = contentBoxSize.inlineSize;
2069
+ this.cssHeight = contentBoxSize.blockSize;
1569
2070
  const oldPixelSize = this.getDevicePixelSize();
1570
- const devicePixelWidth = ((_a = entry.devicePixelContentBoxSize) == null ? void 0 : _a[0].inlineSize) || entry.contentBoxSize[0].inlineSize * devicePixelRatio;
1571
- const devicePixelHeight = ((_b = entry.devicePixelContentBoxSize) == null ? void 0 : _b[0].blockSize) || entry.contentBoxSize[0].blockSize * devicePixelRatio;
2071
+ const devicePixelWidth = ((_c = (_b = entry.devicePixelContentBoxSize) == null ? void 0 : _b[0]) == null ? void 0 : _c.inlineSize) || contentBoxSize.inlineSize * devicePixelRatio;
2072
+ const devicePixelHeight = ((_e = (_d = entry.devicePixelContentBoxSize) == null ? void 0 : _d[0]) == null ? void 0 : _e.blockSize) || contentBoxSize.blockSize * devicePixelRatio;
1572
2073
  const [maxDevicePixelWidth, maxDevicePixelHeight] = this.getMaxDrawingBufferSize();
1573
2074
  this.devicePixelWidth = Math.max(1, Math.min(devicePixelWidth, maxDevicePixelWidth));
1574
2075
  this.devicePixelHeight = Math.max(1, Math.min(devicePixelHeight, maxDevicePixelHeight));
1575
2076
  this._updateDrawingBufferSize();
1576
2077
  this.device.props.onResize(this, { oldPixelSize });
1577
2078
  }
1578
- /** Initiate a deferred update for the canvas drawing buffer size */
1579
2079
  _updateDrawingBufferSize() {
1580
2080
  if (this.props.autoResize) {
1581
2081
  if (typeof this.props.useDevicePixels === "number") {
1582
- const dpr = this.props.useDevicePixels;
1583
- this.setDrawingBufferSize(this.cssWidth * dpr, this.cssHeight * dpr);
2082
+ const devicePixelRatio2 = this.props.useDevicePixels;
2083
+ this.setDrawingBufferSize(this.cssWidth * devicePixelRatio2, this.cssHeight * devicePixelRatio2);
1584
2084
  } else if (this.props.useDevicePixels) {
1585
2085
  this.setDrawingBufferSize(this.devicePixelWidth, this.devicePixelHeight);
1586
2086
  } else {
@@ -1591,7 +2091,6 @@ var _CanvasContext = class {
1591
2091
  this.isInitialized = true;
1592
2092
  this.updatePosition();
1593
2093
  }
1594
- /** Perform a deferred resize of the drawing buffer if needed */
1595
2094
  _resizeDrawingBufferIfNeeded() {
1596
2095
  if (this._needsDrawingBufferResize) {
1597
2096
  this._needsDrawingBufferResize = false;
@@ -1603,31 +2102,23 @@ var _CanvasContext = class {
1603
2102
  }
1604
2103
  }
1605
2104
  }
1606
- /** Monitor DPR changes */
1607
2105
  _observeDevicePixelRatio() {
2106
+ var _a, _b;
2107
+ if (this.destroyed || !this._canvasObserver.started) {
2108
+ return;
2109
+ }
1608
2110
  const oldRatio = this.devicePixelRatio;
1609
2111
  this.devicePixelRatio = window.devicePixelRatio;
1610
2112
  this.updatePosition();
1611
- this.device.props.onDevicePixelRatioChange(this, { oldRatio });
1612
- matchMedia(`(resolution: ${this.devicePixelRatio}dppx)`).addEventListener("change", () => this._observeDevicePixelRatio(), { once: true });
1613
- }
1614
- /** Start tracking positions with a timer */
1615
- _trackPosition(intervalMs = 100) {
1616
- const intervalId = setInterval(() => {
1617
- if (this.destroyed) {
1618
- clearInterval(intervalId);
1619
- } else {
1620
- this.updatePosition();
1621
- }
1622
- }, intervalMs);
2113
+ (_b = (_a = this.device.props).onDevicePixelRatioChange) == null ? void 0 : _b.call(_a, this, {
2114
+ oldRatio
2115
+ });
1623
2116
  }
1624
- /**
1625
- * Calculated the absolute position of the canvas
1626
- * @note - getBoundingClientRect() is normally cheap but can be expensive
1627
- * if called before browser has finished a reflow. Should not be the case here.
1628
- */
1629
2117
  updatePosition() {
1630
2118
  var _a, _b, _c;
2119
+ if (this.destroyed) {
2120
+ return;
2121
+ }
1631
2122
  const newRect = (_a = this.htmlCanvas) == null ? void 0 : _a.getBoundingClientRect();
1632
2123
  if (newRect) {
1633
2124
  const position = [newRect.left, newRect.top];
@@ -1636,13 +2127,15 @@ var _CanvasContext = class {
1636
2127
  if (positionChanged) {
1637
2128
  const oldPosition = this._position;
1638
2129
  this._position = position;
1639
- (_c = (_b = this.device.props).onPositionChange) == null ? void 0 : _c.call(_b, this, { oldPosition });
2130
+ (_c = (_b = this.device.props).onPositionChange) == null ? void 0 : _c.call(_b, this, {
2131
+ oldPosition
2132
+ });
1640
2133
  }
1641
2134
  }
1642
2135
  }
1643
2136
  };
1644
- var CanvasContext = _CanvasContext;
1645
- __publicField(CanvasContext, "defaultProps", {
2137
+ var CanvasSurface = _CanvasSurface;
2138
+ __publicField(CanvasSurface, "defaultProps", {
1646
2139
  id: void 0,
1647
2140
  canvas: null,
1648
2141
  width: 800,
@@ -1670,7 +2163,7 @@ function getContainer(container) {
1670
2163
  }
1671
2164
  function getCanvasFromDOM(canvasId) {
1672
2165
  const canvas = document.getElementById(canvasId);
1673
- if (!CanvasContext.isHTMLCanvas(canvas)) {
2166
+ if (!CanvasSurface.isHTMLCanvas(canvas)) {
1674
2167
  throw new Error("Object is not a canvas element");
1675
2168
  }
1676
2169
  return canvas;
@@ -1694,33 +2187,40 @@ function scalePixels(pixel, ratio, width, height, yInvert) {
1694
2187
  const point = pixel;
1695
2188
  const x = scaleX(point[0], ratio, width);
1696
2189
  let y = scaleY(point[1], ratio, height, yInvert);
1697
- let t = scaleX(point[0] + 1, ratio, width);
1698
- const xHigh = t === width - 1 ? t : t - 1;
1699
- t = scaleY(point[1] + 1, ratio, height, yInvert);
2190
+ let temporary = scaleX(point[0] + 1, ratio, width);
2191
+ const xHigh = temporary === width - 1 ? temporary : temporary - 1;
2192
+ temporary = scaleY(point[1] + 1, ratio, height, yInvert);
1700
2193
  let yHigh;
1701
2194
  if (yInvert) {
1702
- t = t === 0 ? t : t + 1;
2195
+ temporary = temporary === 0 ? temporary : temporary + 1;
1703
2196
  yHigh = y;
1704
- y = t;
2197
+ y = temporary;
1705
2198
  } else {
1706
- yHigh = t === height - 1 ? t : t - 1;
2199
+ yHigh = temporary === height - 1 ? temporary : temporary - 1;
1707
2200
  }
1708
2201
  return {
1709
2202
  x,
1710
2203
  y,
1711
- // when ratio < 1, current css pixel and next css pixel may point to same device pixel, set width/height to 1 in those cases.
1712
2204
  width: Math.max(xHigh - x + 1, 1),
1713
2205
  height: Math.max(yHigh - y + 1, 1)
1714
2206
  };
1715
2207
  }
1716
2208
  function scaleX(x, ratio, width) {
1717
- const r = Math.min(Math.round(x * ratio), width - 1);
1718
- return r;
2209
+ return Math.min(Math.round(x * ratio), width - 1);
1719
2210
  }
1720
2211
  function scaleY(y, ratio, height, yInvert) {
1721
2212
  return yInvert ? Math.max(0, height - 1 - Math.round(y * ratio)) : Math.min(Math.round(y * ratio), height - 1);
1722
2213
  }
1723
2214
 
2215
+ // dist/adapter/canvas-context.js
2216
+ var CanvasContext = class extends CanvasSurface {
2217
+ };
2218
+ __publicField(CanvasContext, "defaultProps", CanvasSurface.defaultProps);
2219
+
2220
+ // dist/adapter/presentation-context.js
2221
+ var PresentationContext = class extends CanvasSurface {
2222
+ };
2223
+
1724
2224
  // dist/adapter/resources/sampler.js
1725
2225
  var _Sampler = class extends Resource {
1726
2226
  get [Symbol.toStringTag]() {
@@ -1775,6 +2275,8 @@ var _Texture = class extends Resource {
1775
2275
  depth;
1776
2276
  /** mip levels in this texture */
1777
2277
  mipLevels;
2278
+ /** sample count */
2279
+ samples;
1778
2280
  /** Rows are multiples of this length, padded with extra bytes if needed */
1779
2281
  byteAlignment;
1780
2282
  /** The ready promise is always resolved. It is provided for type compatibility with DynamicTexture. */
@@ -1800,6 +2302,7 @@ var _Texture = class extends Resource {
1800
2302
  this.height = this.props.height;
1801
2303
  this.depth = this.props.depth;
1802
2304
  this.mipLevels = this.props.mipLevels;
2305
+ this.samples = this.props.samples || 1;
1803
2306
  if (this.dimension === "cube") {
1804
2307
  this.depth = 6;
1805
2308
  }
@@ -1831,9 +2334,25 @@ var _Texture = class extends Resource {
1831
2334
  setSampler(sampler) {
1832
2335
  this.sampler = sampler instanceof Sampler ? sampler : this.device.createSampler(sampler);
1833
2336
  }
2337
+ /**
2338
+ * Copy raw image data (bytes) into the texture.
2339
+ *
2340
+ * @note Deprecated compatibility wrapper over {@link writeData}.
2341
+ * @note Uses the same layout defaults and alignment rules as {@link writeData}.
2342
+ * @note Tightly packed CPU uploads can omit `bytesPerRow` and `rowsPerImage`.
2343
+ * @note If the CPU source rows are padded, pass explicit `bytesPerRow` and `rowsPerImage`.
2344
+ * @deprecated Use writeData()
2345
+ */
2346
+ copyImageData(options) {
2347
+ const { data, depth, ...writeOptions } = options;
2348
+ this.writeData(data, {
2349
+ ...writeOptions,
2350
+ depthOrArrayLayers: writeOptions.depthOrArrayLayers ?? depth
2351
+ });
2352
+ }
1834
2353
  /**
1835
2354
  * Calculates the memory layout of the texture, required when reading and writing data.
1836
- * @return the memory layout of the texture, in particular bytesPerRow which includes required padding
2355
+ * @return the backend-aligned linear layout, in particular bytesPerRow which includes any required padding for buffer copy/read paths
1837
2356
  */
1838
2357
  computeMemoryLayout(options_ = {}) {
1839
2358
  const options = this._normalizeTextureReadOptions(options_);
@@ -1852,9 +2371,11 @@ var _Texture = class extends Resource {
1852
2371
  * @returns A Buffer containing the texture data.
1853
2372
  *
1854
2373
  * @note The memory layout of the texture data is determined by the texture format and dimensions.
1855
- * @note The application can call Texture.computeMemoryLayout() to compute the layout.
2374
+ * @note The application can call Texture.computeMemoryLayout() to compute the backend-aligned layout.
1856
2375
  * @note The application can call Buffer.readAsync()
1857
2376
  * @note If not supplied a buffer will be created and the application needs to call Buffer.destroy
2377
+ * @note On WebGPU this corresponds to a texture-to-buffer copy and uses buffer-copy alignment rules.
2378
+ * @note On WebGL, luma.gl emulates the same logical readback behavior.
1858
2379
  */
1859
2380
  readBuffer(options, buffer) {
1860
2381
  throw new Error("readBuffer not implemented");
@@ -1870,10 +2391,14 @@ var _Texture = class extends Resource {
1870
2391
  throw new Error("readBuffer not implemented");
1871
2392
  }
1872
2393
  /**
1873
- * Writes an GPU Buffer into a texture.
2394
+ * Writes a GPU Buffer into a texture.
1874
2395
  *
2396
+ * @param buffer - Source GPU buffer.
2397
+ * @param options - Destination subresource, extent, and source layout options.
1875
2398
  * @note The memory layout of the texture data is determined by the texture format and dimensions.
1876
- * @note The application can call Texture.computeMemoryLayout() to compute the layout.
2399
+ * @note The application can call Texture.computeMemoryLayout() to compute the backend-aligned layout.
2400
+ * @note On WebGPU this corresponds to a buffer-to-texture copy and uses buffer-copy alignment rules.
2401
+ * @note On WebGL, luma.gl emulates the same destination and layout semantics.
1877
2402
  */
1878
2403
  writeBuffer(buffer, options) {
1879
2404
  throw new Error("readBuffer not implemented");
@@ -1881,8 +2406,11 @@ var _Texture = class extends Resource {
1881
2406
  /**
1882
2407
  * Writes an array buffer into a texture.
1883
2408
  *
1884
- * @note The memory layout of the texture data is determined by the texture format and dimensions.
1885
- * @note The application can call Texture.computeMemoryLayout() to compute the layout.
2409
+ * @param data - Source texel data.
2410
+ * @param options - Destination subresource, extent, and source layout options.
2411
+ * @note If `bytesPerRow` and `rowsPerImage` are omitted, luma.gl computes a tightly packed CPU-memory layout for the requested region.
2412
+ * @note On WebGPU this corresponds to `GPUQueue.writeTexture()` and does not implicitly pad rows to 256 bytes.
2413
+ * @note On WebGL, padded CPU data is supported via the same `bytesPerRow` and `rowsPerImage` options.
1886
2414
  */
1887
2415
  writeData(data, options) {
1888
2416
  throw new Error("readBuffer not implemented");
@@ -1945,37 +2473,148 @@ var _Texture = class extends Resource {
1945
2473
  }
1946
2474
  }
1947
2475
  _normalizeCopyImageDataOptions(options_) {
1948
- const { width, height, depth } = this;
1949
- const options = { ..._Texture.defaultCopyDataOptions, width, height, depth, ...options_ };
1950
- const info = this.device.getTextureFormatInfo(this.format);
1951
- if (!options_.bytesPerRow && !info.bytesPerPixel) {
1952
- throw new Error(`bytesPerRow must be provided for texture format ${this.format}`);
1953
- }
1954
- options.bytesPerRow = options_.bytesPerRow || width * (info.bytesPerPixel || 4);
1955
- options.rowsPerImage = options_.rowsPerImage || height;
1956
- return options;
2476
+ const { data, depth, ...writeOptions } = options_;
2477
+ const options = this._normalizeTextureWriteOptions({
2478
+ ...writeOptions,
2479
+ depthOrArrayLayers: writeOptions.depthOrArrayLayers ?? depth
2480
+ });
2481
+ return { data, depth: options.depthOrArrayLayers, ...options };
1957
2482
  }
1958
2483
  _normalizeCopyExternalImageOptions(options_) {
2484
+ const optionsWithoutUndefined = _Texture._omitUndefined(options_);
2485
+ const mipLevel = optionsWithoutUndefined.mipLevel ?? 0;
2486
+ const mipLevelSize = this._getMipLevelSize(mipLevel);
1959
2487
  const size = this.device.getExternalImageSize(options_.image);
1960
- const options = { ..._Texture.defaultCopyExternalImageOptions, ...size, ...options_ };
1961
- options.width = Math.min(options.width, this.width - options.x);
1962
- options.height = Math.min(options.height, this.height - options.y);
2488
+ const options = {
2489
+ ..._Texture.defaultCopyExternalImageOptions,
2490
+ ...mipLevelSize,
2491
+ ...size,
2492
+ ...optionsWithoutUndefined
2493
+ };
2494
+ options.width = Math.min(options.width, mipLevelSize.width - options.x);
2495
+ options.height = Math.min(options.height, mipLevelSize.height - options.y);
2496
+ options.depth = Math.min(options.depth, mipLevelSize.depthOrArrayLayers - options.z);
1963
2497
  return options;
1964
2498
  }
1965
2499
  _normalizeTextureReadOptions(options_) {
1966
- const { width, height } = this;
1967
- const options = { ..._Texture.defaultTextureReadOptions, width, height, ...options_ };
1968
- options.width = Math.min(options.width, this.width - options.x);
1969
- options.height = Math.min(options.height, this.height - options.y);
2500
+ const optionsWithoutUndefined = _Texture._omitUndefined(options_);
2501
+ const mipLevel = optionsWithoutUndefined.mipLevel ?? 0;
2502
+ const mipLevelSize = this._getMipLevelSize(mipLevel);
2503
+ const options = {
2504
+ ..._Texture.defaultTextureReadOptions,
2505
+ ...mipLevelSize,
2506
+ ...optionsWithoutUndefined
2507
+ };
2508
+ options.width = Math.min(options.width, mipLevelSize.width - options.x);
2509
+ options.height = Math.min(options.height, mipLevelSize.height - options.y);
2510
+ options.depthOrArrayLayers = Math.min(options.depthOrArrayLayers, mipLevelSize.depthOrArrayLayers - options.z);
1970
2511
  return options;
1971
2512
  }
2513
+ /**
2514
+ * Normalizes a texture read request and validates the color-only readback contract used by the
2515
+ * current texture read APIs. Supported dimensions are `2d`, `cube`, `cube-array`,
2516
+ * `2d-array`, and `3d`.
2517
+ *
2518
+ * @throws if the texture format, aspect, or dimension is not supported by the first-pass
2519
+ * color-read implementation.
2520
+ */
2521
+ _getSupportedColorReadOptions(options_) {
2522
+ const options = this._normalizeTextureReadOptions(options_);
2523
+ const formatInfo = textureFormatDecoder.getInfo(this.format);
2524
+ this._validateColorReadAspect(options);
2525
+ this._validateColorReadFormat(formatInfo);
2526
+ switch (this.dimension) {
2527
+ case "2d":
2528
+ case "cube":
2529
+ case "cube-array":
2530
+ case "2d-array":
2531
+ case "3d":
2532
+ return options;
2533
+ default:
2534
+ throw new Error(`${this} color readback does not support ${this.dimension} textures`);
2535
+ }
2536
+ }
2537
+ /** Validates that a read request targets the full color aspect of the texture. */
2538
+ _validateColorReadAspect(options) {
2539
+ if (options.aspect !== "all") {
2540
+ throw new Error(`${this} color readback only supports aspect 'all'`);
2541
+ }
2542
+ }
2543
+ /** Validates that a read request targets an uncompressed color-renderable texture format. */
2544
+ _validateColorReadFormat(formatInfo) {
2545
+ if (formatInfo.compressed) {
2546
+ throw new Error(`${this} color readback does not support compressed formats (${this.format})`);
2547
+ }
2548
+ switch (formatInfo.attachment) {
2549
+ case "color":
2550
+ return;
2551
+ case "depth":
2552
+ throw new Error(`${this} color readback does not support depth formats (${this.format})`);
2553
+ case "stencil":
2554
+ throw new Error(`${this} color readback does not support stencil formats (${this.format})`);
2555
+ case "depth-stencil":
2556
+ throw new Error(`${this} color readback does not support depth-stencil formats (${this.format})`);
2557
+ default:
2558
+ throw new Error(`${this} color readback does not support format ${this.format}`);
2559
+ }
2560
+ }
1972
2561
  _normalizeTextureWriteOptions(options_) {
1973
- const { width, height } = this;
1974
- const options = { ..._Texture.defaultTextureReadOptions, width, height, ...options_ };
1975
- options.width = Math.min(options.width, this.width - options.x);
1976
- options.height = Math.min(options.height, this.height - options.y);
2562
+ const optionsWithoutUndefined = _Texture._omitUndefined(options_);
2563
+ const mipLevel = optionsWithoutUndefined.mipLevel ?? 0;
2564
+ const mipLevelSize = this._getMipLevelSize(mipLevel);
2565
+ const options = {
2566
+ ..._Texture.defaultTextureWriteOptions,
2567
+ ...mipLevelSize,
2568
+ ...optionsWithoutUndefined
2569
+ };
2570
+ options.width = Math.min(options.width, mipLevelSize.width - options.x);
2571
+ options.height = Math.min(options.height, mipLevelSize.height - options.y);
2572
+ options.depthOrArrayLayers = Math.min(options.depthOrArrayLayers, mipLevelSize.depthOrArrayLayers - options.z);
2573
+ const layout = textureFormatDecoder.computeMemoryLayout({
2574
+ format: this.format,
2575
+ width: options.width,
2576
+ height: options.height,
2577
+ depth: options.depthOrArrayLayers,
2578
+ byteAlignment: this.byteAlignment
2579
+ });
2580
+ const minimumBytesPerRow = layout.bytesPerPixel * options.width;
2581
+ options.bytesPerRow = optionsWithoutUndefined.bytesPerRow ?? layout.bytesPerRow;
2582
+ options.rowsPerImage = optionsWithoutUndefined.rowsPerImage ?? options.height;
2583
+ if (options.bytesPerRow < minimumBytesPerRow) {
2584
+ throw new Error(`bytesPerRow (${options.bytesPerRow}) must be at least ${minimumBytesPerRow} for ${this.format}`);
2585
+ }
2586
+ if (options.rowsPerImage < options.height) {
2587
+ throw new Error(`rowsPerImage (${options.rowsPerImage}) must be at least ${options.height} for ${this.format}`);
2588
+ }
2589
+ const bytesPerPixel = this.device.getTextureFormatInfo(this.format).bytesPerPixel;
2590
+ if (bytesPerPixel && options.bytesPerRow % bytesPerPixel !== 0) {
2591
+ throw new Error(`bytesPerRow (${options.bytesPerRow}) must be a multiple of bytesPerPixel (${bytesPerPixel}) for ${this.format}`);
2592
+ }
1977
2593
  return options;
1978
2594
  }
2595
+ _getMipLevelSize(mipLevel) {
2596
+ const width = Math.max(1, this.width >> mipLevel);
2597
+ const height = this.baseDimension === "1d" ? 1 : Math.max(1, this.height >> mipLevel);
2598
+ const depthOrArrayLayers = this.dimension === "3d" ? Math.max(1, this.depth >> mipLevel) : this.depth;
2599
+ return { width, height, depthOrArrayLayers };
2600
+ }
2601
+ getAllocatedByteLength() {
2602
+ let allocatedByteLength = 0;
2603
+ for (let mipLevel = 0; mipLevel < this.mipLevels; mipLevel++) {
2604
+ const { width, height, depthOrArrayLayers } = this._getMipLevelSize(mipLevel);
2605
+ allocatedByteLength += textureFormatDecoder.computeMemoryLayout({
2606
+ format: this.format,
2607
+ width,
2608
+ height,
2609
+ depth: depthOrArrayLayers,
2610
+ byteAlignment: 1
2611
+ }).byteLength;
2612
+ }
2613
+ return allocatedByteLength * this.samples;
2614
+ }
2615
+ static _omitUndefined(options) {
2616
+ return Object.fromEntries(Object.entries(options).filter(([, value]) => value !== void 0));
2617
+ }
1979
2618
  };
1980
2619
  var Texture = _Texture;
1981
2620
  /** The texture can be bound for use as a sampled texture in a shader */
@@ -2011,6 +2650,10 @@ __publicField(Texture, "defaultCopyDataOptions", {
2011
2650
  byteOffset: 0,
2012
2651
  bytesPerRow: void 0,
2013
2652
  rowsPerImage: void 0,
2653
+ width: void 0,
2654
+ height: void 0,
2655
+ depthOrArrayLayers: void 0,
2656
+ depth: 1,
2014
2657
  mipLevel: 0,
2015
2658
  x: 0,
2016
2659
  y: 0,
@@ -2044,6 +2687,19 @@ __publicField(Texture, "defaultTextureReadOptions", {
2044
2687
  mipLevel: 0,
2045
2688
  aspect: "all"
2046
2689
  });
2690
+ __publicField(Texture, "defaultTextureWriteOptions", {
2691
+ byteOffset: 0,
2692
+ bytesPerRow: void 0,
2693
+ rowsPerImage: void 0,
2694
+ x: 0,
2695
+ y: 0,
2696
+ z: 0,
2697
+ width: void 0,
2698
+ height: void 0,
2699
+ depthOrArrayLayers: 1,
2700
+ mipLevel: 0,
2701
+ aspect: "all"
2702
+ });
2047
2703
 
2048
2704
  // dist/adapter/resources/texture-view.js
2049
2705
  var _TextureView = class extends Resource {
@@ -2090,24 +2746,32 @@ function formatCompilerLog(shaderLog, source, options) {
2090
2746
  const log2 = shaderLog.slice().sort((a, b) => a.lineNum - b.lineNum);
2091
2747
  switch ((options == null ? void 0 : options.showSourceCode) || "no") {
2092
2748
  case "all":
2093
- let currentMessage = 0;
2749
+ let currentMessageIndex = 0;
2094
2750
  for (let lineNum = 1; lineNum <= lines.length; lineNum++) {
2095
- formattedLog += getNumberedLine(lines[lineNum - 1], lineNum, options);
2096
- while (log2.length > currentMessage && log2[currentMessage].lineNum === lineNum) {
2097
- const message = log2[currentMessage++];
2098
- formattedLog += formatCompilerMessage(message, lines, message.lineNum, {
2751
+ const line = lines[lineNum - 1];
2752
+ const currentMessage = log2[currentMessageIndex];
2753
+ if (line && currentMessage) {
2754
+ formattedLog += getNumberedLine(line, lineNum, options);
2755
+ }
2756
+ while (log2.length > currentMessageIndex && currentMessage.lineNum === lineNum) {
2757
+ const message = log2[currentMessageIndex++];
2758
+ if (message) {
2759
+ formattedLog += formatCompilerMessage(message, lines, message.lineNum, {
2760
+ ...options,
2761
+ inlineSource: false
2762
+ });
2763
+ }
2764
+ }
2765
+ }
2766
+ while (log2.length > currentMessageIndex) {
2767
+ const message = log2[currentMessageIndex++];
2768
+ if (message) {
2769
+ formattedLog += formatCompilerMessage(message, [], 0, {
2099
2770
  ...options,
2100
2771
  inlineSource: false
2101
2772
  });
2102
2773
  }
2103
2774
  }
2104
- while (log2.length > currentMessage) {
2105
- const message = log2[currentMessage++];
2106
- formattedLog += formatCompilerMessage(message, [], 0, {
2107
- ...options,
2108
- inlineSource: false
2109
- });
2110
- }
2111
2775
  return formattedLog;
2112
2776
  case "issues":
2113
2777
  case "no":
@@ -2129,8 +2793,8 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
2129
2793
 
2130
2794
  `;
2131
2795
  }
2132
- const color = message.type === "error" ? "red" : "#8B4000";
2133
- return (options == null ? void 0 : options.html) ? `<div class='luma-compiler-log-error' style="color:${color};"><b> ${message.type.toUpperCase()}: ${message.message}</b></div>` : `${message.type.toUpperCase()}: ${message.message}`;
2796
+ const color = message.type === "error" ? "red" : "orange";
2797
+ return (options == null ? void 0 : options.html) ? `<div class='luma-compiler-log-${message.type}' style="color:${color};"><b> ${message.type.toUpperCase()}: ${message.message}</b></div>` : `${message.type.toUpperCase()}: ${message.message}`;
2134
2798
  }
2135
2799
  function getNumberedLines(lines, lineNum, options) {
2136
2800
  let numberedLines = "";
@@ -2211,35 +2875,39 @@ var _Shader = class extends Resource {
2211
2875
  * TODO - this HTML formatting code should not be in Device, should be pluggable
2212
2876
  */
2213
2877
  _displayShaderLog(messages, shaderId) {
2214
- var _a;
2215
2878
  if (typeof document === "undefined" || !(document == null ? void 0 : document.createElement)) {
2216
2879
  return;
2217
2880
  }
2218
2881
  const shaderName = shaderId;
2219
2882
  const shaderTitle = `${this.stage} shader "${shaderName}"`;
2220
- let htmlLog = formatCompilerLog(messages, this.source, { showSourceCode: "all", html: true });
2883
+ const htmlLog = formatCompilerLog(messages, this.source, { showSourceCode: "all", html: true });
2221
2884
  const translatedSource = this.getTranslatedSource();
2885
+ const container = document.createElement("div");
2886
+ container.innerHTML = `<h1>Compilation error in ${shaderTitle}</h1>
2887
+ <div style="display:flex;position:fixed;top:10px;right:20px;gap:2px;">
2888
+ <button id="copy">Copy source</button><br/>
2889
+ <button id="close">Close</button>
2890
+ </div>
2891
+ <code><pre>${htmlLog}</pre></code>`;
2222
2892
  if (translatedSource) {
2223
- htmlLog += `<br /><br /><h1>Translated Source</h1><br /><br /><code style="user-select:text;"><pre>${translatedSource}</pre></code>`;
2224
- }
2225
- const button = document.createElement("Button");
2226
- button.innerHTML = `
2227
- <h1>Compilation error in ${shaderTitle}</h1><br /><br />
2228
- <code style="user-select:text;"><pre>
2229
- ${htmlLog}
2230
- </pre></code>`;
2231
- button.style.top = "10px";
2232
- button.style.left = "10px";
2233
- button.style.position = "absolute";
2234
- button.style.zIndex = "9999";
2235
- button.style.width = "100%";
2236
- button.style.textAlign = "left";
2237
- document.body.appendChild(button);
2238
- const errors = document.getElementsByClassName("luma-compiler-log-error");
2239
- (_a = errors[0]) == null ? void 0 : _a.scrollIntoView();
2240
- button.onclick = () => {
2241
- const dataURI = `data:text/plain,${encodeURIComponent(this.source)}`;
2242
- navigator.clipboard.writeText(dataURI);
2893
+ container.innerHTML += `<br /><h1>Translated Source</h1><br /><br /><code><pre>${translatedSource}</pre></code>`;
2894
+ }
2895
+ container.style.top = "0";
2896
+ container.style.left = "0";
2897
+ container.style.background = "white";
2898
+ container.style.position = "fixed";
2899
+ container.style.zIndex = "9999";
2900
+ container.style.maxWidth = "100vw";
2901
+ container.style.maxHeight = "100vh";
2902
+ container.style.overflowY = "auto";
2903
+ document.body.appendChild(container);
2904
+ const error = container.querySelector(".luma-compiler-log-error");
2905
+ error == null ? void 0 : error.scrollIntoView();
2906
+ container.querySelector("button#close").onclick = () => {
2907
+ container.remove();
2908
+ };
2909
+ container.querySelector("button#copy").onclick = () => {
2910
+ navigator.clipboard.writeText(this.source);
2243
2911
  };
2244
2912
  }
2245
2913
  };
@@ -2259,7 +2927,7 @@ function getShaderIdFromProps(props) {
2259
2927
  function getShaderName(shader, defaultName = "unnamed") {
2260
2928
  const SHADER_NAME_REGEXP = /#define[\s*]SHADER_NAME[\s*]([A-Za-z0-9_-]+)[\s*]/;
2261
2929
  const match = SHADER_NAME_REGEXP.exec(shader);
2262
- return match ? match[1] : defaultName;
2930
+ return (match == null ? void 0 : match[1]) ?? defaultName;
2263
2931
  }
2264
2932
 
2265
2933
  // dist/adapter/resources/framebuffer.js
@@ -2363,17 +3031,15 @@ var _Framebuffer = class extends Resource {
2363
3031
  * and destroys existing textures if owned
2364
3032
  */
2365
3033
  resizeAttachments(width, height) {
2366
- for (let i = 0; i < this.colorAttachments.length; ++i) {
2367
- if (this.colorAttachments[i]) {
2368
- const resizedTexture = this.colorAttachments[i].texture.clone({
2369
- width,
2370
- height
2371
- });
2372
- this.destroyAttachedResource(this.colorAttachments[i]);
2373
- this.colorAttachments[i] = resizedTexture.view;
2374
- this.attachResource(resizedTexture.view);
2375
- }
2376
- }
3034
+ this.colorAttachments.forEach((colorAttachment, i) => {
3035
+ const resizedTexture = colorAttachment.texture.clone({
3036
+ width,
3037
+ height
3038
+ });
3039
+ this.destroyAttachedResource(colorAttachment);
3040
+ this.colorAttachments[i] = resizedTexture.view;
3041
+ this.attachResource(resizedTexture.view);
3042
+ });
2377
3043
  if (this.depthStencilAttachment) {
2378
3044
  const resizedTexture = this.depthStencilAttachment.texture.clone({
2379
3045
  width,
@@ -2410,10 +3076,23 @@ var _RenderPipeline = class extends Resource {
2410
3076
  linkStatus = "pending";
2411
3077
  /** The hash of the pipeline */
2412
3078
  hash = "";
3079
+ /** Optional shared backend implementation */
3080
+ sharedRenderPipeline = null;
3081
+ /** Whether shader or pipeline compilation/linking is still in progress */
3082
+ get isPending() {
3083
+ var _a;
3084
+ return this.linkStatus === "pending" || this.vs.compilationStatus === "pending" || ((_a = this.fs) == null ? void 0 : _a.compilationStatus) === "pending";
3085
+ }
3086
+ /** Whether shader or pipeline compilation/linking has failed */
3087
+ get isErrored() {
3088
+ var _a;
3089
+ return this.linkStatus === "error" || this.vs.compilationStatus === "error" || ((_a = this.fs) == null ? void 0 : _a.compilationStatus) === "error";
3090
+ }
2413
3091
  constructor(device, props) {
2414
3092
  super(device, props, _RenderPipeline.defaultProps);
2415
3093
  this.shaderLayout = this.props.shaderLayout;
2416
3094
  this.bufferLayout = this.props.bufferLayout || [];
3095
+ this.sharedRenderPipeline = this.props._sharedRenderPipeline || null;
2417
3096
  }
2418
3097
  };
2419
3098
  var RenderPipeline = _RenderPipeline;
@@ -2431,10 +3110,30 @@ __publicField(RenderPipeline, "defaultProps", {
2431
3110
  colorAttachmentFormats: void 0,
2432
3111
  depthStencilAttachmentFormat: void 0,
2433
3112
  parameters: {},
2434
- bindings: {},
2435
- uniforms: {}
3113
+ varyings: void 0,
3114
+ bufferMode: void 0,
3115
+ disableWarnings: false,
3116
+ _sharedRenderPipeline: void 0,
3117
+ bindings: void 0
2436
3118
  });
2437
3119
 
3120
+ // dist/adapter/resources/shared-render-pipeline.js
3121
+ var SharedRenderPipeline = class extends Resource {
3122
+ get [Symbol.toStringTag]() {
3123
+ return "SharedRenderPipeline";
3124
+ }
3125
+ constructor(device, props) {
3126
+ super(device, props, {
3127
+ ...Resource.defaultProps,
3128
+ handle: void 0,
3129
+ vs: void 0,
3130
+ fs: void 0,
3131
+ varyings: void 0,
3132
+ bufferMode: void 0
3133
+ });
3134
+ }
3135
+ };
3136
+
2438
3137
  // dist/adapter/resources/render-pass.js
2439
3138
  var _RenderPass = class extends Resource {
2440
3139
  get [Symbol.toStringTag]() {
@@ -2517,8 +3216,69 @@ var _CommandEncoder = class extends Resource {
2517
3216
  get [Symbol.toStringTag]() {
2518
3217
  return "CommandEncoder";
2519
3218
  }
3219
+ _timeProfilingQuerySet = null;
3220
+ _timeProfilingSlotCount = 0;
3221
+ _gpuTimeMs;
2520
3222
  constructor(device, props) {
2521
3223
  super(device, props, _CommandEncoder.defaultProps);
3224
+ this._timeProfilingQuerySet = props.timeProfilingQuerySet ?? null;
3225
+ this._timeProfilingSlotCount = 0;
3226
+ this._gpuTimeMs = void 0;
3227
+ }
3228
+ /**
3229
+ * Reads all resolved timestamp pairs on the current profiler query set and caches the sum
3230
+ * as milliseconds on this encoder.
3231
+ */
3232
+ async resolveTimeProfilingQuerySet() {
3233
+ this._gpuTimeMs = void 0;
3234
+ if (!this._timeProfilingQuerySet) {
3235
+ return;
3236
+ }
3237
+ const pairCount = Math.floor(this._timeProfilingSlotCount / 2);
3238
+ if (pairCount <= 0) {
3239
+ return;
3240
+ }
3241
+ const queryCount = pairCount * 2;
3242
+ const results = await this._timeProfilingQuerySet.readResults({
3243
+ firstQuery: 0,
3244
+ queryCount
3245
+ });
3246
+ let totalDurationNanoseconds = 0n;
3247
+ for (let queryIndex = 0; queryIndex < queryCount; queryIndex += 2) {
3248
+ totalDurationNanoseconds += results[queryIndex + 1] - results[queryIndex];
3249
+ }
3250
+ this._gpuTimeMs = Number(totalDurationNanoseconds) / 1e6;
3251
+ }
3252
+ /** Returns the number of query slots consumed by automatic pass profiling on this encoder. */
3253
+ getTimeProfilingSlotCount() {
3254
+ return this._timeProfilingSlotCount;
3255
+ }
3256
+ getTimeProfilingQuerySet() {
3257
+ return this._timeProfilingQuerySet;
3258
+ }
3259
+ /** Internal helper for auto-assigning timestamp slots to render/compute passes on this encoder. */
3260
+ _applyTimeProfilingToPassProps(props) {
3261
+ const passProps = props || {};
3262
+ if (!this._supportsTimestampQueries() || !this._timeProfilingQuerySet) {
3263
+ return passProps;
3264
+ }
3265
+ if (passProps.timestampQuerySet !== void 0 || passProps.beginTimestampIndex !== void 0 || passProps.endTimestampIndex !== void 0) {
3266
+ return passProps;
3267
+ }
3268
+ const beginTimestampIndex = this._timeProfilingSlotCount;
3269
+ if (beginTimestampIndex + 1 >= this._timeProfilingQuerySet.props.count) {
3270
+ return passProps;
3271
+ }
3272
+ this._timeProfilingSlotCount += 2;
3273
+ return {
3274
+ ...passProps,
3275
+ timestampQuerySet: this._timeProfilingQuerySet,
3276
+ beginTimestampIndex,
3277
+ endTimestampIndex: beginTimestampIndex + 1
3278
+ };
3279
+ }
3280
+ _supportsTimestampQueries() {
3281
+ return this.device.features.has("timestamp-query");
2522
3282
  }
2523
3283
  };
2524
3284
  var CommandEncoder = _CommandEncoder;
@@ -2527,7 +3287,8 @@ var CommandEncoder = _CommandEncoder;
2527
3287
  // beginComputePass(optional GPUComputePassDescriptor descriptor = {}): GPUComputePassEncoder;
2528
3288
  __publicField(CommandEncoder, "defaultProps", {
2529
3289
  ...Resource.defaultProps,
2530
- measureExecutionTime: void 0
3290
+ measureExecutionTime: void 0,
3291
+ timeProfilingQuerySet: void 0
2531
3292
  });
2532
3293
 
2533
3294
  // dist/adapter/resources/command-buffer.js
@@ -2546,11 +3307,20 @@ __publicField(CommandBuffer, "defaultProps", {
2546
3307
 
2547
3308
  // dist/shadertypes/data-types/decode-shader-types.js
2548
3309
  function getVariableShaderTypeInfo(format) {
2549
- const decoded = UNIFORM_FORMATS[format];
3310
+ const resolvedFormat = resolveVariableShaderTypeAlias(format);
3311
+ const decoded = UNIFORM_FORMATS[resolvedFormat];
3312
+ if (!decoded) {
3313
+ throw new Error(`Unsupported variable shader type: ${format}`);
3314
+ }
2550
3315
  return decoded;
2551
3316
  }
2552
3317
  function getAttributeShaderTypeInfo(attributeType) {
2553
- const [primitiveType, components] = TYPE_INFO[attributeType];
3318
+ const resolvedAttributeType = resolveAttributeShaderTypeAlias(attributeType);
3319
+ const decoded = TYPE_INFO[resolvedAttributeType];
3320
+ if (!decoded) {
3321
+ throw new Error(`Unsupported attribute shader type: ${attributeType}`);
3322
+ }
3323
+ const [primitiveType, components] = decoded;
2554
3324
  const integer = primitiveType === "i32" || primitiveType === "u32";
2555
3325
  const signed = primitiveType !== "u32";
2556
3326
  const byteLength = PRIMITIVE_TYPE_SIZES[primitiveType] * components;
@@ -2562,6 +3332,12 @@ function getAttributeShaderTypeInfo(attributeType) {
2562
3332
  signed
2563
3333
  };
2564
3334
  }
3335
+ function resolveAttributeShaderTypeAlias(alias) {
3336
+ return WGSL_ATTRIBUTE_TYPE_ALIAS_MAP[alias] || alias;
3337
+ }
3338
+ function resolveVariableShaderTypeAlias(alias) {
3339
+ return WGSL_VARIABLE_TYPE_ALIAS_MAP[alias] || alias;
3340
+ }
2565
3341
  var PRIMITIVE_TYPE_SIZES = {
2566
3342
  f32: 4,
2567
3343
  f16: 2,
@@ -2880,7 +3656,9 @@ __publicField(QuerySet, "defaultProps", {
2880
3656
 
2881
3657
  // dist/adapter/resources/fence.js
2882
3658
  var _Fence = class extends Resource {
2883
- [Symbol.toStringTag] = "WEBGLFence";
3659
+ get [Symbol.toStringTag]() {
3660
+ return "Fence";
3661
+ }
2884
3662
  constructor(device, props = {}) {
2885
3663
  super(device, props, _Fence.defaultProps);
2886
3664
  }
@@ -3040,20 +3818,26 @@ function isNumberArray(value) {
3040
3818
  }
3041
3819
 
3042
3820
  // dist/utils/array-equal.js
3821
+ var MAX_ELEMENTWISE_ARRAY_COMPARE_LENGTH = 128;
3043
3822
  function arrayEqual(a, b, limit = 16) {
3044
- if (a !== b) {
3045
- return false;
3823
+ if (a === b) {
3824
+ return true;
3046
3825
  }
3047
3826
  const arrayA = a;
3048
3827
  const arrayB = b;
3049
- if (!isNumberArray(arrayA)) {
3828
+ if (!isNumberArray(arrayA) || !isNumberArray(arrayB)) {
3050
3829
  return false;
3051
3830
  }
3052
- if (isNumberArray(arrayB) && arrayA.length === arrayB.length) {
3053
- for (let i = 0; i < arrayA.length; ++i) {
3054
- if (arrayB[i] !== arrayA[i]) {
3055
- return false;
3056
- }
3831
+ if (arrayA.length !== arrayB.length) {
3832
+ return false;
3833
+ }
3834
+ const maxCompareLength = Math.min(limit, MAX_ELEMENTWISE_ARRAY_COMPARE_LENGTH);
3835
+ if (arrayA.length > maxCompareLength) {
3836
+ return false;
3837
+ }
3838
+ for (let i = 0; i < arrayA.length; ++i) {
3839
+ if (arrayB[i] !== arrayA[i]) {
3840
+ return false;
3057
3841
  }
3058
3842
  }
3059
3843
  return true;
@@ -3273,7 +4057,7 @@ function readPixel(pixelData, x, y, bitsPerChannel) {
3273
4057
  let bitOffsetWithinPixel = 0;
3274
4058
  const channels = [];
3275
4059
  for (let i = 0; i < 4; i++) {
3276
- const bits = bitsPerChannel[i];
4060
+ const bits = bitsPerChannel[i] ?? 0;
3277
4061
  if (bits <= 0) {
3278
4062
  channels.push(0);
3279
4063
  } else {
@@ -3282,14 +4066,14 @@ function readPixel(pixelData, x, y, bitsPerChannel) {
3282
4066
  bitOffsetWithinPixel += bits;
3283
4067
  }
3284
4068
  }
3285
- return [channels[0], channels[1], channels[2], channels[3]];
4069
+ return [channels[0] ?? 0, channels[1] ?? 0, channels[2] ?? 0, channels[3] ?? 0];
3286
4070
  }
3287
4071
  function writePixel(dataView, bitOffset, bitsPerChannel, pixel) {
3288
4072
  let currentBitOffset = bitOffset;
3289
4073
  for (let channel = 0; channel < 4; channel++) {
3290
- const bits = bitsPerChannel[channel];
4074
+ const bits = bitsPerChannel[channel] ?? 0;
3291
4075
  const maxValue = (1 << bits) - 1;
3292
- const channelValue = pixel[channel] & maxValue;
4076
+ const channelValue = (pixel[channel] ?? 0) & maxValue;
3293
4077
  writeBitsToDataView(dataView, currentBitOffset, bits, channelValue);
3294
4078
  currentBitOffset += bits;
3295
4079
  }