@luma.gl/engine 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 (80) hide show
  1. package/dist/animation-loop/animation-loop.d.ts +3 -1
  2. package/dist/animation-loop/animation-loop.d.ts.map +1 -1
  3. package/dist/animation-loop/animation-loop.js +10 -4
  4. package/dist/animation-loop/animation-loop.js.map +1 -1
  5. package/dist/compute/computation.d.ts.map +1 -1
  6. package/dist/compute/computation.js +3 -2
  7. package/dist/compute/computation.js.map +1 -1
  8. package/dist/compute/swap.d.ts +2 -0
  9. package/dist/compute/swap.d.ts.map +1 -1
  10. package/dist/compute/swap.js +10 -5
  11. package/dist/compute/swap.js.map +1 -1
  12. package/dist/dist.dev.js +1251 -574
  13. package/dist/dist.min.js +216 -48
  14. package/dist/dynamic-texture/dynamic-texture.d.ts +95 -0
  15. package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -0
  16. package/dist/dynamic-texture/dynamic-texture.js +389 -0
  17. package/dist/dynamic-texture/dynamic-texture.js.map +1 -0
  18. package/dist/dynamic-texture/mipmaps.d.ts +6 -0
  19. package/dist/dynamic-texture/mipmaps.d.ts.map +1 -0
  20. package/dist/dynamic-texture/mipmaps.js +441 -0
  21. package/dist/dynamic-texture/mipmaps.js.map +1 -0
  22. package/dist/dynamic-texture/texture-data.d.ts +137 -0
  23. package/dist/dynamic-texture/texture-data.d.ts.map +1 -0
  24. package/dist/dynamic-texture/texture-data.js +183 -0
  25. package/dist/dynamic-texture/texture-data.js.map +1 -0
  26. package/dist/factories/pipeline-factory.d.ts.map +1 -1
  27. package/dist/factories/pipeline-factory.js +3 -3
  28. package/dist/factories/pipeline-factory.js.map +1 -1
  29. package/dist/factories/shader-factory.d.ts.map +1 -1
  30. package/dist/factories/shader-factory.js +3 -2
  31. package/dist/factories/shader-factory.js.map +1 -1
  32. package/dist/index.cjs +1243 -583
  33. package/dist/index.cjs.map +4 -4
  34. package/dist/index.d.ts +8 -3
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +4 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/model/model.d.ts +31 -10
  39. package/dist/model/model.d.ts.map +1 -1
  40. package/dist/model/model.js +34 -14
  41. package/dist/model/model.js.map +1 -1
  42. package/dist/models/billboard-texture-model.d.ts +8 -5
  43. package/dist/models/billboard-texture-model.d.ts.map +1 -1
  44. package/dist/models/billboard-texture-model.js +70 -18
  45. package/dist/models/billboard-texture-model.js.map +1 -1
  46. package/dist/passes/get-fragment-shader.js +15 -11
  47. package/dist/passes/get-fragment-shader.js.map +1 -1
  48. package/dist/passes/shader-pass-renderer.d.ts +5 -5
  49. package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
  50. package/dist/passes/shader-pass-renderer.js +13 -12
  51. package/dist/passes/shader-pass-renderer.js.map +1 -1
  52. package/dist/types.d.ts +7 -0
  53. package/dist/types.d.ts.map +1 -0
  54. package/dist/types.js +5 -0
  55. package/dist/types.js.map +1 -0
  56. package/dist/utils/buffer-layout-order.d.ts.map +1 -1
  57. package/dist/utils/buffer-layout-order.js +12 -2
  58. package/dist/utils/buffer-layout-order.js.map +1 -1
  59. package/package.json +6 -6
  60. package/src/animation-loop/animation-loop.ts +11 -4
  61. package/src/compute/computation.ts +3 -2
  62. package/src/compute/swap.ts +13 -7
  63. package/src/dynamic-texture/dynamic-texture.ts +499 -0
  64. package/src/dynamic-texture/mipmaps.ts +517 -0
  65. package/src/dynamic-texture/texture-data.ts +301 -0
  66. package/src/factories/pipeline-factory.ts +4 -3
  67. package/src/factories/shader-factory.ts +4 -2
  68. package/src/index.ts +9 -4
  69. package/src/model/model.ts +37 -18
  70. package/src/models/billboard-texture-model.ts +81 -22
  71. package/src/passes/get-fragment-shader.ts +15 -11
  72. package/src/passes/shader-pass-renderer.ts +22 -16
  73. package/src/types.ts +11 -0
  74. package/src/utils/buffer-layout-order.ts +18 -2
  75. package/dist/async-texture/async-texture.d.ts +0 -166
  76. package/dist/async-texture/async-texture.d.ts.map +0 -1
  77. package/dist/async-texture/async-texture.js +0 -386
  78. package/dist/async-texture/async-texture.js.map +0 -1
  79. package/src/async-texture/async-texture.ts +0 -551
  80. /package/src/{async-texture/texture-setters.ts.disabled → dynamic-texture/texture-data.ts.disabled} +0 -0
