@carbonenginejs/runtime-resource 0.12.0 → 0.12.1

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 (170) hide show
  1. package/dist/CjsResMan.js +4 -4
  2. package/dist/CjsResMan.js.map +1 -1
  3. package/dist/_virtual/_rollupPluginBabelHelpers.js +1 -4
  4. package/dist/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
  5. package/dist/format/CjsByteReader.js +310 -0
  6. package/dist/format/CjsByteReader.js.map +1 -0
  7. package/dist/format/CjsByteWriter.js +242 -0
  8. package/dist/format/CjsByteWriter.js.map +1 -0
  9. package/dist/format/CjsFormatError.js +41 -0
  10. package/dist/format/CjsFormatError.js.map +1 -0
  11. package/dist/format/CjsStringTable.js +268 -0
  12. package/dist/format/CjsStringTable.js.map +1 -0
  13. package/dist/format/carbonEffect/CjsCarbonEffectReader.js +361 -0
  14. package/dist/format/carbonEffect/CjsCarbonEffectReader.js.map +1 -0
  15. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js +373 -0
  16. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js.map +1 -0
  17. package/dist/format/carbonEffect/carbonDescriptionFromPortable.js +372 -0
  18. package/dist/format/carbonEffect/carbonDescriptionFromPortable.js.map +1 -0
  19. package/dist/format/carbonEffect/carbonEffectBackendBlock.js +427 -0
  20. package/dist/format/carbonEffect/carbonEffectBackendBlock.js.map +1 -0
  21. package/dist/format/carbonEffect/carbonEffectRecords.js +955 -0
  22. package/dist/format/carbonEffect/carbonEffectRecords.js.map +1 -0
  23. package/dist/format/compareUtf8.js +36 -0
  24. package/dist/format/compareUtf8.js.map +1 -0
  25. package/dist/format/index.js +11 -0
  26. package/dist/format/index.js.map +1 -0
  27. package/dist/formats/bnk/CjsBnkFormat.js +10 -4
  28. package/dist/formats/bnk/CjsBnkFormat.js.map +1 -1
  29. package/dist/formats/bnk/core/eventAction.js +305 -0
  30. package/dist/formats/bnk/core/eventAction.js.map +1 -0
  31. package/dist/formats/bnk/core/helpers.js +11 -2
  32. package/dist/formats/bnk/core/helpers.js.map +1 -1
  33. package/dist/formats/bnk/core/nodeBase.js +532 -0
  34. package/dist/formats/bnk/core/nodeBase.js.map +1 -0
  35. package/dist/formats/bnk/core/sfxNodes.js +252 -152
  36. package/dist/formats/bnk/core/sfxNodes.js.map +1 -1
  37. package/dist/formats/hlsl/core/HlslReader.js +7 -257
  38. package/dist/formats/hlsl/core/HlslReader.js.map +1 -1
  39. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js +781 -0
  40. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js.map +1 -0
  41. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js +21 -1
  42. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js.map +1 -1
  43. package/dist/formats/index.js +1 -1
  44. package/dist/formats/webgl/core/cewg/CewgPackage.js +2 -1
  45. package/dist/formats/webgl/core/cewg/CewgPackage.js.map +1 -1
  46. package/dist/formats/webgl/core/cewg/binary.js +11 -93
  47. package/dist/formats/webgl/core/cewg/binary.js.map +1 -1
  48. package/dist/formats/webgl/core/effectPackage.js +1 -1
  49. package/dist/formats/webgpu/CjsWebgpuFormat.js +4 -23
  50. package/dist/formats/webgpu/CjsWebgpuFormat.js.map +1 -1
  51. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js +197 -0
  52. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js.map +1 -0
  53. package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js +368 -0
  54. package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js.map +1 -0
  55. package/dist/formats/webgpu/core/cewgpu/containerViews.js +355 -0
  56. package/dist/formats/webgpu/core/cewgpu/containerViews.js.map +1 -0
  57. package/dist/formats/webgpu/core/cewgpu/validateContainer.js +90 -0
  58. package/dist/formats/webgpu/core/cewgpu/validateContainer.js.map +1 -0
  59. package/dist/formats/webgpu/core/effectBackendBodySet.js +21 -20
  60. package/dist/formats/webgpu/core/effectBackendBodySet.js.map +1 -1
  61. package/dist/formats/webgpu/core/helpers.js +87 -92
  62. package/dist/formats/webgpu/core/helpers.js.map +1 -1
  63. package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js +14 -13
  64. package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js.map +1 -1
  65. package/dist/formats/webgpu/core/ir/inferValueTypes.js +10 -8
  66. package/dist/formats/webgpu/core/ir/inferValueTypes.js.map +1 -1
  67. package/dist/formats/webgpu/core/packageEffect.js +94 -9
  68. package/dist/formats/webgpu/core/packageEffect.js.map +1 -1
  69. package/dist/formats/webgpu/core/packageMetadata.js +10 -1
  70. package/dist/formats/webgpu/core/packageMetadata.js.map +1 -1
  71. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js +17 -15
  72. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js.map +1 -1
  73. package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js +13 -12
  74. package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js.map +1 -1
  75. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js +9 -8
  76. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js.map +1 -1
  77. package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js +23 -22
  78. package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js.map +1 -1
  79. package/dist/formats/webgpu/core/wgsl/selectionPlans.js +46 -44
  80. package/dist/formats/webgpu/core/wgsl/selectionPlans.js.map +1 -1
  81. package/dist/resource/CjsResource.js +700 -684
  82. package/dist/resource/CjsResource.js.map +1 -1
  83. package/dist/resource/Tr2LightProfileRes.js +18 -27
  84. package/dist/resource/Tr2LightProfileRes.js.map +1 -1
  85. package/dist/resource/audio/CjsAudioBufferRes.js +2 -2
  86. package/dist/resource/audio/CjsAudioBufferRes.js.map +1 -1
  87. package/dist/resource/audio/CjsAudioRes.js +2 -2
  88. package/dist/resource/audio/CjsAudioRes.js.map +1 -1
  89. package/dist/resource/geometry/TriGeometryRes.js +605 -586
  90. package/dist/resource/geometry/TriGeometryRes.js.map +1 -1
  91. package/dist/resource/geometry/granny/Tr2GrannyStateRes.js +21 -30
  92. package/dist/resource/geometry/granny/Tr2GrannyStateRes.js.map +1 -1
  93. package/dist/resource/geometry/granny/TriGrannyRes.js +21 -30
  94. package/dist/resource/geometry/granny/TriGrannyRes.js.map +1 -1
  95. package/dist/resource/shader/Tr2EffectRes.js +9 -7
  96. package/dist/resource/shader/Tr2EffectRes.js.map +1 -1
  97. package/dist/resource/shader/Tr2MaterialArea.js +5 -3
  98. package/dist/resource/shader/Tr2MaterialArea.js.map +1 -1
  99. package/dist/resource/shader/Tr2MaterialMesh.js +4 -2
  100. package/dist/resource/shader/Tr2MaterialMesh.js.map +1 -1
  101. package/dist/resource/shader/Tr2MaterialRes.js +5 -3
  102. package/dist/resource/shader/Tr2MaterialRes.js.map +1 -1
  103. package/dist/resource/shader/Tr2Shader.js +26 -21
  104. package/dist/resource/shader/Tr2Shader.js.map +1 -1
  105. package/dist/resource/shader/Tr2ShaderPermutation.js +8 -6
  106. package/dist/resource/shader/Tr2ShaderPermutation.js.map +1 -1
  107. package/dist/resource/shader/reflection/Tr2EffectDefine.js +5 -3
  108. package/dist/resource/shader/reflection/Tr2EffectDefine.js.map +1 -1
  109. package/dist/resource/shader/reflection/Tr2EffectDescription.js +5 -3
  110. package/dist/resource/shader/reflection/Tr2EffectDescription.js.map +1 -1
  111. package/dist/resource/shader/reflection/Tr2EffectLibrary.js +17 -15
  112. package/dist/resource/shader/reflection/Tr2EffectLibrary.js.map +1 -1
  113. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js +11 -9
  114. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js.map +1 -1
  115. package/dist/resource/shader/reflection/Tr2EffectResource.js +8 -6
  116. package/dist/resource/shader/reflection/Tr2EffectResource.js.map +1 -1
  117. package/dist/resource/shader/reflection/Tr2EffectStageInput.js +15 -13
  118. package/dist/resource/shader/reflection/Tr2EffectStageInput.js.map +1 -1
  119. package/dist/resource/shader/reflection/Tr2EffectTechnique.js +7 -5
  120. package/dist/resource/shader/reflection/Tr2EffectTechnique.js.map +1 -1
  121. package/dist/resource/shader/reflection/Tr2Pass.js +11 -9
  122. package/dist/resource/shader/reflection/Tr2Pass.js.map +1 -1
  123. package/dist/resource/shader/sampler/Tr2SamplerSetup.js +8 -6
  124. package/dist/resource/shader/sampler/Tr2SamplerSetup.js.map +1 -1
  125. package/dist/resource/texture/CjsTextureArrayRes.js +400 -411
  126. package/dist/resource/texture/CjsTextureArrayRes.js.map +1 -1
  127. package/dist/resource/texture/Tr2ImageRes.js +99 -95
  128. package/dist/resource/texture/Tr2ImageRes.js.map +1 -1
  129. package/dist/resource/texture/TriTextureRes.js +312 -273
  130. package/dist/resource/texture/TriTextureRes.js.map +1 -1
  131. package/docs/README.md +13 -1
  132. package/docs/architecture.md +3 -3
  133. package/docs/concepts/resource-lifecycle.md +9 -2
  134. package/docs/concepts/shader-resource-model.md +114 -0
  135. package/docs/concepts/writing-an-engine-adapter.md +115 -0
  136. package/docs/formats/README.md +24 -1
  137. package/docs/formats/carbon-effect-container.md +452 -0
  138. package/docs/formats/dxbc/reference/classes/README.md +5 -72
  139. package/docs/formats/hlsl/reference/api.md +5 -4
  140. package/docs/formats/hlsl/reference/classes/README.md +6 -11
  141. package/docs/formats/provenance.md +23 -13
  142. package/docs/formats/webgl/reference/classes/README.md +5 -92
  143. package/docs/formats/webgpu/README.md +19 -16
  144. package/docs/formats/webgpu/architecture.md +15 -12
  145. package/docs/formats/webgpu/formats/cewgpu.md +175 -438
  146. package/docs/formats/webgpu/guides/effect-packaging.md +132 -140
  147. package/docs/formats/webgpu/reference/api.md +131 -131
  148. package/docs/formats/webgpu/reference/classes/README.md +5 -62
  149. package/docs/formats/webgpu/reference/wgsl-compatibility.md +146 -45
  150. package/docs/formats/wwise.md +34 -8
  151. package/docs/reference/classes/core.md +80 -0
  152. package/docs/reference/classes/formats.md +18 -28
  153. package/docs/reference/events.md +25 -0
  154. package/docs/reference/motherlode-cache.md +22 -8
  155. package/docs/reference/workers.md +5 -5
  156. package/docs/roadmap.md +61 -41
  157. package/package.json +2 -1
  158. package/dist/formats/webgpu/core/cewgpu/CewgpuPackage.js +0 -415
  159. package/dist/formats/webgpu/core/cewgpu/CewgpuPackage.js.map +0 -1
  160. package/dist/formats/webgpu/core/cewgpu/CewgpuPackageBuilder.js +0 -100
  161. package/dist/formats/webgpu/core/cewgpu/CewgpuPackageBuilder.js.map +0 -1
  162. package/dist/formats/webgpu/core/cewgpu/binary.js +0 -93
  163. package/dist/formats/webgpu/core/cewgpu/binary.js.map +0 -1
  164. package/dist/formats/webgpu/core/cewgpu/tags.js +0 -17
  165. package/dist/formats/webgpu/core/cewgpu/tags.js.map +0 -1
  166. package/dist/formats/webgpu/core/effectPackageValidation.js +0 -1078
  167. package/dist/formats/webgpu/core/effectPackageValidation.js.map +0 -1
  168. package/docs/formats/hlsl/reference/classes/carbon-compatibility.md +0 -66
  169. package/docs/formats/hlsl/reference/classes/public-api.md +0 -26
  170. package/docs/formats/hlsl/reference/classes/tr2-effect-model.md +0 -150
