@luma.gl/engine 9.3.0-alpha.4 → 9.3.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/animation-loop/animation-loop.d.ts +8 -4
  2. package/dist/animation-loop/animation-loop.d.ts.map +1 -1
  3. package/dist/animation-loop/animation-loop.js +70 -43
  4. package/dist/animation-loop/animation-loop.js.map +1 -1
  5. package/dist/animation-loop/make-animation-loop.js +7 -1
  6. package/dist/animation-loop/make-animation-loop.js.map +1 -1
  7. package/dist/animation-loop/request-animation-frame.d.ts.map +1 -1
  8. package/dist/animation-loop/request-animation-frame.js +23 -6
  9. package/dist/animation-loop/request-animation-frame.js.map +1 -1
  10. package/dist/dist.dev.js +679 -927
  11. package/dist/dist.min.js +39 -240
  12. package/dist/dynamic-texture/dynamic-texture.d.ts +3 -3
  13. package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -1
  14. package/dist/dynamic-texture/dynamic-texture.js +170 -52
  15. package/dist/dynamic-texture/dynamic-texture.js.map +1 -1
  16. package/dist/dynamic-texture/texture-data.d.ts +4 -0
  17. package/dist/dynamic-texture/texture-data.d.ts.map +1 -1
  18. package/dist/dynamic-texture/texture-data.js +8 -0
  19. package/dist/dynamic-texture/texture-data.js.map +1 -1
  20. package/dist/factories/pipeline-factory.d.ts +7 -5
  21. package/dist/factories/pipeline-factory.d.ts.map +1 -1
  22. package/dist/factories/pipeline-factory.js +71 -36
  23. package/dist/factories/pipeline-factory.js.map +1 -1
  24. package/dist/factories/shader-factory.d.ts +0 -3
  25. package/dist/factories/shader-factory.d.ts.map +1 -1
  26. package/dist/factories/shader-factory.js +13 -19
  27. package/dist/factories/shader-factory.js.map +1 -1
  28. package/dist/geometries/cone-geometry.d.ts +3 -1
  29. package/dist/geometries/cone-geometry.d.ts.map +1 -1
  30. package/dist/geometries/cone-geometry.js.map +1 -1
  31. package/dist/geometries/cylinder-geometry.d.ts +2 -1
  32. package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
  33. package/dist/geometries/cylinder-geometry.js.map +1 -1
  34. package/dist/index.cjs +683 -922
  35. package/dist/index.cjs.map +4 -4
  36. package/dist/model/model.d.ts +3 -1
  37. package/dist/model/model.d.ts.map +1 -1
  38. package/dist/model/model.js +11 -9
  39. package/dist/model/model.js.map +1 -1
  40. package/dist/models/billboard-texture-model.d.ts.map +1 -1
  41. package/dist/models/billboard-texture-model.js +10 -8
  42. package/dist/models/billboard-texture-model.js.map +1 -1
  43. package/dist/models/clip-space.js +7 -7
  44. package/dist/modules/picking/index-picking.d.ts +1 -1
  45. package/dist/modules/picking/index-picking.d.ts.map +1 -1
  46. package/dist/modules/picking/index-picking.js +0 -6
  47. package/dist/modules/picking/index-picking.js.map +1 -1
  48. package/dist/passes/get-fragment-shader.js +11 -30
  49. package/dist/passes/get-fragment-shader.js.map +1 -1
  50. package/dist/passes/shader-pass-renderer.d.ts +0 -2
  51. package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
  52. package/dist/passes/shader-pass-renderer.js +4 -31
  53. package/dist/passes/shader-pass-renderer.js.map +1 -1
  54. package/dist/scenegraph/group-node.d.ts +5 -0
  55. package/dist/scenegraph/group-node.d.ts.map +1 -1
  56. package/dist/scenegraph/group-node.js +12 -0
  57. package/dist/scenegraph/group-node.js.map +1 -1
  58. package/dist/scenegraph/model-node.d.ts +2 -2
  59. package/dist/scenegraph/model-node.d.ts.map +1 -1
  60. package/dist/scenegraph/model-node.js.map +1 -1
  61. package/dist/scenegraph/scenegraph-node.d.ts +1 -1
  62. package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
  63. package/dist/scenegraph/scenegraph-node.js +23 -15
  64. package/dist/scenegraph/scenegraph-node.js.map +1 -1
  65. package/package.json +2 -2
  66. package/src/animation-loop/animation-loop.ts +75 -46
  67. package/src/animation-loop/make-animation-loop.ts +13 -5
  68. package/src/animation-loop/request-animation-frame.ts +32 -6
  69. package/src/dynamic-texture/dynamic-texture.ts +226 -65
  70. package/src/dynamic-texture/texture-data.ts +14 -0
  71. package/src/factories/pipeline-factory.ts +87 -46
  72. package/src/factories/shader-factory.ts +16 -20
  73. package/src/geometries/cone-geometry.ts +6 -1
  74. package/src/geometries/cylinder-geometry.ts +5 -1
  75. package/src/model/model.ts +14 -10
  76. package/src/models/billboard-texture-model.ts +10 -8
  77. package/src/models/clip-space.ts +7 -7
  78. package/src/modules/picking/index-picking.ts +0 -6
  79. package/src/passes/get-fragment-shader.ts +11 -30
  80. package/src/passes/shader-pass-renderer.ts +4 -33
  81. package/src/scenegraph/group-node.ts +16 -0
  82. package/src/scenegraph/model-node.ts +2 -2
  83. package/src/scenegraph/scenegraph-node.ts +27 -16
  84. package/dist/dynamic-texture/mipmaps.d.ts +0 -6
  85. package/dist/dynamic-texture/mipmaps.d.ts.map +0 -1
  86. package/dist/dynamic-texture/mipmaps.js +0 -441
  87. package/dist/dynamic-texture/mipmaps.js.map +0 -1
  88. package/src/dynamic-texture/mipmaps.ts +0 -517
