@luma.gl/engine 9.0.0-beta.5 → 9.0.0-beta.7

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 (115) hide show
  1. package/dist/animation/timeline.d.ts.map +1 -1
  2. package/dist/animation/timeline.js +3 -3
  3. package/dist/animation-loop/animation-loop-template.d.ts +1 -1
  4. package/dist/animation-loop/animation-loop-template.d.ts.map +1 -1
  5. package/dist/animation-loop/animation-loop-template.js +3 -1
  6. package/dist/animation-loop/animation-loop.d.ts +2 -2
  7. package/dist/animation-loop/animation-loop.d.ts.map +1 -1
  8. package/dist/animation-loop/animation-loop.js +14 -6
  9. package/dist/animation-loop/animation-props.d.ts +2 -2
  10. package/dist/animation-loop/animation-props.d.ts.map +1 -1
  11. package/dist/animation-loop/make-animation-loop.d.ts +2 -2
  12. package/dist/animation-loop/make-animation-loop.d.ts.map +1 -1
  13. package/dist/animation-loop/make-animation-loop.js +4 -2
  14. package/dist/computation.d.ts +95 -0
  15. package/dist/computation.d.ts.map +1 -0
  16. package/dist/computation.js +248 -0
  17. package/dist/debug/copy-texture-to-image.d.ts.map +1 -1
  18. package/dist/debug/copy-texture-to-image.js +5 -2
  19. package/dist/debug/debug-framebuffer.d.ts.map +1 -1
  20. package/dist/debug/debug-framebuffer.js +0 -1
  21. package/dist/debug/pixel-data-utils.d.ts.map +1 -1
  22. package/dist/debug/pixel-data-utils.js +2 -1
  23. package/dist/dist.dev.js +713 -329
  24. package/dist/geometries/cone-geometry.d.ts +1 -1
  25. package/dist/geometries/cone-geometry.d.ts.map +1 -1
  26. package/dist/geometries/cone-geometry.js +1 -1
  27. package/dist/geometries/cube-geometry.d.ts +1 -1
  28. package/dist/geometries/cube-geometry.d.ts.map +1 -1
  29. package/dist/geometries/cube-geometry.js +16 -14
  30. package/dist/geometries/cylinder-geometry.d.ts +1 -1
  31. package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
  32. package/dist/geometries/cylinder-geometry.js +1 -1
  33. package/dist/geometries/ico-sphere-geometry.d.ts +1 -1
  34. package/dist/geometries/ico-sphere-geometry.d.ts.map +1 -1
  35. package/dist/geometries/ico-sphere-geometry.js +1 -1
  36. package/dist/geometries/plane-geometry.d.ts +1 -1
  37. package/dist/geometries/plane-geometry.d.ts.map +1 -1
  38. package/dist/geometries/plane-geometry.js +2 -2
  39. package/dist/geometries/sphere-geometry.d.ts +1 -1
  40. package/dist/geometries/sphere-geometry.d.ts.map +1 -1
  41. package/dist/geometries/sphere-geometry.js +1 -1
  42. package/dist/geometries/truncated-cone-geometry.d.ts +1 -1
  43. package/dist/geometries/truncated-cone-geometry.d.ts.map +1 -1
  44. package/dist/geometries/truncated-cone-geometry.js +1 -1
  45. package/dist/geometry/geometry-table.d.ts.map +1 -1
  46. package/dist/geometry/geometry-table.js +3 -0
  47. package/dist/geometry/geometry.d.ts.map +1 -1
  48. package/dist/geometry/geometry.js +3 -0
  49. package/dist/geometry/gpu-geometry.d.ts +1 -1
  50. package/dist/geometry/gpu-geometry.d.ts.map +1 -1
  51. package/dist/geometry/gpu-geometry.js +4 -5
  52. package/dist/index.cjs +661 -291
  53. package/dist/index.cjs.map +4 -4
  54. package/dist/index.d.ts +43 -40
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +25 -23
  57. package/dist/lib/clip-space.d.ts +1 -1
  58. package/dist/lib/clip-space.d.ts.map +1 -1
  59. package/dist/lib/clip-space.js +8 -10
  60. package/dist/lib/pipeline-factory.d.ts +10 -6
  61. package/dist/lib/pipeline-factory.d.ts.map +1 -1
  62. package/dist/lib/pipeline-factory.js +47 -22
  63. package/dist/lib/shader-factory.d.ts +17 -0
  64. package/dist/lib/shader-factory.d.ts.map +1 -0
  65. package/dist/lib/shader-factory.js +46 -0
  66. package/dist/model/model.d.ts +58 -45
  67. package/dist/model/model.d.ts.map +1 -1
  68. package/dist/model/model.js +213 -120
  69. package/dist/scenegraph/group-node.d.ts +1 -1
  70. package/dist/scenegraph/group-node.d.ts.map +1 -1
  71. package/dist/scenegraph/group-node.js +10 -5
  72. package/dist/scenegraph/model-node.d.ts +3 -3
  73. package/dist/scenegraph/model-node.d.ts.map +1 -1
  74. package/dist/scenegraph/model-node.js +2 -2
  75. package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
  76. package/dist/shader-inputs.d.ts.map +1 -1
  77. package/dist/shader-inputs.js +3 -0
  78. package/dist/transform/buffer-transform.d.ts +1 -1
  79. package/dist/transform/buffer-transform.d.ts.map +1 -1
  80. package/dist/transform/buffer-transform.js +7 -6
  81. package/dist/transform/texture-transform.d.ts +1 -1
  82. package/dist/transform/texture-transform.d.ts.map +1 -1
  83. package/dist/transform/texture-transform.js +10 -8
  84. package/dist.min.js +2 -2
  85. package/package.json +2 -2
  86. package/src/animation/timeline.ts +20 -20
  87. package/src/animation-loop/animation-loop-template.ts +10 -8
  88. package/src/animation-loop/animation-loop.ts +20 -10
  89. package/src/animation-loop/animation-props.ts +1 -1
  90. package/src/animation-loop/make-animation-loop.ts +17 -8
  91. package/src/computation.ts +346 -0
  92. package/src/debug/copy-texture-to-image.ts +8 -6
  93. package/src/debug/debug-framebuffer.ts +16 -3
  94. package/src/debug/debug-shader-layout.ts +1 -1
  95. package/src/debug/pixel-data-utils.ts +3 -6
  96. package/src/geometries/cube-geometry.ts +17 -13
  97. package/src/geometries/ico-sphere-geometry.ts +1 -1
  98. package/src/geometries/plane-geometry.ts +1 -1
  99. package/src/geometries/sphere-geometry.ts +1 -1
  100. package/src/geometries/truncated-cone-geometry.ts +2 -1
  101. package/src/geometry/geometry-table.ts +9 -6
  102. package/src/geometry/geometry-utils.ts +1 -1
  103. package/src/geometry/geometry.ts +9 -6
  104. package/src/geometry/gpu-geometry.ts +18 -11
  105. package/src/index.ts +3 -0
  106. package/src/lib/clip-space.ts +14 -18
  107. package/src/lib/pipeline-factory.ts +62 -28
  108. package/src/lib/shader-factory.ts +57 -0
  109. package/src/model/model.ts +249 -146
  110. package/src/scenegraph/group-node.ts +14 -10
  111. package/src/scenegraph/model-node.ts +2 -2
  112. package/src/scenegraph/scenegraph-node.ts +2 -2
  113. package/src/shader-inputs.ts +19 -12
  114. package/src/transform/buffer-transform.ts +15 -7
  115. package/src/transform/texture-transform.ts +14 -13
