@carbonenginejs/runtime-resource 0.18.0 → 0.19.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 (81) hide show
  1. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js +16 -4
  2. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js.map +1 -1
  3. package/dist/format/carbonEffect/buildCarbonEffectContainer.js +11 -1
  4. package/dist/format/carbonEffect/buildCarbonEffectContainer.js.map +1 -1
  5. package/dist/format/carbonEffect/carbonEffectBackendBlock.js +36 -0
  6. package/dist/format/carbonEffect/carbonEffectBackendBlock.js.map +1 -1
  7. package/dist/format/carbonEffect/carbonEffectRecords.js +45 -1
  8. package/dist/format/carbonEffect/carbonEffectRecords.js.map +1 -1
  9. package/dist/format/carbonEffect/carbonEffectResourceTransform.js +120 -60
  10. package/dist/format/carbonEffect/carbonEffectResourceTransform.js.map +1 -1
  11. package/dist/format/index.js +1 -1
  12. package/dist/formats/dds/core/helpers.js +35 -6
  13. package/dist/formats/dds/core/helpers.js.map +1 -1
  14. package/dist/formats/fbx/core/helpers.js +1 -1
  15. package/dist/formats/fbx/core/helpers.js.map +1 -1
  16. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js +53 -1
  17. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js.map +1 -1
  18. package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js +10 -1
  19. package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js.map +1 -1
  20. package/dist/formats/index.js +3 -0
  21. package/dist/formats/index.js.map +1 -1
  22. package/dist/formats/jpeg/core/helpers.js +1 -1
  23. package/dist/formats/jpeg/core/helpers.js.map +1 -1
  24. package/dist/formats/pickle/core/CjsPickleProtocol0Reader.js +205 -44
  25. package/dist/formats/pickle/core/CjsPickleProtocol0Reader.js.map +1 -1
  26. package/dist/formats/schemabound/CjsSchemaBoundFormat.js +236 -0
  27. package/dist/formats/schemabound/CjsSchemaBoundFormat.js.map +1 -0
  28. package/dist/formats/schemabound/core/schemaBoundErrors.js +9 -0
  29. package/dist/formats/schemabound/core/schemaBoundErrors.js.map +1 -0
  30. package/dist/formats/schemabound/core/schemaBoundValues.js +336 -0
  31. package/dist/formats/schemabound/core/schemaBoundValues.js.map +1 -0
  32. package/dist/formats/schemabound/index.js +2 -0
  33. package/dist/formats/schemabound/index.js.map +1 -0
  34. package/dist/formats/sqlite/CjsSqliteFormat.js +244 -0
  35. package/dist/formats/sqlite/CjsSqliteFormat.js.map +1 -0
  36. package/dist/formats/sqlite/core/sqlitePages.js +159 -0
  37. package/dist/formats/sqlite/core/sqlitePages.js.map +1 -0
  38. package/dist/formats/sqlite/core/sqliteRecords.js +174 -0
  39. package/dist/formats/sqlite/core/sqliteRecords.js.map +1 -0
  40. package/dist/formats/sqlite/core/sqliteSchema.js +100 -0
  41. package/dist/formats/sqlite/core/sqliteSchema.js.map +1 -0
  42. package/dist/formats/sqlite/index.js +2 -0
  43. package/dist/formats/sqlite/index.js.map +1 -0
  44. package/dist/formats/static/CjsStaticFormat.js +222 -0
  45. package/dist/formats/static/CjsStaticFormat.js.map +1 -0
  46. package/dist/formats/static/index.js +3 -0
  47. package/dist/formats/static/index.js.map +1 -0
  48. package/dist/formats/static/staticContainers.js +157 -0
  49. package/dist/formats/static/staticContainers.js.map +1 -0
  50. package/dist/formats/webgl/core/buildGlslEffectContainer.js +3 -1
  51. package/dist/formats/webgl/core/buildGlslEffectContainer.js.map +1 -1
  52. package/dist/formats/webgl/core/effectPackage.js +11 -2
  53. package/dist/formats/webgl/core/effectPackage.js.map +1 -1
  54. package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js +10 -2
  55. package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js.map +1 -1
  56. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js +3 -1
  57. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js.map +1 -1
  58. package/dist/formats/webgpu/core/carbonWebgpu/containerViews.js +179 -131
  59. package/dist/formats/webgpu/core/carbonWebgpu/containerViews.js.map +1 -1
  60. package/dist/formats/webgpu/core/effectBackendBodySet.js +20 -20
  61. package/dist/formats/webgpu/core/effectBackendBodySet.js.map +1 -1
  62. package/dist/formats/webgpu/core/packageEffect.js +14 -1
  63. package/dist/formats/webgpu/core/packageEffect.js.map +1 -1
  64. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js +9 -1
  65. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js.map +1 -1
  66. package/dist/resource/shader/reflection/Tr2EffectStageInput.js +14 -0
  67. package/dist/resource/shader/reflection/Tr2EffectStageInput.js.map +1 -1
  68. package/docs/concepts/shader-resource-model.md +34 -1
  69. package/docs/formats/README.md +21 -0
  70. package/docs/formats/carbon-effect-container.md +54 -27
  71. package/docs/formats/pickle.md +36 -0
  72. package/docs/formats/schemabound.md +123 -0
  73. package/docs/formats/static.md +166 -0
  74. package/docs/formats/webgl/README.md +5 -3
  75. package/docs/formats/webgpu/README.md +2 -2
  76. package/docs/formats/webgpu/architecture.md +2 -2
  77. package/docs/formats/webgpu/formats/carbon-webgpu.md +13 -2
  78. package/docs/formats/webgpu/guides/effect-packaging.md +1 -2
  79. package/docs/formats/webgpu/reference/wgsl-compatibility.md +2 -2
  80. package/docs/reference/classes/formats.md +33 -0
  81. package/package.json +4 -1
@@ -1,7 +1,7 @@
1
1
  import { CjsByteWriter } from '../CjsByteWriter.js';
2
2
  import { CjsStringTable } from '../CjsStringTable.js';
3
3
  import { CjsFormatWriteError } from '../CjsFormatError.js';
4
- import { writeEffectDescription, collectArena, internArena, CARBON_EFFECT_DATA_VERSION } from './carbonEffectRecords.js';
4
+ import { resolveWriteVersion, writeEffectDescription, collectArena, internArena } from './carbonEffectRecords.js';
5
5
  import { CARBON_EFFECT_SOURCE_HASH_BYTES } from './CjsCarbonEffectReader.js';
6
6
  export { CARBON_EFFECT_RECORD_BYTES } from './CjsCarbonEffectReader.js';
7
7
 