package/dist/dist.dev.js CHANGED
@@ -281,10 +281,22 @@ var __exports__ = (() => {
281
281
 
282
282
  // src/animation-loop/request-animation-frame.ts
283
283
  function requestAnimationFramePolyfill(callback) {
284
- return typeof window !== "undefined" && window.requestAnimationFrame ? window.requestAnimationFrame(callback) : setTimeout(callback, 1e3 / 60);
284
+ const browserRequestAnimationFrame = typeof window !== "undefined" ? window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame : null;
285
+ if (browserRequestAnimationFrame) {
286
+ return browserRequestAnimationFrame.call(window, callback);
287
+ }
288
+ return setTimeout(
289
+ () => callback(typeof performance !== "undefined" ? performance.now() : Date.now()),
290
+ 1e3 / 60
291
+ );
285
292
  }
286
293
  function cancelAnimationFramePolyfill(timerId) {
287
- return typeof window !== "undefined" && window.cancelAnimationFrame ? window.cancelAnimationFrame(timerId) : clearTimeout(timerId);
294
+ const browserCancelAnimationFrame = typeof window !== "undefined" ? window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame : null;
295
+ if (browserCancelAnimationFrame) {
296
+ browserCancelAnimationFrame.call(window, timerId);
297
+ return;
298
+ }
299
+ clearTimeout(timerId);
288
300
  }
289
301
 
290
302
  // ../../node_modules/@probe.gl/stats/dist/utils/hi-res-timestamp.js
@@ -482,6 +494,7 @@ var __exports__ = (() => {
482
494
 
483
495
  // src/animation-loop/animation-loop.ts
484
496
  var statIdCounter = 0;
497
+ var ANIMATION_LOOP_STATS = "Animation Loop";
485
498
  var _AnimationLoop = class {
486
499
  device = null;
487
500
  canvas = null;
@@ -489,6 +502,7 @@ var __exports__ = (() => {
489
502
  animationProps = null;
490
503
  timeline = null;
491
504
  stats;
505
+ sharedStats;
492
506
  cpuTime;
493
507
  gpuTime;
494
508
  frameRate;
@@ -501,7 +515,7 @@ var __exports__ = (() => {
501
515
  _resolveNextFrame = null;
502
516
  _cpuStartTime = 0;
503
517
  _error = null;
504
- // _gpuTimeQuery: Query | null = null;
518
+ _lastFrameTime = 0;
505
519
  /*
506
520
  * @param {HTMLCanvasElement} canvas - if provided, width and height will be passed to context
507
521
  */
@@ -511,10 +525,12 @@ var __exports__ = (() => {
511
525
  if (!props.device) {
512
526
  throw new Error("No device provided");
513
527
  }
514
- this.stats = props.stats || new Stats({ id: "animation-loop-stats" });
528
+ this.stats = props.stats || new Stats({ id: `animation-loop-${statIdCounter++}` });
529
+ this.sharedStats = import_core.luma.stats.get(ANIMATION_LOOP_STATS);
530
+ this.frameRate = this.stats.get("Frame Rate");
531
+ this.frameRate.setSampleSize(1);
515
532
  this.cpuTime = this.stats.get("CPU Time");
516
533
  this.gpuTime = this.stats.get("GPU Time");
517
- this.frameRate = this.stats.get("Frame Rate");
518
534
  this.setProps({ autoResizeViewport: props.autoResizeViewport });
519
535
  this.start = this.start.bind(this);
520
536
  this.stop = this.stop.bind(this);
@@ -524,6 +540,7 @@ var __exports__ = (() => {
524
540
  destroy() {
525
541
  this.stop();
526
542
  this._setDisplay(null);
543
+ this.device?._disableDebugGPUTime();
527
544
  }
528
545
  /** @deprecated Use .destroy() */
529
546
  delete() {
@@ -588,15 +605,16 @@ var __exports__ = (() => {
588
605
  this._nextFramePromise = null;
589
606
  this._resolveNextFrame = null;
590
607
  this._running = false;
608
+ this._lastFrameTime = 0;
591
609
  }
592
610
  return this;
593
611
  }
594
612
  /** Explicitly draw a frame */
595
- redraw() {
613
+ redraw(time) {
596
614
  if (this.device?.isLost || this._error) {
597
615
  return this;
598
616
  }
599
- this._beginFrameTimers();
617
+ this._beginFrameTimers(time);
600
618
  this._setupFrame();
601
619
  this._updateAnimationProps();
602
620
  this._renderFrame(this._getAnimationProps());
@@ -643,6 +661,7 @@ var __exports__ = (() => {
643
661
  this._initializeAnimationProps();
644
662
  this._updateAnimationProps();
645
663
  this._resizeViewport();
664
+ this.device?._enableDebugGPUTime();
646
665
  }
647
666
  _setDisplay(display) {
648
667
  if (this.display) {
@@ -667,11 +686,11 @@ var __exports__ = (() => {
667
686
  cancelAnimationFramePolyfill(this._animationFrameId);
668
687
  this._animationFrameId = null;
669
688
  }
670
- _animationFrame() {
689
+ _animationFrame(time) {
671
690
  if (!this._running) {
672
691
  return;
673
692
  }
674
- this.redraw();
693
+ this.redraw(time);
675
694
  this._requestAnimationFrame();
676
695
  }
677
696
  // Called on each frame, can be overridden to call onRender multiple times
@@ -785,14 +804,8 @@ var __exports__ = (() => {
785
804
  if (!this.device) {
786
805
  return { width: 1, height: 1, aspect: 1 };
787
806
  }
788
- const [width, height] = this.device?.getDefaultCanvasContext().getDevicePixelSize() || [1, 1];
789
- let aspect = 1;
790
- const canvas2 = this.device?.getDefaultCanvasContext().canvas;
791
- if (canvas2 && canvas2.clientHeight) {
792
- aspect = canvas2.clientWidth / canvas2.clientHeight;
793
- } else if (width > 0 && height > 0) {
794
- aspect = width / height;
795
- }
807
+ const [width, height] = this.device.getDefaultCanvasContext().getDrawingBufferSize();
808
+ const aspect = width > 0 && height > 0 ? width / height : 1;
796
809
  return { width, height, aspect };
797
810
  }
798
811
  /** @deprecated Default viewport setup */
@@ -808,13 +821,61 @@ var __exports__ = (() => {
808
821
  );
809
822
  }
810
823
  }
811
- _beginFrameTimers() {
812
- this.frameRate.timeEnd();
813
- this.frameRate.timeStart();
824
+ _beginFrameTimers(time) {
825
+ const now = time ?? (typeof performance !== "undefined" ? performance.now() : Date.now());
826
+ if (this._lastFrameTime) {
827
+ const frameTime = now - this._lastFrameTime;
828
+ if (frameTime > 0) {
829
+ this.frameRate.addTime(frameTime);
830
+ }
831
+ }
832
+ this._lastFrameTime = now;
833
+ if (this.device?._isDebugGPUTimeEnabled()) {
834
+ this._consumeEncodedGpuTime();
835
+ }
814
836
  this.cpuTime.timeStart();
815
837
  }
816
838
  _endFrameTimers() {
839
+ if (this.device?._isDebugGPUTimeEnabled()) {
840
+ this._consumeEncodedGpuTime();
841
+ }
817
842
  this.cpuTime.timeEnd();
843
+ this._updateSharedStats();
844
+ }
845
+ _consumeEncodedGpuTime() {
846
+ if (!this.device) {
847
+ return;
848
+ }
849
+ const gpuTimeMs = this.device.commandEncoder._gpuTimeMs;
850
+ if (gpuTimeMs !== void 0) {
851
+ this.gpuTime.addTime(gpuTimeMs);
852
+ this.device.commandEncoder._gpuTimeMs = void 0;
853
+ }
854
+ }
855
+ _updateSharedStats() {
856
+ if (this.stats === this.sharedStats) {
857
+ return;
858
+ }
859
+ for (const name of Object.keys(this.sharedStats.stats)) {
860
+ if (!this.stats.stats[name]) {
861
+ delete this.sharedStats.stats[name];
862
+ }
863
+ }
864
+ this.stats.forEach((sourceStat) => {
865
+ const targetStat = this.sharedStats.get(sourceStat.name, sourceStat.type);
866
+ targetStat.sampleSize = sourceStat.sampleSize;
867
+ targetStat.time = sourceStat.time;
868
+ targetStat.count = sourceStat.count;
869
+ targetStat.samples = sourceStat.samples;
870
+ targetStat.lastTiming = sourceStat.lastTiming;
871
+ targetStat.lastSampleTime = sourceStat.lastSampleTime;
872
+ targetStat.lastSampleCount = sourceStat.lastSampleCount;
873
+ targetStat._count = sourceStat._count;
874
+ targetStat._time = sourceStat._time;
875
+ targetStat._samples = sourceStat._samples;
876
+ targetStat._startTime = sourceStat._startTime;
877
+ targetStat._timerPending = sourceStat._timerPending;
878
+ });
818
879
  }
819
880
  // Event handling
820
881
  _startEventHandling() {
@@ -843,7 +904,7 @@ var __exports__ = (() => {
843
904
  },
844
905
  onError: (error) => console.error(error),
845
906
  // eslint-disable-line no-console
846
- stats: import_core.luma.stats.get(`animation-loop-${statIdCounter++}`),
907
+ stats: void 0,
847
908
  // view parameters
848
909
  autoResizeViewport: false
849
910
  });
@@ -875,7 +936,10 @@ var __exports__ = (() => {
875
936
  return animationLoop;
876
937
  }
877
938
  function setError(device, error) {
878
- const canvas2 = device?.getDefaultCanvasContext().canvas;
939
+ if (!device) {
940
+ return;
941
+ }
942
+ const canvas2 = device.getDefaultCanvasContext().canvas;
879
943
  if (canvas2 instanceof HTMLCanvasElement) {
880
944
  canvas2.style.overflow = "visible";
881
945
  let errorDiv = document.getElementById("animation-loop-error");
@@ -892,6 +956,9 @@ var __exports__ = (() => {
892
956
  }
893
957
  }
894
958
  function clearError(device) {
959
+ if (!device) {
960
+ return;
961
+ }
895
962
  const errorDiv = document.getElementById("animation-loop-error");
896
963
  if (errorDiv) {
897
964
  errorDiv.remove();
@@ -899,8 +966,8 @@ var __exports__ = (() => {
899
966
  }
900
967
 
901
968
  // src/model/model.ts
902
- var import_core12 = __toESM(require_core(), 1);
903
- var import_shadertools3 = __toESM(require_shadertools(), 1);
969
+ var import_core10 = __toESM(require_core(), 1);
970
+ var import_shadertools2 = __toESM(require_shadertools(), 1);
904
971
 
905
972
  // src/geometry/gpu-geometry.ts
906
973
  var import_core3 = __toESM(require_core(), 1);
@@ -1019,13 +1086,11 @@ var __exports__ = (() => {
1019
1086
  return moduleData.defaultPipelineFactory;
1020
1087
  }
1021
1088
  device;
1022
- cachingEnabled;
1023
- destroyPolicy;
1024
- debug;
1025
1089
  _hashCounter = 0;
1026
1090
  _hashes = {};
1027
1091
  _renderPipelineCache = {};
1028
1092
  _computePipelineCache = {};
1093
+ _sharedRenderPipelineCache = {};
1029
1094
  get [Symbol.toStringTag]() {
1030
1095
  return "PipelineFactory";
1031
1096
  }
@@ -1034,35 +1099,34 @@ var __exports__ = (() => {
1034
1099
  }
1035
1100
  constructor(device) {
1036
1101
  this.device = device;
1037
- this.cachingEnabled = device.props._cachePipelines;
1038
- this.destroyPolicy = device.props._cacheDestroyPolicy;
1039
- this.debug = device.props.debugFactories;
1040
1102
  }
1041
1103
  /** Return a RenderPipeline matching supplied props. Reuses an equivalent pipeline if already created. */
1042
1104
  createRenderPipeline(props) {
1043
- if (!this.cachingEnabled) {
1105
+ if (!this.device.props._cachePipelines) {
1044
1106
  return this.device.createRenderPipeline(props);
1045
1107
  }
1046
1108
  const allProps = { ...import_core4.RenderPipeline.defaultProps, ...props };
1047
1109
  const cache = this._renderPipelineCache;
1048
1110
  const hash = this._hashRenderPipeline(allProps);
1049
- let pipeline = cache[hash]?.pipeline;
1111
+ let pipeline = cache[hash]?.resource;
1050
1112
  if (!pipeline) {
1113
+ const sharedRenderPipeline = this.device.type === "webgl" && this.device.props._sharePipelines ? this.createSharedRenderPipeline(allProps) : void 0;
1051
1114
  pipeline = this.device.createRenderPipeline({
1052
1115
  ...allProps,
1053
- id: allProps.id ? `${allProps.id}-cached` : uid("unnamed-cached")
1116
+ id: allProps.id ? `${allProps.id}-cached` : uid("unnamed-cached"),
1117
+ _sharedRenderPipeline: sharedRenderPipeline
1054
1118
  });
1055
1119
  pipeline.hash = hash;
1056
- cache[hash] = { pipeline, useCount: 1 };
1057
- if (this.debug) {
1120
+ cache[hash] = { resource: pipeline, useCount: 1 };
1121
+ if (this.device.props.debugFactories) {
1058
1122
  import_core4.log.log(3, `${this}: ${pipeline} created, count=${cache[hash].useCount}`)();
1059
1123
  }
1060
1124
  } else {
1061
1125
  cache[hash].useCount++;
1062
- if (this.debug) {
1126
+ if (this.device.props.debugFactories) {
1063
1127
  import_core4.log.log(
1064
1128
  3,
1065
- `${this}: ${cache[hash].pipeline} reused, count=${cache[hash].useCount}, (id=${props.id})`
1129
+ `${this}: ${cache[hash].resource} reused, count=${cache[hash].useCount}, (id=${props.id})`
1066
1130
  )();
1067
1131
  }
1068
1132
  }
@@ -1070,36 +1134,36 @@ var __exports__ = (() => {
1070
1134
  }
1071
1135
  /** Return a ComputePipeline matching supplied props. Reuses an equivalent pipeline if already created. */
1072
1136
  createComputePipeline(props) {
1073
- if (!this.cachingEnabled) {
1137
+ if (!this.device.props._cachePipelines) {
1074
1138
  return this.device.createComputePipeline(props);
1075
1139
  }
1076
1140
  const allProps = { ...import_core4.ComputePipeline.defaultProps, ...props };
1077
1141
  const cache = this._computePipelineCache;
1078
1142
  const hash = this._hashComputePipeline(allProps);
1079
- let pipeline = cache[hash]?.pipeline;
1143
+ let pipeline = cache[hash]?.resource;
1080
1144
  if (!pipeline) {
1081
1145
  pipeline = this.device.createComputePipeline({
1082
1146
  ...allProps,
1083
1147
  id: allProps.id ? `${allProps.id}-cached` : void 0
1084
1148
  });
1085
1149
  pipeline.hash = hash;
1086
- cache[hash] = { pipeline, useCount: 1 };
1087
- if (this.debug) {
1150
+ cache[hash] = { resource: pipeline, useCount: 1 };
1151
+ if (this.device.props.debugFactories) {
1088
1152
  import_core4.log.log(3, `${this}: ${pipeline} created, count=${cache[hash].useCount}`)();
1089
1153
  }
1090
1154
  } else {
1091
1155
  cache[hash].useCount++;
1092
- if (this.debug) {
1156
+ if (this.device.props.debugFactories) {
1093
1157
  import_core4.log.log(
1094
1158
  3,
1095
- `${this}: ${cache[hash].pipeline} reused, count=${cache[hash].useCount}, (id=${props.id})`
1159
+ `${this}: ${cache[hash].resource} reused, count=${cache[hash].useCount}, (id=${props.id})`
1096
1160
  )();
1097
1161
  }
1098
1162
  }
1099
1163
  return pipeline;
1100
1164
  }
1101
1165
  release(pipeline) {
1102
- if (!this.cachingEnabled) {
1166
+ if (!this.device.props._cachePipelines) {
1103
1167
  pipeline.destroy();
1104
1168
  return;
1105
1169
  }
@@ -1108,28 +1172,55 @@ var __exports__ = (() => {
1108
1172
  cache[hash].useCount--;
1109
1173
  if (cache[hash].useCount === 0) {
1110
1174
  this._destroyPipeline(pipeline);
1111
- if (this.debug) {
1175
+ if (this.device.props.debugFactories) {
1112
1176
  import_core4.log.log(3, `${this}: ${pipeline} released and destroyed`)();
1113
1177
  }
1114
1178
  } else if (cache[hash].useCount < 0) {
1115
1179
  import_core4.log.error(`${this}: ${pipeline} released, useCount < 0, resetting`)();
1116
1180
  cache[hash].useCount = 0;
1117
- } else if (this.debug) {
1181
+ } else if (this.device.props.debugFactories) {
1118
1182
  import_core4.log.log(3, `${this}: ${pipeline} released, count=${cache[hash].useCount}`)();
1119
1183
  }
1120
1184
  }
1185
+ createSharedRenderPipeline(props) {
1186
+ const sharedPipelineHash = this._hashSharedRenderPipeline(props);
1187
+ let sharedCacheItem = this._sharedRenderPipelineCache[sharedPipelineHash];
1188
+ if (!sharedCacheItem) {
1189
+ const sharedRenderPipeline = this.device._createSharedRenderPipelineWebGL(props);
1190
+ sharedCacheItem = { resource: sharedRenderPipeline, useCount: 0 };
1191
+ this._sharedRenderPipelineCache[sharedPipelineHash] = sharedCacheItem;
1192
+ }
1193
+ sharedCacheItem.useCount++;
1194
+ return sharedCacheItem.resource;
1195
+ }
1196
+ releaseSharedRenderPipeline(pipeline) {
1197
+ if (!pipeline.sharedRenderPipeline) {
1198
+ return;
1199
+ }
1200
+ const sharedPipelineHash = this._hashSharedRenderPipeline(pipeline.sharedRenderPipeline.props);
1201
+ const sharedCacheItem = this._sharedRenderPipelineCache[sharedPipelineHash];
1202
+ if (!sharedCacheItem) {
1203
+ return;
1204
+ }
1205
+ sharedCacheItem.useCount--;
1206
+ if (sharedCacheItem.useCount === 0) {
1207
+ sharedCacheItem.resource.destroy();
1208
+ delete this._sharedRenderPipelineCache[sharedPipelineHash];
1209
+ }
1210
+ }
1121
1211
  // PRIVATE
1122
- /** Destroy a cached pipeline, removing it from the cache (depending on destroy policy) */
1212
+ /** Destroy a cached pipeline, removing it from the cache if configured to do so. */
1123
1213
  _destroyPipeline(pipeline) {
1124
1214
  const cache = this._getCache(pipeline);
1125
- switch (this.destroyPolicy) {
1126
- case "never":
1127
- return false;
1128
- case "unused":
1129
- delete cache[pipeline.hash];
1130
- pipeline.destroy();
1131
- return true;
1215
+ if (!this.device.props._destroyPipelines) {
1216
+ return false;
1217
+ }
1218
+ delete cache[pipeline.hash];
1219
+ pipeline.destroy();
1220
+ if (pipeline instanceof import_core4.RenderPipeline) {
1221
+ this.releaseSharedRenderPipeline(pipeline);
1132
1222
  }
1223
+ return true;
1133
1224
  }
1134
1225
  /** Get the appropriate cache for the type of pipeline */
1135
1226
  _getCache(pipeline) {
@@ -1158,24 +1249,35 @@ var __exports__ = (() => {
1158
1249
  _hashRenderPipeline(props) {
1159
1250
  const vsHash = props.vs ? this._getHash(props.vs.source) : 0;
1160
1251
  const fsHash = props.fs ? this._getHash(props.fs.source) : 0;
1161
- const varyingHash = "-";
1252
+ const varyingHash = this._getWebGLVaryingHash(props);
1162
1253
  const bufferLayoutHash = this._getHash(JSON.stringify(props.bufferLayout));
1163
1254
  const { type } = this.device;
1164
1255
  switch (type) {
1165
1256
  case "webgl":
1166
- return `${type}/R/${vsHash}/${fsHash}V${varyingHash}BL${bufferLayoutHash}`;
1257
+ const webglParameterHash = this._getHash(JSON.stringify(props.parameters));
1258
+ return `${type}/R/${vsHash}/${fsHash}V${varyingHash}T${props.topology}P${webglParameterHash}BL${bufferLayoutHash}`;
1167
1259
  case "webgpu":
1168
1260
  default:
1169
1261
  const parameterHash = this._getHash(JSON.stringify(props.parameters));
1170
1262
  return `${type}/R/${vsHash}/${fsHash}V${varyingHash}T${props.topology}P${parameterHash}BL${bufferLayoutHash}`;
1171
1263
  }
1172
1264
  }
1265
+ _hashSharedRenderPipeline(props) {
1266
+ const vsHash = props.vs ? this._getHash(props.vs.source) : 0;
1267
+ const fsHash = props.fs ? this._getHash(props.fs.source) : 0;
1268
+ const varyingHash = this._getWebGLVaryingHash(props);
1269
+ return `webgl/S/${vsHash}/${fsHash}V${varyingHash}`;
1270
+ }
1173
1271
  _getHash(key) {
1174
1272
  if (this._hashes[key] === void 0) {
1175
1273
  this._hashes[key] = this._hashCounter++;
1176
1274
  }
1177
1275
  return this._hashes[key];
1178
1276
  }
1277
+ _getWebGLVaryingHash(props) {
1278
+ const { varyings = [], bufferMode = null } = props;
1279
+ return this._getHash(JSON.stringify({ varyings, bufferMode }));
1280
+ }
1179
1281
  };
1180
1282
  var PipelineFactory = _PipelineFactory;
1181
1283
  __publicField(PipelineFactory, "defaultProps", { ...import_core4.RenderPipeline.defaultProps });
@@ -1190,9 +1292,6 @@ var __exports__ = (() => {
1190
1292
  return moduleData.defaultShaderFactory;
1191
1293
  }
1192
1294
  device;
1193
- cachingEnabled;
1194
- destroyPolicy;
1195
- debug;
1196
1295
  _cache = {};
1197
1296
  get [Symbol.toStringTag]() {
1198
1297
  return "ShaderFactory";
@@ -1203,40 +1302,37 @@ var __exports__ = (() => {
1203
1302
  /** @internal */
1204
1303
  constructor(device) {
1205
1304
  this.device = device;
1206
- this.cachingEnabled = device.props._cacheShaders;
1207
- this.destroyPolicy = device.props._cacheDestroyPolicy;
1208
- this.debug = true;
1209
1305
  }
1210
1306
  /** Requests a {@link Shader} from the cache, creating a new Shader only if necessary. */
1211
1307
  createShader(props) {
1212
- if (!this.cachingEnabled) {
1308
+ if (!this.device.props._cacheShaders) {
1213
1309
  return this.device.createShader(props);
1214
1310
  }
1215
1311
  const key = this._hashShader(props);
1216
1312
  let cacheEntry = this._cache[key];
1217
1313
  if (!cacheEntry) {
1218
- const shader = this.device.createShader({
1314
+ const resource = this.device.createShader({
1219
1315
  ...props,
1220
1316
  id: props.id ? `${props.id}-cached` : void 0
1221
1317
  });
1222
- this._cache[key] = cacheEntry = { shader, useCount: 1 };
1223
- if (this.debug) {
1224
- import_core5.log.log(3, `${this}: Created new shader ${shader.id}`)();
1318
+ this._cache[key] = cacheEntry = { resource, useCount: 1 };
1319
+ if (this.device.props.debugFactories) {
1320
+ import_core5.log.log(3, `${this}: Created new shader ${resource.id}`)();
1225
1321
  }
1226
1322
  } else {
1227
1323
  cacheEntry.useCount++;
1228
- if (this.debug) {
1324
+ if (this.device.props.debugFactories) {
1229
1325
  import_core5.log.log(
1230
1326
  3,
1231
- `${this}: Reusing shader ${cacheEntry.shader.id} count=${cacheEntry.useCount}`
1327
+ `${this}: Reusing shader ${cacheEntry.resource.id} count=${cacheEntry.useCount}`
1232
1328
  )();
1233
1329
  }
1234
1330
  }
1235
- return cacheEntry.shader;
1331
+ return cacheEntry.resource;
1236
1332
  }
1237
1333
  /** Releases a previously-requested {@link Shader}, destroying it if no users remain. */
1238
1334
  release(shader) {
1239
- if (!this.cachingEnabled) {
1335
+ if (!this.device.props._cacheShaders) {
1240
1336
  shader.destroy();
1241
1337
  return;
1242
1338
  }
@@ -1245,16 +1341,16 @@ var __exports__ = (() => {
1245
1341
  if (cacheEntry) {
1246
1342
  cacheEntry.useCount--;
1247
1343
  if (cacheEntry.useCount === 0) {
1248
- if (this.destroyPolicy === "unused") {
1344
+ if (this.device.props._destroyShaders) {
1249
1345
  delete this._cache[key];
1250
- cacheEntry.shader.destroy();
1251
- if (this.debug) {
1346
+ cacheEntry.resource.destroy();
1347
+ if (this.device.props.debugFactories) {
1252
1348
  import_core5.log.log(3, `${this}: Releasing shader ${shader.id}, destroyed`)();
1253
1349
  }
1254
1350
  }
1255
1351
  } else if (cacheEntry.useCount < 0) {
1256
1352
  throw new Error(`ShaderFactory: Shader ${shader.id} released too many times`);
1257
- } else if (this.debug) {
1353
+ } else if (this.device.props.debugFactories) {
1258
1354
  import_core5.log.log(3, `${this}: Releasing shader ${shader.id} count=${cacheEntry.useCount}`)();
1259
1355
  }
1260
1356
  }
@@ -1574,7 +1670,7 @@ var __exports__ = (() => {
1574
1670
  };
1575
1671
 
1576
1672
  // src/dynamic-texture/dynamic-texture.ts
1577
- var import_core11 = __toESM(require_core(), 1);
1673
+ var import_core9 = __toESM(require_core(), 1);
1578
1674
 
1579
1675
  // src/dynamic-texture/texture-data.ts
1580
1676
  var import_core8 = __toESM(require_core(), 1);
@@ -1642,6 +1738,15 @@ var __exports__ = (() => {
1642
1738
  function isTextureImageData(data) {
1643
1739
  return typeof data === "object" && data !== null && "data" in data && "width" in data && "height" in data;
1644
1740
  }
1741
+ function resolveTextureImageFormat(data) {
1742
+ const { textureFormat, format } = data;
1743
+ if (textureFormat && format && textureFormat !== format) {
1744
+ throw new Error(
1745
+ `Conflicting texture formats "${textureFormat}" and "${format}" provided for the same mip level`
1746
+ );
1747
+ }
1748
+ return textureFormat ?? format;
1749
+ }
1645
1750
  function getCubeFaceIndex(face) {
1646
1751
  const idx = TEXTURE_CUBE_FACE_MAP[face];
1647
1752
  if (idx === void 0)
@@ -1674,6 +1779,7 @@ var __exports__ = (() => {
1674
1779
  subresources.push({
1675
1780
  type: "texture-data",
1676
1781
  data: imageData,
1782
+ textureFormat: resolveTextureImageFormat(imageData),
1677
1783
  z,
1678
1784
  mipLevel
1679
1785
  });
@@ -1716,656 +1822,6 @@ var __exports__ = (() => {
1716
1822
  return subresources;
1717
1823
  }
1718
1824
 
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) {
1795
- try {
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
2009
- }
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
- `;
2367
- }
2368
-
2369
1825
  // src/dynamic-texture/dynamic-texture.ts
2370
1826
  var _DynamicTexture = class {
2371
1827
  device;
@@ -2422,6 +1878,9 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2422
1878
  try {
2423
1879
  const propsWithSyncData = await this._loadAllData(originalPropsWithAsyncData);
2424
1880
  this._checkNotDestroyed();
1881
+ const subresources = propsWithSyncData.data ? getTextureSubresources(propsWithSyncData) : [];
1882
+ const userProvidedFormat = "format" in originalPropsWithAsyncData && originalPropsWithAsyncData.format !== void 0;
1883
+ const userProvidedUsage = "usage" in originalPropsWithAsyncData && originalPropsWithAsyncData.usage !== void 0;
2425
1884
  const deduceSize = () => {
2426
1885
  if (this.props.width && this.props.height) {
2427
1886
  return { width: this.props.width, height: this.props.height };
@@ -2436,54 +1895,44 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2436
1895
  if (!size || size.width <= 0 || size.height <= 0) {
2437
1896
  throw new Error(`${this} size could not be determined or was zero`);
2438
1897
  }
1898
+ const textureData = analyzeTextureSubresources(this.device, subresources, size, {
1899
+ format: userProvidedFormat ? originalPropsWithAsyncData.format : void 0
1900
+ });
1901
+ const resolvedFormat = textureData.format ?? this.props.format;
2439
1902
  const baseTextureProps = {
2440
1903
  ...this.props,
2441
1904
  ...size,
1905
+ format: resolvedFormat,
2442
1906
  mipLevels: 1,
2443
1907
  // temporary; updated below
2444
1908
  data: void 0
2445
1909
  };
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;
1910
+ if (this.device.isTextureFormatCompressed(resolvedFormat) && !userProvidedUsage) {
1911
+ baseTextureProps.usage = import_core9.Texture.SAMPLE | import_core9.Texture.COPY_DST;
1912
+ }
1913
+ const shouldGenerateMipmaps = this.props.mipmaps && !textureData.hasExplicitMipChain && !this.device.isTextureFormatCompressed(resolvedFormat);
1914
+ if (this.device.type === "webgpu" && shouldGenerateMipmaps) {
1915
+ const requiredUsage = this.props.dimension === "3d" ? import_core9.Texture.SAMPLE | import_core9.Texture.STORAGE | import_core9.Texture.COPY_DST | import_core9.Texture.COPY_SRC : import_core9.Texture.SAMPLE | import_core9.Texture.RENDER | import_core9.Texture.COPY_DST | import_core9.Texture.COPY_SRC;
2448
1916
  baseTextureProps.usage |= requiredUsage;
2449
1917
  }
2450
1918
  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));
1919
+ const desired = textureData.hasExplicitMipChain ? textureData.mipLevels : this.props.mipLevels === "auto" ? maxMips : Math.max(1, Math.min(maxMips, this.props.mipLevels ?? 1));
2452
1920
  const finalTextureProps = { ...baseTextureProps, mipLevels: desired };
2453
1921
  this._texture = this.device.createTexture(finalTextureProps);
2454
1922
  this._sampler = this.texture.sampler;
2455
1923
  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
- }
1924
+ if (textureData.subresources.length) {
1925
+ this._setTextureSubresources(textureData.subresources);
2480
1926
  }
2481
- if (this.props.mipmaps) {
1927
+ if (this.props.mipmaps && !textureData.hasExplicitMipChain && !shouldGenerateMipmaps) {
1928
+ import_core9.log.warn(`${this} skipping auto-generated mipmaps for compressed texture format`)();
1929
+ }
1930
+ if (shouldGenerateMipmaps) {
2482
1931
  this.generateMipmaps();
2483
1932
  }
2484
1933
  this.isReady = true;
2485
1934
  this.resolveReady(this.texture);
2486
- import_core11.log.info(0, `${this} created`)();
1935
+ import_core9.log.info(0, `${this} created`)();
2487
1936
  } catch (e) {
2488
1937
  const err = e instanceof Error ? e : new Error(String(e));
2489
1938
  this.rejectReady(err);
@@ -2503,15 +1952,15 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2503
1952
  if (this.device.type === "webgl") {
2504
1953
  this.texture.generateMipmapsWebGL();
2505
1954
  } else if (this.device.type === "webgpu") {
2506
- generateMipmap(this.device, this.texture);
1955
+ this.device.generateMipmapsWebGPU(this.texture);
2507
1956
  } else {
2508
- import_core11.log.warn(`${this} mipmaps not supported on ${this.device.type}`);
1957
+ import_core9.log.warn(`${this} mipmaps not supported on ${this.device.type}`);
2509
1958
  }
2510
1959
  }
2511
1960
  /** Set sampler or create one from props */
2512
1961
  setSampler(sampler = {}) {
2513
1962
  this._checkReady();
2514
- const s = sampler instanceof import_core11.Sampler ? sampler : this.device.createSampler(sampler);
1963
+ const s = sampler instanceof import_core9.Sampler ? sampler : this.device.createSampler(sampler);
2515
1964
  this.texture.setSampler(s);
2516
1965
  this._sampler = s;
2517
1966
  }
@@ -2529,7 +1978,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2529
1978
  this._sampler = this.texture.sampler;
2530
1979
  this._view = this.texture.view;
2531
1980
  prev.destroy();
2532
- import_core11.log.info(`${this} resized`);
1981
+ import_core9.log.info(`${this} resized`);
2533
1982
  return true;
2534
1983
  }
2535
1984
  /** Convert cube face label to texture slice index. Index can be used with `setTexture2DData()`. */
@@ -2603,8 +2052,13 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2603
2052
  this.texture.copyExternalImage({ image, z, mipLevel, flipY });
2604
2053
  break;
2605
2054
  case "texture-data":
2606
- const { data } = subresource;
2607
- this.texture.writeData(getAlignedUploadData(this.texture, data), {
2055
+ const { data, textureFormat } = subresource;
2056
+ if (textureFormat && textureFormat !== this.texture.format) {
2057
+ throw new Error(
2058
+ `${this} mip level ${mipLevel} uses format "${textureFormat}" but texture format is "${this.texture.format}"`
2059
+ );
2060
+ }
2061
+ this.texture.writeData(data.data, {
2608
2062
  x: 0,
2609
2063
  y: 0,
2610
2064
  z,
@@ -2628,45 +2082,155 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2628
2082
  }
2629
2083
  _checkNotDestroyed() {
2630
2084
  if (this.destroyed) {
2631
- import_core11.log.warn(`${this} already destroyed`);
2085
+ import_core9.log.warn(`${this} already destroyed`);
2632
2086
  }
2633
2087
  }
2634
2088
  _checkReady() {
2635
2089
  if (!this.isReady) {
2636
- import_core11.log.warn(`${this} Cannot perform this operation before ready`);
2090
+ import_core9.log.warn(`${this} Cannot perform this operation before ready`);
2637
2091
  }
2638
2092
  }
2639
2093
  };
2640
2094
  var DynamicTexture = _DynamicTexture;
2641
2095
  __publicField(DynamicTexture, "defaultProps", {
2642
- ...import_core11.Texture.defaultProps,
2096
+ ...import_core9.Texture.defaultProps,
2643
2097
  dimension: "2d",
2644
2098
  data: null,
2645
2099
  mipmaps: false
2646
2100
  });
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
2101
+ function getTextureSubresources(props) {
2102
+ if (!props.data) {
2103
+ return [];
2104
+ }
2105
+ switch (props.dimension) {
2106
+ case "1d":
2107
+ return getTexture1DSubresources(props.data);
2108
+ case "2d":
2109
+ return getTexture2DSubresources(0, props.data);
2110
+ case "3d":
2111
+ return getTexture3DSubresources(props.data);
2112
+ case "2d-array":
2113
+ return getTextureArraySubresources(props.data);
2114
+ case "cube":
2115
+ return getTextureCubeSubresources(props.data);
2116
+ case "cube-array":
2117
+ return getTextureCubeArraySubresources(props.data);
2118
+ default:
2119
+ throw new Error(`Unhandled dimension ${props.dimension}`);
2120
+ }
2121
+ }
2122
+ function analyzeTextureSubresources(device, subresources, size, options) {
2123
+ if (subresources.length === 0) {
2124
+ return {
2125
+ subresources,
2126
+ mipLevels: 1,
2127
+ format: options.format,
2128
+ hasExplicitMipChain: false
2129
+ };
2130
+ }
2131
+ const groupedSubresources = /* @__PURE__ */ new Map();
2132
+ for (const subresource of subresources) {
2133
+ const group = groupedSubresources.get(subresource.z) ?? [];
2134
+ group.push(subresource);
2135
+ groupedSubresources.set(subresource.z, group);
2136
+ }
2137
+ const hasExplicitMipChain = subresources.some((subresource) => subresource.mipLevel > 0);
2138
+ let resolvedFormat = options.format;
2139
+ let resolvedMipLevels = Number.POSITIVE_INFINITY;
2140
+ const validSubresources = [];
2141
+ for (const [z, sliceSubresources] of groupedSubresources) {
2142
+ const sortedSubresources = [...sliceSubresources].sort(
2143
+ (left, right) => left.mipLevel - right.mipLevel
2667
2144
  );
2145
+ const baseLevel = sortedSubresources[0];
2146
+ if (!baseLevel || baseLevel.mipLevel !== 0) {
2147
+ throw new Error(`DynamicTexture: slice ${z} is missing mip level 0`);
2148
+ }
2149
+ const baseSize = getTextureSubresourceSize(device, baseLevel);
2150
+ if (baseSize.width !== size.width || baseSize.height !== size.height) {
2151
+ throw new Error(
2152
+ `DynamicTexture: slice ${z} base level dimensions ${baseSize.width}x${baseSize.height} do not match expected ${size.width}x${size.height}`
2153
+ );
2154
+ }
2155
+ const baseFormat = getTextureSubresourceFormat(baseLevel);
2156
+ if (baseFormat) {
2157
+ if (resolvedFormat && resolvedFormat !== baseFormat) {
2158
+ throw new Error(
2159
+ `DynamicTexture: slice ${z} base level format "${baseFormat}" does not match texture format "${resolvedFormat}"`
2160
+ );
2161
+ }
2162
+ resolvedFormat = baseFormat;
2163
+ }
2164
+ const mipLevelLimit = resolvedFormat && device.isTextureFormatCompressed(resolvedFormat) ? (
2165
+ // Block-compressed formats cannot have mips smaller than a single compression block.
2166
+ getMaxCompressedMipLevels(device, baseSize.width, baseSize.height, resolvedFormat)
2167
+ ) : device.getMipLevelCount(baseSize.width, baseSize.height);
2168
+ let validMipLevelsForSlice = 0;
2169
+ for (let expectedMipLevel = 0; expectedMipLevel < sortedSubresources.length; expectedMipLevel++) {
2170
+ const subresource = sortedSubresources[expectedMipLevel];
2171
+ if (!subresource || subresource.mipLevel !== expectedMipLevel) {
2172
+ break;
2173
+ }
2174
+ if (expectedMipLevel >= mipLevelLimit) {
2175
+ break;
2176
+ }
2177
+ const subresourceSize = getTextureSubresourceSize(device, subresource);
2178
+ const expectedWidth = Math.max(1, baseSize.width >> expectedMipLevel);
2179
+ const expectedHeight = Math.max(1, baseSize.height >> expectedMipLevel);
2180
+ if (subresourceSize.width !== expectedWidth || subresourceSize.height !== expectedHeight) {
2181
+ break;
2182
+ }
2183
+ const subresourceFormat = getTextureSubresourceFormat(subresource);
2184
+ if (subresourceFormat) {
2185
+ if (!resolvedFormat) {
2186
+ resolvedFormat = subresourceFormat;
2187
+ }
2188
+ if (subresourceFormat !== resolvedFormat) {
2189
+ break;
2190
+ }
2191
+ }
2192
+ validMipLevelsForSlice++;
2193
+ validSubresources.push(subresource);
2194
+ }
2195
+ resolvedMipLevels = Math.min(resolvedMipLevels, validMipLevelsForSlice);
2196
+ }
2197
+ const mipLevels = Number.isFinite(resolvedMipLevels) ? Math.max(1, resolvedMipLevels) : 1;
2198
+ return {
2199
+ // Keep every slice trimmed to the same mip count so the texture shape stays internally consistent.
2200
+ subresources: validSubresources.filter((subresource) => subresource.mipLevel < mipLevels),
2201
+ mipLevels,
2202
+ format: resolvedFormat,
2203
+ hasExplicitMipChain
2204
+ };
2205
+ }
2206
+ function getTextureSubresourceFormat(subresource) {
2207
+ if (subresource.type !== "texture-data") {
2208
+ return void 0;
2209
+ }
2210
+ return subresource.textureFormat ?? resolveTextureImageFormat(subresource.data);
2211
+ }
2212
+ function getTextureSubresourceSize(device, subresource) {
2213
+ switch (subresource.type) {
2214
+ case "external-image":
2215
+ return device.getExternalImageSize(subresource.image);
2216
+ case "texture-data":
2217
+ return { width: subresource.data.width, height: subresource.data.height };
2218
+ default:
2219
+ throw new Error("Unsupported texture subresource");
2668
2220
  }
2669
- return paddedBytes;
2221
+ }
2222
+ function getMaxCompressedMipLevels(device, baseWidth, baseHeight, format) {
2223
+ const { blockWidth = 1, blockHeight = 1 } = device.getTextureFormatInfo(format);
2224
+ let mipLevels = 1;
2225
+ for (let mipLevel = 1; ; mipLevel++) {
2226
+ const width = Math.max(1, baseWidth >> mipLevel);
2227
+ const height = Math.max(1, baseHeight >> mipLevel);
2228
+ if (width < blockWidth || height < blockHeight) {
2229
+ break;
2230
+ }
2231
+ mipLevels++;
2232
+ }
2233
+ return mipLevels;
2670
2234
  }
2671
2235
  async function awaitAllPromises(x) {
2672
2236
  x = await x;
@@ -2687,8 +2251,8 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2687
2251
  }
2688
2252
 
2689
2253
  // src/model/model.ts
2690
- var LOG_DRAW_PRIORITY2 = 2;
2691
- var LOG_DRAW_TIMEOUT2 = 1e4;
2254
+ var LOG_DRAW_PRIORITY = 2;
2255
+ var LOG_DRAW_TIMEOUT = 1e4;
2692
2256
  var _Model = class {
2693
2257
  /** Device that created this model */
2694
2258
  device;
@@ -2771,7 +2335,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2771
2335
  );
2772
2336
  const shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap, { disableWarnings: this.props.disableWarnings });
2773
2337
  this.setShaderInputs(shaderInputs);
2774
- const platformInfo = getPlatformInfo2(device);
2338
+ const platformInfo = getPlatformInfo(device);
2775
2339
  const modules = (
2776
2340
  // @ts-ignore shaderInputs is assigned in setShaderInputs above.
2777
2341
  (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || []
@@ -2843,7 +2407,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2843
2407
  if (!this._destroyed) {
2844
2408
  this.pipelineFactory.release(this.pipeline);
2845
2409
  this.shaderFactory.release(this.pipeline.vs);
2846
- if (this.pipeline.fs) {
2410
+ if (this.pipeline.fs && this.pipeline.fs !== this.pipeline.vs) {
2847
2411
  this.shaderFactory.release(this.pipeline.fs);
2848
2412
  }
2849
2413
  this._uniformStore.destroy();
@@ -2878,7 +2442,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2878
2442
  draw(renderPass) {
2879
2443
  const loadingBinding = this._areBindingsLoading();
2880
2444
  if (loadingBinding) {
2881
- import_core12.log.info(LOG_DRAW_PRIORITY2, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
2445
+ import_core10.log.info(LOG_DRAW_PRIORITY, `>>> DRAWING ABORTED ${this.id}: ${loadingBinding} not loaded`)();
2882
2446
  return false;
2883
2447
  }
2884
2448
  try {
@@ -2893,9 +2457,6 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2893
2457
  this._logDrawCallStart();
2894
2458
  this.pipeline = this._updatePipeline();
2895
2459
  const syncBindings = this._getBindings();
2896
- this.pipeline.setBindings(syncBindings, {
2897
- disableWarnings: this.props.disableWarnings
2898
- });
2899
2460
  const { indexBuffer } = this.vertexArray;
2900
2461
  const indexCount = indexBuffer ? indexBuffer.byteLength / (indexBuffer.indexType === "uint32" ? 4 : 2) : void 0;
2901
2462
  drawSuccess = this.pipeline.draw({
@@ -2906,6 +2467,11 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
2906
2467
  instanceCount: this.instanceCount,
2907
2468
  indexCount,
2908
2469
  transformFeedback: this.transformFeedback || void 0,
2470
+ // Pipelines may be shared across models when caching is enabled, so bindings
2471
+ // and WebGL uniforms must be supplied on every draw instead of being stored
2472
+ // on the pipeline instance.
2473
+ bindings: syncBindings,
2474
+ uniforms: this.props.uniforms,
2909
2475
  // WebGL shares underlying cached pipelines even for models that have different parameters and topology,
2910
2476
  // so we must provide our unique parameters to each draw
2911
2477
  // (In WebGPU most parameters are encoded in the pipeline and cannot be changed per draw call)
@@ -3008,7 +2574,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3008
2574
  /** Set the shader inputs */
3009
2575
  setShaderInputs(shaderInputs) {
3010
2576
  this.shaderInputs = shaderInputs;
3011
- this._uniformStore = new import_core12.UniformStore(this.shaderInputs.modules);
2577
+ this._uniformStore = new import_core10.UniformStore(this.shaderInputs.modules);
3012
2578
  for (const [moduleName, module] of Object.entries(this.shaderInputs.modules)) {
3013
2579
  if (shaderModuleHasUniforms(module)) {
3014
2580
  const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
@@ -3052,7 +2618,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3052
2618
  setAttributes(buffers, options) {
3053
2619
  const disableWarnings = options?.disableWarnings ?? this.props.disableWarnings;
3054
2620
  if (buffers["indices"]) {
3055
- import_core12.log.warn(
2621
+ import_core10.log.warn(
3056
2622
  `Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`
3057
2623
  )();
3058
2624
  }
@@ -3065,7 +2631,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3065
2631
  const bufferLayout = bufferLayoutHelper.getBufferLayout(bufferName);
3066
2632
  if (!bufferLayout) {
3067
2633
  if (!disableWarnings) {
3068
- import_core12.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
2634
+ import_core10.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
3069
2635
  }
3070
2636
  continue;
3071
2637
  }
@@ -3080,7 +2646,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3080
2646
  }
3081
2647
  }
3082
2648
  if (!set && !disableWarnings) {
3083
- import_core12.log.warn(
2649
+ import_core10.log.warn(
3084
2650
  `Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`
3085
2651
  )();
3086
2652
  }
@@ -3101,7 +2667,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3101
2667
  if (attributeInfo) {
3102
2668
  this.vertexArray.setConstantWebGL(attributeInfo.location, value);
3103
2669
  } else if (!(options?.disableWarnings ?? this.props.disableWarnings)) {
3104
- import_core12.log.warn(
2670
+ import_core10.log.warn(
3105
2671
  `Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`
3106
2672
  )();
3107
2673
  }
@@ -3136,16 +2702,16 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3136
2702
  _getBindingsUpdateTimestamp() {
3137
2703
  let timestamp = 0;
3138
2704
  for (const binding of Object.values(this.bindings)) {
3139
- if (binding instanceof import_core12.TextureView) {
2705
+ if (binding instanceof import_core10.TextureView) {
3140
2706
  timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
3141
- } else if (binding instanceof import_core12.Buffer || binding instanceof import_core12.Texture) {
2707
+ } else if (binding instanceof import_core10.Buffer || binding instanceof import_core10.Texture) {
3142
2708
  timestamp = Math.max(timestamp, binding.updateTimestamp);
3143
2709
  } else if (binding instanceof DynamicTexture) {
3144
2710
  timestamp = binding.texture ? Math.max(timestamp, binding.texture.updateTimestamp) : (
3145
2711
  // The texture will become available in the future
3146
2712
  Infinity
3147
2713
  );
3148
- } else if (!(binding instanceof import_core12.Sampler)) {
2714
+ } else if (!(binding instanceof import_core10.Sampler)) {
3149
2715
  timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
3150
2716
  }
3151
2717
  }
@@ -3180,7 +2746,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3180
2746
  let prevShaderVs = null;
3181
2747
  let prevShaderFs = null;
3182
2748
  if (this.pipeline) {
3183
- import_core12.log.log(
2749
+ import_core10.log.log(
3184
2750
  1,
3185
2751
  `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
3186
2752
  )();
@@ -3216,14 +2782,15 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3216
2782
  vs: vs3,
3217
2783
  fs: fs3
3218
2784
  });
3219
- this._attributeInfos = (0, import_core12.getAttributeInfosFromLayouts)(
2785
+ this._attributeInfos = (0, import_core10.getAttributeInfosFromLayouts)(
3220
2786
  this.pipeline.shaderLayout,
3221
2787
  this.bufferLayout
3222
2788
  );
3223
2789
  if (prevShaderVs)
3224
2790
  this.shaderFactory.release(prevShaderVs);
3225
- if (prevShaderFs)
2791
+ if (prevShaderFs && prevShaderFs !== prevShaderVs) {
3226
2792
  this.shaderFactory.release(prevShaderFs);
2793
+ }
3227
2794
  }
3228
2795
  return this.pipeline;
3229
2796
  }
@@ -3231,24 +2798,24 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3231
2798
  _lastLogTime = 0;
3232
2799
  _logOpen = false;
3233
2800
  _logDrawCallStart() {
3234
- const logDrawTimeout = import_core12.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT2;
3235
- if (import_core12.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
2801
+ const logDrawTimeout = import_core10.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
2802
+ if (import_core10.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
3236
2803
  return;
3237
2804
  }
3238
2805
  this._lastLogTime = Date.now();
3239
2806
  this._logOpen = true;
3240
- import_core12.log.group(LOG_DRAW_PRIORITY2, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core12.log.level <= 2 })();
2807
+ import_core10.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core10.log.level <= 2 })();
3241
2808
  }
3242
2809
  _logDrawCallEnd() {
3243
2810
  if (this._logOpen) {
3244
2811
  const shaderLayoutTable = getDebugTableForShaderLayout(this.pipeline.shaderLayout, this.id);
3245
- import_core12.log.table(LOG_DRAW_PRIORITY2, shaderLayoutTable)();
2812
+ import_core10.log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
3246
2813
  const uniformTable = this.shaderInputs.getDebugTable();
3247
- import_core12.log.table(LOG_DRAW_PRIORITY2, uniformTable)();
2814
+ import_core10.log.table(LOG_DRAW_PRIORITY, uniformTable)();
3248
2815
  const attributeTable = this._getAttributeDebugTable();
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)();
2816
+ import_core10.log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
2817
+ import_core10.log.table(LOG_DRAW_PRIORITY, attributeTable)();
2818
+ import_core10.log.groupEnd(LOG_DRAW_PRIORITY)();
3252
2819
  this._logOpen = false;
3253
2820
  }
3254
2821
  }
@@ -3287,14 +2854,14 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3287
2854
  }
3288
2855
  // TODO - fix typing of luma data types
3289
2856
  _getBufferOrConstantValues(attribute, dataType) {
3290
- const TypedArrayConstructor = (0, import_core12.getTypedArrayConstructor)(dataType);
3291
- const typedArray = attribute instanceof import_core12.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
2857
+ const TypedArrayConstructor = (0, import_core10.getTypedArrayConstructor)(dataType);
2858
+ const typedArray = attribute instanceof import_core10.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
3292
2859
  return typedArray.toString();
3293
2860
  }
3294
2861
  };
3295
2862
  var Model = _Model;
3296
2863
  __publicField(Model, "defaultProps", {
3297
- ...import_core12.RenderPipeline.defaultProps,
2864
+ ...import_core10.RenderPipeline.defaultProps,
3298
2865
  source: void 0,
3299
2866
  vs: null,
3300
2867
  fs: null,
@@ -3307,6 +2874,8 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3307
2874
  indexBuffer: null,
3308
2875
  attributes: {},
3309
2876
  constantAttributes: {},
2877
+ bindings: {},
2878
+ uniforms: {},
3310
2879
  varyings: [],
3311
2880
  isInstanced: void 0,
3312
2881
  instanceCount: 0,
@@ -3315,14 +2884,14 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3315
2884
  pipelineFactory: void 0,
3316
2885
  shaderFactory: void 0,
3317
2886
  transformFeedback: void 0,
3318
- shaderAssembler: import_shadertools3.ShaderAssembler.getDefaultShaderAssembler(),
2887
+ shaderAssembler: import_shadertools2.ShaderAssembler.getDefaultShaderAssembler(),
3319
2888
  debugShaders: void 0,
3320
2889
  disableWarnings: void 0
3321
2890
  });
3322
2891
  function shaderModuleHasUniforms(module) {
3323
2892
  return Boolean(module.uniformTypes && !isObjectEmpty(module.uniformTypes));
3324
2893
  }
3325
- function getPlatformInfo2(device) {
2894
+ function getPlatformInfo(device) {
3326
2895
  return {
3327
2896
  type: device.type,
3328
2897
  shaderLanguage: device.info.shadingLanguage,
@@ -3340,8 +2909,8 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3340
2909
  }
3341
2910
 
3342
2911
  // src/compute/buffer-transform.ts
3343
- var import_core13 = __toESM(require_core(), 1);
3344
- var import_shadertools4 = __toESM(require_shadertools(), 1);
2912
+ var import_core11 = __toESM(require_core(), 1);
2913
+ var import_shadertools3 = __toESM(require_shadertools(), 1);
3345
2914
  var _BufferTransform = class {
3346
2915
  device;
3347
2916
  model;
@@ -3356,7 +2925,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3356
2925
  this.device = device;
3357
2926
  this.model = new Model(this.device, {
3358
2927
  id: props.id || "buffer-transform-model",
3359
- fs: props.fs || (0, import_shadertools4.getPassthroughFS)(),
2928
+ fs: props.fs || (0, import_shadertools3.getPassthroughFS)(),
3360
2929
  topology: props.topology || "point-list",
3361
2930
  varyings: props.outputs || props.varyings,
3362
2931
  ...props
@@ -3402,7 +2971,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3402
2971
  if (!result) {
3403
2972
  throw new Error("BufferTransform#getBuffer");
3404
2973
  }
3405
- if (result instanceof import_core13.Buffer) {
2974
+ if (result instanceof import_core11.Buffer) {
3406
2975
  return result.readAsync();
3407
2976
  }
3408
2977
  const { buffer, byteOffset = 0, byteLength = buffer.byteLength } = result;
@@ -3417,7 +2986,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3417
2986
  });
3418
2987
 
3419
2988
  // src/compute/texture-transform.ts
3420
- var import_shadertools5 = __toESM(require_shadertools(), 1);
2989
+ var import_shadertools4 = __toESM(require_shadertools(), 1);
3421
2990
  var FS_OUTPUT_VARIABLE = "transform_output";
3422
2991
  var TextureTransform = class {
3423
2992
  device;
@@ -3440,7 +3009,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3440
3009
  });
3441
3010
  this.model = new Model(this.device, {
3442
3011
  id: props.id || uid("texture-transform-model"),
3443
- fs: props.fs || (0, import_shadertools5.getPassthroughFS)({
3012
+ fs: props.fs || (0, import_shadertools4.getPassthroughFS)({
3444
3013
  input: props.targetTextureVarying,
3445
3014
  inputChannels: props.targetTextureChannels,
3446
3015
  output: FS_OUTPUT_VARIABLE
@@ -3611,9 +3180,9 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
3611
3180
  var CLIPSPACE_VERTEX_SHADER_WGSL = (
3612
3181
  /* wgsl */
3613
3182
  `struct VertexInputs {
3614
- @location(0) clipSpacePosition: vec2<f32>,
3615
- @location(1) texCoord: vec2<f32>,
3616
- @location(2) coordinate: vec2<f32>
3183
+ @location(0) clipSpacePositions: vec2<f32>,
3184
+ @location(1) texCoords: vec2<f32>,
3185
+ @location(2) coordinates: vec2<f32>
3617
3186
  }
3618
3187
 
3619
3188
  struct FragmentInputs {
@@ -3626,10 +3195,10 @@ struct FragmentInputs {
3626
3195
  @vertex
3627
3196
  fn vertexMain(inputs: VertexInputs) -> FragmentInputs {
3628
3197
  var outputs: FragmentInputs;
3629
- outputs.Position = vec4(inputs.clipSpacePosition, 0., 1.);
3630
- outputs.position = inputs.clipSpacePosition;
3631
- outputs.coordinate = inputs.coordinate;
3632
- outputs.uv = inputs.texCoord;
3198
+ outputs.Position = vec4(inputs.clipSpacePositions, 0., 1.);
3199
+ outputs.position = inputs.clipSpacePositions;
3200
+ outputs.coordinate = inputs.coordinates;
3201
+ outputs.uv = inputs.texCoords;
3633
3202
  return outputs;
3634
3203
  }
3635
3204
  `
@@ -3695,15 +3264,15 @@ struct backgroundUniforms {
3695
3264
  };
3696
3265
  @group(0) @binding(2) var<uniform> background: backgroundUniforms;
3697
3266
 
3698
- fn billboardTexture_getTextureUV(coordinates: vec2<f32>) -> vec2<f32> {
3267
+ fn billboardTexture_getTextureUV(uv: vec2<f32>) -> vec2<f32> {
3699
3268
  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);
3269
+ var position: vec2<f32> = (uv - vec2<f32>(0.5, 0.5)) / scale + vec2<f32>(0.5, 0.5);
3701
3270
  return position;
3702
3271
  }
3703
3272
 
3704
3273
  @fragment
3705
3274
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
3706
- let position: vec2<f32> = billboardTexture_getTextureUV(inputs.coordinate);
3275
+ let position: vec2<f32> = billboardTexture_getTextureUV(inputs.uv);
3707
3276
  return textureSample(backgroundTexture, backgroundTextureSampler, position);
3708
3277
  }
3709
3278
  `
@@ -3737,20 +3306,22 @@ void main(void) {
3737
3306
  backgroundTexture = null;
3738
3307
  constructor(device, props) {
3739
3308
  super(device, {
3309
+ ...props,
3740
3310
  id: props.id || "background-texture-model",
3741
3311
  source: BACKGROUND_FS_WGSL,
3742
3312
  fs: BACKGROUND_FS,
3743
- modules: [backgroundModule],
3313
+ modules: [...props.modules || [], backgroundModule],
3744
3314
  parameters: {
3745
3315
  depthWriteEnabled: false,
3316
+ ...props.parameters || {},
3746
3317
  ...props.blend ? {
3747
3318
  blend: true,
3748
3319
  blendColorOperation: "add",
3749
3320
  blendAlphaOperation: "add",
3750
- blendColorSrcFactor: "one",
3751
- blendColorDstFactor: "one-minus-src",
3752
- blendAlphaSrcFactor: "one",
3753
- blendAlphaDstFactor: "one-minus-src-alpha"
3321
+ blendColorSrcFactor: "one-minus-dst-alpha",
3322
+ blendColorDstFactor: "one",
3323
+ blendAlphaSrcFactor: "one-minus-dst-alpha",
3324
+ blendAlphaDstFactor: "one"
3754
3325
  } : {}
3755
3326
  }
3756
3327
  });
@@ -5836,6 +5407,11 @@ void main(void) {
5836
5407
  }
5837
5408
 
5838
5409
  // src/scenegraph/scenegraph-node.ts
5410
+ function assert2(condition, message) {
5411
+ if (!condition) {
5412
+ throw new Error(message);
5413
+ }
5414
+ }
5839
5415
  var ScenegraphNode = class {
5840
5416
  id;
5841
5417
  matrix = new Matrix4();
@@ -5867,14 +5443,17 @@ void main(void) {
5867
5443
  return `{type: ScenegraphNode, id: ${this.id})}`;
5868
5444
  }
5869
5445
  setPosition(position) {
5446
+ assert2(position.length === 3, "setPosition requires vector argument");
5870
5447
  this.position = position;
5871
5448
  return this;
5872
5449
  }
5873
5450
  setRotation(rotation) {
5451
+ assert2(rotation.length === 3 || rotation.length === 4, "setRotation requires vector argument");
5874
5452
  this.rotation = rotation;
5875
5453
  return this;
5876
5454
  }
5877
5455
  setScale(scale2) {
5456
+ assert2(scale2.length === 3, "setScale requires vector argument");
5878
5457
  this.scale = scale2;
5879
5458
  return this;
5880
5459
  }
@@ -5902,17 +5481,18 @@ void main(void) {
5902
5481
  return this;
5903
5482
  }
5904
5483
  updateMatrix() {
5905
- const pos = this.position;
5906
- const rot = this.rotation;
5907
- const scale2 = this.scale;
5908
5484
  this.matrix.identity();
5909
- this.matrix.translate(pos);
5910
- this.matrix.rotateXYZ(rot);
5911
- this.matrix.scale(scale2);
5485
+ this.matrix.translate(this.position);
5486
+ if (this.rotation.length === 4) {
5487
+ const rotationMatrix = new Matrix4().fromQuaternion(this.rotation);
5488
+ this.matrix.multiplyRight(rotationMatrix);
5489
+ } else {
5490
+ this.matrix.rotateXYZ(this.rotation);
5491
+ }
5492
+ this.matrix.scale(this.scale);
5912
5493
  return this;
5913
5494
  }
5914
- update(options = {}) {
5915
- const { position, rotation, scale: scale2 } = options;
5495
+ update({ position, rotation, scale: scale2 } = {}) {
5916
5496
  if (position) {
5917
5497
  this.setPosition(position);
5918
5498
  }
@@ -5962,16 +5542,17 @@ void main(void) {
5962
5542
  }
5963
5543
  */
5964
5544
  _setScenegraphNodeProps(props) {
5965
- if ("position" in props) {
5545
+ if (props?.position) {
5966
5546
  this.setPosition(props.position);
5967
5547
  }
5968
- if ("rotation" in props) {
5548
+ if (props?.rotation) {
5969
5549
  this.setRotation(props.rotation);
5970
5550
  }
5971
- if ("scale" in props) {
5551
+ if (props?.scale) {
5972
5552
  this.setScale(props.scale);
5973
5553
  }
5974
- if ("matrix" in props) {
5554
+ this.updateMatrix();
5555
+ if (props?.matrix) {
5975
5556
  this.setMatrix(props.matrix);
5976
5557
  }
5977
5558
  Object.assign(this.props, props);
@@ -5979,13 +5560,13 @@ void main(void) {
5979
5560
  };
5980
5561
 
5981
5562
  // src/scenegraph/group-node.ts
5982
- var import_core16 = __toESM(require_core(), 1);
5563
+ var import_core14 = __toESM(require_core(), 1);
5983
5564
  var GroupNode = class extends ScenegraphNode {
5984
5565
  children;
5985
5566
  constructor(props = {}) {
5986
5567
  props = Array.isArray(props) ? { children: props } : props;
5987
5568
  const { children = [] } = props;
5988
- import_core16.log.assert(
5569
+ import_core14.log.assert(
5989
5570
  children.every((child) => child instanceof ScenegraphNode),
5990
5571
  "every child must an instance of ScenegraphNode"
5991
5572
  );
@@ -6058,6 +5639,17 @@ void main(void) {
6058
5639
  }
6059
5640
  }
6060
5641
  }
5642
+ preorderTraversal(visitor, { worldMatrix = new Matrix4() } = {}) {
5643
+ const modelMatrix = new Matrix4(worldMatrix).multiplyRight(this.matrix);
5644
+ visitor(this, { worldMatrix: modelMatrix });
5645
+ for (const child of this.children) {
5646
+ if (child instanceof GroupNode) {
5647
+ child.preorderTraversal(visitor, { worldMatrix: modelMatrix });
5648
+ } else {
5649
+ visitor(child, { worldMatrix: modelMatrix });
5650
+ }
5651
+ }
5652
+ }
6061
5653
  };
6062
5654
 
6063
5655
  // src/scenegraph/model-node.ts
@@ -7226,10 +6818,10 @@ void main(void) {
7226
6818
  }
7227
6819
 
7228
6820
  // src/passes/shader-pass-renderer.ts
7229
- var import_shadertools6 = __toESM(require_shadertools(), 1);
6821
+ var import_shadertools5 = __toESM(require_shadertools(), 1);
7230
6822
 
7231
6823
  // src/compute/swap.ts
7232
- var import_core18 = __toESM(require_core(), 1);
6824
+ var import_core16 = __toESM(require_core(), 1);
7233
6825
  var Swap = class {
7234
6826
  id;
7235
6827
  /** The current resource - usually the source for renders or computations */
@@ -7261,7 +6853,7 @@ void main(void) {
7261
6853
  (colorAttachment) => typeof colorAttachment !== "string" ? colorAttachment : device.createTexture({
7262
6854
  id: `${props.id}-texture-0`,
7263
6855
  format: colorAttachment,
7264
- usage: import_core18.Texture.SAMPLE | import_core18.Texture.RENDER | import_core18.Texture.COPY_SRC | import_core18.Texture.COPY_DST,
6856
+ usage: import_core16.Texture.SAMPLE | import_core16.Texture.RENDER | import_core16.Texture.COPY_SRC | import_core16.Texture.COPY_DST,
7265
6857
  width,
7266
6858
  height
7267
6859
  })
@@ -7271,7 +6863,7 @@ void main(void) {
7271
6863
  (colorAttachment) => typeof colorAttachment !== "string" ? colorAttachment : device.createTexture({
7272
6864
  id: `${props.id}-texture-1`,
7273
6865
  format: colorAttachment,
7274
- usage: import_core18.Texture.SAMPLE | import_core18.Texture.RENDER | import_core18.Texture.COPY_SRC | import_core18.Texture.COPY_DST,
6866
+ usage: import_core16.Texture.SAMPLE | import_core16.Texture.RENDER | import_core16.Texture.COPY_SRC | import_core16.Texture.COPY_DST,
7275
6867
  width,
7276
6868
  height
7277
6869
  })
@@ -7335,26 +6927,16 @@ void main(void) {
7335
6927
  function getFilterShaderWGSL(func) {
7336
6928
  return (
7337
6929
  /* wgsl */
7338
- `// Binding 0:1 is reserved for shader passes
7339
- // @group(0) @binding(0) var<uniform> brightnessContrast : brightnessContrastUniforms;
7340
- @group(0) @binding(1) var texture: texture_2d<f32>;
7341
- @group(0) @binding(2) var textureSampler: sampler;
7342
-
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
- // };
6930
+ `@group(0) @binding(0) var sourceTexture: texture_2d<f32>;
6931
+ @group(0) @binding(2) var sourceTextureSampler: sampler;
7349
6932
 
7350
6933
  @fragment
7351
6934
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
7352
- let fragUV = inputs.uv;
7353
- let fragCoordinate = inputs.coordinate;
7354
- let texSize = vec2f(textureDimensions(texture, 0));
6935
+ let texCoord = inputs.coordinate;
6936
+ let texSize = vec2f(textureDimensions(sourceTexture));
7355
6937
 
7356
- var fragColor = textureSample(texture, textureSampler, fragUV);
7357
- fragColor = ${func}(fragColor, texSize, fragCoordinate);
6938
+ var fragColor = textureSample(sourceTexture, sourceTextureSampler, texCoord);
6939
+ fragColor = ${func}(fragColor, texSize, texCoord);
7358
6940
  return fragColor;
7359
6941
  }
7360
6942
  `
@@ -7363,23 +6945,14 @@ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
7363
6945
  function getSamplerShaderWGSL(func) {
7364
6946
  return (
7365
6947
  /* wgsl */
7366
- `// Binding 0:1 is reserved for shader passes
7367
- @group(0) @binding(0) var<uniform> brightnessContrast : brightnessContrastUniforms;
7368
- @group(0) @binding(1) var texture: texture_2d<f32>;
7369
- @group(0) @binding(2) var sampler: sampler;
7370
-
7371
- struct FragmentInputs = {
7372
- @location(0) fragUV: vec2f,
7373
- @location(1) fragPosition: vec4f,
7374
- @location(2) fragCoordinate: vec4f
7375
- };
6948
+ `@group(0) @binding(0) var sourceTexture: texture_2d<f32>;
6949
+ @group(0) @binding(2) var sourceTextureSampler: sampler;
7376
6950
 
7377
6951
  @fragment
7378
6952
  fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
7379
- let texSize = vec2f(textureDimensions(texture, 0));
7380
- var fragColor = textureSample(texture, sampler, fragUV);
7381
- fragColor = ${func}(fragColor, texSize, texCoord);
7382
- return fragColor;
6953
+ let texCoord = inputs.coordinate;
6954
+ let texSize = vec2f(textureDimensions(sourceTexture));
6955
+ return ${func}(sourceTexture, sourceTextureSampler, texSize, texCoord);
7383
6956
  }
7384
6957
  `
7385
6958
  );
@@ -7438,12 +7011,10 @@ void main() {
7438
7011
  shaderInputs;
7439
7012
  passRenderers;
7440
7013
  swapFramebuffers;
7441
- /** For rendering to the screen */
7442
- clipSpace;
7443
7014
  textureModel;
7444
7015
  constructor(device, props) {
7445
7016
  this.device = device;
7446
- props.shaderPasses.map((shaderPass) => (0, import_shadertools6.initializeShaderModule)(shaderPass));
7017
+ props.shaderPasses.map((shaderPass) => (0, import_shadertools5.initializeShaderModule)(shaderPass));
7447
7018
  const modules = props.shaderPasses.reduce(
7448
7019
  (object, shaderPass) => ({ ...object, [shaderPass.name]: shaderPass }),
7449
7020
  {}
@@ -7458,33 +7029,6 @@ void main() {
7458
7029
  this.textureModel = new BackgroundTextureModel(device, {
7459
7030
  backgroundTexture: this.swapFramebuffers.current.colorAttachments[0].texture
7460
7031
  });
7461
- this.clipSpace = new ClipSpace(device, {
7462
- source: (
7463
- /* wgsl */
7464
- ` @group(0) @binding(0) var sourceTexture: texture_2d<f32>;
7465
- @group(0) @binding(1) var sourceTextureSampler: sampler;
7466
-
7467
- @fragment
7468
- fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
7469
- let texCoord: vec2<f32> = inputs.coordinate;
7470
- return textureSample(sourceTexture, sourceTextureSampler, texCoord);
7471
- }
7472
- `
7473
- ),
7474
- fs: (
7475
- /* glsl */
7476
- `#version 300 es
7477
-
7478
- uniform sampler2D sourceTexture;
7479
- in vec2 uv;
7480
- out vec4 fragColor;
7481
-
7482
- void main() {
7483
- fragColor = texture(sourceTexture, uv);
7484
- }
7485
- `
7486
- )
7487
- });
7488
7032
  this.passRenderers = props.shaderPasses.map((shaderPass) => new PassRenderer(device, shaderPass));
7489
7033
  }
7490
7034
  /** Destroys resources created by this ShaderPassRenderer */
@@ -7493,7 +7037,6 @@ void main() {
7493
7037
  subPassRenderer.destroy();
7494
7038
  }
7495
7039
  this.swapFramebuffers.destroy();
7496
- this.clipSpace.destroy();
7497
7040
  this.textureModel.destroy();
7498
7041
  }
7499
7042
  resize(size) {
@@ -7505,15 +7048,15 @@ void main() {
7505
7048
  if (!outputTexture) {
7506
7049
  return false;
7507
7050
  }
7508
- const framebuffer = this.device.getDefaultCanvasContext().getCurrentFramebuffer({ depthStencilAttachment: false });
7051
+ const framebuffer = this.device.getDefaultCanvasContext().getCurrentFramebuffer({ depthStencilFormat: false });
7509
7052
  const renderPass = this.device.beginRenderPass({
7510
7053
  id: "shader-pass-renderer-to-screen",
7511
7054
  framebuffer,
7512
7055
  // clearColor: [1, 1, 0, 1],
7513
- clearDepth: 1
7056
+ clearDepth: false
7514
7057
  });
7515
- this.clipSpace.setBindings({ sourceTexture: outputTexture });
7516
- this.clipSpace.draw(renderPass);
7058
+ this.textureModel.setProps({ backgroundTexture: outputTexture });
7059
+ this.textureModel.draw(renderPass);
7517
7060
  renderPass.end();
7518
7061
  return true;
7519
7062
  }
@@ -7618,6 +7161,221 @@ void main() {
7618
7161
  }
7619
7162
  };
7620
7163
 
7164
+ // src/compute/computation.ts
7165
+ var import_core17 = __toESM(require_core(), 1);
7166
+ var import_shadertools6 = __toESM(require_shadertools(), 1);
7167
+ var LOG_DRAW_PRIORITY2 = 2;
7168
+ var LOG_DRAW_TIMEOUT2 = 1e4;
7169
+ var _Computation = class {
7170
+ device;
7171
+ id;
7172
+ pipelineFactory;
7173
+ shaderFactory;
7174
+ userData = {};
7175
+ /** Bindings (textures, samplers, uniform buffers) */
7176
+ bindings = {};
7177
+ /** The underlying GPU pipeline. */
7178
+ pipeline;
7179
+ /** Assembled compute shader source */
7180
+ source;
7181
+ /** the underlying compiled compute shader */
7182
+ // @ts-ignore Set in function called from constructor
7183
+ shader;
7184
+ /** ShaderInputs instance */
7185
+ shaderInputs;
7186
+ // @ts-ignore Set in function called from constructor
7187
+ _uniformStore;
7188
+ _pipelineNeedsUpdate = "newly created";
7189
+ _getModuleUniforms;
7190
+ props;
7191
+ _destroyed = false;
7192
+ constructor(device, props) {
7193
+ if (device.type !== "webgpu") {
7194
+ throw new Error("Computation is only supported in WebGPU");
7195
+ }
7196
+ this.props = { ..._Computation.defaultProps, ...props };
7197
+ props = this.props;
7198
+ this.id = props.id || uid("model");
7199
+ this.device = device;
7200
+ Object.assign(this.userData, props.userData);
7201
+ const moduleMap = Object.fromEntries(
7202
+ this.props.modules?.map((module) => [module.name, module]) || []
7203
+ );
7204
+ this.shaderInputs = props.shaderInputs || new ShaderInputs(moduleMap);
7205
+ this.setShaderInputs(this.shaderInputs);
7206
+ this.props.shaderLayout ||= device.getShaderLayout(this.props.source);
7207
+ const platformInfo = getPlatformInfo2(device);
7208
+ const modules = (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
7209
+ this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
7210
+ this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
7211
+ const { source: source3, getUniforms: getUniforms2 } = this.props.shaderAssembler.assembleWGSLShader({
7212
+ platformInfo,
7213
+ ...this.props,
7214
+ modules
7215
+ });
7216
+ this.source = source3;
7217
+ this._getModuleUniforms = getUniforms2;
7218
+ this.pipeline = this._updatePipeline();
7219
+ if (props.bindings) {
7220
+ this.setBindings(props.bindings);
7221
+ }
7222
+ Object.seal(this);
7223
+ }
7224
+ destroy() {
7225
+ if (this._destroyed)
7226
+ return;
7227
+ this.pipelineFactory.release(this.pipeline);
7228
+ this.shaderFactory.release(this.shader);
7229
+ this._uniformStore.destroy();
7230
+ this._destroyed = true;
7231
+ }
7232
+ // Draw call
7233
+ predraw() {
7234
+ this.updateShaderInputs();
7235
+ }
7236
+ dispatch(computePass, x, y, z) {
7237
+ try {
7238
+ this._logDrawCallStart();
7239
+ this.pipeline = this._updatePipeline();
7240
+ this.pipeline.setBindings(this.bindings);
7241
+ computePass.setPipeline(this.pipeline);
7242
+ computePass.setBindings([]);
7243
+ computePass.dispatch(x, y, z);
7244
+ } finally {
7245
+ this._logDrawCallEnd();
7246
+ }
7247
+ }
7248
+ // Update fixed fields (can trigger pipeline rebuild)
7249
+ // Update dynamic fields
7250
+ /**
7251
+ * Updates the vertex count (used in draw calls)
7252
+ * @note Any attributes with stepMode=vertex need to be at least this big
7253
+ */
7254
+ setVertexCount(vertexCount) {
7255
+ }
7256
+ /**
7257
+ * Updates the instance count (used in draw calls)
7258
+ * @note Any attributes with stepMode=instance need to be at least this big
7259
+ */
7260
+ setInstanceCount(instanceCount) {
7261
+ }
7262
+ setShaderInputs(shaderInputs) {
7263
+ this.shaderInputs = shaderInputs;
7264
+ this._uniformStore = new import_core17.UniformStore(this.shaderInputs.modules);
7265
+ for (const moduleName of Object.keys(this.shaderInputs.modules)) {
7266
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
7267
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
7268
+ }
7269
+ }
7270
+ /**
7271
+ * Updates shader module settings (which results in uniforms being set)
7272
+ */
7273
+ setShaderModuleProps(props) {
7274
+ const uniforms = this._getModuleUniforms(props);
7275
+ const keys = Object.keys(uniforms).filter((k) => {
7276
+ const uniform = uniforms[k];
7277
+ return !isNumericArray(uniform) && typeof uniform !== "number" && typeof uniform !== "boolean";
7278
+ });
7279
+ const bindings = {};
7280
+ for (const k of keys) {
7281
+ bindings[k] = uniforms[k];
7282
+ delete uniforms[k];
7283
+ }
7284
+ }
7285
+ updateShaderInputs() {
7286
+ this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
7287
+ }
7288
+ /**
7289
+ * Sets bindings (textures, samplers, uniform buffers)
7290
+ */
7291
+ setBindings(bindings) {
7292
+ Object.assign(this.bindings, bindings);
7293
+ }
7294
+ _setPipelineNeedsUpdate(reason) {
7295
+ this._pipelineNeedsUpdate = this._pipelineNeedsUpdate || reason;
7296
+ }
7297
+ _updatePipeline() {
7298
+ if (this._pipelineNeedsUpdate) {
7299
+ let prevShader = null;
7300
+ if (this.pipeline) {
7301
+ import_core17.log.log(
7302
+ 1,
7303
+ `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
7304
+ )();
7305
+ prevShader = this.shader;
7306
+ }
7307
+ this._pipelineNeedsUpdate = false;
7308
+ this.shader = this.shaderFactory.createShader({
7309
+ id: `${this.id}-fragment`,
7310
+ stage: "compute",
7311
+ source: this.source,
7312
+ debugShaders: this.props.debugShaders
7313
+ });
7314
+ this.pipeline = this.pipelineFactory.createComputePipeline({
7315
+ ...this.props,
7316
+ shader: this.shader
7317
+ });
7318
+ if (prevShader) {
7319
+ this.shaderFactory.release(prevShader);
7320
+ }
7321
+ }
7322
+ return this.pipeline;
7323
+ }
7324
+ /** Throttle draw call logging */
7325
+ _lastLogTime = 0;
7326
+ _logOpen = false;
7327
+ _logDrawCallStart() {
7328
+ const logDrawTimeout = import_core17.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT2;
7329
+ if (import_core17.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
7330
+ return;
7331
+ }
7332
+ this._lastLogTime = Date.now();
7333
+ this._logOpen = true;
7334
+ import_core17.log.group(LOG_DRAW_PRIORITY2, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core17.log.level <= 2 })();
7335
+ }
7336
+ _logDrawCallEnd() {
7337
+ if (this._logOpen) {
7338
+ const uniformTable = this.shaderInputs.getDebugTable();
7339
+ import_core17.log.table(LOG_DRAW_PRIORITY2, uniformTable)();
7340
+ import_core17.log.groupEnd(LOG_DRAW_PRIORITY2)();
7341
+ this._logOpen = false;
7342
+ }
7343
+ }
7344
+ _drawCount = 0;
7345
+ // TODO - fix typing of luma data types
7346
+ _getBufferOrConstantValues(attribute, dataType) {
7347
+ const TypedArrayConstructor = (0, import_core17.getTypedArrayConstructor)(dataType);
7348
+ const typedArray = attribute instanceof import_core17.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
7349
+ return typedArray.toString();
7350
+ }
7351
+ };
7352
+ var Computation = _Computation;
7353
+ __publicField(Computation, "defaultProps", {
7354
+ ...import_core17.ComputePipeline.defaultProps,
7355
+ id: "unnamed",
7356
+ handle: void 0,
7357
+ userData: {},
7358
+ source: "",
7359
+ modules: [],
7360
+ defines: {},
7361
+ bindings: void 0,
7362
+ shaderInputs: void 0,
7363
+ pipelineFactory: void 0,
7364
+ shaderFactory: void 0,
7365
+ shaderAssembler: import_shadertools6.ShaderAssembler.getDefaultShaderAssembler(),
7366
+ debugShaders: void 0
7367
+ });
7368
+ function getPlatformInfo2(device) {
7369
+ return {
7370
+ type: device.type,
7371
+ shaderLanguage: device.info.shadingLanguage,
7372
+ shaderLanguageVersion: device.info.shadingLanguageVersion,
7373
+ gpu: device.info.gpu,
7374
+ // HACK - we pretend that the DeviceFeatures is a Set, it has a similar API
7375
+ features: device.features
7376
+ };
7377
+ }
7378
+
7621
7379
  // src/modules/picking/picking-uniforms.ts
7622
7380
  var DEFAULT_HIGHLIGHT_COLOR = [0, 1, 1, 1];
7623
7381
  var INVALID_INDEX = -1;
@@ -7810,12 +7568,6 @@ const INDEX_PICKING_MODE_INSTANCE = 0;
7810
7568
  const INDEX_PICKING_MODE_CUSTOM = 1;
7811
7569
  const INDEX_PICKING_INVALID_INDEX = ${INVALID_INDEX}; // 2^32 - 1
7812
7570
 
7813
- struct indexPickingFragmentInputs = {
7814
- objectIndex: int32;
7815
- };
7816
-
7817
- let indexPickingFragmentInputs: indexPickingFragmentInputs;
7818
-
7819
7571
  /**
7820
7572
  * Vertex shaders should call this function to set the object index.
7821
7573
  * If using instance or vertex mode, argument will be ignored, 0 can be supplied.