package/dist/dist.dev.js CHANGED
@@ -63,6 +63,7 @@ var __exports__ = (() => {
63
63
  AnimationLoopTemplate: () => AnimationLoopTemplate,
64
64
  BufferTransform: () => BufferTransform,
65
65
  ClipSpace: () => ClipSpace,
66
+ Computation: () => Computation,
66
67
  ConeGeometry: () => ConeGeometry,
67
68
  CubeGeometry: () => CubeGeometry,
68
69
  CylinderGeometry: () => CylinderGeometry,
@@ -76,6 +77,7 @@ var __exports__ = (() => {
76
77
  PipelineFactory: () => PipelineFactory,
77
78
  PlaneGeometry: () => PlaneGeometry,
78
79
  ScenegraphNode: () => ScenegraphNode,
80
+ ShaderFactory: () => ShaderFactory,
79
81
  SphereGeometry: () => SphereGeometry,
80
82
  TextureTransform: () => TextureTransform,
81
83
  Timeline: () => Timeline,
@@ -276,8 +278,6 @@ var __exports__ = (() => {
276
278
  // ../../node_modules/@probe.gl/stats/dist/lib/stat.js
277
279
  var Stat = class {
278
280
  constructor(name, type) {
279
- this.name = void 0;
280
- this.type = void 0;
281
281
  this.sampleSize = 1;
282
282
  this.time = 0;
283
283
  this.count = 0;
@@ -312,26 +312,31 @@ var __exports__ = (() => {
312
312
  this.sampleSize = samples;
313
313
  return this;
314
314
  }
315
+ /** Call to increment count (+1) */
315
316
  incrementCount() {
316
317
  this.addCount(1);
317
318
  return this;
318
319
  }
320
+ /** Call to decrement count (-1) */
319
321
  decrementCount() {
320
322
  this.subtractCount(1);
321
323
  return this;
322
324
  }
325
+ /** Increase count */
323
326
  addCount(value) {
324
327
  this._count += value;
325
328
  this._samples++;
326
329
  this._checkSampling();
327
330
  return this;
328
331
  }
332
+ /** Decrease count */
329
333
  subtractCount(value) {
330
334
  this._count -= value;
331
335
  this._samples++;
332
336
  this._checkSampling();
333
337
  return this;
334
338
  }
339
+ /** Add an arbitrary timing and bump the count */
335
340
  addTime(time) {
336
341
  this._time += time;
337
342
  this.lastTiming = time;
@@ -339,11 +344,13 @@ var __exports__ = (() => {
339
344
  this._checkSampling();
340
345
  return this;
341
346
  }
347
+ /** Start a timer */
342
348
  timeStart() {
343
349
  this._startTime = getHiResTimestamp();
344
350
  this._timerPending = true;
345
351
  return this;
346
352
  }
353
+ /** End a timer. Adds to time and bumps the timing count. */
347
354
  timeEnd() {
348
355
  if (!this._timerPending) {
349
356
  return this;
@@ -356,18 +363,22 @@ var __exports__ = (() => {
356
363
  getSampleAverageCount() {
357
364
  return this.sampleSize > 0 ? this.lastSampleCount / this.sampleSize : 0;
358
365
  }
366
+ /** Calculate average time / count for the previous window */
359
367
  getSampleAverageTime() {
360
368
  return this.sampleSize > 0 ? this.lastSampleTime / this.sampleSize : 0;
361
369
  }
370
+ /** Calculate counts per second for the previous window */
362
371
  getSampleHz() {
363
372
  return this.lastSampleTime > 0 ? this.sampleSize / (this.lastSampleTime / 1e3) : 0;
364
373
  }
365
374
  getAverageCount() {
366
375
  return this.samples > 0 ? this.count / this.samples : 0;
367
376
  }
377
+ /** Calculate average time / count */
368
378
  getAverageTime() {
369
379
  return this.samples > 0 ? this.time / this.samples : 0;
370
380
  }
381
+ /** Calculate counts per second */
371
382
  getHz() {
372
383
  return this.time > 0 ? this.samples / (this.time / 1e3) : 0;
373
384
  }
@@ -388,23 +399,20 @@ var __exports__ = (() => {
388
399
  // ../../node_modules/@probe.gl/stats/dist/lib/stats.js
389
400
  var Stats = class {
390
401
  constructor(options) {
391
- this.id = void 0;
392
402
  this.stats = {};
393
403
  this.id = options.id;
394
404
  this.stats = {};
395
405
  this._initializeStats(options.stats);
396
406
  Object.seal(this);
397
407
  }
398
- get(name) {
399
- let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "count";
400
- return this._getOrCreate({
401
- name,
402
- type
403
- });
408
+ /** Acquire a stat. Create if it doesn't exist. */
409
+ get(name, type = "count") {
410
+ return this._getOrCreate({ name, type });
404
411
  }
405
412
  get size() {
406
413
  return Object.keys(this.stats).length;
407
414
  }
415
+ /** Reset all stats */
408
416
  reset() {
409
417
  for (const stat of Object.values(this.stats)) {
410
418
  stat.reset();
@@ -428,15 +436,11 @@ var __exports__ = (() => {
428
436
  });
429
437
  return table;
430
438
  }
431
- _initializeStats() {
432
- let stats = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : [];
439
+ _initializeStats(stats = []) {
433
440
  stats.forEach((stat) => this._getOrCreate(stat));
434
441
  }
435
442
  _getOrCreate(stat) {
436
- const {
437
- name,
438
- type
439
- } = stat;
443
+ const { name, type } = stat;
440
444
  let result = this.stats[name];
441
445
  if (!result) {
442
446
  if (stat instanceof Stat) {
@@ -785,7 +789,14 @@ var __exports__ = (() => {
785
789
  /** Default viewport setup */
786
790
  _resizeViewport() {
787
791
  if (this.props.autoResizeViewport && this.device.gl) {
788
- this.device.gl.viewport(0, 0, this.device.gl.drawingBufferWidth, this.device.gl.drawingBufferHeight);
792
+ this.device.gl.viewport(
793
+ 0,
794
+ 0,
795
+ // @ts-expect-error Expose canvasContext
796
+ this.device.gl.drawingBufferWidth,
797
+ // @ts-expect-error Expose canvasContext
798
+ this.device.gl.drawingBufferHeight
799
+ );
789
800
  }
790
801
  }
791
802
  /**
@@ -844,101 +855,14 @@ var __exports__ = (() => {
844
855
  }
845
856
 
846
857
  // src/model/model.ts
847
- var import_core7 = __toESM(require_core(), 1);
848
858
  var import_core8 = __toESM(require_core(), 1);
849
859
  var import_core9 = __toESM(require_core(), 1);
860
+ var import_core10 = __toESM(require_core(), 1);
861
+ var import_core11 = __toESM(require_core(), 1);
850
862
  var import_shadertools2 = __toESM(require_shadertools(), 1);
851
863
 
852
- // src/shader-inputs.ts
853
- var import_core4 = __toESM(require_core(), 1);
854
- var import_shadertools = __toESM(require_shadertools(), 1);
855
- var ShaderInputs = class {
856
- /**
857
- * The map of modules
858
- * @todo should should this include the resolved dependencies?
859
- */
860
- modules;
861
- /** Stores the uniform values for each module */
862
- moduleUniforms;
863
- /** Stores the uniform bindings for each module */
864
- moduleBindings;
865
- /** Tracks if uniforms have changed */
866
- moduleUniformsChanged;
867
- /**
868
- * Create a new UniformStore instance
869
- * @param modules
870
- */
871
- constructor(modules) {
872
- const allModules = (0, import_shadertools._resolveModules)(Object.values(modules));
873
- import_core4.log.log(1, "Creating ShaderInputs with modules", allModules.map((m) => m.name))();
874
- this.modules = modules;
875
- this.moduleUniforms = {};
876
- this.moduleBindings = {};
877
- for (const [name, module] of Object.entries(modules)) {
878
- const moduleName = name;
879
- this.moduleUniforms[moduleName] = module.defaultUniforms || {};
880
- this.moduleBindings[moduleName] = {};
881
- }
882
- }
883
- /** Destroy */
884
- destroy() {
885
- }
886
- /**
887
- * Set module props
888
- */
889
- setProps(props) {
890
- for (const name of Object.keys(props)) {
891
- const moduleName = name;
892
- const moduleProps = props[moduleName];
893
- const module = this.modules[moduleName];
894
- if (!module) {
895
- import_core4.log.warn(`Module ${name} not found`)();
896
- continue;
897
- }
898
- const oldUniforms = this.moduleUniforms[moduleName];
899
- const uniforms = module.getUniforms?.(moduleProps, this.moduleUniforms[moduleName]) || moduleProps;
900
- this.moduleUniforms[moduleName] = { ...oldUniforms, ...uniforms };
901
- }
902
- }
903
- /** Merges all bindings for the shader (from the various modules) */
904
- // getUniformBlocks(): Record<string, Texture | Sampler> {
905
- // return this.moduleUniforms;
906
- // }
907
- /**
908
- * Return the map of modules
909
- * @todo should should this include the resolved dependencies?
910
- */
911
- getModules() {
912
- return Object.values(this.modules);
913
- }
914
- /** Get all uniform values for all modules */
915
- getUniformValues() {
916
- return this.moduleUniforms;
917
- }
918
- /** Merges all bindings for the shader (from the various modules) */
919
- getBindings() {
920
- const bindings = {};
921
- for (const moduleBindings of Object.values(this.moduleBindings)) {
922
- Object.assign(bindings, moduleBindings);
923
- }
924
- return bindings;
925
- }
926
- getDebugTable() {
927
- const table = {};
928
- for (const [moduleName, module] of Object.entries(this.moduleUniforms)) {
929
- for (const [key, value] of Object.entries(module)) {
930
- table[`${moduleName}.${key}`] = {
931
- type: this.modules[moduleName].uniformTypes?.[key],
932
- value: String(value)
933
- };
934
- }
935
- }
936
- return table;
937
- }
938
- };
939
-
940
864
  // src/geometry/gpu-geometry.ts
941
- var import_core5 = __toESM(require_core(), 1);
865
+ var import_core4 = __toESM(require_core(), 1);
942
866
  var GPUGeometry = class {
943
867
  id;
944
868
  userData = {};
@@ -949,22 +873,21 @@ var __exports__ = (() => {
949
873
  indices;
950
874
  attributes;
951
875
  constructor(props) {
952
- this.id = props.id || (0, import_core5.uid)("geometry");
876
+ this.id = props.id || (0, import_core4.uid)("geometry");
953
877
  this.topology = props.topology;
954
878
  this.indices = props.indices || null;
955
879
  this.attributes = props.attributes;
956
880
  this.vertexCount = props.vertexCount;
957
881
  this.bufferLayout = props.bufferLayout || [];
958
882
  if (this.indices) {
959
- (0, import_core5.assert)(this.indices.usage === import_core5.Buffer.INDEX);
883
+ (0, import_core4.assert)(this.indices.usage === import_core4.Buffer.INDEX);
960
884
  }
961
885
  }
962
886
  destroy() {
963
- this.indices.destroy();
964
- this.attributes.positions.destroy();
965
- this.attributes.normals.destroy();
966
- this.attributes.texCoords.destroy();
967
- this.attributes.colors?.destroy();
887
+ this.indices?.destroy();
888
+ for (const attribute of Object.values(this.attributes)) {
889
+ attribute.destroy();
890
+ }
968
891
  }
969
892
  getVertexCount() {
970
893
  return this.vertexCount;
@@ -999,7 +922,7 @@ var __exports__ = (() => {
999
922
  return void 0;
1000
923
  }
1001
924
  const data = geometry.indices.value;
1002
- return device.createBuffer({ usage: import_core5.Buffer.INDEX, data });
925
+ return device.createBuffer({ usage: import_core4.Buffer.INDEX, data });
1003
926
  }
1004
927
  function getAttributeBuffersFromGeometry(device, geometry) {
1005
928
  const bufferLayout = [];
@@ -1022,20 +945,113 @@ var __exports__ = (() => {
1022
945
  }
1023
946
  attributes[name] = device.createBuffer({ data: attribute.value, id: `${attributeName}-buffer` });
1024
947
  const { value, size, normalized } = attribute;
1025
- bufferLayout.push({ name, format: (0, import_core5.getVertexFormatFromAttribute)(value, size, normalized) });
948
+ bufferLayout.push({ name, format: (0, import_core4.getVertexFormatFromAttribute)(value, size, normalized) });
1026
949
  }
1027
950
  const vertexCount = geometry._calculateVertexCount(geometry.attributes, geometry.indices);
1028
951
  return { attributes, bufferLayout, vertexCount };
1029
952
  }
1030
953
 
954
+ // src/shader-inputs.ts
955
+ var import_core5 = __toESM(require_core(), 1);
956
+ var import_shadertools = __toESM(require_shadertools(), 1);
957
+ var ShaderInputs = class {
958
+ /**
959
+ * The map of modules
960
+ * @todo should should this include the resolved dependencies?
961
+ */
962
+ modules;
963
+ /** Stores the uniform values for each module */
964
+ moduleUniforms;
965
+ /** Stores the uniform bindings for each module */
966
+ moduleBindings;
967
+ /** Tracks if uniforms have changed */
968
+ moduleUniformsChanged;
969
+ /**
970
+ * Create a new UniformStore instance
971
+ * @param modules
972
+ */
973
+ constructor(modules) {
974
+ const allModules = (0, import_shadertools._resolveModules)(Object.values(modules));
975
+ import_core5.log.log(
976
+ 1,
977
+ "Creating ShaderInputs with modules",
978
+ allModules.map((m) => m.name)
979
+ )();
980
+ this.modules = modules;
981
+ this.moduleUniforms = {};
982
+ this.moduleBindings = {};
983
+ for (const [name, module] of Object.entries(modules)) {
984
+ const moduleName = name;
985
+ this.moduleUniforms[moduleName] = module.defaultUniforms || {};
986
+ this.moduleBindings[moduleName] = {};
987
+ }
988
+ }
989
+ /** Destroy */
990
+ destroy() {
991
+ }
992
+ /**
993
+ * Set module props
994
+ */
995
+ setProps(props) {
996
+ for (const name of Object.keys(props)) {
997
+ const moduleName = name;
998
+ const moduleProps = props[moduleName];
999
+ const module = this.modules[moduleName];
1000
+ if (!module) {
1001
+ import_core5.log.warn(`Module ${name} not found`)();
1002
+ continue;
1003
+ }
1004
+ const oldUniforms = this.moduleUniforms[moduleName];
1005
+ const uniforms = module.getUniforms?.(moduleProps, this.moduleUniforms[moduleName]) || moduleProps;
1006
+ this.moduleUniforms[moduleName] = { ...oldUniforms, ...uniforms };
1007
+ }
1008
+ }
1009
+ /** Merges all bindings for the shader (from the various modules) */
1010
+ // getUniformBlocks(): Record<string, Texture | Sampler> {
1011
+ // return this.moduleUniforms;
1012
+ // }
1013
+ /**
1014
+ * Return the map of modules
1015
+ * @todo should should this include the resolved dependencies?
1016
+ */
1017
+ getModules() {
1018
+ return Object.values(this.modules);
1019
+ }
1020
+ /** Get all uniform values for all modules */
1021
+ getUniformValues() {
1022
+ return this.moduleUniforms;
1023
+ }
1024
+ /** Merges all bindings for the shader (from the various modules) */
1025
+ getBindings() {
1026
+ const bindings = {};
1027
+ for (const moduleBindings of Object.values(this.moduleBindings)) {
1028
+ Object.assign(bindings, moduleBindings);
1029
+ }
1030
+ return bindings;
1031
+ }
1032
+ getDebugTable() {
1033
+ const table = {};
1034
+ for (const [moduleName, module] of Object.entries(this.moduleUniforms)) {
1035
+ for (const [key, value] of Object.entries(module)) {
1036
+ table[`${moduleName}.${key}`] = {
1037
+ type: this.modules[moduleName].uniformTypes?.[key],
1038
+ value: String(value)
1039
+ };
1040
+ }
1041
+ }
1042
+ return table;
1043
+ }
1044
+ };
1045
+
1031
1046
  // src/lib/pipeline-factory.ts
1032
1047
  var import_core6 = __toESM(require_core(), 1);
1033
1048
  var _PipelineFactory = class {
1034
1049
  device;
1035
1050
  _hashCounter = 0;
1036
1051
  _hashes = {};
1037
- _useCounts = {};
1038
- _pipelineCache = {};
1052
+ _renderPipelineCache = {};
1053
+ _computePipelineCache = {};
1054
+ /** Get the singleton default pipeline factory for the specified device */
1039
1055
  static getDefaultPipelineFactory(device) {
1040
1056
  device._lumaData.defaultPipelineFactory = device._lumaData.defaultPipelineFactory || new _PipelineFactory(device);
1041
1057
  return device._lumaData.defaultPipelineFactory;
@@ -1043,40 +1059,59 @@ var __exports__ = (() => {
1043
1059
  constructor(device) {
1044
1060
  this.device = device;
1045
1061
  }
1046
- createRenderPipeline(options) {
1047
- const props = { ..._PipelineFactory.defaultProps, ...options };
1048
- const hash = this._hashRenderPipeline({ ...props });
1049
- if (!this._pipelineCache[hash]) {
1050
- const pipeline = this.device.createRenderPipeline({ ...props });
1062
+ /** Return a RenderPipeline matching props. Reuses a similar pipeline if already created. */
1063
+ createRenderPipeline(props) {
1064
+ const allProps = { ...import_core6.RenderPipeline.defaultProps, ...props };
1065
+ const hash = this._hashRenderPipeline(allProps);
1066
+ if (!this._renderPipelineCache[hash]) {
1067
+ const pipeline = this.device.createRenderPipeline({
1068
+ ...allProps,
1069
+ id: allProps.id ? `${allProps.id}-cached` : void 0
1070
+ });
1071
+ pipeline.hash = hash;
1072
+ this._renderPipelineCache[hash] = { pipeline, useCount: 0 };
1073
+ }
1074
+ this._renderPipelineCache[hash].useCount++;
1075
+ return this._renderPipelineCache[hash].pipeline;
1076
+ }
1077
+ createComputePipeline(props) {
1078
+ const allProps = { ...import_core6.ComputePipeline.defaultProps, ...props };
1079
+ const hash = this._hashComputePipeline(allProps);
1080
+ if (!this._computePipelineCache[hash]) {
1081
+ const pipeline = this.device.createComputePipeline({
1082
+ ...allProps,
1083
+ id: allProps.id ? `${allProps.id}-cached` : void 0
1084
+ });
1051
1085
  pipeline.hash = hash;
1052
- this._pipelineCache[hash] = pipeline;
1053
- this._useCounts[hash] = 0;
1086
+ this._computePipelineCache[hash] = { pipeline, useCount: 0 };
1054
1087
  }
1055
- this._useCounts[hash]++;
1056
- return this._pipelineCache[hash];
1088
+ this._computePipelineCache[hash].useCount++;
1089
+ return this._computePipelineCache[hash].pipeline;
1057
1090
  }
1058
1091
  release(pipeline) {
1059
1092
  const hash = pipeline.hash;
1060
- this._useCounts[hash]--;
1061
- if (this._useCounts[hash] === 0) {
1062
- this._pipelineCache[hash].destroy();
1063
- delete this._pipelineCache[hash];
1064
- delete this._useCounts[hash];
1093
+ const cache = pipeline instanceof import_core6.ComputePipeline ? this._computePipelineCache : this._renderPipelineCache;
1094
+ cache[hash].useCount--;
1095
+ if (cache[hash].useCount === 0) {
1096
+ cache[hash].pipeline.destroy();
1097
+ delete cache[hash];
1065
1098
  }
1066
1099
  }
1067
1100
  // PRIVATE
1101
+ _hashComputePipeline(props) {
1102
+ const shaderHash = this._getHash(props.shader.source);
1103
+ return `${shaderHash}`;
1104
+ }
1068
1105
  /** Calculate a hash based on all the inputs for a render pipeline */
1069
1106
  _hashRenderPipeline(props) {
1070
1107
  const vsHash = this._getHash(props.vs.source);
1071
1108
  const fsHash = props.fs ? this._getHash(props.fs.source) : 0;
1072
1109
  const varyingHash = "-";
1073
1110
  const bufferLayoutHash = this._getHash(JSON.stringify(props.bufferLayout));
1074
- switch (this.device.info.type) {
1075
- case "webgpu":
1111
+ switch (this.device.type) {
1112
+ default:
1076
1113
  const parameterHash = this._getHash(JSON.stringify(props.parameters));
1077
1114
  return `${vsHash}/${fsHash}V${varyingHash}T${props.topology}P${parameterHash}BL${bufferLayoutHash}`;
1078
- default:
1079
- return `${vsHash}/${fsHash}V${varyingHash}BL${bufferLayoutHash}`;
1080
1115
  }
1081
1116
  }
1082
1117
  _getHash(key) {
@@ -1089,6 +1124,54 @@ var __exports__ = (() => {
1089
1124
  var PipelineFactory = _PipelineFactory;
1090
1125
  __publicField(PipelineFactory, "defaultProps", { ...import_core6.RenderPipeline.defaultProps });
1091
1126
 
1127
+ // src/lib/shader-factory.ts
1128
+ var import_core7 = __toESM(require_core(), 1);
1129
+ var _ShaderFactory = class {
1130
+ device;
1131
+ _cache = {};
1132
+ /** Returns the default ShaderFactory for the given {@link Device}, creating one if necessary. */
1133
+ static getDefaultShaderFactory(device) {
1134
+ device._lumaData.defaultShaderFactory ||= new _ShaderFactory(device);
1135
+ return device._lumaData.defaultShaderFactory;
1136
+ }
1137
+ /** @internal */
1138
+ constructor(device) {
1139
+ this.device = device;
1140
+ }
1141
+ /** Requests a {@link Shader} from the cache, creating a new Shader only if necessary. */
1142
+ createShader(props) {
1143
+ const key = this._hashShader(props);
1144
+ let cacheEntry = this._cache[key];
1145
+ if (!cacheEntry) {
1146
+ const shader = this.device.createShader({
1147
+ ...props,
1148
+ id: props.id ? `${props.id}-cached` : void 0
1149
+ });
1150
+ this._cache[key] = cacheEntry = { shader, useCount: 0 };
1151
+ }
1152
+ cacheEntry.useCount++;
1153
+ return cacheEntry.shader;
1154
+ }
1155
+ /** Releases a previously-requested {@link Shader}, destroying it if no users remain. */
1156
+ release(shader) {
1157
+ const key = this._hashShader(shader);
1158
+ const cacheEntry = this._cache[key];
1159
+ if (cacheEntry) {
1160
+ cacheEntry.useCount--;
1161
+ if (cacheEntry.useCount === 0) {
1162
+ delete this._cache[key];
1163
+ cacheEntry.shader.destroy();
1164
+ }
1165
+ }
1166
+ }
1167
+ // PRIVATE
1168
+ _hashShader(value) {
1169
+ return `${value.stage}:${value.source}`;
1170
+ }
1171
+ };
1172
+ var ShaderFactory = _ShaderFactory;
1173
+ __publicField(ShaderFactory, "defaultProps", { ...import_core7.Shader.defaultProps });
1174
+
1092
1175
  // src/debug/debug-shader-layout.ts
1093
1176
  function getDebugTableForShaderLayout(layout, name) {
1094
1177
  const table = {};
@@ -1112,7 +1195,14 @@ var __exports__ = (() => {
1112
1195
  // src/debug/debug-framebuffer.ts
1113
1196
  var canvas = null;
1114
1197
  var ctx = null;
1115
- function debugFramebuffer(fbo, { id, minimap, opaque, top = "0", left = "0", rgbaScale = 1 }) {
1198
+ function debugFramebuffer(fbo, {
1199
+ id,
1200
+ minimap,
1201
+ opaque,
1202
+ top = "0",
1203
+ left = "0",
1204
+ rgbaScale = 1
1205
+ }) {
1116
1206
  if (!canvas) {
1117
1207
  canvas = document.createElement("canvas");
1118
1208
  canvas.id = id;
@@ -1150,9 +1240,11 @@ var __exports__ = (() => {
1150
1240
  var _Model = class {
1151
1241
  device;
1152
1242
  id;
1243
+ source;
1153
1244
  vs;
1154
1245
  fs;
1155
1246
  pipelineFactory;
1247
+ shaderFactory;
1156
1248
  userData = {};
1157
1249
  // Fixed properties (change can trigger pipeline rebuild)
1158
1250
  /** The render pipeline GPU parameters, depth testing etc */
@@ -1189,51 +1281,57 @@ var __exports__ = (() => {
1189
1281
  /** ShaderInputs instance */
1190
1282
  shaderInputs;
1191
1283
  _uniformStore;
1192
- _pipelineNeedsUpdate = "newly created";
1193
1284
  _attributeInfos = {};
1194
1285
  _gpuGeometry = null;
1195
1286
  _getModuleUniforms;
1196
1287
  props;
1288
+ _pipelineNeedsUpdate = "newly created";
1289
+ _needsRedraw = "initializing";
1290
+ _destroyed = false;
1291
+ /** "Time" of last draw. Monotonically increasing timestamp */
1292
+ _lastDrawTimestamp = -1;
1197
1293
  constructor(device, props) {
1198
1294
  this.props = { ..._Model.defaultProps, ...props };
1199
1295
  props = this.props;
1200
- this.id = props.id || (0, import_core8.uid)("model");
1296
+ this.id = props.id || (0, import_core10.uid)("model");
1201
1297
  this.device = device;
1202
1298
  Object.assign(this.userData, props.userData);
1203
1299
  const moduleMap = Object.fromEntries(
1204
1300
  this.props.modules?.map((module) => [module.name, module]) || []
1205
1301
  );
1206
1302
  this.setShaderInputs(props.shaderInputs || new ShaderInputs(moduleMap));
1207
- const isWebGPU = this.device.info.type === "webgpu";
1208
- if (this.props.source) {
1209
- if (isWebGPU) {
1210
- this.props.shaderLayout ||= (0, import_shadertools2.getShaderLayoutFromWGSL)(this.props.source);
1211
- }
1212
- this.props.fs = this.props.source;
1213
- this.props.vs = this.props.source;
1214
- }
1215
- if (isWebGPU && typeof this.props.vs !== "string") {
1216
- this.props.shaderLayout ||= (0, import_shadertools2.getShaderLayoutFromWGSL)(this.props.vs.wgsl);
1217
- }
1218
1303
  const platformInfo = getPlatformInfo(device);
1219
1304
  const modules = (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
1220
- const { vs, fs, getUniforms } = this.props.shaderAssembler.assembleShaders({
1221
- platformInfo,
1222
- ...this.props,
1223
- modules
1224
- });
1225
- this.vs = vs;
1226
- this.fs = fs;
1227
- this._getModuleUniforms = getUniforms;
1305
+ const isWebGPU = this.device.type === "webgpu";
1306
+ if (isWebGPU && this.props.source) {
1307
+ this.props.shaderLayout ||= (0, import_shadertools2.getShaderLayoutFromWGSL)(this.props.source);
1308
+ const { source, getUniforms } = this.props.shaderAssembler.assembleShader({
1309
+ platformInfo,
1310
+ ...this.props,
1311
+ modules
1312
+ });
1313
+ this.source = source;
1314
+ this._getModuleUniforms = getUniforms;
1315
+ } else {
1316
+ const { vs, fs, getUniforms } = this.props.shaderAssembler.assembleShaderPair({
1317
+ platformInfo,
1318
+ ...this.props,
1319
+ modules
1320
+ });
1321
+ this.vs = vs;
1322
+ this.fs = fs;
1323
+ this._getModuleUniforms = getUniforms;
1324
+ }
1228
1325
  this.vertexCount = this.props.vertexCount;
1229
1326
  this.instanceCount = this.props.instanceCount;
1230
1327
  this.topology = this.props.topology;
1231
1328
  this.bufferLayout = this.props.bufferLayout;
1232
1329
  this.parameters = this.props.parameters;
1233
1330
  if (props.geometry) {
1234
- this._gpuGeometry = this.setGeometry(props.geometry);
1331
+ this.setGeometry(props.geometry);
1235
1332
  }
1236
1333
  this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
1334
+ this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
1237
1335
  this.pipeline = this._updatePipeline();
1238
1336
  this.vertexArray = device.createVertexArray({
1239
1337
  renderPipeline: this.pipeline
@@ -1254,7 +1352,9 @@ var __exports__ = (() => {
1254
1352
  this.setIndexBuffer(props.indexBuffer);
1255
1353
  }
1256
1354
  if (props.attributes) {
1257
- this.setAttributes(props.attributes);
1355
+ this.setAttributes(props.attributes, {
1356
+ ignoreUnknownAttributes: props.ignoreUnknownAttributes
1357
+ });
1258
1358
  }
1259
1359
  if (props.constantAttributes) {
1260
1360
  this.setConstantAttributes(props.constantAttributes);
@@ -1266,7 +1366,7 @@ var __exports__ = (() => {
1266
1366
  this.setUniforms(props.uniforms);
1267
1367
  }
1268
1368
  if (props.moduleSettings) {
1269
- import_core8.log.warn("Model.props.moduleSettings is deprecated. Use Model.shaderInputs.setProps()")();
1369
+ import_core10.log.warn("Model.props.moduleSettings is deprecated. Use Model.shaderInputs.setProps()")();
1270
1370
  this.updateModuleSettings(props.moduleSettings);
1271
1371
  }
1272
1372
  if (props.transformFeedback) {
@@ -1275,23 +1375,48 @@ var __exports__ = (() => {
1275
1375
  Object.seal(this);
1276
1376
  }
1277
1377
  destroy() {
1378
+ if (this._destroyed)
1379
+ return;
1278
1380
  this.pipelineFactory.release(this.pipeline);
1381
+ this.shaderFactory.release(this.pipeline.vs);
1382
+ if (this.pipeline.fs) {
1383
+ this.shaderFactory.release(this.pipeline.fs);
1384
+ }
1279
1385
  this._uniformStore.destroy();
1386
+ this._gpuGeometry?.destroy();
1387
+ this._destroyed = true;
1280
1388
  }
1281
1389
  // Draw call
1390
+ /** Query redraw status. Clears the status. */
1391
+ needsRedraw() {
1392
+ if (this._getBindingsUpdateTimestamp() > this._lastDrawTimestamp) {
1393
+ this.setNeedsRedraw("contents of bound textures or buffers updated");
1394
+ }
1395
+ const needsRedraw = this._needsRedraw;
1396
+ this._needsRedraw = false;
1397
+ return needsRedraw;
1398
+ }
1399
+ /** Mark the model as needing a redraw */
1400
+ setNeedsRedraw(reason) {
1401
+ this._needsRedraw ||= reason;
1402
+ }
1282
1403
  predraw() {
1283
1404
  this.updateShaderInputs();
1405
+ this.pipeline = this._updatePipeline();
1284
1406
  }
1285
1407
  draw(renderPass) {
1286
1408
  this.predraw();
1409
+ let drawSuccess;
1287
1410
  try {
1288
1411
  this._logDrawCallStart();
1289
1412
  this.pipeline = this._updatePipeline();
1290
1413
  this.pipeline.setBindings(this.bindings);
1291
- this.pipeline.setUniforms(this.uniforms);
1414
+ if (!(0, import_core10.isObjectEmpty)(this.uniforms)) {
1415
+ this.pipeline.setUniformsWebGL(this.uniforms);
1416
+ }
1292
1417
  const { indexBuffer } = this.vertexArray;
1293
1418
  const indexCount = indexBuffer ? indexBuffer.byteLength / (indexBuffer.indexType === "uint32" ? 4 : 2) : void 0;
1294
- this.pipeline.draw({
1419
+ drawSuccess = this.pipeline.draw({
1295
1420
  renderPass,
1296
1421
  vertexArray: this.vertexArray,
1297
1422
  vertexCount: this.vertexCount,
@@ -1303,6 +1428,13 @@ var __exports__ = (() => {
1303
1428
  this._logDrawCallEnd();
1304
1429
  }
1305
1430
  this._logFramebuffer(renderPass);
1431
+ if (drawSuccess) {
1432
+ this._lastDrawTimestamp = this.device.timestamp;
1433
+ this._needsRedraw = false;
1434
+ } else {
1435
+ this._needsRedraw = "waiting for resource initialization";
1436
+ }
1437
+ return drawSuccess;
1306
1438
  }
1307
1439
  // Update fixed fields (can trigger pipeline rebuild)
1308
1440
  /**
@@ -1311,30 +1443,16 @@ var __exports__ = (() => {
1311
1443
  * @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
1312
1444
  */
1313
1445
  setGeometry(geometry) {
1446
+ this._gpuGeometry?.destroy();
1314
1447
  const gpuGeometry = geometry && makeGPUGeometry(this.device, geometry);
1315
- this.setTopology(gpuGeometry.topology || "triangle-list");
1316
- this.bufferLayout = mergeBufferLayouts(gpuGeometry.bufferLayout, this.bufferLayout);
1317
- if (this.vertexArray) {
1318
- this._setGeometryAttributes(gpuGeometry);
1319
- }
1320
- return gpuGeometry;
1321
- }
1322
- /**
1323
- * Updates the optional geometry attributes
1324
- * Geometry, sets several attributes, indexBuffer, and also vertex count
1325
- * @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
1326
- */
1327
- _setGeometryAttributes(gpuGeometry) {
1328
- const attributes = { ...gpuGeometry.attributes };
1329
- for (const [attributeName] of Object.entries(attributes)) {
1330
- if (!this.pipeline.shaderLayout.attributes.find((layout) => layout.name === attributeName) && attributeName !== "positions") {
1331
- delete attributes[attributeName];
1448
+ if (gpuGeometry) {
1449
+ this.setTopology(gpuGeometry.topology || "triangle-list");
1450
+ this.bufferLayout = mergeBufferLayouts(gpuGeometry.bufferLayout, this.bufferLayout);
1451
+ if (this.vertexArray) {
1452
+ this._setGeometryAttributes(gpuGeometry);
1332
1453
  }
1333
1454
  }
1334
- this.vertexCount = gpuGeometry.vertexCount;
1335
- this.setIndexBuffer(gpuGeometry.indices);
1336
- this.setAttributes(gpuGeometry.attributes, "ignore-unknown");
1337
- this.setAttributes(attributes);
1455
+ this._gpuGeometry = gpuGeometry;
1338
1456
  }
1339
1457
  /**
1340
1458
  * Updates the primitive topology ('triangle-list', 'triangle-strip' etc).
@@ -1348,11 +1466,10 @@ var __exports__ = (() => {
1348
1466
  }
1349
1467
  /**
1350
1468
  * Updates the buffer layout.
1351
- * @note Triggers a pipeline rebuild / pipeline cache fetch on WebGPU
1469
+ * @note Triggers a pipeline rebuild / pipeline cache fetch
1352
1470
  */
1353
1471
  setBufferLayout(bufferLayout) {
1354
1472
  this.bufferLayout = this._gpuGeometry ? mergeBufferLayouts(bufferLayout, this._gpuGeometry.bufferLayout) : bufferLayout;
1355
- this._setPipelineNeedsUpdate("bufferLayout");
1356
1473
  this.pipeline = this._updatePipeline();
1357
1474
  this.vertexArray = this.device.createVertexArray({
1358
1475
  renderPipeline: this.pipeline
@@ -1360,6 +1477,7 @@ var __exports__ = (() => {
1360
1477
  if (this._gpuGeometry) {
1361
1478
  this._setGeometryAttributes(this._gpuGeometry);
1362
1479
  }
1480
+ this._setPipelineNeedsUpdate("bufferLayout");
1363
1481
  }
1364
1482
  /**
1365
1483
  * Set GPU parameters.
@@ -1367,7 +1485,7 @@ var __exports__ = (() => {
1367
1485
  * @param parameters
1368
1486
  */
1369
1487
  setParameters(parameters) {
1370
- if (!(0, import_core8.deepEqual)(parameters, this.parameters, 2)) {
1488
+ if (!(0, import_core10.deepEqual)(parameters, this.parameters, 2)) {
1371
1489
  this.parameters = parameters;
1372
1490
  this._setPipelineNeedsUpdate("parameters");
1373
1491
  }
@@ -1379,6 +1497,7 @@ var __exports__ = (() => {
1379
1497
  */
1380
1498
  setVertexCount(vertexCount) {
1381
1499
  this.vertexCount = vertexCount;
1500
+ this.setNeedsRedraw("vertexCount");
1382
1501
  }
1383
1502
  /**
1384
1503
  * Updates the instance count (used in draw calls)
@@ -1386,57 +1505,34 @@ var __exports__ = (() => {
1386
1505
  */
1387
1506
  setInstanceCount(instanceCount) {
1388
1507
  this.instanceCount = instanceCount;
1508
+ this.setNeedsRedraw("instanceCount");
1389
1509
  }
1390
1510
  setShaderInputs(shaderInputs) {
1391
1511
  this.shaderInputs = shaderInputs;
1392
- this._uniformStore = new import_core7.UniformStore(this.shaderInputs.modules);
1512
+ this._uniformStore = new import_core9.UniformStore(this.shaderInputs.modules);
1393
1513
  for (const moduleName of Object.keys(this.shaderInputs.modules)) {
1394
1514
  const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
1395
1515
  this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
1396
1516
  }
1397
- }
1398
- /**
1399
- * Updates shader module settings (which results in uniforms being set)
1400
- */
1401
- setShaderModuleProps(props) {
1402
- const uniforms = this._getModuleUniforms(props);
1403
- const keys = Object.keys(uniforms).filter((k) => {
1404
- const uniform = uniforms[k];
1405
- return !(0, import_core8.isNumberArray)(uniform) && typeof uniform !== "number" && typeof uniform !== "boolean";
1406
- });
1407
- const bindings = {};
1408
- for (const k of keys) {
1409
- bindings[k] = uniforms[k];
1410
- delete uniforms[k];
1411
- }
1517
+ this.setNeedsRedraw("shaderInputs");
1412
1518
  }
1413
1519
  updateShaderInputs() {
1414
1520
  this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
1415
- }
1416
- /**
1417
- * @deprecated Updates shader module settings (which results in uniforms being set)
1418
- */
1419
- updateModuleSettings(props) {
1420
- import_core8.log.warn("Model.updateModuleSettings is deprecated. Use Model.shaderInputs.setProps()")();
1421
- const { bindings, uniforms } = (0, import_core8.splitUniformsAndBindings)(this._getModuleUniforms(props));
1422
- Object.assign(this.bindings, bindings);
1423
- Object.assign(this.uniforms, uniforms);
1521
+ this.setNeedsRedraw("shaderInputs");
1424
1522
  }
1425
1523
  /**
1426
1524
  * Sets bindings (textures, samplers, uniform buffers)
1427
1525
  */
1428
1526
  setBindings(bindings) {
1429
1527
  Object.assign(this.bindings, bindings);
1528
+ this.setNeedsRedraw("bindings");
1430
1529
  }
1431
1530
  /**
1432
- * Sets individual uniforms
1433
- * @deprecated WebGL only, use uniform buffers for portability
1434
- * @param uniforms
1435
- * @returns self for chaining
1531
+ * Updates optional transform feedback. WebGL only.
1436
1532
  */
1437
- setUniforms(uniforms) {
1438
- this.pipeline.setUniforms(uniforms);
1439
- Object.assign(this.uniforms, uniforms);
1533
+ setTransformFeedback(transformFeedback) {
1534
+ this.transformFeedback = transformFeedback;
1535
+ this.setNeedsRedraw("transformFeedback");
1440
1536
  }
1441
1537
  /**
1442
1538
  * Sets the index buffer
@@ -1444,27 +1540,24 @@ var __exports__ = (() => {
1444
1540
  */
1445
1541
  setIndexBuffer(indexBuffer) {
1446
1542
  this.vertexArray.setIndexBuffer(indexBuffer);
1447
- }
1448
- /**
1449
- * Updates optional transform feedback. WebGL only.
1450
- */
1451
- setTransformFeedback(transformFeedback) {
1452
- this.transformFeedback = transformFeedback;
1543
+ this.setNeedsRedraw("indexBuffer");
1453
1544
  }
1454
1545
  /**
1455
1546
  * Sets attributes (buffers)
1456
1547
  * @note Overrides any attributes previously set with the same name
1457
1548
  */
1458
- setAttributes(buffers, _option) {
1549
+ setAttributes(buffers, options) {
1459
1550
  if (buffers.indices) {
1460
- import_core8.log.warn(
1551
+ import_core10.log.warn(
1461
1552
  `Model:${this.id} setAttributes() - indexBuffer should be set using setIndexBuffer()`
1462
1553
  )();
1463
1554
  }
1464
1555
  for (const [bufferName, buffer] of Object.entries(buffers)) {
1465
- const bufferLayout = this.bufferLayout.find((layout) => getAttributeNames(layout).includes(bufferName));
1556
+ const bufferLayout = this.bufferLayout.find(
1557
+ (layout) => getAttributeNames(layout).includes(bufferName)
1558
+ );
1466
1559
  if (!bufferLayout) {
1467
- import_core8.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
1560
+ import_core10.log.warn(`Model(${this.id}): Missing layout for buffer "${bufferName}".`)();
1468
1561
  continue;
1469
1562
  }
1470
1563
  const attributeNames = getAttributeNames(bufferLayout);
@@ -1476,12 +1569,13 @@ var __exports__ = (() => {
1476
1569
  set = true;
1477
1570
  }
1478
1571
  }
1479
- if (!set && _option !== "ignore-unknown") {
1480
- import_core8.log.warn(
1572
+ if (!set && (options?.ignoreUnknownAttributes || this.props.ignoreUnknownAttributes)) {
1573
+ import_core10.log.warn(
1481
1574
  `Model(${this.id}): Ignoring buffer "${buffer.id}" for unknown attribute "${bufferName}"`
1482
1575
  )();
1483
1576
  }
1484
1577
  }
1578
+ this.setNeedsRedraw("attributes");
1485
1579
  }
1486
1580
  /**
1487
1581
  * Sets constant attributes
@@ -1495,36 +1589,107 @@ var __exports__ = (() => {
1495
1589
  for (const [attributeName, value] of Object.entries(attributes)) {
1496
1590
  const attributeInfo = this._attributeInfos[attributeName];
1497
1591
  if (attributeInfo) {
1498
- this.vertexArray.setConstant(attributeInfo.location, value);
1592
+ this.vertexArray.setConstantWebGL(attributeInfo.location, value);
1499
1593
  } else {
1500
- import_core8.log.warn(
1594
+ import_core10.log.warn(
1501
1595
  `Model "${this.id}: Ignoring constant supplied for unknown attribute "${attributeName}"`
1502
1596
  )();
1503
1597
  }
1504
1598
  }
1599
+ this.setNeedsRedraw("constants");
1600
+ }
1601
+ // DEPRECATED METHODS
1602
+ /**
1603
+ * Sets individual uniforms
1604
+ * @deprecated WebGL only, use uniform buffers for portability
1605
+ * @param uniforms
1606
+ */
1607
+ setUniforms(uniforms) {
1608
+ if (!(0, import_core10.isObjectEmpty)(uniforms)) {
1609
+ this.pipeline.setUniformsWebGL(uniforms);
1610
+ Object.assign(this.uniforms, uniforms);
1611
+ }
1612
+ this.setNeedsRedraw("uniforms");
1613
+ }
1614
+ /**
1615
+ * @deprecated Updates shader module settings (which results in uniforms being set)
1616
+ */
1617
+ updateModuleSettings(props) {
1618
+ import_core10.log.warn("Model.updateModuleSettings is deprecated. Use Model.shaderInputs.setProps()")();
1619
+ const { bindings, uniforms } = (0, import_core10.splitUniformsAndBindings)(this._getModuleUniforms(props));
1620
+ Object.assign(this.bindings, bindings);
1621
+ Object.assign(this.uniforms, uniforms);
1622
+ this.setNeedsRedraw("moduleSettings");
1623
+ }
1624
+ // Internal methods
1625
+ /** Get the timestamp of the latest updated bound GPU memory resource (buffer/texture). */
1626
+ _getBindingsUpdateTimestamp() {
1627
+ let timestamp = 0;
1628
+ for (const binding of Object.values(this.bindings)) {
1629
+ if (binding instanceof import_core8.TextureView) {
1630
+ timestamp = Math.max(timestamp, binding.texture.updateTimestamp);
1631
+ } else if (binding instanceof import_core8.Buffer || binding instanceof import_core8.Texture) {
1632
+ timestamp = Math.max(timestamp, binding.updateTimestamp);
1633
+ } else if (!(binding instanceof import_core8.Sampler)) {
1634
+ timestamp = Math.max(timestamp, binding.buffer.updateTimestamp);
1635
+ }
1636
+ }
1637
+ return timestamp;
1505
1638
  }
1639
+ /**
1640
+ * Updates the optional geometry attributes
1641
+ * Geometry, sets several attributes, indexBuffer, and also vertex count
1642
+ * @note Can trigger a pipeline rebuild / pipeline cache fetch on WebGPU
1643
+ */
1644
+ _setGeometryAttributes(gpuGeometry) {
1645
+ const attributes = { ...gpuGeometry.attributes };
1646
+ for (const [attributeName] of Object.entries(attributes)) {
1647
+ if (!this.pipeline.shaderLayout.attributes.find((layout) => layout.name === attributeName) && attributeName !== "positions") {
1648
+ delete attributes[attributeName];
1649
+ }
1650
+ }
1651
+ this.vertexCount = gpuGeometry.vertexCount;
1652
+ this.setIndexBuffer(gpuGeometry.indices);
1653
+ this.setAttributes(gpuGeometry.attributes, { ignoreUnknownAttributes: true });
1654
+ this.setAttributes(attributes, { ignoreUnknownAttributes: this.props.ignoreUnknownAttributes });
1655
+ this.setNeedsRedraw("geometry attributes");
1656
+ }
1657
+ /** Mark pipeline as needing update */
1506
1658
  _setPipelineNeedsUpdate(reason) {
1507
- this._pipelineNeedsUpdate = this._pipelineNeedsUpdate || reason;
1659
+ this._pipelineNeedsUpdate ||= reason;
1660
+ this.setNeedsRedraw(reason);
1508
1661
  }
1662
+ /** Update pipeline if needed */
1509
1663
  _updatePipeline() {
1510
1664
  if (this._pipelineNeedsUpdate) {
1665
+ let prevShaderVs = null;
1666
+ let prevShaderFs = null;
1511
1667
  if (this.pipeline) {
1512
- import_core8.log.log(
1668
+ import_core10.log.log(
1513
1669
  1,
1514
1670
  `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
1515
1671
  )();
1672
+ prevShaderVs = this.pipeline.vs;
1673
+ prevShaderFs = this.pipeline.fs;
1516
1674
  }
1517
1675
  this._pipelineNeedsUpdate = false;
1518
- const vs = this.device.createShader({
1676
+ const vs = this.shaderFactory.createShader({
1519
1677
  id: `${this.id}-vertex`,
1520
1678
  stage: "vertex",
1521
- source: this.vs
1679
+ source: this.source || this.vs,
1680
+ debug: this.props.debugShaders
1522
1681
  });
1523
- const fs = this.fs ? this.device.createShader({
1524
- id: `${this.id}-fragment`,
1525
- stage: "fragment",
1526
- source: this.fs
1527
- }) : null;
1682
+ let fs = null;
1683
+ if (this.source) {
1684
+ fs = vs;
1685
+ } else if (this.fs) {
1686
+ fs = this.shaderFactory.createShader({
1687
+ id: `${this.id}-fragment`,
1688
+ stage: "fragment",
1689
+ source: this.source || this.fs,
1690
+ debug: this.props.debugShaders
1691
+ });
1692
+ }
1528
1693
  this.pipeline = this.pipelineFactory.createRenderPipeline({
1529
1694
  ...this.props,
1530
1695
  bufferLayout: this.bufferLayout,
@@ -1533,10 +1698,14 @@ var __exports__ = (() => {
1533
1698
  vs,
1534
1699
  fs
1535
1700
  });
1536
- this._attributeInfos = (0, import_core9.getAttributeInfosFromLayouts)(
1701
+ this._attributeInfos = (0, import_core11.getAttributeInfosFromLayouts)(
1537
1702
  this.pipeline.shaderLayout,
1538
1703
  this.bufferLayout
1539
1704
  );
1705
+ if (prevShaderVs)
1706
+ this.shaderFactory.release(prevShaderVs);
1707
+ if (prevShaderFs)
1708
+ this.shaderFactory.release(prevShaderFs);
1540
1709
  }
1541
1710
  return this.pipeline;
1542
1711
  }
@@ -1544,33 +1713,33 @@ var __exports__ = (() => {
1544
1713
  _lastLogTime = 0;
1545
1714
  _logOpen = false;
1546
1715
  _logDrawCallStart() {
1547
- const logDrawTimeout = import_core8.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
1548
- if (import_core8.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
1716
+ const logDrawTimeout = import_core10.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT;
1717
+ if (import_core10.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
1549
1718
  return;
1550
1719
  }
1551
1720
  this._lastLogTime = Date.now();
1552
1721
  this._logOpen = true;
1553
- import_core8.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core8.log.level <= 2 })();
1722
+ import_core10.log.group(LOG_DRAW_PRIORITY, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core10.log.level <= 2 })();
1554
1723
  }
1555
1724
  _logDrawCallEnd() {
1556
1725
  if (this._logOpen) {
1557
1726
  const shaderLayoutTable = getDebugTableForShaderLayout(this.pipeline.shaderLayout, this.id);
1558
- import_core8.log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
1727
+ import_core10.log.table(LOG_DRAW_PRIORITY, shaderLayoutTable)();
1559
1728
  const uniformTable = this.shaderInputs.getDebugTable();
1560
1729
  for (const [name, value] of Object.entries(this.uniforms)) {
1561
1730
  uniformTable[name] = { value };
1562
1731
  }
1563
- import_core8.log.table(LOG_DRAW_PRIORITY, uniformTable)();
1732
+ import_core10.log.table(LOG_DRAW_PRIORITY, uniformTable)();
1564
1733
  const attributeTable = this._getAttributeDebugTable();
1565
- import_core8.log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
1566
- import_core8.log.table(LOG_DRAW_PRIORITY, attributeTable)();
1567
- import_core8.log.groupEnd(LOG_DRAW_PRIORITY)();
1734
+ import_core10.log.table(LOG_DRAW_PRIORITY, this._attributeInfos)();
1735
+ import_core10.log.table(LOG_DRAW_PRIORITY, attributeTable)();
1736
+ import_core10.log.groupEnd(LOG_DRAW_PRIORITY)();
1568
1737
  this._logOpen = false;
1569
1738
  }
1570
1739
  }
1571
1740
  _drawCount = 0;
1572
1741
  _logFramebuffer(renderPass) {
1573
- const debugFramebuffers = import_core8.log.get("framebuffer");
1742
+ const debugFramebuffers = import_core10.log.get("framebuffer");
1574
1743
  this._drawCount++;
1575
1744
  if (!debugFramebuffers || this._drawCount++ > 3 && this._drawCount % 60) {
1576
1745
  return;
@@ -1605,14 +1774,14 @@ var __exports__ = (() => {
1605
1774
  }
1606
1775
  // TODO - fix typing of luma data types
1607
1776
  _getBufferOrConstantValues(attribute, dataType) {
1608
- const TypedArrayConstructor = (0, import_core7.getTypedArrayFromDataType)(dataType);
1609
- const typedArray = attribute instanceof import_core7.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
1777
+ const TypedArrayConstructor = (0, import_core11.getTypedArrayFromDataType)(dataType);
1778
+ const typedArray = attribute instanceof import_core8.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
1610
1779
  return typedArray.toString();
1611
1780
  }
1612
1781
  };
1613
1782
  var Model = _Model;
1614
1783
  __publicField(Model, "defaultProps", {
1615
- ...import_core7.RenderPipeline.defaultProps,
1784
+ ...import_core9.RenderPipeline.defaultProps,
1616
1785
  source: null,
1617
1786
  vs: null,
1618
1787
  fs: null,
@@ -1629,8 +1798,11 @@ var __exports__ = (() => {
1629
1798
  varyings: [],
1630
1799
  shaderInputs: void 0,
1631
1800
  pipelineFactory: void 0,
1801
+ shaderFactory: void 0,
1632
1802
  transformFeedback: void 0,
1633
- shaderAssembler: import_shadertools2.ShaderAssembler.getDefaultShaderAssembler()
1803
+ shaderAssembler: import_shadertools2.ShaderAssembler.getDefaultShaderAssembler(),
1804
+ debugShaders: void 0,
1805
+ ignoreUnknownAttributes: void 0
1634
1806
  });
1635
1807
  function mergeBufferLayouts(layouts1, layouts2) {
1636
1808
  const layouts = [...layouts1];
@@ -1646,10 +1818,11 @@ var __exports__ = (() => {
1646
1818
  }
1647
1819
  function getPlatformInfo(device) {
1648
1820
  return {
1649
- type: device.info.type,
1821
+ type: device.type,
1650
1822
  shaderLanguage: device.info.shadingLanguage,
1651
1823
  shaderLanguageVersion: device.info.shadingLanguageVersion,
1652
1824
  gpu: device.info.gpu,
1825
+ // HACK - we pretend that the DeviceFeatures is a Set, it has a similar API
1653
1826
  features: device.features
1654
1827
  };
1655
1828
  }
@@ -1658,7 +1831,7 @@ var __exports__ = (() => {
1658
1831
  }
1659
1832
 
1660
1833
  // src/transform/buffer-transform.ts
1661
- var import_core10 = __toESM(require_core(), 1);
1834
+ var import_core12 = __toESM(require_core(), 1);
1662
1835
  var import_shadertools3 = __toESM(require_shadertools(), 1);
1663
1836
  var BufferTransform = class {
1664
1837
  device;
@@ -1666,10 +1839,10 @@ var __exports__ = (() => {
1666
1839
  transformFeedback;
1667
1840
  /** @deprecated Use device feature test. */
1668
1841
  static isSupported(device) {
1669
- return device.features.has("transform-feedback-webgl");
1842
+ return device?.info?.type === "webgl";
1670
1843
  }
1671
1844
  constructor(device, props = Model.defaultProps) {
1672
- (0, import_core10.assert)(device.features.has("transform-feedback-webgl"), "Device must support transform feedback");
1845
+ (0, import_core12.assert)(BufferTransform.isSupported(device), "BufferTransform not yet implemented on WebGPU");
1673
1846
  this.device = device;
1674
1847
  this.model = new Model(this.device, {
1675
1848
  id: props.id || "buffer-transform-model",
@@ -1710,7 +1883,7 @@ var __exports__ = (() => {
1710
1883
  }
1711
1884
  readAsync(varyingName) {
1712
1885
  const result = this.getBuffer(varyingName);
1713
- if (result instanceof import_core10.Buffer) {
1886
+ if (result instanceof import_core12.Buffer) {
1714
1887
  return result.readAsync();
1715
1888
  }
1716
1889
  const { buffer, byteOffset = 0, byteLength = buffer.byteLength } = result;
@@ -1826,10 +1999,10 @@ var __exports__ = (() => {
1826
1999
  };
1827
2000
 
1828
2001
  // src/lib/clip-space.ts
1829
- var import_core12 = __toESM(require_core(), 1);
2002
+ var import_core14 = __toESM(require_core(), 1);
1830
2003
 
1831
2004
  // src/geometry/geometry.ts
1832
- var import_core11 = __toESM(require_core(), 1);
2005
+ var import_core13 = __toESM(require_core(), 1);
1833
2006
  var Geometry = class {
1834
2007
  id;
1835
2008
  /** Determines how vertices are read from the 'vertex' attributes */
@@ -1840,7 +2013,7 @@ var __exports__ = (() => {
1840
2013
  userData = {};
1841
2014
  constructor(props) {
1842
2015
  const { attributes = {}, indices = null, vertexCount = null } = props;
1843
- this.id = props.id || (0, import_core11.uid)("geometry");
2016
+ this.id = props.id || (0, import_core13.uid)("geometry");
1844
2017
  this.topology = props.topology;
1845
2018
  if (indices) {
1846
2019
  this.indices = ArrayBuffer.isView(indices) ? { value: indices, size: 1 } : indices;
@@ -1848,7 +2021,7 @@ var __exports__ = (() => {
1848
2021
  this.attributes = {};
1849
2022
  for (const [attributeName, attributeValue] of Object.entries(attributes)) {
1850
2023
  const attribute = ArrayBuffer.isView(attributeValue) ? { value: attributeValue } : attributeValue;
1851
- (0, import_core11.assert)(
2024
+ (0, import_core13.assert)(
1852
2025
  ArrayBuffer.isView(attribute.value),
1853
2026
  `${this._print(attributeName)}: must be typed array or object with value as typed array`
1854
2027
  );
@@ -1856,7 +2029,7 @@ var __exports__ = (() => {
1856
2029
  attribute.size = 3;
1857
2030
  }
1858
2031
  if (attributeName === "indices") {
1859
- (0, import_core11.assert)(!this.indices);
2032
+ (0, import_core13.assert)(!this.indices);
1860
2033
  this.indices = attribute;
1861
2034
  } else {
1862
2035
  this.attributes[attributeName] = attribute;
@@ -1871,7 +2044,7 @@ var __exports__ = (() => {
1871
2044
  getVertexCount() {
1872
2045
  return this.vertexCount;
1873
2046
  }
1874
- /**
2047
+ /**
1875
2048
  * Return an object with all attributes plus indices added as a field.
1876
2049
  * TODO Geometry types are a mess
1877
2050
  */
@@ -1888,10 +2061,10 @@ var __exports__ = (() => {
1888
2061
  * type: indices, vertices, uvs
1889
2062
  * size: elements per vertex
1890
2063
  * target: WebGL buffer type (string or constant)
1891
- *
1892
- * @param attributes
1893
- * @param indices
1894
- * @returns
2064
+ *
2065
+ * @param attributes
2066
+ * @param indices
2067
+ * @returns
1895
2068
  */
1896
2069
  _setAttributes(attributes, indices) {
1897
2070
  return this;
@@ -1907,13 +2080,13 @@ var __exports__ = (() => {
1907
2080
  vertexCount = Math.min(vertexCount, value.length / size);
1908
2081
  }
1909
2082
  }
1910
- (0, import_core11.assert)(Number.isFinite(vertexCount));
2083
+ (0, import_core13.assert)(Number.isFinite(vertexCount));
1911
2084
  return vertexCount;
1912
2085
  }
1913
2086
  };
1914
2087
 
1915
2088
  // src/lib/clip-space.ts
1916
- var CLIPSPACE_VERTEX_SHADER = import_core12.glsl`\
2089
+ var CLIPSPACE_VERTEX_SHADER = import_core14.glsl`\
1917
2090
  #version 300 es
1918
2091
  in vec2 aClipSpacePosition;
1919
2092
  in vec2 aTexCoord;
@@ -1934,28 +2107,25 @@ void main(void) {
1934
2107
  var ClipSpace = class extends Model {
1935
2108
  constructor(device, opts) {
1936
2109
  const TEX_COORDS = POSITIONS.map((coord) => coord === -1 ? 0 : coord);
1937
- super(
1938
- device,
1939
- {
1940
- ...opts,
1941
- vs: CLIPSPACE_VERTEX_SHADER,
2110
+ super(device, {
2111
+ ...opts,
2112
+ vs: CLIPSPACE_VERTEX_SHADER,
2113
+ vertexCount: 4,
2114
+ geometry: new Geometry({
2115
+ topology: "triangle-strip",
1942
2116
  vertexCount: 4,
1943
- geometry: new Geometry({
1944
- topology: "triangle-strip",
1945
- vertexCount: 4,
1946
- attributes: {
1947
- aClipSpacePosition: { size: 2, value: new Float32Array(POSITIONS) },
1948
- aTexCoord: { size: 2, value: new Float32Array(TEX_COORDS) },
1949
- aCoordinate: { size: 2, value: new Float32Array(TEX_COORDS) }
1950
- }
1951
- })
1952
- }
1953
- );
2117
+ attributes: {
2118
+ aClipSpacePosition: { size: 2, value: new Float32Array(POSITIONS) },
2119
+ aTexCoord: { size: 2, value: new Float32Array(TEX_COORDS) },
2120
+ aCoordinate: { size: 2, value: new Float32Array(TEX_COORDS) }
2121
+ }
2122
+ })
2123
+ });
1954
2124
  }
1955
2125
  };
1956
2126
 
1957
2127
  // src/scenegraph/scenegraph-node.ts
1958
- var import_core13 = __toESM(require_core(), 1);
2128
+ var import_core15 = __toESM(require_core(), 1);
1959
2129
 
1960
2130
  // ../../node_modules/@math.gl/core/dist/lib/common.js
1961
2131
  var RADIANS_TO_DEGREES = 1 / Math.PI * 180;
@@ -3858,7 +4028,7 @@ void main(void) {
3858
4028
  props = {};
3859
4029
  constructor(props = {}) {
3860
4030
  const { id } = props;
3861
- this.id = id || (0, import_core13.uid)(this.constructor.name);
4031
+ this.id = id || (0, import_core15.uid)(this.constructor.name);
3862
4032
  this._setScenegraphNodeProps(props);
3863
4033
  }
3864
4034
  getBounds() {
@@ -3878,17 +4048,17 @@ void main(void) {
3878
4048
  return `{type: ScenegraphNode, id: ${this.id})}`;
3879
4049
  }
3880
4050
  setPosition(position) {
3881
- (0, import_core13.assert)(position.length === 3, "setPosition requires vector argument");
4051
+ (0, import_core15.assert)(position.length === 3, "setPosition requires vector argument");
3882
4052
  this.position = position;
3883
4053
  return this;
3884
4054
  }
3885
4055
  setRotation(rotation) {
3886
- (0, import_core13.assert)(rotation.length === 3, "setRotation requires vector argument");
4056
+ (0, import_core15.assert)(rotation.length === 3, "setRotation requires vector argument");
3887
4057
  this.rotation = rotation;
3888
4058
  return this;
3889
4059
  }
3890
4060
  setScale(scale2) {
3891
- (0, import_core13.assert)(scale2.length === 3, "setScale requires vector argument");
4061
+ (0, import_core15.assert)(scale2.length === 3, "setScale requires vector argument");
3892
4062
  this.scale = scale2;
3893
4063
  return this;
3894
4064
  }
@@ -3940,7 +4110,7 @@ void main(void) {
3940
4110
  return this;
3941
4111
  }
3942
4112
  getCoordinateUniforms(viewMatrix, modelMatrix) {
3943
- (0, import_core13.assert)(viewMatrix);
4113
+ (0, import_core15.assert)(viewMatrix);
3944
4114
  modelMatrix = modelMatrix || this.matrix;
3945
4115
  const worldMatrix = new Matrix4(viewMatrix).multiplyRight(modelMatrix);
3946
4116
  const worldInverse = worldMatrix.invert();
@@ -3997,13 +4167,13 @@ void main(void) {
3997
4167
  };
3998
4168
 
3999
4169
  // src/scenegraph/group-node.ts
4000
- var import_core16 = __toESM(require_core(), 1);
4170
+ var import_core18 = __toESM(require_core(), 1);
4001
4171
  var GroupNode = class extends ScenegraphNode {
4002
4172
  children;
4003
4173
  constructor(props = {}) {
4004
4174
  props = Array.isArray(props) ? { children: props } : props;
4005
4175
  const { children = [] } = props;
4006
- import_core16.log.assert(
4176
+ import_core18.log.assert(
4007
4177
  children.every((child) => child instanceof ScenegraphNode),
4008
4178
  "every child must an instance of ScenegraphNode"
4009
4179
  );
@@ -4011,7 +4181,10 @@ void main(void) {
4011
4181
  this.children = children;
4012
4182
  }
4013
4183
  getBounds() {
4014
- const result = [[Infinity, Infinity, Infinity], [-Infinity, -Infinity, -Infinity]];
4184
+ const result = [
4185
+ [Infinity, Infinity, Infinity],
4186
+ [-Infinity, -Infinity, -Infinity]
4187
+ ];
4015
4188
  this.traverse((node, { worldMatrix }) => {
4016
4189
  const bounds = node.getBounds();
4017
4190
  if (!bounds) {
@@ -4023,11 +4196,7 @@ void main(void) {
4023
4196
  const halfSize = new Vector3(max).subtract(min).divide([2, 2, 2]);
4024
4197
  worldMatrix.transformAsVector(halfSize, halfSize);
4025
4198
  for (let v = 0; v < 8; v++) {
4026
- const position = new Vector3(
4027
- v & 1 ? -1 : 1,
4028
- v & 2 ? -1 : 1,
4029
- v & 4 ? -1 : 1
4030
- ).multiply(halfSize).add(center);
4199
+ const position = new Vector3(v & 1 ? -1 : 1, v & 2 ? -1 : 1, v & 4 ? -1 : 1).multiply(halfSize).add(center);
4031
4200
  for (let i = 0; i < 3; i++) {
4032
4201
  result[0][i] = Math.min(result[0][i], position[i]);
4033
4202
  result[1][i] = Math.max(result[1][i], position[i]);
@@ -4113,10 +4282,10 @@ void main(void) {
4113
4282
  };
4114
4283
 
4115
4284
  // src/geometries/cone-geometry.ts
4116
- var import_core18 = __toESM(require_core(), 1);
4285
+ var import_core20 = __toESM(require_core(), 1);
4117
4286
 
4118
4287
  // src/geometries/truncated-cone-geometry.ts
4119
- var import_core17 = __toESM(require_core(), 1);
4288
+ var import_core19 = __toESM(require_core(), 1);
4120
4289
  var INDEX_OFFSETS = {
4121
4290
  x: [2, 0, 1],
4122
4291
  y: [0, 1, 2],
@@ -4124,7 +4293,7 @@ void main(void) {
4124
4293
  };
4125
4294
  var TruncatedConeGeometry = class extends Geometry {
4126
4295
  constructor(props = {}) {
4127
- const { id = (0, import_core17.uid)("truncated-code-geometry") } = props;
4296
+ const { id = (0, import_core19.uid)("truncated-code-geometry") } = props;
4128
4297
  const { indices, attributes } = tesselateTruncatedCone(props);
4129
4298
  super({
4130
4299
  ...props,
@@ -4228,7 +4397,7 @@ void main(void) {
4228
4397
  // src/geometries/cone-geometry.ts
4229
4398
  var ConeGeometry = class extends TruncatedConeGeometry {
4230
4399
  constructor(props = {}) {
4231
- const { id = (0, import_core18.uid)("cone-geometry"), radius = 1, cap = true } = props;
4400
+ const { id = (0, import_core20.uid)("cone-geometry"), radius = 1, cap = true } = props;
4232
4401
  super({
4233
4402
  ...props,
4234
4403
  id,
@@ -4241,23 +4410,25 @@ void main(void) {
4241
4410
  };
4242
4411
 
4243
4412
  // src/geometries/cube-geometry.ts
4244
- var import_core19 = __toESM(require_core(), 1);
4413
+ var import_core21 = __toESM(require_core(), 1);
4245
4414
  var CubeGeometry = class extends Geometry {
4246
4415
  constructor(props = {}) {
4247
- const { id = (0, import_core19.uid)("cube-geometry"), indices = true } = props;
4248
- super(indices ? {
4249
- ...props,
4250
- id,
4251
- topology: "triangle-list",
4252
- indices: { size: 1, value: CUBE_INDICES },
4253
- attributes: { ...ATTRIBUTES, ...props.attributes }
4254
- } : {
4255
- ...props,
4256
- id,
4257
- topology: "triangle-list",
4258
- indices: void 0,
4259
- attributes: { ...NON_INDEXED_ATTRIBUTES, ...props.attributes }
4260
- });
4416
+ const { id = (0, import_core21.uid)("cube-geometry"), indices = true } = props;
4417
+ super(
4418
+ indices ? {
4419
+ ...props,
4420
+ id,
4421
+ topology: "triangle-list",
4422
+ indices: { size: 1, value: CUBE_INDICES },
4423
+ attributes: { ...ATTRIBUTES, ...props.attributes }
4424
+ } : {
4425
+ ...props,
4426
+ id,
4427
+ topology: "triangle-list",
4428
+ indices: void 0,
4429
+ attributes: { ...NON_INDEXED_ATTRIBUTES, ...props.attributes }
4430
+ }
4431
+ );
4261
4432
  }
4262
4433
  };
4263
4434
  var CUBE_INDICES = new Uint16Array([
@@ -4851,10 +5022,10 @@ void main(void) {
4851
5022
  };
4852
5023
 
4853
5024
  // src/geometries/cylinder-geometry.ts
4854
- var import_core20 = __toESM(require_core(), 1);
5025
+ var import_core22 = __toESM(require_core(), 1);
4855
5026
  var CylinderGeometry = class extends TruncatedConeGeometry {
4856
5027
  constructor(props = {}) {
4857
- const { id = (0, import_core20.uid)("cylinder-geometry"), radius = 1 } = props;
5028
+ const { id = (0, import_core22.uid)("cylinder-geometry"), radius = 1 } = props;
4858
5029
  super({
4859
5030
  ...props,
4860
5031
  id,
@@ -4865,12 +5036,12 @@ void main(void) {
4865
5036
  };
4866
5037
 
4867
5038
  // src/geometries/ico-sphere-geometry.ts
4868
- var import_core21 = __toESM(require_core(), 1);
5039
+ var import_core23 = __toESM(require_core(), 1);
4869
5040
  var ICO_POSITIONS = [-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, 0, 0];
4870
5041
  var ICO_INDICES = [3, 4, 5, 3, 5, 1, 3, 1, 0, 3, 0, 4, 4, 0, 2, 4, 2, 5, 2, 0, 1, 5, 2, 1];
4871
5042
  var IcoSphereGeometry = class extends Geometry {
4872
5043
  constructor(props = {}) {
4873
- const { id = (0, import_core21.uid)("ico-sphere-geometry") } = props;
5044
+ const { id = (0, import_core23.uid)("ico-sphere-geometry") } = props;
4874
5045
  const { indices, attributes } = tesselateIcosaHedron(props);
4875
5046
  super({
4876
5047
  ...props,
@@ -5012,7 +5183,7 @@ void main(void) {
5012
5183
  }
5013
5184
 
5014
5185
  // src/geometries/plane-geometry.ts
5015
- var import_core23 = __toESM(require_core(), 1);
5186
+ var import_core25 = __toESM(require_core(), 1);
5016
5187
 
5017
5188
  // src/geometry/geometry-utils.ts
5018
5189
  function unpackIndexedGeometry(geometry) {
@@ -5045,7 +5216,7 @@ void main(void) {
5045
5216
  // src/geometries/plane-geometry.ts
5046
5217
  var PlaneGeometry = class extends Geometry {
5047
5218
  constructor(props = {}) {
5048
- const { id = (0, import_core23.uid)("plane-geometry") } = props;
5219
+ const { id = (0, import_core25.uid)("plane-geometry") } = props;
5049
5220
  const { indices, attributes } = tesselatePlane(props);
5050
5221
  super({
5051
5222
  ...props,
@@ -5135,10 +5306,10 @@ void main(void) {
5135
5306
  }
5136
5307
 
5137
5308
  // src/geometries/sphere-geometry.ts
5138
- var import_core24 = __toESM(require_core(), 1);
5309
+ var import_core26 = __toESM(require_core(), 1);
5139
5310
  var SphereGeometry = class extends Geometry {
5140
5311
  constructor(props = {}) {
5141
- const { id = (0, import_core24.uid)("sphere-geometry") } = props;
5312
+ const { id = (0, import_core26.uid)("sphere-geometry") } = props;
5142
5313
  const { indices, attributes } = tesselateSphere(props);
5143
5314
  super({
5144
5315
  ...props,
@@ -5212,6 +5383,219 @@ void main(void) {
5212
5383
  }
5213
5384
  };
5214
5385
  }
5386
+
5387
+ // src/computation.ts
5388
+ var import_core27 = __toESM(require_core(), 1);
5389
+ var import_core28 = __toESM(require_core(), 1);
5390
+ var import_core29 = __toESM(require_core(), 1);
5391
+ var import_shadertools5 = __toESM(require_shadertools(), 1);
5392
+ var LOG_DRAW_PRIORITY2 = 2;
5393
+ var LOG_DRAW_TIMEOUT2 = 1e4;
5394
+ var _Computation = class {
5395
+ device;
5396
+ id;
5397
+ pipelineFactory;
5398
+ shaderFactory;
5399
+ userData = {};
5400
+ /** Bindings (textures, samplers, uniform buffers) */
5401
+ bindings = {};
5402
+ /** The underlying GPU "program". @note May be recreated if parameters change */
5403
+ pipeline;
5404
+ /** the underlying compiled compute shader */
5405
+ shader;
5406
+ source;
5407
+ /** ShaderInputs instance */
5408
+ shaderInputs;
5409
+ _uniformStore;
5410
+ _pipelineNeedsUpdate = "newly created";
5411
+ _getModuleUniforms;
5412
+ props;
5413
+ _destroyed = false;
5414
+ constructor(device, props) {
5415
+ if (device.type !== "webgpu") {
5416
+ throw new Error("Computation is only supported in WebGPU");
5417
+ }
5418
+ this.props = { ..._Computation.defaultProps, ...props };
5419
+ props = this.props;
5420
+ this.id = props.id || (0, import_core28.uid)("model");
5421
+ this.device = device;
5422
+ Object.assign(this.userData, props.userData);
5423
+ const moduleMap = Object.fromEntries(
5424
+ this.props.modules?.map((module) => [module.name, module]) || []
5425
+ );
5426
+ this.setShaderInputs(props.shaderInputs || new ShaderInputs(moduleMap));
5427
+ this.props.shaderLayout ||= (0, import_shadertools5.getShaderLayoutFromWGSL)(this.props.source);
5428
+ const platformInfo = getPlatformInfo2(device);
5429
+ const modules = (this.props.modules?.length > 0 ? this.props.modules : this.shaderInputs?.getModules()) || [];
5430
+ this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
5431
+ this.shaderFactory = props.shaderFactory || ShaderFactory.getDefaultShaderFactory(this.device);
5432
+ const { source, getUniforms } = this.props.shaderAssembler.assembleShader({
5433
+ platformInfo,
5434
+ ...this.props,
5435
+ modules
5436
+ });
5437
+ this.source = source;
5438
+ this._getModuleUniforms = getUniforms;
5439
+ this.pipeline = this._updatePipeline();
5440
+ if (props.bindings) {
5441
+ this.setBindings(props.bindings);
5442
+ }
5443
+ Object.seal(this);
5444
+ }
5445
+ destroy() {
5446
+ if (this._destroyed)
5447
+ return;
5448
+ this.pipelineFactory.release(this.pipeline);
5449
+ this.shaderFactory.release(this.shader);
5450
+ this._uniformStore.destroy();
5451
+ this._destroyed = true;
5452
+ }
5453
+ // Draw call
5454
+ predraw() {
5455
+ this.updateShaderInputs();
5456
+ }
5457
+ dispatch(computePass, x, y, z) {
5458
+ try {
5459
+ this._logDrawCallStart();
5460
+ this.pipeline = this._updatePipeline();
5461
+ this.pipeline.setBindings(this.bindings);
5462
+ computePass.setPipeline(this.pipeline);
5463
+ computePass.setBindings([]);
5464
+ computePass.dispatch(x, y, z);
5465
+ } finally {
5466
+ this._logDrawCallEnd();
5467
+ }
5468
+ }
5469
+ // Update fixed fields (can trigger pipeline rebuild)
5470
+ // Update dynamic fields
5471
+ /**
5472
+ * Updates the vertex count (used in draw calls)
5473
+ * @note Any attributes with stepMode=vertex need to be at least this big
5474
+ */
5475
+ setVertexCount(vertexCount) {
5476
+ }
5477
+ /**
5478
+ * Updates the instance count (used in draw calls)
5479
+ * @note Any attributes with stepMode=instance need to be at least this big
5480
+ */
5481
+ setInstanceCount(instanceCount) {
5482
+ }
5483
+ setShaderInputs(shaderInputs) {
5484
+ this.shaderInputs = shaderInputs;
5485
+ this._uniformStore = new import_core27.UniformStore(this.shaderInputs.modules);
5486
+ for (const moduleName of Object.keys(this.shaderInputs.modules)) {
5487
+ const uniformBuffer = this._uniformStore.getManagedUniformBuffer(this.device, moduleName);
5488
+ this.bindings[`${moduleName}Uniforms`] = uniformBuffer;
5489
+ }
5490
+ }
5491
+ /**
5492
+ * Updates shader module settings (which results in uniforms being set)
5493
+ */
5494
+ setShaderModuleProps(props) {
5495
+ const uniforms = this._getModuleUniforms(props);
5496
+ const keys = Object.keys(uniforms).filter((k) => {
5497
+ const uniform = uniforms[k];
5498
+ return !(0, import_core28.isNumberArray)(uniform) && typeof uniform !== "number" && typeof uniform !== "boolean";
5499
+ });
5500
+ const bindings = {};
5501
+ for (const k of keys) {
5502
+ bindings[k] = uniforms[k];
5503
+ delete uniforms[k];
5504
+ }
5505
+ }
5506
+ updateShaderInputs() {
5507
+ this._uniformStore.setUniforms(this.shaderInputs.getUniformValues());
5508
+ }
5509
+ /**
5510
+ * Sets bindings (textures, samplers, uniform buffers)
5511
+ */
5512
+ setBindings(bindings) {
5513
+ Object.assign(this.bindings, bindings);
5514
+ }
5515
+ _setPipelineNeedsUpdate(reason) {
5516
+ this._pipelineNeedsUpdate = this._pipelineNeedsUpdate || reason;
5517
+ }
5518
+ _updatePipeline() {
5519
+ if (this._pipelineNeedsUpdate) {
5520
+ let prevShader = null;
5521
+ if (this.pipeline) {
5522
+ import_core28.log.log(
5523
+ 1,
5524
+ `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`
5525
+ )();
5526
+ prevShader = this.shader;
5527
+ }
5528
+ this._pipelineNeedsUpdate = false;
5529
+ this.shader = this.shaderFactory.createShader({
5530
+ id: `${this.id}-fragment`,
5531
+ stage: "compute",
5532
+ source: this.source,
5533
+ debug: this.props.debugShaders
5534
+ });
5535
+ this.pipeline = this.pipelineFactory.createComputePipeline({
5536
+ ...this.props,
5537
+ shader: this.shader
5538
+ });
5539
+ if (prevShader) {
5540
+ this.shaderFactory.release(prevShader);
5541
+ }
5542
+ }
5543
+ return this.pipeline;
5544
+ }
5545
+ /** Throttle draw call logging */
5546
+ _lastLogTime = 0;
5547
+ _logOpen = false;
5548
+ _logDrawCallStart() {
5549
+ const logDrawTimeout = import_core28.log.level > 3 ? 0 : LOG_DRAW_TIMEOUT2;
5550
+ if (import_core28.log.level < 2 || Date.now() - this._lastLogTime < logDrawTimeout) {
5551
+ return;
5552
+ }
5553
+ this._lastLogTime = Date.now();
5554
+ this._logOpen = true;
5555
+ import_core28.log.group(LOG_DRAW_PRIORITY2, `>>> DRAWING MODEL ${this.id}`, { collapsed: import_core28.log.level <= 2 })();
5556
+ }
5557
+ _logDrawCallEnd() {
5558
+ if (this._logOpen) {
5559
+ const uniformTable = this.shaderInputs.getDebugTable();
5560
+ import_core28.log.table(LOG_DRAW_PRIORITY2, uniformTable)();
5561
+ import_core28.log.groupEnd(LOG_DRAW_PRIORITY2)();
5562
+ this._logOpen = false;
5563
+ }
5564
+ }
5565
+ _drawCount = 0;
5566
+ // TODO - fix typing of luma data types
5567
+ _getBufferOrConstantValues(attribute, dataType) {
5568
+ const TypedArrayConstructor = (0, import_core29.getTypedArrayFromDataType)(dataType);
5569
+ const typedArray = attribute instanceof import_core27.Buffer ? new TypedArrayConstructor(attribute.debugData) : attribute;
5570
+ return typedArray.toString();
5571
+ }
5572
+ };
5573
+ var Computation = _Computation;
5574
+ __publicField(Computation, "defaultProps", {
5575
+ ...import_core27.ComputePipeline.defaultProps,
5576
+ id: "unnamed",
5577
+ handle: void 0,
5578
+ userData: {},
5579
+ source: "",
5580
+ modules: [],
5581
+ defines: {},
5582
+ bindings: void 0,
5583
+ shaderInputs: void 0,
5584
+ pipelineFactory: void 0,
5585
+ shaderFactory: void 0,
5586
+ shaderAssembler: import_shadertools5.ShaderAssembler.getDefaultShaderAssembler(),
5587
+ debugShaders: void 0
5588
+ });
5589
+ function getPlatformInfo2(device) {
5590
+ return {
5591
+ type: device.type,
5592
+ shaderLanguage: device.info.shadingLanguage,
5593
+ shaderLanguageVersion: device.info.shadingLanguageVersion,
5594
+ gpu: device.info.gpu,
5595
+ // HACK - we pretend that the DeviceFeatures is a Set, it has a similar API
5596
+ features: device.features
5597
+ };
5598
+ }
5215
5599
  return __toCommonJS(src_exports);
5216
5600
  })();
5217
5601
  return __exports__;