@luma.gl/core 9.3.0-alpha.9 → 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/dist.dev.js CHANGED
@@ -54,13 +54,13 @@ var __exports__ = (() => {
54
54
  Resource: () => Resource,
55
55
  Sampler: () => Sampler,
56
56
  Shader: () => Shader,
57
+ ShaderBlockWriter: () => ShaderBlockWriter,
57
58
  ShaderFactory: () => ShaderFactory,
58
59
  SharedRenderPipeline: () => SharedRenderPipeline,
59
60
  Texture: () => Texture,
60
61
  TextureView: () => TextureView,
61
62
  TransformFeedback: () => TransformFeedback,
62
63
  UniformBlock: () => UniformBlock,
63
- UniformBufferLayout: () => UniformBufferLayout,
64
64
  UniformStore: () => UniformStore,
65
65
  VertexArray: () => VertexArray,
66
66
  _getDefaultBindGroupFactory: () => _getDefaultBindGroupFactory,
@@ -81,6 +81,7 @@ var __exports__ = (() => {
81
81
  isExternalImage: () => isExternalImage,
82
82
  log: () => log,
83
83
  luma: () => luma,
84
+ makeShaderBlockLayout: () => makeShaderBlockLayout,
84
85
  normalizeBindingsByGroup: () => normalizeBindingsByGroup,
85
86
  readPixel: () => readPixel,
86
87
  setTextureImageData: () => setTextureImageData,
@@ -1451,12 +1452,51 @@ var __exports__ = (() => {
1451
1452
  }
1452
1453
  return `${dataType}x${components}`;
1453
1454
  case "snorm8":
1455
+ if (components === 1) {
1456
+ return "snorm8";
1457
+ }
1458
+ if (components === 3) {
1459
+ return "snorm8x3-webgl";
1460
+ }
1461
+ return `${dataType}x${components}`;
1454
1462
  case "uint8":
1455
1463
  case "sint8":
1464
+ if (components === 1 || components === 3) {
1465
+ throw new Error(`size: ${components}`);
1466
+ }
1467
+ return `${dataType}x${components}`;
1456
1468
  case "uint16":
1469
+ if (components === 1) {
1470
+ return "uint16";
1471
+ }
1472
+ if (components === 3) {
1473
+ return "uint16x3-webgl";
1474
+ }
1475
+ return `${dataType}x${components}`;
1457
1476
  case "sint16":
1477
+ if (components === 1) {
1478
+ return "sint16";
1479
+ }
1480
+ if (components === 3) {
1481
+ return "sint16x3-webgl";
1482
+ }
1483
+ return `${dataType}x${components}`;
1458
1484
  case "unorm16":
1485
+ if (components === 1) {
1486
+ return "unorm16";
1487
+ }
1488
+ if (components === 3) {
1489
+ return "unorm16x3-webgl";
1490
+ }
1491
+ return `${dataType}x${components}`;
1459
1492
  case "snorm16":
1493
+ if (components === 1) {
1494
+ return "snorm16";
1495
+ }
1496
+ if (components === 3) {
1497
+ return "snorm16x3-webgl";
1498
+ }
1499
+ return `${dataType}x${components}`;
1460
1500
  case "float16":
1461
1501
  if (components === 1 || components === 3) {
1462
1502
  throw new Error(`size: ${components}`);
@@ -2159,7 +2199,7 @@ or create a device with the 'debug: true' prop.`;
2159
2199
  throw new Error("_createBindGroupLayoutWebGPU() not implemented");
2160
2200
  }
2161
2201
  /** Internal WebGPU-only helper for creating a native bind group. */
2162
- _createBindGroupWebGPU(_bindGroupLayout, _shaderLayout, _bindings, _group) {
2202
+ _createBindGroupWebGPU(_bindGroupLayout, _shaderLayout, _bindings, _group, _label) {
2163
2203
  throw new Error("_createBindGroupWebGPU() not implemented");
2164
2204
  }
2165
2205
  /**
@@ -4373,12 +4413,14 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
4373
4413
  for (const group of getBindGroupIndicesUpToMax(pipeline.shaderLayout.bindings)) {
4374
4414
  const groupBindings = bindingsByGroup[group];
4375
4415
  const bindGroupLayout = this._getBindGroupLayout(pipeline, group);
4416
+ const bindGroupLabel = getBindGroupLabel(pipeline, pipeline.shaderLayout, group);
4376
4417
  if (!groupBindings || Object.keys(groupBindings).length === 0) {
4377
4418
  if (!hasBindingsInGroup(pipeline.shaderLayout.bindings, group)) {
4378
4419
  resolvedBindGroups[group] = this._getEmptyBindGroup(
4379
4420
  bindGroupLayout,
4380
4421
  pipeline.shaderLayout,
4381
- group
4422
+ group,
4423
+ bindGroupLabel
4382
4424
  );
4383
4425
  }
4384
4426
  continue;
@@ -4394,7 +4436,8 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
4394
4436
  bindGroupLayout,
4395
4437
  pipeline.shaderLayout,
4396
4438
  groupBindings,
4397
- group
4439
+ group,
4440
+ bindGroupLabel
4398
4441
  );
4399
4442
  layoutCache.bindGroupsBySource.set(bindGroupCacheKey, bindGroup);
4400
4443
  resolvedBindGroups[group] = bindGroup;
@@ -4403,7 +4446,8 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
4403
4446
  bindGroupLayout,
4404
4447
  pipeline.shaderLayout,
4405
4448
  groupBindings,
4406
- group
4449
+ group,
4450
+ bindGroupLabel
4407
4451
  );
4408
4452
  }
4409
4453
  }
@@ -4418,9 +4462,9 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
4418
4462
  layoutCache[group] ||= this.device._createBindGroupLayoutWebGPU(pipeline, group);
4419
4463
  return layoutCache[group];
4420
4464
  }
4421
- _getEmptyBindGroup(bindGroupLayout, shaderLayout, group) {
4465
+ _getEmptyBindGroup(bindGroupLayout, shaderLayout, group, label) {
4422
4466
  const layoutCache = this._getLayoutBindGroupCache(bindGroupLayout);
4423
- layoutCache.emptyBindGroup ||= this.device._createBindGroupWebGPU(bindGroupLayout, shaderLayout, {}, group) || null;
4467
+ layoutCache.emptyBindGroup ||= this.device._createBindGroupWebGPU(bindGroupLayout, shaderLayout, {}, group, label) || null;
4424
4468
  return layoutCache.emptyBindGroup;
4425
4469
  }
4426
4470
  _getLayoutBindGroupCache(bindGroupLayout) {
@@ -4446,6 +4490,11 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
4446
4490
  function hasBindingsInGroup(bindings, group) {
4447
4491
  return bindings.some((binding) => binding.group === group);
4448
4492
  }
4493
+ function getBindGroupLabel(pipeline, shaderLayout, group) {
4494
+ const bindingNames = shaderLayout.bindings.filter((binding) => binding.group === group).sort((left, right) => left.location - right.location).map((binding) => binding.name);
4495
+ const bindingSuffix = bindingNames.length > 0 ? bindingNames.join(",") : "empty";
4496
+ return `${pipeline.id}/group${group}[${bindingSuffix}]`;
4497
+ }
4449
4498
 
4450
4499
  // src/adapter/resources/render-pass.ts
4451
4500
  var _RenderPass = class extends Resource {
@@ -5045,6 +5094,170 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
5045
5094
  sint32: ["sint32", "i32", 4, false, Int32Array]
5046
5095
  };
5047
5096
 
5097
+ // src/shadertypes/shader-types/shader-block-layout.ts
5098
+ function makeShaderBlockLayout(uniformTypes, options = {}) {
5099
+ const copiedUniformTypes = { ...uniformTypes };
5100
+ const layout = options.layout ?? "std140";
5101
+ const fields = {};
5102
+ let size = 0;
5103
+ for (const [key, uniformType] of Object.entries(copiedUniformTypes)) {
5104
+ size = addToLayout(fields, key, uniformType, size, layout);
5105
+ }
5106
+ size = alignTo(size, getTypeAlignment(copiedUniformTypes, layout));
5107
+ return {
5108
+ layout,
5109
+ byteLength: size * 4,
5110
+ uniformTypes: copiedUniformTypes,
5111
+ fields
5112
+ };
5113
+ }
5114
+ function getLeafLayoutInfo(type, layout) {
5115
+ const resolvedType = resolveVariableShaderTypeAlias(type);
5116
+ const decodedType = getVariableShaderTypeInfo(resolvedType);
5117
+ const matrixMatch = /^mat(\d)x(\d)<.+>$/.exec(resolvedType);
5118
+ if (matrixMatch) {
5119
+ const columns = Number(matrixMatch[1]);
5120
+ const rows = Number(matrixMatch[2]);
5121
+ const columnInfo = getVectorLayoutInfo(
5122
+ rows,
5123
+ resolvedType,
5124
+ decodedType.type,
5125
+ layout
5126
+ );
5127
+ const columnStride = getMatrixColumnStride(columnInfo.size, columnInfo.alignment, layout);
5128
+ return {
5129
+ alignment: columnInfo.alignment,
5130
+ size: columns * columnStride,
5131
+ components: columns * rows,
5132
+ columns,
5133
+ rows,
5134
+ columnStride,
5135
+ shaderType: resolvedType,
5136
+ type: decodedType.type
5137
+ };
5138
+ }
5139
+ const vectorMatch = /^vec(\d)<.+>$/.exec(resolvedType);
5140
+ if (vectorMatch) {
5141
+ return getVectorLayoutInfo(
5142
+ Number(vectorMatch[1]),
5143
+ resolvedType,
5144
+ decodedType.type,
5145
+ layout
5146
+ );
5147
+ }
5148
+ return {
5149
+ alignment: 1,
5150
+ size: 1,
5151
+ components: 1,
5152
+ columns: 1,
5153
+ rows: 1,
5154
+ columnStride: 1,
5155
+ shaderType: resolvedType,
5156
+ type: decodedType.type
5157
+ };
5158
+ }
5159
+ function isCompositeShaderTypeStruct(value) {
5160
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5161
+ }
5162
+ function addToLayout(fields, name2, type, offset, layout) {
5163
+ if (typeof type === "string") {
5164
+ const info = getLeafLayoutInfo(type, layout);
5165
+ const alignedOffset = alignTo(offset, info.alignment);
5166
+ fields[name2] = {
5167
+ offset: alignedOffset,
5168
+ ...info
5169
+ };
5170
+ return alignedOffset + info.size;
5171
+ }
5172
+ if (Array.isArray(type)) {
5173
+ if (Array.isArray(type[0])) {
5174
+ throw new Error(`Nested arrays are not supported for ${name2}`);
5175
+ }
5176
+ const elementType = type[0];
5177
+ const length = type[1];
5178
+ const stride = getArrayStride(elementType, layout);
5179
+ const arrayOffset = alignTo(offset, getTypeAlignment(type, layout));
5180
+ for (let i = 0; i < length; i++) {
5181
+ addToLayout(fields, `${name2}[${i}]`, elementType, arrayOffset + i * stride, layout);
5182
+ }
5183
+ return arrayOffset + stride * length;
5184
+ }
5185
+ if (isCompositeShaderTypeStruct(type)) {
5186
+ const structAlignment = getTypeAlignment(type, layout);
5187
+ let structOffset = alignTo(offset, structAlignment);
5188
+ for (const [memberName, memberType] of Object.entries(type)) {
5189
+ structOffset = addToLayout(fields, `${name2}.${memberName}`, memberType, structOffset, layout);
5190
+ }
5191
+ return alignTo(structOffset, structAlignment);
5192
+ }
5193
+ throw new Error(`Unsupported CompositeShaderType for ${name2}`);
5194
+ }
5195
+ function getTypeSize(type, layout) {
5196
+ if (typeof type === "string") {
5197
+ return getLeafLayoutInfo(type, layout).size;
5198
+ }
5199
+ if (Array.isArray(type)) {
5200
+ const elementType = type[0];
5201
+ const length = type[1];
5202
+ if (Array.isArray(elementType)) {
5203
+ throw new Error("Nested arrays are not supported");
5204
+ }
5205
+ return getArrayStride(elementType, layout) * length;
5206
+ }
5207
+ let size = 0;
5208
+ for (const memberType of Object.values(type)) {
5209
+ const compositeMemberType = memberType;
5210
+ size = alignTo(size, getTypeAlignment(compositeMemberType, layout));
5211
+ size += getTypeSize(compositeMemberType, layout);
5212
+ }
5213
+ return alignTo(size, getTypeAlignment(type, layout));
5214
+ }
5215
+ function getTypeAlignment(type, layout) {
5216
+ if (typeof type === "string") {
5217
+ return getLeafLayoutInfo(type, layout).alignment;
5218
+ }
5219
+ if (Array.isArray(type)) {
5220
+ const elementType = type[0];
5221
+ const elementAlignment = getTypeAlignment(elementType, layout);
5222
+ return uses16ByteArrayAlignment(layout) ? Math.max(elementAlignment, 4) : elementAlignment;
5223
+ }
5224
+ let maxAlignment = 1;
5225
+ for (const memberType of Object.values(type)) {
5226
+ const memberAlignment = getTypeAlignment(memberType, layout);
5227
+ maxAlignment = Math.max(maxAlignment, memberAlignment);
5228
+ }
5229
+ return uses16ByteStructAlignment(layout) ? Math.max(maxAlignment, 4) : maxAlignment;
5230
+ }
5231
+ function getVectorLayoutInfo(components, shaderType, type, layout) {
5232
+ return {
5233
+ alignment: components === 2 ? 2 : 4,
5234
+ size: components === 3 ? 3 : components,
5235
+ components,
5236
+ columns: 1,
5237
+ rows: components,
5238
+ columnStride: components === 3 ? 3 : components,
5239
+ shaderType,
5240
+ type
5241
+ };
5242
+ }
5243
+ function getArrayStride(elementType, layout) {
5244
+ const elementSize = getTypeSize(elementType, layout);
5245
+ const elementAlignment = getTypeAlignment(elementType, layout);
5246
+ return getArrayLikeStride(elementSize, elementAlignment, layout);
5247
+ }
5248
+ function getArrayLikeStride(size, alignment, layout) {
5249
+ return alignTo(size, uses16ByteArrayAlignment(layout) ? 4 : alignment);
5250
+ }
5251
+ function getMatrixColumnStride(size, alignment, layout) {
5252
+ return layout === "std140" ? 4 : alignTo(size, alignment);
5253
+ }
5254
+ function uses16ByteArrayAlignment(layout) {
5255
+ return layout === "std140" || layout === "wgsl-uniform";
5256
+ }
5257
+ function uses16ByteStructAlignment(layout) {
5258
+ return layout === "std140" || layout === "wgsl-uniform";
5259
+ }
5260
+
5048
5261
  // src/utils/array-utils-flat.ts
5049
5262
  var arrayBuffer;
5050
5263
  function getScratchArrayBuffer(byteLength) {
@@ -5069,49 +5282,56 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
5069
5282
  return isTypedArray(value);
5070
5283
  }
5071
5284
 
5072
- // src/portable/uniform-buffer-layout.ts
5073
- var minBufferSize = 1024;
5074
- var UniformBufferLayout = class {
5075
- layout = {};
5076
- uniformTypes;
5077
- /** number of bytes needed for buffer allocation */
5078
- byteLength;
5079
- /** Create a new UniformBufferLayout given a map of attributes. */
5080
- constructor(uniformTypes) {
5081
- this.uniformTypes = { ...uniformTypes };
5082
- let size = 0;
5083
- for (const [key, uniformType] of Object.entries(this.uniformTypes)) {
5084
- size = this._addToLayout(key, uniformType, size);
5085
- }
5086
- size = alignTo(size, 4);
5087
- this.byteLength = Math.max(size * 4, minBufferSize);
5088
- }
5089
- /** Does this layout have a field with specified name */
5285
+ // src/portable/shader-block-writer.ts
5286
+ var ShaderBlockWriter = class {
5287
+ /** Layout metadata used to flatten and serialize values. */
5288
+ layout;
5289
+ /**
5290
+ * Creates a writer for a precomputed shader-block layout.
5291
+ */
5292
+ constructor(layout) {
5293
+ this.layout = layout;
5294
+ }
5295
+ /**
5296
+ * Returns `true` if the flattened layout contains the given field.
5297
+ */
5090
5298
  has(name2) {
5091
- return Boolean(this.layout[name2]);
5299
+ return Boolean(this.layout.fields[name2]);
5092
5300
  }
5093
- /** Get offset and size for a field with specified name */
5301
+ /**
5302
+ * Returns offset and size metadata for a flattened field.
5303
+ */
5094
5304
  get(name2) {
5095
- const layout = this.layout[name2];
5096
- return layout;
5305
+ const entry = this.layout.fields[name2];
5306
+ return entry ? { offset: entry.offset, size: entry.size } : void 0;
5097
5307
  }
5098
- /** Flatten nested uniform values into leaf-path values understood by UniformBlock. */
5308
+ /**
5309
+ * Flattens nested composite values into leaf-path values understood by {@link UniformBlock}.
5310
+ *
5311
+ * Top-level values may be supplied either in nested object form matching the
5312
+ * declared composite shader types or as already-flattened leaf-path values.
5313
+ */
5099
5314
  getFlatUniformValues(uniformValues) {
5100
5315
  const flattenedUniformValues = {};
5101
5316
  for (const [name2, value] of Object.entries(uniformValues)) {
5102
- const uniformType = this.uniformTypes[name2];
5317
+ const uniformType = this.layout.uniformTypes[name2];
5103
5318
  if (uniformType) {
5104
5319
  this._flattenCompositeValue(flattenedUniformValues, name2, uniformType, value);
5105
- } else if (this.layout[name2]) {
5320
+ } else if (this.layout.fields[name2]) {
5106
5321
  flattenedUniformValues[name2] = value;
5107
5322
  }
5108
5323
  }
5109
5324
  return flattenedUniformValues;
5110
5325
  }
5111
- /** Get the data for the complete buffer */
5326
+ /**
5327
+ * Serializes the supplied values into buffer-backed binary data.
5328
+ *
5329
+ * The returned view length matches {@link ShaderBlockLayout.byteLength}, which
5330
+ * is the exact packed size of the block.
5331
+ */
5112
5332
  getData(uniformValues) {
5113
- const buffer = getScratchArrayBuffer(this.byteLength);
5114
- new Uint8Array(buffer, 0, this.byteLength).fill(0);
5333
+ const buffer = getScratchArrayBuffer(this.layout.byteLength);
5334
+ new Uint8Array(buffer, 0, this.layout.byteLength).fill(0);
5115
5335
  const typedArrays = {
5116
5336
  i32: new Int32Array(buffer),
5117
5337
  u32: new Uint32Array(buffer),
@@ -5122,46 +5342,16 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
5122
5342
  for (const [name2, value] of Object.entries(flattenedUniformValues)) {
5123
5343
  this._writeLeafValue(typedArrays, name2, value);
5124
5344
  }
5125
- return new Uint8Array(buffer, 0, this.byteLength);
5126
- }
5127
- // Recursively add a uniform to the layout
5128
- _addToLayout(name2, type, offset) {
5129
- if (typeof type === "string") {
5130
- const info = getLeafLayoutInfo(type);
5131
- const alignedOffset = alignTo(offset, info.alignment);
5132
- this.layout[name2] = {
5133
- offset: alignedOffset,
5134
- ...info
5135
- };
5136
- return alignedOffset + info.size;
5137
- }
5138
- if (Array.isArray(type)) {
5139
- if (Array.isArray(type[0])) {
5140
- throw new Error(`Nested arrays are not supported for ${name2}`);
5141
- }
5142
- const elementType = type[0];
5143
- const length = type[1];
5144
- const stride = alignTo(getTypeSize(elementType), 4);
5145
- const arrayOffset = alignTo(offset, 4);
5146
- for (let i = 0; i < length; i++) {
5147
- this._addToLayout(`${name2}[${i}]`, elementType, arrayOffset + i * stride);
5148
- }
5149
- return arrayOffset + stride * length;
5150
- }
5151
- if (isCompositeShaderTypeStruct(type)) {
5152
- let structOffset = alignTo(offset, 4);
5153
- for (const [memberName, memberType] of Object.entries(type)) {
5154
- structOffset = this._addToLayout(`${name2}.${memberName}`, memberType, structOffset);
5155
- }
5156
- return alignTo(structOffset, 4);
5157
- }
5158
- throw new Error(`Unsupported CompositeShaderType for ${name2}`);
5345
+ return new Uint8Array(buffer, 0, this.layout.byteLength);
5159
5346
  }
5347
+ /**
5348
+ * Recursively flattens nested values using the declared composite shader type.
5349
+ */
5160
5350
  _flattenCompositeValue(flattenedUniformValues, baseName, uniformType, value) {
5161
5351
  if (value === void 0) {
5162
5352
  return;
5163
5353
  }
5164
- if (typeof uniformType === "string" || this.layout[baseName]) {
5354
+ if (typeof uniformType === "string" || this.layout.fields[baseName]) {
5165
5355
  flattenedUniformValues[baseName] = value;
5166
5356
  return;
5167
5357
  }
@@ -5205,9 +5395,12 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
5205
5395
  }
5206
5396
  log.warn(`Unsupported uniform value for ${baseName}:`, value)();
5207
5397
  }
5398
+ /**
5399
+ * Expands tightly packed numeric arrays into per-element leaf fields.
5400
+ */
5208
5401
  _flattenPackedArray(flattenedUniformValues, baseName, elementType, length, value) {
5209
5402
  const numericValue = value;
5210
- const elementLayout = getLeafLayoutInfo(elementType);
5403
+ const elementLayout = getLeafLayoutInfo(elementType, this.layout.layout);
5211
5404
  const packedElementLength = elementLayout.components;
5212
5405
  for (let index = 0; index < length; index++) {
5213
5406
  const start = index * packedElementLength;
@@ -5225,13 +5418,16 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
5225
5418
  }
5226
5419
  }
5227
5420
  }
5421
+ /**
5422
+ * Writes one flattened leaf value into its typed-array view.
5423
+ */
5228
5424
  _writeLeafValue(typedArrays, name2, value) {
5229
- const layout = this.layout[name2];
5230
- if (!layout) {
5425
+ const entry = this.layout.fields[name2];
5426
+ if (!entry) {
5231
5427
  log.warn(`Uniform ${name2} not found in layout`)();
5232
5428
  return;
5233
5429
  }
5234
- const { type, components, columns, rows, offset } = layout;
5430
+ const { type, components, columns, rows, offset, columnStride } = entry;
5235
5431
  const array = typedArrays[type];
5236
5432
  if (components === 1) {
5237
5433
  array[offset] = Number(value);
@@ -5246,85 +5442,13 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
5246
5442
  }
5247
5443
  let sourceIndex = 0;
5248
5444
  for (let columnIndex = 0; columnIndex < columns; columnIndex++) {
5249
- const columnOffset = offset + columnIndex * 4;
5445
+ const columnOffset = offset + columnIndex * columnStride;
5250
5446
  for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
5251
5447
  array[columnOffset + rowIndex] = Number(sourceValue[sourceIndex++] ?? 0);
5252
5448
  }
5253
5449
  }
5254
5450
  }
5255
5451
  };
5256
- function getTypeSize(type) {
5257
- if (typeof type === "string") {
5258
- return getLeafLayoutInfo(type).size;
5259
- }
5260
- if (Array.isArray(type)) {
5261
- const elementType = type[0];
5262
- const length = type[1];
5263
- if (Array.isArray(elementType)) {
5264
- throw new Error("Nested arrays are not supported");
5265
- }
5266
- return alignTo(getTypeSize(elementType), 4) * length;
5267
- }
5268
- let size = 0;
5269
- for (const memberType of Object.values(type)) {
5270
- const compositeMemberType = memberType;
5271
- size = alignTo(size, getTypeAlignment(compositeMemberType));
5272
- size += getTypeSize(compositeMemberType);
5273
- }
5274
- return alignTo(size, 4);
5275
- }
5276
- function getTypeAlignment(type) {
5277
- if (typeof type === "string") {
5278
- return getLeafLayoutInfo(type).alignment;
5279
- }
5280
- if (Array.isArray(type)) {
5281
- return 4;
5282
- }
5283
- return 4;
5284
- }
5285
- function getLeafLayoutInfo(type) {
5286
- const resolvedType = resolveVariableShaderTypeAlias(type);
5287
- const decodedType = getVariableShaderTypeInfo(resolvedType);
5288
- const matrixMatch = /^mat(\d)x(\d)<.+>$/.exec(resolvedType);
5289
- if (matrixMatch) {
5290
- const columns = Number(matrixMatch[1]);
5291
- const rows = Number(matrixMatch[2]);
5292
- return {
5293
- alignment: 4,
5294
- size: columns * 4,
5295
- components: columns * rows,
5296
- columns,
5297
- rows,
5298
- shaderType: resolvedType,
5299
- type: decodedType.type
5300
- };
5301
- }
5302
- const vectorMatch = /^vec(\d)<.+>$/.exec(resolvedType);
5303
- if (vectorMatch) {
5304
- const components = Number(vectorMatch[1]);
5305
- return {
5306
- alignment: components === 2 ? 2 : 4,
5307
- size: components === 3 ? 4 : components,
5308
- components,
5309
- columns: 1,
5310
- rows: components,
5311
- shaderType: resolvedType,
5312
- type: decodedType.type
5313
- };
5314
- }
5315
- return {
5316
- alignment: 1,
5317
- size: 1,
5318
- components: 1,
5319
- columns: 1,
5320
- rows: 1,
5321
- shaderType: resolvedType,
5322
- type: decodedType.type
5323
- };
5324
- }
5325
- function isCompositeShaderTypeStruct(value) {
5326
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5327
- }
5328
5452
  function isCompositeUniformObject(value) {
5329
5453
  return Boolean(value) && typeof value === "object" && !Array.isArray(value) && !ArrayBuffer.isView(value);
5330
5454
  }
@@ -5417,26 +5541,33 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
5417
5541
  };
5418
5542
 
5419
5543
  // src/portable/uniform-store.ts
5544
+ var minUniformBufferSize = 1024;
5420
5545
  var UniformStore = class {
5546
+ /** Device used to infer layout and allocate buffers. */
5547
+ device;
5421
5548
  /** Stores the uniform values for each uniform block */
5422
5549
  uniformBlocks = /* @__PURE__ */ new Map();
5423
- /** Can generate data for a uniform buffer for each block from data */
5424
- uniformBufferLayouts = /* @__PURE__ */ new Map();
5550
+ /** Flattened layout metadata for each block. */
5551
+ shaderBlockLayouts = /* @__PURE__ */ new Map();
5552
+ /** Serializers for block-backed uniform data. */
5553
+ shaderBlockWriters = /* @__PURE__ */ new Map();
5425
5554
  /** Actual buffer for the blocks */
5426
5555
  uniformBuffers = /* @__PURE__ */ new Map();
5427
5556
  /**
5428
- * Create a new UniformStore instance
5429
- * @param blocks
5557
+ * Creates a new {@link UniformStore} for the supplied device and block definitions.
5430
5558
  */
5431
- constructor(blocks) {
5559
+ constructor(device, blocks) {
5560
+ this.device = device;
5432
5561
  for (const [bufferName, block] of Object.entries(blocks)) {
5433
5562
  const uniformBufferName = bufferName;
5434
- const uniformBufferLayout = new UniformBufferLayout(block.uniformTypes ?? {});
5435
- this.uniformBufferLayouts.set(uniformBufferName, uniformBufferLayout);
5563
+ const shaderBlockLayout = makeShaderBlockLayout(block.uniformTypes ?? {}, {
5564
+ layout: block.layout ?? getDefaultUniformBufferLayout(device)
5565
+ });
5566
+ const shaderBlockWriter = new ShaderBlockWriter(shaderBlockLayout);
5567
+ this.shaderBlockLayouts.set(uniformBufferName, shaderBlockLayout);
5568
+ this.shaderBlockWriters.set(uniformBufferName, shaderBlockWriter);
5436
5569
  const uniformBlock = new UniformBlock({ name: bufferName });
5437
- uniformBlock.setUniforms(
5438
- uniformBufferLayout.getFlatUniformValues(block.defaultUniforms || {})
5439
- );
5570
+ uniformBlock.setUniforms(shaderBlockWriter.getFlatUniformValues(block.defaultUniforms || {}));
5440
5571
  this.uniformBlocks.set(uniformBufferName, uniformBlock);
5441
5572
  }
5442
5573
  }
@@ -5448,38 +5579,51 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
5448
5579
  }
5449
5580
  /**
5450
5581
  * Set uniforms
5451
- * Makes all properties partial
5582
+ *
5583
+ * Makes all group properties partial and eagerly propagates changes to any
5584
+ * managed GPU buffers.
5452
5585
  */
5453
5586
  setUniforms(uniforms) {
5454
5587
  for (const [blockName, uniformValues] of Object.entries(uniforms)) {
5455
5588
  const uniformBufferName = blockName;
5456
- const uniformBufferLayout = this.uniformBufferLayouts.get(uniformBufferName);
5457
- const flattenedUniforms = uniformBufferLayout?.getFlatUniformValues(
5589
+ const shaderBlockWriter = this.shaderBlockWriters.get(uniformBufferName);
5590
+ const flattenedUniforms = shaderBlockWriter?.getFlatUniformValues(
5458
5591
  uniformValues || {}
5459
5592
  );
5460
5593
  this.uniformBlocks.get(uniformBufferName)?.setUniforms(flattenedUniforms || {});
5461
5594
  }
5462
5595
  this.updateUniformBuffers();
5463
5596
  }
5464
- /** Get the required minimum length of the uniform buffer */
5597
+ /**
5598
+ * Returns the allocation size for the named uniform buffer.
5599
+ *
5600
+ * This may exceed the packed layout size because minimum buffer-size policy is
5601
+ * applied at the store layer.
5602
+ */
5465
5603
  getUniformBufferByteLength(uniformBufferName) {
5466
- return this.uniformBufferLayouts.get(uniformBufferName)?.byteLength || 0;
5604
+ const packedByteLength = this.shaderBlockLayouts.get(uniformBufferName)?.byteLength || 0;
5605
+ return Math.max(packedByteLength, minUniformBufferSize);
5467
5606
  }
5468
- /** Get formatted binary memory that can be uploaded to a buffer */
5607
+ /**
5608
+ * Returns packed binary data that can be uploaded to the named uniform buffer.
5609
+ *
5610
+ * The returned view length matches the packed block size and is not padded to
5611
+ * the store's minimum allocation size.
5612
+ */
5469
5613
  getUniformBufferData(uniformBufferName) {
5470
5614
  const uniformValues = this.uniformBlocks.get(uniformBufferName)?.getAllUniforms() || {};
5471
- return this.uniformBufferLayouts.get(uniformBufferName)?.getData(uniformValues);
5615
+ const shaderBlockWriter = this.shaderBlockWriters.get(uniformBufferName);
5616
+ return shaderBlockWriter?.getData(uniformValues) || new Uint8Array(0);
5472
5617
  }
5473
5618
  /**
5474
- * Creates an unmanaged uniform buffer (umnanaged means that application is responsible for destroying it)
5475
- * The new buffer is initialized with current / supplied values
5619
+ * Creates an unmanaged uniform buffer initialized with the current or supplied values.
5476
5620
  */
5477
- createUniformBuffer(device, uniformBufferName, uniforms) {
5621
+ createUniformBuffer(uniformBufferName, uniforms) {
5478
5622
  if (uniforms) {
5479
5623
  this.setUniforms(uniforms);
5480
5624
  }
5481
5625
  const byteLength = this.getUniformBufferByteLength(uniformBufferName);
5482
- const uniformBuffer = device.createBuffer({
5626
+ const uniformBuffer = this.device.createBuffer({
5483
5627
  usage: Buffer2.UNIFORM | Buffer2.COPY_DST,
5484
5628
  byteLength
5485
5629
  });
@@ -5487,11 +5631,11 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
5487
5631
  uniformBuffer.write(uniformBufferData);
5488
5632
  return uniformBuffer;
5489
5633
  }
5490
- /** Get the managed uniform buffer. "managed" resources are destroyed when the uniformStore is destroyed. */
5491
- getManagedUniformBuffer(device, uniformBufferName) {
5634
+ /** Returns the managed uniform buffer for the named block. */
5635
+ getManagedUniformBuffer(uniformBufferName) {
5492
5636
  if (!this.uniformBuffers.get(uniformBufferName)) {
5493
5637
  const byteLength = this.getUniformBufferByteLength(uniformBufferName);
5494
- const uniformBuffer = device.createBuffer({
5638
+ const uniformBuffer = this.device.createBuffer({
5495
5639
  usage: Buffer2.UNIFORM | Buffer2.COPY_DST,
5496
5640
  byteLength
5497
5641
  });
@@ -5499,7 +5643,11 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
5499
5643
  }
5500
5644
  return this.uniformBuffers.get(uniformBufferName);
5501
5645
  }
5502
- /** Updates all uniform buffers where values have changed */
5646
+ /**
5647
+ * Updates every managed uniform buffer whose source uniforms have changed.
5648
+ *
5649
+ * @returns The first redraw reason encountered, or `false` if nothing changed.
5650
+ */
5503
5651
  updateUniformBuffers() {
5504
5652
  let reason = false;
5505
5653
  for (const uniformBufferName of this.uniformBlocks.keys()) {
@@ -5511,7 +5659,11 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
5511
5659
  }
5512
5660
  return reason;
5513
5661
  }
5514
- /** Update one uniform buffer. Only updates if values have changed */
5662
+ /**
5663
+ * Updates one managed uniform buffer if its corresponding block is dirty.
5664
+ *
5665
+ * @returns The redraw reason for the update, or `false` if no write occurred.
5666
+ */
5515
5667
  updateUniformBuffer(uniformBufferName) {
5516
5668
  const uniformBlock = this.uniformBlocks.get(uniformBufferName);
5517
5669
  let uniformBuffer = this.uniformBuffers.get(uniformBufferName);
@@ -5532,6 +5684,9 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
5532
5684
  return reason;
5533
5685
  }
5534
5686
  };
5687
+ function getDefaultUniformBufferLayout(device) {
5688
+ return device.type === "webgpu" ? "wgsl-uniform" : "std140";
5689
+ }
5535
5690
 
5536
5691
  // src/shadertypes/texture-types/texture-layout.ts
5537
5692
  function getTextureImageView(arrayBuffer2, memoryLayout, format, image = 0) {