package/dist/dist.dev.js CHANGED
@@ -70,6 +70,7 @@ var __exports__ = (() => {
70
70
  ConeGeometry: () => ConeGeometry,
71
71
  CubeGeometry: () => CubeGeometry,
72
72
  CylinderGeometry: () => CylinderGeometry,
73
+ DynamicTexture: () => DynamicTexture,
73
74
  GPUGeometry: () => GPUGeometry,
74
75
  Geometry: () => Geometry,
75
76
  GroupNode: () => GroupNode,
@@ -492,7 +493,7 @@ var __exports__ = (() => {
492
493
  gpuTime;
493
494
  frameRate;
494
495
  display;
495
- needsRedraw = "initialized";
496
+ _needsRedraw = "initialized";
496
497
  _initialized = false;
497
498
  _running = false;
498
499
  _animationFrameId = null;
@@ -534,9 +535,15 @@ var __exports__ = (() => {
534
535
  }
535
536
  /** Flags this animation loop as needing redraw */
536
537
  setNeedsRedraw(reason) {
537
- this.needsRedraw = this.needsRedraw || reason;
538
+ this._needsRedraw = this._needsRedraw || reason;
538
539
  return this;
539
540
  }
541
+ /** Query redraw status. Clears the flag. */
542
+ needsRedraw() {
543
+ const reason = this._needsRedraw;
544
+ this._needsRedraw = false;
545
+ return reason;
546
+ }
540
547
  setProps(props) {
541
548
  if ("autoResizeViewport" in props) {
542
549
  this.props.autoResizeViewport = props.autoResizeViewport || false;
@@ -678,7 +685,7 @@ var __exports__ = (() => {
678
685
  this.device?.submit();
679
686
  }
680
687
  _clearNeedsRedraw() {
681
- this.needsRedraw = false;
688
+ this._needsRedraw = false;
682
689
  }
683
690
  _setupFrame() {
684
691
  this._resizeViewport();
@@ -736,7 +743,7 @@ var __exports__ = (() => {
736
743
  this.animationProps.width = width;
737
744
  this.animationProps.height = height;
738
745
  this.animationProps.aspect = aspect;
739
- this.animationProps.needsRedraw = this.needsRedraw;
746
+ this.animationProps.needsRedraw = this._needsRedraw;
740
747
  this.animationProps.engineTime = Date.now() - this.animationProps.startTime;
741
748
  if (this.timeline) {
742
749
  this.timeline.update(this.animationProps.engineTime);
@@ -892,8 +899,8 @@ var __exports__ = (() => {
892
899
  }
893
900
 
894
901
  // src/model/model.ts
895
- var import_core9 = __toESM(require_core(), 1);
896
- var import_shadertools2 = __toESM(require_shadertools(), 1);
902
+ var import_core12 = __toESM(require_core(), 1);
903
+ var import_shadertools3 = __toESM(require_shadertools(), 1);
897
904
 
898
905
  // src/geometry/gpu-geometry.ts
899
906
  var import_core3 = __toESM(require_core(), 1);
@@ -1007,8 +1014,9 @@ var __exports__ = (() => {
1007
1014
  var _PipelineFactory = class {
1008
1015
  /** Get the singleton default pipeline factory for the specified device */
1009
1016
  static getDefaultPipelineFactory(device) {
1010
- device._lumaData["defaultPipelineFactory"] = device._lumaData["defaultPipelineFactory"] || new _PipelineFactory(device);
1011
- return device._lumaData["defaultPipelineFactory"];
1017
+ const moduleData = device.getModuleData("@luma.gl/engine");
1018
+ moduleData.defaultPipelineFactory ||= new _PipelineFactory(device);
1019
+ return moduleData.defaultPipelineFactory;
1012
1020
  }
1013
1021
  device;
1014
1022
  cachingEnabled;
@@ -1177,8 +1185,9 @@ var __exports__ = (() => {
1177
1185
  var _ShaderFactory = class {
1178
1186
  /** Returns the default ShaderFactory for the given {@link Device}, creating one if necessary. */
1179
1187
  static getDefaultShaderFactory(device) {
1180
- device._lumaData["defaultShaderFactory"] ||= new _ShaderFactory(device);
1181
- return device._lumaData["defaultShaderFactory"];
1188
+ const moduleData = device.getModuleData("@luma.gl/engine");
1189
+ moduleData.defaultShaderFactory ||= new _ShaderFactory(device);
1190
+ return moduleData.defaultShaderFactory;
1182
1191
  }
1183
1192
  device;
1184
1193
  cachingEnabled;
@@ -1399,6 +1408,16 @@ var __exports__ = (() => {
1399
1408
  };
1400
1409
 
1401
1410
  // src/utils/buffer-layout-order.ts
1411
+ function getMinLocation(attributeNames, shaderLayoutMap) {
1412
+ let minLocation = Infinity;
1413
+ for (const name of attributeNames) {
1414
+ const location = shaderLayoutMap[name];
1415
+ if (location !== void 0) {
1416
+ minLocation = Math.min(minLocation, location);
1417
+ }
1418
+ }
1419
+ return minLocation;
1420
+ }
1402
1421
  function sortedBufferLayoutByShaderSourceLocations(shaderLayout, bufferLayout) {
1403
1422
  const shaderLayoutMap = Object.fromEntries(
1404
1423
  shaderLayout.attributes.map((attr) => [attr.name, attr.location])
@@ -1407,8 +1426,8 @@ var __exports__ = (() => {
1407
1426
  sortedLayout.sort((a, b) => {
1408
1427
  const attributeNamesA = a.attributes ? a.attributes.map((attr) => attr.attribute) : [a.name];
1409
1428
  const attributeNamesB = b.attributes ? b.attributes.map((attr) => attr.attribute) : [b.name];
1410
- const minLocationA = Math.min(...attributeNamesA.map((name) => shaderLayoutMap[name]));
1411
- const minLocationB = Math.min(...attributeNamesB.map((name) => shaderLayoutMap[name]));
1429
+ const minLocationA = getMinLocation(attributeNamesA, shaderLayoutMap);
1430
+ const minLocationB = getMinLocation(attributeNamesB, shaderLayoutMap);
1412
1431
  return minLocationA - minLocationB;
1413
1432
  });
1414
1433
  return sortedLayout;
@@ -1554,49 +1573,810 @@ var __exports__ = (() => {
1554
1573
  }
1555
1574
  };
1556
1575
 
1557
- // src/async-texture/async-texture.ts
1558
- var import_core8 = __toESM(require_core(), 1);
1576
+ // src/dynamic-texture/dynamic-texture.ts
1577
+ var import_core11 = __toESM(require_core(), 1);
1559
1578
 
1560
- // src/application-utils/load-file.ts
1561
- var pathPrefix = "";
1562
- function setPathPrefix(prefix) {
1563
- pathPrefix = prefix;
1579
+ // src/dynamic-texture/texture-data.ts
1580
+ var import_core8 = __toESM(require_core(), 1);
1581
+ var TEXTURE_CUBE_FACE_MAP = { "+X": 0, "-X": 1, "+Y": 2, "-Y": 3, "+Z": 4, "-Z": 5 };
1582
+ function getFirstMipLevel(layer) {
1583
+ if (!layer)
1584
+ return null;
1585
+ return Array.isArray(layer) ? layer[0] ?? null : layer;
1564
1586
  }
1565
- async function loadImageBitmap(url, opts) {
1566
- const image = new Image();
1567
- image.crossOrigin = opts?.crossOrigin || "anonymous";
1568
- image.src = url.startsWith("http") ? url : pathPrefix + url;
1569
- await image.decode();
1570
- return opts ? await createImageBitmap(image, opts) : await createImageBitmap(image);
1587
+ function getTextureSizeFromData(props) {
1588
+ const { dimension, data } = props;
1589
+ if (!data) {
1590
+ return null;
1591
+ }
1592
+ switch (dimension) {
1593
+ case "1d": {
1594
+ const mipLevel = getFirstMipLevel(data);
1595
+ if (!mipLevel)
1596
+ return null;
1597
+ const { width } = getTextureMipLevelSize(mipLevel);
1598
+ return { width, height: 1 };
1599
+ }
1600
+ case "2d": {
1601
+ const mipLevel = getFirstMipLevel(data);
1602
+ return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
1603
+ }
1604
+ case "3d":
1605
+ case "2d-array": {
1606
+ if (!Array.isArray(data) || data.length === 0)
1607
+ return null;
1608
+ const mipLevel = getFirstMipLevel(data[0]);
1609
+ return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
1610
+ }
1611
+ case "cube": {
1612
+ const face = Object.keys(data)[0] ?? null;
1613
+ if (!face)
1614
+ return null;
1615
+ const faceData = data[face];
1616
+ const mipLevel = getFirstMipLevel(faceData);
1617
+ return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
1618
+ }
1619
+ case "cube-array": {
1620
+ if (!Array.isArray(data) || data.length === 0)
1621
+ return null;
1622
+ const firstCube = data[0];
1623
+ const face = Object.keys(firstCube)[0] ?? null;
1624
+ if (!face)
1625
+ return null;
1626
+ const mipLevel = getFirstMipLevel(firstCube[face]);
1627
+ return mipLevel ? getTextureMipLevelSize(mipLevel) : null;
1628
+ }
1629
+ default:
1630
+ return null;
1631
+ }
1571
1632
  }
1572
- async function loadImage(url, opts) {
1573
- return await new Promise((resolve, reject) => {
1633
+ function getTextureMipLevelSize(data) {
1634
+ if ((0, import_core8.isExternalImage)(data)) {
1635
+ return (0, import_core8.getExternalImageSize)(data);
1636
+ }
1637
+ if (typeof data === "object" && "width" in data && "height" in data) {
1638
+ return { width: data.width, height: data.height };
1639
+ }
1640
+ throw new Error("Unsupported mip-level data");
1641
+ }
1642
+ function isTextureImageData(data) {
1643
+ return typeof data === "object" && data !== null && "data" in data && "width" in data && "height" in data;
1644
+ }
1645
+ function getCubeFaceIndex(face) {
1646
+ const idx = TEXTURE_CUBE_FACE_MAP[face];
1647
+ if (idx === void 0)
1648
+ throw new Error(`Invalid cube face: ${face}`);
1649
+ return idx;
1650
+ }
1651
+ function getCubeArrayFaceIndex(cubeIndex, face) {
1652
+ return 6 * cubeIndex + getCubeFaceIndex(face);
1653
+ }
1654
+ function getTexture1DSubresources(data) {
1655
+ throw new Error("setTexture1DData not supported in WebGL.");
1656
+ }
1657
+ function _normalizeTexture2DData(data) {
1658
+ return Array.isArray(data) ? data : [data];
1659
+ }
1660
+ function getTexture2DSubresources(slice, lodData) {
1661
+ const lodArray = _normalizeTexture2DData(lodData);
1662
+ const z = slice;
1663
+ const subresources = [];
1664
+ for (let mipLevel = 0; mipLevel < lodArray.length; mipLevel++) {
1665
+ const imageData = lodArray[mipLevel];
1666
+ if ((0, import_core8.isExternalImage)(imageData)) {
1667
+ subresources.push({
1668
+ type: "external-image",
1669
+ image: imageData,
1670
+ z,
1671
+ mipLevel
1672
+ });
1673
+ } else if (isTextureImageData(imageData)) {
1674
+ subresources.push({
1675
+ type: "texture-data",
1676
+ data: imageData,
1677
+ z,
1678
+ mipLevel
1679
+ });
1680
+ } else {
1681
+ throw new Error("Unsupported 2D mip-level payload");
1682
+ }
1683
+ }
1684
+ return subresources;
1685
+ }
1686
+ function getTexture3DSubresources(data) {
1687
+ const subresources = [];
1688
+ for (let depth = 0; depth < data.length; depth++) {
1689
+ subresources.push(...getTexture2DSubresources(depth, data[depth]));
1690
+ }
1691
+ return subresources;
1692
+ }
1693
+ function getTextureArraySubresources(data) {
1694
+ const subresources = [];
1695
+ for (let layer = 0; layer < data.length; layer++) {
1696
+ subresources.push(...getTexture2DSubresources(layer, data[layer]));
1697
+ }
1698
+ return subresources;
1699
+ }
1700
+ function getTextureCubeSubresources(data) {
1701
+ const subresources = [];
1702
+ for (const [face, faceData] of Object.entries(data)) {
1703
+ const faceDepth = getCubeFaceIndex(face);
1704
+ subresources.push(...getTexture2DSubresources(faceDepth, faceData));
1705
+ }
1706
+ return subresources;
1707
+ }
1708
+ function getTextureCubeArraySubresources(data) {
1709
+ const subresources = [];
1710
+ data.forEach((cubeData, cubeIndex) => {
1711
+ for (const [face, faceData] of Object.entries(cubeData)) {
1712
+ const faceDepth = getCubeArrayFaceIndex(cubeIndex, face);
1713
+ subresources.push(...getTexture2DSubresources(faceDepth, faceData));
1714
+ }
1715
+ });
1716
+ return subresources;
1717
+ }
1718
+
1719
+ // src/dynamic-texture/mipmaps.ts
1720
+ var import_core10 = __toESM(require_core(), 1);
1721
+
1722
+ // src/compute/computation.ts
1723
+ var import_core9 = __toESM(require_core(), 1);
1724
+ var import_shadertools2 = __toESM(require_shadertools(), 1);
1725
+ var LOG_DRAW_PRIORITY = 2;
1726
+ var LOG_DRAW_TIMEOUT = 1e4;
1727
+ var _Computation = class {
1728
+ device;
1729
+ id;
1730
+ pipelineFactory;
1731
+ shaderFactory;
1732
+ userData = {};
1733
+ /** Bindings (textures, samplers, uniform buffers) */
1734
+ bindings = {};
1735
+ /** The underlying GPU pipeline. */
1736
+ pipeline;
1737
+ /** Assembled compute shader source */
1738
+ source;
1739
+ /** the underlying compiled compute shader */
1740
+ // @ts-ignore Set in function called from constructor
1741
+ shader;
1742
+ /** ShaderInputs instance */
1743
+ shaderInputs;
1744
+ // @ts-ignore Set in function called from constructor
1745
+ _uniformStore;
1746
+ _pipelineNeedsUpdate = "newly created";
1747
+ _getModuleUniforms;
1748
+ props;
1749
+ _destroyed = false;
1750
+ constructor(device, props) {
1751
+ if (device.type !== "webgpu") {
1752
+ throw new Error("Computation is only supported in WebGPU");
1753
+ }
1754
+ this.props = { ..._Computation.defaultProps, ...props };
1755
+ props = this.props;
1756
+ this.id = props.id || uid("model");
1757
+ this.device = device;
1758
+ Object.assign(this.userData, props.userData);
1759
+ const moduleMap = Object.fromEntries(
1760
+ this.props.modules?.map((module) => [module.name, module]) || []
1761
+ );
1762
+ this.shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap);
1763
+ this.setShaderInputs(this.shaderInputs);
1764
+ this.props.shaderLayout ||= device.getShaderLayout(this.props.source);
1765
+ const platformInfo = getPlatformInfo(device);
1766
+ const modules = (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
1767
+ this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
1768
+ this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
1769
+ const { source: source3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleWGSLShader({
1770
+ platformInfo,
1771
+ ...this.props,
1772
+ modules
1773
+ });
1774
+ this.source = source3;
1775
+ this._getModuleUniforms = getUniforms2;
1776
+ this.pipeline = this._updatePipeline();
1777
+ if (props.bindings) {
1778
+ this.setBindings(props.bindings);
1779
+ }
1780
+ Object.seal(this);
1781
+ }
1782
+ destroy() {
1783
+ if (this._destroyed)
1784
+ return;
1785
+ this.pipelineFactory.release(this.pipeline);
1786
+ this.shaderFactory.release(this.shader);
1787
+ this._uniformStore.destroy();
1788
+ this._destroyed = true;
1789
+ }
1790
+ // Draw call
1791
+ predraw() {
1792
+ this.updateShaderInputs();
1793
+ }
1794
+ dispatch(computePass, x, y, z) {
1574
1795
  try {
1575
- const image = new Image();
1576
- image.onload = () => resolve(image);
1577
- image.onerror = () => reject(new Error(`Could not load image ${url}.`));
1578
- image.crossOrigin = opts?.crossOrigin || "anonymous";
1579
- image.src = url.startsWith("http") ? url : pathPrefix + url;
1580
- } catch (error) {
1581
- reject(error);
1796
+ this._logDrawCallStart();
1797
+ this.pipeline = this._updatePipeline();
1798
+ this.pipeline.setBindings(this.bindings);
1799
+ computePass.setPipeline(this.pipeline);
1800
+ computePass.setBindings([]);
1801
+ computePass.dispatch(x, y, z);
1802
+ } finally {
1803
+ this._logDrawCallEnd();
1804
+ }
1805
+ }
1806
+ // Update fixed fields (can trigger pipeline rebuild)
1807
+ // Update dynamic fields
1808
+ /**
1809
+ * Updates the vertex count (used in draw calls)
1810
+ * @note Any attributes with stepMode=vertex need to be at least this big
1811
+ */
1812
+ setVertexCount(vertexCount) {
1813
+ }
1814
+ /**
1815
+ * Updates the instance count (used in draw calls)
1816
+ * @note Any attributes with stepMode=instance need to be at least this big
1817
+ */
1818
+ setInstanceCount(instanceCount) {
1819
+ }
1820
+ setShaderInputs(shaderInputs) {
1821
+ this.shaderInputs = shaderInputs;
1822
+ this._uniformStore = new import_core9.UniformStore(this.shaderInputs.modules);
1823
+ for (const moduleName of Object.keys(this.shaderInputs.modules)) {
1824
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
1825
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
1826
+ }
1827
+ }
1828
+ /**
1829
+ * Updates shader module settings (which results in uniforms being set)
1830
+ */
1831
+ setShaderModuleProps(props) {
1832
+ const uniforms = this._getModuleUniforms(props);
1833
+ const keys = Object.keys(uniforms).filter((k) => {
1834
+ const uniform = uniforms[k];
1835
+ return !isNumericArray(uniform) && typeof uniform !== "number" && typeof uniform !== "boolean";
1836
+ });
1837
+ const bindings = {};
1838
+ for (const k of keys) {
1839
+ bindings[k] = uniforms[k];
1840
+ delete uniforms[k];
1841
+ }
1842
+ }
1843
+ updateShaderInputs() {
1844
+ this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
1845
+ }
1846
+ /**
1847
+ * Sets bindings (textures, samplers, uniform buffers)
1848
+ */
1849
+ setBindings(bindings) {
1850
+ Object.assign(this.bindings, bindings);
1851
+ }
1852
+ _setPipelineNeedsUpdate(reason) {
1853
+ this._pipelineNeedsUpdate = this._pipelineNeedsUpdate || reason;
1854
+ }
1855
+ _updatePipeline() {
1856
+ if (this._pipelineNeedsUpdate) {
1857
+ let prevShader = null;
1858
+ if (this.pipeline) {
1859
+ import_core9.log.log(
1860
+ 1,
1861
+ `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
1862
+ )();
1863
+ prevShader = this.shader;
1864
+ }
1865
+ this._pipelineNeedsUpdate = false;
1866
+ this.shader = this.shaderFactory.createShader({
1867
+ id: `${this.id}-fragment`,
1868
+ stage: "compute",
1869
+ source: this.source,
1870
+ debugShaders: this.props.debugShaders
1871
+ });
1872
+ this.pipeline = this.pipelineFactory.createComputePipeline({
1873
+ ...this.props,
1874
+ shader: this.shader
1875
+ });
1876
+ if (prevShader) {
1877
+ this.shaderFactory.release(prevShader);
1878
+ }
1879
+ }
1880
+ return this.pipeline;
1881
+ }
1882
+ /** Throttle draw call logging */
1883
+ _lastLogTime = 0;
1884
+ _logOpen = false;
1885
+ _logDrawCallStart() {
1886
+ const logDrawTimeout = import_core9.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
1887
+ if (import_core9.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
1888
+ return;
1889
+ }
1890
+ this._lastLogTime = Date.now();
1891
+ this._logOpen = true;
1892
+ import_core9.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core9.log.level <= 2 })();
1893
+ }
1894
+ _logDrawCallEnd() {
1895
+ if (this._logOpen) {
1896
+ const uniformTable = this.shaderInputs.getDebugTable();
1897
+ import_core9.log.table(LOG_DRAW_PRIORITY, uniformTable)();
1898
+ import_core9.log.groupEnd(LOG_DRAW_PRIORITY)();
1899
+ this._logOpen = false;
1900
+ }
1901
+ }
1902
+ _drawCount = 0;
1903
+ // TODO - fix typing of luma data types
1904
+ _getBufferOrConstantValues(attribute, dataType) {
1905
+ const TypedArrayConstructor = (0, import_core9.getTypedArrayConstructor)(dataType);
1906
+ const typedArray = attribute instanceof import_core9.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
1907
+ return typedArray.toString();
1908
+ }
1909
+ };
1910
+ var Computation = _Computation;
1911
+ __publicField(Computation, "defaultProps", {
1912
+ ...import_core9.ComputePipeline.defaultProps,
1913
+ id: "unnamed",
1914
+ handle: void 0,
1915
+ userData: {},
1916
+ source: "",
1917
+ modules: [],
1918
+ defines: {},
1919
+ bindings: void 0,
1920
+ shaderInputs: void 0,
1921
+ pipelineFactory: void 0,
1922
+ shaderFactory: void 0,
1923
+ shaderAssembler: import_shadertools2.ShaderAssembler.getDefaultShaderAssembler(),
1924
+ debugShaders: void 0
1925
+ });
1926
+ function getPlatformInfo(device) {
1927
+ return {
1928
+ type: device.type,
1929
+ shaderLanguage: device.info.shadingLanguage,
1930
+ shaderLanguageVersion: device.info.shadingLanguageVersion,
1931
+ gpu: device.info.gpu,
1932
+ // HACK - we pretend that the DeviceFeatures is a Set, it has a similar API
1933
+ features: device.features
1934
+ };
1935
+ }
1936
+
1937
+ // src/dynamic-texture/mipmaps.ts
1938
+ var RENDER_DIMENSIONS = [
1939
+ "2d",
1940
+ "2d-array",
1941
+ "cube",
1942
+ "cube-array"
1943
+ ];
1944
+ var WORKGROUP_SIZE = {
1945
+ x: 4,
1946
+ y: 4,
1947
+ z: 4
1948
+ };
1949
+ function generateMipmap(device, texture) {
1950
+ if (texture.mipLevels <= 1) {
1951
+ return;
1952
+ }
1953
+ if (device.type !== "webgpu") {
1954
+ throw new Error(
1955
+ `Cannot generate mipmaps on device type "${device.type}". Use generateMipmapsWebGL for WebGL devices.`
1956
+ );
1957
+ }
1958
+ if (texture.dimension === "3d") {
1959
+ generateMipmaps3D(device, texture);
1960
+ return;
1961
+ }
1962
+ if (RENDER_DIMENSIONS.includes(texture.dimension)) {
1963
+ generateMipmapsRender(device, texture);
1964
+ return;
1965
+ }
1966
+ throw new Error(
1967
+ `Cannot generate mipmaps for texture dimension "${texture.dimension}" with WebGPU.`
1968
+ );
1969
+ }
1970
+ function generateMipmapsRender(device, texture) {
1971
+ validateFormatCapabilities(device, texture, ["render", "filter"], "render");
1972
+ const colorAttachmentFormat = getColorAttachmentFormat(
1973
+ texture.format,
1974
+ "render",
1975
+ texture.dimension
1976
+ );
1977
+ const viewDimension = texture.dimension;
1978
+ const shader = getRenderMipmapWGSL(viewDimension);
1979
+ const sampler = device.createSampler({ minFilter: "linear", magFilter: "linear" });
1980
+ const uniformValues = new Uint32Array(1);
1981
+ const uniformsBuffer = device.createBuffer({
1982
+ byteLength: 16,
1983
+ usage: import_core10.Buffer.UNIFORM | import_core10.Buffer.COPY_DST
1984
+ });
1985
+ const model = new Model(device, {
1986
+ source: shader,
1987
+ colorAttachmentFormats: [colorAttachmentFormat],
1988
+ topology: "triangle-list",
1989
+ vertexCount: 3,
1990
+ shaderLayout: {
1991
+ attributes: [],
1992
+ bindings: [
1993
+ { type: "sampler", name: "sourceSampler", group: 0, location: 0 },
1994
+ {
1995
+ type: "texture",
1996
+ name: "sourceTexture",
1997
+ group: 0,
1998
+ location: 1,
1999
+ viewDimension,
2000
+ sampleType: "float"
2001
+ },
2002
+ { type: "uniform", name: "uniforms", group: 0, location: 2 }
2003
+ ]
2004
+ },
2005
+ bindings: {
2006
+ sourceSampler: sampler,
2007
+ sourceTexture: texture,
2008
+ uniforms: uniformsBuffer
1582
2009
  }
1583
2010
  });
2011
+ let sourceWidth = texture.width;
2012
+ let sourceHeight = texture.height;
2013
+ const layerCount = texture.dimension === "2d" ? 1 : texture.depth;
2014
+ try {
2015
+ for (let baseMipLevel = 1; baseMipLevel < texture.mipLevels; ++baseMipLevel) {
2016
+ validateFormatCapabilities(device, texture, ["render", "filter"], "render");
2017
+ const sourceMipLevel = baseMipLevel - 1;
2018
+ const destinationWidth = Math.max(1, sourceWidth >> 1);
2019
+ const destinationHeight = Math.max(1, sourceHeight >> 1);
2020
+ const sourceView = texture.createView({
2021
+ dimension: viewDimension,
2022
+ baseMipLevel: sourceMipLevel,
2023
+ mipLevelCount: 1,
2024
+ baseArrayLayer: 0,
2025
+ arrayLayerCount: texture.depth
2026
+ });
2027
+ model.setBindings({ sourceTexture: sourceView });
2028
+ for (let baseArrayLayer = 0; baseArrayLayer < layerCount; ++baseArrayLayer) {
2029
+ uniformValues[0] = baseArrayLayer;
2030
+ uniformsBuffer.write(uniformValues);
2031
+ const destinationView = texture.createView({
2032
+ dimension: "2d",
2033
+ baseMipLevel,
2034
+ mipLevelCount: 1,
2035
+ baseArrayLayer,
2036
+ arrayLayerCount: 1
2037
+ });
2038
+ const framebuffer = device.createFramebuffer({
2039
+ colorAttachments: [destinationView]
2040
+ });
2041
+ const renderPass = device.beginRenderPass({
2042
+ id: `mipmap-generation:${texture.format}:${baseMipLevel}:${baseArrayLayer}`,
2043
+ framebuffer
2044
+ });
2045
+ renderPass.setParameters({
2046
+ viewport: [0, 0, destinationWidth, destinationHeight, 0, 1],
2047
+ scissorRect: [0, 0, destinationWidth, destinationHeight]
2048
+ });
2049
+ model.draw(renderPass);
2050
+ renderPass.end();
2051
+ device.submit();
2052
+ destinationView.destroy();
2053
+ framebuffer.destroy();
2054
+ }
2055
+ sourceView.destroy();
2056
+ sourceWidth = destinationWidth;
2057
+ sourceHeight = destinationHeight;
2058
+ }
2059
+ } finally {
2060
+ model.destroy();
2061
+ sampler.destroy();
2062
+ uniformsBuffer.destroy();
2063
+ }
2064
+ }
2065
+ function getColorAttachmentFormat(format, path, dimension) {
2066
+ if (import_core10.textureFormatDecoder.isColor(format)) {
2067
+ return format;
2068
+ }
2069
+ throw new Error(
2070
+ `Cannot run ${path} mipmap generation for ${dimension} texture with format "${format}". Only color textures can be used for this operation. Required capabilities: color. Actual capabilities: color=false.`
2071
+ );
2072
+ }
2073
+ function generateMipmaps3D(device, texture) {
2074
+ validateFormatCapabilities(device, texture, ["filter", "store"], "compute");
2075
+ const format = getColorAttachmentFormat(texture.format, "compute", texture.dimension);
2076
+ const shaderSource = get3DComputeMipmapWGSL(format);
2077
+ const uniformsBuffer = device.createBuffer({
2078
+ byteLength: 32,
2079
+ usage: import_core10.Buffer.UNIFORM | import_core10.Buffer.COPY_DST
2080
+ });
2081
+ const uniformValues = new Uint32Array(8);
2082
+ let sourceWidth = texture.width;
2083
+ let sourceHeight = texture.height;
2084
+ let sourceDepth = texture.depth;
2085
+ try {
2086
+ for (let destinationMipLevel = 1; destinationMipLevel < texture.mipLevels; ++destinationMipLevel) {
2087
+ validateFormatCapabilities(device, texture, ["filter", "store"], "compute");
2088
+ const destinationWidth = Math.max(1, sourceWidth >> 1);
2089
+ const destinationHeight = Math.max(1, sourceHeight >> 1);
2090
+ const destinationDepth = Math.max(1, sourceDepth >> 1);
2091
+ uniformValues[0] = sourceWidth;
2092
+ uniformValues[1] = sourceHeight;
2093
+ uniformValues[2] = sourceDepth;
2094
+ uniformValues[3] = destinationWidth;
2095
+ uniformValues[4] = destinationHeight;
2096
+ uniformValues[5] = destinationDepth;
2097
+ uniformValues[6] = 0;
2098
+ uniformsBuffer.write(uniformValues);
2099
+ const sourceView = texture.createView({
2100
+ dimension: "3d",
2101
+ baseMipLevel: destinationMipLevel - 1,
2102
+ mipLevelCount: 1,
2103
+ baseArrayLayer: 0,
2104
+ arrayLayerCount: 1
2105
+ });
2106
+ const destinationView = texture.createView({
2107
+ dimension: "3d",
2108
+ baseMipLevel: destinationMipLevel,
2109
+ mipLevelCount: 1,
2110
+ baseArrayLayer: 0,
2111
+ arrayLayerCount: 1
2112
+ });
2113
+ const computation = new Computation(device, {
2114
+ source: shaderSource,
2115
+ shaderLayout: {
2116
+ bindings: [
2117
+ {
2118
+ type: "texture",
2119
+ name: "sourceTexture",
2120
+ group: 0,
2121
+ location: 0,
2122
+ viewDimension: "3d",
2123
+ sampleType: "float"
2124
+ },
2125
+ {
2126
+ type: "storage",
2127
+ name: "destinationTexture",
2128
+ group: 0,
2129
+ location: 1,
2130
+ format,
2131
+ viewDimension: "3d",
2132
+ access: "write-only"
2133
+ },
2134
+ { type: "uniform", name: "uniforms", group: 0, location: 2 }
2135
+ ]
2136
+ },
2137
+ bindings: {
2138
+ sourceTexture: sourceView,
2139
+ destinationTexture: destinationView,
2140
+ uniforms: uniformsBuffer
2141
+ }
2142
+ });
2143
+ const workgroupsX = Math.ceil(destinationWidth / WORKGROUP_SIZE.x);
2144
+ const workgroupsY = Math.ceil(destinationHeight / WORKGROUP_SIZE.y);
2145
+ const workgroupsZ = Math.ceil(destinationDepth / WORKGROUP_SIZE.z);
2146
+ const computePass = device.beginComputePass({});
2147
+ computation.dispatch(computePass, workgroupsX, workgroupsY, workgroupsZ);
2148
+ computePass.end();
2149
+ device.submit();
2150
+ computation.destroy();
2151
+ sourceView.destroy();
2152
+ destinationView.destroy();
2153
+ sourceWidth = destinationWidth;
2154
+ sourceHeight = destinationHeight;
2155
+ sourceDepth = destinationDepth;
2156
+ }
2157
+ } finally {
2158
+ uniformsBuffer.destroy();
2159
+ }
2160
+ }
2161
+ function validateFormatCapabilities(device, texture, requiredCapabilities, path) {
2162
+ const { format, dimension } = texture;
2163
+ const capabilities = device.getTextureFormatCapabilities(format);
2164
+ const missingCapabilities = requiredCapabilities.filter((capability) => !capabilities[capability]);
2165
+ if (missingCapabilities.length > 0) {
2166
+ const required = requiredCapabilities.join(" + ");
2167
+ const actual = requiredCapabilities.map((capability) => `${capability}=${capabilities[capability]}`).join(", ");
2168
+ throw new Error(
2169
+ `Cannot run ${path} mipmap generation for ${dimension} texture with format "${format}". Required capabilities: ${required}. Actual capabilities: ${actual}.`
2170
+ );
2171
+ }
2172
+ }
2173
+ function getSourceTextureType(dimension) {
2174
+ switch (dimension) {
2175
+ case "2d":
2176
+ return "texture_2d<f32>";
2177
+ case "2d-array":
2178
+ return "texture_2d_array<f32>";
2179
+ case "cube":
2180
+ return "texture_cube<f32>";
2181
+ case "cube-array":
2182
+ return "texture_cube_array<f32>";
2183
+ default:
2184
+ throw new Error(`Unsupported render dimension "${dimension}" for mipmap generation.`);
2185
+ }
2186
+ }
2187
+ function getRenderMipmapWGSL(dimension) {
2188
+ const sourceSnippet = getRenderMipmapSampleSnippet(dimension);
2189
+ return `
2190
+ struct MipmapUniforms {
2191
+ sourceLayer: u32,
2192
+ };
2193
+
2194
+ fn _touchUniform(uniforms: MipmapUniforms) {
2195
+ let unusedSourceLayer = uniforms.sourceLayer;
2196
+ }
2197
+
2198
+ const faceMat = array(
2199
+ mat3x3f(
2200
+ 0.0, 0.0, -2.0,
2201
+ 0.0, -2.0, 0.0,
2202
+ 1.0, 1.0, 1.0
2203
+ ), // pos-x
2204
+ mat3x3f(
2205
+ 0.0, 0.0, 2.0,
2206
+ 0.0, -2.0, 0.0,
2207
+ -1.0, 1.0, -1.0
2208
+ ), // neg-x
2209
+ mat3x3f(
2210
+ 2.0, 0.0, 0.0,
2211
+ 0.0, 0.0, 2.0,
2212
+ -1.0, 1.0, -1.0
2213
+ ), // pos-y
2214
+ mat3x3f(
2215
+ 2.0, 0.0, 0.0,
2216
+ 0.0, 0.0, -2.0,
2217
+ -1.0, -1.0, 1.0
2218
+ ), // neg-y
2219
+ mat3x3f(
2220
+ 2.0, 0.0, 0.0,
2221
+ 0.0, -2.0, 0.0,
2222
+ -1.0, 1.0, 1.0
2223
+ ), // pos-z
2224
+ mat3x3f(
2225
+ -2.0, 0.0, 0.0,
2226
+ 0.0, -2.0, 0.0,
2227
+ 1.0, 1.0, -1.0
2228
+ ) // neg-z
2229
+ );
2230
+
2231
+ struct FragmentInputs {
2232
+ @builtin(position) position: vec4f,
2233
+ @location(0) texcoord: vec2f
2234
+ };
2235
+
2236
+ struct VertexOutput {
2237
+ @builtin(position) position: vec4f,
2238
+ @location(0) texcoord: vec2f
2239
+ };
2240
+
2241
+ @group(0) @binding(0) var sourceSampler: sampler;
2242
+ @group(0) @binding(1) var sourceTexture: ${getSourceTextureType(dimension)};
2243
+ @group(0) @binding(2) var<uniform> uniforms: MipmapUniforms;
2244
+
2245
+ @vertex
2246
+ fn vertexMain(
2247
+ @builtin(vertex_index) vertexIndex: u32
2248
+ ) -> VertexOutput {
2249
+ const positions = array(
2250
+ vec2f(-1.0, -1.0),
2251
+ vec2f(-1.0, 3.0),
2252
+ vec2f( 3.0, -1.0)
2253
+ );
2254
+
2255
+ let xy = positions[vertexIndex];
2256
+ return VertexOutput(
2257
+ vec4f(xy, 0.0, 1.0),
2258
+ xy * vec2f(0.5, -0.5) + vec2f(0.5)
2259
+ );
2260
+ }
2261
+
2262
+ @fragment
2263
+ fn fragmentMain(fsInput: VertexOutput) -> @location(0) vec4f {
2264
+ _touchUniform(uniforms);
2265
+ return ${sourceSnippet};
2266
+ }
2267
+ `;
2268
+ }
2269
+ function getRenderMipmapSampleSnippet(dimension) {
2270
+ const layer = "uniforms.sourceLayer";
2271
+ switch (dimension) {
2272
+ case "2d":
2273
+ return "textureSampleLevel(sourceTexture, sourceSampler, fsInput.texcoord, 0.0)";
2274
+ case "2d-array":
2275
+ return `textureSampleLevel(sourceTexture, sourceSampler, fsInput.texcoord, i32(${layer}), 0.0)`;
2276
+ case "cube":
2277
+ return `textureSampleLevel(sourceTexture, sourceSampler, faceMat[i32(${layer})] * vec3f(fract(fsInput.texcoord), 1.0), 0.0)`;
2278
+ case "cube-array":
2279
+ return `textureSampleLevel(sourceTexture, sourceSampler, faceMat[i32(${layer} % 6u)] * vec3f(fract(fsInput.texcoord), 1.0), i32(${layer} / 6u), 0.0)`;
2280
+ default:
2281
+ throw new Error(`Unsupported render dimension "${dimension}" for mipmap generation.`);
2282
+ }
2283
+ }
2284
+ function get3DComputeMipmapWGSL(format) {
2285
+ return `
2286
+ struct MipmapUniforms {
2287
+ sourceWidth: u32,
2288
+ sourceHeight: u32,
2289
+ sourceDepth: u32,
2290
+ destinationWidth: u32,
2291
+ destinationHeight: u32,
2292
+ destinationDepth: u32,
2293
+ padding: u32,
2294
+ };
2295
+
2296
+ @group(0) @binding(0) var sourceTexture: texture_3d<f32>;
2297
+ @group(0) @binding(1) var destinationTexture: texture_storage_3d<${format}, write>;
2298
+ @group(0) @binding(2) var<uniform> uniforms: MipmapUniforms;
2299
+
2300
+ @compute @workgroup_size(${WORKGROUP_SIZE.x}, ${WORKGROUP_SIZE.y}, ${WORKGROUP_SIZE.z})
2301
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2302
+ if (
2303
+ id.x >= uniforms.destinationWidth ||
2304
+ id.y >= uniforms.destinationHeight ||
2305
+ id.z >= uniforms.destinationDepth
2306
+ ) {
2307
+ return;
2308
+ }
2309
+
2310
+ let sourceBase = id * 2u;
2311
+ let sourceX0 = min(sourceBase.x, uniforms.sourceWidth - 1u);
2312
+ let sourceY0 = min(sourceBase.y, uniforms.sourceHeight - 1u);
2313
+ let sourceZ0 = min(sourceBase.z, uniforms.sourceDepth - 1u);
2314
+
2315
+ let sourceX1 = min(sourceBase.x + 1u, uniforms.sourceWidth - 1u);
2316
+ let sourceY1 = min(sourceBase.y + 1u, uniforms.sourceHeight - 1u);
2317
+ let sourceZ1 = min(sourceBase.z + 1u, uniforms.sourceDepth - 1u);
2318
+
2319
+ var sum = textureLoad(
2320
+ sourceTexture,
2321
+ vec3<i32>(i32(sourceX0), i32(sourceY0), i32(sourceZ0)),
2322
+ 0
2323
+ );
2324
+ sum += textureLoad(
2325
+ sourceTexture,
2326
+ vec3<i32>(i32(sourceX1), i32(sourceY0), i32(sourceZ0)),
2327
+ 0
2328
+ );
2329
+ sum += textureLoad(
2330
+ sourceTexture,
2331
+ vec3<i32>(i32(sourceX0), i32(sourceY1), i32(sourceZ0)),
2332
+ 0
2333
+ );
2334
+ sum += textureLoad(
2335
+ sourceTexture,
2336
+ vec3<i32>(i32(sourceX1), i32(sourceY1), i32(sourceZ0)),
2337
+ 0
2338
+ );
2339
+ sum += textureLoad(
2340
+ sourceTexture,
2341
+ vec3<i32>(i32(sourceX0), i32(sourceY0), i32(sourceZ1)),
2342
+ 0
2343
+ );
2344
+ sum += textureLoad(
2345
+ sourceTexture,
2346
+ vec3<i32>(i32(sourceX1), i32(sourceY0), i32(sourceZ1)),
2347
+ 0
2348
+ );
2349
+ sum += textureLoad(
2350
+ sourceTexture,
2351
+ vec3<i32>(i32(sourceX0), i32(sourceY1), i32(sourceZ1)),
2352
+ 0
2353
+ );
2354
+ sum += textureLoad(
2355
+ sourceTexture,
2356
+ vec3<i32>(i32(sourceX1), i32(sourceY1), i32(sourceZ1)),
2357
+ 0
2358
+ );
2359
+
2360
+ textureStore(
2361
+ destinationTexture,
2362
+ vec3<i32>(i32(id.x), i32(id.y), i32(id.z)),
2363
+ vec4<f32>(sum.xyz / 8.0, sum.w / 8.0)
2364
+ );
2365
+ }
2366
+ `;
1584
2367
  }
1585
2368
 
1586
- // src/async-texture/async-texture.ts
1587
- var TextureCubeFaces = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
1588
- var CubeFaces = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
1589
- var _AsyncTexture = class {
2369
+ // src/dynamic-texture/dynamic-texture.ts
2370
+ var _DynamicTexture = class {
1590
2371
  device;
1591
2372
  id;
2373
+ /** Props with defaults resolved (except `data` which is processed separately) */
1592
2374
  props;
1593
- // TODO - should we type these as possibly `null`? It will make usage harder?
1594
- // @ts-expect-error
1595
- texture;
1596
- // @ts-expect-error
1597
- sampler;
1598
- // @ts-expect-error
1599
- view;
2375
+ /** Created resources */
2376
+ _texture = null;
2377
+ _sampler = null;
2378
+ _view = null;
2379
+ /** Ready when GPU texture has been created and data (if any) uploaded */
1600
2380
  ready;
1601
2381
  isReady = false;
1602
2382
  destroyed = false;
@@ -1604,274 +2384,290 @@ var __exports__ = (() => {
1604
2384
  };
1605
2385
  rejectReady = () => {
1606
2386
  };
2387
+ get texture() {
2388
+ if (!this._texture)
2389
+ throw new Error("Texture not initialized yet");
2390
+ return this._texture;
2391
+ }
2392
+ get sampler() {
2393
+ if (!this._sampler)
2394
+ throw new Error("Sampler not initialized yet");
2395
+ return this._sampler;
2396
+ }
2397
+ get view() {
2398
+ if (!this._view)
2399
+ throw new Error("View not initialized yet");
2400
+ return this._view;
2401
+ }
1607
2402
  get [Symbol.toStringTag]() {
1608
- return "AsyncTexture";
2403
+ return "DynamicTexture";
1609
2404
  }
1610
2405
  toString() {
1611
- return `AsyncTexture:"${this.id}"(${this.isReady ? "ready" : "loading"})`;
2406
+ return `DynamicTexture:"${this.id}":${this.texture.width}x${this.texture.height}px:(${this.isReady ? "ready" : "loading..."})`;
1612
2407
  }
1613
2408
  constructor(device, props) {
1614
2409
  this.device = device;
1615
- const id = uid("async-texture");
1616
- this.props = { ..._AsyncTexture.defaultProps, id, ...props };
2410
+ const id = uid("dynamic-texture");
2411
+ const originalPropsWithAsyncData = props;
2412
+ this.props = { ..._DynamicTexture.defaultProps, id, ...props, data: null };
1617
2413
  this.id = this.props.id;
1618
- props = { ...props };
1619
- if (typeof props?.data === "string" && props.dimension === "2d") {
1620
- props.data = loadImageBitmap(props.data);
1621
- }
1622
- if (props.mipmaps) {
1623
- props.mipLevels = "auto";
1624
- }
1625
2414
  this.ready = new Promise((resolve, reject) => {
1626
- this.resolveReady = () => {
1627
- this.isReady = true;
1628
- resolve();
1629
- };
2415
+ this.resolveReady = resolve;
1630
2416
  this.rejectReady = reject;
1631
2417
  });
1632
- this.initAsync(props);
2418
+ this.initAsync(originalPropsWithAsyncData);
1633
2419
  }
1634
- async initAsync(props) {
1635
- const asyncData = props.data;
1636
- const data = await awaitAllPromises(asyncData).then(
1637
- void 0,
1638
- this.rejectReady
1639
- );
1640
- if (this.destroyed) {
1641
- return;
1642
- }
1643
- const size = this.props.width && this.props.height ? { width: this.props.width, height: this.props.height } : this.getTextureDataSize(data);
1644
- if (!size) {
1645
- throw new Error("Texture size could not be determined");
1646
- }
1647
- const syncProps = { ...size, ...props, data: void 0, mipLevels: 1 };
1648
- const maxMips = this.device.getMipLevelCount(syncProps.width, syncProps.height);
1649
- syncProps.mipLevels = this.props.mipLevels === "auto" ? maxMips : Math.min(maxMips, this.props.mipLevels);
1650
- this.texture = this.device.createTexture(syncProps);
1651
- this.sampler = this.texture.sampler;
1652
- this.view = this.texture.view;
1653
- if (props.data) {
1654
- switch (this.props.dimension) {
1655
- case "1d":
1656
- this._setTexture1DData(this.texture, data);
1657
- break;
1658
- case "2d":
1659
- this._setTexture2DData(data);
1660
- break;
1661
- case "3d":
1662
- this._setTexture3DData(this.texture, data);
1663
- break;
1664
- case "2d-array":
1665
- this._setTextureArrayData(this.texture, data);
1666
- break;
1667
- case "cube":
1668
- this._setTextureCubeData(this.texture, data);
1669
- break;
1670
- case "cube-array":
1671
- this._setTextureCubeArrayData(this.texture, data);
1672
- break;
2420
+ /** @note Fire and forget; caller can await `ready` */
2421
+ async initAsync(originalPropsWithAsyncData) {
2422
+ try {
2423
+ const propsWithSyncData = await this._loadAllData(originalPropsWithAsyncData);
2424
+ this._checkNotDestroyed();
2425
+ const deduceSize = () => {
2426
+ if (this.props.width && this.props.height) {
2427
+ return { width: this.props.width, height: this.props.height };
2428
+ }
2429
+ const size2 = getTextureSizeFromData(propsWithSyncData);
2430
+ if (size2) {
2431
+ return size2;
2432
+ }
2433
+ return { width: this.props.width || 1, height: this.props.height || 1 };
2434
+ };
2435
+ const size = deduceSize();
2436
+ if (!size || size.width <= 0 || size.height <= 0) {
2437
+ throw new Error(`${this} size could not be determined or was zero`);
1673
2438
  }
2439
+ const baseTextureProps = {
2440
+ ...this.props,
2441
+ ...size,
2442
+ mipLevels: 1,
2443
+ // temporary; updated below
2444
+ data: void 0
2445
+ };
2446
+ if (this.device.type === "webgpu" && this.props.mipmaps) {
2447
+ const requiredUsage = this.props.dimension === "3d" ? import_core11.Texture.SAMPLE | import_core11.Texture.STORAGE | import_core11.Texture.COPY_DST | import_core11.Texture.COPY_SRC : import_core11.Texture.SAMPLE | import_core11.Texture.RENDER | import_core11.Texture.COPY_DST | import_core11.Texture.COPY_SRC;
2448
+ baseTextureProps.usage |= requiredUsage;
2449
+ }
2450
+ const maxMips = this.device.getMipLevelCount(baseTextureProps.width, baseTextureProps.height);
2451
+ const desired = this.props.mipLevels === "auto" ? maxMips : Math.max(1, Math.min(maxMips, this.props.mipLevels ?? 1));
2452
+ const finalTextureProps = { ...baseTextureProps, mipLevels: desired };
2453
+ this._texture = this.device.createTexture(finalTextureProps);
2454
+ this._sampler = this.texture.sampler;
2455
+ this._view = this.texture.view;
2456
+ if (propsWithSyncData.data) {
2457
+ switch (propsWithSyncData.dimension) {
2458
+ case "1d":
2459
+ this.setTexture1DData(propsWithSyncData.data);
2460
+ break;
2461
+ case "2d":
2462
+ this.setTexture2DData(propsWithSyncData.data);
2463
+ break;
2464
+ case "3d":
2465
+ this.setTexture3DData(propsWithSyncData.data);
2466
+ break;
2467
+ case "2d-array":
2468
+ this.setTextureArrayData(propsWithSyncData.data);
2469
+ break;
2470
+ case "cube":
2471
+ this.setTextureCubeData(propsWithSyncData.data);
2472
+ break;
2473
+ case "cube-array":
2474
+ this.setTextureCubeArrayData(propsWithSyncData.data);
2475
+ break;
2476
+ default: {
2477
+ throw new Error(`Unhandled dimension ${propsWithSyncData.dimension}`);
2478
+ }
2479
+ }
2480
+ }
2481
+ if (this.props.mipmaps) {
2482
+ this.generateMipmaps();
2483
+ }
2484
+ this.isReady = true;
2485
+ this.resolveReady(this.texture);
2486
+ import_core11.log.info(0, `${this} created`)();
2487
+ } catch (e) {
2488
+ const err = e instanceof Error ? e : new Error(String(e));
2489
+ this.rejectReady(err);
2490
+ throw err;
1674
2491
  }
1675
- if (this.props.mipmaps) {
1676
- this.generateMipmaps();
1677
- }
1678
- import_core8.log.info(1, `${this} loaded`);
1679
- this.resolveReady();
1680
2492
  }
1681
2493
  destroy() {
1682
- if (this.texture) {
1683
- this.texture.destroy();
1684
- this.texture = null;
2494
+ if (this._texture) {
2495
+ this._texture.destroy();
2496
+ this._texture = null;
2497
+ this._sampler = null;
2498
+ this._view = null;
1685
2499
  }
1686
2500
  this.destroyed = true;
1687
2501
  }
1688
2502
  generateMipmaps() {
1689
- this.texture.generateMipmapsWebGL();
2503
+ if (this.device.type === "webgl") {
2504
+ this.texture.generateMipmapsWebGL();
2505
+ } else if (this.device.type === "webgpu") {
2506
+ generateMipmap(this.device, this.texture);
2507
+ } else {
2508
+ import_core11.log.warn(`${this} mipmaps not supported on ${this.device.type}`);
2509
+ }
1690
2510
  }
1691
- /** Set sampler or create and set new Sampler from SamplerProps */
2511
+ /** Set sampler or create one from props */
1692
2512
  setSampler(sampler = {}) {
1693
- this.texture.setSampler(
1694
- sampler instanceof import_core8.Sampler ? sampler : this.device.createSampler(sampler)
1695
- );
2513
+ this._checkReady();
2514
+ const s = sampler instanceof import_core11.Sampler ? sampler : this.device.createSampler(sampler);
2515
+ this.texture.setSampler(s);
2516
+ this._sampler = s;
1696
2517
  }
1697
2518
  /**
1698
- * Textures are immutable and cannot be resized after creation,
1699
- * but we can create a similar texture with the same parameters but a new size.
1700
- * @note Does not copy contents of the texture
1701
- * @note Mipmaps may need to be regenerated after resizing / setting new data
1702
- * @todo Abort pending promise and create a texture with the new size?
2519
+ * Resize by cloning the underlying immutable texture.
2520
+ * Does not copy contents; caller may need to re-upload and/or regenerate mips.
1703
2521
  */
1704
2522
  resize(size) {
1705
- if (!this.isReady) {
1706
- throw new Error("Cannot resize texture before it is ready");
1707
- }
2523
+ this._checkReady();
1708
2524
  if (size.width === this.texture.width && size.height === this.texture.height) {
1709
2525
  return false;
1710
2526
  }
1711
- if (this.texture) {
1712
- const texture = this.texture;
1713
- this.texture = texture.clone(size);
1714
- texture.destroy();
1715
- }
2527
+ const prev = this.texture;
2528
+ this._texture = prev.clone(size);
2529
+ this._sampler = this.texture.sampler;
2530
+ this._view = this.texture.view;
2531
+ prev.destroy();
2532
+ import_core11.log.info(`${this} resized`);
1716
2533
  return true;
1717
2534
  }
1718
- /** Check if texture data is a typed array */
1719
- isTextureLevelData(data) {
1720
- const typedArray = data?.data;
1721
- return ArrayBuffer.isView(typedArray);
1722
- }
1723
- /** Get the size of the texture described by the provided TextureData */
1724
- getTextureDataSize(data) {
1725
- if (!data) {
1726
- return null;
1727
- }
1728
- if (ArrayBuffer.isView(data)) {
1729
- return null;
1730
- }
1731
- if (Array.isArray(data)) {
1732
- return this.getTextureDataSize(data[0]);
1733
- }
1734
- if (this.device.isExternalImage(data)) {
1735
- return this.device.getExternalImageSize(data);
1736
- }
1737
- if (data && typeof data === "object" && data.constructor === Object) {
1738
- const textureDataArray = Object.values(data);
1739
- const untypedData = textureDataArray[0];
1740
- return { width: untypedData.width, height: untypedData.height };
1741
- }
1742
- throw new Error("texture size deduction failed");
1743
- }
1744
- /** Convert luma.gl cubemap face constants to depth index */
1745
- getCubeFaceDepth(face) {
1746
- switch (face) {
1747
- case "+X":
1748
- return 0;
1749
- case "-X":
1750
- return 1;
1751
- case "+Y":
1752
- return 2;
1753
- case "-Y":
1754
- return 3;
1755
- case "+Z":
1756
- return 4;
1757
- case "-Z":
1758
- return 5;
1759
- default:
1760
- throw new Error(face);
1761
- }
1762
- }
1763
- // EXPERIMENTAL
1764
- setTextureData(data) {
1765
- }
1766
- /** Experimental: Set multiple mip levels */
1767
- _setTexture1DData(texture, data) {
1768
- throw new Error("setTexture1DData not supported in WebGL.");
1769
- }
1770
- /** Experimental: Set multiple mip levels */
1771
- _setTexture2DData(lodData, depth = 0) {
1772
- if (!this.texture) {
1773
- throw new Error("Texture not initialized");
1774
- }
1775
- const lodArray = this._normalizeTextureData(lodData);
1776
- if (lodArray.length > 1 && this.props.mipmaps !== false) {
1777
- import_core8.log.warn(`Texture ${this.id} mipmap and multiple LODs.`)();
1778
- }
1779
- for (let mipLevel = 0; mipLevel < lodArray.length; mipLevel++) {
1780
- const imageData = lodArray[mipLevel];
1781
- if (this.device.isExternalImage(imageData)) {
1782
- this.texture.copyExternalImage({ image: imageData, depth, mipLevel, flipY: true });
1783
- } else {
1784
- this.texture.copyImageData({ data: imageData.data, mipLevel });
2535
+ /** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
2536
+ getCubeFaceIndex(face) {
2537
+ const index = TEXTURE_CUBE_FACE_MAP[face];
2538
+ if (index === void 0)
2539
+ throw new Error(`Invalid cube face: ${face}`);
2540
+ return index;
2541
+ }
2542
+ /** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
2543
+ getCubeArrayFaceIndex(cubeIndex, face) {
2544
+ return 6 * cubeIndex + this.getCubeFaceIndex(face);
2545
+ }
2546
+ /** @note experimental: Set multiple mip levels (1D) */
2547
+ setTexture1DData(data) {
2548
+ this._checkReady();
2549
+ if (this.texture.props.dimension !== "1d") {
2550
+ throw new Error(`${this} is not 1d`);
2551
+ }
2552
+ const subresources = getTexture1DSubresources(data);
2553
+ this._setTextureSubresources(subresources);
2554
+ }
2555
+ /** @note experimental: Set multiple mip levels (2D), optionally at `z`, slice (depth/array level) index */
2556
+ setTexture2DData(lodData, z = 0) {
2557
+ this._checkReady();
2558
+ if (this.texture.props.dimension !== "2d") {
2559
+ throw new Error(`${this} is not 2d`);
2560
+ }
2561
+ const subresources = getTexture2DSubresources(z, lodData);
2562
+ this._setTextureSubresources(subresources);
2563
+ }
2564
+ /** 3D: multiple depth slices, each may carry multiple mip levels */
2565
+ setTexture3DData(data) {
2566
+ if (this.texture.props.dimension !== "3d") {
2567
+ throw new Error(`${this} is not 3d`);
2568
+ }
2569
+ const subresources = getTexture3DSubresources(data);
2570
+ this._setTextureSubresources(subresources);
2571
+ }
2572
+ /** 2D array: multiple layers, each may carry multiple mip levels */
2573
+ setTextureArrayData(data) {
2574
+ if (this.texture.props.dimension !== "2d-array") {
2575
+ throw new Error(`${this} is not 2d-array`);
2576
+ }
2577
+ const subresources = getTextureArraySubresources(data);
2578
+ this._setTextureSubresources(subresources);
2579
+ }
2580
+ /** Cube: 6 faces, each may carry multiple mip levels */
2581
+ setTextureCubeData(data) {
2582
+ if (this.texture.props.dimension !== "cube") {
2583
+ throw new Error(`${this} is not cube`);
2584
+ }
2585
+ const subresources = getTextureCubeSubresources(data);
2586
+ this._setTextureSubresources(subresources);
2587
+ }
2588
+ /** Cube array: multiple cubes (faces×layers), each face may carry multiple mips */
2589
+ setTextureCubeArrayData(data) {
2590
+ if (this.texture.props.dimension !== "cube-array") {
2591
+ throw new Error(`${this} is not cube-array`);
2592
+ }
2593
+ const subresources = getTextureCubeArraySubresources(data);
2594
+ this._setTextureSubresources(subresources);
2595
+ }
2596
+ /** Sets multiple mip levels on different `z` slices (depth/array index) */
2597
+ _setTextureSubresources(subresources) {
2598
+ for (const subresource of subresources) {
2599
+ const { z, mipLevel } = subresource;
2600
+ switch (subresource.type) {
2601
+ case "external-image":
2602
+ const { image, flipY } = subresource;
2603
+ this.texture.copyExternalImage({ image, z, mipLevel, flipY });
2604
+ break;
2605
+ case "texture-data":
2606
+ const { data } = subresource;
2607
+ this.texture.writeData(getAlignedUploadData(this.texture, data), {
2608
+ x: 0,
2609
+ y: 0,
2610
+ z,
2611
+ width: data.width,
2612
+ height: data.height,
2613
+ depthOrArrayLayers: 1,
2614
+ mipLevel
2615
+ });
2616
+ break;
2617
+ default:
2618
+ throw new Error("Unsupported 2D mip-level payload");
1785
2619
  }
1786
2620
  }
1787
2621
  }
1788
- /**
1789
- * Experimental: Sets 3D texture data: multiple depth slices, multiple mip levels
1790
- * @param data
1791
- */
1792
- _setTexture3DData(texture, data) {
1793
- if (this.texture?.props.dimension !== "3d") {
1794
- throw new Error(this.id);
1795
- }
1796
- for (let depth = 0; depth < data.length; depth++) {
1797
- this._setTexture2DData(data[depth], depth);
1798
- }
2622
+ // ------------------ helpers ------------------
2623
+ /** Recursively resolve all promises in data structures */
2624
+ async _loadAllData(props) {
2625
+ const syncData = await awaitAllPromises(props.data);
2626
+ const dimension = props.dimension ?? "2d";
2627
+ return { dimension, data: syncData ?? null };
1799
2628
  }
1800
- /**
1801
- * Experimental: Set Cube texture data, multiple faces, multiple mip levels
1802
- * @todo - could support TextureCubeArray with depth
1803
- * @param data
1804
- * @param index
1805
- */
1806
- _setTextureCubeData(texture, data) {
1807
- if (this.texture?.props.dimension !== "cube") {
1808
- throw new Error(this.id);
1809
- }
1810
- for (const [face, faceData] of Object.entries(data)) {
1811
- const faceDepth = CubeFaces.indexOf(face);
1812
- this._setTexture2DData(faceData, faceDepth);
1813
- }
1814
- }
1815
- /**
1816
- * Experimental: Sets texture array data, multiple levels, multiple depth slices
1817
- * @param data
1818
- */
1819
- _setTextureArrayData(texture, data) {
1820
- if (this.texture?.props.dimension !== "2d-array") {
1821
- throw new Error(this.id);
1822
- }
1823
- for (let depth = 0; depth < data.length; depth++) {
1824
- this._setTexture2DData(data[depth], depth);
1825
- }
1826
- }
1827
- /**
1828
- * Experimental: Sets texture cube array, multiple faces, multiple levels, multiple mip levels
1829
- * @param data
1830
- */
1831
- _setTextureCubeArrayData(texture, data) {
1832
- throw new Error("setTextureCubeArrayData not supported in WebGL2.");
1833
- }
1834
- /** Experimental */
1835
- _setTextureCubeFaceData(texture, lodData, face, depth = 0) {
1836
- if (Array.isArray(lodData) && lodData.length > 1 && this.props.mipmaps !== false) {
1837
- import_core8.log.warn(`${this.id} has mipmap and multiple LODs.`)();
2629
+ _checkNotDestroyed() {
2630
+ if (this.destroyed) {
2631
+ import_core11.log.warn(`${this} already destroyed`);
1838
2632
  }
1839
- const faceDepth = TextureCubeFaces.indexOf(face);
1840
- this._setTexture2DData(lodData, faceDepth);
1841
2633
  }
1842
- /**
1843
- * Normalize TextureData to an array of TextureImageData / ExternalImages
1844
- * @param data
1845
- * @param options
1846
- * @returns array of TextureImageData / ExternalImages
1847
- */
1848
- _normalizeTextureData(data) {
1849
- const options = this.texture;
1850
- let mipLevelArray;
1851
- if (ArrayBuffer.isView(data)) {
1852
- mipLevelArray = [
1853
- {
1854
- // ts-expect-error does data really need to be Uint8ClampedArray?
1855
- data,
1856
- width: options.width,
1857
- height: options.height
1858
- // depth: options.depth
1859
- }
1860
- ];
1861
- } else if (!Array.isArray(data)) {
1862
- mipLevelArray = [data];
1863
- } else {
1864
- mipLevelArray = data;
2634
+ _checkReady() {
2635
+ if (!this.isReady) {
2636
+ import_core11.log.warn(`${this} Cannot perform this operation before ready`);
1865
2637
  }
1866
- return mipLevelArray;
1867
2638
  }
1868
2639
  };
1869
- var AsyncTexture = _AsyncTexture;
1870
- __publicField(AsyncTexture, "defaultProps", {
1871
- ...import_core8.Texture.defaultProps,
2640
+ var DynamicTexture = _DynamicTexture;
2641
+ __publicField(DynamicTexture, "defaultProps", {
2642
+ ...import_core11.Texture.defaultProps,
2643
+ dimension: "2d",
1872
2644
  data: null,
1873
2645
  mipmaps: false
1874
2646
  });
2647
+ function getAlignedUploadData(texture, data) {
2648
+ const { width, height, data: uploadData } = data;
2649
+ const { bytesPerPixel } = texture.device.getTextureFormatInfo(texture.format);
2650
+ const bytesPerRow = width * bytesPerPixel;
2651
+ const alignedBytesPerRow = Math.ceil(bytesPerRow / texture.byteAlignment) * texture.byteAlignment;
2652
+ if (alignedBytesPerRow === bytesPerRow) {
2653
+ return uploadData;
2654
+ }
2655
+ const sourceBytes = new Uint8Array(
2656
+ uploadData.buffer,
2657
+ uploadData.byteOffset,
2658
+ uploadData.byteLength
2659
+ );
2660
+ const paddedBytes = new Uint8Array(alignedBytesPerRow * height);
2661
+ for (let row = 0; row < height; row++) {
2662
+ const sourceOffset = row * bytesPerRow;
2663
+ const destinationOffset = row * alignedBytesPerRow;
2664
+ paddedBytes.set(
2665
+ sourceBytes.subarray(sourceOffset, sourceOffset + bytesPerRow),
2666
+ destinationOffset
2667
+ );
2668
+ }
2669
+ return paddedBytes;
2670
+ }
1875
2671
  async function awaitAllPromises(x) {
1876
2672
  x = await x;
1877
2673
  if (Array.isArray(x)) {
@@ -1891,19 +2687,27 @@ var __exports__ = (() => {
1891
2687
  }
1892
2688
 
1893
2689
  // src/model/model.ts
1894
- var LOG_DRAW_PRIORITY = 2;
1895
- var LOG_DRAW_TIMEOUT = 1e4;
2690
+ var LOG_DRAW_PRIORITY2 = 2;
2691
+ var LOG_DRAW_TIMEOUT2 = 1e4;
1896
2692
  var _Model = class {
2693
+ /** Device that created this model */
1897
2694
  device;
2695
+ /** Application provided identifier */
1898
2696
  id;
2697
+ /** WGSL shader source when using unified shader */
1899
2698
  // @ts-expect-error assigned in function called from constructor
1900
2699
  source;
2700
+ /** GLSL vertex shader source */
1901
2701
  // @ts-expect-error assigned in function called from constructor
1902
2702
  vs;
2703
+ /** GLSL fragment shader source */
1903
2704
  // @ts-expect-error assigned in function called from constructor
1904
2705
  fs;
2706
+ /** Factory used to create render pipelines */
1905
2707
  pipelineFactory;
2708
+ /** Factory used to create shaders */
1906
2709
  shaderFactory;
2710
+ /** User-supplied per-model data */
1907
2711
  userData = {};
1908
2712
  // Fixed properties (change can trigger pipeline rebuild)
1909
2713
  /** The render pipeline GPU parameters, depth testing etc */
@@ -1967,7 +2771,7 @@ var __exports__ = (() => {
1967
2771
  );
1968
2772
  const shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap, { disableWarnings: this.props.disableWarnings });
1969
2773
  this.setShaderInputs(shaderInputs);
1970
- const platformInfo = getPlatformInfo(device);
2774
+ const platformInfo = getPlatformInfo2(device);
1971
2775
  const modules = (
1972
2776
  // @ts-ignore shaderInputs is assigned in setShaderInputs above.
1973
2777
  (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || []
@@ -1981,7 +2785,7 @@ var __exports__ = (() => {
1981
2785
  });
1982
2786
  this.source = source3;
1983
2787
  this._getModuleUniforms = getUniforms2;
1984
- this.props.shaderLayout ||= (0, import_shadertools2.getShaderLayoutFromWGSL)(this.source);
2788
+ this.props.shaderLayout ||= device.getShaderLayout(this.source);
1985
2789
  } else {
1986
2790
  const { vs: vs3, fs: fs3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleGLSLShaderPair({
1987
2791
  platformInfo,
@@ -2034,7 +2838,6 @@ var __exports__ = (() => {
2034
2838
  if (props.transformFeedback) {
2035
2839
  this.transformFeedback = props.transformFeedback;
2036
2840
  }
2037
- Object.seal(this);
2038
2841
  }
2039
2842
  destroy() {
2040
2843
  if (!this._destroyed) {
@@ -2062,14 +2865,20 @@ var __exports__ = (() => {
2062
2865
  setNeedsRedraw(reason) {
2063
2866
  this._needsRedraw ||= reason;
2064
2867
  }
2868
+ /** Update uniforms and pipeline state prior to drawing. */
2065
2869
  predraw() {
2066
2870
  this.updateShaderInputs();
2067
2871
  this.pipeline = this._updatePipeline();
2068
2872
  }
2873
+ /**
2874
+ * Issue one draw call.
2875
+ * @param renderPass - render pass to draw into
2876
+ * @returns `true` if the draw call was executed, `false` if resources were not ready.
2877
+ */
2069
2878
  draw(renderPass) {
2070
2879
  const loadingBinding = this._areBindingsLoading();
2071
2880
  if (loadingBinding) {
2072
- import_core9.log.info(LOG_DRAW_PRIORITY, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
2881
+ import_core12.log.info(LOG_DRAW_PRIORITY2, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
2073
2882
  return false;
2074
2883
  }
2075
2884
  try {
@@ -2199,7 +3008,7 @@ var __exports__ = (() => {
2199
3008
  /** Set the shader inputs */
2200
3009
  setShaderInputs(shaderInputs) {
2201
3010
  this.shaderInputs = shaderInputs;
2202
- this._uniformStore = new import_core9.UniformStore(this.shaderInputs.modules);
3011
+ this._uniformStore = new import_core12.UniformStore(this.shaderInputs.modules);
2203
3012
  for (const [moduleName, module] of Object.entries(this.shaderInputs.modules)) {
2204
3013
  if (shaderModuleHasUniforms(module)) {
2205
3014
  const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
@@ -2243,7 +3052,7 @@ var __exports__ = (() => {
2243
3052
  setAttributes(buffers, options) {
2244
3053
  const disableWarnings = options?.disableWarnings ?? this.props.disableWarnings;
2245
3054
  if (buffers["indices"]) {
2246
- import_core9.log.warn(
3055
+ import_core12.log.warn(
2247
3056
  `Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`
2248
3057
  )();
2249
3058
  }
@@ -2256,7 +3065,7 @@ var __exports__ = (() => {
2256
3065
  const bufferLayout = bufferLayoutHelper.getBufferLayout(bufferName);
2257
3066
  if (!bufferLayout) {
2258
3067
  if (!disableWarnings) {
2259
- import_core9.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
3068
+ import_core12.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
2260
3069
  }
2261
3070
  continue;
2262
3071
  }
@@ -2271,7 +3080,7 @@ var __exports__ = (() => {
2271
3080
  }
2272
3081
  }
2273
3082
  if (!set && !disableWarnings) {
2274
- import_core9.log.warn(
3083
+ import_core12.log.warn(
2275
3084
  `Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`
2276
3085
  )();
2277
3086
  }
@@ -2292,7 +3101,7 @@ var __exports__ = (() => {
2292
3101
  if (attributeInfo) {
2293
3102
  this.vertexArray.setConstantWebGL(attributeInfo.location, value);
2294
3103
  } else if (!(options?.disableWarnings ?? this.props.disableWarnings)) {
2295
- import_core9.log.warn(
3104
+ import_core12.log.warn(
2296
3105
  `Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`
2297
3106
  )();
2298
3107
  }
@@ -2303,7 +3112,7 @@ var __exports__ = (() => {
2303
3112
  /** Check that bindings are loaded. Returns id of first binding that is still loading. */
2304
3113
  _areBindingsLoading() {
2305
3114
  for (const binding of Object.values(this.bindings)) {
2306
- if (binding instanceof AsyncTexture && !binding.isReady) {
3115
+ if (binding instanceof DynamicTexture && !binding.isReady) {
2307
3116
  return binding.id;
2308
3117
  }
2309
3118
  }
@@ -2313,7 +3122,7 @@ var __exports__ = (() => {
2313
3122
  _getBindings() {
2314
3123
  const validBindings = {};
2315
3124
  for (const [name, binding] of Object.entries(this.bindings)) {
2316
- if (binding instanceof AsyncTexture) {
3125
+ if (binding instanceof DynamicTexture) {
2317
3126
  if (binding.isReady) {
2318
3127
  validBindings[name] = binding.texture;
2319
3128
  }
@@ -2327,16 +3136,16 @@ var __exports__ = (() => {
2327
3136
  _getBindingsUpdateTimestamp() {
2328
3137
  let timestamp = 0;
2329
3138
  for (const binding of Object.values(this.bindings)) {
2330
- if (binding instanceof import_core9.TextureView) {
3139
+ if (binding instanceof import_core12.TextureView) {
2331
3140
  timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
2332
- } else if (binding instanceof import_core9.Buffer || binding instanceof import_core9.Texture) {
3141
+ } else if (binding instanceof import_core12.Buffer || binding instanceof import_core12.Texture) {
2333
3142
  timestamp = Math.max(timestamp, binding.updateTimestamp);
2334
- } else if (binding instanceof AsyncTexture) {
3143
+ } else if (binding instanceof DynamicTexture) {
2335
3144
  timestamp = binding.texture ? Math.max(timestamp, binding.texture.updateTimestamp) : (
2336
3145
  // The texture will become available in the future
2337
3146
  Infinity
2338
3147
  );
2339
- } else if (!(binding instanceof import_core9.Sampler)) {
3148
+ } else if (!(binding instanceof import_core12.Sampler)) {
2340
3149
  timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
2341
3150
  }
2342
3151
  }
@@ -2371,7 +3180,7 @@ var __exports__ = (() => {
2371
3180
  let prevShaderVs = null;
2372
3181
  let prevShaderFs = null;
2373
3182
  if (this.pipeline) {
2374
- import_core9.log.log(
3183
+ import_core12.log.log(
2375
3184
  1,
2376
3185
  `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
2377
3186
  )();
@@ -2407,7 +3216,7 @@ var __exports__ = (() => {
2407
3216
  vs: vs3,
2408
3217
  fs: fs3
2409
3218
  });
2410
- this._attributeInfos = (0, import_core9.getAttributeInfosFromLayouts)(
3219
+ this._attributeInfos = (0, import_core12.getAttributeInfosFromLayouts)(
2411
3220
  this.pipeline.shaderLayout,
2412
3221
  this.bufferLayout
2413
3222
  );
@@ -2422,24 +3231,24 @@ var __exports__ = (() => {
2422
3231
  _lastLogTime = 0;
2423
3232
  _logOpen = false;
2424
3233
  _logDrawCallStart() {
2425
- const logDrawTimeout = import_core9.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
2426
- if (import_core9.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
3234
+ const logDrawTimeout = import_core12.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT2;
3235
+ if (import_core12.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
2427
3236
  return;
2428
3237
  }
2429
3238
  this._lastLogTime = Date.now();
2430
3239
  this._logOpen = true;
2431
- import_core9.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core9.log.level <= 2 })();
3240
+ import_core12.log.group(LOG_DRAW_PRIORITY2, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core12.log.level <= 2 })();
2432
3241
  }
2433
3242
  _logDrawCallEnd() {
2434
3243
  if (this._logOpen) {
2435
3244
  const shaderLayoutTable = getDebugTableForShaderLayout(this.pipeline.shaderLayout, this.id);
2436
- import_core9.log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
3245
+ import_core12.log.table(LOG_DRAW_PRIORITY2, shaderLayoutTable)();
2437
3246
  const uniformTable = this.shaderInputs.getDebugTable();
2438
- import_core9.log.table(LOG_DRAW_PRIORITY, uniformTable)();
3247
+ import_core12.log.table(LOG_DRAW_PRIORITY2, uniformTable)();
2439
3248
  const attributeTable = this._getAttributeDebugTable();
2440
- import_core9.log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
2441
- import_core9.log.table(LOG_DRAW_PRIORITY, attributeTable)();
2442
- import_core9.log.groupEnd(LOG_DRAW_PRIORITY)();
3249
+ import_core12.log.table(LOG_DRAW_PRIORITY2, this._attributeInfos)();
3250
+ import_core12.log.table(LOG_DRAW_PRIORITY2, attributeTable)();
3251
+ import_core12.log.groupEnd(LOG_DRAW_PRIORITY2)();
2443
3252
  this._logOpen = false;
2444
3253
  }
2445
3254
  }
@@ -2478,14 +3287,14 @@ var __exports__ = (() => {
2478
3287
  }
2479
3288
  // TODO - fix typing of luma data types
2480
3289
  _getBufferOrConstantValues(attribute, dataType) {
2481
- const TypedArrayConstructor = (0, import_core9.getTypedArrayConstructor)(dataType);
2482
- const typedArray = attribute instanceof import_core9.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
3290
+ const TypedArrayConstructor = (0, import_core12.getTypedArrayConstructor)(dataType);
3291
+ const typedArray = attribute instanceof import_core12.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
2483
3292
  return typedArray.toString();
2484
3293
  }
2485
3294
  };
2486
3295
  var Model = _Model;
2487
3296
  __publicField(Model, "defaultProps", {
2488
- ...import_core9.RenderPipeline.defaultProps,
3297
+ ...import_core12.RenderPipeline.defaultProps,
2489
3298
  source: void 0,
2490
3299
  vs: null,
2491
3300
  fs: null,
@@ -2506,14 +3315,14 @@ var __exports__ = (() => {
2506
3315
  pipelineFactory: void 0,
2507
3316
  shaderFactory: void 0,
2508
3317
  transformFeedback: void 0,
2509
- shaderAssembler: import_shadertools2.ShaderAssembler.getDefaultShaderAssembler(),
3318
+ shaderAssembler: import_shadertools3.ShaderAssembler.getDefaultShaderAssembler(),
2510
3319
  debugShaders: void 0,
2511
3320
  disableWarnings: void 0
2512
3321
  });
2513
3322
  function shaderModuleHasUniforms(module) {
2514
3323
  return Boolean(module.uniformTypes && !isObjectEmpty(module.uniformTypes));
2515
3324
  }
2516
- function getPlatformInfo(device) {
3325
+ function getPlatformInfo2(device) {
2517
3326
  return {
2518
3327
  type: device.type,
2519
3328
  shaderLanguage: device.info.shadingLanguage,
@@ -2531,8 +3340,8 @@ var __exports__ = (() => {
2531
3340
  }
2532
3341
 
2533
3342
  // src/compute/buffer-transform.ts
2534
- var import_core10 = __toESM(require_core(), 1);
2535
- var import_shadertools3 = __toESM(require_shadertools(), 1);
3343
+ var import_core13 = __toESM(require_core(), 1);
3344
+ var import_shadertools4 = __toESM(require_shadertools(), 1);
2536
3345
  var _BufferTransform = class {
2537
3346
  device;
2538
3347
  model;
@@ -2547,7 +3356,7 @@ var __exports__ = (() => {
2547
3356
  this.device = device;
2548
3357
  this.model = new Model(this.device, {
2549
3358
  id: props.id || "buffer-transform-model",
2550
- fs: props.fs || (0, import_shadertools3.getPassthroughFS)(),
3359
+ fs: props.fs || (0, import_shadertools4.getPassthroughFS)(),
2551
3360
  topology: props.topology || "point-list",
2552
3361
  varyings: props.outputs || props.varyings,
2553
3362
  ...props
@@ -2593,7 +3402,7 @@ var __exports__ = (() => {
2593
3402
  if (!result) {
2594
3403
  throw new Error("BufferTransform#getBuffer");
2595
3404
  }
2596
- if (result instanceof import_core10.Buffer) {
3405
+ if (result instanceof import_core13.Buffer) {
2597
3406
  return result.readAsync();
2598
3407
  }
2599
3408
  const { buffer, byteOffset = 0, byteLength = buffer.byteLength } = result;
@@ -2608,7 +3417,7 @@ var __exports__ = (() => {
2608
3417
  });
2609
3418
 
2610
3419
  // src/compute/texture-transform.ts
2611
- var import_shadertools4 = __toESM(require_shadertools(), 1);
3420
+ var import_shadertools5 = __toESM(require_shadertools(), 1);
2612
3421
  var FS_OUTPUT_VARIABLE = "transform_output";
2613
3422
  var TextureTransform = class {
2614
3423
  device;
@@ -2631,7 +3440,7 @@ var __exports__ = (() => {
2631
3440
  });
2632
3441
  this.model = new Model(this.device, {
2633
3442
  id: props.id || uid("texture-transform-model"),
2634
- fs: props.fs || (0, import_shadertools4.getPassthroughFS)({
3443
+ fs: props.fs || (0, import_shadertools5.getPassthroughFS)({
2635
3444
  input: props.targetTextureVarying,
2636
3445
  inputChannels: props.targetTextureChannels,
2637
3446
  output: FS_OUTPUT_VARIABLE
@@ -2871,22 +3680,31 @@ ${props.source}` };
2871
3680
  };
2872
3681
 
2873
3682
  // src/models/billboard-texture-model.ts
3683
+ var backgroundModule = {
3684
+ name: "background",
3685
+ uniformTypes: {
3686
+ scale: "vec2<f32>"
3687
+ }
3688
+ };
2874
3689
  var BACKGROUND_FS_WGSL = (
2875
3690
  /* wgsl */
2876
3691
  `@group(0) @binding(0) var backgroundTexture: texture_2d<f32>;
2877
3692
  @group(0) @binding(1) var backgroundTextureSampler: sampler;
3693
+ struct backgroundUniforms {
3694
+ scale: vec2<f32>,
3695
+ };
3696
+ @group(0) @binding(2) var<uniform> background: backgroundUniforms;
2878
3697
 
2879
3698
  fn billboardTexture_getTextureUV(coordinates: vec2<f32>) -> vec2<f32> {
2880
- let iTexSize: vec2<u32> = textureDimensions(backgroundTexture, 0);
2881
- let texSize: vec2<f32> = vec2<f32>(f32(iTexSize.x), f32(iTexSize.y));
2882
- var position: vec2<f32> = coordinates.xy / texSize;
2883
- return position;
2884
- }
3699
+ let scale: vec2<f32> = background.scale;
3700
+ var position: vec2<f32> = (coordinates - vec2<f32>(0.5, 0.5)) / scale + vec2<f32>(0.5, 0.5);
3701
+ return position;
3702
+ }
2885
3703
 
2886
3704
  @fragment
2887
3705
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
2888
- let position: vec2<f32> = billboardTexture_getTextureUV(inputs.coordinate);
2889
- return textureSample(backgroundTexture, backgroundTextureSampler, position);
3706
+ let position: vec2<f32> = billboardTexture_getTextureUV(inputs.coordinate);
3707
+ return textureSample(backgroundTexture, backgroundTextureSampler, position);
2890
3708
  }
2891
3709
  `
2892
3710
  );
@@ -2896,27 +3714,33 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
2896
3714
  precision highp float;
2897
3715
 
2898
3716
  uniform sampler2D backgroundTexture;
3717
+
3718
+ uniform backgroundUniforms {
3719
+ vec2 scale;
3720
+ } background;
3721
+
3722
+ in vec2 coordinate;
2899
3723
  out vec4 fragColor;
2900
3724
 
2901
- vec2 billboardTexture_getTextureUV() {
2902
- ivec2 iTexSize = textureSize(backgroundTexture, 0);
2903
- vec2 texSize = vec2(float(iTexSize.x), float(iTexSize.y));
2904
- vec2 position = gl_FragCoord.xy / texSize;
3725
+ vec2 billboardTexture_getTextureUV(vec2 coord) {
3726
+ vec2 position = (coord - 0.5) / background.scale + 0.5;
2905
3727
  return position;
2906
3728
  }
2907
3729
 
2908
3730
  void main(void) {
2909
- vec2 position = billboardTexture_getTextureUV();
3731
+ vec2 position = billboardTexture_getTextureUV(coordinate);
2910
3732
  fragColor = texture(backgroundTexture, position);
2911
3733
  }
2912
3734
  `
2913
3735
  );
2914
3736
  var BackgroundTextureModel = class extends ClipSpace {
3737
+ backgroundTexture = null;
2915
3738
  constructor(device, props) {
2916
3739
  super(device, {
2917
3740
  id: props.id || "background-texture-model",
2918
3741
  source: BACKGROUND_FS_WGSL,
2919
3742
  fs: BACKGROUND_FS,
3743
+ modules: [backgroundModule],
2920
3744
  parameters: {
2921
3745
  depthWriteEnabled: false,
2922
3746
  ...props.blend ? {
@@ -2933,17 +3757,47 @@ void main(void) {
2933
3757
  if (!props.backgroundTexture) {
2934
3758
  throw new Error("BackgroundTextureModel requires a backgroundTexture prop");
2935
3759
  }
2936
- this.setTexture(props.backgroundTexture);
3760
+ this.setProps(props);
2937
3761
  }
2938
- setTexture(backgroundTexture) {
2939
- this.setBindings({
2940
- backgroundTexture
2941
- });
3762
+ /** Update the background texture */
3763
+ setProps(props) {
3764
+ const { backgroundTexture } = props;
3765
+ if (backgroundTexture) {
3766
+ this.setBindings({ backgroundTexture });
3767
+ if (backgroundTexture.isReady) {
3768
+ const texture = backgroundTexture instanceof DynamicTexture ? backgroundTexture.texture : backgroundTexture;
3769
+ this.backgroundTexture = texture;
3770
+ this.updateScale(texture);
3771
+ } else {
3772
+ backgroundTexture.ready.then((texture) => {
3773
+ this.backgroundTexture = texture;
3774
+ this.updateScale(texture);
3775
+ });
3776
+ }
3777
+ }
2942
3778
  }
2943
3779
  predraw() {
2944
- this.shaderInputs.setProps({});
2945
3780
  super.predraw();
2946
3781
  }
3782
+ updateScale(texture) {
3783
+ if (!texture) {
3784
+ this.shaderInputs.setProps({ background: { scale: [1, 1] } });
3785
+ return;
3786
+ }
3787
+ const [screenWidth, screenHeight] = this.device.getCanvasContext().getDrawingBufferSize();
3788
+ const textureWidth = texture.width;
3789
+ const textureHeight = texture.height;
3790
+ const screenAspect = screenWidth / screenHeight;
3791
+ const textureAspect = textureWidth / textureHeight;
3792
+ let scaleX = 1;
3793
+ let scaleY = 1;
3794
+ if (screenAspect > textureAspect) {
3795
+ scaleY = screenAspect / textureAspect;
3796
+ } else {
3797
+ scaleX = textureAspect / screenAspect;
3798
+ }
3799
+ this.shaderInputs.setProps({ background: { scale: [scaleX, scaleY] } });
3800
+ }
2947
3801
  };
2948
3802
 
2949
3803
  // ../../node_modules/@math.gl/core/dist/lib/common.js
@@ -5125,13 +5979,13 @@ void main(void) {
5125
5979
  };
5126
5980
 
5127
5981
  // src/scenegraph/group-node.ts
5128
- var import_core13 = __toESM(require_core(), 1);
5982
+ var import_core16 = __toESM(require_core(), 1);
5129
5983
  var GroupNode = class extends ScenegraphNode {
5130
5984
  children;
5131
5985
  constructor(props = {}) {
5132
5986
  props = Array.isArray(props) ? { children: props } : props;
5133
5987
  const { children = [] } = props;
5134
- import_core13.log.assert(
5988
+ import_core16.log.assert(
5135
5989
  children.every((child) => child instanceof ScenegraphNode),
5136
5990
  "every child must an instance of ScenegraphNode"
5137
5991
  );
@@ -6345,17 +7199,45 @@ void main(void) {
6345
7199
  return n - Math.floor(n);
6346
7200
  }
6347
7201
 
7202
+ // src/application-utils/load-file.ts
7203
+ var pathPrefix = "";
7204
+ function setPathPrefix(prefix) {
7205
+ pathPrefix = prefix;
7206
+ }
7207
+ async function loadImageBitmap(url, opts) {
7208
+ const image = new Image();
7209
+ image.crossOrigin = opts?.crossOrigin || "anonymous";
7210
+ image.src = url.startsWith("http") ? url : pathPrefix + url;
7211
+ await image.decode();
7212
+ return opts ? await createImageBitmap(image, opts) : await createImageBitmap(image);
7213
+ }
7214
+ async function loadImage(url, opts) {
7215
+ return await new Promise((resolve, reject) => {
7216
+ try {
7217
+ const image = new Image();
7218
+ image.onload = () => resolve(image);
7219
+ image.onerror = () => reject(new Error(`Could not load image ${url}.`));
7220
+ image.crossOrigin = opts?.crossOrigin || "anonymous";
7221
+ image.src = url.startsWith("http") ? url : pathPrefix + url;
7222
+ } catch (error) {
7223
+ reject(error);
7224
+ }
7225
+ });
7226
+ }
7227
+
6348
7228
  // src/passes/shader-pass-renderer.ts
6349
- var import_shadertools5 = __toESM(require_shadertools(), 1);
7229
+ var import_shadertools6 = __toESM(require_shadertools(), 1);
6350
7230
 
6351
7231
  // src/compute/swap.ts
6352
- var import_core15 = __toESM(require_core(), 1);
7232
+ var import_core18 = __toESM(require_core(), 1);
6353
7233
  var Swap = class {
7234
+ id;
6354
7235
  /** The current resource - usually the source for renders or computations */
6355
7236
  current;
6356
7237
  /** The next resource - usually the target/destination for transforms / computations */
6357
7238
  next;
6358
7239
  constructor(props) {
7240
+ this.id = props.id || "swap";
6359
7241
  this.current = props.current;
6360
7242
  this.next = props.next;
6361
7243
  }
@@ -6374,21 +7256,24 @@ void main(void) {
6374
7256
  var SwapFramebuffers = class extends Swap {
6375
7257
  constructor(device, props) {
6376
7258
  props = { ...props };
7259
+ const { width = 1, height = 1 } = props;
6377
7260
  let colorAttachments = props.colorAttachments?.map(
6378
7261
  (colorAttachment) => typeof colorAttachment !== "string" ? colorAttachment : device.createTexture({
7262
+ id: `${props.id}-texture-0`,
6379
7263
  format: colorAttachment,
6380
- usage: import_core15.Texture.SAMPLE | import_core15.Texture.RENDER | import_core15.Texture.COPY_SRC | import_core15.Texture.COPY_DST,
6381
- width: 1,
6382
- height: 1
7264
+ usage: import_core18.Texture.SAMPLE | import_core18.Texture.RENDER | import_core18.Texture.COPY_SRC | import_core18.Texture.COPY_DST,
7265
+ width,
7266
+ height
6383
7267
  })
6384
7268
  );
6385
7269
  const current = device.createFramebuffer({ ...props, colorAttachments });
6386
7270
  colorAttachments = props.colorAttachments?.map(
6387
7271
  (colorAttachment) => typeof colorAttachment !== "string" ? colorAttachment : device.createTexture({
7272
+ id: `${props.id}-texture-1`,
6388
7273
  format: colorAttachment,
6389
- usage: import_core15.Texture.TEXTURE | import_core15.Texture.COPY_SRC | import_core15.Texture.COPY_DST | import_core15.Texture.RENDER_ATTACHMENT,
6390
- width: 1,
6391
- height: 1
7274
+ usage: import_core18.Texture.SAMPLE | import_core18.Texture.RENDER | import_core18.Texture.COPY_SRC | import_core18.Texture.COPY_DST,
7275
+ width,
7276
+ height
6392
7277
  })
6393
7278
  );
6394
7279
  const next = device.createFramebuffer({ ...props, colorAttachments });
@@ -6453,19 +7338,23 @@ void main(void) {
6453
7338
  `// Binding 0:1 is reserved for shader passes
6454
7339
  // @group(0) @binding(0) var<uniform> brightnessContrast : brightnessContrastUniforms;
6455
7340
  @group(0) @binding(1) var texture: texture_2d<f32>;
6456
- @group(0) @binding(2) var sampler: sampler;
7341
+ @group(0) @binding(2) var textureSampler: sampler;
6457
7342
 
6458
- struct FragmentInputs {
6459
- @location(0) fragUV: vec2f,
6460
- @location(1) fragPosition: vec4f,
6461
- @location(2) fragCoordinate: vec4f
6462
- };
7343
+ // This needs to be aligned with
7344
+ // struct FragmentInputs {
7345
+ // @location(0) fragUV: vec2f,
7346
+ // @location(1) fragPosition: vec4f,
7347
+ // @location(2) fragCoordinate: vec4f
7348
+ // };
6463
7349
 
6464
7350
  @fragment
6465
7351
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
6466
- let texSize = textureDimensions(texture, 0);
6467
- var fragColor = textureSample(texture, sampler, fragUV);
6468
- fragColor = ${func}(gl_FragColor, texSize, texCoord);
7352
+ let fragUV = inputs.uv;
7353
+ let fragCoordinate = inputs.coordinate;
7354
+ let texSize = vec2f(textureDimensions(texture, 0));
7355
+
7356
+ var fragColor = textureSample(texture, textureSampler, fragUV);
7357
+ fragColor = ${func}(fragColor, texSize, fragCoordinate);
6469
7358
  return fragColor;
6470
7359
  }
6471
7360
  `
@@ -6487,9 +7376,9 @@ struct FragmentInputs = {
6487
7376
 
6488
7377
  @fragment
6489
7378
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
6490
- let texSize = textureDimensions(texture, 0);
7379
+ let texSize = vec2f(textureDimensions(texture, 0));
6491
7380
  var fragColor = textureSample(texture, sampler, fragUV);
6492
- fragColor = ${func}(gl_FragColor, texSize, texCoord);
7381
+ fragColor = ${func}(fragColor, texSize, texCoord);
6493
7382
  return fragColor;
6494
7383
  }
6495
7384
  `
@@ -6554,7 +7443,7 @@ void main() {
6554
7443
  textureModel;
6555
7444
  constructor(device, props) {
6556
7445
  this.device = device;
6557
- props.shaderPasses.map((shaderPass) => (0, import_shadertools5.initializeShaderModule)(shaderPass));
7446
+ props.shaderPasses.map((shaderPass) => (0, import_shadertools6.initializeShaderModule)(shaderPass));
6558
7447
  const modules = props.shaderPasses.reduce(
6559
7448
  (object, shaderPass) => ({ ...object, [shaderPass.name]: shaderPass }),
6560
7449
  {}
@@ -6588,12 +7477,10 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
6588
7477
 
6589
7478
  uniform sampler2D sourceTexture;
6590
7479
  in vec2 uv;
6591
- in vec2 coordinate;
6592
7480
  out vec4 fragColor;
6593
7481
 
6594
7482
  void main() {
6595
- vec2 texCoord = coordinate;
6596
- fragColor = texture(sourceTexture, coordinate);
7483
+ fragColor = texture(sourceTexture, uv);
6597
7484
  }
6598
7485
  `
6599
7486
  )
@@ -6607,9 +7494,11 @@ void main() {
6607
7494
  }
6608
7495
  this.swapFramebuffers.destroy();
6609
7496
  this.clipSpace.destroy();
7497
+ this.textureModel.destroy();
6610
7498
  }
6611
- resize(width, height) {
6612
- this.swapFramebuffers.resize({ width, height });
7499
+ resize(size) {
7500
+ size ||= this.device.getCanvasContext().getDrawingBufferSize();
7501
+ this.swapFramebuffers.resize({ width: size[0], height: size[1] });
6613
7502
  }
6614
7503
  renderToScreen(options) {
6615
7504
  const outputTexture = this.renderToTexture(options);
@@ -6620,7 +7509,7 @@ void main() {
6620
7509
  const renderPass = this.device.beginRenderPass({
6621
7510
  id: "shader-pass-renderer-to-screen",
6622
7511
  framebuffer,
6623
- clearColor: [0, 0, 0, 1],
7512
+ // clearColor: [1, 1, 0, 1],
6624
7513
  clearDepth: 1
6625
7514
  });
6626
7515
  this.clipSpace.setBindings({ sourceTexture: outputTexture });
@@ -6636,14 +7525,14 @@ void main() {
6636
7525
  if (!sourceTexture.isReady) {
6637
7526
  return null;
6638
7527
  }
6639
- this.textureModel.destroy();
6640
- this.textureModel = new BackgroundTextureModel(this.device, {
6641
- backgroundTexture: sourceTexture
6642
- });
7528
+ if (this.passRenderers.length === 0) {
7529
+ return sourceTexture.texture;
7530
+ }
7531
+ this.textureModel.setProps({ backgroundTexture: sourceTexture });
6643
7532
  const clearTexturePass = this.device.beginRenderPass({
6644
7533
  id: "shader-pass-renderer-clear-texture",
6645
7534
  framebuffer: this.swapFramebuffers.current,
6646
- clearColor: [0, 0, 0, 1]
7535
+ clearColor: [1, 0, 0, 1]
6647
7536
  });
6648
7537
  this.textureModel.draw(clearTexturePass);
6649
7538
  clearTexturePass.end();
@@ -6729,221 +7618,6 @@ void main() {
6729
7618
  }
6730
7619
  };
6731
7620
 
6732
- // src/compute/computation.ts
6733
- var import_core16 = __toESM(require_core(), 1);
6734
- var import_shadertools6 = __toESM(require_shadertools(), 1);
6735
- var LOG_DRAW_PRIORITY2 = 2;
6736
- var LOG_DRAW_TIMEOUT2 = 1e4;
6737
- var _Computation = class {
6738
- device;
6739
- id;
6740
- pipelineFactory;
6741
- shaderFactory;
6742
- userData = {};
6743
- /** Bindings (textures, samplers, uniform buffers) */
6744
- bindings = {};
6745
- /** The underlying GPU pipeline. */
6746
- pipeline;
6747
- /** Assembled compute shader source */
6748
- source;
6749
- /** the underlying compiled compute shader */
6750
- // @ts-ignore Set in function called from constructor
6751
- shader;
6752
- /** ShaderInputs instance */
6753
- shaderInputs;
6754
- // @ts-ignore Set in function called from constructor
6755
- _uniformStore;
6756
- _pipelineNeedsUpdate = "newly created";
6757
- _getModuleUniforms;
6758
- props;
6759
- _destroyed = false;
6760
- constructor(device, props) {
6761
- if (device.type !== "webgpu") {
6762
- throw new Error("Computation is only supported in WebGPU");
6763
- }
6764
- this.props = { ..._Computation.defaultProps, ...props };
6765
- props = this.props;
6766
- this.id = props.id || uid("model");
6767
- this.device = device;
6768
- Object.assign(this.userData, props.userData);
6769
- const moduleMap = Object.fromEntries(
6770
- this.props.modules?.map((module) => [module.name, module]) || []
6771
- );
6772
- this.shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap);
6773
- this.setShaderInputs(this.shaderInputs);
6774
- this.props.shaderLayout ||= (0, import_shadertools6.getShaderLayoutFromWGSL)(this.props.source);
6775
- const platformInfo = getPlatformInfo2(device);
6776
- const modules = (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
6777
- this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
6778
- this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
6779
- const { source: source3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleWGSLShader({
6780
- platformInfo,
6781
- ...this.props,
6782
- modules
6783
- });
6784
- this.source = source3;
6785
- this._getModuleUniforms = getUniforms2;
6786
- this.pipeline = this._updatePipeline();
6787
- if (props.bindings) {
6788
- this.setBindings(props.bindings);
6789
- }
6790
- Object.seal(this);
6791
- }
6792
- destroy() {
6793
- if (this._destroyed)
6794
- return;
6795
- this.pipelineFactory.release(this.pipeline);
6796
- this.shaderFactory.release(this.shader);
6797
- this._uniformStore.destroy();
6798
- this._destroyed = true;
6799
- }
6800
- // Draw call
6801
- predraw() {
6802
- this.updateShaderInputs();
6803
- }
6804
- dispatch(computePass, x, y, z) {
6805
- try {
6806
- this._logDrawCallStart();
6807
- this.pipeline = this._updatePipeline();
6808
- this.pipeline.setBindings(this.bindings);
6809
- computePass.setPipeline(this.pipeline);
6810
- computePass.setBindings([]);
6811
- computePass.dispatch(x, y, z);
6812
- } finally {
6813
- this._logDrawCallEnd();
6814
- }
6815
- }
6816
- // Update fixed fields (can trigger pipeline rebuild)
6817
- // Update dynamic fields
6818
- /**
6819
- * Updates the vertex count (used in draw calls)
6820
- * @note Any attributes with stepMode=vertex need to be at least this big
6821
- */
6822
- setVertexCount(vertexCount) {
6823
- }
6824
- /**
6825
- * Updates the instance count (used in draw calls)
6826
- * @note Any attributes with stepMode=instance need to be at least this big
6827
- */
6828
- setInstanceCount(instanceCount) {
6829
- }
6830
- setShaderInputs(shaderInputs) {
6831
- this.shaderInputs = shaderInputs;
6832
- this._uniformStore = new import_core16.UniformStore(this.shaderInputs.modules);
6833
- for (const moduleName of Object.keys(this.shaderInputs.modules)) {
6834
- const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
6835
- this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
6836
- }
6837
- }
6838
- /**
6839
- * Updates shader module settings (which results in uniforms being set)
6840
- */
6841
- setShaderModuleProps(props) {
6842
- const uniforms = this._getModuleUniforms(props);
6843
- const keys = Object.keys(uniforms).filter((k) => {
6844
- const uniform = uniforms[k];
6845
- return !isNumericArray(uniform) && typeof uniform !== "number" && typeof uniform !== "boolean";
6846
- });
6847
- const bindings = {};
6848
- for (const k of keys) {
6849
- bindings[k] = uniforms[k];
6850
- delete uniforms[k];
6851
- }
6852
- }
6853
- updateShaderInputs() {
6854
- this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
6855
- }
6856
- /**
6857
- * Sets bindings (textures, samplers, uniform buffers)
6858
- */
6859
- setBindings(bindings) {
6860
- Object.assign(this.bindings, bindings);
6861
- }
6862
- _setPipelineNeedsUpdate(reason) {
6863
- this._pipelineNeedsUpdate = this._pipelineNeedsUpdate || reason;
6864
- }
6865
- _updatePipeline() {
6866
- if (this._pipelineNeedsUpdate) {
6867
- let prevShader = null;
6868
- if (this.pipeline) {
6869
- import_core16.log.log(
6870
- 1,
6871
- `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
6872
- )();
6873
- prevShader = this.shader;
6874
- }
6875
- this._pipelineNeedsUpdate = false;
6876
- this.shader = this.shaderFactory.createShader({
6877
- id: `${this.id}-fragment`,
6878
- stage: "compute",
6879
- source: this.source,
6880
- debugShaders: this.props.debugShaders
6881
- });
6882
- this.pipeline = this.pipelineFactory.createComputePipeline({
6883
- ...this.props,
6884
- shader: this.shader
6885
- });
6886
- if (prevShader) {
6887
- this.shaderFactory.release(prevShader);
6888
- }
6889
- }
6890
- return this.pipeline;
6891
- }
6892
- /** Throttle draw call logging */
6893
- _lastLogTime = 0;
6894
- _logOpen = false;
6895
- _logDrawCallStart() {
6896
- const logDrawTimeout = import_core16.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT2;
6897
- if (import_core16.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
6898
- return;
6899
- }
6900
- this._lastLogTime = Date.now();
6901
- this._logOpen = true;
6902
- import_core16.log.group(LOG_DRAW_PRIORITY2, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core16.log.level <= 2 })();
6903
- }
6904
- _logDrawCallEnd() {
6905
- if (this._logOpen) {
6906
- const uniformTable = this.shaderInputs.getDebugTable();
6907
- import_core16.log.table(LOG_DRAW_PRIORITY2, uniformTable)();
6908
- import_core16.log.groupEnd(LOG_DRAW_PRIORITY2)();
6909
- this._logOpen = false;
6910
- }
6911
- }
6912
- _drawCount = 0;
6913
- // TODO - fix typing of luma data types
6914
- _getBufferOrConstantValues(attribute, dataType) {
6915
- const TypedArrayConstructor = (0, import_core16.getTypedArrayConstructor)(dataType);
6916
- const typedArray = attribute instanceof import_core16.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
6917
- return typedArray.toString();
6918
- }
6919
- };
6920
- var Computation = _Computation;
6921
- __publicField(Computation, "defaultProps", {
6922
- ...import_core16.ComputePipeline.defaultProps,
6923
- id: "unnamed",
6924
- handle: void 0,
6925
- userData: {},
6926
- source: "",
6927
- modules: [],
6928
- defines: {},
6929
- bindings: void 0,
6930
- shaderInputs: void 0,
6931
- pipelineFactory: void 0,
6932
- shaderFactory: void 0,
6933
- shaderAssembler: import_shadertools6.ShaderAssembler.getDefaultShaderAssembler(),
6934
- debugShaders: void 0
6935
- });
6936
- function getPlatformInfo2(device) {
6937
- return {
6938
- type: device.type,
6939
- shaderLanguage: device.info.shadingLanguage,
6940
- shaderLanguageVersion: device.info.shadingLanguageVersion,
6941
- gpu: device.info.gpu,
6942
- // HACK - we pretend that the DeviceFeatures is a Set, it has a similar API
6943
- features: device.features
6944
- };
6945
- }
6946
-
6947
7621
  // src/modules/picking/picking-uniforms.ts
6948
7622
  var DEFAULT_HIGHLIGHT_COLOR = [0, 1, 1, 1];
6949
7623
  var INVALID_INDEX = -1;
@@ -7480,6 +8154,9 @@ vec4 picking_filterColor(vec4 color) {
7480
8154
  return [pickX, pickY];
7481
8155
  }
7482
8156
  };
8157
+
8158
+ // src/index.ts
8159
+ var AsyncTexture = DynamicTexture;
7483
8160
  return __toCommonJS(bundle_exports);
7484
8161
  })();
7485
8162
  return __exports__;