@carbonenginejs/runtime-resource 0.13.0 → 0.15.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 (454) hide show
  1. package/LICENSE +21 -21
  2. package/NOTICE +32 -32
  3. package/README.md +129 -129
  4. package/dist/CjsMotherLode.js +1271 -1271
  5. package/dist/CjsResMan.js +3858 -3858
  6. package/dist/CjsResManFetchProvider.js +94 -94
  7. package/dist/CjsResManWorkQueue.js +301 -301
  8. package/dist/_virtual/_rollupPluginBabelHelpers.js +150 -150
  9. package/dist/format/CjsBlueReader.js +358 -358
  10. package/dist/format/CjsByteReader.js +310 -310
  11. package/dist/format/CjsByteWriter.js +242 -242
  12. package/dist/format/CjsFormat.js +223 -223
  13. package/dist/format/CjsFormatError.js +41 -41
  14. package/dist/format/CjsReader.js +22 -22
  15. package/dist/format/CjsResourceProbe.js +279 -279
  16. package/dist/format/CjsStringTable.js +268 -268
  17. package/dist/format/carbonEffect/CjsCarbonEffectReader.js +265 -265
  18. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js +373 -373
  19. package/dist/format/carbonEffect/buildCarbonEffectContainer.js +182 -182
  20. package/dist/format/carbonEffect/carbonEffectBackendBlock.js +321 -321
  21. package/dist/format/carbonEffect/carbonEffectRecords.js +1147 -1147
  22. package/dist/format/carbonEffect/carbonEffectResourceTransform.js +197 -197
  23. package/dist/format/compareUtf8.js +36 -36
  24. package/dist/format/effect/effectBodyInventory.js +146 -146
  25. package/dist/format/effect/effectPermutationGraph.js +257 -257
  26. package/dist/format/effect/sha256.js +114 -114
  27. package/dist/format/index.js +11 -11
  28. package/dist/format/payloadContract.js +193 -193
  29. package/dist/formats/black/CjsBlackFormat.js +310 -310
  30. package/dist/formats/black/core/CjsBlackBinaryReader.js +260 -260
  31. package/dist/formats/black/core/CjsBlackPropertyReaders.js +448 -448
  32. package/dist/formats/black/core/CjsBlackReader.js +764 -764
  33. package/dist/formats/black/core/CjsBlackSchemaRegistry.js +540 -540
  34. package/dist/formats/black/core/black-schema-v1-2026-07-23.json.js +4 -4
  35. package/dist/formats/black/core/blackConstants.js +7 -7
  36. package/dist/formats/black/core/blackDefinitions.js +10 -10
  37. package/dist/formats/black/core/blackEnums.js +6 -6
  38. package/dist/formats/black/core/blackSchema.js +3 -3
  39. package/dist/formats/black/core/blackVersion.js +22 -22
  40. package/dist/formats/black/core/helpers.js +199 -199
  41. package/dist/formats/black/core/schema.js +4 -4
  42. package/dist/formats/black/index.js +2 -2
  43. package/dist/formats/bnk/CjsBnkFormat.js +175 -175
  44. package/dist/formats/bnk/core/busNodes.js +252 -252
  45. package/dist/formats/bnk/core/effectNodes.js +147 -147
  46. package/dist/formats/bnk/core/eventAction.js +416 -416
  47. package/dist/formats/bnk/core/globalSettings.js +215 -215
  48. package/dist/formats/bnk/core/graph.js +137 -137
  49. package/dist/formats/bnk/core/helpers.js +512 -512
  50. package/dist/formats/bnk/core/musicNodes.js +540 -540
  51. package/dist/formats/bnk/core/nodeBase.js +553 -553
  52. package/dist/formats/bnk/core/sfxNodes.js +632 -632
  53. package/dist/formats/bnk/core/soundbanksInfo.js +209 -209
  54. package/dist/formats/bnk/index.js +2 -2
  55. package/dist/formats/cmf/CjsCmfFormat.js +497 -497
  56. package/dist/formats/cmf/core/binary.js +194 -194
  57. package/dist/formats/cmf/core/buffers.js +237 -237
  58. package/dist/formats/cmf/core/constants.js +47 -47
  59. package/dist/formats/cmf/core/gr2Anim.js +453 -453
  60. package/dist/formats/cmf/core/helpers.js +318 -318
  61. package/dist/formats/cmf/core/pack.js +276 -276
  62. package/dist/formats/cmf/core/schema.js +374 -374
  63. package/dist/formats/cmf/core/shared.js +277 -277
  64. package/dist/formats/cmf/core/writer.js +571 -571
  65. package/dist/formats/cmf/index.js +2 -2
  66. package/dist/formats/dds/CjsDdsFormat.js +200 -200
  67. package/dist/formats/dds/core/bc6h.js +298 -298
  68. package/dist/formats/dds/core/bc7.js +272 -272
  69. package/dist/formats/dds/core/helpers.js +901 -862
  70. package/dist/formats/dds/core/helpers.js.map +1 -1
  71. package/dist/formats/dds/index.js +2 -2
  72. package/dist/formats/dxbc/CjsDxbcFormat.js +187 -142
  73. package/dist/formats/dxbc/CjsDxbcFormat.js.map +1 -1
  74. package/dist/formats/dxbc/core/DxbcReader.js +266 -266
  75. package/dist/formats/dxbc/core/container.js +169 -169
  76. package/dist/formats/dxbc/core/decoder.js +789 -789
  77. package/dist/formats/dxbc/core/disassemble.js +240 -0
  78. package/dist/formats/dxbc/core/disassemble.js.map +1 -0
  79. package/dist/formats/dxbc/core/errors.js +19 -19
  80. package/dist/formats/dxbc/core/helpers.js +220 -220
  81. package/dist/formats/dxbc/core/opcodes.js +45 -45
  82. package/dist/formats/dxbc/core/program.js +91 -91
  83. package/dist/formats/dxbc/core/signature.js +172 -172
  84. package/dist/formats/dxbc/index.js +2 -2
  85. package/dist/formats/fbx/CjsFbxFormat.js +266 -266
  86. package/dist/formats/fbx/core/helpers.js +3932 -3932
  87. package/dist/formats/fbx/index.js +2 -2
  88. package/dist/formats/flac/CjsFlacFormat.js +142 -142
  89. package/dist/formats/flac/core/helpers.js +315 -315
  90. package/dist/formats/flac/index.js +2 -2
  91. package/dist/formats/gif/CjsGifFormat.js +141 -141
  92. package/dist/formats/gif/core/helpers.js +380 -380
  93. package/dist/formats/gif/index.js +2 -2
  94. package/dist/formats/gltf/CjsGltfFormat.js +252 -290
  95. package/dist/formats/gltf/CjsGltfFormat.js.map +1 -1
  96. package/dist/formats/gltf/core/helpers.js +287 -307
  97. package/dist/formats/gltf/core/helpers.js.map +1 -1
  98. package/dist/formats/gltf/core/json.js +79 -79
  99. package/dist/formats/gltf/core/parser.js +679 -679
  100. package/dist/formats/gltf/core/targets.js +173 -173
  101. package/dist/formats/gltf/index.js +2 -2
  102. package/dist/formats/gr2/CjsGr2Format.js +289 -289
  103. package/dist/formats/gr2/core/bitknit2.js +282 -282
  104. package/dist/formats/gr2/core/curves.js +1047 -1047
  105. package/dist/formats/gr2/core/gsf.js +72 -72
  106. package/dist/formats/gr2/core/helpers.js +352 -352
  107. package/dist/formats/gr2/core/json.js +622 -622
  108. package/dist/formats/gr2/core/oodle1.js +388 -388
  109. package/dist/formats/gr2/core/tangents.js +48 -48
  110. package/dist/formats/gr2/core/targets.js +361 -361
  111. package/dist/formats/gr2/index.js +2 -2
  112. package/dist/formats/hlsl/CjsHlslFormat.js +233 -254
  113. package/dist/formats/hlsl/CjsHlslFormat.js.map +1 -1
  114. package/dist/formats/hlsl/core/HlslBinaryUtils.js +15 -15
  115. package/dist/formats/hlsl/core/HlslEffectReadError.js +19 -19
  116. package/dist/formats/hlsl/core/HlslEffectStateManager.js +130 -130
  117. package/dist/formats/hlsl/core/HlslRenderStateSetup.js +37 -37
  118. package/dist/formats/hlsl/core/HlslResourceSetDescription.js +94 -94
  119. package/dist/formats/hlsl/core/HlslShaderBytecode.js +44 -44
  120. package/dist/formats/hlsl/core/analysis.js +51 -51
  121. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js +850 -850
  122. package/dist/formats/hlsl/core/detailMapFamily.js +128 -128
  123. package/dist/formats/hlsl/core/helpers.js +270 -270
  124. package/dist/formats/hlsl/core/json.js +284 -284
  125. package/dist/formats/hlsl/core/localLightFamily.js +133 -133
  126. package/dist/formats/hlsl/core/metadata.js +327 -327
  127. package/dist/formats/hlsl/core/render-states.js +280 -280
  128. package/dist/formats/hlsl/core/tr2/HlslRenderContextEnum.js +43 -43
  129. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js +320 -320
  130. package/dist/formats/hlsl/core/tr2/resources/HlslShaderPermutation.js +33 -33
  131. package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js +423 -416
  132. package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js.map +1 -1
  133. package/dist/formats/hlsl/core/tr2/shader/HlslEffectConstant.js +40 -40
  134. package/dist/formats/hlsl/core/tr2/shader/HlslEffectDescription.js +62 -62
  135. package/dist/formats/hlsl/core/tr2/shader/HlslEffectLibrary.js +52 -52
  136. package/dist/formats/hlsl/core/tr2/shader/HlslEffectParameterAnnotation.js +38 -38
  137. package/dist/formats/hlsl/core/tr2/shader/HlslEffectResource.js +50 -50
  138. package/dist/formats/hlsl/core/tr2/shader/HlslEffectStageInput.js +86 -86
  139. package/dist/formats/hlsl/core/tr2/shader/HlslEffectTechnique.js +31 -31
  140. package/dist/formats/hlsl/core/tr2/shader/HlslPass.js +42 -42
  141. package/dist/formats/hlsl/core/tr2/shader/HlslSamplerDescription.js +55 -55
  142. package/dist/formats/hlsl/core/tr2/shader/HlslSamplerSetup.js +29 -29
  143. package/dist/formats/hlsl/core/tr2/shader/HlslShader.js +220 -220
  144. package/dist/formats/hlsl/core/tr2/shader/HlslShaderOption.js +30 -30
  145. package/dist/formats/hlsl/index.js +3 -3
  146. package/dist/formats/index.js +33 -33
  147. package/dist/formats/jpeg/CjsJpegFormat.js +212 -212
  148. package/dist/formats/jpeg/core/helpers.js +402 -402
  149. package/dist/formats/jpeg/core/jpeg.js +480 -480
  150. package/dist/formats/jpeg/index.js +2 -2
  151. package/dist/formats/mp3/CjsMp3Format.js +197 -197
  152. package/dist/formats/mp3/core/helpers.js +375 -375
  153. package/dist/formats/mp3/index.js +2 -2
  154. package/dist/formats/mp4/CjsMp4Format.js +197 -197
  155. package/dist/formats/mp4/core/helpers.js +484 -484
  156. package/dist/formats/mp4/index.js +2 -2
  157. package/dist/formats/obj/CjsObjFormat.js +232 -253
  158. package/dist/formats/obj/CjsObjFormat.js.map +1 -1
  159. package/dist/formats/obj/core/helpers.js +553 -573
  160. package/dist/formats/obj/core/helpers.js.map +1 -1
  161. package/dist/formats/obj/core/json.js +64 -64
  162. package/dist/formats/obj/core/parser.js +321 -321
  163. package/dist/formats/obj/index.js +2 -2
  164. package/dist/formats/ogg/CjsOggFormat.js +143 -143
  165. package/dist/formats/ogg/core/helpers.js +410 -410
  166. package/dist/formats/ogg/core/imdct.js +178 -178
  167. package/dist/formats/ogg/core/vorbis.js +1017 -1017
  168. package/dist/formats/ogg/index.js +2 -2
  169. package/dist/formats/pickle/CjsPickleFormat.js +214 -214
  170. package/dist/formats/pickle/core/CjsPickleProtocol0Reader.js +551 -551
  171. package/dist/formats/pickle/index.js +2 -2
  172. package/dist/formats/png/CjsPngFormat.js +201 -201
  173. package/dist/formats/png/core/helpers.js +635 -635
  174. package/dist/formats/png/index.js +2 -2
  175. package/dist/formats/red/CjsRedFormat.js +263 -263
  176. package/dist/formats/red/core/CjsRedReader.js +246 -246
  177. package/dist/formats/red/core/blackDefinitions.js +3 -3
  178. package/dist/formats/red/core/helpers.js +158 -158
  179. package/dist/formats/red/core/redGraph.js +71 -71
  180. package/dist/formats/red/core/schema.js +4 -4
  181. package/dist/formats/red/index.js +2 -2
  182. package/dist/formats/stl/CjsStlFormat.js +320 -365
  183. package/dist/formats/stl/CjsStlFormat.js.map +1 -1
  184. package/dist/formats/stl/core/helpers.js +241 -261
  185. package/dist/formats/stl/core/helpers.js.map +1 -1
  186. package/dist/formats/stl/core/json.js +51 -51
  187. package/dist/formats/stl/core/stl.js +642 -642
  188. package/dist/formats/stl/core/targets.js +173 -173
  189. package/dist/formats/stl/index.js +2 -2
  190. package/dist/formats/tga/CjsTgaFormat.js +197 -197
  191. package/dist/formats/tga/core/helpers.js +493 -493
  192. package/dist/formats/tga/index.js +2 -2
  193. package/dist/formats/wav/CjsWavFormat.js +198 -198
  194. package/dist/formats/wav/core/helpers.js +365 -365
  195. package/dist/formats/wav/index.js +2 -2
  196. package/dist/formats/webgl/CjsWebglFormat.js +199 -199
  197. package/dist/formats/webgl/core/buildGlslEffectContainer.js +70 -70
  198. package/dist/formats/webgl/core/effectPackage.js +900 -900
  199. package/dist/formats/webgl/core/errors.js +27 -27
  200. package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js +2820 -2820
  201. package/dist/formats/webgl/core/glsl/DxbcGlslHelpers.js +89 -89
  202. package/dist/formats/webgl/core/glsl/DxbcGlslOperandFormatter.js +486 -486
  203. package/dist/formats/webgl/core/glsl/packedLightFixups.js +98 -98
  204. package/dist/formats/webgl/core/glslBackendBlock.js +552 -552
  205. package/dist/formats/webgl/core/glslBackendBodySet.js +243 -243
  206. package/dist/formats/webgl/core/glslEffectCompleteness.js +85 -85
  207. package/dist/formats/webgl/core/glslEffectCompleteness.js.map +1 -1
  208. package/dist/formats/webgl/core/helpers.js +167 -167
  209. package/dist/formats/webgl/core/inspectGlslEffectContainer.js +122 -122
  210. package/dist/formats/webgl/core/readGlslEffectContainer.js +202 -256
  211. package/dist/formats/webgl/core/readGlslEffectContainer.js.map +1 -1
  212. package/dist/formats/webgl/index.js +2 -2
  213. package/dist/formats/webgpu/CjsWebgpuFormat.js +357 -357
  214. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js +89 -89
  215. package/dist/formats/webgpu/core/carbonWebgpu/CarbonWebgpuContainer.js +354 -354
  216. package/dist/formats/webgpu/core/carbonWebgpu/containerViews.js +355 -355
  217. package/dist/formats/webgpu/core/carbonWebgpu/validateContainer.js +90 -90
  218. package/dist/formats/webgpu/core/effectAnalysis.js +82 -82
  219. package/dist/formats/webgpu/core/effectBackendBodySet.js +306 -306
  220. package/dist/formats/webgpu/core/errors.js +20 -20
  221. package/dist/formats/webgpu/core/helpers.js +443 -443
  222. package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js +212 -212
  223. package/dist/formats/webgpu/core/ir/buildControlFlow.js +220 -220
  224. package/dist/formats/webgpu/core/ir/indexableTemps.js +137 -137
  225. package/dist/formats/webgpu/core/ir/inferValueTypes.js +449 -449
  226. package/dist/formats/webgpu/core/ir/lowerDxbcToIr.js +494 -494
  227. package/dist/formats/webgpu/core/ir/resolveRegisterFlow.js +177 -177
  228. package/dist/formats/webgpu/core/ir/sourceLanes.js +61 -61
  229. package/dist/formats/webgpu/core/packageEffect.js +381 -381
  230. package/dist/formats/webgpu/core/packageEffectSelection.js +164 -164
  231. package/dist/formats/webgpu/core/packageMetadata.js +17 -17
  232. package/dist/formats/webgpu/core/schema.js +4 -4
  233. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js +263 -263
  234. package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js +172 -172
  235. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js +325 -325
  236. package/dist/formats/webgpu/core/wgsl/emitWgsl.js +356 -356
  237. package/dist/formats/webgpu/core/wgsl/hoistEscapingValues.js +77 -77
  238. package/dist/formats/webgpu/core/wgsl/lowerBindingLayout.js +451 -451
  239. package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js +735 -735
  240. package/dist/formats/webgpu/core/wgsl/lowerCreateHistogramsComputeProgram.js +457 -457
  241. package/dist/formats/webgpu/core/wgsl/lowerFragmentProgram.js +1572 -1572
  242. package/dist/formats/webgpu/core/wgsl/lowerMergeHistogramsComputeProgram.js +659 -659
  243. package/dist/formats/webgpu/core/wgsl/lowerParticleClearComputePrograms.js +734 -734
  244. package/dist/formats/webgpu/core/wgsl/lowerParticleEmitComputeProgram.js +583 -583
  245. package/dist/formats/webgpu/core/wgsl/lowerSkinVerticesComputeProgram.js +621 -621
  246. package/dist/formats/webgpu/core/wgsl/lowerSortComputeProgram.js +824 -824
  247. package/dist/formats/webgpu/core/wgsl/lowerSortInnerComputeProgram.js +697 -697
  248. package/dist/formats/webgpu/core/wgsl/lowerSortStepComputeProgram.js +559 -559
  249. package/dist/formats/webgpu/core/wgsl/lowerVertexProgram.js +1328 -1328
  250. package/dist/formats/webgpu/core/wgsl/particleEmitSemanticDigest.js +110 -110
  251. package/dist/formats/webgpu/core/wgsl/precisionControls.js +55 -55
  252. package/dist/formats/webgpu/core/wgsl/selectionPlans.js +717 -717
  253. package/dist/formats/webgpu/core/wgsl/uniformity.js +78 -78
  254. package/dist/formats/webgpu/core/wgsl/validateExactComputeIr.js +196 -196
  255. package/dist/formats/webgpu/core/wgsl/validateHandleOperand.js +37 -37
  256. package/dist/formats/webgpu/index.js +2 -2
  257. package/dist/formats/webm/CjsWebmFormat.js +197 -197
  258. package/dist/formats/webm/core/helpers.js +572 -572
  259. package/dist/formats/webm/index.js +2 -2
  260. package/dist/formats/webp/CjsWebpFormat.js +140 -140
  261. package/dist/formats/webp/core/helpers.js +237 -237
  262. package/dist/formats/webp/index.js +2 -2
  263. package/dist/formats/wem/CjsWemFormat.js +250 -250
  264. package/dist/formats/wem/core/bitStream.js +261 -261
  265. package/dist/formats/wem/core/codebookLibrary.js +164 -164
  266. package/dist/formats/wem/core/helpers.js +437 -437
  267. package/dist/formats/wem/core/packedCodebooksAotuv603.js +30 -30
  268. package/dist/formats/wem/core/ptadpcm.js +77 -77
  269. package/dist/formats/wem/core/resolve.js +121 -121
  270. package/dist/formats/wem/core/wemToOgg.js +485 -485
  271. package/dist/formats/wem/index.js +2 -2
  272. package/dist/formats/yaml/CjsYamlFormat.js +134 -134
  273. package/dist/formats/yaml/core/CjsYamlReader.js +400 -400
  274. package/dist/formats/yaml/core/helpers.js +196 -196
  275. package/dist/formats/yaml/index.js +2 -2
  276. package/dist/index.js +64 -64
  277. package/dist/resource/CjsLoadingObject.js +19 -19
  278. package/dist/resource/CjsResource.js +801 -801
  279. package/dist/resource/ResourceHandlerMode.js +14 -14
  280. package/dist/resource/Tr2LightProfileRes.js +32 -32
  281. package/dist/resource/audio/AudioGeometryResData.js +47 -47
  282. package/dist/resource/audio/CjsAudioBufferRes.js +86 -86
  283. package/dist/resource/audio/CjsAudioRes.js +213 -213
  284. package/dist/resource/audio/index.js +4 -4
  285. package/dist/resource/geometry/MeshDecalData.js +37 -37
  286. package/dist/resource/geometry/MeshDecalLodData.js +34 -34
  287. package/dist/resource/geometry/TriGeometryRes.js +570 -467
  288. package/dist/resource/geometry/TriGeometryRes.js.map +1 -1
  289. package/dist/resource/geometry/TriGeometryResAreaData.js +59 -59
  290. package/dist/resource/geometry/TriGeometryResJointData.js +38 -38
  291. package/dist/resource/geometry/TriGeometryResLodData.js +88 -88
  292. package/dist/resource/geometry/TriGeometryResMeshData.js +63 -63
  293. package/dist/resource/geometry/TriGeometryResSkeletonData.js +34 -34
  294. package/dist/resource/geometry/TriJointBinding.js +38 -38
  295. package/dist/resource/geometry/TriMorphTargetGeometryConstants.js +46 -46
  296. package/dist/resource/geometry/TriRtGeometryConstants.js +88 -88
  297. package/dist/resource/geometry/granny/GStateBindingCallbackData.js +31 -31
  298. package/dist/resource/geometry/granny/Tr2GrannyIntersectionResult.js +60 -60
  299. package/dist/resource/geometry/granny/Tr2GrannyStateRes.js +36 -36
  300. package/dist/resource/geometry/granny/TriGrannyRes.js +35 -35
  301. package/dist/resource/geometry/granny/enums.js +10 -10
  302. package/dist/resource/geometry/granny/index.js +6 -6
  303. package/dist/resource/geometry/index.js +17 -17
  304. package/dist/resource/index.js +54 -54
  305. package/dist/resource/resourceBoundary.js +64 -64
  306. package/dist/resource/shader/Tr2EffectRes.js +336 -336
  307. package/dist/resource/shader/Tr2MaterialArea.js +31 -31
  308. package/dist/resource/shader/Tr2MaterialMesh.js +27 -27
  309. package/dist/resource/shader/Tr2MaterialRes.js +31 -31
  310. package/dist/resource/shader/Tr2Shader.js +283 -283
  311. package/dist/resource/shader/Tr2ShaderPermutation.js +43 -43
  312. package/dist/resource/shader/index.js +17 -17
  313. package/dist/resource/shader/reflection/Tr2EffectConstant.js +143 -143
  314. package/dist/resource/shader/reflection/Tr2EffectDefine.js +30 -30
  315. package/dist/resource/shader/reflection/Tr2EffectDescription.js +114 -114
  316. package/dist/resource/shader/reflection/Tr2EffectLibrary.js +168 -168
  317. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js +125 -125
  318. package/dist/resource/shader/reflection/Tr2EffectResource.js +120 -120
  319. package/dist/resource/shader/reflection/Tr2EffectStageInput.js +372 -372
  320. package/dist/resource/shader/reflection/Tr2EffectTechnique.js +78 -78
  321. package/dist/resource/shader/reflection/Tr2Pass.js +168 -168
  322. package/dist/resource/shader/reflection/carbonRecordFields.js +159 -159
  323. package/dist/resource/shader/reflection/shaderStage.js +22 -22
  324. package/dist/resource/shader/sampler/Tr2SamplerSetup.js +135 -135
  325. package/dist/resource/texture/CjsTextureArrayRes.js +472 -472
  326. package/dist/resource/texture/CjsTextureArrayResParameterProxy.js +179 -179
  327. package/dist/resource/texture/Tr2ImageRes.js +120 -120
  328. package/dist/resource/texture/Tr2TextureLodManager.js +82 -82
  329. package/dist/resource/texture/Tr2TextureLodUpdateRequest.js +37 -37
  330. package/dist/resource/texture/Tr2TexturePackChannel.js +37 -37
  331. package/dist/resource/texture/Tr2TexturePipeline.js +54 -54
  332. package/dist/resource/texture/Tr2TexturePipelineParams.js +34 -34
  333. package/dist/resource/texture/Tr2TexturePipelineStepCompress.js +40 -40
  334. package/dist/resource/texture/Tr2TexturePipelineStepGenerateMips.js +22 -22
  335. package/dist/resource/texture/Tr2TexturePipelineStepLimitSize.js +34 -34
  336. package/dist/resource/texture/Tr2TexturePipelineStepLoad.js +31 -31
  337. package/dist/resource/texture/Tr2TexturePipelineStepPack.js +43 -43
  338. package/dist/resource/texture/TriTextureRes.js +359 -359
  339. package/dist/resource/texture/index.js +15 -15
  340. package/dist/resource/texture/texturePipelineBehavior.js +308 -308
  341. package/dist/worker/CjsResManMainThreadLoader.js +89 -89
  342. package/dist/worker/CjsResManWorker.js +218 -218
  343. package/dist/worker/CjsResManWorkerLoader.js +437 -437
  344. package/dist/worker/protocol.js +12 -12
  345. package/docs/README.md +98 -98
  346. package/docs/architecture.md +118 -118
  347. package/docs/concepts/resource-lifecycle.md +226 -226
  348. package/docs/concepts/shader-resource-model.md +111 -111
  349. package/docs/concepts/writing-an-engine-adapter.md +115 -115
  350. package/docs/formats/README.md +138 -138
  351. package/docs/formats/carbon-effect-container.md +553 -553
  352. package/docs/formats/dxbc/README.md +68 -68
  353. package/docs/formats/dxbc/architecture.md +80 -80
  354. package/docs/formats/dxbc/reference/api.md +105 -77
  355. package/docs/formats/dxbc/reference/classes/README.md +9 -9
  356. package/docs/formats/dxbc/reference/decoded-output.md +122 -122
  357. package/docs/formats/gr2.md +160 -160
  358. package/docs/formats/hlsl/README.md +54 -54
  359. package/docs/formats/hlsl/architecture.md +66 -65
  360. package/docs/formats/hlsl/guides/hydrating-json-output.md +60 -60
  361. package/docs/formats/hlsl/guides/reading-effects.md +68 -64
  362. package/docs/formats/hlsl/reference/advanced-analysis.md +61 -61
  363. package/docs/formats/hlsl/reference/api.md +91 -92
  364. package/docs/formats/hlsl/reference/classes/README.md +11 -11
  365. package/docs/formats/hlsl/reference/json-graph.md +97 -97
  366. package/docs/formats/pickle.md +82 -82
  367. package/docs/formats/provenance.md +196 -196
  368. package/docs/formats/stl.md +37 -37
  369. package/docs/formats/webgl/README.md +115 -115
  370. package/docs/formats/webgl/architecture.md +69 -69
  371. package/docs/formats/webgl/carbon-constant-layouts.md +326 -326
  372. package/docs/formats/webgl/decl-io.md +1234 -1234
  373. package/docs/formats/webgl/memory-structured.md +890 -890
  374. package/docs/formats/webgl/reference/classes/README.md +9 -9
  375. package/docs/formats/webgl/texture-sample.md +964 -964
  376. package/docs/formats/webgpu/README.md +84 -84
  377. package/docs/formats/webgpu/architecture.md +95 -95
  378. package/docs/formats/webgpu/formats/carbon-webgpu.md +215 -215
  379. package/docs/formats/webgpu/guides/effect-packaging.md +189 -189
  380. package/docs/formats/webgpu/reference/api.md +196 -196
  381. package/docs/formats/webgpu/reference/classes/README.md +9 -9
  382. package/docs/formats/webgpu/reference/wgsl-compatibility.md +1546 -1546
  383. package/docs/formats/wwise.md +146 -146
  384. package/docs/reference/classes/README.md +35 -35
  385. package/docs/reference/classes/audio.md +30 -30
  386. package/docs/reference/classes/core.md +216 -216
  387. package/docs/reference/classes/dropped.md +46 -46
  388. package/docs/reference/classes/formats.md +944 -944
  389. package/docs/reference/classes/resources.md +456 -456
  390. package/docs/reference/classes/texture.md +26 -26
  391. package/docs/reference/events.md +117 -117
  392. package/docs/reference/motherlode-cache.md +275 -275
  393. package/docs/reference/queues.md +194 -194
  394. package/docs/reference/reload.md +107 -107
  395. package/docs/reference/texture-arrays.md +113 -113
  396. package/docs/reference/texture-pipeline.md +53 -53
  397. package/docs/reference/workers.md +142 -142
  398. package/docs/roadmap.md +150 -150
  399. package/format-notices/black/LICENSE +21 -21
  400. package/format-notices/black/NOTICE +47 -47
  401. package/format-notices/bnk/LICENSE +21 -21
  402. package/format-notices/bnk/NOTICE +21 -21
  403. package/format-notices/cmf/LICENSE +21 -21
  404. package/format-notices/cmf/NOTICE +36 -36
  405. package/format-notices/dds/LICENSE +21 -21
  406. package/format-notices/dds/NOTICE +14 -14
  407. package/format-notices/dxbc/LICENSE +21 -21
  408. package/format-notices/dxbc/NOTICE +20 -20
  409. package/format-notices/fbx/LICENSE +21 -21
  410. package/format-notices/fbx/NOTICE +14 -14
  411. package/format-notices/flac/LICENSE +21 -21
  412. package/format-notices/flac/NOTICE +14 -14
  413. package/format-notices/gif/LICENSE +21 -21
  414. package/format-notices/gif/NOTICE +14 -14
  415. package/format-notices/gltf/LICENSE +21 -21
  416. package/format-notices/gltf/NOTICE +27 -27
  417. package/format-notices/gr2/LICENSE +21 -21
  418. package/format-notices/gr2/NOTICE +60 -60
  419. package/format-notices/gr2/THIRD-PARTY-NOTICES.md +93 -93
  420. package/format-notices/hlsl/LICENSE +21 -21
  421. package/format-notices/hlsl/NOTICE +25 -25
  422. package/format-notices/jpeg/LICENSE +21 -21
  423. package/format-notices/jpeg/NOTICE +14 -14
  424. package/format-notices/mp3/LICENSE +21 -21
  425. package/format-notices/mp3/NOTICE +14 -14
  426. package/format-notices/mp4/LICENSE +21 -21
  427. package/format-notices/mp4/NOTICE +14 -14
  428. package/format-notices/obj/LICENSE +21 -21
  429. package/format-notices/obj/NOTICE +26 -26
  430. package/format-notices/ogg/LICENSE +21 -21
  431. package/format-notices/ogg/NOTICE +28 -28
  432. package/format-notices/png/LICENSE +21 -21
  433. package/format-notices/png/NOTICE +14 -14
  434. package/format-notices/red/LICENSE +21 -21
  435. package/format-notices/red/NOTICE +31 -31
  436. package/format-notices/stl/LICENSE +21 -21
  437. package/format-notices/stl/NOTICE +21 -21
  438. package/format-notices/tga/LICENSE +21 -21
  439. package/format-notices/tga/NOTICE +14 -14
  440. package/format-notices/wav/LICENSE +21 -21
  441. package/format-notices/wav/NOTICE +14 -14
  442. package/format-notices/webgl/LICENSE +21 -21
  443. package/format-notices/webgl/NOTICE +35 -35
  444. package/format-notices/webgpu/LICENSE +21 -21
  445. package/format-notices/webgpu/NOTICE +31 -31
  446. package/format-notices/webm/LICENSE +21 -21
  447. package/format-notices/webm/NOTICE +14 -14
  448. package/format-notices/webp/LICENSE +21 -21
  449. package/format-notices/webp/NOTICE +14 -14
  450. package/format-notices/wem/LICENSE +57 -57
  451. package/format-notices/wem/NOTICE +33 -33
  452. package/format-notices/yaml/LICENSE +21 -21
  453. package/format-notices/yaml/NOTICE +44 -44
  454. package/package.json +63 -63
