@luma.gl/engine 9.0.0-alpha.26 → 9.0.0-alpha.29

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.
package/dist/dist.dev.js CHANGED
@@ -2028,6 +2028,7 @@ var __exports__ = (() => {
2028
2028
  };
2029
2029
  /** Set attributes (stored on pipeline and set before each call) */
2030
2030
  /** Set attributes (stored on pipeline and set before each call) */
2031
+ /** Set constant attributes (WebGL only) */
2031
2032
  /** Set bindings (stored on pipeline and set before each call) */
2032
2033
  /** Uniforms (only supported on WebGL devices. Reset before each call to enable pipeline sharing) */
2033
2034
  /** Draw call */
@@ -2146,8 +2147,8 @@ var __exports__ = (() => {
2146
2147
  function decodeVertexType(type) {
2147
2148
  const dataType = TYPE_MAP[type];
2148
2149
  const bytes = getDataTypeBytes(dataType);
2149
- const integer = !type.startsWith("float");
2150
2150
  const normalized = type.includes("norm");
2151
+ const integer = !normalized && !type.startsWith("float");
2151
2152
  const signed = type.startsWith("s");
2152
2153
  return {
2153
2154
  dataType: TYPE_MAP[type],
@@ -2433,6 +2434,43 @@ var __exports__ = (() => {
2433
2434
  });
2434
2435
  }
2435
2436
 
2437
+ // ../api/src/lib/utils/array-utils-flat.ts
2438
+ var arrayBuffer;
2439
+ function getScratchArrayBuffer(byteLength) {
2440
+ if (!arrayBuffer || arrayBuffer.byteLength < byteLength) {
2441
+ arrayBuffer = new ArrayBuffer(byteLength);
2442
+ }
2443
+ return arrayBuffer;
2444
+ }
2445
+ function getScratchArray(Type, length) {
2446
+ const scratchArrayBuffer = getScratchArrayBuffer(Type.BYTES_PER_ELEMENT * length);
2447
+ return new Type(scratchArrayBuffer, 0, length);
2448
+ }
2449
+ function fillArray(options) {
2450
+ const {
2451
+ target,
2452
+ source,
2453
+ start = 0,
2454
+ count = 1
2455
+ } = options;
2456
+ const length = source.length;
2457
+ const total = count * length;
2458
+ let copied = 0;
2459
+ for (let i = start; copied < length; copied++) {
2460
+ target[i++] = source[copied];
2461
+ }
2462
+ while (copied < total) {
2463
+ if (copied < total - copied) {
2464
+ target.copyWithin(start + copied, start, start + copied);
2465
+ copied *= 2;
2466
+ } else {
2467
+ target.copyWithin(start + copied, start, start + total - copied);
2468
+ copied = total;
2469
+ }
2470
+ }
2471
+ return options.target;
2472
+ }
2473
+
2436
2474
  // ../api/src/lib/request-animation-frame.ts
2437
2475
  function requestAnimationFrame(callback) {
2438
2476
  return typeof window !== "undefined" && window.requestAnimationFrame ? window.requestAnimationFrame(callback) : setTimeout(callback, 1e3 / 60);
@@ -5834,16 +5872,28 @@ void main() {
5834
5872
  };
5835
5873
  var Model = class {
5836
5874
  fs = null;
5875
+ /** The underlying GPU "program". @note May be recreated if parameters change */
5837
5876
  userData = {};
5877
+ // readonly props: Required<ModelProps>;
5878
+ /** Vertex count */
5879
+ /** instance count */
5880
+ instanceCount = 0;
5881
+ /** Buffer-valued attributes */
5882
+ bufferAttributes = {};
5883
+ /** Constant-valued attributes */
5884
+ constantAttributes = {};
5885
+ /** Bindings (textures, samplers, uniform buffers) */
5886
+ bindings = {};
5887
+ /** Uniforms */
5888
+ uniforms = {};
5838
5889
  constructor(device, props) {
5839
- this.props = {
5890
+ props = {
5840
5891
  ...DEFAULT_MODEL_PROPS,
5841
5892
  ...props
5842
5893
  };
5843
- props = this.props;
5844
- this.id = this.props.id;
5894
+ this.id = props.id;
5845
5895
  this.device = device;
5846
- Object.assign(this.userData, this.props.userData);
5896
+ Object.assign(this.userData, props.userData);
5847
5897
  if (!props.vs) {
5848
5898
  throw new Error("no vertex shader");
5849
5899
  }
@@ -5851,18 +5901,19 @@ void main() {
5851
5901
  if (props.fs) {
5852
5902
  this.fs = getShaderSource(this.device, props.fs);
5853
5903
  }
5854
- this.vertexCount = this.props.vertexCount;
5855
- this.topology = this.props.topology;
5856
- if (this.props.geometry) {
5857
- this.vertexCount = this.props.geometry.vertexCount;
5858
- this.topology = this.props.geometry.topology || "triangle-list";
5904
+ this.vertexCount = props.vertexCount;
5905
+ this.instanceCount = props.instanceCount;
5906
+ this.topology = props.topology;
5907
+ if (props.geometry) {
5908
+ this.vertexCount = props.geometry.vertexCount;
5909
+ this.topology = props.geometry.topology || "triangle-list";
5859
5910
  }
5860
- this.pipelineFactory = this.props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
5911
+ this.pipelineFactory = props.pipelineFactory || PipelineFactory.getDefaultPipelineFactory(this.device);
5861
5912
  const {
5862
5913
  pipeline,
5863
5914
  getUniforms
5864
5915
  } = this.pipelineFactory.createRenderPipeline({
5865
- ...this.props,
5916
+ ...props,
5866
5917
  vs: this.vs,
5867
5918
  fs: this.fs,
5868
5919
  topology: this.topology,
@@ -5872,11 +5923,11 @@ void main() {
5872
5923
  });
5873
5924
  this.pipeline = pipeline;
5874
5925
  this._getModuleUniforms = getUniforms;
5875
- if (this.props.geometry) {
5876
- this._setGeometry(this.props.geometry);
5926
+ if (props.geometry) {
5927
+ this._setGeometry(props.geometry);
5877
5928
  }
5878
5929
  this.setUniforms(this._getModuleUniforms());
5879
- this.setProps(this.props);
5930
+ this.setProps(props);
5880
5931
  }
5881
5932
  destroy() {
5882
5933
  this.pipelineFactory.release(this.pipeline);
@@ -5885,9 +5936,8 @@ void main() {
5885
5936
  this.pipeline.draw({
5886
5937
  renderPass,
5887
5938
  vertexCount: this.vertexCount,
5888
- instanceCount: this.props.instanceCount
5939
+ instanceCount: this.instanceCount
5889
5940
  });
5890
- return this;
5891
5941
  }
5892
5942
  setProps(props) {
5893
5943
  if (props.indices) {
@@ -5905,40 +5955,33 @@ void main() {
5905
5955
  if (props.moduleSettings) {
5906
5956
  this.updateModuleSettings(props.moduleSettings);
5907
5957
  }
5908
- return this;
5909
5958
  }
5910
5959
  updateModuleSettings(props) {
5911
5960
  const uniforms = this._getModuleUniforms(props);
5912
5961
  this.setUniforms(uniforms);
5913
- return this;
5914
5962
  }
5915
5963
  setIndexBuffer(indices) {
5916
5964
  this.pipeline.setIndexBuffer(indices);
5917
- return this;
5918
5965
  }
5919
- setAttributes(attributes) {
5920
- if (attributes.indices) {
5921
- this.setIndexBuffer(attributes.indices);
5922
- attributes = {
5923
- ...attributes
5924
- };
5925
- delete attributes.indices;
5926
- console.warn("luma.gl: indices should not be part of attributes");
5966
+ setAttributes(bufferAttributes) {
5967
+ if (bufferAttributes.indices) {
5968
+ log.warn(`Model:${this.id} setAttributes() - indices should be set using setIndexBuffer()`);
5927
5969
  }
5928
- this.pipeline.setAttributes(attributes);
5929
- Object.assign(this.props.attributes, attributes);
5930
- return this;
5970
+ this.pipeline.setAttributes(bufferAttributes);
5971
+ Object.assign(this.bufferAttributes, bufferAttributes);
5972
+ }
5973
+ setConstantAttributes(constantAttributes) {
5974
+ this.pipeline.setConstantAttributes(constantAttributes);
5975
+ Object.assign(this.constantAttributes, constantAttributes);
5931
5976
  }
5932
5977
  /** Set the bindings */
5933
5978
  setBindings(bindings) {
5934
5979
  this.pipeline.setBindings(bindings);
5935
- Object.assign(this.props.bindings, bindings);
5936
- return this;
5980
+ Object.assign(this.bindings, bindings);
5937
5981
  }
5938
5982
  setUniforms(uniforms) {
5939
5983
  this.pipeline.setUniforms(uniforms);
5940
- Object.assign(this.props.uniforms, uniforms);
5941
- return this;
5984
+ Object.assign(this.uniforms, uniforms);
5942
5985
  }
5943
5986
  _setGeometry(geometry) {
5944
5987
  const geometryBuffers = getAttributeBuffersFromGeometry(this.device, geometry);
@@ -7413,8 +7456,8 @@ void main() {
7413
7456
  [GL.PIXEL_PACK_BUFFER_BINDING]: GL.PIXEL_PACK_BUFFER,
7414
7457
  [GL.PIXEL_UNPACK_BUFFER_BINDING]: GL.PIXEL_UNPACK_BUFFER
7415
7458
  };
7416
- const target = bindingMap[key];
7417
- gl.bindBuffer(target, value);
7459
+ const glTarget = bindingMap[key];
7460
+ gl.bindBuffer(glTarget, value);
7418
7461
  };
7419
7462
  function isArray2(array) {
7420
7463
  return Array.isArray(array) || ArrayBuffer.isView(array) && !(array instanceof DataView);
@@ -9535,8 +9578,14 @@ void main(void) {}`;
9535
9578
  // ../webgl/src/adapter/resources/webgl-buffer.ts
9536
9579
  var DEBUG_DATA_LENGTH = 10;
9537
9580
  var WEBGLBuffer = class extends Buffer2 {
9581
+ /** Target in OpenGL defines the type of buffer */
9582
+ /** Usage is a hint on how frequently the buffer will be updates */
9583
+ /** Index type is needed when issuing draw calls, so we pre-compute it */
9584
+ glIndexType = GL.UNSIGNED_SHORT;
9585
+ /** Number of bytes allocated on the GPU for this buffer */
9586
+ /** Number of bytes used */
9587
+ /** A partial CPU-side copy of the data in this buffer, for debugging purposes */
9538
9588
  debugData = null;
9539
- // accessor: {};
9540
9589
  constructor(device, props = {}) {
9541
9590
  super(device, props);
9542
9591
  this.device = device;
@@ -9548,8 +9597,9 @@ void main(void) {}`;
9548
9597
  ...this.props,
9549
9598
  data: typeof this.props.data
9550
9599
  });
9551
- this.target = this.props.target || getWebGLTarget(this.props.usage);
9552
- this.webglUsage = this.props.webglUsage || getWebGLUsage(this.props.usage);
9600
+ this.glTarget = getWebGLTarget(this.props.usage);
9601
+ this.glUsage = getWebGLUsage(this.props.usage);
9602
+ this.glIndexType = this.props.indexType === "uint32" ? GL.UNSIGNED_INT : GL.UNSIGNED_SHORT;
9553
9603
  this.debugData = null;
9554
9604
  if (props.data) {
9555
9605
  this._initWithData(props.data, props.byteOffset, props.byteLength);
@@ -9558,14 +9608,14 @@ void main(void) {}`;
9558
9608
  }
9559
9609
  }
9560
9610
  // PRIVATE METHODS
9561
- // Allocate a new buffer and initialize to contents of typed array
9611
+ /** Allocate a new buffer and initialize to contents of typed array */
9562
9612
  _initWithData(data, byteOffset = 0, byteLength = data.byteLength + byteOffset) {
9563
9613
  assert2(ArrayBuffer.isView(data));
9564
- const target = this._getWriteTarget();
9565
- this.gl.bindBuffer(target, this.handle);
9566
- this.gl.bufferData(target, byteLength, this.webglUsage);
9567
- this.gl.bufferSubData(target, byteOffset, data);
9568
- this.gl.bindBuffer(target, null);
9614
+ const glTarget = this._getWriteTarget();
9615
+ this.gl.bindBuffer(glTarget, this.handle);
9616
+ this.gl.bufferData(glTarget, byteLength, this.glUsage);
9617
+ this.gl.bufferSubData(glTarget, byteOffset, data);
9618
+ this.gl.bindBuffer(glTarget, null);
9569
9619
  this.debugData = data.slice(0, DEBUG_DATA_LENGTH);
9570
9620
  this.bytesUsed = byteLength;
9571
9621
  this.byteLength = byteLength;
@@ -9579,10 +9629,10 @@ void main(void) {}`;
9579
9629
  if (byteLength === 0) {
9580
9630
  data = new Float32Array(0);
9581
9631
  }
9582
- const target = this._getWriteTarget();
9583
- this.gl.bindBuffer(target, this.handle);
9584
- this.gl.bufferData(target, data, this.webglUsage);
9585
- this.gl.bindBuffer(target, null);
9632
+ const glTarget = this._getWriteTarget();
9633
+ this.gl.bindBuffer(glTarget, this.handle);
9634
+ this.gl.bufferData(glTarget, data, this.glUsage);
9635
+ this.gl.bindBuffer(glTarget, null);
9586
9636
  this.debugData = null;
9587
9637
  this.bytesUsed = byteLength;
9588
9638
  this.byteLength = byteLength;
@@ -9600,15 +9650,15 @@ void main(void) {}`;
9600
9650
  write(data, byteOffset = 0) {
9601
9651
  const srcOffset = 0;
9602
9652
  const byteLength = void 0;
9603
- const target = this.device.isWebGL2 ? GL.COPY_WRITE_BUFFER : this.target;
9604
- this.gl.bindBuffer(target, this.handle);
9653
+ const glTarget = this.device.isWebGL2 ? GL.COPY_WRITE_BUFFER : this.glTarget;
9654
+ this.gl.bindBuffer(glTarget, this.handle);
9605
9655
  if (srcOffset !== 0 || byteLength !== void 0) {
9606
9656
  this.device.assertWebGL2();
9607
- this.gl2.bufferSubData(target, byteOffset, data, srcOffset, byteLength);
9657
+ this.gl2.bufferSubData(glTarget, byteOffset, data, srcOffset, byteLength);
9608
9658
  } else {
9609
- this.gl.bufferSubData(target, byteOffset, data);
9659
+ this.gl.bufferSubData(glTarget, byteOffset, data);
9610
9660
  }
9611
- this.gl.bindBuffer(target, null);
9661
+ this.gl.bindBuffer(glTarget, null);
9612
9662
  }
9613
9663
  /** Read data from the buffer */
9614
9664
  async readAsync(byteOffset = 0, byteLength) {
@@ -9625,10 +9675,10 @@ void main(void) {}`;
9625
9675
  this.debugData = null;
9626
9676
  }
9627
9677
  _getWriteTarget() {
9628
- return this.target;
9678
+ return this.glTarget;
9629
9679
  }
9630
9680
  _getReadTarget() {
9631
- return this.target;
9681
+ return this.glTarget;
9632
9682
  }
9633
9683
  };
9634
9684
  function getWebGLTarget(usage) {
@@ -9653,7 +9703,7 @@ void main(void) {}`;
9653
9703
  if (usage & Buffer2.UNIFORM) {
9654
9704
  return GL.DYNAMIC_DRAW;
9655
9705
  }
9656
- return GL.DYNAMIC_DRAW;
9706
+ return GL.STATIC_DRAW;
9657
9707
  }
9658
9708
 
9659
9709
  // ../webgl/src/adapter/resources/webgl-sampler.ts
@@ -11074,7 +11124,7 @@ void main(void) {}`;
11074
11124
  return gl.luma;
11075
11125
  }
11076
11126
  async function loadWebGLDeveloperTools() {
11077
- if (!globalThis.WebGLDebugUtils) {
11127
+ if (isBrowser() && !globalThis.WebGLDebugUtils) {
11078
11128
  globalThis.global = globalThis.global || globalThis;
11079
11129
  globalThis.global.module = {};
11080
11130
  await loadScript(WEBGL_DEBUG_CDN_URL);
@@ -11371,9 +11421,6 @@ void main(void) {}`;
11371
11421
  const bufferProps = {
11372
11422
  ...props
11373
11423
  };
11374
- if (bufferProps.offset) {
11375
- bufferProps.byteOffset = bufferProps.offset;
11376
- }
11377
11424
  return bufferProps;
11378
11425
  }
11379
11426
  var BufferWithAccessor = class extends WEBGLBuffer {
@@ -11416,11 +11463,11 @@ void main(void) {}`;
11416
11463
  };
11417
11464
  }
11418
11465
  props = checkProps("Buffer", props, PROP_CHECKS_INITIALIZE);
11419
- this.webglUsage = props.webglUsage || GL.STATIC_DRAW;
11466
+ this.glUsage = props.glUsage || GL.STATIC_DRAW;
11420
11467
  this.debugData = null;
11421
11468
  this.setAccessor(Object.assign({}, props, props.accessor));
11422
11469
  if (props.data) {
11423
- this._setData(props.data, props.offset, props.byteLength);
11470
+ this._setData(props.data, props.byteOffset, props.byteLength);
11424
11471
  } else {
11425
11472
  this._setByteLength(props.byteLength || 0);
11426
11473
  }
@@ -11476,15 +11523,15 @@ void main(void) {}`;
11476
11523
  } = options;
11477
11524
  const byteLength = options.byteLength || options.length;
11478
11525
  assert2(data);
11479
- const target = this.gl.webgl2 ? GL.COPY_WRITE_BUFFER : this.target;
11480
- this.gl.bindBuffer(target, this.handle);
11526
+ const glTarget = this.gl.webgl2 ? GL.COPY_WRITE_BUFFER : this.glTarget;
11527
+ this.gl.bindBuffer(glTarget, this.handle);
11481
11528
  if (srcOffset !== 0 || byteLength !== void 0) {
11482
11529
  assertWebGL2Context(this.gl);
11483
- this.gl.bufferSubData(this.target, offset, data, srcOffset, byteLength);
11530
+ this.gl.bufferSubData(this.glTarget, offset, data, srcOffset, byteLength);
11484
11531
  } else {
11485
- this.gl.bufferSubData(target, offset, data);
11532
+ this.gl.bufferSubData(glTarget, offset, data);
11486
11533
  }
11487
- this.gl.bindBuffer(target, null);
11534
+ this.gl.bindBuffer(glTarget, null);
11488
11535
  this.debugData = null;
11489
11536
  this._inferType(data);
11490
11537
  return this;
@@ -11559,35 +11606,35 @@ void main(void) {}`;
11559
11606
  */
11560
11607
  bind(options) {
11561
11608
  const {
11562
- target = this.target,
11609
+ glTarget = this.glTarget,
11563
11610
  // target for the bind operation
11564
11611
  index = this.accessor && this.accessor.index,
11565
11612
  // index = index of target (indexed bind point)
11566
11613
  offset = 0,
11567
11614
  size
11568
11615
  } = options || {};
11569
- if (target === GL.UNIFORM_BUFFER || target === GL.TRANSFORM_FEEDBACK_BUFFER) {
11616
+ if (glTarget === GL.UNIFORM_BUFFER || glTarget === GL.TRANSFORM_FEEDBACK_BUFFER) {
11570
11617
  if (size !== void 0) {
11571
- this.gl2?.bindBufferRange(target, index, this.handle, offset, size);
11618
+ this.gl2?.bindBufferRange(glTarget, index, this.handle, offset, size);
11572
11619
  } else {
11573
11620
  assert2(offset === 0);
11574
- this.gl2?.bindBufferBase(target, index, this.handle);
11621
+ this.gl2?.bindBufferBase(glTarget, index, this.handle);
11575
11622
  }
11576
11623
  } else {
11577
- this.gl.bindBuffer(target, this.handle);
11624
+ this.gl.bindBuffer(glTarget, this.handle);
11578
11625
  }
11579
11626
  return this;
11580
11627
  }
11581
11628
  unbind(options) {
11582
11629
  const {
11583
- target = this.target,
11630
+ glTarget = this.glTarget,
11584
11631
  index = this.accessor && this.accessor.index
11585
11632
  } = options || {};
11586
- const isIndexedBuffer = target === GL.UNIFORM_BUFFER || target === GL.TRANSFORM_FEEDBACK_BUFFER;
11633
+ const isIndexedBuffer = glTarget === GL.UNIFORM_BUFFER || glTarget === GL.TRANSFORM_FEEDBACK_BUFFER;
11587
11634
  if (isIndexedBuffer) {
11588
- this.gl2?.bindBufferBase(target, index, null);
11635
+ this.gl2?.bindBufferBase(glTarget, index, null);
11589
11636
  } else {
11590
- this.gl.bindBuffer(target, null);
11637
+ this.gl.bindBuffer(glTarget, null);
11591
11638
  }
11592
11639
  return this;
11593
11640
  }
@@ -11618,7 +11665,7 @@ void main(void) {}`;
11618
11665
  this.trackDeallocatedMemory();
11619
11666
  const target = this._getTarget();
11620
11667
  this.gl.bindBuffer(target, this.handle);
11621
- this.gl.bufferData(target, byteLength, this.webglUsage);
11668
+ this.gl.bufferData(target, byteLength, this.glUsage);
11622
11669
  this.gl.bufferSubData(target, offset, data);
11623
11670
  this.gl.bindBuffer(target, null);
11624
11671
  this.debugData = data.slice(0, DEBUG_DATA_LENGTH2);
@@ -11633,18 +11680,17 @@ void main(void) {}`;
11633
11680
  return this;
11634
11681
  }
11635
11682
  // Allocate a GPU buffer of specified size.
11636
- _setByteLength(byteLength, webglUsage = this.webglUsage) {
11683
+ _setByteLength(byteLength) {
11637
11684
  assert2(byteLength >= 0);
11638
11685
  this.trackDeallocatedMemory();
11639
11686
  let data = byteLength;
11640
11687
  if (byteLength === 0) {
11641
11688
  data = new Float32Array(0);
11642
11689
  }
11643
- const target = this._getTarget();
11644
- this.gl.bindBuffer(target, this.handle);
11645
- this.gl.bufferData(target, data, webglUsage);
11646
- this.gl.bindBuffer(target, null);
11647
- this.webglUsage = webglUsage;
11690
+ const glTarget = this._getTarget();
11691
+ this.gl.bindBuffer(glTarget, this.handle);
11692
+ this.gl.bufferData(glTarget, data, this.glUsage);
11693
+ this.gl.bindBuffer(glTarget, null);
11648
11694
  this.debugData = null;
11649
11695
  this.bytesUsed = byteLength;
11650
11696
  this.byteLength = byteLength;
@@ -11654,7 +11700,7 @@ void main(void) {}`;
11654
11700
  // Binding a buffer for the first time locks the type
11655
11701
  // In WebGL2, use GL.COPY_WRITE_BUFFER to avoid locking the type
11656
11702
  _getTarget() {
11657
- return this.gl.webgl2 ? GL.COPY_WRITE_BUFFER : this.target;
11703
+ return this.gl.webgl2 ? GL.COPY_WRITE_BUFFER : this.glTarget;
11658
11704
  }
11659
11705
  _getAvailableElementCount(srcByteOffset) {
11660
11706
  const ArrayType = getTypedArrayFromGLType(this.accessor.type || GL.FLOAT, {
@@ -11674,9 +11720,9 @@ void main(void) {}`;
11674
11720
  }
11675
11721
  // RESOURCE METHODS
11676
11722
  getParameter(pname) {
11677
- this.gl.bindBuffer(this.target, this.handle);
11678
- const value = this.gl.getBufferParameter(this.target, pname);
11679
- this.gl.bindBuffer(this.target, null);
11723
+ this.gl.bindBuffer(this.glTarget, this.handle);
11724
+ const value = this.gl.getBufferParameter(this.glTarget, pname);
11725
+ this.gl.bindBuffer(this.glTarget, null);
11680
11726
  return value;
11681
11727
  }
11682
11728
  // DEPRECATIONS - v7.0
@@ -12022,12 +12068,12 @@ ${formattedLog}`)();
12022
12068
 
12023
12069
  // ../webgl/src/adapter/helpers/get-shader-layout.ts
12024
12070
  function getShaderLayout(gl, program) {
12025
- const programBindings = getProgramBindings(gl, program);
12026
12071
  const shaderLayout = {
12027
12072
  attributes: [],
12028
12073
  bindings: []
12029
12074
  };
12030
- for (const attribute of programBindings.attributes) {
12075
+ const attributes = readAttributeBindings(gl, program);
12076
+ for (const attribute of attributes) {
12031
12077
  const size = Math.min(attribute.accessor.size, 4);
12032
12078
  const format = (
12033
12079
  // attribute.accessor.format ||
@@ -12040,7 +12086,8 @@ ${formattedLog}`)();
12040
12086
  stepMode: attribute.accessor.divisor === 1 ? "instance" : "vertex"
12041
12087
  });
12042
12088
  }
12043
- for (const uniformBlock of programBindings.uniformBlocks) {
12089
+ const uniformBlocks = readUniformBlocks(gl, program);
12090
+ for (const uniformBlock of uniformBlocks) {
12044
12091
  const uniforms2 = uniformBlock.uniforms.map((uniform) => ({
12045
12092
  name: uniform.name,
12046
12093
  format: uniform.format,
@@ -12057,8 +12104,9 @@ ${formattedLog}`)();
12057
12104
  uniforms: uniforms2
12058
12105
  });
12059
12106
  }
12107
+ const uniforms = readUniformBindings(gl, program);
12060
12108
  let textureUnit = 0;
12061
- for (const uniform of programBindings.uniforms) {
12109
+ for (const uniform of uniforms) {
12062
12110
  if (isSamplerUniform(uniform.type)) {
12063
12111
  const {
12064
12112
  viewDimension,
@@ -12075,24 +12123,65 @@ ${formattedLog}`)();
12075
12123
  textureUnit += 1;
12076
12124
  }
12077
12125
  }
12078
- const uniforms = programBindings.uniforms?.filter((uniform) => uniform.location !== null) || [];
12079
12126
  if (uniforms.length) {
12080
12127
  shaderLayout.uniforms = uniforms;
12081
12128
  }
12082
- if (programBindings.varyings?.length) {
12083
- shaderLayout.varyings = programBindings.varyings;
12129
+ const varyings = readVaryings(gl, program);
12130
+ if (varyings?.length) {
12131
+ shaderLayout.varyings = varyings;
12084
12132
  }
12085
12133
  return shaderLayout;
12086
12134
  }
12087
- function getProgramBindings(gl, program) {
12088
- const config2 = {
12089
- attributes: readAttributeBindings(gl, program),
12090
- uniforms: readUniformBindings(gl, program),
12091
- uniformBlocks: readUniformBlocks(gl, program),
12092
- varyings: readVaryings(gl, program)
12135
+ function mergeShaderLayout(baseLayout, overrideLayout) {
12136
+ const mergedLayout = {
12137
+ ...baseLayout,
12138
+ attributes: baseLayout.attributes.map((attribute) => ({
12139
+ ...attribute
12140
+ }))
12093
12141
  };
12094
- Object.seal(config2);
12095
- return config2;
12142
+ for (const attribute of overrideLayout?.attributes || []) {
12143
+ const baseAttribute = mergedLayout.attributes.find((attr) => attr.name === attribute.name);
12144
+ if (!baseAttribute) {
12145
+ log.warn(`shader layout attribute ${attribute.name} not present in shader`);
12146
+ } else {
12147
+ baseAttribute.format = attribute.format || baseAttribute.format;
12148
+ baseAttribute.stepMode = attribute.stepMode || baseAttribute.stepMode;
12149
+ }
12150
+ }
12151
+ return mergedLayout;
12152
+ }
12153
+ function mergeBufferMap(baseLayout, bufferMap) {
12154
+ const mergedLayout = {
12155
+ ...baseLayout,
12156
+ attributes: baseLayout.attributes.map((attribute) => ({
12157
+ ...attribute
12158
+ }))
12159
+ };
12160
+ for (const bufferMapping of bufferMap) {
12161
+ switch (bufferMapping.type) {
12162
+ case "interleave":
12163
+ for (const attributeOverride of bufferMapping.attributes) {
12164
+ overrideShaderLayoutAttribute(mergedLayout, attributeOverride);
12165
+ }
12166
+ break;
12167
+ default:
12168
+ overrideShaderLayoutAttribute(mergedLayout, bufferMapping);
12169
+ }
12170
+ }
12171
+ return mergedLayout;
12172
+ }
12173
+ function overrideShaderLayoutAttribute(layout, attributeOverride) {
12174
+ const attribute = getAttributeFromLayout(layout, attributeOverride.name);
12175
+ if (attribute && attributeOverride.format) {
12176
+ attribute.format = attributeOverride.format;
12177
+ }
12178
+ }
12179
+ function getAttributeFromLayout(shaderLayout, name) {
12180
+ const attribute = shaderLayout.attributes.find((attr) => attr.name === name);
12181
+ if (!attribute) {
12182
+ log.warn(`shader layout attribute "${name}" not present in shader`);
12183
+ }
12184
+ return attribute || null;
12096
12185
  }
12097
12186
  function readAttributeBindings(gl, program) {
12098
12187
  const attributes = [];
@@ -12391,8 +12480,25 @@ ${formattedLog}`)();
12391
12480
  get [Symbol.toStringTag]() {
12392
12481
  return "BaseVertexArrayObject";
12393
12482
  }
12483
+ /** Buffer constant */
12484
+ buffer = null;
12485
+ bufferValue = null;
12486
+ static isConstantAttributeZeroSupported(device) {
12487
+ return device.info.type === "webgl2" || getBrowser() === "Chrome";
12488
+ }
12489
+ // Create a VertexArray
12394
12490
  constructor(device, props) {
12395
- super(device, props, {});
12491
+ super(device, props, {
12492
+ ...Resource.defaultProps,
12493
+ constantAttributeZero: false
12494
+ });
12495
+ Object.seal(this);
12496
+ }
12497
+ destroy() {
12498
+ super.destroy();
12499
+ if (this.buffer) {
12500
+ this.buffer?.destroy();
12501
+ }
12396
12502
  }
12397
12503
  _createHandle() {
12398
12504
  return this.gl2.createVertexArray();
@@ -12404,19 +12510,33 @@ ${formattedLog}`)();
12404
12510
  _bindHandle(handle) {
12405
12511
  this.gl2.bindVertexArray(handle);
12406
12512
  }
12513
+ /**
12514
+ * Enabling an attribute location makes it reference the currently bound buffer
12515
+ * Disabling an attribute location makes it reference the global constant value
12516
+ * TODO - handle single values for size 1 attributes?
12517
+ * TODO - convert classic arrays based on known type?
12518
+ */
12519
+ enable(location, enable2 = true) {
12520
+ const canDisableAttributeZero = this.device.isWebGL2 || getBrowser() === "Chrome";
12521
+ const canDisableAttribute = canDisableAttributeZero || location !== 0;
12522
+ if (enable2 || canDisableAttribute) {
12523
+ location = Number(location);
12524
+ this.bind(() => enable2 ? this.gl.enableVertexAttribArray(location) : this.gl.disableVertexAttribArray(location));
12525
+ }
12526
+ }
12407
12527
  // Set (bind) an elements buffer, for indexed rendering.
12408
12528
  // Must be a Buffer bound to GL.ELEMENT_ARRAY_BUFFER. Constants not supported
12409
12529
  setElementBuffer(elementBuffer = null, opts = {}) {
12410
- assert2(!elementBuffer || elementBuffer.target === GL.ELEMENT_ARRAY_BUFFER, ERR_ELEMENTS);
12530
+ assert2(!elementBuffer || elementBuffer.glTarget === GL.ELEMENT_ARRAY_BUFFER, ERR_ELEMENTS);
12411
12531
  this.bind(() => {
12412
12532
  this.gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, elementBuffer ? elementBuffer.handle : null);
12413
12533
  });
12414
- return this;
12415
12534
  }
12416
12535
  /** Set a location in vertex attributes array to a buffer, enables the location, sets divisor */
12417
12536
  setBuffer(location, buffer, accessor) {
12418
- if (buffer.target === GL.ELEMENT_ARRAY_BUFFER) {
12419
- return this.setElementBuffer(buffer, accessor);
12537
+ if (buffer.glTarget === GL.ELEMENT_ARRAY_BUFFER) {
12538
+ this.setElementBuffer(buffer, accessor);
12539
+ return;
12420
12540
  }
12421
12541
  const {
12422
12542
  size,
@@ -12443,33 +12563,127 @@ ${formattedLog}`)();
12443
12563
  gl.enableVertexAttribArray(location);
12444
12564
  gl2.vertexAttribDivisor(location, divisor || 0);
12445
12565
  });
12446
- return this;
12447
12566
  }
12448
12567
  /**
12449
- * Enabling an attribute location makes it reference the currently bound buffer
12450
- * Disabling an attribute location makes it reference the global constant value
12451
- * TODO - handle single values for size 1 attributes?
12452
- * TODO - convert classic arrays based on known type?
12568
+ * Set an attribute to a constant value
12569
+ * @param device
12570
+ * @param location
12571
+ * @param array
12572
+ *
12573
+ * @note Constants are stored globally on the WebGL context, not the VAO
12574
+ * so they need to be updated before every render
12575
+ * @todo - use known type (in configuration or passed in) to allow non-typed arrays?
12576
+ * @todo - remember/cache values to avoid setting them unnecessarily?
12453
12577
  */
12454
- enable(location, enable2 = true) {
12455
- const canDisableAttributeZero = this.device.isWebGL2 || getBrowser() === "Chrome";
12456
- const canDisableAttribute = canDisableAttributeZero || location !== 0;
12457
- if (enable2 || canDisableAttribute) {
12458
- location = Number(location);
12459
- this.bind(() => enable2 ? this.gl.enableVertexAttribArray(location) : this.gl.disableVertexAttribArray(location));
12578
+ setConstant(location, array) {
12579
+ switch (array.constructor) {
12580
+ case Float32Array:
12581
+ setConstantFloatArray(this.device, location, array);
12582
+ break;
12583
+ case Int32Array:
12584
+ setConstantIntArray(this.device, location, array);
12585
+ break;
12586
+ case Uint32Array:
12587
+ setConstantUintArray(this.device, location, array);
12588
+ break;
12589
+ default:
12590
+ assert2(false);
12460
12591
  }
12461
- return this;
12592
+ }
12593
+ /**
12594
+ * Provide a means to create a buffer that is equivalent to a constant.
12595
+ * NOTE: Desktop OpenGL cannot disable attribute 0.
12596
+ * https://stackoverflow.com/questions/20305231/webgl-warning-attribute-0-is-disabled-
12597
+ * this-has-significant-performance-penalty
12598
+ */
12599
+ getConstantBuffer(elementCount, value) {
12600
+ const constantValue = normalizeConstantArrayValue(value);
12601
+ const byteLength = constantValue.byteLength * elementCount;
12602
+ const length = constantValue.length * elementCount;
12603
+ let updateNeeded = !this.buffer;
12604
+ this.buffer = this.buffer || this.device.createBuffer({
12605
+ byteLength
12606
+ });
12607
+ updateNeeded = updateNeeded || this.buffer.reallocate(byteLength);
12608
+ updateNeeded = updateNeeded || !compareConstantArrayValues(constantValue, this.bufferValue);
12609
+ if (updateNeeded) {
12610
+ const typedArray = getScratchArray(value.constructor, length);
12611
+ fillArray({
12612
+ target: typedArray,
12613
+ source: constantValue,
12614
+ start: 0,
12615
+ count: length
12616
+ });
12617
+ this.buffer.subData(typedArray);
12618
+ this.bufferValue = value;
12619
+ }
12620
+ return this.buffer;
12462
12621
  }
12463
12622
  };
12623
+ function setConstantFloatArray(device, location, array) {
12624
+ switch (array.length) {
12625
+ case 1:
12626
+ device.gl.vertexAttrib1fv(location, array);
12627
+ break;
12628
+ case 2:
12629
+ device.gl.vertexAttrib2fv(location, array);
12630
+ break;
12631
+ case 3:
12632
+ device.gl.vertexAttrib3fv(location, array);
12633
+ break;
12634
+ case 4:
12635
+ device.gl.vertexAttrib4fv(location, array);
12636
+ break;
12637
+ default:
12638
+ assert2(false);
12639
+ }
12640
+ }
12641
+ function setConstantIntArray(device, location, array) {
12642
+ device.assertWebGL2();
12643
+ device.gl2?.vertexAttribI4iv(location, array);
12644
+ }
12645
+ function setConstantUintArray(device, location, array) {
12646
+ device.assertWebGL2();
12647
+ device.gl2?.vertexAttribI4uiv(location, array);
12648
+ }
12649
+ function normalizeConstantArrayValue(arrayValue) {
12650
+ if (Array.isArray(arrayValue)) {
12651
+ return new Float32Array(arrayValue);
12652
+ }
12653
+ return arrayValue;
12654
+ }
12655
+ function compareConstantArrayValues(v1, v2) {
12656
+ if (!v1 || !v2 || v1.length !== v2.length || v1.constructor !== v2.constructor) {
12657
+ return false;
12658
+ }
12659
+ for (let i = 0; i < v1.length; ++i) {
12660
+ if (v1[i] !== v2[i]) {
12661
+ return false;
12662
+ }
12663
+ }
12664
+ return true;
12665
+ }
12464
12666
 
12465
12667
  // ../webgl/src/adapter/resources/webgl-render-pipeline.ts
12466
12668
  var LOG_PROGRAM_PERF_PRIORITY = 4;
12467
12669
  var WEBGLRenderPipeline = class extends RenderPipeline {
12468
- // configuration: ProgramConfiguration;
12469
- // Experimental flag to avoid deleting Program object while it is cached
12470
- varyings = null;
12670
+ /** The WebGL device that created this render pipeline */
12671
+ /** Handle to underlying WebGL program */
12672
+ /** vertex shader */
12673
+ /** fragment shader */
12674
+ /** The merged layout */
12675
+ /** The layout extracted from shader by WebGL introspection APIs */
12676
+ /** Buffer map describing buffer interleaving etc */
12677
+ /** Uniforms set on this model */
12471
12678
  uniforms = {};
12679
+ /** Bindings set on this model */
12472
12680
  bindings = {};
12681
+ /** Any constant attributes */
12682
+ constantAttributes = {};
12683
+ /** Index buffer is stored separately */
12684
+ /** WebGL varyings */
12685
+ varyings = null;
12686
+ /** Stores attribute bindings */
12473
12687
  _textureUniforms = {};
12474
12688
  _textureIndexCounter = 0;
12475
12689
  _uniformCount = 0;
@@ -12494,7 +12708,10 @@ ${formattedLog}`)();
12494
12708
  this.device.gl2?.transformFeedbackVaryings(this.handle, varyings, bufferMode);
12495
12709
  }
12496
12710
  this._compileAndLink();
12497
- this.layout = props.layout || getShaderLayout(this.device.gl, this.handle);
12711
+ this.introspectedLayout = getShaderLayout(this.device.gl, this.handle);
12712
+ this.layout = mergeShaderLayout(this.introspectedLayout, props.layout);
12713
+ this.bufferMap = props.bufferMap || [];
12714
+ this.layout = mergeBufferMap(this.layout, this.bufferMap);
12498
12715
  this.vertexArrayObject = new WEBGLVertexArrayObject(this.device);
12499
12716
  }
12500
12717
  destroy() {
@@ -12506,7 +12723,7 @@ ${formattedLog}`)();
12506
12723
  setIndexBuffer(indexBuffer) {
12507
12724
  const webglBuffer = cast(indexBuffer);
12508
12725
  this.vertexArrayObject.setElementBuffer(webglBuffer);
12509
- this._indexBuffer = indexBuffer;
12726
+ this._indexBuffer = webglBuffer;
12510
12727
  }
12511
12728
  /** @todo needed for portable model */
12512
12729
  setAttributes(attributes) {
@@ -12538,7 +12755,27 @@ ${formattedLog}`)();
12538
12755
  });
12539
12756
  }
12540
12757
  }
12541
- /** @todo needed for portable model */
12758
+ /**
12759
+ * Constant attributes are only supported in WebGL, not in WebGPU
12760
+ * Any attribute that is disabled in the current vertex array object
12761
+ * is read from the context's global constant value for that attribute location.
12762
+ * @param attributes
12763
+ */
12764
+ setConstantAttributes(attributes) {
12765
+ for (const [name, value] of Object.entries(attributes)) {
12766
+ const attribute = getAttributeLayout(this.layout, name);
12767
+ if (!attribute) {
12768
+ log.warn(`Ignoring constant value supplied for unknown attribute "${name}" in pipeline "${this.id}"`)();
12769
+ continue;
12770
+ }
12771
+ this.vertexArrayObject.setConstant(attribute.location, value);
12772
+ }
12773
+ Object.assign(this.constantAttributes, attributes);
12774
+ }
12775
+ /**
12776
+ * Bindings include: textures, samplers and uniform buffers
12777
+ * @todo needed for portable model
12778
+ */
12542
12779
  setBindings(bindings) {
12543
12780
  for (const [name, value] of Object.entries(bindings)) {
12544
12781
  const binding = this.layout.bindings.find((binding2) => binding2.name === name);
@@ -12589,7 +12826,7 @@ ${formattedLog}`)();
12589
12826
  } = options;
12590
12827
  const drawMode = getDrawMode(this.props.topology);
12591
12828
  const isIndexed = Boolean(this._indexBuffer);
12592
- const indexType = this._indexBuffer?.props.indexType === "uint16" ? GL.UNSIGNED_SHORT : GL.UNSIGNED_INT;
12829
+ const indexType = this._indexBuffer?.glIndexType;
12593
12830
  const isInstanced = Number(options.instanceCount) > 0;
12594
12831
  if (!this._areTexturesRenderable() || options.vertexCount === 0) {
12595
12832
  return false;
@@ -12603,6 +12840,7 @@ ${formattedLog}`)();
12603
12840
  }
12604
12841
  this._applyBindings();
12605
12842
  this._applyUniforms();
12843
+ this._applyConstantAttributes();
12606
12844
  const webglRenderPass = renderPass;
12607
12845
  withDeviceParameters(this.device, this.props.parameters, () => {
12608
12846
  withGLParameters(this.device, webglRenderPass.glParameters, () => {
@@ -12736,6 +12974,21 @@ ${formattedLog}`)();
12736
12974
  }
12737
12975
  }
12738
12976
  }
12977
+ /**
12978
+ * Constant attributes are only supported in WebGL, not in WebGPU
12979
+ * Any attribute that is disabled in the current vertex array object
12980
+ * is read from the context's global constant value for that attribute location.
12981
+ */
12982
+ _applyConstantAttributes() {
12983
+ for (const [name, value] of Object.entries(this.constantAttributes)) {
12984
+ const attribute = getAttributeLayout(this.layout, name);
12985
+ if (!attribute) {
12986
+ log.warn(`Ignoring constant value supplied for unknown attribute "${name}" in pipeline "${this.id}"`)();
12987
+ continue;
12988
+ }
12989
+ this.vertexArrayObject.setConstant(attribute.location, value);
12990
+ }
12991
+ }
12739
12992
  };
12740
12993
  function getDrawMode(topology) {
12741
12994
  switch (topology) {
@@ -13074,10 +13327,13 @@ ${formattedLog}`)();
13074
13327
  if (typeof props.canvas === "string") {
13075
13328
  await CanvasContext.pageLoaded;
13076
13329
  }
13077
- if (props.debug) {
13330
+ if (log.get("debug") || props.debug) {
13078
13331
  await loadWebGLDeveloperTools();
13079
13332
  }
13080
- if (props.spector) {
13333
+ const {
13334
+ spector: spector2
13335
+ } = props;
13336
+ if (log.get("spector") || spector2) {
13081
13337
  await loadSpectorJS();
13082
13338
  }
13083
13339
  log.probe(LOG_LEVEL2 + 1, "DOM is loaded")();