@@ -98,6 +98,8 @@ function normalizeCompilerVersion(value) {
98
98
  * arena's own `u32` length prefix (`StringTable.cpp:82-85`).
99
99
  *
100
100
  * @param {object} parts Resolved container parts.
101
+ * @param {number} [parts.version] Container data version to emit; must be one of
102
+ * `CARBON_EFFECT_WRITE_VERSIONS`. Defaults to the current version.
101
103
  * @param {number[]|Uint8Array} parts.compilerVersion Four version bytes.
102
104
  * @param {string|Uint8Array} parts.sourceHash 32 ASCII hash bytes.
103
105
  * @param {Uint8Array} parts.stringTableBytes Arena payload, without its prefix.
@@ -106,6 +108,7 @@ function normalizeCompilerVersion(value) {
106
108
  * @returns {Uint8Array} Container bytes.
107
109
  */
108
110
  function writeCarbonEffectFile(parts) {
111
+ const version = resolveWriteVersion(parts.version);
109
112
  const compilerVersion = normalizeCompilerVersion(parts.compilerVersion ?? [0, 0, 0, 0]);
110
113
  const sourceHash = normalizeSourceHash(parts.sourceHash ?? "0".repeat(CARBON_EFFECT_SOURCE_HASH_BYTES));
111
114
  const stringTableBytes = parts.stringTableBytes ?? new Uint8Array(0);
@@ -178,7 +181,7 @@ function writeCarbonEffectFile(parts) {
178
181
  cursor += body.bytes.length;
179
182
  }
180
183
  const writer = new CjsByteWriter(cursor);
181
- writer.u32(CARBON_EFFECT_DATA_VERSION);
184
+ writer.u32(version);
182
185
  writer.bytes(compilerVersion);
183
186
  writer.bytes(sourceHash);
184
187
  writer.u32(stringTableBytes.length);
@@ -231,17 +234,23 @@ class CjsCarbonEffectWriter {
231
234
  #compilerVersion;
232
235
  #sourceHash;
233
236
  #backend;
237
+ #version;
234
238
 
235
239
  /**
236
240
  * Creates an empty container builder.
237
241
  *
238
242
  * @param {object} [options] Builder options.
243
+ * @param {number} [options.version] Container data version to emit; must be
244
+ * one of `CARBON_EFFECT_WRITE_VERSIONS`. Defaults to the current
245
+ * version. Rejected at construction, not at `finish()`, so a caller
246
+ * learns it asked for something unwritable before doing the work.
239
247
  * @param {number[]|Uint8Array} [options.compilerVersion] Four version bytes.
240
248
  * @param {string|Uint8Array} [options.sourceHash] 32 ASCII hash bytes.
241
249
  * @param {CjsStringTable} [options.stringTable] Arena to intern into.
242
250
  * @param {boolean} [options.backend] Emit the optional trailing block per pass.
243
251
  */
244
252
  constructor(options = {}) {
253
+ this.#version = resolveWriteVersion(options.version);
245
254
  this.#table = options.stringTable ?? new CjsStringTable();
246
255
  this.#compilerVersion = options.compilerVersion ?? [0, 0, 0, 0];
247
256
  this.#sourceHash = options.sourceHash ?? "0".repeat(CARBON_EFFECT_SOURCE_HASH_BYTES);
@@ -330,7 +339,8 @@ class CjsCarbonEffectWriter {
330
339
  if (body.description) {
331
340
  writeEffectDescription(new CjsByteWriter(), body.description, {
332
341
  arena: collectArena(this.#table),
333
- backend: this.#backend
342
+ backend: this.#backend,
343
+ version: this.#version
334
344
  });
335
345
  }
336
346
  }
@@ -344,7 +354,8 @@ class CjsCarbonEffectWriter {
344
354
  const writer = new CjsByteWriter();
345
355
  writeEffectDescription(writer, body.description, {
346
356
  arena,
347
- backend: this.#backend
357
+ backend: this.#backend,
358
+ version: this.#version
348
359
  });
349
360
  return {
350
361
  index: body.index,
@@ -360,6 +371,7 @@ class CjsCarbonEffectWriter {
360
371
  options: axis.optionRefs.map(reference => this.#table.offsetOf(reference))
361
372
  }));
362
373
  return writeCarbonEffectFile({
374
+ version: this.#version,
363
375
  compilerVersion: this.#compilerVersion,
364
376
  sourceHash: this.#sourceHash,
365
377
  stringTableBytes: this.#table.toBytes(),
@@ -1 +1 @@
1
- {"version":3,"file":"CjsCarbonEffectWriter.js","sources":["../../../../src/format/carbonEffect/CjsCarbonEffectWriter.js"],"sourcesContent":["// Source: trinity/shadercompiler/ShaderCompiler.cpp:717-845 (alias dedupe, file assembly)\n\nimport { CjsByteWriter } from \"../CjsByteWriter.js\";\nimport { CjsStringTable } from \"../CjsStringTable.js\";\nimport { CjsFormatWriteError } from \"../CjsFormatError.js\";\nimport {\n CARBON_EFFECT_DATA_VERSION,\n collectArena,\n internArena,\n writeEffectDescription\n} from \"./carbonEffectRecords.js\";\nimport { CARBON_EFFECT_RECORD_BYTES, CARBON_EFFECT_SOURCE_HASH_BYTES } from \"./CjsCarbonEffectReader.js\";\n\nconst textEncoder = new TextEncoder();\n\n/**\n * Byte count of one permutation record's fixed part: `u32 name`,\n * `u8 defaultOption`, `u32 description`, `u8 type`, `u8 optionCount`\n * (`ShaderCompiler.cpp:757-761`).\n */\nconst PERMUTATION_FIXED_BYTES = 11;\n\n/**\n * FNV-1a over every byte, used to bucket body-dedupe candidates.\n *\n * @param {Uint8Array} bytes Byte run to hash.\n * @returns {number} 32-bit hash.\n */\nfunction hashBytes(bytes)\n{\n let hash = 0x811c9dc5;\n for (let index = 0; index < bytes.length; index += 1)\n {\n hash ^= bytes[index];\n hash = Math.imul(hash, 0x01000193);\n }\n return hash >>> 0;\n}\n\n/**\n * Reports whether two byte runs are identical.\n *\n * @param {Uint8Array} a First byte run.\n * @param {Uint8Array} b Second byte run.\n * @returns {boolean} True when the runs match exactly.\n */\nfunction bytesEqual(a, b)\n{\n if (a.length !== b.length) return false;\n for (let index = 0; index < a.length; index += 1)\n {\n if (a[index] !== b[index]) return false;\n }\n return true;\n}\n\n/**\n * Normalises a source hash to the 32 ASCII bytes the header slot holds.\n *\n * @param {string|ArrayBufferView|Uint8Array} value 32 hex characters or 32 bytes.\n * @returns {Uint8Array} Exactly 32 bytes.\n */\nfunction normalizeSourceHash(value)\n{\n const bytes = typeof value === \"string\"\n ? textEncoder.encode(value)\n : (value instanceof Uint8Array\n ? value\n : new Uint8Array(value.buffer, value.byteOffset, value.byteLength));\n if (bytes.length !== CARBON_EFFECT_SOURCE_HASH_BYTES)\n {\n throw new CjsFormatWriteError(\n `Source hash must be exactly ${CARBON_EFFECT_SOURCE_HASH_BYTES} bytes`,\n { byteLength: bytes.length }\n );\n }\n return bytes;\n}\n\n/**\n * Normalises a compiler version to the four bytes the header slot holds.\n *\n * The slot is `uint8_t[4]` — `{major, minor, patch, tweak}` from\n * `ShaderCompilerConfig.h.in:5`, of which Carbon's rebuild check compares only\n * the first three (`ModifiedTime.cpp:77`). Carbon's own runtime skips the field\n * entirely (`Tr2EffectRes.cpp:246-249`).\n *\n * @param {number[]|ArrayBufferView|Uint8Array} value Four version components.\n * @returns {Uint8Array} Exactly 4 bytes.\n */\nfunction normalizeCompilerVersion(value)\n{\n const bytes = Array.isArray(value)\n ? Uint8Array.from(value, (part) => Number(part) & 0xff)\n : (value instanceof Uint8Array\n ? value\n : new Uint8Array(value.buffer, value.byteOffset, value.byteLength));\n if (bytes.length !== 4)\n {\n throw new CjsFormatWriteError(\"Compiler version must be exactly 4 bytes\", {\n byteLength: bytes.length\n });\n }\n return bytes;\n}\n\n/**\n * Assembles a Carbon compiled-effect container from fully resolved parts.\n *\n * This is the low-level primitive: every arena reference in `permutationRows`\n * is already a byte offset and every body is already serialised, so the\n * function reproduces exactly Carbon's header order, alias dedupe and\n * body-offset arithmetic and nothing else. Re-emitting a file through this\n * function with its own arena and body bytes must reproduce it byte for byte.\n *\n * Body-offset base is `4 + 4 + 32 + headerSize + stringTable.GetSize()`\n * (`ShaderCompiler.cpp:801`), where `headerSize` is the byte size of the\n * permutation block plus the offset table (`:765`) and `GetSize()` includes the\n * arena's own `u32` length prefix (`StringTable.cpp:82-85`).\n *\n * @param {object} parts Resolved container parts.\n * @param {number[]|Uint8Array} parts.compilerVersion Four version bytes.\n * @param {string|Uint8Array} parts.sourceHash 32 ASCII hash bytes.\n * @param {Uint8Array} parts.stringTableBytes Arena payload, without its prefix.\n * @param {object[]} parts.permutationRows Permutation axes with resolved offsets.\n * @param {Array<{index:number, bytes:Uint8Array}>} parts.bodies Serialised bodies.\n * @returns {Uint8Array} Container bytes.\n */\nexport function writeCarbonEffectFile(parts)\n{\n const compilerVersion = normalizeCompilerVersion(parts.compilerVersion ?? [ 0, 0, 0, 0 ]);\n const sourceHash = normalizeSourceHash(parts.sourceHash ?? \"0\".repeat(CARBON_EFFECT_SOURCE_HASH_BYTES));\n const stringTableBytes = parts.stringTableBytes ?? new Uint8Array(0);\n const permutationRows = parts.permutationRows ?? [];\n const bodies = parts.bodies ?? [];\n\n if (bodies.length === 0)\n {\n throw new CjsFormatWriteError(\"A Carbon effect container needs at least one body\");\n }\n if (permutationRows.length > 0xff)\n {\n throw new CjsFormatWriteError(\"Permutation axis count does not fit in a u8\", {\n count: permutationRows.length\n });\n }\n\n const ordered = bodies.slice().sort((a, b) => a.index - b.index);\n for (let position = 0; position < ordered.length; position += 1)\n {\n if (ordered[position].index !== position)\n {\n throw new CjsFormatWriteError(\n \"Carbon effect bodies must be dense and start at index 0; the reader indexes the offset table positionally\",\n { position, index: ordered[position].index }\n );\n }\n }\n\n let permutationBytes = 1;\n for (const row of permutationRows)\n {\n if (row.options.length > 0xff)\n {\n throw new CjsFormatWriteError(\"Permutation option count does not fit in a u8\", {\n name: row.name,\n count: row.options.length\n });\n }\n permutationBytes += PERMUTATION_FIXED_BYTES + row.options.length * 4;\n }\n\n const headerSize = (ordered.length * 3 + 1) * 4 + permutationBytes;\n const base = 4 + 4 + CARBON_EFFECT_SOURCE_HASH_BYTES + headerSize + (stringTableBytes.length + 4);\n\n // Carbon compares packed bodies pairwise and points a duplicate's row at the\n // surviving twin, so the offset table stays dense while the file stores each\n // distinct body once (`ShaderCompiler.cpp:717-744`, `:804-820`). Keying the\n // first occurrence by content reaches the same rows in one pass.\n const byContent = new Map();\n const records = [];\n const uniqueBodies = [];\n let cursor = base;\n for (const body of ordered)\n {\n const key = hashBytes(body.bytes);\n const candidates = byContent.get(key);\n const existing = candidates?.find((candidate) => bytesEqual(candidate.bytes, body.bytes));\n if (existing)\n {\n records.push({ index: body.index, offset: existing.offset, size: existing.size });\n continue;\n }\n const record = { index: body.index, offset: cursor, size: body.bytes.length };\n if (candidates) candidates.push({ bytes: body.bytes, ...record });\n else byContent.set(key, [ { bytes: body.bytes, ...record } ]);\n records.push(record);\n uniqueBodies.push(body.bytes);\n cursor += body.bytes.length;\n }\n\n const writer = new CjsByteWriter(cursor);\n writer.u32(CARBON_EFFECT_DATA_VERSION);\n writer.bytes(compilerVersion);\n writer.bytes(sourceHash);\n\n writer.u32(stringTableBytes.length);\n if (stringTableBytes.length > 0) writer.bytes(stringTableBytes);\n\n writer.u8(permutationRows.length);\n for (const row of permutationRows)\n {\n writer.u32(row.nameOffset);\n writer.u8(row.defaultOption);\n writer.u32(row.descriptionOffset);\n writer.u8(row.type);\n writer.u8(row.options.length);\n for (const optionOffset of row.options)\n {\n writer.u32(optionOffset);\n }\n }\n\n writer.u32(records.length);\n for (const record of records)\n {\n writer.u32(record.index);\n writer.u32(record.offset);\n writer.u32(record.size);\n }\n\n if (writer.length !== base)\n {\n throw new CjsFormatWriteError(\"Carbon effect header size arithmetic disagrees with the bytes written\", {\n written: writer.length,\n expected: base,\n headerSize,\n permutationBytes\n });\n }\n\n for (const bytes of uniqueBodies)\n {\n writer.bytes(bytes);\n }\n\n return writer.toBytes();\n}\n\n/**\n * Builder for a Carbon compiled-effect container.\n *\n * Interning and emission are two passes over the same records, in that order,\n * because an arena offset is only stable once every entry is present: adding\n * after an offset has been handed out would shift every offset already written.\n * `collectArena` runs the first pass and `internArena` the second, and because\n * both drive the same `writeEffectDescription` walk they cannot fall out of\n * step with each other.\n */\nexport class CjsCarbonEffectWriter\n{\n #table;\n #permutations = [];\n #bodies = [];\n #compilerVersion;\n #sourceHash;\n #backend;\n\n /**\n * Creates an empty container builder.\n *\n * @param {object} [options] Builder options.\n * @param {number[]|Uint8Array} [options.compilerVersion] Four version bytes.\n * @param {string|Uint8Array} [options.sourceHash] 32 ASCII hash bytes.\n * @param {CjsStringTable} [options.stringTable] Arena to intern into.\n * @param {boolean} [options.backend] Emit the optional trailing block per pass.\n */\n constructor(options = {})\n {\n this.#table = options.stringTable ?? new CjsStringTable();\n this.#compilerVersion = options.compilerVersion ?? [ 0, 0, 0, 0 ];\n this.#sourceHash = options.sourceHash ?? \"0\".repeat(CARBON_EFFECT_SOURCE_HASH_BYTES);\n this.#backend = options.backend === true;\n }\n\n /**\n * Returns the arena this builder interns into.\n *\n * @returns {CjsStringTable} String table.\n */\n get stringTable()\n {\n return this.#table;\n }\n\n /**\n * Adds a permutation axis.\n *\n * @param {object} axis Axis description.\n * @param {string} axis.name Axis name.\n * @param {number} [axis.defaultOption] Index of the default option.\n * @param {string} [axis.description] Axis description text.\n * @param {number} [axis.type] Axis type byte.\n * @param {string[]} axis.options Option names.\n * @returns {CjsCarbonEffectWriter} This builder.\n */\n addPermutation(axis)\n {\n this.#permutations.push({\n name: String(axis.name ?? \"\"),\n defaultOption: axis.defaultOption ?? 0,\n description: String(axis.description ?? \"\"),\n type: axis.type ?? 0,\n options: (axis.options ?? []).map((option) => String(option))\n });\n return this;\n }\n\n /**\n * Adds one permutation's description record tree.\n *\n * @param {number} index Permutation index.\n * @param {object} description Description record tree.\n * @returns {CjsCarbonEffectWriter} This builder.\n */\n addBody(index, description)\n {\n this.#bodies.push({ index, description, bytes: null });\n return this;\n }\n\n /**\n * Adds one permutation's already-serialised description bytes.\n *\n * Used when re-emitting a file whose arena is being reused verbatim: the\n * bytes already carry resolved offsets into that arena, so they must not be\n * re-interned.\n *\n * @param {number} index Permutation index.\n * @param {Uint8Array} bytes Serialised description blob.\n * @returns {CjsCarbonEffectWriter} This builder.\n */\n addRawBody(index, bytes)\n {\n this.#bodies.push({ index, description: null, bytes: Uint8Array.from(bytes) });\n return this;\n }\n\n /**\n * Interns, serialises and assembles the container.\n *\n * @returns {Uint8Array} Container bytes.\n */\n toBytes()\n {\n for (const axis of this.#permutations)\n {\n axis.nameRef = this.#table.addString(axis.name);\n axis.descriptionRef = this.#table.addString(axis.description);\n axis.optionRefs = axis.options.map((option) => this.#table.addString(option));\n }\n for (const body of this.#bodies)\n {\n if (body.description)\n {\n writeEffectDescription(new CjsByteWriter(), body.description, {\n arena: collectArena(this.#table),\n backend: this.#backend\n });\n }\n }\n\n this.#table.finish();\n\n const arena = internArena(this.#table);\n const bodies = this.#bodies.map((body) =>\n {\n if (body.bytes) return { index: body.index, bytes: body.bytes };\n const writer = new CjsByteWriter();\n writeEffectDescription(writer, body.description, { arena, backend: this.#backend });\n return { index: body.index, bytes: writer.toBytes() };\n });\n\n const permutationRows = this.#permutations.map((axis) => ({\n name: axis.name,\n nameOffset: this.#table.offsetOf(axis.nameRef),\n defaultOption: axis.defaultOption,\n descriptionOffset: this.#table.offsetOf(axis.descriptionRef),\n type: axis.type,\n options: axis.optionRefs.map((reference) => this.#table.offsetOf(reference))\n }));\n\n return writeCarbonEffectFile({\n compilerVersion: this.#compilerVersion,\n sourceHash: this.#sourceHash,\n stringTableBytes: this.#table.toBytes(),\n permutationRows,\n bodies\n });\n }\n}\n\nexport { CARBON_EFFECT_RECORD_BYTES, CARBON_EFFECT_SOURCE_HASH_BYTES };\nexport default CjsCarbonEffectWriter;\n"],"names":["textEncoder","TextEncoder","PERMUTATION_FIXED_BYTES","hashBytes","bytes","hash","index","length","Math","imul","bytesEqual","a","b","normalizeSourceHash","value","encode","Uint8Array","buffer","byteOffset","byteLength","CARBON_EFFECT_SOURCE_HASH_BYTES","CjsFormatWriteError","normalizeCompilerVersion","Array","isArray","from","part","Number","writeCarbonEffectFile","parts","compilerVersion","sourceHash","repeat","stringTableBytes","permutationRows","bodies","count","ordered","slice","sort","position","permutationBytes","row","options","name","headerSize","base","byContent","Map","records","uniqueBodies","cursor","body","key","candidates","get","existing","find","candidate","push","offset","size","record","set","writer","CjsByteWriter","u32","CARBON_EFFECT_DATA_VERSION","u8","nameOffset","defaultOption","descriptionOffset","type","optionOffset","written","expected","toBytes","CjsCarbonEffectWriter","constructor","stringTable","CjsStringTable","backend","addPermutation","axis","String","description","map","option","addBody","addRawBody","nameRef","addString","descriptionRef","optionRefs","writeEffectDescription","arena","collectArena","finish","internArena","offsetOf","reference"],"mappings":"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"}
1
+ {"version":3,"file":"CjsCarbonEffectWriter.js","sources":["../../../../src/format/carbonEffect/CjsCarbonEffectWriter.js"],"sourcesContent":["// Source: trinity/shadercompiler/ShaderCompiler.cpp:717-845 (alias dedupe, file assembly)\n\nimport { CjsByteWriter } from \"../CjsByteWriter.js\";\nimport { CjsStringTable } from \"../CjsStringTable.js\";\nimport { CjsFormatWriteError } from \"../CjsFormatError.js\";\nimport {\n collectArena,\n internArena,\n resolveWriteVersion,\n writeEffectDescription\n} from \"./carbonEffectRecords.js\";\nimport { CARBON_EFFECT_RECORD_BYTES, CARBON_EFFECT_SOURCE_HASH_BYTES } from \"./CjsCarbonEffectReader.js\";\n\nconst textEncoder = new TextEncoder();\n\n/**\n * Byte count of one permutation record's fixed part: `u32 name`,\n * `u8 defaultOption`, `u32 description`, `u8 type`, `u8 optionCount`\n * (`ShaderCompiler.cpp:757-761`).\n */\nconst PERMUTATION_FIXED_BYTES = 11;\n\n/**\n * FNV-1a over every byte, used to bucket body-dedupe candidates.\n *\n * @param {Uint8Array} bytes Byte run to hash.\n * @returns {number} 32-bit hash.\n */\nfunction hashBytes(bytes)\n{\n let hash = 0x811c9dc5;\n for (let index = 0; index < bytes.length; index += 1)\n {\n hash ^= bytes[index];\n hash = Math.imul(hash, 0x01000193);\n }\n return hash >>> 0;\n}\n\n/**\n * Reports whether two byte runs are identical.\n *\n * @param {Uint8Array} a First byte run.\n * @param {Uint8Array} b Second byte run.\n * @returns {boolean} True when the runs match exactly.\n */\nfunction bytesEqual(a, b)\n{\n if (a.length !== b.length) return false;\n for (let index = 0; index < a.length; index += 1)\n {\n if (a[index] !== b[index]) return false;\n }\n return true;\n}\n\n/**\n * Normalises a source hash to the 32 ASCII bytes the header slot holds.\n *\n * @param {string|ArrayBufferView|Uint8Array} value 32 hex characters or 32 bytes.\n * @returns {Uint8Array} Exactly 32 bytes.\n */\nfunction normalizeSourceHash(value)\n{\n const bytes = typeof value === \"string\"\n ? textEncoder.encode(value)\n : (value instanceof Uint8Array\n ? value\n : new Uint8Array(value.buffer, value.byteOffset, value.byteLength));\n if (bytes.length !== CARBON_EFFECT_SOURCE_HASH_BYTES)\n {\n throw new CjsFormatWriteError(\n `Source hash must be exactly ${CARBON_EFFECT_SOURCE_HASH_BYTES} bytes`,\n { byteLength: bytes.length }\n );\n }\n return bytes;\n}\n\n/**\n * Normalises a compiler version to the four bytes the header slot holds.\n *\n * The slot is `uint8_t[4]` — `{major, minor, patch, tweak}` from\n * `ShaderCompilerConfig.h.in:5`, of which Carbon's rebuild check compares only\n * the first three (`ModifiedTime.cpp:77`). Carbon's own runtime skips the field\n * entirely (`Tr2EffectRes.cpp:246-249`).\n *\n * @param {number[]|ArrayBufferView|Uint8Array} value Four version components.\n * @returns {Uint8Array} Exactly 4 bytes.\n */\nfunction normalizeCompilerVersion(value)\n{\n const bytes = Array.isArray(value)\n ? Uint8Array.from(value, (part) => Number(part) & 0xff)\n : (value instanceof Uint8Array\n ? value\n : new Uint8Array(value.buffer, value.byteOffset, value.byteLength));\n if (bytes.length !== 4)\n {\n throw new CjsFormatWriteError(\"Compiler version must be exactly 4 bytes\", {\n byteLength: bytes.length\n });\n }\n return bytes;\n}\n\n/**\n * Assembles a Carbon compiled-effect container from fully resolved parts.\n *\n * This is the low-level primitive: every arena reference in `permutationRows`\n * is already a byte offset and every body is already serialised, so the\n * function reproduces exactly Carbon's header order, alias dedupe and\n * body-offset arithmetic and nothing else. Re-emitting a file through this\n * function with its own arena and body bytes must reproduce it byte for byte.\n *\n * Body-offset base is `4 + 4 + 32 + headerSize + stringTable.GetSize()`\n * (`ShaderCompiler.cpp:801`), where `headerSize` is the byte size of the\n * permutation block plus the offset table (`:765`) and `GetSize()` includes the\n * arena's own `u32` length prefix (`StringTable.cpp:82-85`).\n *\n * @param {object} parts Resolved container parts.\n * @param {number} [parts.version] Container data version to emit; must be one of\n * `CARBON_EFFECT_WRITE_VERSIONS`. Defaults to the current version.\n * @param {number[]|Uint8Array} parts.compilerVersion Four version bytes.\n * @param {string|Uint8Array} parts.sourceHash 32 ASCII hash bytes.\n * @param {Uint8Array} parts.stringTableBytes Arena payload, without its prefix.\n * @param {object[]} parts.permutationRows Permutation axes with resolved offsets.\n * @param {Array<{index:number, bytes:Uint8Array}>} parts.bodies Serialised bodies.\n * @returns {Uint8Array} Container bytes.\n */\nexport function writeCarbonEffectFile(parts)\n{\n const version = resolveWriteVersion(parts.version);\n const compilerVersion = normalizeCompilerVersion(parts.compilerVersion ?? [ 0, 0, 0, 0 ]);\n const sourceHash = normalizeSourceHash(parts.sourceHash ?? \"0\".repeat(CARBON_EFFECT_SOURCE_HASH_BYTES));\n const stringTableBytes = parts.stringTableBytes ?? new Uint8Array(0);\n const permutationRows = parts.permutationRows ?? [];\n const bodies = parts.bodies ?? [];\n\n if (bodies.length === 0)\n {\n throw new CjsFormatWriteError(\"A Carbon effect container needs at least one body\");\n }\n if (permutationRows.length > 0xff)\n {\n throw new CjsFormatWriteError(\"Permutation axis count does not fit in a u8\", {\n count: permutationRows.length\n });\n }\n\n const ordered = bodies.slice().sort((a, b) => a.index - b.index);\n for (let position = 0; position < ordered.length; position += 1)\n {\n if (ordered[position].index !== position)\n {\n throw new CjsFormatWriteError(\n \"Carbon effect bodies must be dense and start at index 0; the reader indexes the offset table positionally\",\n { position, index: ordered[position].index }\n );\n }\n }\n\n let permutationBytes = 1;\n for (const row of permutationRows)\n {\n if (row.options.length > 0xff)\n {\n throw new CjsFormatWriteError(\"Permutation option count does not fit in a u8\", {\n name: row.name,\n count: row.options.length\n });\n }\n permutationBytes += PERMUTATION_FIXED_BYTES + row.options.length * 4;\n }\n\n const headerSize = (ordered.length * 3 + 1) * 4 + permutationBytes;\n const base = 4 + 4 + CARBON_EFFECT_SOURCE_HASH_BYTES + headerSize + (stringTableBytes.length + 4);\n\n // Carbon compares packed bodies pairwise and points a duplicate's row at the\n // surviving twin, so the offset table stays dense while the file stores each\n // distinct body once (`ShaderCompiler.cpp:717-744`, `:804-820`). Keying the\n // first occurrence by content reaches the same rows in one pass.\n const byContent = new Map();\n const records = [];\n const uniqueBodies = [];\n let cursor = base;\n for (const body of ordered)\n {\n const key = hashBytes(body.bytes);\n const candidates = byContent.get(key);\n const existing = candidates?.find((candidate) => bytesEqual(candidate.bytes, body.bytes));\n if (existing)\n {\n records.push({ index: body.index, offset: existing.offset, size: existing.size });\n continue;\n }\n const record = { index: body.index, offset: cursor, size: body.bytes.length };\n if (candidates) candidates.push({ bytes: body.bytes, ...record });\n else byContent.set(key, [ { bytes: body.bytes, ...record } ]);\n records.push(record);\n uniqueBodies.push(body.bytes);\n cursor += body.bytes.length;\n }\n\n const writer = new CjsByteWriter(cursor);\n writer.u32(version);\n writer.bytes(compilerVersion);\n writer.bytes(sourceHash);\n\n writer.u32(stringTableBytes.length);\n if (stringTableBytes.length > 0) writer.bytes(stringTableBytes);\n\n writer.u8(permutationRows.length);\n for (const row of permutationRows)\n {\n writer.u32(row.nameOffset);\n writer.u8(row.defaultOption);\n writer.u32(row.descriptionOffset);\n writer.u8(row.type);\n writer.u8(row.options.length);\n for (const optionOffset of row.options)\n {\n writer.u32(optionOffset);\n }\n }\n\n writer.u32(records.length);\n for (const record of records)\n {\n writer.u32(record.index);\n writer.u32(record.offset);\n writer.u32(record.size);\n }\n\n if (writer.length !== base)\n {\n throw new CjsFormatWriteError(\"Carbon effect header size arithmetic disagrees with the bytes written\", {\n written: writer.length,\n expected: base,\n headerSize,\n permutationBytes\n });\n }\n\n for (const bytes of uniqueBodies)\n {\n writer.bytes(bytes);\n }\n\n return writer.toBytes();\n}\n\n/**\n * Builder for a Carbon compiled-effect container.\n *\n * Interning and emission are two passes over the same records, in that order,\n * because an arena offset is only stable once every entry is present: adding\n * after an offset has been handed out would shift every offset already written.\n * `collectArena` runs the first pass and `internArena` the second, and because\n * both drive the same `writeEffectDescription` walk they cannot fall out of\n * step with each other.\n */\nexport class CjsCarbonEffectWriter\n{\n #table;\n #permutations = [];\n #bodies = [];\n #compilerVersion;\n #sourceHash;\n #backend;\n #version;\n\n /**\n * Creates an empty container builder.\n *\n * @param {object} [options] Builder options.\n * @param {number} [options.version] Container data version to emit; must be\n * one of `CARBON_EFFECT_WRITE_VERSIONS`. Defaults to the current\n * version. Rejected at construction, not at `finish()`, so a caller\n * learns it asked for something unwritable before doing the work.\n * @param {number[]|Uint8Array} [options.compilerVersion] Four version bytes.\n * @param {string|Uint8Array} [options.sourceHash] 32 ASCII hash bytes.\n * @param {CjsStringTable} [options.stringTable] Arena to intern into.\n * @param {boolean} [options.backend] Emit the optional trailing block per pass.\n */\n constructor(options = {})\n {\n this.#version = resolveWriteVersion(options.version);\n this.#table = options.stringTable ?? new CjsStringTable();\n this.#compilerVersion = options.compilerVersion ?? [ 0, 0, 0, 0 ];\n this.#sourceHash = options.sourceHash ?? \"0\".repeat(CARBON_EFFECT_SOURCE_HASH_BYTES);\n this.#backend = options.backend === true;\n }\n\n /**\n * Returns the arena this builder interns into.\n *\n * @returns {CjsStringTable} String table.\n */\n get stringTable()\n {\n return this.#table;\n }\n\n /**\n * Adds a permutation axis.\n *\n * @param {object} axis Axis description.\n * @param {string} axis.name Axis name.\n * @param {number} [axis.defaultOption] Index of the default option.\n * @param {string} [axis.description] Axis description text.\n * @param {number} [axis.type] Axis type byte.\n * @param {string[]} axis.options Option names.\n * @returns {CjsCarbonEffectWriter} This builder.\n */\n addPermutation(axis)\n {\n this.#permutations.push({\n name: String(axis.name ?? \"\"),\n defaultOption: axis.defaultOption ?? 0,\n description: String(axis.description ?? \"\"),\n type: axis.type ?? 0,\n options: (axis.options ?? []).map((option) => String(option))\n });\n return this;\n }\n\n /**\n * Adds one permutation's description record tree.\n *\n * @param {number} index Permutation index.\n * @param {object} description Description record tree.\n * @returns {CjsCarbonEffectWriter} This builder.\n */\n addBody(index, description)\n {\n this.#bodies.push({ index, description, bytes: null });\n return this;\n }\n\n /**\n * Adds one permutation's already-serialised description bytes.\n *\n * Used when re-emitting a file whose arena is being reused verbatim: the\n * bytes already carry resolved offsets into that arena, so they must not be\n * re-interned.\n *\n * @param {number} index Permutation index.\n * @param {Uint8Array} bytes Serialised description blob.\n * @returns {CjsCarbonEffectWriter} This builder.\n */\n addRawBody(index, bytes)\n {\n this.#bodies.push({ index, description: null, bytes: Uint8Array.from(bytes) });\n return this;\n }\n\n /**\n * Interns, serialises and assembles the container.\n *\n * @returns {Uint8Array} Container bytes.\n */\n toBytes()\n {\n for (const axis of this.#permutations)\n {\n axis.nameRef = this.#table.addString(axis.name);\n axis.descriptionRef = this.#table.addString(axis.description);\n axis.optionRefs = axis.options.map((option) => this.#table.addString(option));\n }\n for (const body of this.#bodies)\n {\n if (body.description)\n {\n writeEffectDescription(new CjsByteWriter(), body.description, {\n arena: collectArena(this.#table),\n backend: this.#backend,\n version: this.#version\n });\n }\n }\n\n this.#table.finish();\n\n const arena = internArena(this.#table);\n const bodies = this.#bodies.map((body) =>\n {\n if (body.bytes) return { index: body.index, bytes: body.bytes };\n const writer = new CjsByteWriter();\n writeEffectDescription(writer, body.description, { arena, backend: this.#backend, version: this.#version });\n return { index: body.index, bytes: writer.toBytes() };\n });\n\n const permutationRows = this.#permutations.map((axis) => ({\n name: axis.name,\n nameOffset: this.#table.offsetOf(axis.nameRef),\n defaultOption: axis.defaultOption,\n descriptionOffset: this.#table.offsetOf(axis.descriptionRef),\n type: axis.type,\n options: axis.optionRefs.map((reference) => this.#table.offsetOf(reference))\n }));\n\n return writeCarbonEffectFile({\n version: this.#version,\n compilerVersion: this.#compilerVersion,\n sourceHash: this.#sourceHash,\n stringTableBytes: this.#table.toBytes(),\n permutationRows,\n bodies\n });\n }\n}\n\nexport { CARBON_EFFECT_RECORD_BYTES, CARBON_EFFECT_SOURCE_HASH_BYTES };\nexport default CjsCarbonEffectWriter;\n"],"names":["textEncoder","TextEncoder","PERMUTATION_FIXED_BYTES","hashBytes","bytes","hash","index","length","Math","imul","bytesEqual","a","b","normalizeSourceHash","value","encode","Uint8Array","buffer","byteOffset","byteLength","CARBON_EFFECT_SOURCE_HASH_BYTES","CjsFormatWriteError","normalizeCompilerVersion","Array","isArray","from","part","Number","writeCarbonEffectFile","parts","version","resolveWriteVersion","compilerVersion","sourceHash","repeat","stringTableBytes","permutationRows","bodies","count","ordered","slice","sort","position","permutationBytes","row","options","name","headerSize","base","byContent","Map","records","uniqueBodies","cursor","body","key","candidates","get","existing","find","candidate","push","offset","size","record","set","writer","CjsByteWriter","u32","u8","nameOffset","defaultOption","descriptionOffset","type","optionOffset","written","expected","toBytes","CjsCarbonEffectWriter","constructor","stringTable","CjsStringTable","backend","addPermutation","axis","String","description","map","option","addBody","addRawBody","nameRef","addString","descriptionRef","optionRefs","writeEffectDescription","arena","collectArena","finish","internArena","offsetOf","reference"],"mappings":"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"}
@@ -117,12 +117,15 @@ function describeBody(reader, permutationIndex, passUnits, backend) {
117
117
  * Builds a complete backend effect container.
118
118
  *
119
119
  * @param {object} effectRes Loaded version-15 `Tr2EffectRes`.
120
- * @param {object} permutationGraph Validated `PGRF` document.
120
+ * @param {object} permutationGraph Validated derived `CJS_EFFECT_PERMUTATION_GRAPH` document (no chunk is stored).
121
121
  * @param {object} backendBodySet Translated backend bodies.
122
122
  * @param {object} backend Backend encoders.
123
123
  * @param {(shader:object, stage:object, context:object)=>Uint8Array} backend.encodeProgram Encodes one stage program.
124
124
  * @param {(unit:object, passKey:string)=>(Uint8Array|null)} backend.encodeBackendBlock Builds one pass's trailing block.
125
125
  * @param {object} [options] Container options.
126
+ * @param {number} [options.version] Container data version to emit; must be one
127
+ * of `CARBON_EFFECT_WRITE_VERSIONS`. Defaults to the current version, and is
128
+ * independent of the source effect's version.
126
129
  * @param {number[]|Uint8Array} [options.compilerVersion] Four version bytes.
127
130
  * @param {string} [options.sourceHash] 32 ASCII hash characters.
128
131
  * @returns {{bytes:Uint8Array, permutationCount:number, bodyCount:number}} Container and its body accounting.
@@ -142,6 +145,13 @@ function buildCarbonEffectContainer(effectRes, permutationGraph, backendBodySet,
142
145
  const bodyByKey = new Map(backendBodySet.bodies.map(body => [body.bodyKey, body]));
143
146
  const writer = new CjsCarbonEffectWriter({
144
147
  backend: true,
148
+ // Not defaulted from the SOURCE effect's version. The source can be any
149
+ // version the reader accepts; what we emit is a version we have a
150
+ // writer for. Conflating the two would have us claim to emit whatever
151
+ // we happened to read.
152
+ ...(options.version === undefined ? {} : {
153
+ version: options.version
154
+ }),
145
155
  compilerVersion: options.compilerVersion ?? effectRes.m_compilerVersionBytes ?? [0, 0, 0, 0],
146
156
  ...(options.sourceHash ? {
147
157
  sourceHash: options.sourceHash
@@ -1 +1 @@
1
- {"version":3,"file":"buildCarbonEffectContainer.js","sources":["../../../../src/format/carbonEffect/buildCarbonEffectContainer.js"],"sourcesContent":["import { CjsCarbonEffectWriter } from \"./CjsCarbonEffectWriter.js\";\nimport { CjsCarbonEffectReader } from \"./CjsCarbonEffectReader.js\";\nimport { HlslShaderStageNames } from \"../../formats/hlsl/core/tr2/HlslRenderContextEnum.js\";\nimport { Tr2EffectDescription } from \"../../resource/shader/reflection/Tr2EffectDescription.js\";\nimport { CjsFormatWriteError } from \"../CjsFormatError.js\";\n\n/**\n * Assembles a backend effect container: Carbon's v15 record layout carrying our\n * programs where a shipped file carries DXBC.\n *\n * The Carbon record layout is backend-invariant, and this module is the part\n * that holds it that way. It walks the permutation graph, emits one description\n * per distinct body, and substitutes stage programs and one optional trailing\n * block per pass. What a program *is* and what the block *contains* are the only\n * backend-varying decisions, so those two are the injected seam\n * (`encodeProgram`, `encodeBackendBlock`) and nothing else here varies.\n *\n * Two backends use it. WGSL for WebGPU, GLSL ES 3.00 for WebGL 2. Their blocks\n * are genuinely different documents - bind-group topology on one side, sampler\n * fusion and synthesised bindings on the other - and neither is derivable from\n * Carbon reflection. Which reader parses a given block is decided by the\n * resource path the file came from, the same way the backend itself is chosen,\n * so the two codecs may both start at their own version 1 without ambiguity.\n */\n\n/**\n * There is no envelope, no magic and no version of our own. That is deliberate.\n *\n * A twelve-byte prefix (`magic | containerVersion | payloadKind`) was carried\n * here and has been removed, along with a proposed \"v16\" for this variant.\n * Neither survived the only question worth asking of an addition: what breaks\n * without it?\n *\n * - `payloadKind` is redundant with the directory the file came from. Backend\n * identity belongs to the `effect.webgpu/` or `effect.webgl2/` resource path.\n * - The magic only distinguished our file from a Carbon one, which the same\n * directory already answers, and which the file *name* answers too.\n * - A version of our own would have claimed a number CCP owns, in the one field\n * whose job is telling a reader how to parse. A real v16 from them would then\n * collide with ours.\n *\n * What remains is a bare Carbon v15 record file. The shared record reader\n * detects the per-pass block from the description's declared end.\n *\n * The one addition that does survive the question is the block itself: neither\n * backend's binding topology is derivable from Carbon reflection, so without it\n * there is no way to bind anything and no pipeline.\n */\n\n/**\n * Maps one body's translated passes by pass key.\n *\n * @param {object} body Backend body-set record.\n * @param {Map<string, object>} unitsByKey Units indexed by key.\n * @returns {Map<string, object>} Units indexed by pass key.\n */\nfunction unitsByPassKey(body, unitsByKey)\n{\n const result = new Map();\n for (const pass of body.passes ?? [])\n {\n result.set(pass.passKey, unitsByKey.get(pass.unitKey));\n }\n return result;\n}\n\n/**\n * Builds one body's Carbon description record tree with backend programs\n * substituted in.\n *\n * A body the translator could not lower retains its representable non-program\n * description fields and carries zero-length programs. Emitting empty\n * `shaderData` says exactly what the wire knows: no backend program was stored.\n * Full portable source reflection remains in the in-memory build result, not\n * in this emitted body.\n *\n * @param {object} effectRes Loaded version-15 `Tr2EffectRes`.\n * @param {number} permutationIndex Representative permutation for this body.\n * @param {Map<string, object>|null} passUnits Units by pass key, or null when unsupported.\n * @param {object} backend Backend encoders.\n * @returns {object} Description record tree.\n */\nfunction describeBody(reader, permutationIndex, passUnits, backend)\n{\n // Read the source body once, as the resource layer reads any Carbon body,\n // then substitute on the resulting graph and emit it. The substitution is\n // two edits - a stage's program and a pass's trailing block - so there is\n // nothing here that wants a bespoke mapping of its own.\n const effect = Tr2EffectDescription.fromCarbonBinary(\n reader.readDescription(permutationIndex)\n );\n\n for (const technique of effect.techniques)\n {\n for (const [ passIndex, pass ] of technique.passes.entries())\n {\n const passKey = `${technique.name}.pass${passIndex}`;\n const unit = passUnits ? passUnits.get(passKey) : null;\n\n for (const stage of pass.stageInputs)\n {\n if (!stage.exists) continue;\n\n const stageName = HlslShaderStageNames[stage.stageType];\n const shader = unit?.shaders.find(\n (entry) => entry.key === `${passKey}.${stageName}`\n );\n // A body the translator could not lower keeps its describable\n // fields and carries a zero-length program: the wire then says\n // exactly what is known, which is that no backend program was\n // stored. The source DXBC is never re-emitted.\n const bytes = shader\n ? backend.encodeProgram(shader)\n : new Uint8Array(0);\n stage.sourceProgram = {\n ...stage.sourceProgram,\n bytes,\n shaderSize: bytes.byteLength\n };\n }\n\n const block = unit ? backend.encodeBackendBlock(unit, passKey) : null;\n pass.backendBlock = block\n ? { bytes: block, size: block.byteLength }\n : null;\n }\n }\n\n return effect.toCarbonBinary();\n}\n\n/**\n * Builds a complete backend effect container.\n *\n * @param {object} effectRes Loaded version-15 `Tr2EffectRes`.\n * @param {object} permutationGraph Validated `PGRF` document.\n * @param {object} backendBodySet Translated backend bodies.\n * @param {object} backend Backend encoders.\n * @param {(shader:object, stage:object, context:object)=>Uint8Array} backend.encodeProgram Encodes one stage program.\n * @param {(unit:object, passKey:string)=>(Uint8Array|null)} backend.encodeBackendBlock Builds one pass's trailing block.\n * @param {object} [options] Container options.\n * @param {number[]|Uint8Array} [options.compilerVersion] Four version bytes.\n * @param {string} [options.sourceHash] 32 ASCII hash characters.\n * @returns {{bytes:Uint8Array, permutationCount:number, bodyCount:number}} Container and its body accounting.\n */\nexport function buildCarbonEffectContainer(\n effectRes,\n permutationGraph,\n backendBodySet,\n backend,\n options = {}\n)\n{\n if (typeof backend?.encodeProgram !== \"function\"\n || typeof backend?.encodeBackendBlock !== \"function\")\n {\n throw new CjsFormatWriteError(\n \"buildCarbonEffectContainer requires a backend with encodeProgram and encodeBackendBlock\"\n );\n }\n\n // One reader over the source file, shared by every body: the arena and the\n // offset table are read once, and a body is a seek into them. A dx11 effect\n // is a Carbon v15 container, so the same reader serves here and at load.\n const reader = new CjsCarbonEffectReader(effectRes.m_data, {\n source: effectRes.sourcePath || \"effect source\"\n });\n const unitsByKey = new Map(backendBodySet.passUnits.map((unit) => [ unit.key, unit ]));\n const bodyByKey = new Map(backendBodySet.bodies.map((body) => [ body.bodyKey, body ]));\n const writer = new CjsCarbonEffectWriter({\n backend: true,\n compilerVersion: options.compilerVersion ?? effectRes.m_compilerVersionBytes ?? [ 0, 0, 0, 0 ],\n ...(options.sourceHash ? { sourceHash: options.sourceHash } : {})\n });\n\n for (const axis of permutationGraph.axes)\n {\n writer.addPermutation({\n name: axis.name,\n defaultOption: axis.defaultOption,\n description: axis.description,\n type: axis.type,\n options: axis.options\n });\n }\n\n // Build one emitted description per source body key and reuse it for each\n // matching permutation. The writer may dedupe additional descriptions when\n // their emitted bytes become identical after source programs are replaced;\n // the offset table remains dense.\n const describedByBodyKey = new Map();\n\n for (const [ permutationIndex, variant ] of permutationGraph.variants.entries())\n {\n let description = describedByBodyKey.get(variant.bodyKey);\n\n if (!description)\n {\n const body = bodyByKey.get(variant.bodyKey);\n description = describeBody(\n reader,\n body?.representativePermutationIndex ?? permutationIndex,\n body?.status === \"translated\" ? unitsByPassKey(body, unitsByKey) : null,\n backend\n );\n describedByBodyKey.set(variant.bodyKey, description);\n }\n\n writer.addBody(permutationIndex, description);\n }\n\n return {\n bytes: writer.toBytes(),\n permutationCount: permutationGraph.variants.length,\n bodyCount: describedByBodyKey.size\n };\n}\n\nexport default buildCarbonEffectContainer;\n"],"names":["unitsByPassKey","body","unitsByKey","result","Map","pass","passes","set","passKey","get","unitKey","describeBody","reader","permutationIndex","passUnits","backend","effect","Tr2EffectDescription","fromCarbonBinary","readDescription","technique","techniques","passIndex","entries","name","unit","stage","stageInputs","exists","stageName","HlslShaderStageNames","stageType","shader","shaders","find","entry","key","bytes","encodeProgram","Uint8Array","sourceProgram","shaderSize","byteLength","block","encodeBackendBlock","backendBlock","size","toCarbonBinary","buildCarbonEffectContainer","effectRes","permutationGraph","backendBodySet","options","CjsFormatWriteError","CjsCarbonEffectReader","m_data","source","sourcePath","map","bodyByKey","bodies","bodyKey","writer","CjsCarbonEffectWriter","compilerVersion","m_compilerVersionBytes","sourceHash","axis","axes","addPermutation","defaultOption","description","type","describedByBodyKey","variant","variants","representativePermutationIndex","status","addBody","toBytes","permutationCount","length","bodyCount"],"mappings":"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"}
1
+ {"version":3,"file":"buildCarbonEffectContainer.js","sources":["../../../../src/format/carbonEffect/buildCarbonEffectContainer.js"],"sourcesContent":["import { CjsCarbonEffectWriter } from \"./CjsCarbonEffectWriter.js\";\nimport { CjsCarbonEffectReader } from \"./CjsCarbonEffectReader.js\";\nimport { HlslShaderStageNames } from \"../../formats/hlsl/core/tr2/HlslRenderContextEnum.js\";\nimport { Tr2EffectDescription } from \"../../resource/shader/reflection/Tr2EffectDescription.js\";\nimport { CjsFormatWriteError } from \"../CjsFormatError.js\";\n\n/**\n * Assembles a backend effect container: Carbon's v15 record layout carrying our\n * programs where a shipped file carries DXBC.\n *\n * The Carbon record layout is backend-invariant, and this module is the part\n * that holds it that way. It walks the permutation graph, emits one description\n * per distinct body, and substitutes stage programs and one optional trailing\n * block per pass. What a program *is* and what the block *contains* are the only\n * backend-varying decisions, so those two are the injected seam\n * (`encodeProgram`, `encodeBackendBlock`) and nothing else here varies.\n *\n * Two backends use it. WGSL for WebGPU, GLSL ES 3.00 for WebGL 2. Their blocks\n * are genuinely different documents - bind-group topology on one side, sampler\n * fusion and synthesised bindings on the other - and neither is derivable from\n * Carbon reflection. Which reader parses a given block is decided by the\n * resource path the file came from, the same way the backend itself is chosen,\n * so the two codecs may both start at their own version 1 without ambiguity.\n */\n\n/**\n * There is no envelope, no magic and no version of our own. That is deliberate.\n *\n * A twelve-byte prefix (`magic | containerVersion | payloadKind`) was carried\n * here and has been removed, along with a proposed \"v16\" for this variant.\n * Neither survived the only question worth asking of an addition: what breaks\n * without it?\n *\n * - `payloadKind` is redundant with the directory the file came from. Backend\n * identity belongs to the `effect.webgpu/` or `effect.webgl2/` resource path.\n * - The magic only distinguished our file from a Carbon one, which the same\n * directory already answers, and which the file *name* answers too.\n * - A version of our own would have claimed a number CCP owns, in the one field\n * whose job is telling a reader how to parse. A real v16 from them would then\n * collide with ours.\n *\n * What remains is a bare Carbon v15 record file. The shared record reader\n * detects the per-pass block from the description's declared end.\n *\n * The one addition that does survive the question is the block itself: neither\n * backend's binding topology is derivable from Carbon reflection, so without it\n * there is no way to bind anything and no pipeline.\n */\n\n/**\n * Maps one body's translated passes by pass key.\n *\n * @param {object} body Backend body-set record.\n * @param {Map<string, object>} unitsByKey Units indexed by key.\n * @returns {Map<string, object>} Units indexed by pass key.\n */\nfunction unitsByPassKey(body, unitsByKey)\n{\n const result = new Map();\n for (const pass of body.passes ?? [])\n {\n result.set(pass.passKey, unitsByKey.get(pass.unitKey));\n }\n return result;\n}\n\n/**\n * Builds one body's Carbon description record tree with backend programs\n * substituted in.\n *\n * A body the translator could not lower retains its representable non-program\n * description fields and carries zero-length programs. Emitting empty\n * `shaderData` says exactly what the wire knows: no backend program was stored.\n * Full portable source reflection remains in the in-memory build result, not\n * in this emitted body.\n *\n * @param {object} effectRes Loaded version-15 `Tr2EffectRes`.\n * @param {number} permutationIndex Representative permutation for this body.\n * @param {Map<string, object>|null} passUnits Units by pass key, or null when unsupported.\n * @param {object} backend Backend encoders.\n * @returns {object} Description record tree.\n */\nfunction describeBody(reader, permutationIndex, passUnits, backend)\n{\n // Read the source body once, as the resource layer reads any Carbon body,\n // then substitute on the resulting graph and emit it. The substitution is\n // two edits - a stage's program and a pass's trailing block - so there is\n // nothing here that wants a bespoke mapping of its own.\n const effect = Tr2EffectDescription.fromCarbonBinary(\n reader.readDescription(permutationIndex)\n );\n\n for (const technique of effect.techniques)\n {\n for (const [ passIndex, pass ] of technique.passes.entries())\n {\n const passKey = `${technique.name}.pass${passIndex}`;\n const unit = passUnits ? passUnits.get(passKey) : null;\n\n for (const stage of pass.stageInputs)\n {\n if (!stage.exists) continue;\n\n const stageName = HlslShaderStageNames[stage.stageType];\n const shader = unit?.shaders.find(\n (entry) => entry.key === `${passKey}.${stageName}`\n );\n // A body the translator could not lower keeps its describable\n // fields and carries a zero-length program: the wire then says\n // exactly what is known, which is that no backend program was\n // stored. The source DXBC is never re-emitted.\n const bytes = shader\n ? backend.encodeProgram(shader)\n : new Uint8Array(0);\n stage.sourceProgram = {\n ...stage.sourceProgram,\n bytes,\n shaderSize: bytes.byteLength\n };\n }\n\n const block = unit ? backend.encodeBackendBlock(unit, passKey) : null;\n pass.backendBlock = block\n ? { bytes: block, size: block.byteLength }\n : null;\n }\n }\n\n return effect.toCarbonBinary();\n}\n\n/**\n * Builds a complete backend effect container.\n *\n * @param {object} effectRes Loaded version-15 `Tr2EffectRes`.\n * @param {object} permutationGraph Validated derived `CJS_EFFECT_PERMUTATION_GRAPH` document (no chunk is stored).\n * @param {object} backendBodySet Translated backend bodies.\n * @param {object} backend Backend encoders.\n * @param {(shader:object, stage:object, context:object)=>Uint8Array} backend.encodeProgram Encodes one stage program.\n * @param {(unit:object, passKey:string)=>(Uint8Array|null)} backend.encodeBackendBlock Builds one pass's trailing block.\n * @param {object} [options] Container options.\n * @param {number} [options.version] Container data version to emit; must be one\n * of `CARBON_EFFECT_WRITE_VERSIONS`. Defaults to the current version, and is\n * independent of the source effect's version.\n * @param {number[]|Uint8Array} [options.compilerVersion] Four version bytes.\n * @param {string} [options.sourceHash] 32 ASCII hash characters.\n * @returns {{bytes:Uint8Array, permutationCount:number, bodyCount:number}} Container and its body accounting.\n */\nexport function buildCarbonEffectContainer(\n effectRes,\n permutationGraph,\n backendBodySet,\n backend,\n options = {}\n)\n{\n if (typeof backend?.encodeProgram !== \"function\"\n || typeof backend?.encodeBackendBlock !== \"function\")\n {\n throw new CjsFormatWriteError(\n \"buildCarbonEffectContainer requires a backend with encodeProgram and encodeBackendBlock\"\n );\n }\n\n // One reader over the source file, shared by every body: the arena and the\n // offset table are read once, and a body is a seek into them. A dx11 effect\n // is a Carbon v15 container, so the same reader serves here and at load.\n const reader = new CjsCarbonEffectReader(effectRes.m_data, {\n source: effectRes.sourcePath || \"effect source\"\n });\n const unitsByKey = new Map(backendBodySet.passUnits.map((unit) => [ unit.key, unit ]));\n const bodyByKey = new Map(backendBodySet.bodies.map((body) => [ body.bodyKey, body ]));\n const writer = new CjsCarbonEffectWriter({\n backend: true,\n // Not defaulted from the SOURCE effect's version. The source can be any\n // version the reader accepts; what we emit is a version we have a\n // writer for. Conflating the two would have us claim to emit whatever\n // we happened to read.\n ...(options.version === undefined ? {} : { version: options.version }),\n compilerVersion: options.compilerVersion ?? effectRes.m_compilerVersionBytes ?? [ 0, 0, 0, 0 ],\n ...(options.sourceHash ? { sourceHash: options.sourceHash } : {})\n });\n\n for (const axis of permutationGraph.axes)\n {\n writer.addPermutation({\n name: axis.name,\n defaultOption: axis.defaultOption,\n description: axis.description,\n type: axis.type,\n options: axis.options\n });\n }\n\n // Build one emitted description per source body key and reuse it for each\n // matching permutation. The writer may dedupe additional descriptions when\n // their emitted bytes become identical after source programs are replaced;\n // the offset table remains dense.\n const describedByBodyKey = new Map();\n\n for (const [ permutationIndex, variant ] of permutationGraph.variants.entries())\n {\n let description = describedByBodyKey.get(variant.bodyKey);\n\n if (!description)\n {\n const body = bodyByKey.get(variant.bodyKey);\n description = describeBody(\n reader,\n body?.representativePermutationIndex ?? permutationIndex,\n body?.status === \"translated\" ? unitsByPassKey(body, unitsByKey) : null,\n backend\n );\n describedByBodyKey.set(variant.bodyKey, description);\n }\n\n writer.addBody(permutationIndex, description);\n }\n\n return {\n bytes: writer.toBytes(),\n permutationCount: permutationGraph.variants.length,\n bodyCount: describedByBodyKey.size\n };\n}\n\nexport default buildCarbonEffectContainer;\n"],"names":["unitsByPassKey","body","unitsByKey","result","Map","pass","passes","set","passKey","get","unitKey","describeBody","reader","permutationIndex","passUnits","backend","effect","Tr2EffectDescription","fromCarbonBinary","readDescription","technique","techniques","passIndex","entries","name","unit","stage","stageInputs","exists","stageName","HlslShaderStageNames","stageType","shader","shaders","find","entry","key","bytes","encodeProgram","Uint8Array","sourceProgram","shaderSize","byteLength","block","encodeBackendBlock","backendBlock","size","toCarbonBinary","buildCarbonEffectContainer","effectRes","permutationGraph","backendBodySet","options","CjsFormatWriteError","CjsCarbonEffectReader","m_data","source","sourcePath","map","bodyByKey","bodies","bodyKey","writer","CjsCarbonEffectWriter","version","undefined","compilerVersion","m_compilerVersionBytes","sourceHash","axis","axes","addPermutation","defaultOption","description","type","describedByBodyKey","variant","variants","representativePermutationIndex","status","addBody","toBytes","permutationCount","length","bodyCount"],"mappings":";;;;;;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,cAAcA,CAACC,IAAI,EAAEC,UAAU,EACxC;AACI,EAAA,MAAMC,MAAM,GAAG,IAAIC,GAAG,EAAE;EACxB,KAAK,MAAMC,IAAI,IAAIJ,IAAI,CAACK,MAAM,IAAI,EAAE,EACpC;AACIH,IAAAA,MAAM,CAACI,GAAG,CAACF,IAAI,CAACG,OAAO,EAAEN,UAAU,CAACO,GAAG,CAACJ,IAAI,CAACK,OAAO,CAAC,CAAC;AAC1D,EAAA;AACA,EAAA,OAAOP,MAAM;AACjB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASQ,YAAYA,CAACC,MAAM,EAAEC,gBAAgB,EAAEC,SAAS,EAAEC,OAAO,EAClE;AACI;AACA;AACA;AACA;AACA,EAAA,MAAMC,MAAM,GAAGC,oBAAoB,CAACC,gBAAgB,CAChDN,MAAM,CAACO,eAAe,CAACN,gBAAgB,CAC3C,CAAC;AAED,EAAA,KAAK,MAAMO,SAAS,IAAIJ,MAAM,CAACK,UAAU,EACzC;AACI,IAAA,KAAK,MAAM,CAAEC,SAAS,EAAEjB,IAAI,CAAE,IAAIe,SAAS,CAACd,MAAM,CAACiB,OAAO,EAAE,EAC5D;MACI,MAAMf,OAAO,GAAG,CAAA,EAAGY,SAAS,CAACI,IAAI,CAAA,KAAA,EAAQF,SAAS,CAAA,CAAE;MACpD,MAAMG,IAAI,GAAGX,SAAS,GAAGA,SAAS,CAACL,GAAG,CAACD,OAAO,CAAC,GAAG,IAAI;AAEtD,MAAA,KAAK,MAAMkB,KAAK,IAAIrB,IAAI,CAACsB,WAAW,EACpC;AACI,QAAA,IAAI,CAACD,KAAK,CAACE,MAAM,EAAE;AAEnB,QAAA,MAAMC,SAAS,GAAGC,oBAAoB,CAACJ,KAAK,CAACK,SAAS,CAAC;AACvD,QAAA,MAAMC,MAAM,GAAGP,IAAI,EAAEQ,OAAO,CAACC,IAAI,CAC5BC,KAAK,IAAKA,KAAK,CAACC,GAAG,KAAK,CAAA,EAAG5B,OAAO,CAAA,CAAA,EAAIqB,SAAS,EACpD,CAAC;AACD;AACA;AACA;AACA;AACA,QAAA,MAAMQ,KAAK,GAAGL,MAAM,GACdjB,OAAO,CAACuB,aAAa,CAACN,MAAM,CAAC,GAC7B,IAAIO,UAAU,CAAC,CAAC,CAAC;QACvBb,KAAK,CAACc,aAAa,GAAG;UAClB,GAAGd,KAAK,CAACc,aAAa;UACtBH,KAAK;UACLI,UAAU,EAAEJ,KAAK,CAACK;SACrB;AACL,MAAA;AAEA,MAAA,MAAMC,KAAK,GAAGlB,IAAI,GAAGV,OAAO,CAAC6B,kBAAkB,CAACnB,IAAI,EAAEjB,OAAO,CAAC,GAAG,IAAI;AACrEH,MAAAA,IAAI,CAACwC,YAAY,GAAGF,KAAK,GACnB;AAAEN,QAAAA,KAAK,EAAEM,KAAK;QAAEG,IAAI,EAAEH,KAAK,CAACD;AAAW,OAAC,GACxC,IAAI;AACd,IAAA;AACJ,EAAA;AAEA,EAAA,OAAO1B,MAAM,CAAC+B,cAAc,EAAE;AAClC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,0BAA0BA,CACtCC,SAAS,EACTC,gBAAgB,EAChBC,cAAc,EACdpC,OAAO,EACPqC,OAAO,GAAG,EAAE,EAEhB;AACI,EAAA,IAAI,OAAOrC,OAAO,EAAEuB,aAAa,KAAK,UAAU,IACzC,OAAOvB,OAAO,EAAE6B,kBAAkB,KAAK,UAAU,EACxD;AACI,IAAA,MAAM,IAAIS,mBAAmB,CACzB,yFACJ,CAAC;AACL,EAAA;;AAEA;AACA;AACA;EACA,MAAMzC,MAAM,GAAG,IAAI0C,qBAAqB,CAACL,SAAS,CAACM,MAAM,EAAE;AACvDC,IAAAA,MAAM,EAAEP,SAAS,CAACQ,UAAU,IAAI;AACpC,GAAC,CAAC;EACF,MAAMvD,UAAU,GAAG,IAAIE,GAAG,CAAC+C,cAAc,CAACrC,SAAS,CAAC4C,GAAG,CAAEjC,IAAI,IAAK,CAAEA,IAAI,CAACW,GAAG,EAAEX,IAAI,CAAE,CAAC,CAAC;EACtF,MAAMkC,SAAS,GAAG,IAAIvD,GAAG,CAAC+C,cAAc,CAACS,MAAM,CAACF,GAAG,CAAEzD,IAAI,IAAK,CAAEA,IAAI,CAAC4D,OAAO,EAAE5D,IAAI,CAAE,CAAC,CAAC;AACtF,EAAA,MAAM6D,MAAM,GAAG,IAAIC,qBAAqB,CAAC;AACrChD,IAAAA,OAAO,EAAE,IAAI;AACb;AACA;AACA;AACA;IACA,IAAIqC,OAAO,CAACY,OAAO,KAAKC,SAAS,GAAG,EAAE,GAAG;MAAED,OAAO,EAAEZ,OAAO,CAACY;AAAQ,KAAC,CAAC;AACtEE,IAAAA,eAAe,EAAEd,OAAO,CAACc,eAAe,IAAIjB,SAAS,CAACkB,sBAAsB,IAAI,CAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAE;IAC9F,IAAIf,OAAO,CAACgB,UAAU,GAAG;MAAEA,UAAU,EAAEhB,OAAO,CAACgB;KAAY,GAAG,EAAE;AACpE,GAAC,CAAC;AAEF,EAAA,KAAK,MAAMC,IAAI,IAAInB,gBAAgB,CAACoB,IAAI,EACxC;IACIR,MAAM,CAACS,cAAc,CAAC;MAClB/C,IAAI,EAAE6C,IAAI,CAAC7C,IAAI;MACfgD,aAAa,EAAEH,IAAI,CAACG,aAAa;MACjCC,WAAW,EAAEJ,IAAI,CAACI,WAAW;MAC7BC,IAAI,EAAEL,IAAI,CAACK,IAAI;MACftB,OAAO,EAAEiB,IAAI,CAACjB;AAClB,KAAC,CAAC;AACN,EAAA;;AAEA;AACA;AACA;AACA;AACA,EAAA,MAAMuB,kBAAkB,GAAG,IAAIvE,GAAG,EAAE;AAEpC,EAAA,KAAK,MAAM,CAAES,gBAAgB,EAAE+D,OAAO,CAAE,IAAI1B,gBAAgB,CAAC2B,QAAQ,CAACtD,OAAO,EAAE,EAC/E;IACI,IAAIkD,WAAW,GAAGE,kBAAkB,CAAClE,GAAG,CAACmE,OAAO,CAACf,OAAO,CAAC;IAEzD,IAAI,CAACY,WAAW,EAChB;MACI,MAAMxE,IAAI,GAAG0D,SAAS,CAAClD,GAAG,CAACmE,OAAO,CAACf,OAAO,CAAC;MAC3CY,WAAW,GAAG9D,YAAY,CACtBC,MAAM,EACNX,IAAI,EAAE6E,8BAA8B,IAAIjE,gBAAgB,EACxDZ,IAAI,EAAE8E,MAAM,KAAK,YAAY,GAAG/E,cAAc,CAACC,IAAI,EAAEC,UAAU,CAAC,GAAG,IAAI,EACvEa,OACJ,CAAC;MACD4D,kBAAkB,CAACpE,GAAG,CAACqE,OAAO,CAACf,OAAO,EAAEY,WAAW,CAAC;AACxD,IAAA;AAEAX,IAAAA,MAAM,CAACkB,OAAO,CAACnE,gBAAgB,EAAE4D,WAAW,CAAC;AACjD,EAAA;EAEA,OAAO;AACHpC,IAAAA,KAAK,EAAEyB,MAAM,CAACmB,OAAO,EAAE;AACvBC,IAAAA,gBAAgB,EAAEhC,gBAAgB,CAAC2B,QAAQ,CAACM,MAAM;IAClDC,SAAS,EAAET,kBAAkB,CAAC7B;GACjC;AACL;;;;"}
@@ -196,6 +196,41 @@ const TEXTURE_DESCRIPTORS = Object.freeze({
196
196
  * @param {object[]} [block.transforms] Resource transforms.
197
197
  * @returns {Uint8Array} Self-contained block bytes.
198
198
  */
199
+ /**
200
+ * Refuses a binding whose derived fields disagree with what the reader rebuilds.
201
+ *
202
+ * `identity`, `scopeIdentity` and the per-binding `group` are not stored: the
203
+ * reader recomputes them from the register triple, the visibility mask and the
204
+ * enclosing group. A binding that carries a different value therefore does not
205
+ * fail - it round-trips into a different binding, and the engine matches on
206
+ * exactly these strings.
207
+ *
208
+ * `scopeIdentity` deserves the sharpest check, because the wire cannot preserve
209
+ * what it is derived from. Visibility is stored as a **bitmask**, so it is a
210
+ * set; `scopeIdentity` is built from `visibility[0]`, so it depends on an
211
+ * order. A binding written as `[fragment, vertex]` reads back as
212
+ * `[vertex, fragment]` and its scope silently moves from `@fragment` to
213
+ * `@vertex`. Canonical order is the only order the format can represent, so a
214
+ * producer must write it.
215
+ *
216
+ * @param {object} binding Binding record.
217
+ * @param {object} group Enclosing bind group.
218
+ */
219
+ function assertDerivedBindingFieldsAgree(binding, group) {
220
+ const identity = `${binding.resourceKind}:${binding.registerSpace}:${binding.registerIndex}`;
221
+ const canonical = unpackVisibility(packVisibility(binding.visibility));
222
+ const derived = [["identity", binding.identity, identity], ["scopeIdentity", binding.scopeIdentity, `${identity}@${canonical[0]}`], ["group", binding.group, group.group]];
223
+ for (const [field, actual, expected] of derived) {
224
+ if (actual === undefined || actual === null) continue;
225
+ if (actual === expected) continue;
226
+ throw new CjsFormatWriteError(`Binding ${identity} sets ${field} to ${JSON.stringify(actual)}, but the reader rebuilds` + ` ${JSON.stringify(expected)}. The value is not stored, so writing this would silently` + ` change the binding.`, {
227
+ identity,
228
+ field,
229
+ actual,
230
+ expected
231
+ });
232
+ }
233
+ }
199
234
  function writeBackendBlock(block) {
200
235
  const bindGroups = block.bindGroups ?? [];
201
236
  const transforms = block.transforms ?? [];
@@ -212,6 +247,7 @@ function writeBackendBlock(block) {
212
247
  resourceKind: binding.resourceKind
213
248
  });
214
249
  }
250
+ assertDerivedBindingFieldsAgree(binding, group);
215
251
  writer.u8(kind);
216
252
  writer.u8(binding.registerSpace);
217
253
  writer.u8(binding.binding);
@@ -1 +1 @@
1
- {"version":3,"file":"carbonEffectBackendBlock.js","sources":["../../../../src/format/carbonEffect/carbonEffectBackendBlock.js"],"sourcesContent":["import { CjsByteWriter } from \"../CjsByteWriter.js\";\nimport { CjsByteReader } from \"../CjsByteReader.js\";\nimport { CjsFormatReadError, CjsFormatWriteError } from \"../CjsFormatError.js\";\nimport {\n CARBON_BACKEND_ENGINE_ID,\n readBackendEngineId\n} from \"./backendEngineId.js\";\nimport {\n CARBON_BACKEND_TRANSFORM_FAMILY,\n DETAIL_MAP_ARRAY_DEFAULTS,\n readInlineString,\n readTransformSection,\n writeInlineString,\n writeTransformSection\n} from \"./carbonEffectResourceTransform.js\";\n\n/**\n * The WebGPU pass block: bind-group layouts plus the shared resource-transform\n * section.\n *\n * Neither is derivable from Carbon reflection. Carbon's `registers[]` is the D3D\n * binding model, while `(group, binding, visibility, generatedSymbol)` comes from\n * the *lowered IR's* bindings. So this is the port's only genuine invention, and\n * it is built entirely from Carbon's own techniques: count-prefixed fixed-width\n * records inside a blob referenced by `{u32 size, u32 offset}`, exactly like\n * program source.\n *\n * Both sections share **one** block rather than occupying two, because the\n * invariant worth keeping is \"the Carbon region is backend-invariant, with\n * exactly one optional trailing block\". They are also mutually required — a\n * transform needs a carrier binding in the same pass's layout and a transformed\n * binding needs a recipe — so one unit is the honest shape.\n *\n * ## Why the block is self-contained\n *\n * The block lives in the arena so that identical layouts dedupe across bodies the\n * way program source does; measured sharing is 30.5:1 at `(body, pass)`\n * granularity, 22 distinct blocks across 672 pairs.\n *\n * That forces one design consequence: **the block cannot contain arena offsets.**\n * An offset is only known after the arena's content sort, and the sort depends on\n * every blob's bytes — including this one. A block referencing the arena would\n * have to be interned before its own contents could be computed. Carbon never\n * meets this because no Carbon arena blob refers to the arena.\n *\n * So strings are inline here, length-prefixed, not interned. The cost is real but\n * small: a repeated `\"t11\"` inside 22 distinct blocks rather than one shared copy.\n * The block itself is what dedupes, and it dedupes 30x.\n */\n\n/**\n * The leading byte is the TARGET ENGINE IDENTIFIER, not a version.\n *\n * It was called CARBON_EFFECT_BACKEND_BLOCK_VERSION and emitted 1, while the\n * WebGL2 writer also emitted 1 meaning something unrelated - so the byte could\n * not do the one job it was there for. Renamed to what it is, and given a\n * distinct value per backend, it identifies the block.\n *\n * Carbon container version remains the only version. Nothing stores this block:\n * a consumer builds one and reads it back in the same process, so a block from\n * a different build is caught by the trailing-byte check at the end of the read.\n *\n * The identifier is shared with the WebGL2 block rather than duplicated, so the\n * two cannot drift into meaning the same number again.\n */\n\n/**\n * Resource kinds, ordered so the wire value is stable\n * (`buildWgslSet.js:117-122`).\n */\nexport const CARBON_BACKEND_RESOURCE_KIND = Object.freeze([\n \"uniform-buffer\",\n \"sampled-resource\",\n \"sampler\",\n \"storage-resource\"\n]);\n\n/** Shader stages, as a bit position in the visibility mask. */\nexport const CARBON_BACKEND_VISIBILITY = Object.freeze([ \"vertex\", \"fragment\", \"compute\" ]);\n\n// The transform section is shared with the GLSL block; both re-export the\n// family enum and defaults so a consumer of either codec sees them.\nexport { CARBON_BACKEND_TRANSFORM_FAMILY, DETAIL_MAP_ARRAY_DEFAULTS };\n\n/** Marks an absent optional `u32`. Carbon's null-reference value. */\nconst ABSENT = 0xffffffff;\n\n/**\n * Encodes a visibility list as a bit mask.\n *\n * @param {string[]} visibility Stage names.\n * @returns {number} Bit mask.\n */\nfunction packVisibility(visibility)\n{\n let mask = 0;\n for (const stage of visibility ?? [])\n {\n const bit = CARBON_BACKEND_VISIBILITY.indexOf(stage);\n if (bit < 0)\n {\n throw new CjsFormatWriteError(`Unknown binding visibility \"${stage}\"`, { stage });\n }\n mask |= 1 << bit;\n }\n return mask;\n}\n\n/**\n * Decodes a visibility bit mask, in the canonical stage order.\n *\n * @param {number} mask Bit mask.\n * @returns {string[]} Stage names.\n */\nfunction unpackVisibility(mask)\n{\n const stages = [];\n for (let bit = 0; bit < CARBON_BACKEND_VISIBILITY.length; bit += 1)\n {\n if (mask & (1 << bit)) stages.push(CARBON_BACKEND_VISIBILITY[bit]);\n }\n return stages;\n}\n\n/**\n * Rebuilds a binding's WebGPU descriptor from what the block does store.\n *\n * The descriptor — `buffer`, `texture` or `sampler` — is not on the wire because\n * it is a pure function of the WGSL type, the resource kind and the structure\n * stride, all three of which are. `lowerBindingLayout.js` derives it from the\n * same three inputs on the way out; this derives it back on the way in.\n *\n * The pairs that could collide do not. `array<u32>` reaches both a structured\n * buffer and a compute typed buffer, and the structure stride separates them:\n * structured always carries one, typed never does.\n *\n * @param {object} binding Decoded binding record.\n * @returns {object} The descriptor fragment, as one `{buffer|texture|sampler}` key.\n */\nfunction deriveBindingDescriptor(binding)\n{\n const stride = binding.structureStride;\n\n if (binding.resourceKind === \"sampler\")\n {\n return { sampler: { type: \"filtering\" } };\n }\n\n if (binding.resourceKind === \"uniform-buffer\")\n {\n const vec4Count = /^array<vec4<f32>, (\\d+)>$/u.exec(binding.type)?.[1];\n if (!vec4Count)\n {\n throw new CjsFormatReadError(\n `Backend block uniform binding has untranslatable type \"${binding.type}\"`,\n { type: binding.type }\n );\n }\n return {\n buffer: {\n type: \"uniform\",\n hasDynamicOffset: false,\n minBindingSize: Number(vec4Count) * 16\n }\n };\n }\n\n if (binding.resourceKind === \"sampled-resource\")\n {\n const texture = TEXTURE_DESCRIPTORS[binding.type];\n if (texture) return { texture: { ...texture } };\n\n return {\n buffer: {\n type: \"read-only-storage\",\n hasDynamicOffset: false,\n minBindingSize: stride === undefined ? 4 : stride\n }\n };\n }\n\n return {\n buffer: {\n type: \"storage\",\n hasDynamicOffset: false,\n minBindingSize: stride === undefined ? 4 : stride\n }\n };\n}\n\n/** Texture descriptors by WGSL type (`lowerBindingLayout.js:90-95`). */\nconst TEXTURE_DESCRIPTORS = Object.freeze({\n \"texture_2d<f32>\": Object.freeze({ sampleType: \"float\", viewDimension: \"2d\", multisampled: false }),\n \"texture_cube<f32>\": Object.freeze({ sampleType: \"float\", viewDimension: \"cube\", multisampled: false }),\n \"texture_3d<f32>\": Object.freeze({ sampleType: \"float\", viewDimension: \"3d\", multisampled: false }),\n \"texture_2d_array<f32>\": Object.freeze({ sampleType: \"float\", viewDimension: \"2d-array\", multisampled: false })\n});\n\n/**\n * Serialises one pass's backend block.\n *\n * @param {object} block Block contents.\n * @param {object[]} [block.bindGroups] Bind groups with their bindings.\n * @param {object[]} [block.transforms] Resource transforms.\n * @returns {Uint8Array} Self-contained block bytes.\n */\nexport function writeBackendBlock(block)\n{\n const bindGroups = block.bindGroups ?? [];\n const transforms = block.transforms ?? [];\n const writer = new CjsByteWriter(256);\n\n writer.u8(CARBON_BACKEND_ENGINE_ID.webgpu);\n\n writer.u8(bindGroups.length);\n for (const group of bindGroups)\n {\n writer.u8(group.group);\n writer.u8(group.bindings.length);\n for (const binding of group.bindings)\n {\n const kind = CARBON_BACKEND_RESOURCE_KIND.indexOf(binding.resourceKind);\n if (kind < 0)\n {\n throw new CjsFormatWriteError(`Unknown resource kind \"${binding.resourceKind}\"`, {\n resourceKind: binding.resourceKind\n });\n }\n writer.u8(kind);\n writer.u8(binding.registerSpace);\n writer.u8(binding.binding);\n writer.u8(packVisibility(binding.visibility));\n writer.u32(binding.registerIndex);\n writer.u32(binding.structureStride ?? ABSENT);\n writer.u8(binding.arrayLayerCount ?? 0);\n writeInlineString(writer, binding.type);\n writeInlineString(writer, binding.generatedSymbol);\n // A binding either carries a transform id or it does not; an empty\n // string is not a legal id, so length zero encodes absence.\n writeInlineString(writer, binding.transformId ?? \"\");\n }\n }\n\n writeTransformSection(writer, transforms);\n\n return writer.toBytes();\n}\n\n/**\n * Parses one pass's backend block, restoring every derived field.\n *\n * @param {ArrayBuffer|ArrayBufferView|Uint8Array} bytes Block bytes.\n * @param {object} [options] Read options.\n * @param {string} [options.layoutKey] Enclosing pass key, restored onto records.\n * @param {string} [options.source] Source name for error details.\n * @returns {object} Block contents with derived fields restored.\n */\nexport function readBackendBlock(bytes, options = {})\n{\n const reader = new CjsByteReader(bytes, { source: options.source ?? \"backend block\" });\n const layoutKey = options.layoutKey ?? null;\n\n readBackendEngineId(reader, CARBON_BACKEND_ENGINE_ID.webgpu, options.source ?? \"backend block\");\n\n const bindGroups = [];\n const groupCount = reader.readUint8();\n for (let groupIndex = 0; groupIndex < groupCount; groupIndex += 1)\n {\n const group = reader.readUint8();\n const bindings = [];\n const bindingCount = reader.readUint8();\n for (let index = 0; index < bindingCount; index += 1)\n {\n const resourceKind = CARBON_BACKEND_RESOURCE_KIND[reader.readUint8()];\n const registerSpace = reader.readUint8();\n const binding = reader.readUint8();\n const visibility = unpackVisibility(reader.readUint8());\n const registerIndex = reader.readUint32();\n const structureStride = reader.readUint32();\n const arrayLayerCount = reader.readUint8();\n const type = readInlineString(reader);\n const generatedSymbol = readInlineString(reader);\n const transformId = readInlineString(reader);\n\n const identity = `${resourceKind}:${registerSpace}:${registerIndex}`;\n const record = {\n group,\n binding,\n resourceKind,\n registerSpace,\n registerIndex,\n visibility,\n type,\n generatedSymbol,\n identity,\n scopeIdentity: `${identity}@${visibility[0]}`,\n ...(structureStride === ABSENT ? {} : { structureStride }),\n ...(arrayLayerCount === 0 ? {} : { arrayLayerCount }),\n ...(transformId === \"\" ? {} : { transformId })\n };\n bindings.push({ ...record, ...deriveBindingDescriptor(record) });\n }\n bindGroups.push({ group, bindings });\n }\n\n const transforms = readTransformSection(reader, layoutKey);\n\n // A sized record must land exactly on its declared end. Trailing bytes mean\n // the writer knew fields this reader does not, which with no version byte is\n // the ONLY signal that a block came from a different build - so this check is\n // load-bearing rather than defensive.\n // This is the only surviving form of a closed-schema check: under a record\n // layout a field either exists at its offset or the read fails, so there is\n // nothing per-field left to assert, but exhaustiveness is still checkable and\n // is exactly what a silently-discarded tail would violate.\n if (reader.remaining !== 0)\n {\n throw new CjsFormatReadError(\n `Backend block has ${reader.remaining} unparsed trailing byte(s); rebuild the effect package`,\n { source: options.source ?? \"backend block\", trailingBytes: reader.remaining }\n );\n }\n\n return {\n layoutKey,\n bindGroups,\n transforms,\n trailingBytes: 0\n };\n}\n"],"names":["CARBON_BACKEND_RESOURCE_KIND","Object","freeze","CARBON_BACKEND_VISIBILITY","ABSENT","packVisibility","visibility","mask","stage","bit","indexOf","CjsFormatWriteError","unpackVisibility","stages","length","push","deriveBindingDescriptor","binding","stride","structureStride","resourceKind","sampler","type","vec4Count","exec","CjsFormatReadError","buffer","hasDynamicOffset","minBindingSize","Number","texture","TEXTURE_DESCRIPTORS","undefined","sampleType","viewDimension","multisampled","writeBackendBlock","block","bindGroups","transforms","writer","CjsByteWriter","u8","CARBON_BACKEND_ENGINE_ID","webgpu","group","bindings","kind","registerSpace","u32","registerIndex","arrayLayerCount","writeInlineString","generatedSymbol","transformId","writeTransformSection","toBytes","readBackendBlock","bytes","options","reader","CjsByteReader","source","layoutKey","readBackendEngineId","groupCount","readUint8","groupIndex","bindingCount","index","readUint32","readInlineString","identity","record","scopeIdentity","readTransformSection","remaining","trailingBytes"],"mappings":"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"}
1
+ {"version":3,"file":"carbonEffectBackendBlock.js","sources":["../../../../src/format/carbonEffect/carbonEffectBackendBlock.js"],"sourcesContent":["import { CjsByteWriter } from \"../CjsByteWriter.js\";\nimport { CjsByteReader } from \"../CjsByteReader.js\";\nimport { CjsFormatReadError, CjsFormatWriteError } from \"../CjsFormatError.js\";\nimport {\n CARBON_BACKEND_ENGINE_ID,\n readBackendEngineId\n} from \"./backendEngineId.js\";\nimport {\n CARBON_BACKEND_TRANSFORM_FAMILY,\n DETAIL_MAP_ARRAY_DEFAULTS,\n readInlineString,\n readTransformSection,\n writeInlineString,\n writeTransformSection\n} from \"./carbonEffectResourceTransform.js\";\n\n/**\n * The WebGPU pass block: bind-group layouts plus the shared resource-transform\n * section.\n *\n * Neither is derivable from Carbon reflection. Carbon's `registers[]` is the D3D\n * binding model, while `(group, binding, visibility, generatedSymbol)` comes from\n * the *lowered IR's* bindings. So this is the port's only genuine invention, and\n * it is built entirely from Carbon's own techniques: count-prefixed fixed-width\n * records inside a blob referenced by `{u32 size, u32 offset}`, exactly like\n * program source.\n *\n * Both sections share **one** block rather than occupying two, because the\n * invariant worth keeping is \"the Carbon region is backend-invariant, with\n * exactly one optional trailing block\". They are also mutually required — a\n * transform needs a carrier binding in the same pass's layout and a transformed\n * binding needs a recipe — so one unit is the honest shape.\n *\n * ## Why the block is self-contained\n *\n * The block lives in the arena so that identical layouts dedupe across bodies the\n * way program source does; measured sharing is 30.5:1 at `(body, pass)`\n * granularity, 22 distinct blocks across 672 pairs.\n *\n * That forces one design consequence: **the block cannot contain arena offsets.**\n * An offset is only known after the arena's content sort, and the sort depends on\n * every blob's bytes — including this one. A block referencing the arena would\n * have to be interned before its own contents could be computed. Carbon never\n * meets this because no Carbon arena blob refers to the arena.\n *\n * So strings are inline here, length-prefixed, not interned. The cost is real but\n * small: a repeated `\"t11\"` inside 22 distinct blocks rather than one shared copy.\n * The block itself is what dedupes, and it dedupes 30x.\n */\n\n/**\n * The leading byte is the TARGET ENGINE IDENTIFIER, not a version.\n *\n * It was called CARBON_EFFECT_BACKEND_BLOCK_VERSION and emitted 1, while the\n * WebGL2 writer also emitted 1 meaning something unrelated - so the byte could\n * not do the one job it was there for. Renamed to what it is, and given a\n * distinct value per backend, it identifies the block.\n *\n * Carbon container version remains the only version. Nothing stores this block:\n * a consumer builds one and reads it back in the same process, so a block from\n * a different build is caught by the trailing-byte check at the end of the read.\n *\n * The identifier is shared with the WebGL2 block rather than duplicated, so the\n * two cannot drift into meaning the same number again.\n */\n\n/**\n * Resource kinds, ordered so the wire value is stable\n * (`buildWgslSet.js:117-122`).\n */\nexport const CARBON_BACKEND_RESOURCE_KIND = Object.freeze([\n \"uniform-buffer\",\n \"sampled-resource\",\n \"sampler\",\n \"storage-resource\"\n]);\n\n/** Shader stages, as a bit position in the visibility mask. */\nexport const CARBON_BACKEND_VISIBILITY = Object.freeze([ \"vertex\", \"fragment\", \"compute\" ]);\n\n// The transform section is shared with the GLSL block; both re-export the\n// family enum and defaults so a consumer of either codec sees them.\nexport { CARBON_BACKEND_TRANSFORM_FAMILY, DETAIL_MAP_ARRAY_DEFAULTS };\n\n/** Marks an absent optional `u32`. Carbon's null-reference value. */\nconst ABSENT = 0xffffffff;\n\n/**\n * Encodes a visibility list as a bit mask.\n *\n * @param {string[]} visibility Stage names.\n * @returns {number} Bit mask.\n */\nfunction packVisibility(visibility)\n{\n let mask = 0;\n for (const stage of visibility ?? [])\n {\n const bit = CARBON_BACKEND_VISIBILITY.indexOf(stage);\n if (bit < 0)\n {\n throw new CjsFormatWriteError(`Unknown binding visibility \"${stage}\"`, { stage });\n }\n mask |= 1 << bit;\n }\n return mask;\n}\n\n/**\n * Decodes a visibility bit mask, in the canonical stage order.\n *\n * @param {number} mask Bit mask.\n * @returns {string[]} Stage names.\n */\nfunction unpackVisibility(mask)\n{\n const stages = [];\n for (let bit = 0; bit < CARBON_BACKEND_VISIBILITY.length; bit += 1)\n {\n if (mask & (1 << bit)) stages.push(CARBON_BACKEND_VISIBILITY[bit]);\n }\n return stages;\n}\n\n/**\n * Rebuilds a binding's WebGPU descriptor from what the block does store.\n *\n * The descriptor — `buffer`, `texture` or `sampler` — is not on the wire because\n * it is a pure function of the WGSL type, the resource kind and the structure\n * stride, all three of which are. `lowerBindingLayout.js` derives it from the\n * same three inputs on the way out; this derives it back on the way in.\n *\n * The pairs that could collide do not. `array<u32>` reaches both a structured\n * buffer and a compute typed buffer, and the structure stride separates them:\n * structured always carries one, typed never does.\n *\n * @param {object} binding Decoded binding record.\n * @returns {object} The descriptor fragment, as one `{buffer|texture|sampler}` key.\n */\nfunction deriveBindingDescriptor(binding)\n{\n const stride = binding.structureStride;\n\n if (binding.resourceKind === \"sampler\")\n {\n return { sampler: { type: \"filtering\" } };\n }\n\n if (binding.resourceKind === \"uniform-buffer\")\n {\n const vec4Count = /^array<vec4<f32>, (\\d+)>$/u.exec(binding.type)?.[1];\n if (!vec4Count)\n {\n throw new CjsFormatReadError(\n `Backend block uniform binding has untranslatable type \"${binding.type}\"`,\n { type: binding.type }\n );\n }\n return {\n buffer: {\n type: \"uniform\",\n hasDynamicOffset: false,\n minBindingSize: Number(vec4Count) * 16\n }\n };\n }\n\n if (binding.resourceKind === \"sampled-resource\")\n {\n const texture = TEXTURE_DESCRIPTORS[binding.type];\n if (texture) return { texture: { ...texture } };\n\n return {\n buffer: {\n type: \"read-only-storage\",\n hasDynamicOffset: false,\n minBindingSize: stride === undefined ? 4 : stride\n }\n };\n }\n\n return {\n buffer: {\n type: \"storage\",\n hasDynamicOffset: false,\n minBindingSize: stride === undefined ? 4 : stride\n }\n };\n}\n\n/** Texture descriptors by WGSL type (`lowerBindingLayout.js:90-95`). */\nconst TEXTURE_DESCRIPTORS = Object.freeze({\n \"texture_2d<f32>\": Object.freeze({ sampleType: \"float\", viewDimension: \"2d\", multisampled: false }),\n \"texture_cube<f32>\": Object.freeze({ sampleType: \"float\", viewDimension: \"cube\", multisampled: false }),\n \"texture_3d<f32>\": Object.freeze({ sampleType: \"float\", viewDimension: \"3d\", multisampled: false }),\n \"texture_2d_array<f32>\": Object.freeze({ sampleType: \"float\", viewDimension: \"2d-array\", multisampled: false })\n});\n\n/**\n * Serialises one pass's backend block.\n *\n * @param {object} block Block contents.\n * @param {object[]} [block.bindGroups] Bind groups with their bindings.\n * @param {object[]} [block.transforms] Resource transforms.\n * @returns {Uint8Array} Self-contained block bytes.\n */\n/**\n * Refuses a binding whose derived fields disagree with what the reader rebuilds.\n *\n * `identity`, `scopeIdentity` and the per-binding `group` are not stored: the\n * reader recomputes them from the register triple, the visibility mask and the\n * enclosing group. A binding that carries a different value therefore does not\n * fail - it round-trips into a different binding, and the engine matches on\n * exactly these strings.\n *\n * `scopeIdentity` deserves the sharpest check, because the wire cannot preserve\n * what it is derived from. Visibility is stored as a **bitmask**, so it is a\n * set; `scopeIdentity` is built from `visibility[0]`, so it depends on an\n * order. A binding written as `[fragment, vertex]` reads back as\n * `[vertex, fragment]` and its scope silently moves from `@fragment` to\n * `@vertex`. Canonical order is the only order the format can represent, so a\n * producer must write it.\n *\n * @param {object} binding Binding record.\n * @param {object} group Enclosing bind group.\n */\nfunction assertDerivedBindingFieldsAgree(binding, group)\n{\n const identity = `${binding.resourceKind}:${binding.registerSpace}:${binding.registerIndex}`;\n const canonical = unpackVisibility(packVisibility(binding.visibility));\n const derived = [\n [ \"identity\", binding.identity, identity ],\n [ \"scopeIdentity\", binding.scopeIdentity, `${identity}@${canonical[0]}` ],\n [ \"group\", binding.group, group.group ]\n ];\n\n for (const [ field, actual, expected ] of derived)\n {\n if (actual === undefined || actual === null) continue;\n if (actual === expected) continue;\n\n throw new CjsFormatWriteError(\n `Binding ${identity} sets ${field} to ${JSON.stringify(actual)}, but the reader rebuilds`\n + ` ${JSON.stringify(expected)}. The value is not stored, so writing this would silently`\n + ` change the binding.`,\n { identity, field, actual, expected }\n );\n }\n}\n\nexport function writeBackendBlock(block)\n{\n const bindGroups = block.bindGroups ?? [];\n const transforms = block.transforms ?? [];\n const writer = new CjsByteWriter(256);\n\n writer.u8(CARBON_BACKEND_ENGINE_ID.webgpu);\n\n writer.u8(bindGroups.length);\n for (const group of bindGroups)\n {\n writer.u8(group.group);\n writer.u8(group.bindings.length);\n for (const binding of group.bindings)\n {\n const kind = CARBON_BACKEND_RESOURCE_KIND.indexOf(binding.resourceKind);\n if (kind < 0)\n {\n throw new CjsFormatWriteError(`Unknown resource kind \"${binding.resourceKind}\"`, {\n resourceKind: binding.resourceKind\n });\n }\n assertDerivedBindingFieldsAgree(binding, group);\n writer.u8(kind);\n writer.u8(binding.registerSpace);\n writer.u8(binding.binding);\n writer.u8(packVisibility(binding.visibility));\n writer.u32(binding.registerIndex);\n writer.u32(binding.structureStride ?? ABSENT);\n writer.u8(binding.arrayLayerCount ?? 0);\n writeInlineString(writer, binding.type);\n writeInlineString(writer, binding.generatedSymbol);\n // A binding either carries a transform id or it does not; an empty\n // string is not a legal id, so length zero encodes absence.\n writeInlineString(writer, binding.transformId ?? \"\");\n }\n }\n\n writeTransformSection(writer, transforms);\n\n return writer.toBytes();\n}\n\n/**\n * Parses one pass's backend block, restoring every derived field.\n *\n * @param {ArrayBuffer|ArrayBufferView|Uint8Array} bytes Block bytes.\n * @param {object} [options] Read options.\n * @param {string} [options.layoutKey] Enclosing pass key, restored onto records.\n * @param {string} [options.source] Source name for error details.\n * @returns {object} Block contents with derived fields restored.\n */\nexport function readBackendBlock(bytes, options = {})\n{\n const reader = new CjsByteReader(bytes, { source: options.source ?? \"backend block\" });\n const layoutKey = options.layoutKey ?? null;\n\n readBackendEngineId(reader, CARBON_BACKEND_ENGINE_ID.webgpu, options.source ?? \"backend block\");\n\n const bindGroups = [];\n const groupCount = reader.readUint8();\n for (let groupIndex = 0; groupIndex < groupCount; groupIndex += 1)\n {\n const group = reader.readUint8();\n const bindings = [];\n const bindingCount = reader.readUint8();\n for (let index = 0; index < bindingCount; index += 1)\n {\n const resourceKind = CARBON_BACKEND_RESOURCE_KIND[reader.readUint8()];\n const registerSpace = reader.readUint8();\n const binding = reader.readUint8();\n const visibility = unpackVisibility(reader.readUint8());\n const registerIndex = reader.readUint32();\n const structureStride = reader.readUint32();\n const arrayLayerCount = reader.readUint8();\n const type = readInlineString(reader);\n const generatedSymbol = readInlineString(reader);\n const transformId = readInlineString(reader);\n\n const identity = `${resourceKind}:${registerSpace}:${registerIndex}`;\n const record = {\n group,\n binding,\n resourceKind,\n registerSpace,\n registerIndex,\n visibility,\n type,\n generatedSymbol,\n identity,\n scopeIdentity: `${identity}@${visibility[0]}`,\n ...(structureStride === ABSENT ? {} : { structureStride }),\n ...(arrayLayerCount === 0 ? {} : { arrayLayerCount }),\n ...(transformId === \"\" ? {} : { transformId })\n };\n bindings.push({ ...record, ...deriveBindingDescriptor(record) });\n }\n bindGroups.push({ group, bindings });\n }\n\n const transforms = readTransformSection(reader, layoutKey);\n\n // A sized record must land exactly on its declared end. Trailing bytes mean\n // the writer knew fields this reader does not, which with no version byte is\n // the ONLY signal that a block came from a different build - so this check is\n // load-bearing rather than defensive.\n // This is the only surviving form of a closed-schema check: under a record\n // layout a field either exists at its offset or the read fails, so there is\n // nothing per-field left to assert, but exhaustiveness is still checkable and\n // is exactly what a silently-discarded tail would violate.\n if (reader.remaining !== 0)\n {\n throw new CjsFormatReadError(\n `Backend block has ${reader.remaining} unparsed trailing byte(s); rebuild the effect package`,\n { source: options.source ?? \"backend block\", trailingBytes: reader.remaining }\n );\n }\n\n return {\n layoutKey,\n bindGroups,\n transforms,\n trailingBytes: 0\n };\n}\n"],"names":["CARBON_BACKEND_RESOURCE_KIND","Object","freeze","CARBON_BACKEND_VISIBILITY","ABSENT","packVisibility","visibility","mask","stage","bit","indexOf","CjsFormatWriteError","unpackVisibility","stages","length","push","deriveBindingDescriptor","binding","stride","structureStride","resourceKind","sampler","type","vec4Count","exec","CjsFormatReadError","buffer","hasDynamicOffset","minBindingSize","Number","texture","TEXTURE_DESCRIPTORS","undefined","sampleType","viewDimension","multisampled","assertDerivedBindingFieldsAgree","group","identity","registerSpace","registerIndex","canonical","derived","scopeIdentity","field","actual","expected","JSON","stringify","writeBackendBlock","block","bindGroups","transforms","writer","CjsByteWriter","u8","CARBON_BACKEND_ENGINE_ID","webgpu","bindings","kind","u32","arrayLayerCount","writeInlineString","generatedSymbol","transformId","writeTransformSection","toBytes","readBackendBlock","bytes","options","reader","CjsByteReader","source","layoutKey","readBackendEngineId","groupCount","readUint8","groupIndex","bindingCount","index","readUint32","readInlineString","record","readTransformSection","remaining","trailingBytes"],"mappings":"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"}
@@ -18,6 +18,42 @@ import { compareUtf8 } from '../compareUtf8.js';
18
18
  */
19
19
  const CARBON_EFFECT_DATA_VERSION = 15;
20
20
 
21
+ /**
22
+ * Every version the WRITER can actually produce, newest first.
23
+ *
24
+ * This is deliberately separate from what the reader accepts
25
+ * (`CARBON_EFFECT_MIN_DATA_VERSION`..`CARBON_EFFECT_DATA_VERSION`) and from the
26
+ * default above. Reading a version needs a branch that interprets bytes;
27
+ * emitting one needs a branch that produces them, and we have only ever written
28
+ * the v15 shape.
29
+ *
30
+ * **The version is a parameter, not a constant, so that adding v16 is a matter
31
+ * of adding branches and one entry here rather than re-plumbing every caller.**
32
+ * It is not a promise that any listed version can be requested today.
33
+ *
34
+ * The failure this guards against is the tempting one: accepting a version we
35
+ * cannot emit and writing v15 bytes under its number. That produces a file that
36
+ * lies about itself, which is strictly worse than refusing.
37
+ */
38
+ const CARBON_EFFECT_WRITE_VERSIONS = Object.freeze([15]);
39
+
40
+ /**
41
+ * Validate a requested write version, or fall back to the default.
42
+ *
43
+ * @param {number} [version] Requested container data version.
44
+ * @returns {number} A version this writer can emit.
45
+ */
46
+ function resolveWriteVersion(version) {
47
+ if (version === undefined || version === null) return CARBON_EFFECT_DATA_VERSION;
48
+ if (!CARBON_EFFECT_WRITE_VERSIONS.includes(version)) {
49
+ throw new CjsFormatWriteError(`Cannot write Carbon effect container version ${version}`, {
50
+ requested: version,
51
+ writable: [...CARBON_EFFECT_WRITE_VERSIONS]
52
+ });
53
+ }
54
+ return version;
55
+ }
56
+
21
57
  /**
22
58
  * The oldest description-blob version this module reads.
23
59
  *
@@ -1116,11 +1152,19 @@ function readEffectDescription(reader, options = {}) {
1116
1152
  * @param {object} [options.arena] Arena resolver; defaults to passthrough.
1117
1153
  * @param {boolean} [options.backend] Emit our optional per-pass trailing block.
1118
1154
  * Leave false to produce bytes a Carbon reader accepts unchanged.
1155
+ * @param {number} [options.version] Container data version to emit; must be one
1156
+ * of `CARBON_EFFECT_WRITE_VERSIONS`. Defaults to the current version.
1119
1157
  * @returns {import("../CjsByteWriter.js").CjsByteWriter} The writer.
1120
1158
  */
1121
1159
  function writeEffectDescription(writer, description, options = {}) {
1122
1160
  const arena = options.arena ?? passthroughArena;
1123
1161
  const backend = options.backend === true;
1162
+ // Validated, though nothing below branches on it yet: today every writable
1163
+ // version produces the same body shape. A caller asking for a version we
1164
+ // cannot emit must be refused here, at the description boundary, rather
1165
+ // than have its request ignored and v15 bytes written under another number.
1166
+ // When a version changes the body, branch on this.
1167
+ resolveWriteVersion(options.version);
1124
1168
  writer.u8(description.techniques.length);
1125
1169
  for (const technique of description.techniques) {
1126
1170
  writer.u32(arena.string(technique.name));
@@ -1143,5 +1187,5 @@ function writeEffectDescription(writer, description, options = {}) {
1143
1187
  return writer;
1144
1188
  }
1145
1189
 
1146
- export { CARBON_ANNOTATION_TYPE, CARBON_EFFECT_COUNT_CAPS, CARBON_EFFECT_DATA_VERSION, CARBON_EFFECT_MIN_DATA_VERSION, CARBON_SHADER_CONSTANTS_MAX, collectArena, compareAnnotationNames, internArena, passthroughArena, readEffectDescription, writeEffectDescription };
1190
+ export { CARBON_ANNOTATION_TYPE, CARBON_EFFECT_COUNT_CAPS, CARBON_EFFECT_DATA_VERSION, CARBON_EFFECT_MIN_DATA_VERSION, CARBON_EFFECT_WRITE_VERSIONS, CARBON_SHADER_CONSTANTS_MAX, collectArena, compareAnnotationNames, internArena, passthroughArena, readEffectDescription, resolveWriteVersion, writeEffectDescription };
1147
1191
  //# sourceMappingURL=carbonEffectRecords.js.map