@@ -1,1147 +1,1147 @@
1
- import { CjsFormatWriteError } from '../CjsFormatError.js';
2
- import { compareUtf8 } from '../compareUtf8.js';
3
-
4
- // Source: trinity/shadercompiler/EffectData.h (Save methods)
5
- // Source: trinity/trinity/Shader/Tr2EffectDescription.cpp (Read)
6
-
7
-
8
- /**
9
- * The current description-blob version, and the only one this module writes.
10
- *
11
- * `DATA_VERSION` in `EffectData.h:10`. Carbon's reader accepts 2..15
12
- * (`Tr2EffectRes.cpp:209`) but annotates the v13/v14 field-order boundaries as
13
- * unverified — `// CHECK IS IT IN RIGHT FUNCTION?` at
14
- * `Tr2EffectDescription.cpp:177`, `// CHECK` at `:257` and `:578`. The v15
15
- * layout is the one with an authoritative writer to check against, it is the
16
- * only version the shipped corpus contains, and it is byte-identical to v14 in
17
- * the body — v15 differs from v14 only by the 36 outer header bytes.
18
- */
19
- const CARBON_EFFECT_DATA_VERSION = 15;
20
-
21
- /**
22
- * The oldest description-blob version this module reads.
23
- *
24
- * Carbon threads the version through parsing rather than gating on it twice,
25
- * and its branches reach back to version 2. Maintainer decision, 2026-08-02:
26
- * **8 is the lowest version worth reading** — the branches below 8 have no
27
- * examined file, no writer, and no consumer, so restoring them would be code
28
- * with no evidence. Reading a version is not supporting it: the reader's job
29
- * ends at a correct record tree, normalized to the v15 record shape.
30
- */
31
- const CARBON_EFFECT_MIN_DATA_VERSION = 8;
32
-
33
- /**
34
- * Annotation type codes (`EffectData.h:35-41`). Only `STRING` changes the wire
35
- * shape of the value that follows.
36
- */
37
- const CARBON_ANNOTATION_TYPE = Object.freeze({
38
- BOOL: 0,
39
- INT: 1,
40
- FLOAT: 2,
41
- STRING: 3
42
- });
43
-
44
- /**
45
- * Carbon's inclusive count limits, from the `SanityCheck` call sites in
46
- * `Tr2EffectDescription.cpp:28-36`. `SanityCheck` throws only when
47
- * `value > limit`, so each number is the largest accepted count.
48
- *
49
- * The absent entries are absent in Carbon too: `techniques`, `registers`,
50
- * `staticSamplers`, `constants`, `libraries`, `exports` and the per-annotation
51
- * counts are read with no limit at all, guarded only by the end-of-buffer
52
- * check. We do not invent caps Carbon does not have.
53
- */
54
- const CARBON_EFFECT_COUNT_CAPS = Object.freeze({
55
- pipelineInputs: 64,
56
- passes: 64,
57
- stages: 6,
58
- textures: 64,
59
- samplers: 64,
60
- uavs: 64,
61
- renderStates: 64,
62
- effectAnnotations: 256
63
- });
64
-
65
- /**
66
- * `SHADER_CONSTANTS_MAX` (`Tr2EffectDescription.h:160`) — Carbon clamps a
67
- * stage's default-constant-value copy to this without rejecting the file, while
68
- * still advancing the cursor by the untruncated size.
69
- */
70
- const CARBON_SHADER_CONSTANTS_MAX = 4096;
71
-
72
- /**
73
- * Compares two annotation names the way Carbon sorts annotation keys before
74
- * writing them (`strcmp`, `EffectData.h:613-616` and `:839-842`).
75
- *
76
- * `strcmp` orders by unsigned byte, which for names outside ASCII is not the
77
- * same as JavaScript's UTF-16 code-unit order — hence the explicit UTF-8
78
- * comparison.
79
- *
80
- * @param {string} a First name.
81
- * @param {string} b Second name.
82
- * @returns {number} Negative, zero, or positive ordering result.
83
- */
84
- function compareAnnotationNames(a, b) {
85
- return compareUtf8(a, b);
86
- }
87
-
88
- /**
89
- * Resolver that writes each arena reference back at the offset it was read
90
- * from, reusing the source arena verbatim.
91
- *
92
- * This is what proves field order independently of arena policy: a re-emit
93
- * through this resolver must reproduce the original description bytes exactly,
94
- * whatever the arena happens to contain.
95
- */
96
- const passthroughArena = Object.freeze({
97
- /**
98
- * Returns a parsed string reference's original offset.
99
- *
100
- * @param {{offset:number}} reference Parsed string reference.
101
- * @returns {number} Original arena offset.
102
- */
103
- string(reference) {
104
- return reference.offset;
105
- },
106
- /**
107
- * Returns a parsed blob reference's original offset.
108
- *
109
- * @param {{offset:number}} reference Parsed blob reference.
110
- * @returns {number} Original arena offset.
111
- */
112
- blob(reference) {
113
- return reference.offset;
114
- }
115
- });
116
-
117
- /**
118
- * Creates a resolver that re-interns every reference into a fresh string table,
119
- * so the emitted arena is rebuilt under Carbon's sorted-offset policy.
120
- *
121
- * @param {import("../CjsStringTable.js").CjsStringTable} table Target arena.
122
- * @returns {{string:Function, blob:Function}} Arena resolver.
123
- */
124
- function internArena(table) {
125
- return {
126
- /**
127
- * Interns a string reference's text and returns its offset.
128
- *
129
- * @param {{value:string}} reference Parsed string reference.
130
- * @returns {number} Arena offset.
131
- */
132
- string(reference) {
133
- return table.offsetOf(table.addString(reference.value));
134
- },
135
- /**
136
- * Interns a blob reference's bytes and returns its offset. A zero-size
137
- * blob keeps its original reference — Carbon leaves the offset word
138
- * unset in that case, and the reader never dereferences it.
139
- *
140
- * @param {{size:number, offset:number, bytes:Uint8Array}} reference Parsed blob reference.
141
- * @returns {number} Arena offset.
142
- */
143
- blob(reference) {
144
- if (reference.size === 0) return reference.offset;
145
- return table.offsetOf(table.addBytes(reference.bytes));
146
- }
147
- };
148
- }
149
-
150
- /**
151
- * Creates a resolver that only interns, emitting a placeholder offset.
152
- *
153
- * Carbon resolves an arena offset while packing a body
154
- * (`PackedStream::Save(StringReference)`, `EffectData.h:187-193`), by which
155
- * point every string must already be in the table — a late `AddString` marks
156
- * the table dirty and the next `GetOffset` re-sorts it, silently invalidating
157
- * every offset already baked into a packed body. Carbon dodges this by
158
- * interning all late strings up front (`ShaderCompiler.cpp:686-694`, before the
159
- * packing loop at `:697-714`).
160
- *
161
- * Running the write walk twice — once collecting, once emitting — reproduces
162
- * that discipline without depending on a caller remembering it, and because
163
- * both passes traverse the same code they cannot drift apart.
164
- *
165
- * @param {import("../CjsStringTable.js").CjsStringTable} table Target arena.
166
- * @returns {{string:Function, blob:Function}} Collect-only resolver.
167
- */
168
- function collectArena(table) {
169
- return {
170
- /**
171
- * Interns a string reference's text.
172
- *
173
- * @param {{value:string}} reference Parsed string reference.
174
- * @returns {number} Placeholder offset.
175
- */
176
- string(reference) {
177
- table.addString(reference.value);
178
- return 0;
179
- },
180
- /**
181
- * Interns a blob reference's bytes, skipping zero-size blobs.
182
- *
183
- * @param {{size:number, bytes:Uint8Array}} reference Parsed blob reference.
184
- * @returns {number} Placeholder offset.
185
- */
186
- blob(reference) {
187
- if (reference.size !== 0) table.addBytes(reference.bytes);
188
- return 0;
189
- }
190
- };
191
- }
192
-
193
- /**
194
- * Reads a bare `u32` arena offset and the NUL-terminated string behind it.
195
- *
196
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
197
- * @returns {{offset:number, value:string}} String reference.
198
- */
199
- function readStringRef(reader) {
200
- const offset = reader.readUint32();
201
- return {
202
- offset,
203
- value: reader.readStringAt(offset)
204
- };
205
- }
206
-
207
- /**
208
- * Reads a `{u32 size, u32 offset}` blob reference.
209
- *
210
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
211
- * @returns {{size:number, offset:number, bytes:Uint8Array}} Blob reference.
212
- */
213
- function readBlobRef(reader) {
214
- const size = reader.readUint32();
215
- const blob = reader.readTableBlobOptional(size);
216
- return {
217
- size,
218
- offset: blob.offset,
219
- bytes: blob.bytes
220
- };
221
- }
222
-
223
- /**
224
- * Writes a `{u32 size, u32 offset}` blob reference.
225
- *
226
- * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
227
- * @param {object} arena Arena resolver.
228
- * @param {{size:number}} reference Blob reference.
229
- */
230
- function writeBlobRef(writer, arena, reference) {
231
- writer.u32(reference.size);
232
- writer.u32(arena.blob(reference));
233
- }
234
-
235
- /**
236
- * An absent sized blob: zero length, Carbon's null offset. The offset word is
237
- * still written, and a reader must not dereference it.
238
- */
239
- const EMPTY_BLOB = Object.freeze({
240
- size: 0,
241
- offset: 0xffffffff,
242
- bytes: new Uint8Array(0)
243
- });
244
-
245
- /**
246
- * Error factory used by the write path's cap checks.
247
- *
248
- * @param {string} message Failure reason.
249
- * @param {object} details Structured details.
250
- * @returns {CjsFormatWriteError} Write error.
251
- */
252
- function writeError(message, details) {
253
- return new CjsFormatWriteError(message, details);
254
- }
255
-
256
- /**
257
- * Requires that a sized record parsed to exactly its declared end.
258
- *
259
- * This is the universal form of the rule the backend block discovered, and it
260
- * carries real weight: it is the successor to roughly 600 lines of cross-chunk
261
- * agreement checks that the container rewrite deletes. Those checks caught a
262
- * malformed *tree* — our writer emitting something structurally wrong — not only a
263
- * malformed file. Under a record layout a writer bug either fails to parse, which
264
- * is self-announcing, or it parses and leaves the cursor somewhere other than the
265
- * declared end. Trailing bytes therefore mean one of two things, both fatal: the
266
- * writer knew fields this reader does not, or the writer miscounted.
267
- *
268
- * Applying it to some sized records and not others is a gap that stays invisible
269
- * until a writer bug hides in one of the others, so every sized record gets it.
270
- *
271
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Reader at the record end.
272
- * @param {string} what Record name for the error message.
273
- * @param {Function} makeError Error factory.
274
- */
275
- function requireExhaustive(reader, what, makeError) {
276
- if (reader.remaining !== 0) {
277
- throw makeError(`Carbon effect ${what} has ${reader.remaining} unparsed trailing byte(s)`, {
278
- trailingBytes: reader.remaining,
279
- offset: reader.offset,
280
- end: reader.end
281
- });
282
- }
283
- }
284
-
285
- /**
286
- * Rejects a count above Carbon's inclusive limit for that field.
287
- *
288
- * @param {number} value Count read or about to be written.
289
- * @param {string} field Cap key in `CARBON_EFFECT_COUNT_CAPS`.
290
- * @param {Function} makeError Error factory taking a message and details.
291
- * @returns {number} The count.
292
- */
293
- function sanityCheck(value, field, makeError) {
294
- const limit = CARBON_EFFECT_COUNT_CAPS[field];
295
- if (limit !== undefined && value > limit) {
296
- throw makeError(`Effect ${field} count ${value} exceeds Carbon's limit of ${limit}`, {
297
- field,
298
- value,
299
- limit
300
- });
301
- }
302
- return value;
303
- }
304
-
305
- /**
306
- * Reads one annotation map: `u8 count`, then name/type/value triples
307
- * (`ParameterAnnotation::Save` `EffectData.h:601-623`, `ReadAnnotations`
308
- * `Tr2EffectDescription.cpp:114-134`).
309
- *
310
- * A non-string value is kept as its raw four bytes. Carbon writes it through
311
- * the `float` member of a `{float,int32_t}` union and reads it back through a
312
- * different union, so the bytes are the only faithful representation; applying
313
- * an int/float conversion here would corrupt round-trips.
314
- *
315
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
316
- * @returns {object[]} Annotation records.
317
- */
318
- function readAnnotations(reader) {
319
- const count = reader.readUint8();
320
- const annotations = [];
321
- for (let index = 0; index < count; index += 1) {
322
- const name = readStringRef(reader);
323
- const type = reader.readUint8();
324
- if (type === CARBON_ANNOTATION_TYPE.STRING) {
325
- annotations.push({
326
- name,
327
- type,
328
- stringValue: readStringRef(reader),
329
- rawValue: null
330
- });
331
- } else {
332
- annotations.push({
333
- name,
334
- type,
335
- stringValue: null,
336
- rawValue: Uint8Array.from(reader.readRaw(4))
337
- });
338
- }
339
- }
340
- return annotations;
341
- }
342
-
343
- /**
344
- * Writes one annotation map.
345
- *
346
- * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
347
- * @param {object} arena Arena resolver.
348
- * @param {object[]} annotations Annotation records.
349
- */
350
- function writeAnnotations(writer, arena, annotations) {
351
- writer.u8(annotations.length);
352
- for (const annotation of annotations) {
353
- writer.u32(arena.string(annotation.name));
354
- writer.u8(annotation.type);
355
- if (annotation.type === CARBON_ANNOTATION_TYPE.STRING) {
356
- writer.u32(arena.string(annotation.stringValue));
357
- } else {
358
- writer.bytes(annotation.rawValue);
359
- }
360
- }
361
- }
362
-
363
- /**
364
- * Maps a pre-v11 constant type byte onto the current enum
365
- * (`Tr2EffectDescription.cpp:141-158`). The old wire knew FLOAT/INT/BOOL;
366
- * the current numbering is FLOAT 0, INT 1, UINT 2, BOOL 3, OTHER 4.
367
- *
368
- * @param {number} value Pre-v11 type byte.
369
- * @returns {number} Current constant type value.
370
- */
371
- function mapOldConstantType(value) {
372
- if (value === 0) return 0;
373
- if (value === 1) return 1;
374
- if (value === 2) return 3;
375
- return 4;
376
- }
377
-
378
- /**
379
- * Reads one `Constant` (`EffectData.h:283-294`, `ReadConstant`
380
- * `Tr2EffectDescription.cpp:136-170`). The byte layout is identical across
381
- * versions 8..15; only the type byte's numbering changed at v11.
382
- *
383
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
384
- * @param {number} version Container data version.
385
- * @returns {object} Constant record.
386
- */
387
- function readConstant(reader, version) {
388
- const name = readStringRef(reader);
389
- const offset = reader.readUint32();
390
- const size = reader.readUint32();
391
- const rawType = reader.readUint8();
392
- return {
393
- name,
394
- offset,
395
- size,
396
- type: version < 11 ? mapOldConstantType(rawType) : rawType,
397
- dimension: reader.readUint8(),
398
- elements: reader.readUint32(),
399
- isSRGB: reader.readUint8(),
400
- isAutoregister: reader.readUint8()
401
- };
402
- }
403
-
404
- /**
405
- * Writes one `Constant`.
406
- *
407
- * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
408
- * @param {object} arena Arena resolver.
409
- * @param {object} constant Constant record.
410
- */
411
- function writeConstant(writer, arena, constant) {
412
- writer.u32(arena.string(constant.name));
413
- writer.u32(constant.offset);
414
- writer.u32(constant.size);
415
- writer.u8(constant.type);
416
- writer.u8(constant.dimension);
417
- writer.u32(constant.elements);
418
- writer.u8(constant.isSRGB);
419
- writer.u8(constant.isAutoregister);
420
- }
421
-
422
- /**
423
- * Reads one `Texture` (`EffectData.h:320-328`, `ReadResource`
424
- * `Tr2EffectDescription.cpp:172-190`). `count` is the reader's
425
- * `arrayElements`; before v13 the field is not on the wire and Carbon
426
- * defaults it to one element.
427
- *
428
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
429
- * @param {number} version Container data version.
430
- * @returns {object} Texture record.
431
- */
432
- function readTexture(reader, version) {
433
- return {
434
- name: readStringRef(reader),
435
- type: reader.readUint8(),
436
- count: version >= 13 ? reader.readUint32() : 1,
437
- isSRGB: reader.readUint8(),
438
- isAutoregister: reader.readUint8()
439
- };
440
- }
441
-
442
- /**
443
- * Writes one `Texture`.
444
- *
445
- * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
446
- * @param {object} arena Arena resolver.
447
- * @param {object} texture Texture record.
448
- */
449
- function writeTexture(writer, arena, texture) {
450
- writer.u32(arena.string(texture.name));
451
- writer.u8(texture.type);
452
- writer.u32(texture.count);
453
- writer.u8(texture.isSRGB);
454
- writer.u8(texture.isAutoregister);
455
- }
456
-
457
- /**
458
- * Reads one `Uav` (`EffectData.h:345-352`, inline at
459
- * `Tr2EffectDescription.cpp:449-463`).
460
- *
461
- * A UAV record is one byte shorter than a texture record: it carries no
462
- * `isSRGB`. Carbon's reader hardcodes `isSRGB = false`
463
- * (`Tr2EffectDescription.cpp:450`) rather than reading it, and `Uav::Save`
464
- * omits it. Reusing a shared "resource" codec for both is the mistake this
465
- * comment exists to prevent.
466
- *
467
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
468
- * @param {number} version Container data version.
469
- * @returns {object} UAV record.
470
- */
471
- function readUav(reader, version) {
472
- return {
473
- name: readStringRef(reader),
474
- type: reader.readUint8(),
475
- count: version >= 13 ? reader.readUint32() : 1,
476
- isAutoregister: reader.readUint8()
477
- };
478
- }
479
-
480
- /**
481
- * Writes one `Uav`.
482
- *
483
- * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
484
- * @param {object} arena Arena resolver.
485
- * @param {object} uav UAV record.
486
- */
487
- function writeUav(writer, arena, uav) {
488
- writer.u32(arena.string(uav.name));
489
- writer.u8(uav.type);
490
- writer.u32(uav.count);
491
- writer.u8(uav.isAutoregister);
492
- }
493
-
494
- /**
495
- * Reads one `Sampler` (`EffectData.h:368-390`, `ReadSampler`
496
- * `Tr2EffectDescription.cpp:192-232` plus the name at `:413` and `isDynamic`
497
- * at `:429`). Border colour here is four floats — unlike a static sampler,
498
- * where it is a one-byte enum.
499
- *
500
- * `isDynamic` joined the wire at v13. For older versions the key is OMITTED
501
- * from the record rather than synthesised: the concept did not exist, so any
502
- * invented value would either lie or (via the v15 rule that a non-dynamic
503
- * sampler's name is cleared) silently erase names a legacy file does carry.
504
- *
505
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
506
- * @param {number} version Container data version.
507
- * @returns {object} Sampler record.
508
- */
509
- function readSampler(reader, version) {
510
- const record = {
511
- name: readStringRef(reader),
512
- comparison: reader.readUint8(),
513
- minFilter: reader.readUint8(),
514
- magFilter: reader.readUint8(),
515
- mipFilter: reader.readUint8(),
516
- addressU: reader.readUint8(),
517
- addressV: reader.readUint8(),
518
- addressW: reader.readUint8(),
519
- mipLODBias: reader.readFloat32(),
520
- maxAnisotropy: reader.readUint8(),
521
- comparisonFunc: reader.readUint8(),
522
- borderColor: [reader.readFloat32(), reader.readFloat32(), reader.readFloat32(), reader.readFloat32()],
523
- minLOD: reader.readFloat32(),
524
- maxLOD: reader.readFloat32()
525
- };
526
- if (version > 12) {
527
- record.isDynamic = reader.readUint8();
528
- }
529
- return record;
530
- }
531
-
532
- /**
533
- * Writes one `Sampler`.
534
- *
535
- * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
536
- * @param {object} arena Arena resolver.
537
- * @param {object} sampler Sampler record.
538
- */
539
- function writeSampler(writer, arena, sampler) {
540
- writer.u32(arena.string(sampler.name));
541
- writer.u8(sampler.comparison);
542
- writer.u8(sampler.minFilter);
543
- writer.u8(sampler.magFilter);
544
- writer.u8(sampler.mipFilter);
545
- writer.u8(sampler.addressU);
546
- writer.u8(sampler.addressV);
547
- writer.u8(sampler.addressW);
548
- writer.f32(sampler.mipLODBias);
549
- writer.u8(sampler.maxAnisotropy);
550
- writer.u8(sampler.comparisonFunc);
551
- writer.f32(sampler.borderColor[0]);
552
- writer.f32(sampler.borderColor[1]);
553
- writer.f32(sampler.borderColor[2]);
554
- writer.f32(sampler.borderColor[3]);
555
- writer.f32(sampler.minLOD);
556
- writer.f32(sampler.maxLOD);
557
- writer.u8(sampler.isDynamic);
558
- }
559
-
560
- /**
561
- * Reads one `StaticSampler` (`EffectData.h:512-531`, read inside
562
- * `ReadRegisters` at `Tr2EffectDescription.cpp:324-365`). Border colour is a
563
- * one-byte enum here, and there is no name.
564
- *
565
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
566
- * @returns {object} Static sampler record.
567
- */
568
- function readStaticSampler(reader) {
569
- return {
570
- registerIndex: reader.readUint32(),
571
- registerSpace: reader.readUint8(),
572
- comparison: reader.readUint8(),
573
- minFilter: reader.readUint8(),
574
- magFilter: reader.readUint8(),
575
- mipFilter: reader.readUint8(),
576
- addressU: reader.readUint8(),
577
- addressV: reader.readUint8(),
578
- addressW: reader.readUint8(),
579
- mipLODBias: reader.readFloat32(),
580
- maxAnisotropy: reader.readUint8(),
581
- comparisonFunc: reader.readUint8(),
582
- borderColor: reader.readUint8(),
583
- minLOD: reader.readFloat32(),
584
- maxLOD: reader.readFloat32()
585
- };
586
- }
587
-
588
- /**
589
- * Writes one `StaticSampler`.
590
- *
591
- * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
592
- * @param {object} sampler Static sampler record.
593
- */
594
- function writeStaticSampler(writer, sampler) {
595
- writer.u32(sampler.registerIndex);
596
- writer.u8(sampler.registerSpace);
597
- writer.u8(sampler.comparison);
598
- writer.u8(sampler.minFilter);
599
- writer.u8(sampler.magFilter);
600
- writer.u8(sampler.mipFilter);
601
- writer.u8(sampler.addressU);
602
- writer.u8(sampler.addressV);
603
- writer.u8(sampler.addressW);
604
- writer.f32(sampler.mipLODBias);
605
- writer.u8(sampler.maxAnisotropy);
606
- writer.u8(sampler.comparisonFunc);
607
- writer.u8(sampler.borderColor);
608
- writer.f32(sampler.minLOD);
609
- writer.f32(sampler.maxLOD);
610
- }
611
-
612
- /**
613
- * Maps a pre-v10 register type byte onto the current register-type values
614
- * (`Tr2EffectDescription.cpp:262-276`).
615
- *
616
- * @param {number} value Pre-v10 register type byte.
617
- * @returns {number} Current register type value.
618
- */
619
- function mapOldRegisterType(value) {
620
- if (value === 0) return 0;
621
- if (value === 1) return 36;
622
- if (value === 2) return 68;
623
- if (value === 3) return 1;
624
- return 36;
625
- }
626
-
627
- /**
628
- * Reads the register signature and (v13+) static-sampler table
629
- * (`ReadRegisters`, `Tr2EffectDescription.cpp:258-367`).
630
- *
631
- * Before v13 a register carries only its type and index; Carbon defaults the
632
- * count to one and the space to the stage type (`:340-343`), and the record is
633
- * normalized here so consumers see one shape.
634
- *
635
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
636
- * @param {number} version Container data version.
637
- * @param {number} stageType Stage type owning the signature; only consulted
638
- * for the pre-v13 register-space default.
639
- * @returns {{registers:object[], staticSamplers:object[]}} Signature records.
640
- */
641
- function readSignature(reader, version, stageType) {
642
- const registers = [];
643
- const registerCount = reader.readUint8();
644
- for (let index = 0; index < registerCount; index += 1) {
645
- const rawType = reader.readUint8();
646
- const registerType = version > 9 ? rawType : mapOldRegisterType(rawType);
647
- const registerIndex = reader.readUint32();
648
- if (version > 12) {
649
- registers.push({
650
- registerType,
651
- registerIndex,
652
- registerCount: reader.readUint32(),
653
- registerSpace: reader.readUint8()
654
- });
655
- } else {
656
- registers.push({
657
- registerType,
658
- registerIndex,
659
- registerCount: 1,
660
- registerSpace: stageType
661
- });
662
- }
663
- }
664
- const staticSamplers = [];
665
- if (version > 12) {
666
- const staticSamplerCount = reader.readUint8();
667
- for (let index = 0; index < staticSamplerCount; index += 1) {
668
- staticSamplers.push(readStaticSampler(reader));
669
- }
670
- }
671
- return {
672
- registers,
673
- staticSamplers
674
- };
675
- }
676
-
677
- /**
678
- * Reads the resource half of a `StageData` block: constants, default constant
679
- * values, textures, samplers, UAVs, annotations (`ReadInput`,
680
- * `Tr2EffectDescription.cpp:369-472`).
681
- *
682
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
683
- * @param {number} version Container data version.
684
- * @param {Function} makeError Error factory.
685
- * @returns {object} Stage resource records.
686
- */
687
- function readStageResources(reader, version, makeError) {
688
- const constants = [];
689
- const constantCount = reader.readUint32();
690
- for (let index = 0; index < constantCount; index += 1) {
691
- constants.push(readConstant(reader, version));
692
- }
693
- const defaultValues = readBlobRef(reader);
694
- const textures = [];
695
- const textureCount = sanityCheck(reader.readUint8(), "textures", makeError);
696
- for (let index = 0; index < textureCount; index += 1) {
697
- const registerIndex = reader.readUint8();
698
- textures.push({
699
- registerIndex,
700
- ...readTexture(reader, version)
701
- });
702
- }
703
- const samplers = [];
704
- const samplerCount = sanityCheck(reader.readUint8(), "samplers", makeError);
705
- for (let index = 0; index < samplerCount; index += 1) {
706
- const registerIndex = reader.readUint8();
707
- samplers.push({
708
- registerIndex,
709
- ...readSampler(reader, version)
710
- });
711
- }
712
- const uavs = [];
713
- const uavCount = sanityCheck(reader.readUint8(), "uavs", makeError);
714
- for (let index = 0; index < uavCount; index += 1) {
715
- const registerIndex = reader.readUint8();
716
- uavs.push({
717
- registerIndex,
718
- ...readUav(reader, version)
719
- });
720
- }
721
- return {
722
- constants,
723
- defaultValues,
724
- textures,
725
- samplers,
726
- uavs,
727
- annotations: readAnnotations(reader)
728
- };
729
- }
730
-
731
- /**
732
- * Reads a whole `StageData` block: registers, static samplers, then the
733
- * resource half (`StageData::Save` `EffectData.h:631-676`).
734
- *
735
- * Carbon's reader splits this single contiguous run across two functions —
736
- * `ReadRegisters` takes the first two sub-records
737
- * (`Tr2EffectDescription.cpp:258-367`) and `ReadInput` the remaining six
738
- * (`:369-472`). The split exists here too, because before v14 the two halves
739
- * are not adjacent on the wire — the signature precedes the program blob
740
- * while the resources follow the thread-group size — and `readStage` places
741
- * each half where its version put it.
742
- *
743
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
744
- * @param {number} version Container data version.
745
- * @param {Function} makeError Error factory.
746
- * @param {number} stageType Stage type owning the signature.
747
- * @returns {object} Stage data record.
748
- */
749
- function readStageData(reader, version, makeError, stageType) {
750
- return {
751
- ...readSignature(reader, version, stageType),
752
- ...readStageResources(reader, version, makeError)
753
- };
754
- }
755
-
756
- /**
757
- * Writes the `StageData` block.
758
- *
759
- * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
760
- * @param {object} arena Arena resolver.
761
- * @param {object} stageData Stage data record.
762
- */
763
- function writeStageData(writer, arena, stageData) {
764
- writer.u8(stageData.registers.length);
765
- for (const register of stageData.registers) {
766
- writer.u8(register.registerType);
767
- writer.u32(register.registerIndex);
768
- writer.u32(register.registerCount);
769
- writer.u8(register.registerSpace);
770
- }
771
- writer.u8(stageData.staticSamplers.length);
772
- for (const sampler of stageData.staticSamplers) {
773
- writeStaticSampler(writer, sampler);
774
- }
775
- writer.u32(stageData.constants.length);
776
- for (const constant of stageData.constants) {
777
- writeConstant(writer, arena, constant);
778
- }
779
- writeBlobRef(writer, arena, stageData.defaultValues);
780
-
781
- // Carbon's compiler enforces none of these caps while its runtime rejects
782
- // anything above them, so an over-large effect compiles and then fails to
783
- // load. Check on the way out instead.
784
- sanityCheck(stageData.textures.length, "textures", writeError);
785
- sanityCheck(stageData.samplers.length, "samplers", writeError);
786
- sanityCheck(stageData.uavs.length, "uavs", writeError);
787
- writer.u8(stageData.textures.length);
788
- for (const texture of stageData.textures) {
789
- writer.u8(texture.registerIndex);
790
- writeTexture(writer, arena, texture);
791
- }
792
- writer.u8(stageData.samplers.length);
793
- for (const sampler of stageData.samplers) {
794
- writer.u8(sampler.registerIndex);
795
- writeSampler(writer, arena, sampler);
796
- }
797
- writer.u8(stageData.uavs.length);
798
- for (const uav of stageData.uavs) {
799
- writer.u8(uav.registerIndex);
800
- writeUav(writer, arena, uav);
801
- }
802
- writeAnnotations(writer, arena, stageData.annotations);
803
- }
804
-
805
- /**
806
- * Reads the pipeline-input list (`Tr2EffectDescription.cpp:234-256`).
807
- *
808
- * The `type`/`dimension` bytes joined the wire at v11; before that Carbon
809
- * derives them — UINT for the blend-index usage, FLOAT otherwise, always four
810
- * components (`:247-252`) — and the record is normalized here the same way.
811
- *
812
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
813
- * @param {number} version Container data version.
814
- * @param {Function} makeError Error factory.
815
- * @returns {object[]} Pipeline-input records.
816
- */
817
- function readPipelineInputs(reader, version, makeError) {
818
- const pipelineInputs = [];
819
- const pipelineInputCount = sanityCheck(reader.readUint8(), "pipelineInputs", makeError);
820
- for (let index = 0; index < pipelineInputCount; index += 1) {
821
- const usage = reader.readUint8();
822
- const registerIndex = reader.readUint8();
823
- const usageIndex = reader.readUint8();
824
- const usedMask = reader.readUint8();
825
- if (version > 10) {
826
- pipelineInputs.push({
827
- usage,
828
- registerIndex,
829
- usageIndex,
830
- usedMask,
831
- type: reader.readUint8(),
832
- dimension: reader.readUint8()
833
- });
834
- } else {
835
- pipelineInputs.push({
836
- usage,
837
- registerIndex,
838
- usageIndex,
839
- usedMask,
840
- // Constant-type values: 2 is UINT, 0 is FLOAT; usage 6 is the
841
- // blend-index semantic.
842
- type: usage === 6 ? 2 : 0,
843
- dimension: 4
844
- });
845
- }
846
- }
847
- return pipelineInputs;
848
- }
849
-
850
- /**
851
- * Reads one stage (`StageInput::Save` `EffectData.h:691-709`,
852
- * `Tr2EffectDescription.cpp:532-585`).
853
- *
854
- * At v15 the program payload comes first and the signature tables follow.
855
- * Before v14 it was the other way round; the reorder is the v14 change, and
856
- * Carbon marks its own v14 branch `// CHECK` (`:578`). Verified against the
857
- * writer for v15: `type`, `shaderSize`, `shaderData`,
858
- * `threadGroupSize[0..2]`, `pipelineInputs`, then `StageData`. Two further
859
- * legacy wrinkles, both from Carbon's reader: a stage carries no register
860
- * signature at all before v9 (`:544`), and two legacy dwords follow the
861
- * program reference before v12, read and discarded (`:559-563`).
862
- *
863
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
864
- * @param {number} version Container data version.
865
- * @param {Function} makeError Error factory.
866
- * @returns {object} Stage record.
867
- */
868
- function readStage(reader, version, makeError) {
869
- const type = reader.readUint8();
870
- let pipelineInputs = [];
871
- let signature = {
872
- registers: [],
873
- staticSamplers: []
874
- };
875
- if (version < 14) {
876
- pipelineInputs = readPipelineInputs(reader, version, makeError);
877
- if (version > 8) {
878
- signature = readSignature(reader, version, type);
879
- }
880
- }
881
- const shaderData = readBlobRef(reader);
882
- if (version < 12) {
883
- reader.readUint32();
884
- reader.readUint32();
885
- }
886
- const threadGroupSize = [reader.readUint32(), reader.readUint32(), reader.readUint32()];
887
- if (version >= 14) {
888
- pipelineInputs = readPipelineInputs(reader, version, makeError);
889
- signature = readSignature(reader, version, type);
890
- }
891
- return {
892
- type,
893
- shaderData,
894
- threadGroupSize,
895
- pipelineInputs,
896
- ...signature,
897
- ...readStageResources(reader, version, makeError)
898
- };
899
- }
900
-
901
- /**
902
- * Writes one stage.
903
- *
904
- * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
905
- * @param {object} arena Arena resolver.
906
- * @param {object} stage Stage record.
907
- */
908
- function writeStage(writer, arena, stage) {
909
- sanityCheck(stage.pipelineInputs.length, "pipelineInputs", writeError);
910
- writer.u8(stage.type);
911
- writeBlobRef(writer, arena, stage.shaderData);
912
- writer.u32(stage.threadGroupSize[0]);
913
- writer.u32(stage.threadGroupSize[1]);
914
- writer.u32(stage.threadGroupSize[2]);
915
- writer.u8(stage.pipelineInputs.length);
916
- for (const input of stage.pipelineInputs) {
917
- writer.u8(input.usage);
918
- writer.u8(input.registerIndex);
919
- writer.u8(input.usageIndex);
920
- writer.u8(input.usedMask);
921
- writer.u8(input.type);
922
- writer.u8(input.dimension);
923
- }
924
- writeStageData(writer, arena, stage);
925
- }
926
-
927
- /**
928
- * Reads one pass (`Pass::Save` `EffectData.h:724-738`).
929
- *
930
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
931
- * @param {number} version Container data version.
932
- * @param {Function} makeError Error factory.
933
- * @param {boolean} backend Whether the optional trailing block is present.
934
- * @returns {object} Pass record.
935
- */
936
- function readPass(reader, version, makeError, backend) {
937
- const stages = [];
938
- const stageCount = sanityCheck(reader.readUint8(), "stages", makeError);
939
- for (let index = 0; index < stageCount; index += 1) {
940
- stages.push(readStage(reader, version, makeError));
941
- }
942
- const renderStates = [];
943
- const stateCount = sanityCheck(reader.readUint8(), "renderStates", makeError);
944
- for (let index = 0; index < stateCount; index += 1) {
945
- renderStates.push({
946
- state: reader.readUint32(),
947
- value: reader.readUint32()
948
- });
949
- }
950
-
951
- // The one optional trailing block, present only in our own containers. A
952
- // Carbon file ends the pass at the render-state table, so leaving this gate
953
- // closed reproduces Carbon's bytes exactly.
954
- const backendBlock = backend ? readBlobRef(reader) : null;
955
- return {
956
- stages,
957
- renderStates,
958
- ...(backendBlock ? {
959
- backendBlock
960
- } : {})
961
- };
962
- }
963
-
964
- /**
965
- * Writes one pass.
966
- *
967
- * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
968
- * @param {object} arena Arena resolver.
969
- * @param {object} pass Pass record.
970
- * @param {boolean} backend Whether to emit the optional trailing block.
971
- */
972
- function writePass(writer, arena, pass, backend) {
973
- writer.u8(pass.stages.length);
974
- for (const stage of pass.stages) {
975
- writeStage(writer, arena, stage);
976
- }
977
- writer.u8(pass.renderStates.length);
978
- for (const entry of pass.renderStates) {
979
- writer.u32(entry.state);
980
- writer.u32(entry.value);
981
- }
982
- if (backend) {
983
- writeBlobRef(writer, arena, pass.backendBlock ?? EMPTY_BLOB);
984
- }
985
- }
986
-
987
- /**
988
- * Reads one raytracing library (`Library::Save` `EffectData.h:760-775`,
989
- * `Tr2EffectDescription.cpp:675-721`). A library carries two `StageData`
990
- * blocks and no pipeline inputs, thread group size, or stage type.
991
- *
992
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
993
- * @param {number} version Container data version.
994
- * @param {Function} makeError Error factory.
995
- * @returns {object} Library record.
996
- */
997
- function readLibrary(reader, version, makeError) {
998
- const payloadSize = reader.readUint32();
999
- const shaderData = readBlobRef(reader);
1000
- const exports = [];
1001
- const exportCount = reader.readUint32();
1002
- for (let index = 0; index < exportCount; index += 1) {
1003
- exports.push({
1004
- type: reader.readUint8(),
1005
- name: readStringRef(reader)
1006
- });
1007
- }
1008
-
1009
- // Libraries exist only from v14, so the pre-v13 register-space default in
1010
- // the signature reader is unreachable here; the stage type it would need
1011
- // is passed as zero.
1012
- return {
1013
- payloadSize,
1014
- shaderData,
1015
- exports,
1016
- hitGroupName: readStringRef(reader),
1017
- globalInputs: readStageData(reader, version, makeError, 0),
1018
- localInputs: readStageData(reader, version, makeError, 0)
1019
- };
1020
- }
1021
-
1022
- /**
1023
- * Writes one raytracing library.
1024
- *
1025
- * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
1026
- * @param {object} arena Arena resolver.
1027
- * @param {object} library Library record.
1028
- */
1029
- function writeLibrary(writer, arena, library) {
1030
- writer.u32(library.payloadSize);
1031
- writeBlobRef(writer, arena, library.shaderData);
1032
- writer.u32(library.exports.length);
1033
- for (const entry of library.exports) {
1034
- writer.u8(entry.type);
1035
- writer.u32(arena.string(entry.name));
1036
- }
1037
- writer.u32(arena.string(library.hitGroupName));
1038
- writeStageData(writer, arena, library.globalInputs);
1039
- writeStageData(writer, arena, library.localInputs);
1040
- }
1041
-
1042
- /**
1043
- * Reads a whole v15 description blob (`EffectData::Save`
1044
- * `EffectData.h:821-849`, `Tr2EffectDescription::Read`
1045
- * `Tr2EffectDescription.cpp:476-731`).
1046
- *
1047
- * @param {import("../CjsByteReader.js").CjsByteReader} reader Reader positioned at the blob.
1048
- * @param {object} [options] Read options.
1049
- * @param {Function} [options.makeError] Error factory taking a message and details.
1050
- * @param {number} [options.version] Container data version; defaults to the
1051
- * current version. Accepted range is `CARBON_EFFECT_MIN_DATA_VERSION` to
1052
- * `CARBON_EFFECT_DATA_VERSION`; the record tree comes out normalized to
1053
- * the v15 shape whatever the wire carried.
1054
- * @param {boolean} [options.backend] Expect our optional per-pass trailing block.
1055
- * Leave false for a Carbon file, which ends each pass at the render states.
1056
- * @returns {object} Description record tree.
1057
- */
1058
- function readEffectDescription(reader, options = {}) {
1059
- const makeError = options.makeError ?? ((message, details) => reader._error(message, details));
1060
- const backend = options.backend === true;
1061
- const version = Number.isInteger(options.version) ? options.version : CARBON_EFFECT_DATA_VERSION;
1062
- if (version < CARBON_EFFECT_MIN_DATA_VERSION || version > CARBON_EFFECT_DATA_VERSION) {
1063
- throw makeError(`Unsupported Carbon effect version ${version}; expected ${CARBON_EFFECT_MIN_DATA_VERSION}..${CARBON_EFFECT_DATA_VERSION}`, {
1064
- version
1065
- });
1066
- }
1067
- const techniques = [];
1068
- const techniqueCount = reader.readUint8();
1069
- for (let techniqueIndex = 0; techniqueIndex < techniqueCount; techniqueIndex += 1) {
1070
- const name = readStringRef(reader);
1071
- const passes = [];
1072
- const passCount = sanityCheck(reader.readUint8(), "passes", makeError);
1073
- for (let passIndex = 0; passIndex < passCount; passIndex += 1) {
1074
- passes.push(readPass(reader, version, makeError, backend));
1075
- }
1076
-
1077
- // The library table joined the wire at v14; older files end the
1078
- // technique at its passes.
1079
- const libraries = [];
1080
- if (version > 13) {
1081
- const libraryCount = reader.readUint8();
1082
- for (let libraryIndex = 0; libraryIndex < libraryCount; libraryIndex += 1) {
1083
- libraries.push(readLibrary(reader, version, makeError));
1084
- }
1085
- }
1086
- techniques.push({
1087
- name,
1088
- passes,
1089
- libraries
1090
- });
1091
- }
1092
- const annotations = [];
1093
- const parameterCount = sanityCheck(reader.readUint16(), "effectAnnotations", makeError);
1094
- for (let index = 0; index < parameterCount; index += 1) {
1095
- annotations.push({
1096
- name: readStringRef(reader),
1097
- annotations: readAnnotations(reader)
1098
- });
1099
- }
1100
- requireExhaustive(reader, "description blob", makeError);
1101
- return {
1102
- techniques,
1103
- annotations
1104
- };
1105
- }
1106
-
1107
- /**
1108
- * Writes a whole v15 description blob.
1109
- *
1110
- * The `arena` resolver decides what an arena reference becomes: `passthroughArena`
1111
- * keeps the source offsets, `internArena(table)` re-interns into a new table.
1112
- *
1113
- * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
1114
- * @param {object} description Description record tree.
1115
- * @param {object} [options] Write options.
1116
- * @param {object} [options.arena] Arena resolver; defaults to passthrough.
1117
- * @param {boolean} [options.backend] Emit our optional per-pass trailing block.
1118
- * Leave false to produce bytes a Carbon reader accepts unchanged.
1119
- * @returns {import("../CjsByteWriter.js").CjsByteWriter} The writer.
1120
- */
1121
- function writeEffectDescription(writer, description, options = {}) {
1122
- const arena = options.arena ?? passthroughArena;
1123
- const backend = options.backend === true;
1124
- writer.u8(description.techniques.length);
1125
- for (const technique of description.techniques) {
1126
- writer.u32(arena.string(technique.name));
1127
- writer.u8(sanityCheck(technique.passes.length, "passes", writeError));
1128
- for (const pass of technique.passes) {
1129
- sanityCheck(pass.stages.length, "stages", writeError);
1130
- sanityCheck(pass.renderStates.length, "renderStates", writeError);
1131
- writePass(writer, arena, pass, backend);
1132
- }
1133
- writer.u8(technique.libraries.length);
1134
- for (const library of technique.libraries) {
1135
- writeLibrary(writer, arena, library);
1136
- }
1137
- }
1138
- writer.u16(sanityCheck(description.annotations.length, "effectAnnotations", writeError));
1139
- for (const parameter of description.annotations) {
1140
- writer.u32(arena.string(parameter.name));
1141
- writeAnnotations(writer, arena, parameter.annotations);
1142
- }
1143
- return writer;
1144
- }
1145
-
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 };
1147
- //# sourceMappingURL=carbonEffectRecords.js.map
1
+ import { CjsFormatWriteError } from '../CjsFormatError.js';
2
+ import { compareUtf8 } from '../compareUtf8.js';
3
+
4
+ // Source: trinity/shadercompiler/EffectData.h (Save methods)
5
+ // Source: trinity/trinity/Shader/Tr2EffectDescription.cpp (Read)
6
+
7
+
8
+ /**
9
+ * The current description-blob version, and the only one this module writes.
10
+ *
11
+ * `DATA_VERSION` in `EffectData.h:10`. Carbon's reader accepts 2..15
12
+ * (`Tr2EffectRes.cpp:209`) but annotates the v13/v14 field-order boundaries as
13
+ * unverified — `// CHECK IS IT IN RIGHT FUNCTION?` at
14
+ * `Tr2EffectDescription.cpp:177`, `// CHECK` at `:257` and `:578`. The v15
15
+ * layout is the one with an authoritative writer to check against, it is the
16
+ * only version the shipped corpus contains, and it is byte-identical to v14 in
17
+ * the body — v15 differs from v14 only by the 36 outer header bytes.
18
+ */
19
+ const CARBON_EFFECT_DATA_VERSION = 15;
20
+
21
+ /**
22
+ * The oldest description-blob version this module reads.
23
+ *
24
+ * Carbon threads the version through parsing rather than gating on it twice,
25
+ * and its branches reach back to version 2. Maintainer decision, 2026-08-02:
26
+ * **8 is the lowest version worth reading** — the branches below 8 have no
27
+ * examined file, no writer, and no consumer, so restoring them would be code
28
+ * with no evidence. Reading a version is not supporting it: the reader's job
29
+ * ends at a correct record tree, normalized to the v15 record shape.
30
+ */
31
+ const CARBON_EFFECT_MIN_DATA_VERSION = 8;
32
+
33
+ /**
34
+ * Annotation type codes (`EffectData.h:35-41`). Only `STRING` changes the wire
35
+ * shape of the value that follows.
36
+ */
37
+ const CARBON_ANNOTATION_TYPE = Object.freeze({
38
+ BOOL: 0,
39
+ INT: 1,
40
+ FLOAT: 2,
41
+ STRING: 3
42
+ });
43
+
44
+ /**
45
+ * Carbon's inclusive count limits, from the `SanityCheck` call sites in
46
+ * `Tr2EffectDescription.cpp:28-36`. `SanityCheck` throws only when
47
+ * `value > limit`, so each number is the largest accepted count.
48
+ *
49
+ * The absent entries are absent in Carbon too: `techniques`, `registers`,
50
+ * `staticSamplers`, `constants`, `libraries`, `exports` and the per-annotation
51
+ * counts are read with no limit at all, guarded only by the end-of-buffer
52
+ * check. We do not invent caps Carbon does not have.
53
+ */
54
+ const CARBON_EFFECT_COUNT_CAPS = Object.freeze({
55
+ pipelineInputs: 64,
56
+ passes: 64,
57
+ stages: 6,
58
+ textures: 64,
59
+ samplers: 64,
60
+ uavs: 64,
61
+ renderStates: 64,
62
+ effectAnnotations: 256
63
+ });
64
+
65
+ /**
66
+ * `SHADER_CONSTANTS_MAX` (`Tr2EffectDescription.h:160`) — Carbon clamps a
67
+ * stage's default-constant-value copy to this without rejecting the file, while
68
+ * still advancing the cursor by the untruncated size.
69
+ */
70
+ const CARBON_SHADER_CONSTANTS_MAX = 4096;
71
+
72
+ /**
73
+ * Compares two annotation names the way Carbon sorts annotation keys before
74
+ * writing them (`strcmp`, `EffectData.h:613-616` and `:839-842`).
75
+ *
76
+ * `strcmp` orders by unsigned byte, which for names outside ASCII is not the
77
+ * same as JavaScript's UTF-16 code-unit order — hence the explicit UTF-8
78
+ * comparison.
79
+ *
80
+ * @param {string} a First name.
81
+ * @param {string} b Second name.
82
+ * @returns {number} Negative, zero, or positive ordering result.
83
+ */
84
+ function compareAnnotationNames(a, b) {
85
+ return compareUtf8(a, b);
86
+ }
87
+
88
+ /**
89
+ * Resolver that writes each arena reference back at the offset it was read
90
+ * from, reusing the source arena verbatim.
91
+ *
92
+ * This is what proves field order independently of arena policy: a re-emit
93
+ * through this resolver must reproduce the original description bytes exactly,
94
+ * whatever the arena happens to contain.
95
+ */
96
+ const passthroughArena = Object.freeze({
97
+ /**
98
+ * Returns a parsed string reference's original offset.
99
+ *
100
+ * @param {{offset:number}} reference Parsed string reference.
101
+ * @returns {number} Original arena offset.
102
+ */
103
+ string(reference) {
104
+ return reference.offset;
105
+ },
106
+ /**
107
+ * Returns a parsed blob reference's original offset.
108
+ *
109
+ * @param {{offset:number}} reference Parsed blob reference.
110
+ * @returns {number} Original arena offset.
111
+ */
112
+ blob(reference) {
113
+ return reference.offset;
114
+ }
115
+ });
116
+
117
+ /**
118
+ * Creates a resolver that re-interns every reference into a fresh string table,
119
+ * so the emitted arena is rebuilt under Carbon's sorted-offset policy.
120
+ *
121
+ * @param {import("../CjsStringTable.js").CjsStringTable} table Target arena.
122
+ * @returns {{string:Function, blob:Function}} Arena resolver.
123
+ */
124
+ function internArena(table) {
125
+ return {
126
+ /**
127
+ * Interns a string reference's text and returns its offset.
128
+ *
129
+ * @param {{value:string}} reference Parsed string reference.
130
+ * @returns {number} Arena offset.
131
+ */
132
+ string(reference) {
133
+ return table.offsetOf(table.addString(reference.value));
134
+ },
135
+ /**
136
+ * Interns a blob reference's bytes and returns its offset. A zero-size
137
+ * blob keeps its original reference — Carbon leaves the offset word
138
+ * unset in that case, and the reader never dereferences it.
139
+ *
140
+ * @param {{size:number, offset:number, bytes:Uint8Array}} reference Parsed blob reference.
141
+ * @returns {number} Arena offset.
142
+ */
143
+ blob(reference) {
144
+ if (reference.size === 0) return reference.offset;
145
+ return table.offsetOf(table.addBytes(reference.bytes));
146
+ }
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Creates a resolver that only interns, emitting a placeholder offset.
152
+ *
153
+ * Carbon resolves an arena offset while packing a body
154
+ * (`PackedStream::Save(StringReference)`, `EffectData.h:187-193`), by which
155
+ * point every string must already be in the table — a late `AddString` marks
156
+ * the table dirty and the next `GetOffset` re-sorts it, silently invalidating
157
+ * every offset already baked into a packed body. Carbon dodges this by
158
+ * interning all late strings up front (`ShaderCompiler.cpp:686-694`, before the
159
+ * packing loop at `:697-714`).
160
+ *
161
+ * Running the write walk twice — once collecting, once emitting — reproduces
162
+ * that discipline without depending on a caller remembering it, and because
163
+ * both passes traverse the same code they cannot drift apart.
164
+ *
165
+ * @param {import("../CjsStringTable.js").CjsStringTable} table Target arena.
166
+ * @returns {{string:Function, blob:Function}} Collect-only resolver.
167
+ */
168
+ function collectArena(table) {
169
+ return {
170
+ /**
171
+ * Interns a string reference's text.
172
+ *
173
+ * @param {{value:string}} reference Parsed string reference.
174
+ * @returns {number} Placeholder offset.
175
+ */
176
+ string(reference) {
177
+ table.addString(reference.value);
178
+ return 0;
179
+ },
180
+ /**
181
+ * Interns a blob reference's bytes, skipping zero-size blobs.
182
+ *
183
+ * @param {{size:number, bytes:Uint8Array}} reference Parsed blob reference.
184
+ * @returns {number} Placeholder offset.
185
+ */
186
+ blob(reference) {
187
+ if (reference.size !== 0) table.addBytes(reference.bytes);
188
+ return 0;
189
+ }
190
+ };
191
+ }
192
+
193
+ /**
194
+ * Reads a bare `u32` arena offset and the NUL-terminated string behind it.
195
+ *
196
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
197
+ * @returns {{offset:number, value:string}} String reference.
198
+ */
199
+ function readStringRef(reader) {
200
+ const offset = reader.readUint32();
201
+ return {
202
+ offset,
203
+ value: reader.readStringAt(offset)
204
+ };
205
+ }
206
+
207
+ /**
208
+ * Reads a `{u32 size, u32 offset}` blob reference.
209
+ *
210
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
211
+ * @returns {{size:number, offset:number, bytes:Uint8Array}} Blob reference.
212
+ */
213
+ function readBlobRef(reader) {
214
+ const size = reader.readUint32();
215
+ const blob = reader.readTableBlobOptional(size);
216
+ return {
217
+ size,
218
+ offset: blob.offset,
219
+ bytes: blob.bytes
220
+ };
221
+ }
222
+
223
+ /**
224
+ * Writes a `{u32 size, u32 offset}` blob reference.
225
+ *
226
+ * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
227
+ * @param {object} arena Arena resolver.
228
+ * @param {{size:number}} reference Blob reference.
229
+ */
230
+ function writeBlobRef(writer, arena, reference) {
231
+ writer.u32(reference.size);
232
+ writer.u32(arena.blob(reference));
233
+ }
234
+
235
+ /**
236
+ * An absent sized blob: zero length, Carbon's null offset. The offset word is
237
+ * still written, and a reader must not dereference it.
238
+ */
239
+ const EMPTY_BLOB = Object.freeze({
240
+ size: 0,
241
+ offset: 0xffffffff,
242
+ bytes: new Uint8Array(0)
243
+ });
244
+
245
+ /**
246
+ * Error factory used by the write path's cap checks.
247
+ *
248
+ * @param {string} message Failure reason.
249
+ * @param {object} details Structured details.
250
+ * @returns {CjsFormatWriteError} Write error.
251
+ */
252
+ function writeError(message, details) {
253
+ return new CjsFormatWriteError(message, details);
254
+ }
255
+
256
+ /**
257
+ * Requires that a sized record parsed to exactly its declared end.
258
+ *
259
+ * This is the universal form of the rule the backend block discovered, and it
260
+ * carries real weight: it is the successor to roughly 600 lines of cross-chunk
261
+ * agreement checks that the container rewrite deletes. Those checks caught a
262
+ * malformed *tree* — our writer emitting something structurally wrong — not only a
263
+ * malformed file. Under a record layout a writer bug either fails to parse, which
264
+ * is self-announcing, or it parses and leaves the cursor somewhere other than the
265
+ * declared end. Trailing bytes therefore mean one of two things, both fatal: the
266
+ * writer knew fields this reader does not, or the writer miscounted.
267
+ *
268
+ * Applying it to some sized records and not others is a gap that stays invisible
269
+ * until a writer bug hides in one of the others, so every sized record gets it.
270
+ *
271
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Reader at the record end.
272
+ * @param {string} what Record name for the error message.
273
+ * @param {Function} makeError Error factory.
274
+ */
275
+ function requireExhaustive(reader, what, makeError) {
276
+ if (reader.remaining !== 0) {
277
+ throw makeError(`Carbon effect ${what} has ${reader.remaining} unparsed trailing byte(s)`, {
278
+ trailingBytes: reader.remaining,
279
+ offset: reader.offset,
280
+ end: reader.end
281
+ });
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Rejects a count above Carbon's inclusive limit for that field.
287
+ *
288
+ * @param {number} value Count read or about to be written.
289
+ * @param {string} field Cap key in `CARBON_EFFECT_COUNT_CAPS`.
290
+ * @param {Function} makeError Error factory taking a message and details.
291
+ * @returns {number} The count.
292
+ */
293
+ function sanityCheck(value, field, makeError) {
294
+ const limit = CARBON_EFFECT_COUNT_CAPS[field];
295
+ if (limit !== undefined && value > limit) {
296
+ throw makeError(`Effect ${field} count ${value} exceeds Carbon's limit of ${limit}`, {
297
+ field,
298
+ value,
299
+ limit
300
+ });
301
+ }
302
+ return value;
303
+ }
304
+
305
+ /**
306
+ * Reads one annotation map: `u8 count`, then name/type/value triples
307
+ * (`ParameterAnnotation::Save` `EffectData.h:601-623`, `ReadAnnotations`
308
+ * `Tr2EffectDescription.cpp:114-134`).
309
+ *
310
+ * A non-string value is kept as its raw four bytes. Carbon writes it through
311
+ * the `float` member of a `{float,int32_t}` union and reads it back through a
312
+ * different union, so the bytes are the only faithful representation; applying
313
+ * an int/float conversion here would corrupt round-trips.
314
+ *
315
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
316
+ * @returns {object[]} Annotation records.
317
+ */
318
+ function readAnnotations(reader) {
319
+ const count = reader.readUint8();
320
+ const annotations = [];
321
+ for (let index = 0; index < count; index += 1) {
322
+ const name = readStringRef(reader);
323
+ const type = reader.readUint8();
324
+ if (type === CARBON_ANNOTATION_TYPE.STRING) {
325
+ annotations.push({
326
+ name,
327
+ type,
328
+ stringValue: readStringRef(reader),
329
+ rawValue: null
330
+ });
331
+ } else {
332
+ annotations.push({
333
+ name,
334
+ type,
335
+ stringValue: null,
336
+ rawValue: Uint8Array.from(reader.readRaw(4))
337
+ });
338
+ }
339
+ }
340
+ return annotations;
341
+ }
342
+
343
+ /**
344
+ * Writes one annotation map.
345
+ *
346
+ * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
347
+ * @param {object} arena Arena resolver.
348
+ * @param {object[]} annotations Annotation records.
349
+ */
350
+ function writeAnnotations(writer, arena, annotations) {
351
+ writer.u8(annotations.length);
352
+ for (const annotation of annotations) {
353
+ writer.u32(arena.string(annotation.name));
354
+ writer.u8(annotation.type);
355
+ if (annotation.type === CARBON_ANNOTATION_TYPE.STRING) {
356
+ writer.u32(arena.string(annotation.stringValue));
357
+ } else {
358
+ writer.bytes(annotation.rawValue);
359
+ }
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Maps a pre-v11 constant type byte onto the current enum
365
+ * (`Tr2EffectDescription.cpp:141-158`). The old wire knew FLOAT/INT/BOOL;
366
+ * the current numbering is FLOAT 0, INT 1, UINT 2, BOOL 3, OTHER 4.
367
+ *
368
+ * @param {number} value Pre-v11 type byte.
369
+ * @returns {number} Current constant type value.
370
+ */
371
+ function mapOldConstantType(value) {
372
+ if (value === 0) return 0;
373
+ if (value === 1) return 1;
374
+ if (value === 2) return 3;
375
+ return 4;
376
+ }
377
+
378
+ /**
379
+ * Reads one `Constant` (`EffectData.h:283-294`, `ReadConstant`
380
+ * `Tr2EffectDescription.cpp:136-170`). The byte layout is identical across
381
+ * versions 8..15; only the type byte's numbering changed at v11.
382
+ *
383
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
384
+ * @param {number} version Container data version.
385
+ * @returns {object} Constant record.
386
+ */
387
+ function readConstant(reader, version) {
388
+ const name = readStringRef(reader);
389
+ const offset = reader.readUint32();
390
+ const size = reader.readUint32();
391
+ const rawType = reader.readUint8();
392
+ return {
393
+ name,
394
+ offset,
395
+ size,
396
+ type: version < 11 ? mapOldConstantType(rawType) : rawType,
397
+ dimension: reader.readUint8(),
398
+ elements: reader.readUint32(),
399
+ isSRGB: reader.readUint8(),
400
+ isAutoregister: reader.readUint8()
401
+ };
402
+ }
403
+
404
+ /**
405
+ * Writes one `Constant`.
406
+ *
407
+ * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
408
+ * @param {object} arena Arena resolver.
409
+ * @param {object} constant Constant record.
410
+ */
411
+ function writeConstant(writer, arena, constant) {
412
+ writer.u32(arena.string(constant.name));
413
+ writer.u32(constant.offset);
414
+ writer.u32(constant.size);
415
+ writer.u8(constant.type);
416
+ writer.u8(constant.dimension);
417
+ writer.u32(constant.elements);
418
+ writer.u8(constant.isSRGB);
419
+ writer.u8(constant.isAutoregister);
420
+ }
421
+
422
+ /**
423
+ * Reads one `Texture` (`EffectData.h:320-328`, `ReadResource`
424
+ * `Tr2EffectDescription.cpp:172-190`). `count` is the reader's
425
+ * `arrayElements`; before v13 the field is not on the wire and Carbon
426
+ * defaults it to one element.
427
+ *
428
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
429
+ * @param {number} version Container data version.
430
+ * @returns {object} Texture record.
431
+ */
432
+ function readTexture(reader, version) {
433
+ return {
434
+ name: readStringRef(reader),
435
+ type: reader.readUint8(),
436
+ count: version >= 13 ? reader.readUint32() : 1,
437
+ isSRGB: reader.readUint8(),
438
+ isAutoregister: reader.readUint8()
439
+ };
440
+ }
441
+
442
+ /**
443
+ * Writes one `Texture`.
444
+ *
445
+ * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
446
+ * @param {object} arena Arena resolver.
447
+ * @param {object} texture Texture record.
448
+ */
449
+ function writeTexture(writer, arena, texture) {
450
+ writer.u32(arena.string(texture.name));
451
+ writer.u8(texture.type);
452
+ writer.u32(texture.count);
453
+ writer.u8(texture.isSRGB);
454
+ writer.u8(texture.isAutoregister);
455
+ }
456
+
457
+ /**
458
+ * Reads one `Uav` (`EffectData.h:345-352`, inline at
459
+ * `Tr2EffectDescription.cpp:449-463`).
460
+ *
461
+ * A UAV record is one byte shorter than a texture record: it carries no
462
+ * `isSRGB`. Carbon's reader hardcodes `isSRGB = false`
463
+ * (`Tr2EffectDescription.cpp:450`) rather than reading it, and `Uav::Save`
464
+ * omits it. Reusing a shared "resource" codec for both is the mistake this
465
+ * comment exists to prevent.
466
+ *
467
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
468
+ * @param {number} version Container data version.
469
+ * @returns {object} UAV record.
470
+ */
471
+ function readUav(reader, version) {
472
+ return {
473
+ name: readStringRef(reader),
474
+ type: reader.readUint8(),
475
+ count: version >= 13 ? reader.readUint32() : 1,
476
+ isAutoregister: reader.readUint8()
477
+ };
478
+ }
479
+
480
+ /**
481
+ * Writes one `Uav`.
482
+ *
483
+ * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
484
+ * @param {object} arena Arena resolver.
485
+ * @param {object} uav UAV record.
486
+ */
487
+ function writeUav(writer, arena, uav) {
488
+ writer.u32(arena.string(uav.name));
489
+ writer.u8(uav.type);
490
+ writer.u32(uav.count);
491
+ writer.u8(uav.isAutoregister);
492
+ }
493
+
494
+ /**
495
+ * Reads one `Sampler` (`EffectData.h:368-390`, `ReadSampler`
496
+ * `Tr2EffectDescription.cpp:192-232` plus the name at `:413` and `isDynamic`
497
+ * at `:429`). Border colour here is four floats — unlike a static sampler,
498
+ * where it is a one-byte enum.
499
+ *
500
+ * `isDynamic` joined the wire at v13. For older versions the key is OMITTED
501
+ * from the record rather than synthesised: the concept did not exist, so any
502
+ * invented value would either lie or (via the v15 rule that a non-dynamic
503
+ * sampler's name is cleared) silently erase names a legacy file does carry.
504
+ *
505
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
506
+ * @param {number} version Container data version.
507
+ * @returns {object} Sampler record.
508
+ */
509
+ function readSampler(reader, version) {
510
+ const record = {
511
+ name: readStringRef(reader),
512
+ comparison: reader.readUint8(),
513
+ minFilter: reader.readUint8(),
514
+ magFilter: reader.readUint8(),
515
+ mipFilter: reader.readUint8(),
516
+ addressU: reader.readUint8(),
517
+ addressV: reader.readUint8(),
518
+ addressW: reader.readUint8(),
519
+ mipLODBias: reader.readFloat32(),
520
+ maxAnisotropy: reader.readUint8(),
521
+ comparisonFunc: reader.readUint8(),
522
+ borderColor: [reader.readFloat32(), reader.readFloat32(), reader.readFloat32(), reader.readFloat32()],
523
+ minLOD: reader.readFloat32(),
524
+ maxLOD: reader.readFloat32()
525
+ };
526
+ if (version > 12) {
527
+ record.isDynamic = reader.readUint8();
528
+ }
529
+ return record;
530
+ }
531
+
532
+ /**
533
+ * Writes one `Sampler`.
534
+ *
535
+ * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
536
+ * @param {object} arena Arena resolver.
537
+ * @param {object} sampler Sampler record.
538
+ */
539
+ function writeSampler(writer, arena, sampler) {
540
+ writer.u32(arena.string(sampler.name));
541
+ writer.u8(sampler.comparison);
542
+ writer.u8(sampler.minFilter);
543
+ writer.u8(sampler.magFilter);
544
+ writer.u8(sampler.mipFilter);
545
+ writer.u8(sampler.addressU);
546
+ writer.u8(sampler.addressV);
547
+ writer.u8(sampler.addressW);
548
+ writer.f32(sampler.mipLODBias);
549
+ writer.u8(sampler.maxAnisotropy);
550
+ writer.u8(sampler.comparisonFunc);
551
+ writer.f32(sampler.borderColor[0]);
552
+ writer.f32(sampler.borderColor[1]);
553
+ writer.f32(sampler.borderColor[2]);
554
+ writer.f32(sampler.borderColor[3]);
555
+ writer.f32(sampler.minLOD);
556
+ writer.f32(sampler.maxLOD);
557
+ writer.u8(sampler.isDynamic);
558
+ }
559
+
560
+ /**
561
+ * Reads one `StaticSampler` (`EffectData.h:512-531`, read inside
562
+ * `ReadRegisters` at `Tr2EffectDescription.cpp:324-365`). Border colour is a
563
+ * one-byte enum here, and there is no name.
564
+ *
565
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
566
+ * @returns {object} Static sampler record.
567
+ */
568
+ function readStaticSampler(reader) {
569
+ return {
570
+ registerIndex: reader.readUint32(),
571
+ registerSpace: reader.readUint8(),
572
+ comparison: reader.readUint8(),
573
+ minFilter: reader.readUint8(),
574
+ magFilter: reader.readUint8(),
575
+ mipFilter: reader.readUint8(),
576
+ addressU: reader.readUint8(),
577
+ addressV: reader.readUint8(),
578
+ addressW: reader.readUint8(),
579
+ mipLODBias: reader.readFloat32(),
580
+ maxAnisotropy: reader.readUint8(),
581
+ comparisonFunc: reader.readUint8(),
582
+ borderColor: reader.readUint8(),
583
+ minLOD: reader.readFloat32(),
584
+ maxLOD: reader.readFloat32()
585
+ };
586
+ }
587
+
588
+ /**
589
+ * Writes one `StaticSampler`.
590
+ *
591
+ * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
592
+ * @param {object} sampler Static sampler record.
593
+ */
594
+ function writeStaticSampler(writer, sampler) {
595
+ writer.u32(sampler.registerIndex);
596
+ writer.u8(sampler.registerSpace);
597
+ writer.u8(sampler.comparison);
598
+ writer.u8(sampler.minFilter);
599
+ writer.u8(sampler.magFilter);
600
+ writer.u8(sampler.mipFilter);
601
+ writer.u8(sampler.addressU);
602
+ writer.u8(sampler.addressV);
603
+ writer.u8(sampler.addressW);
604
+ writer.f32(sampler.mipLODBias);
605
+ writer.u8(sampler.maxAnisotropy);
606
+ writer.u8(sampler.comparisonFunc);
607
+ writer.u8(sampler.borderColor);
608
+ writer.f32(sampler.minLOD);
609
+ writer.f32(sampler.maxLOD);
610
+ }
611
+
612
+ /**
613
+ * Maps a pre-v10 register type byte onto the current register-type values
614
+ * (`Tr2EffectDescription.cpp:262-276`).
615
+ *
616
+ * @param {number} value Pre-v10 register type byte.
617
+ * @returns {number} Current register type value.
618
+ */
619
+ function mapOldRegisterType(value) {
620
+ if (value === 0) return 0;
621
+ if (value === 1) return 36;
622
+ if (value === 2) return 68;
623
+ if (value === 3) return 1;
624
+ return 36;
625
+ }
626
+
627
+ /**
628
+ * Reads the register signature and (v13+) static-sampler table
629
+ * (`ReadRegisters`, `Tr2EffectDescription.cpp:258-367`).
630
+ *
631
+ * Before v13 a register carries only its type and index; Carbon defaults the
632
+ * count to one and the space to the stage type (`:340-343`), and the record is
633
+ * normalized here so consumers see one shape.
634
+ *
635
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
636
+ * @param {number} version Container data version.
637
+ * @param {number} stageType Stage type owning the signature; only consulted
638
+ * for the pre-v13 register-space default.
639
+ * @returns {{registers:object[], staticSamplers:object[]}} Signature records.
640
+ */
641
+ function readSignature(reader, version, stageType) {
642
+ const registers = [];
643
+ const registerCount = reader.readUint8();
644
+ for (let index = 0; index < registerCount; index += 1) {
645
+ const rawType = reader.readUint8();
646
+ const registerType = version > 9 ? rawType : mapOldRegisterType(rawType);
647
+ const registerIndex = reader.readUint32();
648
+ if (version > 12) {
649
+ registers.push({
650
+ registerType,
651
+ registerIndex,
652
+ registerCount: reader.readUint32(),
653
+ registerSpace: reader.readUint8()
654
+ });
655
+ } else {
656
+ registers.push({
657
+ registerType,
658
+ registerIndex,
659
+ registerCount: 1,
660
+ registerSpace: stageType
661
+ });
662
+ }
663
+ }
664
+ const staticSamplers = [];
665
+ if (version > 12) {
666
+ const staticSamplerCount = reader.readUint8();
667
+ for (let index = 0; index < staticSamplerCount; index += 1) {
668
+ staticSamplers.push(readStaticSampler(reader));
669
+ }
670
+ }
671
+ return {
672
+ registers,
673
+ staticSamplers
674
+ };
675
+ }
676
+
677
+ /**
678
+ * Reads the resource half of a `StageData` block: constants, default constant
679
+ * values, textures, samplers, UAVs, annotations (`ReadInput`,
680
+ * `Tr2EffectDescription.cpp:369-472`).
681
+ *
682
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
683
+ * @param {number} version Container data version.
684
+ * @param {Function} makeError Error factory.
685
+ * @returns {object} Stage resource records.
686
+ */
687
+ function readStageResources(reader, version, makeError) {
688
+ const constants = [];
689
+ const constantCount = reader.readUint32();
690
+ for (let index = 0; index < constantCount; index += 1) {
691
+ constants.push(readConstant(reader, version));
692
+ }
693
+ const defaultValues = readBlobRef(reader);
694
+ const textures = [];
695
+ const textureCount = sanityCheck(reader.readUint8(), "textures", makeError);
696
+ for (let index = 0; index < textureCount; index += 1) {
697
+ const registerIndex = reader.readUint8();
698
+ textures.push({
699
+ registerIndex,
700
+ ...readTexture(reader, version)
701
+ });
702
+ }
703
+ const samplers = [];
704
+ const samplerCount = sanityCheck(reader.readUint8(), "samplers", makeError);
705
+ for (let index = 0; index < samplerCount; index += 1) {
706
+ const registerIndex = reader.readUint8();
707
+ samplers.push({
708
+ registerIndex,
709
+ ...readSampler(reader, version)
710
+ });
711
+ }
712
+ const uavs = [];
713
+ const uavCount = sanityCheck(reader.readUint8(), "uavs", makeError);
714
+ for (let index = 0; index < uavCount; index += 1) {
715
+ const registerIndex = reader.readUint8();
716
+ uavs.push({
717
+ registerIndex,
718
+ ...readUav(reader, version)
719
+ });
720
+ }
721
+ return {
722
+ constants,
723
+ defaultValues,
724
+ textures,
725
+ samplers,
726
+ uavs,
727
+ annotations: readAnnotations(reader)
728
+ };
729
+ }
730
+
731
+ /**
732
+ * Reads a whole `StageData` block: registers, static samplers, then the
733
+ * resource half (`StageData::Save` `EffectData.h:631-676`).
734
+ *
735
+ * Carbon's reader splits this single contiguous run across two functions —
736
+ * `ReadRegisters` takes the first two sub-records
737
+ * (`Tr2EffectDescription.cpp:258-367`) and `ReadInput` the remaining six
738
+ * (`:369-472`). The split exists here too, because before v14 the two halves
739
+ * are not adjacent on the wire — the signature precedes the program blob
740
+ * while the resources follow the thread-group size — and `readStage` places
741
+ * each half where its version put it.
742
+ *
743
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
744
+ * @param {number} version Container data version.
745
+ * @param {Function} makeError Error factory.
746
+ * @param {number} stageType Stage type owning the signature.
747
+ * @returns {object} Stage data record.
748
+ */
749
+ function readStageData(reader, version, makeError, stageType) {
750
+ return {
751
+ ...readSignature(reader, version, stageType),
752
+ ...readStageResources(reader, version, makeError)
753
+ };
754
+ }
755
+
756
+ /**
757
+ * Writes the `StageData` block.
758
+ *
759
+ * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
760
+ * @param {object} arena Arena resolver.
761
+ * @param {object} stageData Stage data record.
762
+ */
763
+ function writeStageData(writer, arena, stageData) {
764
+ writer.u8(stageData.registers.length);
765
+ for (const register of stageData.registers) {
766
+ writer.u8(register.registerType);
767
+ writer.u32(register.registerIndex);
768
+ writer.u32(register.registerCount);
769
+ writer.u8(register.registerSpace);
770
+ }
771
+ writer.u8(stageData.staticSamplers.length);
772
+ for (const sampler of stageData.staticSamplers) {
773
+ writeStaticSampler(writer, sampler);
774
+ }
775
+ writer.u32(stageData.constants.length);
776
+ for (const constant of stageData.constants) {
777
+ writeConstant(writer, arena, constant);
778
+ }
779
+ writeBlobRef(writer, arena, stageData.defaultValues);
780
+
781
+ // Carbon's compiler enforces none of these caps while its runtime rejects
782
+ // anything above them, so an over-large effect compiles and then fails to
783
+ // load. Check on the way out instead.
784
+ sanityCheck(stageData.textures.length, "textures", writeError);
785
+ sanityCheck(stageData.samplers.length, "samplers", writeError);
786
+ sanityCheck(stageData.uavs.length, "uavs", writeError);
787
+ writer.u8(stageData.textures.length);
788
+ for (const texture of stageData.textures) {
789
+ writer.u8(texture.registerIndex);
790
+ writeTexture(writer, arena, texture);
791
+ }
792
+ writer.u8(stageData.samplers.length);
793
+ for (const sampler of stageData.samplers) {
794
+ writer.u8(sampler.registerIndex);
795
+ writeSampler(writer, arena, sampler);
796
+ }
797
+ writer.u8(stageData.uavs.length);
798
+ for (const uav of stageData.uavs) {
799
+ writer.u8(uav.registerIndex);
800
+ writeUav(writer, arena, uav);
801
+ }
802
+ writeAnnotations(writer, arena, stageData.annotations);
803
+ }
804
+
805
+ /**
806
+ * Reads the pipeline-input list (`Tr2EffectDescription.cpp:234-256`).
807
+ *
808
+ * The `type`/`dimension` bytes joined the wire at v11; before that Carbon
809
+ * derives them — UINT for the blend-index usage, FLOAT otherwise, always four
810
+ * components (`:247-252`) — and the record is normalized here the same way.
811
+ *
812
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
813
+ * @param {number} version Container data version.
814
+ * @param {Function} makeError Error factory.
815
+ * @returns {object[]} Pipeline-input records.
816
+ */
817
+ function readPipelineInputs(reader, version, makeError) {
818
+ const pipelineInputs = [];
819
+ const pipelineInputCount = sanityCheck(reader.readUint8(), "pipelineInputs", makeError);
820
+ for (let index = 0; index < pipelineInputCount; index += 1) {
821
+ const usage = reader.readUint8();
822
+ const registerIndex = reader.readUint8();
823
+ const usageIndex = reader.readUint8();
824
+ const usedMask = reader.readUint8();
825
+ if (version > 10) {
826
+ pipelineInputs.push({
827
+ usage,
828
+ registerIndex,
829
+ usageIndex,
830
+ usedMask,
831
+ type: reader.readUint8(),
832
+ dimension: reader.readUint8()
833
+ });
834
+ } else {
835
+ pipelineInputs.push({
836
+ usage,
837
+ registerIndex,
838
+ usageIndex,
839
+ usedMask,
840
+ // Constant-type values: 2 is UINT, 0 is FLOAT; usage 6 is the
841
+ // blend-index semantic.
842
+ type: usage === 6 ? 2 : 0,
843
+ dimension: 4
844
+ });
845
+ }
846
+ }
847
+ return pipelineInputs;
848
+ }
849
+
850
+ /**
851
+ * Reads one stage (`StageInput::Save` `EffectData.h:691-709`,
852
+ * `Tr2EffectDescription.cpp:532-585`).
853
+ *
854
+ * At v15 the program payload comes first and the signature tables follow.
855
+ * Before v14 it was the other way round; the reorder is the v14 change, and
856
+ * Carbon marks its own v14 branch `// CHECK` (`:578`). Verified against the
857
+ * writer for v15: `type`, `shaderSize`, `shaderData`,
858
+ * `threadGroupSize[0..2]`, `pipelineInputs`, then `StageData`. Two further
859
+ * legacy wrinkles, both from Carbon's reader: a stage carries no register
860
+ * signature at all before v9 (`:544`), and two legacy dwords follow the
861
+ * program reference before v12, read and discarded (`:559-563`).
862
+ *
863
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
864
+ * @param {number} version Container data version.
865
+ * @param {Function} makeError Error factory.
866
+ * @returns {object} Stage record.
867
+ */
868
+ function readStage(reader, version, makeError) {
869
+ const type = reader.readUint8();
870
+ let pipelineInputs = [];
871
+ let signature = {
872
+ registers: [],
873
+ staticSamplers: []
874
+ };
875
+ if (version < 14) {
876
+ pipelineInputs = readPipelineInputs(reader, version, makeError);
877
+ if (version > 8) {
878
+ signature = readSignature(reader, version, type);
879
+ }
880
+ }
881
+ const shaderData = readBlobRef(reader);
882
+ if (version < 12) {
883
+ reader.readUint32();
884
+ reader.readUint32();
885
+ }
886
+ const threadGroupSize = [reader.readUint32(), reader.readUint32(), reader.readUint32()];
887
+ if (version >= 14) {
888
+ pipelineInputs = readPipelineInputs(reader, version, makeError);
889
+ signature = readSignature(reader, version, type);
890
+ }
891
+ return {
892
+ type,
893
+ shaderData,
894
+ threadGroupSize,
895
+ pipelineInputs,
896
+ ...signature,
897
+ ...readStageResources(reader, version, makeError)
898
+ };
899
+ }
900
+
901
+ /**
902
+ * Writes one stage.
903
+ *
904
+ * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
905
+ * @param {object} arena Arena resolver.
906
+ * @param {object} stage Stage record.
907
+ */
908
+ function writeStage(writer, arena, stage) {
909
+ sanityCheck(stage.pipelineInputs.length, "pipelineInputs", writeError);
910
+ writer.u8(stage.type);
911
+ writeBlobRef(writer, arena, stage.shaderData);
912
+ writer.u32(stage.threadGroupSize[0]);
913
+ writer.u32(stage.threadGroupSize[1]);
914
+ writer.u32(stage.threadGroupSize[2]);
915
+ writer.u8(stage.pipelineInputs.length);
916
+ for (const input of stage.pipelineInputs) {
917
+ writer.u8(input.usage);
918
+ writer.u8(input.registerIndex);
919
+ writer.u8(input.usageIndex);
920
+ writer.u8(input.usedMask);
921
+ writer.u8(input.type);
922
+ writer.u8(input.dimension);
923
+ }
924
+ writeStageData(writer, arena, stage);
925
+ }
926
+
927
+ /**
928
+ * Reads one pass (`Pass::Save` `EffectData.h:724-738`).
929
+ *
930
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
931
+ * @param {number} version Container data version.
932
+ * @param {Function} makeError Error factory.
933
+ * @param {boolean} backend Whether the optional trailing block is present.
934
+ * @returns {object} Pass record.
935
+ */
936
+ function readPass(reader, version, makeError, backend) {
937
+ const stages = [];
938
+ const stageCount = sanityCheck(reader.readUint8(), "stages", makeError);
939
+ for (let index = 0; index < stageCount; index += 1) {
940
+ stages.push(readStage(reader, version, makeError));
941
+ }
942
+ const renderStates = [];
943
+ const stateCount = sanityCheck(reader.readUint8(), "renderStates", makeError);
944
+ for (let index = 0; index < stateCount; index += 1) {
945
+ renderStates.push({
946
+ state: reader.readUint32(),
947
+ value: reader.readUint32()
948
+ });
949
+ }
950
+
951
+ // The one optional trailing block, present only in our own containers. A
952
+ // Carbon file ends the pass at the render-state table, so leaving this gate
953
+ // closed reproduces Carbon's bytes exactly.
954
+ const backendBlock = backend ? readBlobRef(reader) : null;
955
+ return {
956
+ stages,
957
+ renderStates,
958
+ ...(backendBlock ? {
959
+ backendBlock
960
+ } : {})
961
+ };
962
+ }
963
+
964
+ /**
965
+ * Writes one pass.
966
+ *
967
+ * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
968
+ * @param {object} arena Arena resolver.
969
+ * @param {object} pass Pass record.
970
+ * @param {boolean} backend Whether to emit the optional trailing block.
971
+ */
972
+ function writePass(writer, arena, pass, backend) {
973
+ writer.u8(pass.stages.length);
974
+ for (const stage of pass.stages) {
975
+ writeStage(writer, arena, stage);
976
+ }
977
+ writer.u8(pass.renderStates.length);
978
+ for (const entry of pass.renderStates) {
979
+ writer.u32(entry.state);
980
+ writer.u32(entry.value);
981
+ }
982
+ if (backend) {
983
+ writeBlobRef(writer, arena, pass.backendBlock ?? EMPTY_BLOB);
984
+ }
985
+ }
986
+
987
+ /**
988
+ * Reads one raytracing library (`Library::Save` `EffectData.h:760-775`,
989
+ * `Tr2EffectDescription.cpp:675-721`). A library carries two `StageData`
990
+ * blocks and no pipeline inputs, thread group size, or stage type.
991
+ *
992
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Source reader.
993
+ * @param {number} version Container data version.
994
+ * @param {Function} makeError Error factory.
995
+ * @returns {object} Library record.
996
+ */
997
+ function readLibrary(reader, version, makeError) {
998
+ const payloadSize = reader.readUint32();
999
+ const shaderData = readBlobRef(reader);
1000
+ const exports = [];
1001
+ const exportCount = reader.readUint32();
1002
+ for (let index = 0; index < exportCount; index += 1) {
1003
+ exports.push({
1004
+ type: reader.readUint8(),
1005
+ name: readStringRef(reader)
1006
+ });
1007
+ }
1008
+
1009
+ // Libraries exist only from v14, so the pre-v13 register-space default in
1010
+ // the signature reader is unreachable here; the stage type it would need
1011
+ // is passed as zero.
1012
+ return {
1013
+ payloadSize,
1014
+ shaderData,
1015
+ exports,
1016
+ hitGroupName: readStringRef(reader),
1017
+ globalInputs: readStageData(reader, version, makeError, 0),
1018
+ localInputs: readStageData(reader, version, makeError, 0)
1019
+ };
1020
+ }
1021
+
1022
+ /**
1023
+ * Writes one raytracing library.
1024
+ *
1025
+ * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
1026
+ * @param {object} arena Arena resolver.
1027
+ * @param {object} library Library record.
1028
+ */
1029
+ function writeLibrary(writer, arena, library) {
1030
+ writer.u32(library.payloadSize);
1031
+ writeBlobRef(writer, arena, library.shaderData);
1032
+ writer.u32(library.exports.length);
1033
+ for (const entry of library.exports) {
1034
+ writer.u8(entry.type);
1035
+ writer.u32(arena.string(entry.name));
1036
+ }
1037
+ writer.u32(arena.string(library.hitGroupName));
1038
+ writeStageData(writer, arena, library.globalInputs);
1039
+ writeStageData(writer, arena, library.localInputs);
1040
+ }
1041
+
1042
+ /**
1043
+ * Reads a whole v15 description blob (`EffectData::Save`
1044
+ * `EffectData.h:821-849`, `Tr2EffectDescription::Read`
1045
+ * `Tr2EffectDescription.cpp:476-731`).
1046
+ *
1047
+ * @param {import("../CjsByteReader.js").CjsByteReader} reader Reader positioned at the blob.
1048
+ * @param {object} [options] Read options.
1049
+ * @param {Function} [options.makeError] Error factory taking a message and details.
1050
+ * @param {number} [options.version] Container data version; defaults to the
1051
+ * current version. Accepted range is `CARBON_EFFECT_MIN_DATA_VERSION` to
1052
+ * `CARBON_EFFECT_DATA_VERSION`; the record tree comes out normalized to
1053
+ * the v15 shape whatever the wire carried.
1054
+ * @param {boolean} [options.backend] Expect our optional per-pass trailing block.
1055
+ * Leave false for a Carbon file, which ends each pass at the render states.
1056
+ * @returns {object} Description record tree.
1057
+ */
1058
+ function readEffectDescription(reader, options = {}) {
1059
+ const makeError = options.makeError ?? ((message, details) => reader._error(message, details));
1060
+ const backend = options.backend === true;
1061
+ const version = Number.isInteger(options.version) ? options.version : CARBON_EFFECT_DATA_VERSION;
1062
+ if (version < CARBON_EFFECT_MIN_DATA_VERSION || version > CARBON_EFFECT_DATA_VERSION) {
1063
+ throw makeError(`Unsupported Carbon effect version ${version}; expected ${CARBON_EFFECT_MIN_DATA_VERSION}..${CARBON_EFFECT_DATA_VERSION}`, {
1064
+ version
1065
+ });
1066
+ }
1067
+ const techniques = [];
1068
+ const techniqueCount = reader.readUint8();
1069
+ for (let techniqueIndex = 0; techniqueIndex < techniqueCount; techniqueIndex += 1) {
1070
+ const name = readStringRef(reader);
1071
+ const passes = [];
1072
+ const passCount = sanityCheck(reader.readUint8(), "passes", makeError);
1073
+ for (let passIndex = 0; passIndex < passCount; passIndex += 1) {
1074
+ passes.push(readPass(reader, version, makeError, backend));
1075
+ }
1076
+
1077
+ // The library table joined the wire at v14; older files end the
1078
+ // technique at its passes.
1079
+ const libraries = [];
1080
+ if (version > 13) {
1081
+ const libraryCount = reader.readUint8();
1082
+ for (let libraryIndex = 0; libraryIndex < libraryCount; libraryIndex += 1) {
1083
+ libraries.push(readLibrary(reader, version, makeError));
1084
+ }
1085
+ }
1086
+ techniques.push({
1087
+ name,
1088
+ passes,
1089
+ libraries
1090
+ });
1091
+ }
1092
+ const annotations = [];
1093
+ const parameterCount = sanityCheck(reader.readUint16(), "effectAnnotations", makeError);
1094
+ for (let index = 0; index < parameterCount; index += 1) {
1095
+ annotations.push({
1096
+ name: readStringRef(reader),
1097
+ annotations: readAnnotations(reader)
1098
+ });
1099
+ }
1100
+ requireExhaustive(reader, "description blob", makeError);
1101
+ return {
1102
+ techniques,
1103
+ annotations
1104
+ };
1105
+ }
1106
+
1107
+ /**
1108
+ * Writes a whole v15 description blob.
1109
+ *
1110
+ * The `arena` resolver decides what an arena reference becomes: `passthroughArena`
1111
+ * keeps the source offsets, `internArena(table)` re-interns into a new table.
1112
+ *
1113
+ * @param {import("../CjsByteWriter.js").CjsByteWriter} writer Target writer.
1114
+ * @param {object} description Description record tree.
1115
+ * @param {object} [options] Write options.
1116
+ * @param {object} [options.arena] Arena resolver; defaults to passthrough.
1117
+ * @param {boolean} [options.backend] Emit our optional per-pass trailing block.
1118
+ * Leave false to produce bytes a Carbon reader accepts unchanged.
1119
+ * @returns {import("../CjsByteWriter.js").CjsByteWriter} The writer.
1120
+ */
1121
+ function writeEffectDescription(writer, description, options = {}) {
1122
+ const arena = options.arena ?? passthroughArena;
1123
+ const backend = options.backend === true;
1124
+ writer.u8(description.techniques.length);
1125
+ for (const technique of description.techniques) {
1126
+ writer.u32(arena.string(technique.name));
1127
+ writer.u8(sanityCheck(technique.passes.length, "passes", writeError));
1128
+ for (const pass of technique.passes) {
1129
+ sanityCheck(pass.stages.length, "stages", writeError);
1130
+ sanityCheck(pass.renderStates.length, "renderStates", writeError);
1131
+ writePass(writer, arena, pass, backend);
1132
+ }
1133
+ writer.u8(technique.libraries.length);
1134
+ for (const library of technique.libraries) {
1135
+ writeLibrary(writer, arena, library);
1136
+ }
1137
+ }
1138
+ writer.u16(sanityCheck(description.annotations.length, "effectAnnotations", writeError));
1139
+ for (const parameter of description.annotations) {
1140
+ writer.u32(arena.string(parameter.name));
1141
+ writeAnnotations(writer, arena, parameter.annotations);
1142
+ }
1143
+ return writer;
1144
+ }
1145
+
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 };
1147
+ //# sourceMappingURL=carbonEffectRecords.js.map