@luma.gl/core 9.2.6 → 9.3.0-alpha.4

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 (86) hide show
  1. package/dist/adapter/canvas-context.d.ts +23 -3
  2. package/dist/adapter/canvas-context.d.ts.map +1 -1
  3. package/dist/adapter/canvas-context.js +77 -15
  4. package/dist/adapter/canvas-context.js.map +1 -1
  5. package/dist/adapter/device.d.ts +5 -5
  6. package/dist/adapter/device.d.ts.map +1 -1
  7. package/dist/adapter/device.js +12 -2
  8. package/dist/adapter/device.js.map +1 -1
  9. package/dist/adapter/luma.js +1 -1
  10. package/dist/adapter/luma.js.map +1 -1
  11. package/dist/adapter/resources/command-encoder.d.ts +5 -5
  12. package/dist/adapter/resources/command-encoder.d.ts.map +1 -1
  13. package/dist/adapter/resources/fence.d.ts +16 -0
  14. package/dist/adapter/resources/fence.d.ts.map +1 -0
  15. package/dist/adapter/resources/fence.js +15 -0
  16. package/dist/adapter/resources/fence.js.map +1 -0
  17. package/dist/adapter/resources/framebuffer.d.ts.map +1 -1
  18. package/dist/adapter/resources/framebuffer.js +15 -12
  19. package/dist/adapter/resources/framebuffer.js.map +1 -1
  20. package/dist/adapter/resources/resource.d.ts +5 -0
  21. package/dist/adapter/resources/resource.d.ts.map +1 -1
  22. package/dist/adapter/resources/resource.js +3 -0
  23. package/dist/adapter/resources/resource.js.map +1 -1
  24. package/dist/adapter/resources/shader.js +27 -25
  25. package/dist/adapter/resources/shader.js.map +1 -1
  26. package/dist/adapter/resources/texture.d.ts +97 -24
  27. package/dist/adapter/resources/texture.d.ts.map +1 -1
  28. package/dist/adapter/resources/texture.js +116 -10
  29. package/dist/adapter/resources/texture.js.map +1 -1
  30. package/dist/adapter-utils/format-compiler-log.d.ts.map +1 -1
  31. package/dist/adapter-utils/format-compiler-log.js +23 -15
  32. package/dist/adapter-utils/format-compiler-log.js.map +1 -1
  33. package/dist/dist.dev.js +695 -279
  34. package/dist/dist.min.js +10 -9
  35. package/dist/index.cjs +481 -161
  36. package/dist/index.cjs.map +4 -4
  37. package/dist/index.d.ts +6 -2
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +5 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/portable/uniform-buffer-layout.d.ts +13 -4
  42. package/dist/portable/uniform-buffer-layout.d.ts.map +1 -1
  43. package/dist/portable/uniform-buffer-layout.js +88 -55
  44. package/dist/portable/uniform-buffer-layout.js.map +1 -1
  45. package/dist/shadertypes/data-types/decode-data-types.d.ts.map +1 -1
  46. package/dist/shadertypes/data-types/decode-data-types.js +2 -1
  47. package/dist/shadertypes/data-types/decode-data-types.js.map +1 -1
  48. package/dist/shadertypes/data-types/shader-types.d.ts +5 -7
  49. package/dist/shadertypes/data-types/shader-types.d.ts.map +1 -1
  50. package/dist/shadertypes/textures/pixel-utils.js +4 -4
  51. package/dist/shadertypes/textures/pixel-utils.js.map +1 -1
  52. package/dist/shadertypes/textures/texture-format-decoder.d.ts +25 -8
  53. package/dist/shadertypes/textures/texture-format-decoder.d.ts.map +1 -1
  54. package/dist/shadertypes/textures/texture-format-decoder.js +60 -34
  55. package/dist/shadertypes/textures/texture-format-decoder.js.map +1 -1
  56. package/dist/shadertypes/textures/texture-formats.d.ts +43 -16
  57. package/dist/shadertypes/textures/texture-formats.d.ts.map +1 -1
  58. package/dist/shadertypes/textures/texture-formats.js.map +1 -1
  59. package/dist/shadertypes/textures/texture-layout.d.ts +5 -0
  60. package/dist/shadertypes/textures/texture-layout.d.ts.map +1 -0
  61. package/dist/shadertypes/textures/texture-layout.js +41 -0
  62. package/dist/shadertypes/textures/texture-layout.js.map +1 -0
  63. package/dist/utils/assert.d.ts +5 -0
  64. package/dist/utils/assert.d.ts.map +1 -0
  65. package/dist/utils/assert.js +17 -0
  66. package/dist/utils/assert.js.map +1 -0
  67. package/package.json +5 -5
  68. package/src/adapter/canvas-context.ts +87 -20
  69. package/src/adapter/device.ts +23 -5
  70. package/src/adapter/resources/command-buffer.ts +1 -1
  71. package/src/adapter/resources/command-encoder.ts +7 -7
  72. package/src/adapter/resources/fence.ts +30 -0
  73. package/src/adapter/resources/framebuffer.ts +15 -12
  74. package/src/adapter/resources/resource.ts +5 -0
  75. package/src/adapter/resources/shader.ts +28 -28
  76. package/src/adapter/resources/texture.ts +176 -28
  77. package/src/adapter-utils/format-compiler-log.ts +23 -15
  78. package/src/index.ts +12 -2
  79. package/src/portable/uniform-buffer-layout.ts +122 -63
  80. package/src/shadertypes/data-types/decode-data-types.ts +2 -1
  81. package/src/shadertypes/data-types/shader-types.ts +14 -7
  82. package/src/shadertypes/textures/pixel-utils.ts +4 -4
  83. package/src/shadertypes/textures/texture-format-decoder.ts +97 -42
  84. package/src/shadertypes/textures/texture-formats.ts +54 -15
  85. package/src/shadertypes/textures/texture-layout.ts +60 -0
  86. package/src/utils/assert.ts +18 -0