@@ -0,0 +1,452 @@
1
+ # Carbon compiled-effect container
2
+
3
+ Status: Stable
4
+ Visibility: Public
5
+ Scope: `@carbonenginejs/runtime-resource` — `src/format/carbonEffect/`, `src/format/CjsByteReader.js`, `src/format/CjsByteWriter.js`, `src/format/CjsStringTable.js`
6
+ Audience: Anyone reading or writing compiled shader effect bytes, or extending `.cewgpu`
7
+ Summary: The v15 binary layout of Carbon's compiled effect files, the shared byte reader and writer that implement it, and the arena offset policy a byte-exact re-emit depends on.
8
+
9
+ ## What this is
10
+
11
+ Carbon's shader compiler emits one file per effect containing **every permutation**,
12
+ selected at read time through an offset table. The format has three parts: a header,
13
+ a deduplicated blob arena ("string table"), one dense offset-table row per
14
+ permutation, and one stored description blob per distinct encoded body.
15
+
16
+ This package implements it as a shared reader and writer, verified byte-exact against
17
+ CCP's own shipped files. It is the wire foundation for `.cewgpu`. The `.cewg`
18
+ WebGL package retains its separate `CEWG` magic and chunk layout.
19
+
20
+ | module | role |
21
+ |---|---|
22
+ | `src/format/CjsByteReader.js` | little-endian cursor plus arena resolution primitives; shared by the HLSL, Carbon-effect, and WebGL readers |
23
+ | `src/format/CjsByteWriter.js` | growable append cursor with reserve-and-patch |
24
+ | `src/format/CjsStringTable.js` | the blob arena, with Carbon's bytewise-sorted offset assignment |
25
+ | `src/format/carbonEffect/carbonEffectRecords.js` | the v15 description-blob record codec |
26
+ | `src/format/carbonEffect/CjsCarbonEffectReader.js` | container reader and structural checks |
27
+ | `src/format/carbonEffect/CjsCarbonEffectWriter.js` | container writer, offset arithmetic and alias dedupe |
28
+
29
+ ## Why v15 only
30
+
31
+ The reader and writer accept and emit version 15 and nothing else.
32
+
33
+ Carbon's own reader accepts versions 2 through 15, but its v13/v14 branches mark
34
+ the field-order boundaries as uncertain. Version 15 is the version with an
35
+ authoritative writer to check against, and the entire audited shipped corpus at
36
+ build 3444265 is v15 — 3222 files across `effect.dx11` and `effect.dx12`, plus
37
+ the same 537 shaders again under `effect.metal`. Nothing older appears in that
38
+ audited corpus.
39
+
40
+ The v15 body is byte-identical to v14. Version 15 differs from 14 only by the 36
41
+ extra header bytes: the compiler version and the source hash.
42
+
43
+ ## Layout
44
+
45
+ ### Header
46
+
47
+ ```
48
+ u32 version = 15
49
+ u8[4] shaderCompilerVersion {major, minor, patch, tweak}
50
+ char[32] sourceHash ASCII hex MD5 of the HLSL source inputs
51
+ u32 stringTableSize | arena payload
52
+ u8 permutationCount | permutation records
53
+ u32 recordCount | recordCount x { u32 index, u32 offset, u32 size }
54
+ description blobs
55
+ ```
56
+
57
+ The compiler writes this order and the runtime reads the same order.
58
+
59
+ The compiler version is **four bytes, not a `u32`**, and Carbon's rebuild check
60
+ compares only the first three. A shipped v15 header reads `01 02 06 00` —
61
+ compiler 1.2.6.0, matching the ShaderCompiler project version. As a `u32` those
62
+ bytes are `0x00060201`, which means nothing. `HlslEffectRes` historically read
63
+ the field as a dword; it now also exposes `m_compilerVersionBytes`, which is the
64
+ truthful reading and what new code should use. The dword form survives only
65
+ because it is republished as `source.compilerVersion` in the portable
66
+ reflection, where it is asserted to be an unsigned integer and covered by a
67
+ package digest.
68
+
69
+ The compiler's rebuild check reads the 32-byte hash, while the runtime skips it.
70
+ It is provenance, not integrity.
71
+
72
+ A permutation record is:
73
+
74
+ ```
75
+ u32 nameOffset | u8 defaultOption | u32 descriptionOffset | u8 type | u8 optionCount | u32 optionOffset[optionCount]
76
+ ```
77
+
78
+ Note the field order: `defaultOption` sits between the name and the description.
79
+ Carbon writes that byte inside a conditional loop with no `else`, so a
80
+ permutation whose declared default matches no option would emit a record one
81
+ byte short and desynchronise the entire rest of the header. Our writer always
82
+ emits it.
83
+
84
+ ### Body-offset arithmetic
85
+
86
+ ```
87
+ base = 4 + 4 + 32 + headerSize + stringTable.GetSize()
88
+ headerSize = (recordCount * 3 + 1) * 4 + permutationBytes
89
+ permutationBytes = 1 + Σ (11 + optionCount * 4)
90
+ ```
91
+
92
+ `GetSize()` **includes** the arena's own `u32` length prefix, so the prefix is
93
+ counted exactly once. Row offsets are absolute from byte 0 of the file.
94
+ `CjsCarbonEffectWriter` asserts that the bytes it actually wrote before the
95
+ first body equal this computed base, so an arithmetic error fails loudly rather
96
+ than shifting every body.
97
+
98
+ ### The arena
99
+
100
+ `CjsStringTable` is Carbon's `StringTable`. Three properties matter:
101
+
102
+ 1. **Offsets are assigned by a bytewise sort, not by insertion order.**
103
+ The comparison is `memcmp` over the shorter length, then shorter-wins on a
104
+ tie, before cumulative offsets are assigned. Any writer that assigns
105
+ first-seen offsets produces a valid file that is not byte-identical to
106
+ Carbon's.
107
+ 2. **Dedupe is on exact bytes with no suffix merging.** `"red"` gets its own
108
+ entry even though it is a suffix of `"shared"`.
109
+ 3. **There are two kinds of entry and no manifest.** A NUL-terminated string is
110
+ added with its terminator and referenced by a bare `u32` offset. A sized blob
111
+ — shader bytecode, program source, default constant values — is added with
112
+ exactly its own bytes and referenced by a `{u32 size, u32 offset}` pair. The
113
+ arena writes a `u32` payload size and the payload, nothing else; every
114
+ reference site resolves its own entry.
115
+
116
+ ## Two rules for anything added later
117
+
118
+ Neither is visible from Carbon's own code, and both were found by implementing
119
+ rather than by reading. They constrain every future addition to this format.
120
+
121
+ **Rule 1: every sized record must parse to exactly its declared end.** Trailing
122
+ bytes mean one of two things and both are fatal — the writer knew fields this reader
123
+ does not, or the writer miscounted. Enforced for the description blob
124
+ (`readEffectDescription`), for the per-pass backend block (`readBackendBlock`), and
125
+ for the header, whose end must equal where the body region begins.
126
+
127
+ This rule carries weight that used to live elsewhere. The chunk container it replaces
128
+ spent roughly 600 lines asserting that its several projections of one effect still
129
+ agreed with each other, and those checks caught a malformed *tree* — our writer
130
+ emitting something structurally wrong — not only a malformed file. A record layout
131
+ makes most of that question unaskable, because containment replaces reference and
132
+ position replaces key. What remains is this: a writer bug either fails to parse,
133
+ which announces itself, or it parses and leaves the cursor somewhere other than the
134
+ declared end. Applying the rule to some sized records and not others is a gap that
135
+ stays invisible until a writer bug hides in one of the others.
136
+
137
+ **Rule 2: anything placed in the arena must be arena-independent.** An arena entry
138
+ cannot contain an arena offset. Offsets are assigned by the content sort, the sort
139
+ depends on every entry's bytes, so an entry that referred to the arena would have to
140
+ be interned before its own contents could be computed — a circular dependency with
141
+ no fixed point. This is invisible in Carbon's own code because no Carbon arena blob
142
+ refers to the arena: strings, bytecode and default constant values are all leaves.
143
+ Our per-pass backend block is the first non-leaf candidate, and it is why that block
144
+ carries inline length-prefixed strings instead of references. A test pins the
145
+ property directly — the block's bytes must be identical whichever arena it is
146
+ interned into. Any future arena entry must satisfy the same rule.
147
+
148
+ ### One field the container cannot round-trip
149
+
150
+ For a **non-dynamic sampler, the name is not preserved.** The file stores one, but
151
+ Carbon's reader nulls it before any producer sees it, so a package built from
152
+ our reflection carries the empty string.
153
+
154
+ This is a property of the input, not a bug in the mapping: the name is unrecoverable
155
+ by the time we receive the data, rather than dropped on the way out. Carbon nulls it
156
+ precisely because a non-dynamic sampler is never looked up by name — `FindSamplerByName`
157
+ only matters for the dynamic case. Recorded here because it will otherwise be
158
+ rediscovered as a bug: a diff against the source effect will always show it.
159
+
160
+ **Corollary: the container admits all six of Carbon's stage types.** `stages` is
161
+ capped at `SHADER_TYPE_COUNT` = 6, and the stage-type byte uses Carbon's
162
+ `InputStageType` numbering: vertex, pixel, compute, geometry, hull, domain. A
163
+ backend that can only express three of those rejects the rest in its own layer;
164
+ the container does not narrow on its behalf. The Carbon region is
165
+ backend-invariant, and restrictions belong to the backend.
166
+
167
+ `0xffffffff` is the null reference. It is legal at **exactly one wire
168
+ position**: a stage's default-constant-value offset when the accompanying size
169
+ is zero, which the optional-value reader consumes without dereferencing.
170
+ Everywhere else a `0xffffffff` offset fails the load.
171
+
172
+ Two deliberate departures from Carbon, both of which make byte-identical output
173
+ more likely rather than less:
174
+
175
+ - **`m_size` is initialised.** Carbon's constructor leaves it indeterminate and
176
+ gets away with it only because the one instance is a zero-initialised global.
177
+ - **Adding after an offset has been handed out is an error.** In Carbon,
178
+ `GetOffset` re-sorts a dirty table, which reassigns *every* offset — including
179
+ offsets already baked into packed bodies. Carbon avoids the corruption by
180
+ interning all late strings before the packing pass. `CjsCarbonEffectWriter`
181
+ reproduces that discipline structurally: it runs the record walk twice, once
182
+ with `collectArena` to intern and once with `internArena` to emit. Because both
183
+ passes drive the same `writeEffectDescription`, they cannot drift apart.
184
+
185
+ ### Description blob, v15 field order
186
+
187
+ Derived independently from the writer's save order and the reader's load order,
188
+ then confirmed to agree field for field. Counts are `u8` unless marked.
189
+
190
+ ```
191
+ u8 techniqueCount
192
+ u32 name
193
+ u8 passCount cap 64
194
+ u8 stageCount cap 6 (SHADER_TYPE_COUNT)
195
+ u8 stageType
196
+ u32 shaderSize | u32 shaderDataOffset program payload, arena blob
197
+ u32 threadGroupSize[3]
198
+ u8 pipelineInputCount cap 64
199
+ u8 usage, registerIndex, usageIndex, usedMask, type, dimension
200
+ -- StageData --
201
+ u8 registerCount
202
+ u8 registerType | u32 registerIndex | u32 registerCount | u8 registerSpace
203
+ u8 staticSamplerCount
204
+ u32 registerIndex | u8 registerSpace | u8 x7 filters/address
205
+ f32 mipLODBias | u8 maxAnisotropy | u8 comparisonFunc
206
+ u8 borderColor enum, NOT four floats
207
+ f32 minLOD | f32 maxLOD
208
+ u32 constantCount u32, not u8
209
+ u32 name | u32 offset | u32 size | u8 type | u8 dimension
210
+ u32 elements | u8 isSRGB | u8 isAutoregister
211
+ u32 defaultValuesSize | u32 defaultValuesOffset 0xffffffff legal when size 0
212
+ u8 textureCount cap 64
213
+ u8 registerIndex | u32 name | u8 type | u32 count | u8 isSRGB | u8 isAutoregister
214
+ u8 samplerCount cap 64
215
+ u8 registerIndex | u32 name | u8 x7 | f32 mipLODBias | u8 maxAnisotropy
216
+ u8 comparisonFunc | f32 borderColor[4] | f32 minLOD | f32 maxLOD | u8 isDynamic
217
+ u8 uavCount cap 64
218
+ u8 registerIndex | u32 name | u8 type | u32 count | u8 isAutoregister
219
+ u8 annotationCount
220
+ u32 name | u8 type | (u32 stringOffset if type == STRING else 4 raw bytes)
221
+ u8 renderStateCount cap 64
222
+ u32 state | u32 value
223
+ u8 libraryCount
224
+ u32 payloadSize | u32 shaderSize | u32 shaderDataOffset
225
+ u32 exportCount u32, not u8
226
+ u8 type | u32 name
227
+ u32 hitGroupName
228
+ StageData globalInputs
229
+ StageData localInputs
230
+ u16 parameterCount cap 256
231
+ u32 name | annotation map as above
232
+ ```
233
+
234
+ Four places this is easy to get wrong:
235
+
236
+ - **At v15 the program payload comes first and the signature tables follow.**
237
+ `pipelineInputs` and `registers` sit *after* `shaderCode` and `threadGroupSize`.
238
+ Before v14 it was the other way round; v14 moved them, and Carbon marks its own
239
+ v14 branch as uncertain.
240
+ - **A UAV record is one byte shorter than a texture record** — it has no `isSRGB`.
241
+ Carbon's reader hardcodes `isSRGB = false` and the writer omits it. Sharing one
242
+ "resource" codec between the two silently corrupts every subsequent field.
243
+ - **`borderColor` is four floats on a sampler and one byte on a static sampler**
244
+ because the two records mirror different D3D binding models.
245
+ - **A non-string annotation value is four raw bytes.** Carbon writes it through the
246
+ `float` member of a `{float,int32_t}` union and reads it back through a different
247
+ union. The bytes round-trip; applying an int/float conversion does not. The codec
248
+ keeps `rawValue` as bytes for exactly this reason.
249
+
250
+ Carbon writes `textures`, `samplers`, `uavs` and render states in ascending key
251
+ order and sorts annotation keys by bytewise string comparison.
252
+ `compareAnnotationNames` implements that comparison over UTF-8 bytes, which is
253
+ *not* the same as JavaScript's UTF-16 code-unit order for names outside ASCII
254
+ — `"Z"` sorts before `"a"`.
255
+
256
+ ### The optional trailing block
257
+
258
+ Our own containers add exactly one optional block per pass, after the render-state
259
+ table, referenced by a `{u32 size, u32 offset}` pair into the arena. A Carbon file
260
+ ends the pass at the render states, so the reader and writer gate it on
261
+ `{ backend: true }` and produce Carbon's bytes unchanged when it is closed.
262
+
263
+ The block carries the two sections that are not derivable from Carbon reflection —
264
+ WebGPU bind-group layouts and resource transforms — in **one** unit, because they
265
+ are mutually required and because "the Carbon region is backend-invariant, with
266
+ exactly one optional trailing block" is the invariant worth keeping.
267
+
268
+ It lives in the arena so identical layouts dedupe across bodies the way program
269
+ source does; measured sharing is 30.5:1 at `(body, pass)` granularity, 22 distinct
270
+ blocks across 672 pairs. That forces one property: **the block contains no arena
271
+ offsets.** An offset is only known after the arena's content sort, which depends on
272
+ every blob's bytes including this one, so a block referencing the arena could not be
273
+ built before it was interned. Strings inside it are inline and length-prefixed.
274
+
275
+ ```
276
+ u8 blobVersion = 1
277
+ u8 bindGroupCount
278
+ u8 group | u8 bindingCount
279
+ u8 resourceKind | u8 registerSpace | u8 binding | u8 visibilityMask
280
+ u32 registerIndex | u32 structureStride (0xffffffff absent) | u8 arrayLayerCount (0 absent)
281
+ str type | str generatedSymbol | str transformId (empty = none)
282
+ u8 transformCount
283
+ u8 familyCode | str id | u8 inputCount
284
+ u8 registerSpace | u8 registerIndex | str parameter
285
+ ```
286
+
287
+ `identity` and `group` on each binding, and a transform's `kind`,
288
+ `stage`, `representation`, `missingLayer`, `viewDimension`, `layerCount`,
289
+ `output.identity`, `output.scopeIdentity`, `output.name`, `layoutKey` and every
290
+ input's `layer` are restored on read, not stored. The family byte is what keeps them
291
+ derivable without pinning the format to one recognizer. `id` and each input's
292
+ `parameter` stay on the wire deliberately — `id` because a caller may supply it,
293
+ `parameter` because it keeps layer identity cross-checkable rather than asserted by
294
+ position.
295
+
296
+ Backend-block version 1 stores visibility but not the original
297
+ `scopeIdentity`. The reader reconstructs `${identity}@${visibility[0]}`. A
298
+ multi-stage shared binding therefore rereads as stage-qualified rather than
299
+ recovering its original bare scope. Callers must not infer that original
300
+ sharing decision from the wire view.
301
+
302
+ An unknown `blobVersion` reports the pass as having no backend data rather than
303
+ misparsing it; the enclosing size makes it skippable.
304
+
305
+ ### Count caps
306
+
307
+ `CARBON_EFFECT_COUNT_CAPS` mirrors the runtime's inclusive limits. Carbon's
308
+ compiler enforces none of them while its runtime rejects anything above them,
309
+ so an over-large effect compiles and then fails to load; our writer checks on
310
+ the way out. The caps Carbon does *not* have — techniques, registers, static
311
+ samplers, constants, libraries, exports, annotation counts — are deliberately
312
+ not invented here.
313
+
314
+ ### The alias path
315
+
316
+ Carbon compares packed bodies pairwise and points a duplicate's row at the
317
+ surviving twin. The row is **kept**, so the offset table stays dense while the
318
+ file stores each distinct body once. Across the shipped corpus 22% of files
319
+ alias, at roughly 2.1 rows per distinct body.
320
+
321
+ ## Offset-table density
322
+
323
+ Carbon indexes the offset table **positionally** and never reads each row's
324
+ stored `index` field. A sparse or misordered table therefore does not fail — it
325
+ silently returns the wrong shader body.
326
+
327
+ Density is incidental in Carbon: it falls out of `g_compiledEffects` being a
328
+ `std::map` densely keyed by the work-queue builder, and is promised nowhere.
329
+
330
+ Measured twice at build 3444265. A header-only sweep of every `.sm_hi`, `.sm_lo`
331
+ and `.sm_depth` under `effect.dx11` and `effect.dx12` — 3222 files, 52,332 rows —
332
+ and the full round-trip run below across all three backends — 4833 files, 78,498
333
+ rows, 40,645 distinct bodies. **Every file is dense and positionally indexed**, and
334
+ every row's byte range lies inside the file and clear of the header. In every
335
+ single file the body region tiles the post-header space exactly, with no leading or
336
+ trailing slack.
337
+
338
+ Given that, the checks are implemented as follows:
339
+
340
+ - `CjsCarbonEffectReader` **always** collects `dense` and `indicesMatchPosition` as
341
+ diagnostics, and **always** fails closed on an out-of-range row.
342
+ - Density and positional indexing **fail closed on read by default**.
343
+ `{ permissive: true }` skips the check and leaves the diagnostics in place, for
344
+ forensic inspection of a file already known to be malformed. It is not a load
345
+ option.
346
+ - `writeCarbonEffectFile` **always** fails closed: it refuses to emit bodies that
347
+ are not dense from index 0. Where we own the bytes there is no reason to be
348
+ lenient.
349
+
350
+ `--ignore-permutations` does make CCP's compiler emit only key 0 while declaring
351
+ every axis, so a sparse file is producible. That argues for the escape hatch, not
352
+ for permissive defaults: Carbon does not reject such a file, it returns the wrong
353
+ permutation's shader silently, which is the failure class this port exists to
354
+ close.
355
+
356
+ ## Backend selection and envelope removal
357
+
358
+ **Historical compatibility.** Flat-package builds once prepended
359
+ `magic | containerVersion | payloadKind` before Carbon's byte-compatible
360
+ layout. Current packaging emits no such prefix and the former helper is no
361
+ longer part of the package.
362
+
363
+ **Current WebGPU wire.** CEWGPU bytes have no envelope, magic, `payloadKind`,
364
+ or independent container version. They are bare Carbon v15 records resolved
365
+ from `effect.webgpu/`, with one optional per-pass backend block. `.cewg`
366
+ remains a separate CEWG chunk format rather than this Carbon-record wire.
367
+
368
+ `CewgpuContainer` reads the optional blocks, and the shared record reader can
369
+ auto-detect them from a description's declared size. Direct conversion from
370
+ those descriptions into the portable reflection envelope consumed by
371
+ `Tr2EffectRes` remains an adapter boundary.
372
+
373
+ Versioning remains local to what it versions: Carbon's version dword governs
374
+ the Carbon region, while `blobVersion` governs the optional backend block. An
375
+ unknown block version is skipped rather than misparsed. The package does not
376
+ claim a version in CCP's namespace.
377
+
378
+ Loose program bytes without a resource path can be identified from their
379
+ payload: DXBC opens with `"DXBC"`, AIR is bitcode (`BC 0xC0DE`), and WGSL and
380
+ GLSL have distinct text syntax. The stage record itself carries no language
381
+ tag; program interpretation remains a backend/path responsibility.
382
+
383
+ ## Verification
384
+
385
+ `node --test` in this package. Two gates.
386
+
387
+ **Always green.** `test/format/byte-primitives.test.js` and
388
+ `test/format/carbon-effect.test.js` build a synthetic four-permutation v15
389
+ container exercising every record type — static samplers, UAVs, annotations of
390
+ every value type, render states, a raytracing library with both stage-data blocks
391
+ — and assert a byte-exact write → read → write round trip, the arena sort order,
392
+ the caps, the structural checks and, until the switchover, the legacy envelope's
393
+ disjointness.
394
+
395
+ **Env-gated real-file proof.** `test/format/carbon-effect-corpus.test.js`, enabled
396
+ with `CARBON_EFFECT_CORPUS_DIR`. Game bytes are never committed. Supply a
397
+ separately acquired corpus at pinned build 3444265. The test re-emits each file
398
+ three ways:
399
+
400
+ 1. every description blob through the file's own arena — proves the field order;
401
+ 2. the whole container from raw bodies and the source arena — proves the header
402
+ order, the base arithmetic and the alias path;
403
+ 3. the whole container with the arena rebuilt from the references found — proves
404
+ the sorted-offset policy.
405
+
406
+ Only the third can legitimately differ, because an arena may retain blobs the file
407
+ no longer references. When it does differ the divergence is reported exactly and
408
+ asserted to be unreferenced-blob retention; it is never downgraded to a weaker
409
+ comparison such as "same strings, any order", which would look green and prove
410
+ nothing.
411
+
412
+ Measured result over the complete corpus — 4833 files (537 shaders × 3 variants ×
413
+ 3 backends), 78,498 offset-table rows, 40,645 distinct description bodies:
414
+ **all three modes byte-exact, with zero arena-rebuild divergences, zero sparse
415
+ tables and zero misordered tables.** Not one shipped file retains an unreferenced
416
+ arena blob, so the sorted-offset policy reproduces CCP's arena exactly.
417
+ Supply a separately acquired corpus and set `CARBON_EFFECT_CORPUS_DIR`; no
418
+ corpus data ships with the package.
419
+
420
+ That result is the container port's central evidence. The same reader and the same
421
+ writer reproduce, byte for byte, files whose program payloads are DXBC in two
422
+ dialects and AIR — with no language field anywhere in the format. The metadata
423
+ region is backend-invariant as a measured fact rather than an argument from the
424
+ writer. Backend selection therefore belongs at the resource-path boundary, not
425
+ in an envelope or per-stage record.
426
+
427
+ `effect.gles2` is deliberately not a validation target for **this package**: those
428
+ shaders are v8, and nothing in the container port reads or writes them.
429
+
430
+ **Do not read that as "obsolete".** `effect.gles2` is the shader tree ccpwgl
431
+ actually renders with today — it is the only one that currently works end to end.
432
+
433
+ The two statements coexist because **v15-only constrains what we write and
434
+ validate against, not what a reader may accept.** Version-branching is the
435
+ format's own mechanism. A reader that wants all supported generations branches
436
+ on the version dword — v2..8 legacy gles2, v15 everything current — which is one
437
+ reader, not a bespoke path per format.
438
+
439
+ **Our containers are v15, not a version of our own.** A "v16" was considered for
440
+ the variant carrying the per-pass backend block and **rejected**: CCP owns that
441
+ number space, so claiming 16 would collide with any real v16 they ship, in the one
442
+ field whose entire job is telling a reader how to parse. It also failed the rule
443
+ the rest of this format is held to — invent something only because it *has to*
444
+ exist, never because we think it should.
445
+
446
+ The container needs no new version. Each description blob carries a declared
447
+ size in the offset table, and [Rule 1](#two-rules-for-anything-added-later)
448
+ already requires it to parse to exactly that end. A reader parses a blob without
449
+ blocks and re-parses with them if the cursor misses the declared end, so the
450
+ presence of the block is **self-describing** with no new field, no container
451
+ version and no out-of-band flag. `blobVersion` inside the block versions the
452
+ extension itself.
@@ -1,76 +1,9 @@
1
- # Class catalog
1
+ # DXBC class catalog
2
2
 
3
- Status: Evolving
3
+ Status: Deprecated
4
4
  Scope: `@carbonenginejs/runtime-resource/formats/dxbc` maintained classes
5
5
  Audience: Users, maintainers, and automated readers
6
- Summary: Provides one-sentence purpose descriptors for every maintained class in the DXBC format package.
6
+ Summary: Points to the canonical package-wide class catalog after format-package consolidation.
7
7
 
8
- <!-- class:CjsDxbcFormat -->
9
- ## `CjsDxbcFormat`
10
-
11
- CarbonEngineJS-facing DXBC (Direct3D shader bytecode) reader.
12
-
13
- - Export: `@carbonenginejs/runtime-resource/formats/dxbc`
14
- - Source: `src/CjsDxbcFormat.js`
15
- - Visibility: Public
16
- - Kind: CarbonEngineJS
17
-
18
- <!-- class:CjsBinaryReader -->
19
- ## `CjsBinaryReader`
20
-
21
- Little-endian binary reader with optional shared string-table references.
22
-
23
- - Export: None
24
- - Source: `src/carbon/CjsBinaryReader.js`
25
- - Visibility: Internal
26
- - Kind: Internal implementation
27
-
28
- <!-- class:DxbcContainer -->
29
- ## `DxbcContainer`
30
-
31
- DirectX shader bytecode container reader.
32
-
33
- - Export: None
34
- - Source: `src/core/container.js`
35
- - Visibility: Internal
36
- - Kind: Internal implementation
37
-
38
- <!-- class:DxbcInstructionDecoder -->
39
- ## `DxbcInstructionDecoder`
40
-
41
- SM4/SM5 instruction-stream decoder over a `DxbcShaderProgram` token array.
42
-
43
- - Export: None
44
- - Source: `src/core/decoder.js`
45
- - Visibility: Internal
46
- - Kind: Internal implementation
47
-
48
- <!-- class:DxbcReadError -->
49
- ## `DxbcReadError`
50
-
51
- Error raised when DirectX shader bytecode cannot be decoded safely.
52
-
53
- - Export: None
54
- - Source: `src/core/errors.js`
55
- - Visibility: Internal
56
- - Kind: Internal implementation
57
-
58
- <!-- class:DxbcShaderProgram -->
59
- ## `DxbcShaderProgram`
60
-
61
- DXBC shader program chunk reader for `SHEX`/`SHDR` token streams.
62
-
63
- - Export: None
64
- - Source: `src/core/program.js`
65
- - Visibility: Internal
66
- - Kind: Internal implementation
67
-
68
- <!-- class:DxbcSignatureChunk -->
69
- ## `DxbcSignatureChunk`
70
-
71
- DXBC input/output signature chunk reader for `ISGN`-family chunks.
72
-
73
- - Export: None
74
- - Source: `src/core/signature.js`
75
- - Visibility: Internal
76
- - Kind: Internal implementation
8
+ The maintained DXBC entries now live in the
9
+ [package-wide formats class catalog](../../../../reference/classes/formats.md#dxbc).
@@ -76,13 +76,14 @@ The `json` and `metadata` modes are the supported data contracts described in
76
76
  `Tr2EffectRes` objects, not the canonical runtime-resource class, and may
77
77
  change without a major version bump.
78
78
 
79
- ## CLI
79
+ ## Repository metadata adapter
80
80
 
81
- The package installs a Node CLI that writes metadata JSON:
81
+ The repository provides a development script that writes metadata JSON. It is
82
+ not installed as a package `bin`:
82
83
 
83
84
  ```sh
84
- format-hlsl metadata effect.sm_hi
85
- format-hlsl metadata effect.sm_hi effect.json
85
+ npm run metadata:hlsl -- effect.sm_hi
86
+ npm run metadata:hlsl -- effect.sm_hi effect.json
86
87
  ```
87
88
 
88
89
  When the output path is omitted, the CLI writes `<input-name>.json` in the
@@ -1,16 +1,11 @@
1
1
  # Class catalog
2
2
 
3
- Status: Evolving
3
+ Status: Deprecated
4
4
  Scope: `@carbonenginejs/runtime-resource/formats/hlsl` maintained classes
5
5
  Audience: Users, maintainers, and automated readers
6
- Summary: Indexes the one-sentence purpose descriptors for every maintained class in the package.
6
+ Summary: Points to the canonical package-wide catalogs after format-package consolidation.
7
7
 
8
- ## Catalog pages
9
-
10
- - [Public API classes](public-api.md)
11
- - [Carbon compatibility classes](carbon-compatibility.md)
12
- - [Tr2 effect-model classes](tr2-effect-model.md)
13
-
14
- Public availability and stability are separate concerns. In particular,
15
- `Tr2EffectBindingManifest` is exported for advanced tooling but remains an
16
- experimental surface.
8
+ The maintained HLSL format entries now live in the
9
+ [package-wide formats class catalog](../../../../reference/classes/formats.md#hlsl).
10
+ Canonical runtime effect-resource and shader-reflection entries live in the
11
+ [resources class catalog](../../../../reference/classes/resources.md).
@@ -6,8 +6,10 @@ Audience: Users, integrators, and maintainers
6
6
  Summary: Records where each format implementation came from, retained snapshots and digests, and what was deliberately not copied.
7
7
 
8
8
  On 2026-07-13, the non-shader runtime format implementations below were copied
9
- once into `runtime-resource`. Their standalone repositories remain frozen with
10
- their existing APIs and names; they are not upstreams for the runtime copies.
9
+ once into `runtime-resource`. The table records the donor revisions or
10
+ working-tree states used for that migration; it does not assert that a
11
+ standalone repository still exists. Any surviving standalone repository is
12
+ historical provenance, not an upstream for the runtime copy.
11
13
 
12
14
  Copied paths were `src/` and the behavioral `test/` corpus. Package publishing
13
15
  scripts and CLIs were not copied. Exact donor license and notice files are kept
@@ -61,18 +63,24 @@ notice files are kept under `format-notices/gr2/`.
61
63
  ## Black definition snapshot
62
64
 
63
65
  The Black reader uses the package-owned generated definition snapshot at
64
- `src/formats/black/core/black-schema-v1-2026-07-11.json`. The Red format exposes
66
+ `src/formats/black/core/black-schema-v1-2026-07-23.json`. The Red format exposes
65
67
  the same catalog for discovery, but its YAML reader currently accepts named
66
- fields without registry enforcement. The snapshot was copied from
67
- `format-carbon` revision `d2a3c67cf3d46e8ba78ca19e66558d868178ec24`
68
- with SHA-256
69
- `008ECB29E670EFC678B471A6EFF099600A29C2907912FC42B854995904604691`.
68
+ fields without registry enforcement. The refreshed consumer snapshot landed
69
+ in runtime-resource revision `6beda70` with SHA-256
70
+ `9234DD86DBB5EB3676EF060E383ADBCE8F073941E76A32865D3528DD5DA7B737`.
71
+ Its exact tools-core generator revision was not recorded and remains unknown;
72
+ this page does not infer one from the consumer commit.
70
73
 
71
74
  This retained generated artifact keeps the published readers deterministic and
72
75
  browser-safe without a runtime dependency on a sibling checkout or an
73
- unpublished `format-carbon` export. `format-carbon` remains the build-time
74
- authority for future schema regeneration; an updated snapshot must record its
75
- new source revision and digest here.
76
+ unpublished generator export. Future schema generation is owned by
77
+ `tools-core/schema`. A consumer of generated output is responsible for
78
+ reviewing and copying the result into its own worktree; an updated snapshot
79
+ must record the generator revision and new digest here.
80
+
81
+ This generator boundary is transitional. As runtime classes gain embedded
82
+ schema, the shared snapshot and its copy-in step can be reduced and eventually
83
+ removed once every required class is self-describing.
76
84
 
77
85
  ## Native additions
78
86
 
@@ -162,9 +170,11 @@ snapshot must record its new source and digest here.
162
170
  - `format-gr2` migrated into `formats/gr2` on 2026-07-24 (see the dated
163
171
  table above) after its EUPL constraint was resolved; its standalone
164
172
  repository is now a frozen legacy distribution like the other donors.
165
- - `format-carbon` remains the schema emitter/generator and build-time schema
166
- authority. Black consumes its published definitions; Red exposes the copied
167
- catalog but does not yet enforce it while reading YAML fields.
173
+ - `format-carbon` is historical provenance for the current copied snapshot,
174
+ not the authority for future regeneration. `tools-core/schema` owns schema
175
+ generation, while each consuming package reviews and copies generated output
176
+ into its own worktree. Black consumes the retained snapshot; Red exposes the
177
+ copied catalog but does not yet enforce it while reading YAML fields.
168
178
  - The shader formats were excluded from the original migration while their
169
179
  implementations were still being finished. That sequencing guard was lifted
170
180
  on 2026-07-29 and they moved in as ordinary formats. All four landed: