@luma.gl/core 9.3.0-alpha.8 → 9.3.0

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 (63) hide show
  1. package/dist/adapter/device.d.ts +1 -1
  2. package/dist/adapter/device.d.ts.map +1 -1
  3. package/dist/adapter/device.js +3 -2
  4. package/dist/adapter/device.js.map +1 -1
  5. package/dist/adapter/luma.d.ts.map +1 -1
  6. package/dist/adapter/luma.js +2 -1
  7. package/dist/adapter/luma.js.map +1 -1
  8. package/dist/dist.dev.js +328 -173
  9. package/dist/dist.min.js +5 -5
  10. package/dist/factories/bind-group-factory.d.ts.map +1 -1
  11. package/dist/factories/bind-group-factory.js +14 -5
  12. package/dist/factories/bind-group-factory.js.map +1 -1
  13. package/dist/index.cjs +317 -173
  14. package/dist/index.cjs.map +4 -4
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/portable/shader-block-writer.d.ts +51 -0
  20. package/dist/portable/shader-block-writer.d.ts.map +1 -0
  21. package/dist/portable/shader-block-writer.js +185 -0
  22. package/dist/portable/shader-block-writer.js.map +1 -0
  23. package/dist/portable/uniform-store.d.ts +52 -20
  24. package/dist/portable/uniform-store.d.ts.map +1 -1
  25. package/dist/portable/uniform-store.js +71 -26
  26. package/dist/portable/uniform-store.js.map +1 -1
  27. package/dist/shadertypes/data-types/data-type-decoder.js +2 -2
  28. package/dist/shadertypes/data-types/data-type-decoder.js.map +1 -1
  29. package/dist/shadertypes/data-types/decode-data-types.js +2 -2
  30. package/dist/shadertypes/data-types/decode-data-types.js.map +1 -1
  31. package/dist/shadertypes/shader-types/shader-block-layout.d.ts +72 -0
  32. package/dist/shadertypes/shader-types/shader-block-layout.d.ts.map +1 -0
  33. package/dist/shadertypes/shader-types/shader-block-layout.js +209 -0
  34. package/dist/shadertypes/shader-types/shader-block-layout.js.map +1 -0
  35. package/dist/shadertypes/texture-types/texture-format-decoder.js +1 -1
  36. package/dist/shadertypes/texture-types/texture-format-decoder.js.map +1 -1
  37. package/dist/shadertypes/texture-types/texture-format-table.js +2 -2
  38. package/dist/shadertypes/texture-types/texture-format-table.js.map +1 -1
  39. package/dist/shadertypes/vertex-types/vertex-format-decoder.d.ts.map +1 -1
  40. package/dist/shadertypes/vertex-types/vertex-format-decoder.js +41 -3
  41. package/dist/shadertypes/vertex-types/vertex-format-decoder.js.map +1 -1
  42. package/dist/shadertypes/vertex-types/vertex-formats.d.ts +6 -6
  43. package/dist/shadertypes/vertex-types/vertex-formats.d.ts.map +1 -1
  44. package/package.json +2 -2
  45. package/src/adapter/device.ts +4 -2
  46. package/src/adapter/luma.ts +1 -0
  47. package/src/factories/bind-group-factory.ts +23 -5
  48. package/src/index.ts +7 -1
  49. package/src/portable/shader-block-writer.ts +254 -0
  50. package/src/portable/uniform-store.ts +92 -37
  51. package/src/shadertypes/data-types/data-type-decoder.ts +2 -2
  52. package/src/shadertypes/data-types/decode-data-types.ts +2 -2
  53. package/src/shadertypes/shader-types/shader-block-layout.ts +340 -0
  54. package/src/shadertypes/shader-types/shader-types.ts +5 -5
  55. package/src/shadertypes/texture-types/texture-format-decoder.ts +1 -1
  56. package/src/shadertypes/texture-types/texture-format-table.ts +2 -2
  57. package/src/shadertypes/vertex-types/vertex-format-decoder.ts +47 -3
  58. package/src/shadertypes/vertex-types/vertex-formats.ts +18 -5
  59. package/dist/portable/uniform-buffer-layout.d.ts +0 -42
  60. package/dist/portable/uniform-buffer-layout.d.ts.map +0 -1
  61. package/dist/portable/uniform-buffer-layout.js +0 -274
  62. package/dist/portable/uniform-buffer-layout.js.map +0 -1
  63. package/src/portable/uniform-buffer-layout.ts +0 -384
package/dist/index.cjs CHANGED
@@ -47,13 +47,13 @@ __export(dist_exports, {
47
47
  Resource: () => Resource,
48
48
  Sampler: () => Sampler,
49
49
  Shader: () => Shader,
50
+ ShaderBlockWriter: () => ShaderBlockWriter,
50
51
  ShaderFactory: () => ShaderFactory,
51
52
  SharedRenderPipeline: () => SharedRenderPipeline,
52
53
  Texture: () => Texture,
53
54
  TextureView: () => TextureView,
54
55
  TransformFeedback: () => TransformFeedback,
55
56
  UniformBlock: () => UniformBlock,
56
- UniformBufferLayout: () => UniformBufferLayout,
57
57
  UniformStore: () => UniformStore,
58
58
  VertexArray: () => VertexArray,
59
59
  _getDefaultBindGroupFactory: () => _getDefaultBindGroupFactory,
@@ -74,6 +74,7 @@ __export(dist_exports, {
74
74
  isExternalImage: () => isExternalImage,
75
75
  log: () => log,
76
76
  luma: () => luma,
77
+ makeShaderBlockLayout: () => makeShaderBlockLayout,
77
78
  normalizeBindingsByGroup: () => normalizeBindingsByGroup,
78
79
  readPixel: () => readPixel,
79
80
  setTextureImageData: () => setTextureImageData,
@@ -731,12 +732,51 @@ var VertexFormatDecoder = class {
731
732
  }
732
733
  return `${dataType}x${components}`;
733
734
  case "snorm8":
735
+ if (components === 1) {
736
+ return "snorm8";
737
+ }
738
+ if (components === 3) {
739
+ return "snorm8x3-webgl";
740
+ }
741
+ return `${dataType}x${components}`;
734
742
  case "uint8":
735
743
  case "sint8":
744
+ if (components === 1 || components === 3) {
745
+ throw new Error(`size: ${components}`);
746
+ }
747
+ return `${dataType}x${components}`;
736
748
  case "uint16":
749
+ if (components === 1) {
750
+ return "uint16";
751
+ }
752
+ if (components === 3) {
753
+ return "uint16x3-webgl";
754
+ }
755
+ return `${dataType}x${components}`;
737
756
  case "sint16":
757
+ if (components === 1) {
758
+ return "sint16";
759
+ }
760
+ if (components === 3) {
761
+ return "sint16x3-webgl";
762
+ }
763
+ return `${dataType}x${components}`;
738
764
  case "unorm16":
765
+ if (components === 1) {
766
+ return "unorm16";
767
+ }
768
+ if (components === 3) {
769
+ return "unorm16x3-webgl";
770
+ }
771
+ return `${dataType}x${components}`;
739
772
  case "snorm16":
773
+ if (components === 1) {
774
+ return "snorm16";
775
+ }
776
+ if (components === 3) {
777
+ return "snorm16x3-webgl";
778
+ }
779
+ return `${dataType}x${components}`;
740
780
  case "float16":
741
781
  if (components === 1 || components === 3) {
742
782
  throw new Error(`size: ${components}`);
@@ -1427,7 +1467,7 @@ or create a device with the 'debug: true' prop.`;
1427
1467
  throw new Error("_createBindGroupLayoutWebGPU() not implemented");
1428
1468
  }
1429
1469
  /** Internal WebGPU-only helper for creating a native bind group. */
1430
- _createBindGroupWebGPU(_bindGroupLayout, _shaderLayout, _bindings, _group) {
1470
+ _createBindGroupWebGPU(_bindGroupLayout, _shaderLayout, _bindings, _group, _label) {
1431
1471
  throw new Error("_createBindGroupWebGPU() not implemented");
1432
1472
  }
1433
1473
  /**
@@ -1652,7 +1692,7 @@ var _Luma = class {
1652
1692
  VERSION = (
1653
1693
  // Version detection using build plugin
1654
1694
  // @ts-expect-error no-undef
1655
- true ? "9.3.0-alpha.8" : "running from source"
1695
+ true ? "9.3.0" : "running from source"
1656
1696
  );
1657
1697
  spector;
1658
1698
  preregisteredAdapters = /* @__PURE__ */ new Map();
@@ -3608,9 +3648,10 @@ var BindGroupFactory = class {
3608
3648
  for (const group of getBindGroupIndicesUpToMax(pipeline.shaderLayout.bindings)) {
3609
3649
  const groupBindings = bindingsByGroup[group];
3610
3650
  const bindGroupLayout = this._getBindGroupLayout(pipeline, group);
3651
+ const bindGroupLabel = getBindGroupLabel(pipeline, pipeline.shaderLayout, group);
3611
3652
  if (!groupBindings || Object.keys(groupBindings).length === 0) {
3612
3653
  if (!hasBindingsInGroup(pipeline.shaderLayout.bindings, group)) {
3613
- resolvedBindGroups[group] = this._getEmptyBindGroup(bindGroupLayout, pipeline.shaderLayout, group);
3654
+ resolvedBindGroups[group] = this._getEmptyBindGroup(bindGroupLayout, pipeline.shaderLayout, group, bindGroupLabel);
3614
3655
  }
3615
3656
  continue;
3616
3657
  }
@@ -3621,11 +3662,11 @@ var BindGroupFactory = class {
3621
3662
  resolvedBindGroups[group] = layoutCache.bindGroupsBySource.get(bindGroupCacheKey) || null;
3622
3663
  continue;
3623
3664
  }
3624
- const bindGroup = this.device._createBindGroupWebGPU(bindGroupLayout, pipeline.shaderLayout, groupBindings, group);
3665
+ const bindGroup = this.device._createBindGroupWebGPU(bindGroupLayout, pipeline.shaderLayout, groupBindings, group, bindGroupLabel);
3625
3666
  layoutCache.bindGroupsBySource.set(bindGroupCacheKey, bindGroup);
3626
3667
  resolvedBindGroups[group] = bindGroup;
3627
3668
  } else {
3628
- resolvedBindGroups[group] = this.device._createBindGroupWebGPU(bindGroupLayout, pipeline.shaderLayout, groupBindings, group);
3669
+ resolvedBindGroups[group] = this.device._createBindGroupWebGPU(bindGroupLayout, pipeline.shaderLayout, groupBindings, group, bindGroupLabel);
3629
3670
  }
3630
3671
  }
3631
3672
  return resolvedBindGroups;
@@ -3639,9 +3680,9 @@ var BindGroupFactory = class {
3639
3680
  layoutCache[group] ||= this.device._createBindGroupLayoutWebGPU(pipeline, group);
3640
3681
  return layoutCache[group];
3641
3682
  }
3642
- _getEmptyBindGroup(bindGroupLayout, shaderLayout, group) {
3683
+ _getEmptyBindGroup(bindGroupLayout, shaderLayout, group, label) {
3643
3684
  const layoutCache = this._getLayoutBindGroupCache(bindGroupLayout);
3644
- layoutCache.emptyBindGroup ||= this.device._createBindGroupWebGPU(bindGroupLayout, shaderLayout, {}, group) || null;
3685
+ layoutCache.emptyBindGroup ||= this.device._createBindGroupWebGPU(bindGroupLayout, shaderLayout, {}, group, label) || null;
3645
3686
  return layoutCache.emptyBindGroup;
3646
3687
  }
3647
3688
  _getLayoutBindGroupCache(bindGroupLayout) {
@@ -3664,6 +3705,11 @@ function getBindGroupIndicesUpToMax(bindings) {
3664
3705
  function hasBindingsInGroup(bindings, group) {
3665
3706
  return bindings.some((binding) => binding.group === group);
3666
3707
  }
3708
+ function getBindGroupLabel(pipeline, shaderLayout, group) {
3709
+ const bindingNames = shaderLayout.bindings.filter((binding) => binding.group === group).sort((left, right) => left.location - right.location).map((binding) => binding.name);
3710
+ const bindingSuffix = bindingNames.length > 0 ? bindingNames.join(",") : "empty";
3711
+ return `${pipeline.id}/group${group}[${bindingSuffix}]`;
3712
+ }
3667
3713
 
3668
3714
  // dist/adapter/resources/render-pass.js
3669
3715
  var _RenderPass = class extends Resource {
@@ -4257,6 +4303,160 @@ var NORMALIZED_TYPE_MAP2 = {
4257
4303
  sint32: ["sint32", "i32", 4, false, Int32Array]
4258
4304
  };
4259
4305
 
4306
+ // dist/shadertypes/shader-types/shader-block-layout.js
4307
+ function makeShaderBlockLayout(uniformTypes, options = {}) {
4308
+ const copiedUniformTypes = { ...uniformTypes };
4309
+ const layout = options.layout ?? "std140";
4310
+ const fields = {};
4311
+ let size = 0;
4312
+ for (const [key, uniformType] of Object.entries(copiedUniformTypes)) {
4313
+ size = addToLayout(fields, key, uniformType, size, layout);
4314
+ }
4315
+ size = alignTo(size, getTypeAlignment(copiedUniformTypes, layout));
4316
+ return {
4317
+ layout,
4318
+ byteLength: size * 4,
4319
+ uniformTypes: copiedUniformTypes,
4320
+ fields
4321
+ };
4322
+ }
4323
+ function getLeafLayoutInfo(type, layout) {
4324
+ const resolvedType = resolveVariableShaderTypeAlias(type);
4325
+ const decodedType = getVariableShaderTypeInfo(resolvedType);
4326
+ const matrixMatch = /^mat(\d)x(\d)<.+>$/.exec(resolvedType);
4327
+ if (matrixMatch) {
4328
+ const columns = Number(matrixMatch[1]);
4329
+ const rows = Number(matrixMatch[2]);
4330
+ const columnInfo = getVectorLayoutInfo(rows, resolvedType, decodedType.type, layout);
4331
+ const columnStride = getMatrixColumnStride(columnInfo.size, columnInfo.alignment, layout);
4332
+ return {
4333
+ alignment: columnInfo.alignment,
4334
+ size: columns * columnStride,
4335
+ components: columns * rows,
4336
+ columns,
4337
+ rows,
4338
+ columnStride,
4339
+ shaderType: resolvedType,
4340
+ type: decodedType.type
4341
+ };
4342
+ }
4343
+ const vectorMatch = /^vec(\d)<.+>$/.exec(resolvedType);
4344
+ if (vectorMatch) {
4345
+ return getVectorLayoutInfo(Number(vectorMatch[1]), resolvedType, decodedType.type, layout);
4346
+ }
4347
+ return {
4348
+ alignment: 1,
4349
+ size: 1,
4350
+ components: 1,
4351
+ columns: 1,
4352
+ rows: 1,
4353
+ columnStride: 1,
4354
+ shaderType: resolvedType,
4355
+ type: decodedType.type
4356
+ };
4357
+ }
4358
+ function isCompositeShaderTypeStruct(value) {
4359
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4360
+ }
4361
+ function addToLayout(fields, name2, type, offset, layout) {
4362
+ if (typeof type === "string") {
4363
+ const info = getLeafLayoutInfo(type, layout);
4364
+ const alignedOffset = alignTo(offset, info.alignment);
4365
+ fields[name2] = {
4366
+ offset: alignedOffset,
4367
+ ...info
4368
+ };
4369
+ return alignedOffset + info.size;
4370
+ }
4371
+ if (Array.isArray(type)) {
4372
+ if (Array.isArray(type[0])) {
4373
+ throw new Error(`Nested arrays are not supported for ${name2}`);
4374
+ }
4375
+ const elementType = type[0];
4376
+ const length = type[1];
4377
+ const stride = getArrayStride(elementType, layout);
4378
+ const arrayOffset = alignTo(offset, getTypeAlignment(type, layout));
4379
+ for (let i = 0; i < length; i++) {
4380
+ addToLayout(fields, `${name2}[${i}]`, elementType, arrayOffset + i * stride, layout);
4381
+ }
4382
+ return arrayOffset + stride * length;
4383
+ }
4384
+ if (isCompositeShaderTypeStruct(type)) {
4385
+ const structAlignment = getTypeAlignment(type, layout);
4386
+ let structOffset = alignTo(offset, structAlignment);
4387
+ for (const [memberName, memberType] of Object.entries(type)) {
4388
+ structOffset = addToLayout(fields, `${name2}.${memberName}`, memberType, structOffset, layout);
4389
+ }
4390
+ return alignTo(structOffset, structAlignment);
4391
+ }
4392
+ throw new Error(`Unsupported CompositeShaderType for ${name2}`);
4393
+ }
4394
+ function getTypeSize(type, layout) {
4395
+ if (typeof type === "string") {
4396
+ return getLeafLayoutInfo(type, layout).size;
4397
+ }
4398
+ if (Array.isArray(type)) {
4399
+ const elementType = type[0];
4400
+ const length = type[1];
4401
+ if (Array.isArray(elementType)) {
4402
+ throw new Error("Nested arrays are not supported");
4403
+ }
4404
+ return getArrayStride(elementType, layout) * length;
4405
+ }
4406
+ let size = 0;
4407
+ for (const memberType of Object.values(type)) {
4408
+ const compositeMemberType = memberType;
4409
+ size = alignTo(size, getTypeAlignment(compositeMemberType, layout));
4410
+ size += getTypeSize(compositeMemberType, layout);
4411
+ }
4412
+ return alignTo(size, getTypeAlignment(type, layout));
4413
+ }
4414
+ function getTypeAlignment(type, layout) {
4415
+ if (typeof type === "string") {
4416
+ return getLeafLayoutInfo(type, layout).alignment;
4417
+ }
4418
+ if (Array.isArray(type)) {
4419
+ const elementType = type[0];
4420
+ const elementAlignment = getTypeAlignment(elementType, layout);
4421
+ return uses16ByteArrayAlignment(layout) ? Math.max(elementAlignment, 4) : elementAlignment;
4422
+ }
4423
+ let maxAlignment = 1;
4424
+ for (const memberType of Object.values(type)) {
4425
+ const memberAlignment = getTypeAlignment(memberType, layout);
4426
+ maxAlignment = Math.max(maxAlignment, memberAlignment);
4427
+ }
4428
+ return uses16ByteStructAlignment(layout) ? Math.max(maxAlignment, 4) : maxAlignment;
4429
+ }
4430
+ function getVectorLayoutInfo(components, shaderType, type, layout) {
4431
+ return {
4432
+ alignment: components === 2 ? 2 : 4,
4433
+ size: components === 3 ? 3 : components,
4434
+ components,
4435
+ columns: 1,
4436
+ rows: components,
4437
+ columnStride: components === 3 ? 3 : components,
4438
+ shaderType,
4439
+ type
4440
+ };
4441
+ }
4442
+ function getArrayStride(elementType, layout) {
4443
+ const elementSize = getTypeSize(elementType, layout);
4444
+ const elementAlignment = getTypeAlignment(elementType, layout);
4445
+ return getArrayLikeStride(elementSize, elementAlignment, layout);
4446
+ }
4447
+ function getArrayLikeStride(size, alignment, layout) {
4448
+ return alignTo(size, uses16ByteArrayAlignment(layout) ? 4 : alignment);
4449
+ }
4450
+ function getMatrixColumnStride(size, alignment, layout) {
4451
+ return layout === "std140" ? 4 : alignTo(size, alignment);
4452
+ }
4453
+ function uses16ByteArrayAlignment(layout) {
4454
+ return layout === "std140" || layout === "wgsl-uniform";
4455
+ }
4456
+ function uses16ByteStructAlignment(layout) {
4457
+ return layout === "std140" || layout === "wgsl-uniform";
4458
+ }
4459
+
4260
4460
  // dist/utils/array-utils-flat.js
4261
4461
  var arrayBuffer;
4262
4462
  function getScratchArrayBuffer(byteLength) {
@@ -4281,49 +4481,56 @@ function isNumberArray(value) {
4281
4481
  return isTypedArray(value);
4282
4482
  }
4283
4483
 
4284
- // dist/portable/uniform-buffer-layout.js
4285
- var minBufferSize = 1024;
4286
- var UniformBufferLayout = class {
4287
- layout = {};
4288
- uniformTypes;
4289
- /** number of bytes needed for buffer allocation */
4290
- byteLength;
4291
- /** Create a new UniformBufferLayout given a map of attributes. */
4292
- constructor(uniformTypes) {
4293
- this.uniformTypes = { ...uniformTypes };
4294
- let size = 0;
4295
- for (const [key, uniformType] of Object.entries(this.uniformTypes)) {
4296
- size = this._addToLayout(key, uniformType, size);
4297
- }
4298
- size = alignTo(size, 4);
4299
- this.byteLength = Math.max(size * 4, minBufferSize);
4300
- }
4301
- /** Does this layout have a field with specified name */
4484
+ // dist/portable/shader-block-writer.js
4485
+ var ShaderBlockWriter = class {
4486
+ /** Layout metadata used to flatten and serialize values. */
4487
+ layout;
4488
+ /**
4489
+ * Creates a writer for a precomputed shader-block layout.
4490
+ */
4491
+ constructor(layout) {
4492
+ this.layout = layout;
4493
+ }
4494
+ /**
4495
+ * Returns `true` if the flattened layout contains the given field.
4496
+ */
4302
4497
  has(name2) {
4303
- return Boolean(this.layout[name2]);
4498
+ return Boolean(this.layout.fields[name2]);
4304
4499
  }
4305
- /** Get offset and size for a field with specified name */
4500
+ /**
4501
+ * Returns offset and size metadata for a flattened field.
4502
+ */
4306
4503
  get(name2) {
4307
- const layout = this.layout[name2];
4308
- return layout;
4504
+ const entry = this.layout.fields[name2];
4505
+ return entry ? { offset: entry.offset, size: entry.size } : void 0;
4309
4506
  }
4310
- /** Flatten nested uniform values into leaf-path values understood by UniformBlock. */
4507
+ /**
4508
+ * Flattens nested composite values into leaf-path values understood by {@link UniformBlock}.
4509
+ *
4510
+ * Top-level values may be supplied either in nested object form matching the
4511
+ * declared composite shader types or as already-flattened leaf-path values.
4512
+ */
4311
4513
  getFlatUniformValues(uniformValues) {
4312
4514
  const flattenedUniformValues = {};
4313
4515
  for (const [name2, value] of Object.entries(uniformValues)) {
4314
- const uniformType = this.uniformTypes[name2];
4516
+ const uniformType = this.layout.uniformTypes[name2];
4315
4517
  if (uniformType) {
4316
4518
  this._flattenCompositeValue(flattenedUniformValues, name2, uniformType, value);
4317
- } else if (this.layout[name2]) {
4519
+ } else if (this.layout.fields[name2]) {
4318
4520
  flattenedUniformValues[name2] = value;
4319
4521
  }
4320
4522
  }
4321
4523
  return flattenedUniformValues;
4322
4524
  }
4323
- /** Get the data for the complete buffer */
4525
+ /**
4526
+ * Serializes the supplied values into buffer-backed binary data.
4527
+ *
4528
+ * The returned view length matches {@link ShaderBlockLayout.byteLength}, which
4529
+ * is the exact packed size of the block.
4530
+ */
4324
4531
  getData(uniformValues) {
4325
- const buffer = getScratchArrayBuffer(this.byteLength);
4326
- new Uint8Array(buffer, 0, this.byteLength).fill(0);
4532
+ const buffer = getScratchArrayBuffer(this.layout.byteLength);
4533
+ new Uint8Array(buffer, 0, this.layout.byteLength).fill(0);
4327
4534
  const typedArrays = {
4328
4535
  i32: new Int32Array(buffer),
4329
4536
  u32: new Uint32Array(buffer),
@@ -4334,46 +4541,16 @@ var UniformBufferLayout = class {
4334
4541
  for (const [name2, value] of Object.entries(flattenedUniformValues)) {
4335
4542
  this._writeLeafValue(typedArrays, name2, value);
4336
4543
  }
4337
- return new Uint8Array(buffer, 0, this.byteLength);
4338
- }
4339
- // Recursively add a uniform to the layout
4340
- _addToLayout(name2, type, offset) {
4341
- if (typeof type === "string") {
4342
- const info = getLeafLayoutInfo(type);
4343
- const alignedOffset = alignTo(offset, info.alignment);
4344
- this.layout[name2] = {
4345
- offset: alignedOffset,
4346
- ...info
4347
- };
4348
- return alignedOffset + info.size;
4349
- }
4350
- if (Array.isArray(type)) {
4351
- if (Array.isArray(type[0])) {
4352
- throw new Error(`Nested arrays are not supported for ${name2}`);
4353
- }
4354
- const elementType = type[0];
4355
- const length = type[1];
4356
- const stride = alignTo(getTypeSize(elementType), 4);
4357
- const arrayOffset = alignTo(offset, 4);
4358
- for (let i = 0; i < length; i++) {
4359
- this._addToLayout(`${name2}[${i}]`, elementType, arrayOffset + i * stride);
4360
- }
4361
- return arrayOffset + stride * length;
4362
- }
4363
- if (isCompositeShaderTypeStruct(type)) {
4364
- let structOffset = alignTo(offset, 4);
4365
- for (const [memberName, memberType] of Object.entries(type)) {
4366
- structOffset = this._addToLayout(`${name2}.${memberName}`, memberType, structOffset);
4367
- }
4368
- return alignTo(structOffset, 4);
4369
- }
4370
- throw new Error(`Unsupported CompositeShaderType for ${name2}`);
4544
+ return new Uint8Array(buffer, 0, this.layout.byteLength);
4371
4545
  }
4546
+ /**
4547
+ * Recursively flattens nested values using the declared composite shader type.
4548
+ */
4372
4549
  _flattenCompositeValue(flattenedUniformValues, baseName, uniformType, value) {
4373
4550
  if (value === void 0) {
4374
4551
  return;
4375
4552
  }
4376
- if (typeof uniformType === "string" || this.layout[baseName]) {
4553
+ if (typeof uniformType === "string" || this.layout.fields[baseName]) {
4377
4554
  flattenedUniformValues[baseName] = value;
4378
4555
  return;
4379
4556
  }
@@ -4412,9 +4589,12 @@ var UniformBufferLayout = class {
4412
4589
  }
4413
4590
  log.warn(`Unsupported uniform value for ${baseName}:`, value)();
4414
4591
  }
4592
+ /**
4593
+ * Expands tightly packed numeric arrays into per-element leaf fields.
4594
+ */
4415
4595
  _flattenPackedArray(flattenedUniformValues, baseName, elementType, length, value) {
4416
4596
  const numericValue = value;
4417
- const elementLayout = getLeafLayoutInfo(elementType);
4597
+ const elementLayout = getLeafLayoutInfo(elementType, this.layout.layout);
4418
4598
  const packedElementLength = elementLayout.components;
4419
4599
  for (let index = 0; index < length; index++) {
4420
4600
  const start = index * packedElementLength;
@@ -4428,13 +4608,16 @@ var UniformBufferLayout = class {
4428
4608
  }
4429
4609
  }
4430
4610
  }
4611
+ /**
4612
+ * Writes one flattened leaf value into its typed-array view.
4613
+ */
4431
4614
  _writeLeafValue(typedArrays, name2, value) {
4432
- const layout = this.layout[name2];
4433
- if (!layout) {
4615
+ const entry = this.layout.fields[name2];
4616
+ if (!entry) {
4434
4617
  log.warn(`Uniform ${name2} not found in layout`)();
4435
4618
  return;
4436
4619
  }
4437
- const { type, components, columns, rows, offset } = layout;
4620
+ const { type, components, columns, rows, offset, columnStride } = entry;
4438
4621
  const array = typedArrays[type];
4439
4622
  if (components === 1) {
4440
4623
  array[offset] = Number(value);
@@ -4449,85 +4632,13 @@ var UniformBufferLayout = class {
4449
4632
  }
4450
4633
  let sourceIndex = 0;
4451
4634
  for (let columnIndex = 0; columnIndex < columns; columnIndex++) {
4452
- const columnOffset = offset + columnIndex * 4;
4635
+ const columnOffset = offset + columnIndex * columnStride;
4453
4636
  for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
4454
4637
  array[columnOffset + rowIndex] = Number(sourceValue[sourceIndex++] ?? 0);
4455
4638
  }
4456
4639
  }
4457
4640
  }
4458
4641
  };
4459
- function getTypeSize(type) {
4460
- if (typeof type === "string") {
4461
- return getLeafLayoutInfo(type).size;
4462
- }
4463
- if (Array.isArray(type)) {
4464
- const elementType = type[0];
4465
- const length = type[1];
4466
- if (Array.isArray(elementType)) {
4467
- throw new Error("Nested arrays are not supported");
4468
- }
4469
- return alignTo(getTypeSize(elementType), 4) * length;
4470
- }
4471
- let size = 0;
4472
- for (const memberType of Object.values(type)) {
4473
- const compositeMemberType = memberType;
4474
- size = alignTo(size, getTypeAlignment(compositeMemberType));
4475
- size += getTypeSize(compositeMemberType);
4476
- }
4477
- return alignTo(size, 4);
4478
- }
4479
- function getTypeAlignment(type) {
4480
- if (typeof type === "string") {
4481
- return getLeafLayoutInfo(type).alignment;
4482
- }
4483
- if (Array.isArray(type)) {
4484
- return 4;
4485
- }
4486
- return 4;
4487
- }
4488
- function getLeafLayoutInfo(type) {
4489
- const resolvedType = resolveVariableShaderTypeAlias(type);
4490
- const decodedType = getVariableShaderTypeInfo(resolvedType);
4491
- const matrixMatch = /^mat(\d)x(\d)<.+>$/.exec(resolvedType);
4492
- if (matrixMatch) {
4493
- const columns = Number(matrixMatch[1]);
4494
- const rows = Number(matrixMatch[2]);
4495
- return {
4496
- alignment: 4,
4497
- size: columns * 4,
4498
- components: columns * rows,
4499
- columns,
4500
- rows,
4501
- shaderType: resolvedType,
4502
- type: decodedType.type
4503
- };
4504
- }
4505
- const vectorMatch = /^vec(\d)<.+>$/.exec(resolvedType);
4506
- if (vectorMatch) {
4507
- const components = Number(vectorMatch[1]);
4508
- return {
4509
- alignment: components === 2 ? 2 : 4,
4510
- size: components === 3 ? 4 : components,
4511
- components,
4512
- columns: 1,
4513
- rows: components,
4514
- shaderType: resolvedType,
4515
- type: decodedType.type
4516
- };
4517
- }
4518
- return {
4519
- alignment: 1,
4520
- size: 1,
4521
- components: 1,
4522
- columns: 1,
4523
- rows: 1,
4524
- shaderType: resolvedType,
4525
- type: decodedType.type
4526
- };
4527
- }
4528
- function isCompositeShaderTypeStruct(value) {
4529
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4530
- }
4531
4642
  function isCompositeUniformObject(value) {
4532
4643
  return Boolean(value) && typeof value === "object" && !Array.isArray(value) && !ArrayBuffer.isView(value);
4533
4644
  }
@@ -4619,24 +4730,33 @@ var UniformBlock = class {
4619
4730
  };
4620
4731
 
4621
4732
  // dist/portable/uniform-store.js
4733
+ var minUniformBufferSize = 1024;
4622
4734
  var UniformStore = class {
4735
+ /** Device used to infer layout and allocate buffers. */
4736
+ device;
4623
4737
  /** Stores the uniform values for each uniform block */
4624
4738
  uniformBlocks = /* @__PURE__ */ new Map();
4625
- /** Can generate data for a uniform buffer for each block from data */
4626
- uniformBufferLayouts = /* @__PURE__ */ new Map();
4739
+ /** Flattened layout metadata for each block. */
4740
+ shaderBlockLayouts = /* @__PURE__ */ new Map();
4741
+ /** Serializers for block-backed uniform data. */
4742
+ shaderBlockWriters = /* @__PURE__ */ new Map();
4627
4743
  /** Actual buffer for the blocks */
4628
4744
  uniformBuffers = /* @__PURE__ */ new Map();
4629
4745
  /**
4630
- * Create a new UniformStore instance
4631
- * @param blocks
4746
+ * Creates a new {@link UniformStore} for the supplied device and block definitions.
4632
4747
  */
4633
- constructor(blocks) {
4748
+ constructor(device, blocks) {
4749
+ this.device = device;
4634
4750
  for (const [bufferName, block] of Object.entries(blocks)) {
4635
4751
  const uniformBufferName = bufferName;
4636
- const uniformBufferLayout = new UniformBufferLayout(block.uniformTypes ?? {});
4637
- this.uniformBufferLayouts.set(uniformBufferName, uniformBufferLayout);
4752
+ const shaderBlockLayout = makeShaderBlockLayout(block.uniformTypes ?? {}, {
4753
+ layout: block.layout ?? getDefaultUniformBufferLayout(device)
4754
+ });
4755
+ const shaderBlockWriter = new ShaderBlockWriter(shaderBlockLayout);
4756
+ this.shaderBlockLayouts.set(uniformBufferName, shaderBlockLayout);
4757
+ this.shaderBlockWriters.set(uniformBufferName, shaderBlockWriter);
4638
4758
  const uniformBlock = new UniformBlock({ name: bufferName });
4639
- uniformBlock.setUniforms(uniformBufferLayout.getFlatUniformValues(block.defaultUniforms || {}));
4759
+ uniformBlock.setUniforms(shaderBlockWriter.getFlatUniformValues(block.defaultUniforms || {}));
4640
4760
  this.uniformBlocks.set(uniformBufferName, uniformBlock);
4641
4761
  }
4642
4762
  }
@@ -4648,39 +4768,52 @@ var UniformStore = class {
4648
4768
  }
4649
4769
  /**
4650
4770
  * Set uniforms
4651
- * Makes all properties partial
4771
+ *
4772
+ * Makes all group properties partial and eagerly propagates changes to any
4773
+ * managed GPU buffers.
4652
4774
  */
4653
4775
  setUniforms(uniforms) {
4654
4776
  var _a;
4655
4777
  for (const [blockName, uniformValues] of Object.entries(uniforms)) {
4656
4778
  const uniformBufferName = blockName;
4657
- const uniformBufferLayout = this.uniformBufferLayouts.get(uniformBufferName);
4658
- const flattenedUniforms = uniformBufferLayout == null ? void 0 : uniformBufferLayout.getFlatUniformValues(uniformValues || {});
4779
+ const shaderBlockWriter = this.shaderBlockWriters.get(uniformBufferName);
4780
+ const flattenedUniforms = shaderBlockWriter == null ? void 0 : shaderBlockWriter.getFlatUniformValues(uniformValues || {});
4659
4781
  (_a = this.uniformBlocks.get(uniformBufferName)) == null ? void 0 : _a.setUniforms(flattenedUniforms || {});
4660
4782
  }
4661
4783
  this.updateUniformBuffers();
4662
4784
  }
4663
- /** Get the required minimum length of the uniform buffer */
4785
+ /**
4786
+ * Returns the allocation size for the named uniform buffer.
4787
+ *
4788
+ * This may exceed the packed layout size because minimum buffer-size policy is
4789
+ * applied at the store layer.
4790
+ */
4664
4791
  getUniformBufferByteLength(uniformBufferName) {
4665
4792
  var _a;
4666
- return ((_a = this.uniformBufferLayouts.get(uniformBufferName)) == null ? void 0 : _a.byteLength) || 0;
4793
+ const packedByteLength = ((_a = this.shaderBlockLayouts.get(uniformBufferName)) == null ? void 0 : _a.byteLength) || 0;
4794
+ return Math.max(packedByteLength, minUniformBufferSize);
4667
4795
  }
4668
- /** Get formatted binary memory that can be uploaded to a buffer */
4796
+ /**
4797
+ * Returns packed binary data that can be uploaded to the named uniform buffer.
4798
+ *
4799
+ * The returned view length matches the packed block size and is not padded to
4800
+ * the store's minimum allocation size.
4801
+ */
4669
4802
  getUniformBufferData(uniformBufferName) {
4670
- var _a, _b;
4803
+ var _a;
4671
4804
  const uniformValues = ((_a = this.uniformBlocks.get(uniformBufferName)) == null ? void 0 : _a.getAllUniforms()) || {};
4672
- return (_b = this.uniformBufferLayouts.get(uniformBufferName)) == null ? void 0 : _b.getData(uniformValues);
4805
+ const shaderBlockWriter = this.shaderBlockWriters.get(uniformBufferName);
4806
+ return (shaderBlockWriter == null ? void 0 : shaderBlockWriter.getData(uniformValues)) || new Uint8Array(0);
4673
4807
  }
4674
4808
  /**
4675
- * Creates an unmanaged uniform buffer (umnanaged means that application is responsible for destroying it)
4676
- * The new buffer is initialized with current / supplied values
4809
+ * Creates an unmanaged uniform buffer initialized with the current or supplied values.
4677
4810
  */
4678
- createUniformBuffer(device, uniformBufferName, uniforms) {
4811
+ createUniformBuffer(uniformBufferName, uniforms) {
4679
4812
  if (uniforms) {
4680
4813
  this.setUniforms(uniforms);
4681
4814
  }
4682
4815
  const byteLength = this.getUniformBufferByteLength(uniformBufferName);
4683
- const uniformBuffer = device.createBuffer({
4816
+ const uniformBuffer = this.device.createBuffer({
4684
4817
  usage: Buffer2.UNIFORM | Buffer2.COPY_DST,
4685
4818
  byteLength
4686
4819
  });
@@ -4688,11 +4821,11 @@ var UniformStore = class {
4688
4821
  uniformBuffer.write(uniformBufferData);
4689
4822
  return uniformBuffer;
4690
4823
  }
4691
- /** Get the managed uniform buffer. "managed" resources are destroyed when the uniformStore is destroyed. */
4692
- getManagedUniformBuffer(device, uniformBufferName) {
4824
+ /** Returns the managed uniform buffer for the named block. */
4825
+ getManagedUniformBuffer(uniformBufferName) {
4693
4826
  if (!this.uniformBuffers.get(uniformBufferName)) {
4694
4827
  const byteLength = this.getUniformBufferByteLength(uniformBufferName);
4695
- const uniformBuffer = device.createBuffer({
4828
+ const uniformBuffer = this.device.createBuffer({
4696
4829
  usage: Buffer2.UNIFORM | Buffer2.COPY_DST,
4697
4830
  byteLength
4698
4831
  });
@@ -4700,7 +4833,11 @@ var UniformStore = class {
4700
4833
  }
4701
4834
  return this.uniformBuffers.get(uniformBufferName);
4702
4835
  }
4703
- /** Updates all uniform buffers where values have changed */
4836
+ /**
4837
+ * Updates every managed uniform buffer whose source uniforms have changed.
4838
+ *
4839
+ * @returns The first redraw reason encountered, or `false` if nothing changed.
4840
+ */
4704
4841
  updateUniformBuffers() {
4705
4842
  let reason = false;
4706
4843
  for (const uniformBufferName of this.uniformBlocks.keys()) {
@@ -4712,7 +4849,11 @@ var UniformStore = class {
4712
4849
  }
4713
4850
  return reason;
4714
4851
  }
4715
- /** Update one uniform buffer. Only updates if values have changed */
4852
+ /**
4853
+ * Updates one managed uniform buffer if its corresponding block is dirty.
4854
+ *
4855
+ * @returns The redraw reason for the update, or `false` if no write occurred.
4856
+ */
4716
4857
  updateUniformBuffer(uniformBufferName) {
4717
4858
  var _a;
4718
4859
  const uniformBlock = this.uniformBlocks.get(uniformBufferName);
@@ -4729,6 +4870,9 @@ var UniformStore = class {
4729
4870
  return reason;
4730
4871
  }
4731
4872
  };
4873
+ function getDefaultUniformBufferLayout(device) {
4874
+ return device.type === "webgpu" ? "wgsl-uniform" : "std140";
4875
+ }
4732
4876
 
4733
4877
  // dist/shadertypes/texture-types/texture-layout.js
4734
4878
  function getTextureImageView(arrayBuffer2, memoryLayout, format, image = 0) {