package/dist/index.cjs CHANGED
@@ -36,6 +36,7 @@ __export(dist_exports, {
36
36
  DeviceFeatures: () => DeviceFeatures,
37
37
  DeviceLimits: () => DeviceLimits,
38
38
  ExternalTexture: () => ExternalTexture,
39
+ Fence: () => Fence,
39
40
  Framebuffer: () => Framebuffer,
40
41
  PipelineLayout: () => PipelineLayout,
41
42
  QuerySet: () => QuerySet,
@@ -54,20 +55,26 @@ __export(dist_exports, {
54
55
  VertexArray: () => VertexArray,
55
56
  _getTextureFormatDefinition: () => getTextureFormatDefinition,
56
57
  _getTextureFormatTable: () => getTextureFormatTable,
58
+ assert: () => assert,
59
+ assertDefined: () => assertDefined,
57
60
  getAttributeInfosFromLayouts: () => getAttributeInfosFromLayouts,
58
61
  getAttributeShaderTypeInfo: () => getAttributeShaderTypeInfo,
59
62
  getDataType: () => getDataType,
60
63
  getDataTypeInfo: () => getDataTypeInfo,
64
+ getExternalImageSize: () => getExternalImageSize,
61
65
  getNormalizedDataType: () => getNormalizedDataType,
62
66
  getScratchArray: () => getScratchArray,
67
+ getTextureImageView: () => getTextureImageView,
63
68
  getTypedArrayConstructor: () => getTypedArrayConstructor,
64
69
  getVariableShaderTypeInfo: () => getVariableShaderTypeInfo,
65
70
  getVertexFormatFromAttribute: () => getVertexFormatFromAttribute,
66
71
  getVertexFormatInfo: () => getVertexFormatInfo,
72
+ isExternalImage: () => isExternalImage,
67
73
  log: () => log,
68
74
  luma: () => luma,
69
75
  makeVertexFormat: () => makeVertexFormat,
70
76
  readPixel: () => readPixel,
77
+ setTextureImageData: () => setTextureImageData,
71
78
  textureFormatDecoder: () => textureFormatDecoder,
72
79
  writePixel: () => writePixel
73
80
  });
@@ -108,8 +115,11 @@ var Resource = class {
108
115
  }
109
116
  /** props.id, for debugging. */
110
117
  id;
118
+ /** The props that this resource was created with */
111
119
  props;
120
+ /** User data object, reserved for the application */
112
121
  userData = {};
122
+ /** The device that this resource is associated with - TODO can we remove this dup? */
113
123
  _device;
114
124
  /** Whether this resource has been destroyed */
115
125
  destroyed = false;
@@ -313,10 +323,11 @@ __publicField(Buffer2, "defaultProps", {
313
323
 
314
324
  // dist/shadertypes/data-types/decode-data-types.js
315
325
  function getDataTypeInfo(type) {
316
- const [signedType, primitiveType, byteLength] = NORMALIZED_TYPE_MAP[type];
317
326
  const normalized = type.includes("norm");
318
327
  const integer = !normalized && !type.startsWith("float");
319
328
  const signed = type.startsWith("s");
329
+ const typeInfo = NORMALIZED_TYPE_MAP[type];
330
+ const [signedType, primitiveType, byteLength] = typeInfo || ["uint8 ", "i32", 1];
320
331
  return {
321
332
  signedType,
322
333
  primitiveType,
@@ -632,6 +643,9 @@ var TEXTURE_FORMAT_TABLE = {
632
643
  };
633
644
 
634
645
  // dist/shadertypes/textures/texture-format-decoder.js
646
+ var RGB_FORMAT_REGEX = /^(r|rg|rgb|rgba|bgra)([0-9]*)([a-z]*)(-srgb)?(-webgl)?$/;
647
+ var COLOR_FORMAT_PREFIXES = ["rgb", "rgba", "bgra"];
648
+ var DEPTH_FORMAT_PREFIXES = ["depth", "stencil"];
635
649
  var COMPRESSED_TEXTURE_FORMAT_PREFIXES = [
636
650
  "bc1",
637
651
  "bc2",
@@ -647,49 +661,67 @@ var COMPRESSED_TEXTURE_FORMAT_PREFIXES = [
647
661
  "astc",
648
662
  "pvrtc"
649
663
  ];
650
- var RGB_FORMAT_REGEX = /^(r|rg|rgb|rgba|bgra)([0-9]*)([a-z]*)(-srgb)?(-webgl)?$/;
651
664
  var TextureFormatDecoder = class {
652
- /** Returns information about a texture format, e.g. attatchment type, components, byte length and flags (integer, signed, normalized) */
653
- getInfo(format) {
654
- return getTextureFormatInfo(format);
655
- }
656
665
  /** Checks if a texture format is color */
657
666
  isColor(format) {
658
- return format.startsWith("rgba") || format.startsWith("bgra") || format.startsWith("rgb");
667
+ return COLOR_FORMAT_PREFIXES.some((prefix) => format.startsWith(prefix));
659
668
  }
660
669
  /** Checks if a texture format is depth or stencil */
661
670
  isDepthStencil(format) {
662
- return format.startsWith("depth") || format.startsWith("stencil");
671
+ return DEPTH_FORMAT_PREFIXES.some((prefix) => format.startsWith(prefix));
663
672
  }
664
673
  /** Checks if a texture format is compressed */
665
674
  isCompressed(format) {
666
675
  return COMPRESSED_TEXTURE_FORMAT_PREFIXES.some((prefix) => format.startsWith(prefix));
667
676
  }
668
- /**
669
- * Returns the "static" capabilities of a texture format.
670
- * @note Needs to be checked against current device
671
- */
677
+ /** Returns information about a texture format, e.g. attachment type, components, byte length and flags (integer, signed, normalized) */
678
+ getInfo(format) {
679
+ return getTextureFormatInfo(format);
680
+ }
681
+ /** "static" capabilities of a texture format. @note Needs to be adjusted against current device */
672
682
  getCapabilities(format) {
673
- const info = getTextureFormatDefinition(format);
674
- const formatCapabilities = {
675
- format,
676
- create: info.f ?? true,
677
- render: info.render ?? true,
678
- filter: info.filter ?? true,
679
- blend: info.blend ?? true,
680
- store: info.store ?? true
681
- };
682
- const formatInfo = getTextureFormatInfo(format);
683
- const isDepthStencil = format.startsWith("depth") || format.startsWith("stencil");
684
- const isSigned = formatInfo == null ? void 0 : formatInfo.signed;
685
- const isInteger = formatInfo == null ? void 0 : formatInfo.integer;
686
- const isWebGLSpecific = formatInfo == null ? void 0 : formatInfo.webgl;
687
- formatCapabilities.render &&= !isSigned;
688
- formatCapabilities.filter &&= !isDepthStencil && !isSigned && !isInteger && !isWebGLSpecific;
689
- return formatCapabilities;
683
+ return getTextureFormatCapabilities(format);
684
+ }
685
+ /** Computes the memory layout for a texture, in particular including row byte alignment */
686
+ computeMemoryLayout(opts) {
687
+ return computeTextureMemoryLayout(opts);
690
688
  }
691
689
  };
692
690
  var textureFormatDecoder = new TextureFormatDecoder();
691
+ function computeTextureMemoryLayout({ format, width, height, depth, byteAlignment }) {
692
+ const { bytesPerPixel } = textureFormatDecoder.getInfo(format);
693
+ const unpaddedBytesPerRow = width * bytesPerPixel;
694
+ const bytesPerRow = Math.ceil(unpaddedBytesPerRow / byteAlignment) * byteAlignment;
695
+ const rowsPerImage = height;
696
+ const byteLength = bytesPerRow * rowsPerImage * depth;
697
+ return {
698
+ bytesPerPixel,
699
+ bytesPerRow,
700
+ rowsPerImage,
701
+ depthOrArrayLayers: depth,
702
+ bytesPerImage: bytesPerRow * rowsPerImage,
703
+ byteLength
704
+ };
705
+ }
706
+ function getTextureFormatCapabilities(format) {
707
+ const info = getTextureFormatDefinition(format);
708
+ const formatCapabilities = {
709
+ format,
710
+ create: info.f ?? true,
711
+ render: info.render ?? true,
712
+ filter: info.filter ?? true,
713
+ blend: info.blend ?? true,
714
+ store: info.store ?? true
715
+ };
716
+ const formatInfo = getTextureFormatInfo(format);
717
+ const isDepthStencil = format.startsWith("depth") || format.startsWith("stencil");
718
+ const isSigned = formatInfo == null ? void 0 : formatInfo.signed;
719
+ const isInteger = formatInfo == null ? void 0 : formatInfo.integer;
720
+ const isWebGLSpecific = formatInfo == null ? void 0 : formatInfo.webgl;
721
+ formatCapabilities.render &&= !isSigned;
722
+ formatCapabilities.filter &&= !isDepthStencil && !isSigned && !isInteger && !isWebGLSpecific;
723
+ return formatCapabilities;
724
+ }
693
725
  function getTextureFormatInfo(format) {
694
726
  let formatInfo = getTextureFormatInfoUsingTable(format);
695
727
  if (textureFormatDecoder.isCompressed(format)) {
@@ -710,7 +742,7 @@ function getTextureFormatInfo(format) {
710
742
  const dataType = `${type}${length}`;
711
743
  const decodedType = getDataTypeInfo(dataType);
712
744
  const bits = decodedType.byteLength * 8;
713
- const components = channels.length;
745
+ const components = (channels == null ? void 0 : channels.length) ?? 1;
714
746
  const bitsPerChannel = [
715
747
  bits,
716
748
  components >= 2 ? bits : 0,
@@ -727,7 +759,7 @@ function getTextureFormatInfo(format) {
727
759
  signed: decodedType.signed,
728
760
  normalized: decodedType.normalized,
729
761
  bitsPerChannel,
730
- bytesPerPixel: decodedType.byteLength * channels.length,
762
+ bytesPerPixel: decodedType.byteLength * components,
731
763
  packed: formatInfo.packed,
732
764
  srgb: formatInfo.srgb
733
765
  };
@@ -845,7 +877,7 @@ var _Device = class {
845
877
  /** True if this device has been reused during device creation (app has multiple references) */
846
878
  _reused = false;
847
879
  /** Used by other luma.gl modules to store data on the device */
848
- _lumaData = {};
880
+ _moduleData = {};
849
881
  _textureCaps = {};
850
882
  constructor(props) {
851
883
  this.props = { ..._Device.defaultProps, ...props };
@@ -944,7 +976,7 @@ var _Device = class {
944
976
  reportError(error, context, ...args) {
945
977
  const isHandled = this.props.onError(error, context);
946
978
  if (!isHandled) {
947
- return log.error(error.message, context, ...args);
979
+ return log.error(this.type === "webgl" ? "%cWebGL" : "%cWebGPU", "color: white; background: red; padding: 2px 6px; border-radius: 3px;", error.message, context, ...args);
948
980
  }
949
981
  return () => {
950
982
  };
@@ -966,6 +998,10 @@ or create a device with the 'debug: true' prop.`;
966
998
  }
967
999
  return this.canvasContext;
968
1000
  }
1001
+ /** Create a fence sync object */
1002
+ createFence() {
1003
+ throw new Error("createFence() not implemented");
1004
+ }
969
1005
  /** Create a RenderPass using the default CommandEncoder */
970
1006
  beginRenderPass(props) {
971
1007
  return this.commandEncoder.beginRenderPass(props);
@@ -1009,6 +1045,12 @@ or create a device with the 'debug: true' prop.`;
1009
1045
  resetWebGL() {
1010
1046
  throw new Error("not implemented");
1011
1047
  }
1048
+ // INTERNAL LUMA.GL METHODS
1049
+ getModuleData(moduleName) {
1050
+ this._moduleData[moduleName] ||= {};
1051
+ return this._moduleData[moduleName];
1052
+ }
1053
+ // INTERNAL HELPERS
1012
1054
  // IMPLEMENTATION
1013
1055
  /** Helper to get the canvas context props */
1014
1056
  static _getCanvasContextProps(props) {
@@ -1116,7 +1158,7 @@ var _Luma = class {
1116
1158
  VERSION = (
1117
1159
  // Version detection using build plugin
1118
1160
  // @ts-expect-error no-undef
1119
- true ? "9.2.6" : "running from source"
1161
+ true ? "9.3.0-alpha.4" : "running from source"
1120
1162
  );
1121
1163
  spector;
1122
1164
  preregisteredAdapters = /* @__PURE__ */ new Map();
@@ -1295,6 +1337,20 @@ function withResolvers() {
1295
1337
  return { promise, resolve, reject };
1296
1338
  }
1297
1339
 
1340
+ // dist/utils/assert.js
1341
+ function assert(condition, message) {
1342
+ var _a;
1343
+ if (!condition) {
1344
+ const error = new Error(message ?? "luma.gl assertion failed.");
1345
+ (_a = Error.captureStackTrace) == null ? void 0 : _a.call(Error, error, assert);
1346
+ throw error;
1347
+ }
1348
+ }
1349
+ function assertDefined(value, message) {
1350
+ assert(value, message);
1351
+ return value;
1352
+ }
1353
+
1298
1354
  // dist/adapter/canvas-context.js
1299
1355
  var _CanvasContext = class {
1300
1356
  static isHTMLCanvas(canvas) {
@@ -1330,11 +1386,19 @@ var _CanvasContext = class {
1330
1386
  drawingBufferWidth;
1331
1387
  /** Height of drawing buffer: automatically tracks this.pixelHeight if props.autoResize is true */
1332
1388
  drawingBufferHeight;
1389
+ /** Resolves when the canvas is initialized, i.e. when the ResizeObserver has updated the pixel size */
1333
1390
  _initializedResolvers = withResolvers();
1391
+ /** ResizeObserver to track canvas size changes */
1334
1392
  _resizeObserver;
1393
+ /** IntersectionObserver to track canvas visibility changes */
1335
1394
  _intersectionObserver;
1336
- _position;
1395
+ _observeDevicePixelRatioTimeout = null;
1396
+ /** Position of the canvas in the document, updated by a timer */
1397
+ _position = [0, 0];
1398
+ /** Whether this canvas context has been destroyed */
1337
1399
  destroyed = false;
1400
+ /** Whether the drawing buffer size needs to be resized (deferred resizing to avoid flicker) */
1401
+ _needsDrawingBufferResize = true;
1338
1402
  toString() {
1339
1403
  return `${this[Symbol.toStringTag]}(${this.id})`;
1340
1404
  }
@@ -1381,14 +1445,24 @@ var _CanvasContext = class {
1381
1445
  } catch {
1382
1446
  this._resizeObserver.observe(this.canvas, { box: "content-box" });
1383
1447
  }
1384
- setTimeout(() => this._observeDevicePixelRatio(), 0);
1448
+ this._observeDevicePixelRatioTimeout = setTimeout(() => this._observeDevicePixelRatio(), 0);
1385
1449
  if (this.props.trackPosition) {
1386
1450
  this._trackPosition();
1387
1451
  }
1388
1452
  }
1389
1453
  }
1390
1454
  destroy() {
1391
- this.destroyed = true;
1455
+ var _a, _b;
1456
+ if (!this.destroyed) {
1457
+ this.destroyed = true;
1458
+ if (this._observeDevicePixelRatioTimeout) {
1459
+ clearTimeout(this._observeDevicePixelRatioTimeout);
1460
+ this._observeDevicePixelRatioTimeout = null;
1461
+ }
1462
+ this.device = null;
1463
+ (_a = this._resizeObserver) == null ? void 0 : _a.disconnect();
1464
+ (_b = this._intersectionObserver) == null ? void 0 : _b.disconnect();
1465
+ }
1392
1466
  }
1393
1467
  setProps(props) {
1394
1468
  if ("useDevicePixels" in props) {
@@ -1397,6 +1471,11 @@ var _CanvasContext = class {
1397
1471
  }
1398
1472
  return this;
1399
1473
  }
1474
+ /** Returns a framebuffer with properly resized current 'swap chain' textures */
1475
+ getCurrentFramebuffer(options) {
1476
+ this._resizeDrawingBufferIfNeeded();
1477
+ return this._getCurrentFramebuffer(options);
1478
+ }
1400
1479
  // SIZE METHODS
1401
1480
  /**
1402
1481
  * Returns the size covered by the canvas in CSS pixels
@@ -1426,12 +1505,21 @@ var _CanvasContext = class {
1426
1505
  const maxTextureDimension = this.device.limits.maxTextureDimension2D;
1427
1506
  return [maxTextureDimension, maxTextureDimension];
1428
1507
  }
1429
- /** Update the canvas drawing buffer size. Called automatically if props.autoResize is true. */
1508
+ /**
1509
+ * Update the canvas drawing buffer size.
1510
+ * @note - Called automatically if props.autoResize is true.
1511
+ * @note - Defers update of drawing buffer size until framebuffer is requested to avoid flicker
1512
+ * (resizing clears the drawing buffer)!
1513
+ */
1430
1514
  setDrawingBufferSize(width, height) {
1431
- this.canvas.width = width;
1432
- this.canvas.height = height;
1515
+ width = Math.floor(width);
1516
+ height = Math.floor(height);
1517
+ if (this.drawingBufferWidth === width && this.drawingBufferHeight === height) {
1518
+ return;
1519
+ }
1433
1520
  this.drawingBufferWidth = width;
1434
1521
  this.drawingBufferHeight = height;
1522
+ this._needsDrawingBufferResize = true;
1435
1523
  }
1436
1524
  /**
1437
1525
  * Returns the current DPR (number of physical pixels per CSS pixel), if props.useDevicePixels is true
@@ -1487,6 +1575,9 @@ var _CanvasContext = class {
1487
1575
  }
1488
1576
  /** reacts to an observed intersection */
1489
1577
  _handleIntersection(entries) {
1578
+ if (this.destroyed) {
1579
+ return;
1580
+ }
1490
1581
  const entry = entries.find((entry_) => entry_.target === this.canvas);
1491
1582
  if (!entry) {
1492
1583
  return;
@@ -1503,22 +1594,27 @@ var _CanvasContext = class {
1503
1594
  * @see https://webgpufundamentals.org/webgpu/lessons/webgpu-resizing-the-canvas.html
1504
1595
  */
1505
1596
  _handleResize(entries) {
1506
- var _a, _b;
1597
+ var _a, _b, _c, _d, _e;
1598
+ if (this.destroyed) {
1599
+ return;
1600
+ }
1507
1601
  const entry = entries.find((entry_) => entry_.target === this.canvas);
1508
1602
  if (!entry) {
1509
1603
  return;
1510
1604
  }
1511
- this.cssWidth = entry.contentBoxSize[0].inlineSize;
1512
- this.cssHeight = entry.contentBoxSize[0].blockSize;
1605
+ const contentBoxSize = assertDefined((_a = entry.contentBoxSize) == null ? void 0 : _a[0]);
1606
+ this.cssWidth = contentBoxSize.inlineSize;
1607
+ this.cssHeight = contentBoxSize.blockSize;
1513
1608
  const oldPixelSize = this.getDevicePixelSize();
1514
- const devicePixelWidth = ((_a = entry.devicePixelContentBoxSize) == null ? void 0 : _a[0].inlineSize) || entry.contentBoxSize[0].inlineSize * devicePixelRatio;
1515
- const devicePixelHeight = ((_b = entry.devicePixelContentBoxSize) == null ? void 0 : _b[0].blockSize) || entry.contentBoxSize[0].blockSize * devicePixelRatio;
1609
+ const devicePixelWidth = ((_c = (_b = entry.devicePixelContentBoxSize) == null ? void 0 : _b[0]) == null ? void 0 : _c.inlineSize) || contentBoxSize.inlineSize * devicePixelRatio;
1610
+ const devicePixelHeight = ((_e = (_d = entry.devicePixelContentBoxSize) == null ? void 0 : _d[0]) == null ? void 0 : _e.blockSize) || contentBoxSize.blockSize * devicePixelRatio;
1516
1611
  const [maxDevicePixelWidth, maxDevicePixelHeight] = this.getMaxDrawingBufferSize();
1517
1612
  this.devicePixelWidth = Math.max(1, Math.min(devicePixelWidth, maxDevicePixelWidth));
1518
1613
  this.devicePixelHeight = Math.max(1, Math.min(devicePixelHeight, maxDevicePixelHeight));
1519
1614
  this._updateDrawingBufferSize();
1520
1615
  this.device.props.onResize(this, { oldPixelSize });
1521
1616
  }
1617
+ /** Initiate a deferred update for the canvas drawing buffer size */
1522
1618
  _updateDrawingBufferSize() {
1523
1619
  if (this.props.autoResize) {
1524
1620
  if (typeof this.props.useDevicePixels === "number") {
@@ -1529,18 +1625,33 @@ var _CanvasContext = class {
1529
1625
  } else {
1530
1626
  this.setDrawingBufferSize(this.cssWidth, this.cssHeight);
1531
1627
  }
1532
- this._updateDevice();
1533
1628
  }
1534
1629
  this._initializedResolvers.resolve();
1535
1630
  this.isInitialized = true;
1536
1631
  this.updatePosition();
1537
1632
  }
1633
+ /** Perform a deferred resize of the drawing buffer if needed */
1634
+ _resizeDrawingBufferIfNeeded() {
1635
+ if (this._needsDrawingBufferResize) {
1636
+ this._needsDrawingBufferResize = false;
1637
+ const sizeChanged = this.drawingBufferWidth !== this.canvas.width || this.drawingBufferHeight !== this.canvas.height;
1638
+ if (sizeChanged) {
1639
+ this.canvas.width = this.drawingBufferWidth;
1640
+ this.canvas.height = this.drawingBufferHeight;
1641
+ this._configureDevice();
1642
+ }
1643
+ }
1644
+ }
1538
1645
  /** Monitor DPR changes */
1539
1646
  _observeDevicePixelRatio() {
1647
+ var _a, _b;
1648
+ if (this.destroyed) {
1649
+ return;
1650
+ }
1540
1651
  const oldRatio = this.devicePixelRatio;
1541
1652
  this.devicePixelRatio = window.devicePixelRatio;
1542
1653
  this.updatePosition();
1543
- this.device.props.onDevicePixelRatioChange(this, { oldRatio });
1654
+ (_b = (_a = this.device.props).onDevicePixelRatioChange) == null ? void 0 : _b.call(_a, this, { oldRatio });
1544
1655
  matchMedia(`(resolution: ${this.devicePixelRatio}dppx)`).addEventListener("change", () => this._observeDevicePixelRatio(), { once: true });
1545
1656
  }
1546
1657
  /** Start tracking positions with a timer */
@@ -1560,6 +1671,9 @@ var _CanvasContext = class {
1560
1671
  */
1561
1672
  updatePosition() {
1562
1673
  var _a, _b, _c;
1674
+ if (this.destroyed) {
1675
+ return;
1676
+ }
1563
1677
  const newRect = (_a = this.htmlCanvas) == null ? void 0 : _a.getBoundingClientRect();
1564
1678
  if (newRect) {
1565
1679
  const position = [newRect.left, newRect.top];
@@ -1707,7 +1821,13 @@ var _Texture = class extends Resource {
1707
1821
  depth;
1708
1822
  /** mip levels in this texture */
1709
1823
  mipLevels;
1710
- /** "Time" of last update. Monotonically increasing timestamp. TODO move to AsyncTexture? */
1824
+ /** Rows are multiples of this length, padded with extra bytes if needed */
1825
+ byteAlignment;
1826
+ /** The ready promise is always resolved. It is provided for type compatibility with DynamicTexture. */
1827
+ ready = Promise.resolve(this);
1828
+ /** isReady is always true. It is provided for type compatibility with DynamicTexture. */
1829
+ isReady = true;
1830
+ /** "Time" of last update. Monotonically increasing timestamp. TODO move to DynamicTexture? */
1711
1831
  updateTimestamp;
1712
1832
  get [Symbol.toStringTag]() {
1713
1833
  return "Texture";
@@ -1716,7 +1836,7 @@ var _Texture = class extends Resource {
1716
1836
  return `Texture(${this.id},${this.format},${this.width}x${this.height})`;
1717
1837
  }
1718
1838
  /** Do not use directly. Create with device.createTexture() */
1719
- constructor(device, props) {
1839
+ constructor(device, props, backendProps) {
1720
1840
  props = _Texture.normalizeProps(device, props);
1721
1841
  super(device, props, _Texture.defaultProps);
1722
1842
  this.dimension = this.props.dimension;
@@ -1726,6 +1846,9 @@ var _Texture = class extends Resource {
1726
1846
  this.height = this.props.height;
1727
1847
  this.depth = this.props.depth;
1728
1848
  this.mipLevels = this.props.mipLevels;
1849
+ if (this.dimension === "cube") {
1850
+ this.depth = 6;
1851
+ }
1729
1852
  if (this.props.width === void 0 || this.props.height === void 0) {
1730
1853
  if (device.isExternalImage(props.data)) {
1731
1854
  const size = device.getExternalImageSize(props.data);
@@ -1735,16 +1858,13 @@ var _Texture = class extends Resource {
1735
1858
  this.width = 1;
1736
1859
  this.height = 1;
1737
1860
  if (this.props.width === void 0 || this.props.height === void 0) {
1738
- log.warn(`${this} created with undefined width or height. This is deprecated. Use AsyncTexture instead.`)();
1861
+ log.warn(`${this} created with undefined width or height. This is deprecated. Use DynamicTexture instead.`)();
1739
1862
  }
1740
1863
  }
1741
1864
  }
1865
+ this.byteAlignment = (backendProps == null ? void 0 : backendProps.byteAlignment) || 1;
1742
1866
  this.updateTimestamp = device.incrementTimestamp();
1743
1867
  }
1744
- /** Set sampler props associated with this texture */
1745
- setSampler(sampler) {
1746
- this.sampler = sampler instanceof Sampler ? sampler : this.device.createSampler(sampler);
1747
- }
1748
1868
  /**
1749
1869
  * Create a new texture with the same parameters and optionally a different size
1750
1870
  * @note Textures are immutable and cannot be resized after creation, but we can create a similar texture with the same parameters but a new size.
@@ -1753,6 +1873,79 @@ var _Texture = class extends Resource {
1753
1873
  clone(size) {
1754
1874
  return this.device.createTexture({ ...this.props, ...size });
1755
1875
  }
1876
+ /** Set sampler props associated with this texture */
1877
+ setSampler(sampler) {
1878
+ this.sampler = sampler instanceof Sampler ? sampler : this.device.createSampler(sampler);
1879
+ }
1880
+ /**
1881
+ * Calculates the memory layout of the texture, required when reading and writing data.
1882
+ * @return the memory layout of the texture, in particular bytesPerRow which includes required padding
1883
+ */
1884
+ computeMemoryLayout(options_ = {}) {
1885
+ const options = this._normalizeTextureReadOptions(options_);
1886
+ const { width = this.width, height = this.height, depthOrArrayLayers = this.depth } = options;
1887
+ const { format, byteAlignment } = this;
1888
+ return textureFormatDecoder.computeMemoryLayout({
1889
+ format,
1890
+ width,
1891
+ height,
1892
+ depth: depthOrArrayLayers,
1893
+ byteAlignment
1894
+ });
1895
+ }
1896
+ /**
1897
+ * Read the contents of a texture into a GPU Buffer.
1898
+ * @returns A Buffer containing the texture data.
1899
+ *
1900
+ * @note The memory layout of the texture data is determined by the texture format and dimensions.
1901
+ * @note The application can call Texture.computeMemoryLayout() to compute the layout.
1902
+ * @note The application can call Buffer.readAsync()
1903
+ * @note If not supplied a buffer will be created and the application needs to call Buffer.destroy
1904
+ */
1905
+ readBuffer(options, buffer) {
1906
+ throw new Error("readBuffer not implemented");
1907
+ }
1908
+ /**
1909
+ * Reads data from a texture into an ArrayBuffer.
1910
+ * @returns An ArrayBuffer containing the texture data.
1911
+ *
1912
+ * @note The memory layout of the texture data is determined by the texture format and dimensions.
1913
+ * @note The application can call Texture.computeMemoryLayout() to compute the layout.
1914
+ */
1915
+ readDataAsync(options) {
1916
+ throw new Error("readBuffer not implemented");
1917
+ }
1918
+ /**
1919
+ * Writes an GPU Buffer into a texture.
1920
+ *
1921
+ * @note The memory layout of the texture data is determined by the texture format and dimensions.
1922
+ * @note The application can call Texture.computeMemoryLayout() to compute the layout.
1923
+ */
1924
+ writeBuffer(buffer, options) {
1925
+ throw new Error("readBuffer not implemented");
1926
+ }
1927
+ /**
1928
+ * Writes an array buffer into a texture.
1929
+ *
1930
+ * @note The memory layout of the texture data is determined by the texture format and dimensions.
1931
+ * @note The application can call Texture.computeMemoryLayout() to compute the layout.
1932
+ */
1933
+ writeData(data, options) {
1934
+ throw new Error("readBuffer not implemented");
1935
+ }
1936
+ // IMPLEMENTATION SPECIFIC
1937
+ /**
1938
+ * WebGL can read data synchronously.
1939
+ * @note While it is convenient, the performance penalty is very significant
1940
+ */
1941
+ readDataSyncWebGL(options) {
1942
+ throw new Error("readDataSyncWebGL not available");
1943
+ }
1944
+ /** Generate mipmaps (WebGL only) */
1945
+ generateMipmapsWebGL() {
1946
+ throw new Error("generateMipmapsWebGL not available");
1947
+ }
1948
+ // HELPERS
1756
1949
  /** Ensure we have integer coordinates */
1757
1950
  static normalizeProps(device, props) {
1758
1951
  const newProps = { ...props };
@@ -1765,7 +1958,6 @@ var _Texture = class extends Resource {
1765
1958
  }
1766
1959
  return newProps;
1767
1960
  }
1768
- // HELPERS
1769
1961
  /** Initialize texture with supplied props */
1770
1962
  // eslint-disable-next-line max-statements
1771
1963
  _initializeData(data) {
@@ -1816,6 +2008,20 @@ var _Texture = class extends Resource {
1816
2008
  options.height = Math.min(options.height, this.height - options.y);
1817
2009
  return options;
1818
2010
  }
2011
+ _normalizeTextureReadOptions(options_) {
2012
+ const { width, height } = this;
2013
+ const options = { ..._Texture.defaultTextureReadOptions, width, height, ...options_ };
2014
+ options.width = Math.min(options.width, this.width - options.x);
2015
+ options.height = Math.min(options.height, this.height - options.y);
2016
+ return options;
2017
+ }
2018
+ _normalizeTextureWriteOptions(options_) {
2019
+ const { width, height } = this;
2020
+ const options = { ..._Texture.defaultTextureReadOptions, width, height, ...options_ };
2021
+ options.width = Math.min(options.width, this.width - options.x);
2022
+ options.height = Math.min(options.height, this.height - options.y);
2023
+ return options;
2024
+ }
1819
2025
  };
1820
2026
  var Texture = _Texture;
1821
2027
  /** The texture can be bound for use as a sampled texture in a shader */
@@ -1832,13 +2038,12 @@ __publicField(Texture, "COPY_DST", 2);
1832
2038
  __publicField(Texture, "TEXTURE", 4);
1833
2039
  /** @deprecated Use Texture.RENDER */
1834
2040
  __publicField(Texture, "RENDER_ATTACHMENT", 16);
1835
- /** Default options */
1836
2041
  __publicField(Texture, "defaultProps", {
1837
2042
  ...Resource.defaultProps,
1838
2043
  data: null,
1839
2044
  dimension: "2d",
1840
2045
  format: "rgba8unorm",
1841
- usage: _Texture.TEXTURE | _Texture.RENDER_ATTACHMENT | _Texture.COPY_DST,
2046
+ usage: _Texture.SAMPLE | _Texture.RENDER | _Texture.COPY_DST,
1842
2047
  width: void 0,
1843
2048
  height: void 0,
1844
2049
  depth: 1,
@@ -1875,6 +2080,16 @@ __publicField(Texture, "defaultCopyExternalImageOptions", {
1875
2080
  premultipliedAlpha: false,
1876
2081
  flipY: false
1877
2082
  });
2083
+ __publicField(Texture, "defaultTextureReadOptions", {
2084
+ x: 0,
2085
+ y: 0,
2086
+ z: 0,
2087
+ width: void 0,
2088
+ height: void 0,
2089
+ depthOrArrayLayers: 1,
2090
+ mipLevel: 0,
2091
+ aspect: "all"
2092
+ });
1878
2093
 
1879
2094
  // dist/adapter/resources/texture-view.js
1880
2095
  var _TextureView = class extends Resource {
@@ -1921,24 +2136,32 @@ function formatCompilerLog(shaderLog, source, options) {
1921
2136
  const log2 = shaderLog.slice().sort((a, b) => a.lineNum - b.lineNum);
1922
2137
  switch ((options == null ? void 0 : options.showSourceCode) || "no") {
1923
2138
  case "all":
1924
- let currentMessage = 0;
2139
+ let currentMessageIndex = 0;
1925
2140
  for (let lineNum = 1; lineNum <= lines.length; lineNum++) {
1926
- formattedLog += getNumberedLine(lines[lineNum - 1], lineNum, options);
1927
- while (log2.length > currentMessage && log2[currentMessage].lineNum === lineNum) {
1928
- const message = log2[currentMessage++];
1929
- formattedLog += formatCompilerMessage(message, lines, message.lineNum, {
2141
+ const line = lines[lineNum - 1];
2142
+ const currentMessage = log2[currentMessageIndex];
2143
+ if (line && currentMessage) {
2144
+ formattedLog += getNumberedLine(line, lineNum, options);
2145
+ }
2146
+ while (log2.length > currentMessageIndex && currentMessage.lineNum === lineNum) {
2147
+ const message = log2[currentMessageIndex++];
2148
+ if (message) {
2149
+ formattedLog += formatCompilerMessage(message, lines, message.lineNum, {
2150
+ ...options,
2151
+ inlineSource: false
2152
+ });
2153
+ }
2154
+ }
2155
+ }
2156
+ while (log2.length > currentMessageIndex) {
2157
+ const message = log2[currentMessageIndex++];
2158
+ if (message) {
2159
+ formattedLog += formatCompilerMessage(message, [], 0, {
1930
2160
  ...options,
1931
2161
  inlineSource: false
1932
2162
  });
1933
2163
  }
1934
2164
  }
1935
- while (log2.length > currentMessage) {
1936
- const message = log2[currentMessage++];
1937
- formattedLog += formatCompilerMessage(message, [], 0, {
1938
- ...options,
1939
- inlineSource: false
1940
- });
1941
- }
1942
2165
  return formattedLog;
1943
2166
  case "issues":
1944
2167
  case "no":
@@ -1960,8 +2183,8 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
1960
2183
 
1961
2184
  `;
1962
2185
  }
1963
- const color = message.type === "error" ? "red" : "#8B4000";
1964
- 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}`;
2186
+ const color = message.type === "error" ? "red" : "orange";
2187
+ 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}`;
1965
2188
  }
1966
2189
  function getNumberedLines(lines, lineNum, options) {
1967
2190
  let numberedLines = "";
@@ -2042,35 +2265,39 @@ var _Shader = class extends Resource {
2042
2265
  * TODO - this HTML formatting code should not be in Device, should be pluggable
2043
2266
  */
2044
2267
  _displayShaderLog(messages, shaderId) {
2045
- var _a;
2046
2268
  if (typeof document === "undefined" || !(document == null ? void 0 : document.createElement)) {
2047
2269
  return;
2048
2270
  }
2049
2271
  const shaderName = shaderId;
2050
2272
  const shaderTitle = `${this.stage} shader "${shaderName}"`;
2051
- let htmlLog = formatCompilerLog(messages, this.source, { showSourceCode: "all", html: true });
2273
+ const htmlLog = formatCompilerLog(messages, this.source, { showSourceCode: "all", html: true });
2052
2274
  const translatedSource = this.getTranslatedSource();
2275
+ const container = document.createElement("div");
2276
+ container.innerHTML = `<h1>Compilation error in ${shaderTitle}</h1>
2277
+ <div style="display:flex;position:fixed;top:10px;right:20px;gap:2px;">
2278
+ <button id="copy">Copy source</button><br/>
2279
+ <button id="close">Close</button>
2280
+ </div>
2281
+ <code><pre>${htmlLog}</pre></code>`;
2053
2282
  if (translatedSource) {
2054
- htmlLog += `<br /><br /><h1>Translated Source</h1><br /><br /><code style="user-select:text;"><pre>${translatedSource}</pre></code>`;
2055
- }
2056
- const button = document.createElement("Button");
2057
- button.innerHTML = `
2058
- <h1>Compilation error in ${shaderTitle}</h1><br /><br />
2059
- <code style="user-select:text;"><pre>
2060
- ${htmlLog}
2061
- </pre></code>`;
2062
- button.style.top = "10px";
2063
- button.style.left = "10px";
2064
- button.style.position = "absolute";
2065
- button.style.zIndex = "9999";
2066
- button.style.width = "100%";
2067
- button.style.textAlign = "left";
2068
- document.body.appendChild(button);
2069
- const errors = document.getElementsByClassName("luma-compiler-log-error");
2070
- (_a = errors[0]) == null ? void 0 : _a.scrollIntoView();
2071
- button.onclick = () => {
2072
- const dataURI = `data:text/plain,${encodeURIComponent(this.source)}`;
2073
- navigator.clipboard.writeText(dataURI);
2283
+ container.innerHTML += `<br /><h1>Translated Source</h1><br /><br /><code><pre>${translatedSource}</pre></code>`;
2284
+ }
2285
+ container.style.top = "0";
2286
+ container.style.left = "0";
2287
+ container.style.background = "white";
2288
+ container.style.position = "fixed";
2289
+ container.style.zIndex = "9999";
2290
+ container.style.maxWidth = "100vw";
2291
+ container.style.maxHeight = "100vh";
2292
+ container.style.overflowY = "auto";
2293
+ document.body.appendChild(container);
2294
+ const error = container.querySelector(".luma-compiler-log-error");
2295
+ error == null ? void 0 : error.scrollIntoView();
2296
+ container.querySelector("button#close").onclick = () => {
2297
+ container.remove();
2298
+ };
2299
+ container.querySelector("button#copy").onclick = () => {
2300
+ navigator.clipboard.writeText(this.source);
2074
2301
  };
2075
2302
  }
2076
2303
  };
@@ -2090,7 +2317,7 @@ function getShaderIdFromProps(props) {
2090
2317
  function getShaderName(shader, defaultName = "unnamed") {
2091
2318
  const SHADER_NAME_REGEXP = /#define[\s*]SHADER_NAME[\s*]([A-Za-z0-9_-]+)[\s*]/;
2092
2319
  const match = SHADER_NAME_REGEXP.exec(shader);
2093
- return match ? match[1] : defaultName;
2320
+ return (match == null ? void 0 : match[1]) ?? defaultName;
2094
2321
  }
2095
2322
 
2096
2323
  // dist/adapter/resources/framebuffer.js
@@ -2114,7 +2341,12 @@ var _Framebuffer = class extends Resource {
2114
2341
  clone(size) {
2115
2342
  const colorAttachments = this.colorAttachments.map((colorAttachment) => colorAttachment.texture.clone(size));
2116
2343
  const depthStencilAttachment = this.depthStencilAttachment && this.depthStencilAttachment.texture.clone(size);
2117
- return this.device.createFramebuffer({ ...this.props, colorAttachments, depthStencilAttachment });
2344
+ return this.device.createFramebuffer({
2345
+ ...this.props,
2346
+ ...size,
2347
+ colorAttachments,
2348
+ depthStencilAttachment
2349
+ });
2118
2350
  }
2119
2351
  resize(size) {
2120
2352
  let updateSize = !size;
@@ -2189,17 +2421,15 @@ var _Framebuffer = class extends Resource {
2189
2421
  * and destroys existing textures if owned
2190
2422
  */
2191
2423
  resizeAttachments(width, height) {
2192
- for (let i = 0; i < this.colorAttachments.length; ++i) {
2193
- if (this.colorAttachments[i]) {
2194
- const resizedTexture = this.colorAttachments[i].texture.clone({
2195
- width,
2196
- height
2197
- });
2198
- this.destroyAttachedResource(this.colorAttachments[i]);
2199
- this.colorAttachments[i] = resizedTexture.view;
2200
- this.attachResource(resizedTexture.view);
2201
- }
2202
- }
2424
+ this.colorAttachments.forEach((colorAttachment, i) => {
2425
+ const resizedTexture = colorAttachment.texture.clone({
2426
+ width,
2427
+ height
2428
+ });
2429
+ this.destroyAttachedResource(colorAttachment);
2430
+ this.colorAttachments[i] = resizedTexture.view;
2431
+ this.attachResource(resizedTexture.view);
2432
+ });
2203
2433
  if (this.depthStencilAttachment) {
2204
2434
  const resizedTexture = this.depthStencilAttachment.texture.clone({
2205
2435
  width,
@@ -2704,6 +2934,18 @@ __publicField(QuerySet, "defaultProps", {
2704
2934
  count: void 0
2705
2935
  });
2706
2936
 
2937
+ // dist/adapter/resources/fence.js
2938
+ var _Fence = class extends Resource {
2939
+ [Symbol.toStringTag] = "WEBGLFence";
2940
+ constructor(device, props = {}) {
2941
+ super(device, props, _Fence.defaultProps);
2942
+ }
2943
+ };
2944
+ var Fence = _Fence;
2945
+ __publicField(Fence, "defaultProps", {
2946
+ ...Resource.defaultProps
2947
+ });
2948
+
2707
2949
  // dist/adapter/resources/pipeline-layout.js
2708
2950
  var _PipelineLayout = class extends Resource {
2709
2951
  get [Symbol.toStringTag]() {
@@ -2735,17 +2977,6 @@ function getScratchArray(Type, length) {
2735
2977
  return new Type(scratchArrayBuffer, 0, length);
2736
2978
  }
2737
2979
 
2738
- // dist/utils/is-array.js
2739
- function isTypedArray(value) {
2740
- return ArrayBuffer.isView(value) && !(value instanceof DataView);
2741
- }
2742
- function isNumberArray(value) {
2743
- if (Array.isArray(value)) {
2744
- return value.length === 0 || typeof value[0] === "number";
2745
- }
2746
- return isTypedArray(value);
2747
- }
2748
-
2749
2980
  // dist/portable/uniform-buffer-layout.js
2750
2981
  var minBufferSize = 1024;
2751
2982
  var UniformBufferLayout = class {
@@ -2756,63 +2987,114 @@ var UniformBufferLayout = class {
2756
2987
  constructor(uniformTypes, uniformSizes = {}) {
2757
2988
  let size = 0;
2758
2989
  for (const [key, uniformType] of Object.entries(uniformTypes)) {
2759
- const typeAndComponents = getVariableShaderTypeInfo(uniformType);
2760
- const { type, components } = typeAndComponents;
2761
- const count = components * ((uniformSizes == null ? void 0 : uniformSizes[key]) ?? 1);
2762
- size = alignTo(size, count);
2763
- const offset = size;
2764
- size += count;
2765
- this.layout[key] = { type, size: count, offset };
2990
+ size = this._addToLayout(key, uniformType, size, uniformSizes == null ? void 0 : uniformSizes[key]);
2766
2991
  }
2767
2992
  size += (4 - size % 4) % 4;
2768
- const actualByteLength = size * 4;
2769
- this.byteLength = Math.max(actualByteLength, minBufferSize);
2993
+ this.byteLength = Math.max(size * 4, minBufferSize);
2994
+ }
2995
+ /** Does this layout have a field with specified name */
2996
+ has(name2) {
2997
+ return Boolean(this.layout[name2]);
2998
+ }
2999
+ /** Get offset and size for a field with specified name */
3000
+ get(name2) {
3001
+ const layout = this.layout[name2];
3002
+ return layout;
2770
3003
  }
2771
3004
  /** Get the data for the complete buffer */
2772
3005
  getData(uniformValues) {
2773
- const arrayBuffer2 = getScratchArrayBuffer(this.byteLength);
3006
+ const buffer = getScratchArrayBuffer(this.byteLength);
2774
3007
  const typedArrays = {
2775
- i32: new Int32Array(arrayBuffer2),
2776
- u32: new Uint32Array(arrayBuffer2),
2777
- f32: new Float32Array(arrayBuffer2),
2778
- // TODO not implemented
2779
- f16: new Uint16Array(arrayBuffer2)
3008
+ i32: new Int32Array(buffer),
3009
+ u32: new Uint32Array(buffer),
3010
+ f32: new Float32Array(buffer),
3011
+ f16: new Uint16Array(buffer)
2780
3012
  };
2781
3013
  for (const [name2, value] of Object.entries(uniformValues)) {
2782
- const uniformLayout = this.layout[name2];
2783
- if (!uniformLayout) {
2784
- log.warn(`Supplied uniform value ${name2} not present in uniform block layout`)();
2785
- continue;
3014
+ this._writeCompositeValue(typedArrays, name2, value);
3015
+ }
3016
+ return new Uint8Array(buffer, 0, this.byteLength);
3017
+ }
3018
+ // Recursively add a uniform to the layout
3019
+ _addToLayout(name2, type, offset, count = 1) {
3020
+ if (typeof type === "string") {
3021
+ const info = getVariableShaderTypeInfo(type);
3022
+ const sizeInSlots = info.components * count;
3023
+ const alignedOffset = alignTo(offset, info.components);
3024
+ this.layout[name2] = {
3025
+ offset: alignedOffset,
3026
+ size: sizeInSlots,
3027
+ type: info.type
3028
+ };
3029
+ return alignedOffset + sizeInSlots;
3030
+ }
3031
+ if (Array.isArray(type)) {
3032
+ const elementType = type[0];
3033
+ const length = count > 1 ? count : type.length > 1 ? type[1] : 1;
3034
+ let arrayOffset = alignTo(offset, 4);
3035
+ for (let i = 0; i < length; i++) {
3036
+ arrayOffset = this._addToLayout(`${name2}[${i}]`, elementType, arrayOffset);
2786
3037
  }
2787
- const { type, size, offset } = uniformLayout;
2788
- const typedArray = typedArrays[type];
2789
- if (size === 1) {
2790
- if (typeof value !== "number" && typeof value !== "boolean") {
2791
- log.warn(`Supplied value for single component uniform ${name2} is not a number: ${value}`)();
2792
- continue;
2793
- }
2794
- typedArray[offset] = Number(value);
2795
- } else {
2796
- if (!isNumberArray(value)) {
2797
- log.warn(`Supplied value for multi component / array uniform ${name2} is not a numeric array: ${value}`)();
2798
- continue;
2799
- }
2800
- typedArray.set(value, offset);
3038
+ return arrayOffset;
3039
+ }
3040
+ if (typeof type === "object") {
3041
+ let structOffset = alignTo(offset, 4);
3042
+ for (const [memberName, memberType] of Object.entries(type)) {
3043
+ structOffset = this._addToLayout(`${name2}.${memberName}`, memberType, structOffset);
2801
3044
  }
3045
+ return structOffset;
2802
3046
  }
2803
- return new Uint8Array(arrayBuffer2, 0, this.byteLength);
3047
+ throw new Error(`Unsupported CompositeShaderType for ${name2}`);
2804
3048
  }
2805
- /** Does this layout have a field with specified name */
2806
- has(name2) {
2807
- return Boolean(this.layout[name2]);
3049
+ _writeCompositeValue(typedArrays, baseName, value) {
3050
+ if (this.layout[baseName]) {
3051
+ this._writeToBuffer(typedArrays, baseName, value);
3052
+ return;
3053
+ }
3054
+ if (Array.isArray(value)) {
3055
+ for (let i = 0; i < value.length; i++) {
3056
+ const element = value[i];
3057
+ const indexedName = `${baseName}[${i}]`;
3058
+ this._writeCompositeValue(typedArrays, indexedName, element);
3059
+ }
3060
+ return;
3061
+ }
3062
+ if (typeof value === "object" && value !== null) {
3063
+ for (const [key, subValue] of Object.entries(value)) {
3064
+ const nestedName = `${baseName}.${key}`;
3065
+ this._writeCompositeValue(typedArrays, nestedName, subValue);
3066
+ }
3067
+ return;
3068
+ }
3069
+ log.warn(`Unsupported uniform value for ${baseName}:`, value)();
2808
3070
  }
2809
- /** Get offset and size for a field with specified name */
2810
- get(name2) {
3071
+ _writeToBuffer(typedArrays, name2, value) {
2811
3072
  const layout = this.layout[name2];
2812
- return layout;
3073
+ if (!layout) {
3074
+ log.warn(`Uniform ${name2} not found in layout`)();
3075
+ return;
3076
+ }
3077
+ const { type, size, offset } = layout;
3078
+ const array = typedArrays[type];
3079
+ if (size === 1) {
3080
+ array[offset] = Number(value);
3081
+ } else {
3082
+ array.set(value, offset);
3083
+ }
2813
3084
  }
2814
3085
  };
2815
3086
 
3087
+ // dist/utils/is-array.js
3088
+ function isTypedArray(value) {
3089
+ return ArrayBuffer.isView(value) && !(value instanceof DataView);
3090
+ }
3091
+ function isNumberArray(value) {
3092
+ if (Array.isArray(value)) {
3093
+ return value.length === 0 || typeof value[0] === "number";
3094
+ }
3095
+ return isTypedArray(value);
3096
+ }
3097
+
2816
3098
  // dist/utils/array-equal.js
2817
3099
  function arrayEqual(a, b, limit = 16) {
2818
3100
  if (a !== b) {
@@ -2999,6 +3281,44 @@ var UniformStore = class {
2999
3281
  }
3000
3282
  };
3001
3283
 
3284
+ // dist/shadertypes/textures/texture-layout.js
3285
+ function getTextureImageView(arrayBuffer2, memoryLayout, format, image = 0) {
3286
+ const formatInfo = textureFormatDecoder.getInfo(format);
3287
+ const bytesPerComponent = formatInfo.bytesPerPixel / formatInfo.components;
3288
+ const { bytesPerImage } = memoryLayout;
3289
+ const offset = bytesPerImage * image;
3290
+ const totalPixels = memoryLayout.bytesPerImage / bytesPerComponent;
3291
+ switch (format) {
3292
+ case "rgba8unorm":
3293
+ case "bgra8unorm":
3294
+ case "rgba8uint":
3295
+ return new Uint8Array(arrayBuffer2, offset, totalPixels);
3296
+ case "r8unorm":
3297
+ return new Uint8Array(arrayBuffer2, offset, totalPixels);
3298
+ case "r16uint":
3299
+ case "rgba16uint":
3300
+ return new Uint16Array(arrayBuffer2, offset, totalPixels);
3301
+ case "r32uint":
3302
+ case "rgba32uint":
3303
+ return new Uint32Array(arrayBuffer2, offset, totalPixels);
3304
+ case "r32float":
3305
+ return new Float32Array(arrayBuffer2, offset, totalPixels);
3306
+ case "rgba16float":
3307
+ return new Uint16Array(arrayBuffer2, offset, totalPixels);
3308
+ case "rgba32float":
3309
+ return new Float32Array(arrayBuffer2, offset, totalPixels);
3310
+ default:
3311
+ throw new Error(`Unsupported format: ${format}`);
3312
+ }
3313
+ }
3314
+ function setTextureImageData(arrayBuffer2, memoryLayout, format, data, image = 0) {
3315
+ const offset = 0;
3316
+ const totalPixels = memoryLayout.bytesPerImage / memoryLayout.bytesPerPixel;
3317
+ const subArray = data.subarray(0, totalPixels);
3318
+ const typedArray = getTextureImageView(arrayBuffer2, memoryLayout, format, image);
3319
+ typedArray.set(subArray, offset);
3320
+ }
3321
+
3002
3322
  // dist/shadertypes/textures/pixel-utils.js
3003
3323
  function readPixel(pixelData, x, y, bitsPerChannel) {
3004
3324
  if (x < 0 || x >= pixelData.width || y < 0 || y >= pixelData.height) {
@@ -3009,7 +3329,7 @@ function readPixel(pixelData, x, y, bitsPerChannel) {
3009
3329
  let bitOffsetWithinPixel = 0;
3010
3330
  const channels = [];
3011
3331
  for (let i = 0; i < 4; i++) {
3012
- const bits = bitsPerChannel[i];
3332
+ const bits = bitsPerChannel[i] ?? 0;
3013
3333
  if (bits <= 0) {
3014
3334
  channels.push(0);
3015
3335
  } else {
@@ -3018,14 +3338,14 @@ function readPixel(pixelData, x, y, bitsPerChannel) {
3018
3338
  bitOffsetWithinPixel += bits;
3019
3339
  }
3020
3340
  }
3021
- return [channels[0], channels[1], channels[2], channels[3]];
3341
+ return [channels[0] ?? 0, channels[1] ?? 0, channels[2] ?? 0, channels[3] ?? 0];
3022
3342
  }
3023
3343
  function writePixel(dataView, bitOffset, bitsPerChannel, pixel) {
3024
3344
  let currentBitOffset = bitOffset;
3025
3345
  for (let channel = 0; channel < 4; channel++) {
3026
- const bits = bitsPerChannel[channel];
3346
+ const bits = bitsPerChannel[channel] ?? 0;
3027
3347
  const maxValue = (1 << bits) - 1;
3028
- const channelValue = pixel[channel] & maxValue;
3348
+ const channelValue = (pixel[channel] ?? 0) & maxValue;
3029
3349
  writeBitsToDataView(dataView, currentBitOffset, bits, channelValue);
3030
3350
  currentBitOffset += bits;
3031
3351
  }