@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,890 +1,890 @@
1
- # DXBC → GLSL ES 3.00 Lowering Spec: Memory-Structured Family
2
-
3
- Status: Evolving
4
- Scope: `@carbonenginejs/runtime-resource/formats/webgl` structured-memory lowering
5
- Audience: Shader translator maintainers and reviewers
6
- Summary: Defines bounded WebGL2 adaptations for DXBC structured-memory operations.
7
-
8
- Family key: `memory-structured`
9
- Target: GLSL ES 3.00 (WebGL2), vertex + pixel stages only (no compute, no SSBOs).
10
- Register model: every DXBC register is stored by the emitter as a `float` `vec4`; all
11
- integer/unsigned reads and writes go through `floatBitsToInt` / `floatBitsToUint` /
12
- `intBitsToFloat` / `uintBitsToFloat` at the use site, mirroring HLSLcc's own
13
- `GetBitcastOp` (`vendor/HLSLcc/src/toGLSLOperand.cpp:327-353`) and
14
- `AddOpAssignToDestWithMask` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:28-153`)
15
- machinery, which HLSLcc itself falls back to whenever static data-type analysis is
16
- unavailable (exactly the reflection-stripped situation this fork runs in per
17
- `vendor/HLSLcc/CARBONENGINEJS-FORK.md:34-52`).
18
-
19
- Corpus counts (450k-instruction sweep, 1611 EVE Online DX11 effects):
20
-
21
- | Opcode | Count | Stage reality |
22
- |---|---:|---|
23
- | `ld_structured` | 4014 | vs (BoneTransforms skinning) + ps (LightBuffer/LightIndexBuffer-style tbuffer reads) |
24
- | `store_structured` | 930 | compute-only in D3D11 (requires a UAV write target); **not observed as reachable in vs/ps stages actually shipped to WebGL2** — see per-opcode note |
25
- | `store_uav_typed` | 642 | compute-only in practice for this corpus (UAV write) |
26
- | `sync` | 183 | compute-only (`sync` only has meaning with `dcl_thread_group`) |
27
- | `ld_raw` | 54 | no confirmed vs/ps corpus example found (see per-opcode note); same SSBO problem as `ld_structured` if it does appear in vs/ps |
28
- | `store_raw` | 54 | compute-only in practice (RWByteAddressBuffer write) |
29
- | `atomic_iadd` | 27 | **confirmed present in a pixel shader** (`lensflareoccludert.sm_depth`, `stageName: "pixel"` in `dx11-instruction-coverage.json`'s `initialCandidateOpcodeExamples`) — not compute-only, see per-opcode note |
30
- | `imm_atomic_iadd` | 24 | compute-only (UAV atomic with previous-value return) |
31
- | `ld_uav_typed` | 18 | compute-only in this corpus — sampled instance is `measureexposure.sm_depth` (tone-mapping luminance compute pass), **not** `lensflareoccludert` (that file's confirmed opcode is `atomic_iadd`/`dcl_unordered_access_view_typed`, per `TRANSPILING-GAPS.md:100-114`); see per-opcode note |
32
- | `imm_atomic_exch` | 9 | compute-only |
33
- | `atomic_umax` | 6 | compute-only |
34
- | `atomic_umin` | 3 | compute-only |
35
- | `bufinfo` | 0 | not observed in this corpus; specified for completeness only |
36
-
37
- **Central WebGL2 constraint** (governs every opcode below): GLSL ES 3.00 has **no
38
- shader storage buffers** (`buffer` blocks require `#version 310 es` or GL 4.3+), **no
39
- image load/store types** (`image2D`/`imageLoad`/`imageStore` require ES 3.10+), and
40
- **no atomic-memory built-ins outside compute shaders** (`atomicAdd`/`imageAtomicAdd`
41
- etc. and `barrier()`/`memoryBarrier()` are ES 3.10 compute-shader built-ins, not part
42
- of the ES 3.00 vertex/fragment built-in set). Every HLSLcc GLSL template shown below
43
- is therefore **reference material for what desktop/Vulkan/Metal HLSLcc emits**, not
44
- directly compilable WebGL2 output. The one opcode with a proven, shipping WebGL2
45
- lowering is `ld_structured` restricted to the `BoneTransforms` skinning case, via the
46
- package-time `cb3` rewrite described in its section below.
47
-
48
- ---
49
-
50
- ## Shared machinery referenced by this family
51
-
52
- - `HaveUnsignedTypes(eLang)` and `HaveBitEncodingOps(eLang)`
53
- (`vendor/HLSLcc/src/internal_includes/languages.h:156-180`) both return `1` for
54
- every target except `LANG_ES_100`/`LANG_120`. `LANG_ES_300` (this project's
55
- target) has unsigned integer types and bit-encoding intrinsics
56
- (`floatBitsToInt`/`floatBitsToUint`/`intBitsToFloat`/`uintBitsToFloat`) available,
57
- so all the `TO_FLAG_UNSIGNED_INTEGER` / bitcast branches below are live for our
58
- target.
59
- - `AddAssignToDest` / `AddAssignPrologue`
60
- (`vendor/HLSLcc/src/toGLSLInstruction.cpp:155-171`) write the destination operand,
61
- the write mask, `= `, and the correct number of constructor/bitcast open-parens;
62
- `AddAssignPrologue` closes them and appends `;\n`. This is the generic
63
- "assign-with-implicit-bitcast" pattern the emitter must reproduce for `ld_structured`,
64
- `ld_raw`, `ld_uav_typed`, and `bufinfo`.
65
- - `_sat` (saturate) is applied **generically, after the main switch**, only to
66
- operand 0 (`vendor/HLSLcc/src/toGLSLInstruction.cpp:4821-4844`): it re-clamps
67
- `dest = clamp(dest, 0.0, 1.0)` (with an Adreno `min(max(dest,0.0),1.0)` workaround
68
- path gated behind `#ifdef UNITY_ADRENO_ES3`, since `eTargetLanguage == LANG_ES_300`
69
- triggers the workaround branch). This only matters for opcodes that write a
70
- register destination — i.e. `ld_structured`, `ld_raw`, `ld_uav_typed`, `bufinfo`,
71
- `imm_atomic_*` (previous-value destination). Store/`sync`/non-`imm_` atomics have
72
- no float destination and DXBC does not encode `_sat` for them in practice.
73
- - Structured/raw buffer declaration (`DeclareBufferVariable`,
74
- `vendor/HLSLcc/src/toGLSLDeclaration.cpp:1027-1092`) is the HLSLcc reference
75
- declaration these instructions index into:
76
- ```glsl
77
- struct t0_type { uint[<stride/4>] value; };
78
- layout(std430, binding = N) readonly buffer t0 { t0_type t0_buf[]; };
79
- ```
80
- (raw buffers use `uint t0_buf[]` / `int t0_buf[]` directly, no wrapper struct,
81
- selected by `HaveUnsignedTypes`). This declaration is unusable in GLSL ES 3.00 —
82
- documented here only because the per-instruction `t0_buf[...]` indexing expression
83
- it feeds is the exact text pattern the package-time `cb3` rewrite (see
84
- `ld_structured`) pattern-matches against.
85
-
86
- ---
87
-
88
- ## `ld_structured` (4014 instances) — highest priority in this family
89
-
90
- ### Semantics
91
- Loads a structured-buffer element: given a structure index (operand 1) and a
92
- byte offset within the structure (operand 2), reads one or more 32-bit components
93
- from resource operand 3 (a `t#`/`u#` structured buffer) into the destination
94
- register, per D3D11 `ld_structured` semantics (index-then-byte-offset addressing
95
- into an array of fixed-stride structures).
96
-
97
- ### GLSL lowering — A. HLSLcc reference (desktop/Vulkan/Metal SSBO path)
98
- `TranslateShaderStorageLoad`, case `OPCODE_LD_STRUCTURED`
99
- (`vendor/HLSLcc/src/toGLSLInstruction.cpp:1585-1683`, dispatched at
100
- `toGLSLInstruction.cpp:4256-4264`). Operands: `psDest=asOperands[0]`,
101
- `psSrcAddr=asOperands[1]` (struct index), `psSrcByteOff=asOperands[2]` (byte
102
- offset), `psSrc=asOperands[3]` (resource).
103
-
104
- For each destination component `c` present in the destination write mask:
105
- ```glsl
106
- <bitcast_open><resourceName>_buf[<structIndexExpr>].value[(<byteOffExpr> >> 2u) + <swz>u]<bitcast_close>
107
- ```
108
- where `<swz>` is `psSrc->aui32Swizzle[c]` if the resource operand carries an
109
- explicit swizzle (`OPERAND_4_COMPONENT_SWIZZLE_MODE`), else `c` itself
110
- (`toGLSLInstruction.cpp:1673`) — i.e. the *resource* operand's own swizzle can
111
- remap which dword of the structure element component `c` reads from. All such
112
- per-component expressions are joined by `AddAssignToDest`/constructor into:
113
- ```glsl
114
- dest.mask = <ctor>(comp0, comp1, ...);
115
- ```
116
- (`toGLSLInstruction.cpp:1623-1682`).
117
-
118
- `<bitcast_open>`/`<bitcast_close>` depend on `destDataType = psDest->GetDataType()`
119
- (`toGLSLInstruction.cpp:1642-1654`):
120
- - `SVT_FLOAT` → `uintBitsToFloat(...)` (since `HaveBitEncodingOps` is true for ES
121
- 3.00); the code falls back to `float(...)` only for pre-bit-encoding targets.
122
- - `SVT_INT`/`SVT_INT16`/`SVT_INT12` → `int(...)`.
123
- - `SVT_UINT` → no wrapper at all (`addedBitcast` stays `0`) because the backing
124
- `t0_type.value` array element type is always `uint` — "always uint array atm"
125
- (`toGLSLInstruction.cpp:1641`).
126
-
127
- The struct-index operand is translated with **both** `TO_FLAG_UNSIGNED_INTEGER |
128
- TO_FLAG_INTEGER` set simultaneously (`toGLSLInstruction.cpp:1664`) — reproduce
129
- this by picking whichever the index operand's own declared type implies (uint by
130
- default on ES 3.00). The byte-offset operand's flag (`srcOffFlag`) is
131
- `TO_FLAG_UNSIGNED_INTEGER` unless the target lacks unsigned types or the operand's
132
- own `SVT_INT`/`SVT_INT16`/`SVT_INT12` type forces `TO_FLAG_INTEGER`
133
- (`toGLSLInstruction.cpp:1618-1621`); when unsigned, both the `>> 2` and the `+
134
- <swz>` component addend get a trailing `u` suffix (`toGLSLInstruction.cpp:1669-1675`).
135
-
136
- ### GLSL lowering — B. WebGL2 `cb3` joint-matrix rewrite contract (the shipping path)
137
- This is the **only** `ld_structured` lowering this emitter must actually produce
138
- runnable WebGL2 GLSL for, restricted to the `BoneTransforms` skinning case
139
- (vertex stage). It is a two-stage pipeline:
140
-
141
- 1. Emit HLSLcc's reference SSBO GLSL exactly as in section A (this project's
142
- HLSLcc fork already tolerates missing `RDEF` bindings for
143
- `dcl_resource_structured` by falling back to the DXBC declaration's encoded
144
- stride — see `CARBONENGINEJS-FORK.md:41-44` and
145
- `TRANSPILING-GAPS.md:51-54`), producing text of the exact shape:
146
- ```glsl
147
- struct t0_type { uint[1] value; };
148
- layout(std430, binding = 0) readonly buffer t0 { t0_type t0_buf[]; };
149
- ...
150
- dest = vec4(uintBitsToFloat(t0_buf[idx].value[(0 >> 2) + 0]),
151
- uintBitsToFloat(t0_buf[idx].value[(0 >> 2) + 1]),
152
- uintBitsToFloat(t0_buf[idx].value[(0 >> 2) + 2]),
153
- uintBitsToFloat(t0_buf[idx].value[(0 >> 2) + 3]));
154
- ```
155
- Verified against an actual generated fixture
156
- (`../shaderdiscovery/artifacts/ab-shader-set/work/skinned_fxdirectionalv5/skinned_fxdirectionalv5.sm_hi.dxbc_29291f5662ed6781.vertex.es300.glsl:60-64`):
157
- all four components of one row share the **same** byte offset (row 0 uses
158
- `0`, row 1 uses `16`, row 2 uses `32` — i.e. the row's base byte offset, not
159
- a per-component offset of `0/4/8/12`), differing only in the trailing
160
- `+ 0/1/2/3` dword index, and **without** a `u` suffix on either the shift or
161
- the addend. The missing `u` is not a formatting nit: in this corpus the
162
- byte-offset operand is declared `SVT_INT` (not `SVT_UINT`), so
163
- `srcOffFlag` resolves to `TO_FLAG_INTEGER`
164
- (`toGLSLInstruction.cpp:1618-1621`), which suppresses the `u` suffix
165
- entirely (see `printImmediate32`, `toGLSLOperand.cpp:371-387` vs. `388-395`).
166
- This also matters functionally: `lowerStructuredBoneLoad`'s regex
167
- (`packageTr2WebglEffect.js:875`) matches literal `(\d+)\s*>>\s*2\s*\)` with
168
- no `u` tolerance, and its 4-component agreement check requires all four
169
- loads to share one `byteOffset` — a per-component-varying offset of
170
- `0/4/8/12` would both fail to match the regex *and* fail the "same
171
- byteOffset" agreement check, silently no-opping the rewrite. The previous
172
- worked example here was internally inconsistent with the very rewrite
173
- contract it was illustrating.
174
- 2. A package-time rewrite pass over the generated GLSL text, performing the
175
- steps below **in this exact order**.
176
-
177
- > **Superseded (2026-08-02).** This pass no longer exists. The emitter now
178
- > declares vertex-stage structured buffers as `std140` UBOs when it emits the
179
- > shader, with real bindings, rather than rewriting text afterwards — see
180
- > `DxbcGlslEmitter.js`, the `dcl_resource_structured` vertex branch. The
181
- > regex functions it describes were deleted from
182
- > `scripts/packageTr2WebglEffect.js`, so the line citations below point at
183
- > nothing. The section is kept because it records *why* each rewrite was
184
- > needed, which the ccpwgl runtime ABI still constrains; read it as history,
185
- > not as a description of the code.
186
- 1. Strip the `#ifdef GL_ARB_shader_storage_buffer_object` /
187
- `GL_ARB_shader_image_load_store` extension guard blocks (regexes at
188
- `packageTr2WebglEffect.js:832-833`).
189
- 2. Strip the `struct t0_type { uint[1] value; };` declaration (regex at
190
- `packageTr2WebglEffect.js:834`, matches only the 1-word/4-byte stride
191
- shape).
192
- 3. Strip the `layout(std430, binding = 0) readonly buffer t0 { t0_type
193
- t0_buf[]; };` declaration (regex at `packageTr2WebglEffect.js:835`).
194
- 4. Grow the vertex stage's `ConstantBuffer3` (`cb3`) declaration to
195
- `vec4 data[max(existingSize, 200)]` (`packageTr2WebglEffect.js:836-839`) —
196
- this is where `JointMat` lives at `cb3.data[26..199]` per the ccpwgl runtime
197
- ABI (`cb3[26..199]`, 58 joints × 12 floats = 696 floats,
198
- `AGENT-FINDINGS/decisions/016-cewg-skinning-abi-lowering-for-ccpwgl-2026-06-30.md`
199
- and `015-joint-matrix-jointmat-findings-ccpwgl-runtime-truth.md`).
200
- 5. Convert `uvec4`/`ivec4` (or `uvec2/3`, `ivec2/3`) `in_BLENDINDICES<n>` vertex
201
- inputs to plain `vec<N>` (regex at `packageTr2WebglEffect.js:840-843`) —
202
- ccpwgl binds GR2 mesh blend indices as float attributes via
203
- `vertexAttribPointer`, not `vertexAttribIPointer`
204
- (`AGENT-FINDINGS/decisions/028-cewg-skinned-blend-index-abi-lowering.md`).
205
- 6. Strip the DX11 global bone-offset add HLSLcc emits when combining
206
- `in_BLENDINDICES0` with a `cb3.data[26]` offset uniform (two regex forms at
207
- `packageTr2WebglEffect.js:845-852`, covering both the scalar `int(...) +
208
- floatBitsToInt(cb3.data[26].x)` shape and the vector `(i)uvec/ivec(...) +
209
- floatBitsToInt(cb3.data[26].<swz>)` shape) — ccpwgl's `cb3.data[26]` slot is
210
- reused for `JointMat`, not the native bone-ring-buffer offset, so this add
211
- must be removed entirely rather than merely rewired.
212
- 7. Rewrite each 4-component `t0_buf[...]` row-load expression
213
- (`vec4(t0_buf[idx].value[(byteOff>>2)+0], ...)`, all 4 components required,
214
- `lowerStructuredBoneLoad`) into:
215
- ```glsl
216
- cb3.data[26 + (<idx>) * 3 + <row>]
217
- ```
218
- where `<row> = floor(byteOff / 16)` and must be in `{0,1,2}` (a `Float4x3`
219
- bone matrix is 3 `vec4` rows); if any of the 4 component sub-expressions
220
- disagree on `idx`/`byteOff`, or `row` falls outside `0..2`, or the swizzle
221
- is anything but the identity `xyzw`/`.xyzw` is stripped when default,
222
- **the rewrite silently no-ops and leaves the un-lowered `t0_buf` text in
223
- place** (`packageTr2WebglEffect.js:886,890,895,898` all `return match`) —
224
- this is a silent-failure edge case the implementing engineer must guard
225
- against (add a diagnostic) rather than trust to fail loudly.
226
- 8. If any rewrite happened, inject a `// Carbon WebGL: BoneTransforms lowered to cb3
227
- JointMat rows.` marker comment after `#version 300 es`
228
- (`packageTr2WebglEffect.js:859-861`).
229
-
230
- ### GLSL lowering — C. `ld_structured` in pixel stage / non-skinning resources
231
- The corpus also uses `ld_structured` on `t#` "packed tbuffer" resources unrelated
232
- to skinning — e.g. `LightBuffer`/`LightIndexBuffer` reads in pixel shaders such as
233
- `decalcylindricv5.sm_depth` (`AGENT-FINDINGS/decisions/005-structured-resource-only-opcodes-2026-06-26.md`).
234
- **No `cb3`-style *functional* ABI rewrite exists for these** — they hit the same
235
- SSBO-unavailability wall as section A. The emitter's default path lowers them to
236
- pixel `usampler2D` data textures (see `DxbcGlslEmitter.js` `dcl_resource_structured`
237
- pixel branch), which compiles but consumes a texture unit each; on real drivers
238
- the `_depth` quad variants overflow `MAX_TEXTURE_IMAGE_UNITS`(16).
239
-
240
- **Superseded (2026-08-02) — the lights fit, and are kept.** The two light
241
- buffers now lower to a single packed `RGBA32UI` data texture rather than one
242
- texture each, which frees two units, and the `Detail1/2/3Map` textures merge into
243
- one array texture, which frees one or two more. Both `.sm_depth` quad variants
244
- land at or under 16 with lighting intact: `unpackedskinned_quaddetailv5` at 15,
245
- `unpackedskinned_quadheatdetailv5` at 16. See
246
- `/docs/contracts/webgl2-texture-budget.md`. Dropping the lights is still
247
- available as `--stub-light-resources` for isolating a lighting problem, but it is
248
- no longer the answer to the budget. The paragraph below describes that opt-in
249
- path and remains accurate.
250
-
251
- **Earlier resolution (2026-07-08) — stub, not rewrite.** Since Carbon WebGL does not
252
- support this tiled lighting, the packager can DROP it instead of lowering it. Run
253
- `packageTr2WebglEffect.js --stub-light-resources`: it resolves the light resource
254
- names (`LightBuffer`, `LightIndexBuffer`, `LightProfileArray`) to `t#` registers
255
- from the Carbon `.sm` reflection (RDEF is stripped, so names live only there —
256
- and the registers vary per permutation, so this is name-driven, not fixed to
257
- sb11/sb12/s13) and passes them to `emitGlsl` as `stubResourceRegisters`. The
258
- emitter then drops their decl+binding and lowers reads to `uintBitsToFloat(0u)`
259
- (structured) / `vec4(0.0)` (sampled) — zeroing the per-tile light count makes the
260
- light loop dead. The packager also strips those `resource` bindings from the
261
- manifest JSON (`stripLightResourcesFromManifest`) so the Carbon WebGL runtime does not
262
- synthesize a texture def (glType 0 → "Invalid shader texture definition") for the
263
- now-undeclared light buffers. Opt-in, default off; every other package is
264
- unchanged. A functional light constant-buffer path remains possible but was not
265
- built.
266
-
267
- Tested by `test/glsl-emitter.test.js` (synthetic pixel shaders with a structured
268
- buffer / sampler2DArray: declared by default, dropped + no binding when the
269
- register is in `stubResourceRegisters`, and only listed registers dropped) and
270
- `test/stub-light-resources.test.js` (the packager's `resolveStubLightRegisters`
271
- name→register resolution and `stripLightResourcesFromManifest` manifest filter,
272
- in `scripts/stubLightResources.js`).
273
-
274
- ### Type rules
275
- - Struct index operand: read as int/uint (both flags set in HLSLcc; pick uint by
276
- default for ES 3.00).
277
- - Byte-offset operand: uint unless the operand's own declared type is signed int.
278
- - Result component type follows the *destination* register's inferred type
279
- (float → `uintBitsToFloat`, int → `int(...)`, uint → passthrough), **not** the
280
- source resource's declared return type — this is purely dest-driven, matching
281
- the "everything is `float vec4`, bitcast at use" register model this project
282
- already commits to.
283
- - This is a data-movement instruction, not a comparison — it does not produce a
284
- 0xFFFFFFFF/0 mask.
285
-
286
- ### Helpers needed
287
- - `structuredLoadComponent(bufName, structIndex, byteOffset, component, destType)`
288
- — reference-only (HLSLcc SSBO shape), needed if the emitter ever targets a
289
- non-WebGL2 backend or documents the pre-rewrite intermediate form.
290
- - `lowerBoneTransformsToCb3` (package-time text pass; port of
291
- `lowerWebgl2SkinningAbi` + `lowerStructuredBoneLoad`).
292
- - `lowerBlendIndicesToFloatAttribute` (package-time text pass, part of the same
293
- rewrite; port of the `in_BLENDINDICES` regex).
294
- - `refuseNonSkinningStructuredLoad` (detection helper for section C).
295
-
296
- ### Edge cases
297
- - NaN/inf: none introduced by the load itself; `uintBitsToFloat` is a pure
298
- bit-reinterpret, so any NaN bit pattern already in the buffer round-trips as
299
- NaN.
300
- - The resource-operand swizzle indirection (`psSrc->aui32Swizzle[component]`)
301
- means component `c` of the destination is not guaranteed to read structure
302
- dword `c` — verify this against real corpus DXBC before assuming identity
303
- swizzle always holds.
304
- - The row/index-agreement check in `lowerStructuredBoneLoad` requires **all 4**
305
- components to share the same `idx`/`byteOffset`; a destination write mask
306
- narrower than `.xyzw` (e.g. `.xy`) will not match the 4-load regex and will
307
- silently fail to rewrite — this is plausible for shaders that only need part of
308
- a bone row and needs explicit test coverage.
309
- - `bSaturate` on `ld_structured` is legal per the generic post-switch handling
310
- but is not expected to appear in real bone/light-buffer loads; still must be
311
- implemented for correctness if the corpus is ever re-scanned with `_sat`
312
- detection.
313
-
314
- ### WebGL2 notes
315
- - SSBOs (`buffer` blocks) do not exist in GLSL ES 3.00 at all; section A's
316
- output is fundamentally uncompilable in WebGL2 and must never reach the
317
- final package unless rewritten by section B.
318
- - `layout(std430, binding=N)` is likewise unavailable in ES 3.00 (`std430` and UBO
319
- binding indices exist, but SSBO binding does not).
320
-
321
- ### Confidence
322
- **High** for the `BoneTransforms`/`cb3` path (validated end-to-end:
323
- `336/336` and `240/240` WebGL2 program links per
324
- `TRANSPILING-GAPS.md:60-63` and `016-cewg-skinning-abi-lowering-for-ccpwgl-2026-06-30.md`).
325
- **Low** for non-skinning `ld_structured` in pixel stages — no validated WebGL2
326
- lowering exists; treat as blocked pending a decision.
327
-
328
- ---
329
-
330
- ## `store_structured` (930 instances)
331
-
332
- ### Semantics
333
- Writes one or more 32-bit components of a structured-buffer element addressed by
334
- a structure index and byte offset, per D3D11 `store_structured` (the inverse of
335
- `ld_structured`; only valid against a UAV, since D3D11 `structured buffer`
336
- read-only resources cannot be written).
337
-
338
- ### GLSL lowering
339
- `TranslateShaderStorageStore`, case `OPCODE_STORE_STRUCTURED`
340
- (`vendor/HLSLcc/src/toGLSLInstruction.cpp:1500-1583`, dispatched at
341
- `toGLSLInstruction.cpp:4348-4357`). Operands: `psDest=asOperands[0]` (the UAV
342
- resource, carrying the write mask via `OPERAND_4_COMPONENT_MASK_MODE`),
343
- `psDestAddr=asOperands[1]` (struct index), `psDestByteOff=asOperands[2]` (byte
344
- offset), `psSrc=asOperands[3]` (value to store).
345
-
346
- For each component present in `psDest->ui32CompMask`:
347
- ```glsl
348
- <name>_buf[<structIndexExpr>].value[(<byteOffExpr> >> 2u) + <comp>u] = <uint-or-int-cast>(src.<swz-or-x>);
349
- ```
350
- (`toGLSLInstruction.cpp:1534-1582`). `dstOffFlag` follows the same
351
- unsigned-unless-signed-typed rule as `ld_structured`. The source cast (`srcFlag`)
352
- is `TO_FLAG_UNSIGNED_INTEGER` by default, flipped to `TO_FLAG_INTEGER` only when
353
- `DeclareRWStructuredBufferTemplateTypeAsInteger` reports the target buffer as a
354
- single-`int`-typed `RWStructuredBuffer<int4>` (the "avoid calling the wrong
355
- `AtomicMin` overload" special case, `toGLSLDeclaration.cpp:998-1025`) — otherwise
356
- the destination array element type is always `uint` (`toGLSLInstruction.cpp:1569-1572`).
357
- Source components are consumed left-to-right via an incrementing `srcComponent`
358
- counter if the source operand has more than one swizzle element, else always
359
- `.x` (`toGLSLInstruction.cpp:1575-1578`).
360
-
361
- ### Type rules
362
- - Destination index/byte-offset: same int/uint rule as `ld_structured`.
363
- - Source value: bitcast to uint (default) or int (special RWStructuredBuffer<int>
364
- case) before storing — never stored as float, consistent with "backing array is
365
- always uint".
366
-
367
- ### Helpers needed
368
- - `structuredStoreComponent(bufName, structIndex, byteOffset, component, value,
369
- srcType)` — reference-only; no WebGL2 target exists for this opcode (see below).
370
-
371
- ### Edge cases
372
- - No destination register write, so `_sat` never applies (DXBC does not attach
373
- `_sat` to store instructions).
374
- - Per-component write masking must exactly follow `psDest->ui32CompMask`
375
- (`OPERAND_4_COMPONENT_MASK_MODE`), not the source operand's own mask.
376
-
377
- ### WebGL2 notes
378
- `store_structured` requires a writable UAV, which in turn requires an SSBO
379
- (`buffer`, not `readonly buffer`) — completely unavailable in GLSL ES 3.00. No
380
- package-time rewrite analogous to the `cb3` skinning path exists for writes
381
- (there is no ccpwgl uniform target that plausibly receives a per-invocation
382
- compute-style scatter write). **This opcode is out of scope for the WebGL2
383
- emitter.** The 930 corpus instances should be treated as evidence this opcode
384
- occurs in DX11 stages that are not shipped to the current WebGL2 vs/ps target
385
- (the sampled corpus instance is `createhistograms.sm_depth`, tagged
386
- `stageName: "geometry"` in `dx11-instruction-coverage.json` but much more
387
- plausibly a mislabeled compute shader given the histogram-building workload
388
- and its co-occurring `sync`/`dcl_thread_group`-shaped instruction in the same
389
- file — see the `atomic_iadd` section for the one confirmed **non**-compute
390
- counterexample in this family, `lensflareoccludert`, which is a pixel shader,
391
- not compute); the emitter should detect and refuse rather than attempt
392
- emission.
393
-
394
- ### Confidence
395
- **Medium** on the HLSLcc reference lowering itself (directly read from source);
396
- **high** on the WebGL2-scope conclusion (no counter-evidence of a vs/ps
397
- `store_structured` shipping shader was found in the decision corpus, and the
398
- project's own draft transpiler already special-cases the sibling UAV write
399
- opcode `store_uav_typed` as a target blocker — see that section).
400
-
401
- ---
402
-
403
- ## `store_uav_typed` (642 instances)
404
-
405
- ### Semantics
406
- Writes a full-precision (or format-converted) texel/element to a typed UAV
407
- (`RWTexture*`/`RWBuffer`) at an integer address, per D3D11 `store_uav_typed`.
408
-
409
- ### GLSL lowering
410
- Case `OPCODE_STORE_UAV_TYPED` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:4359-4415`):
411
- ```glsl
412
- imageStore(<uavName>, <addr-expr-by-dimension>, <value-cast-by-return-type>);
413
- ```
414
- The address component mask and any `TO_AUTO_EXPAND_TO_VEC{2,3,4}` flag are chosen
415
- from the UAV's reflected `REFLECT_RESOURCE_DIMENSION_*`
416
- (`toGLSLInstruction.cpp:4382-4407`): 1D/Buffer → `.x` only; 2D/1DArray/2DMS →
417
- `.xy` with `TO_AUTO_EXPAND_TO_VEC2`; 2DArray/3D/2DMSArray/Cube → `.xyz` with
418
- `TO_AUTO_EXPAND_TO_VEC3`; CubeArray → `TO_AUTO_EXPAND_TO_VEC4`. The stored value
419
- is translated with `ResourceReturnTypeToFlag(psRes->ui32ReturnType)` — i.e. cast
420
- to match the UAV's declared return type (float/int/uint).
421
-
422
- ### Type rules
423
- Value operand bitcast is driven entirely by the UAV's reflected return type
424
- (`RETURN_TYPE_FLOAT`/`SINT`/`UINT`/`UNORM`/`SNORM`), not by any DXBC instruction
425
- flag — this requires resource-binding reflection, which per
426
- `CARBONENGINEJS-FORK.md:34-39` is frequently stripped in this project's shipped
427
- DXBC and falls back to register-stable naming only (no return-type recovery
428
- implied by that fallback).
429
-
430
- ### Helpers needed
431
- None for the WebGL2 emitter proper — see WebGL2 notes. Reference-only helper:
432
- `imageStoreTyped(uav, addr, value, returnType)`.
433
-
434
- ### Edge cases
435
- - `imageStore` swizzle/expansion must match the UAV's declared dimensionality
436
- exactly, or GLSL will reject a texel with the wrong component count.
437
- - Missing `RDEF` reflection (this project's normal stripped-DXBC case) leaves the
438
- return type unrecoverable, which independently blocks this opcode even before
439
- the SSBO/image-type gap is considered.
440
-
441
- ### WebGL2 notes
442
- `image2D`/`imageBuffer` UAV types and `imageStore` are GLSL ES 3.10+ built-ins,
443
- not part of ES 3.00. **Fully out of scope for the WebGL2 emitter.** This matches
444
- the project's own draft-transpiler decision, which already lists
445
- `store_uav_typed` as a hard `TARGET_BLOCKER_OPCODES` entry
446
- (`../shaderdiscovery/src/core/transpiler/gles/Dx11GlesDraftTranspiler.js:130`) and
447
- the `TRANSPILING-GAPS.md:100-114` "UAV and atomic path... blocked for current
448
- WebGL2 target" decision. The emitter must detect `dcl_unordered_access_view_*`
449
- declarations plus this opcode and refuse the stage (or the whole effect) with an
450
- explicit diagnostic, never attempt best-effort emission.
451
-
452
- ### Confidence
453
- **High** — corroborated independently by HLSLcc source, the draft transpiler's
454
- explicit blocker list, and the shaderdiscovery decision log.
455
-
456
- ---
457
-
458
- ## `sync` (183 instances)
459
-
460
- ### Semantics
461
- A compute-shader thread-group synchronization barrier. DXBC encodes which memory
462
- domains/threads to synchronize via `ui32SyncFlags`: `SYNC_THREAD_GROUP_SHARED_MEMORY`,
463
- `SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GROUP`/`_GLOBAL`, and `SYNC_THREADS_IN_GROUP`.
464
- It only has defined meaning inside a compute shader with a `dcl_thread_group` size.
465
-
466
- ### GLSL lowering
467
- Case `OPCODE_SYNC` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:3958-3984`):
468
- ```glsl
469
- if (flags & SYNC_THREAD_GROUP_SHARED_MEMORY) memoryBarrierShared();
470
- if (flags & (SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GROUP|_GLOBAL)) memoryBarrier();
471
- if (flags & SYNC_THREADS_IN_GROUP) barrier();
472
- ```
473
- Each condition independently emits its statement (not mutually exclusive; a
474
- single `sync` instruction can emit all three lines).
475
-
476
- ### Type rules
477
- N/A — no operands, no data type.
478
-
479
- ### Helpers needed
480
- None — this opcode has no GLSL ES 3.00 equivalent to helper-wrap; see WebGL2
481
- notes.
482
-
483
- ### Edge cases
484
- None beyond the flag decoding itself (a bitmask, not an enum — must check all
485
- three bits independently, not `switch`/`else if`).
486
-
487
- ### WebGL2 notes
488
- `barrier()`, `memoryBarrier()`, and `memoryBarrierShared()` are **compute-shader-
489
- only** built-ins in GLSL ES (introduced with ES 3.10 compute shaders); they do
490
- not exist in the ES 3.00 vertex/fragment built-in set at all, and vertex/fragment
491
- shaders have no thread-group concept regardless of GLSL version. **Fully out of
492
- scope for the WebGL2 emitter.** Detect `sync` (and its precondition,
493
- `dcl_thread_group`) and refuse the stage.
494
-
495
- ### Confidence
496
- **High** — the DXBC semantics and the compute-only nature of `barrier`/
497
- `memoryBarrier` in GLSL ES are unambiguous.
498
-
499
- ---
500
-
501
- ## `ld_raw` (54 instances)
502
-
503
- ### Semantics
504
- Reads one or more 32-bit components from a raw (`ByteAddressBuffer`) resource at
505
- a byte offset, per D3D11 `ld_raw` — same addressing model as `ld_structured` but
506
- without a structure index (flat byte-addressed array).
507
-
508
- ### GLSL lowering
509
- Same function as `ld_structured`, `TranslateShaderStorageLoad`, case
510
- `OPCODE_LD_RAW` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:1602-1606`, dispatched
511
- at `toGLSLInstruction.cpp:4416-4425`). Operands: `psDest=asOperands[0]`,
512
- `psSrcByteOff=asOperands[1]`, `psSrc=asOperands[2]` — no `psSrcAddr`, so the
513
- `TranslateShaderStorageLoad` body skips the `[<idx>].value` indirection
514
- entirely (`toGLSLInstruction.cpp:1661-1666`, gated on `if (psSrcAddr)`), producing:
515
- ```glsl
516
- <bitcast_open><name>_buf[(<byteOffExpr> >> 2u) + <comp>u]<bitcast_close>
517
- ```
518
- against a flat `uint`/`int` array (`vendor/HLSLcc/src/toGLSLDeclaration.cpp:1078-1088`,
519
- `isRaw` branch: `uint <name>_buf[];` or `int <name>_buf[];` chosen by
520
- `HaveUnsignedTypes`), not a `_type` struct array.
521
-
522
- ### Type rules
523
- Identical bitcast-by-destination-type rule as `ld_structured` (float →
524
- `uintBitsToFloat`, int → `int(...)`, uint → passthrough).
525
-
526
- ### Helpers needed
527
- - `rawLoadComponent(bufName, byteOffset, component, destType)` — reference-only;
528
- see WebGL2 notes for scope.
529
-
530
- ### Edge cases
531
- Same swizzle-indirection caveat as `ld_structured` (component addressing follows
532
- the resource operand's own swizzle if present).
533
-
534
- ### WebGL2 notes
535
- Same SSBO unavailability as `ld_structured` section A/C. No `cb3`-style rewrite
536
- is known or defined for raw-buffer reads — the `BoneTransforms` rewrite is
537
- specific to the structured-buffer `t0_buf[idx].value[...]` shape, not the flat
538
- `t0_buf[...]` raw shape. **Out of scope for the WebGL2 emitter** unless/until a
539
- specific raw-buffer resource is proven to need a package-time ABI rewrite
540
- analogous to skinning.
541
-
542
- The corpus table's "vs/ps" stage claim was overreach: the only sampled
543
- instance in `dx11-instruction-coverage.json`'s `allOpcodeExamples` is
544
- `graphics\effect.dx11\managed\space\specialfx\particles\gpu\emit.sm_depth`
545
- tagged `stageName: "geometry"`, paired with the sibling `store_raw` opcode in
546
- the exact same file/technique (also tagged `"geometry"`) — a GPU particle
547
- emission pass, which is much more plausibly a compute-style workload than a
548
- genuine vertex/pixel `ByteAddressBuffer` read. Do not assume `ld_raw` is
549
- `vs/ps`-reachable without a concrete counter-example (unlike `atomic_iadd`,
550
- which has one — see that section).
551
-
552
- ### Confidence
553
- **Medium** — HLSLcc source lowering is directly read and clear, but no shipping
554
- corpus evidence of `ld_raw` reaching a WebGL2-validated vs/ps program was found
555
- (only 54 instances total, none flagged in the skinning/JointMat decision
556
- trail); the sole sampled corpus example is paired with `store_raw` in a
557
- likely-compute GPU-particle-emission shader, not a vs/ps stage.
558
-
559
- ---
560
-
561
- ## `store_raw` (54 instances)
562
-
563
- ### Semantics
564
- Writes one or more 32-bit components to a raw UAV (`RWByteAddressBuffer`) at a
565
- byte offset, per D3D11 `store_raw` — inverse of `ld_raw`.
566
-
567
- ### GLSL lowering
568
- Same function as `store_structured`, `TranslateShaderStorageStore`, case
569
- `OPCODE_STORE_RAW` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:1519-1523`,
570
- dispatched at `toGLSLInstruction.cpp:4338-4346`). Operands: `psDest=asOperands[0]`,
571
- `psDestByteOff=asOperands[1]`, `psSrc=asOperands[2]` — no `psDestAddr`, so no
572
- `[<idx>].value` indirection (`toGLSLInstruction.cpp:1546-1551`, gated on `if
573
- (psDestAddr)`):
574
- ```glsl
575
- <name>_buf[(<byteOffExpr> >> 2u) + <comp>u] = <uint-or-int-cast>(src.<swz-or-x>);
576
- ```
577
-
578
- ### Type rules
579
- Same as `store_structured`: uint by default, int only for the special
580
- single-int `RWStructuredBuffer<int4>`-shaped buffer detection (which does not
581
- actually apply to raw buffers in practice, since that check is structured-buffer
582
- specific, but the code path is shared).
583
-
584
- ### Helpers needed
585
- - `rawStoreComponent(bufName, byteOffset, component, value, srcType)` —
586
- reference-only.
587
-
588
- ### Edge cases
589
- No destination register, so `_sat` never applies.
590
-
591
- ### WebGL2 notes
592
- Requires a writable UAV/SSBO — unavailable in ES 3.00. **Out of scope for the
593
- WebGL2 emitter**; treat identically to `store_structured` (detect and refuse).
594
-
595
- ### Confidence
596
- **Medium** — same reasoning as `ld_raw`; low corpus volume (54) with no
597
- known vs/ps shipping path.
598
-
599
- ---
600
-
601
- ## `atomic_iadd` (27) / `imm_atomic_iadd` (24) / `imm_atomic_exch` (9) / `atomic_umax` (6) / `atomic_umin` (3)
602
-
603
- Grouped: all five are handled by the single `TranslateAtomicMemOp` function and
604
- differ only in GLSL function name and whether a previous-value destination
605
- exists.
606
-
607
- ### Semantics
608
- - `atomic_iadd` / `imm_atomic_iadd`: atomically add a value to a UAV or
609
- groupshared (TGSM) memory location; the `imm_` form additionally returns the
610
- pre-add value into a destination register, the non-`imm_` form discards it.
611
- - `imm_atomic_exch`: atomically replace the memory location's value and return
612
- the previous value (exchange has no non-`imm_` counterpart in DXBC).
613
- - `atomic_umax` / `atomic_umin`: atomically store `max`/`min` of the current
614
- value and the source value (unsigned comparison), discarding the previous
615
- value (no `imm_` variants observed in this corpus, though DXBC defines
616
- `imm_atomic_umax`/`umin` too).
617
-
618
- ### GLSL lowering
619
- `TranslateAtomicMemOp` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:1685-2087`,
620
- dispatched at `toGLSLInstruction.cpp:4428-4450`). Per-opcode operand layout and
621
- GLSL function name selected at the top of the function
622
- (`toGLSLInstruction.cpp:1700-1935`):
623
-
624
- | Opcode | `func` | operands (dest addr order) |
625
- |---|---|---|
626
- | `imm_atomic_iadd` | `"Add"` | `[0]=previousValue, [1]=dest, [2]=destAddr, [3]=src` |
627
- | `atomic_iadd` | `"Add"` | `[0]=dest, [1]=destAddr, [2]=src` (no previousValue) |
628
- | `imm_atomic_exch` | `"Exchange"` | `[0]=previousValue, [1]=dest, [2]=destAddr, [3]=src` |
629
- | `atomic_umin` | `"Min"` | `[0]=dest, [1]=destAddr, [2]=src` |
630
- | `atomic_umax` | `"Max"` | `[0]=dest, [1]=destAddr, [2]=src` |
631
-
632
- Resource-kind detection (`toGLSLInstruction.cpp:1968-2011`): if `dest` is not
633
- `OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY`, look up its UAV binding.
634
- `RTYPE_UAV_RWTYPED` → `isUint = (returnType == RETURN_TYPE_UINT)` and derive
635
- `texDim` (1/2/3) from the UAV's resource dimension; `RTYPE_UAV_RWSTRUCTURED` →
636
- `isUint=false, ui32DstDataTypeFlag |= TO_FLAG_INTEGER` only if
637
- `DeclareRWStructuredBufferTemplateTypeAsInteger` says so; TGSM defaults to
638
- `isUint=true, texDim=0`. Then:
639
- ```glsl
640
- ui32DataTypeFlag = isUint ? (TO_FLAG_UNSIGNED_INTEGER|TO_AUTO_BITCAST_TO_UINT)
641
- : (TO_FLAG_INTEGER|TO_AUTO_BITCAST_TO_INT);
642
- ```
643
- (`toGLSLInstruction.cpp:2013-2016`). If `previousValue` exists, it's assigned via
644
- `AddAssignToDest(previousValue, isUint?SVT_UINT:SVT_INT, 1, ...)` **before** the
645
- atomic call text is emitted (`toGLSLInstruction.cpp:2018-2019`). Final emitted
646
- shape (`toGLSLInstruction.cpp:2021-2086`):
647
- - Typed-UAV (image) form (`texDim>0`):
648
- ```glsl
649
- [prev =] imageAtomic<Func>(<uavName>, <addr>.<xy|xyz|x>, <src-cast>);
650
- ```
651
- - Buffer/TGSM form (`texDim==0`):
652
- ```glsl
653
- [prev =] atomic<Func>(<name>[_buf][<addr> >> 2u], <src-cast>);
654
- ```
655
- where `<name>_buf[<addr>]` gains an extra `.value[<addrY> >> 2u]` indirection
656
- if the destAddr operand carries **two** swizzle components (X and Y) — "structured
657
- buf if we have both x & y swizzles" (`toGLSLInstruction.cpp:2055-2062`) — vs. a
658
- raw buffer's single-component addressing.
659
- - No trailing `;` is appended when `previousValue` is set (the assignment prologue
660
- handles closing parens/semicolon via `AddAssignPrologue`); otherwise the line
661
- is terminated with `;\n` directly (`toGLSLInstruction.cpp:2081-2086`).
662
-
663
- ### Type rules
664
- - Both `compare`/`src` (when present) are cast via the same
665
- `ui32DataTypeFlag` (`TO_AUTO_BITCAST_TO_UINT` or `_INT`), i.e. bitcast from the
666
- underlying float-vec4 register storage to whichever integer type the target
667
- memory location actually holds.
668
- - `previousValue` destination type is `SVT_UINT` or `SVT_INT` matching `isUint`,
669
- never float directly (though the register itself is stored as float and
670
- bitcast at the destination-write site per this project's register model).
671
- - These do **not** produce DXBC comparison masks; `atomic_umin`/`umax` compare
672
- internally to select which value to store, but the DXBC/GLSL result is the
673
- stored/previous *value*, not a boolean or 0xFFFFFFFF/0 mask.
674
-
675
- ### Helpers needed
676
- None implementable for WebGL2 (see below). Reference-only helpers if ever
677
- targeting ES 3.10+/desktop: `atomicUavOrTgsmOp(func, dest, addr, src,
678
- [compare], [returnsPrevious])`.
679
-
680
- ### Edge cases
681
- - `atomic_umin`/`atomic_umax` are unsigned-only per DXBC (`imin`/`imax` variants
682
- exist separately for signed); do not conflate with `atomic_imin`/`imax`.
683
- - The 1-vs-2-swizzle-component destAddr distinction (structured vs. raw
684
- addressing within the same atomic path) must be preserved if this is ever
685
- ported.
686
-
687
- ### WebGL2 notes
688
- `atomicAdd`/`atomicMin`/`atomicMax`/`atomicExchange`/`atomicCompSwap` (buffer/
689
- shared-memory atomics) and `imageAtomicAdd` etc. (image atomics) are GLSL ES
690
- 3.10+ compute-shader-only built-ins; none exist in ES 3.00 vertex/fragment
691
- shaders, regardless of which D3D11 stage the source instruction came from.
692
- **Fully out of scope for the WebGL2 emitter.** Detect any `atomic_*`/
693
- `imm_atomic_*` opcode and refuse the stage.
694
-
695
- Do **not** rely on "these only occur in compute shaders" as the reason for
696
- skipping this check in the pixel-stage emitter: `dx11-instruction-coverage.json`
697
- (`initialCandidateOpcodeExamples`) records a concrete `atomic_iadd` instance in
698
- `graphics\effect.dx11\managed\space\specialfx\lensflares\lensflareoccludert.sm_depth`
699
- tagged `stageIndex: 1` / `stageName: "pixel"` — an actual D3D11 pixel shader
700
- (D3D11.1 permits UAV access from pixel shaders; a lens-flare occlusion query
701
- written into a UAV counter from the pixel stage is a plausible, ordinary
702
- technique, not a mislabeled compute pass). The other four sampled corpus
703
- instances of this opcode group (`atomic_iadd` again in `createhistograms.sm_depth`,
704
- `imm_atomic_iadd` in `clear.sm_depth`, `imm_atomic_exch`/`atomic_umax`/
705
- `atomic_umin` in `computelightlists.sm_depth`) are all tagged `stageName:
706
- "geometry"` by the same tool, but those files' names (histogram building,
707
- particle-buffer clear, light-list culling) are classic GPGPU compute-shader
708
- workloads, and the coverage tool has no `"compute"` stage label at all in its
709
- vocabulary (only `vertex`/`pixel`/`geometry`/`hull`) — those four are much
710
- better explained as compute shaders whose slot the tool mislabels than as
711
- genuine geometry shaders (a real DX11 geometry shader cannot declare
712
- `dcl_thread_group`/`sync`, which the *sibling* `createhistograms.sm_depth`
713
- example for the `sync` opcode does, and DXBC does not permit that in a
714
- geometry stage). The `lensflareoccludert` pixel-stage instance is not
715
- explained away by that reasoning and must be treated as real: the emitter's
716
- **ps-stage translation path itself** needs the detect-and-refuse check, not
717
- just a pre-filter that assumes this opcode class never survives into a vs/ps
718
- compile.
719
-
720
- `TRANSPILING-GAPS.md:100-114` ("UAV and atomic path... blocked for current
721
- WebGL2 target... `atomic_iadd`: 6... Affects the `lensflareoccludert` path" —
722
- note that count was measured on a narrower earlier corpus slice than this
723
- family's 450k-instruction sweep, which shows 27) already names
724
- `lensflareoccludert` as the blocked case; it does not itself claim the
725
- instruction is compute-only, and the per-instruction corpus scan confirms it
726
- is not.
727
-
728
- ### Confidence
729
- **High** for the DXBC/GLSL semantics (read directly from source) and for the
730
- WebGL2-out-of-scope conclusion (ES 3.00 has no atomics in any stage, so the
731
- `lensflareoccludert` pixel-shader counterexample does not change the outcome).
732
- **Medium** on the blanket "compute-only" framing carried over from the
733
- project's decision log — confirmed false for at least the `lensflareoccludert`
734
- `atomic_iadd` instance, which is a pixel shader; the emitter must not assume
735
- this opcode family is filtered out before reaching ps-stage translation.
736
-
737
- ---
738
-
739
- ## `ld_uav_typed` (18 instances)
740
-
741
- ### Semantics
742
- Reads a texel/element from a typed UAV (`RWTexture*`/`RWBuffer`) at an integer
743
- address, per D3D11 `ld_uav_typed` — the read counterpart of `store_uav_typed`.
744
-
745
- ### GLSL lowering
746
- Case `OPCODE_LD_UAV_TYPED` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:4266-4336`).
747
- Operands: `psDest=asOperands[0]`, `psSrcAddr=asOperands[1]`,
748
- `psSrc=asOperands[2]` (UAV resource). Address component mask derived from
749
- `psInst->eResDim` the same way as `store_uav_typed` (1/2/3 components,
750
- `toGLSLInstruction.cpp:4281-4300`). Source data type resolved from the UAV's
751
- reflected return type (`RETURN_TYPE_FLOAT/SINT/UINT/SNORM/UNORM`,
752
- `toGLSLInstruction.cpp:4302-4325`, with `UNORM`/`SNORM` both mapping to
753
- `SVT_FLOAT`). Emitted form:
754
- ```glsl
755
- dest.mask = imageLoad(<uavName>, <addr-by-dimension>)<swizzle-with-mask>;
756
- ```
757
- using `AddAssignToDest`/`AddAssignPrologue` for the destination, and
758
- `TranslateOperandSwizzleWithMask` to apply the resource operand's own swizzle to
759
- the 4-component `imageLoad` result restricted to the destination write mask
760
- (`toGLSLInstruction.cpp:4327-4335`).
761
-
762
- ### Type rules
763
- Destination component type follows the UAV's reflected return type, not a fixed
764
- uint-array convention (unlike `ld_structured`/`ld_raw`) — `imageLoad` always
765
- returns a 4-component vector of the sampler/image's declared base type
766
- (`ivec4`/`uvec4`/`vec4`), then swizzled down to the requested mask.
767
-
768
- ### Helpers needed
769
- None for WebGL2 (see below). Reference-only: `imageLoadTyped(uav, addr,
770
- returnType)`.
771
-
772
- ### Edge cases
773
- Requires resource-binding reflection (return type, dimension) exactly like
774
- `store_uav_typed` — doubly blocked when `RDEF` is stripped, independent of the
775
- image-type gap below.
776
-
777
- ### WebGL2 notes
778
- `imageLoad` and image sampler types (`image2D` etc.) are ES 3.10+-only.
779
- **Fully out of scope for the WebGL2 emitter.** Matches the draft transpiler's
780
- explicit blocker (`Dx11GlesDraftTranspiler.js:129`) and the
781
- `TRANSPILING-GAPS.md` UAV decision. With only 18 corpus instances (vs.
782
- `store_uav_typed`'s 642), this is very likely the read side of a small set of
783
- compute/UAV effects rather than any vs/ps-reachable code path — but the
784
- concrete sampled instance (`dx11-instruction-coverage.json`, `allOpcodeExamples`)
785
- is `graphics\effect.dx11\managed\space\postprocess\measureexposure.sm_depth`
786
- (a tone-mapping average-luminance compute pass), **not** `lensflareoccludert`.
787
- `lensflareoccludert` is confirmed by `TRANSPILING-GAPS.md:100-114` to carry
788
- `dcl_unordered_access_view_typed`/`atomic_iadd` (and per this family's own
789
- corpus scan, `atomic_iadd` there is a **pixel**-stage instance — see that
790
- section); do not conflate the two effects or assume `ld_uav_typed`'s
791
- compute-only reasoning transfers to `atomic_iadd`.
792
-
793
- ### Confidence
794
- **High** — same corroboration as `store_uav_typed`, corrected to cite the
795
- right sampled effect (`measureexposure.sm_depth`, not `lensflareoccludert`).
796
-
797
- ---
798
-
799
- ## `bufinfo` (0 instances — specified for completeness only)
800
-
801
- ### Semantics
802
- Returns the element count of a structured/raw/typed buffer resource, per D3D11
803
- `bufinfo` (used for bounds-checking dynamic buffer accesses).
804
-
805
- ### GLSL lowering
806
- Case `OPCODE_BUFINFO` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:4753-4766`):
807
- ```glsl
808
- dest = <ctor>(<resourceName>_buf.length());
809
- ```
810
- via `AddAssignToDest(dest, SVT_INT, 1, ...)` then
811
- `<resourceName>_buf.length()` then `AddAssignPrologue`.
812
-
813
- ### Type rules
814
- Result is always `SVT_INT` (a signed count), regardless of the buffer's element
815
- type.
816
-
817
- ### Helpers needed
818
- None — zero corpus instances; no helper is justified without evidence of use.
819
-
820
- ### Edge cases
821
- N/A — not observed in this corpus.
822
-
823
- ### WebGL2 notes
824
- `.length()` on a GLSL array returns a compile-time constant for fixed-size
825
- arrays (legal in ES 3.00) but is a **run-time** query only for SSBO
826
- runtime-sized arrays (`buffer T { ... x[]; }`), which do not exist in ES 3.00.
827
- Since this opcode's only real use (bounds-checking a structured/raw buffer) is
828
- tied to the same SSBO-only resource kinds as `ld_structured`/`ld_raw`, it would
829
- be out of scope for the same reason if it appeared. Zero corpus instances means
830
- no immediate action item, but the emitter should still recognize and refuse the
831
- opcode defensively rather than silently mis-translate.
832
-
833
- ### Confidence
834
- **Low** — zero real-world evidence in this corpus; semantics and lowering are
835
- read directly from HLSLcc source but entirely unvalidated against any actual
836
- shader.
837
-
838
- ---
839
-
840
- ## Helpers summary
841
-
842
- Helpers the memory-structured family needs the emitter to provide (grouped by
843
- whether they produce runnable WebGL2 output or are reference/detection-only):
844
-
845
- **Shipping (WebGL2-runnable) helpers:**
846
- 1. `lowerBoneTransformsToCb3` — package-time text-rewrite pass that removes the
847
- HLSLcc `t0` SSBO declaration, grows `cb3.data[]` to at least 200 `vec4`s,
848
- strips the native bone-ring-buffer offset add, and rewrites each 4-component
849
- `t0_buf[idx].value[...]` row load to `cb3.data[26 + idx*3 + row]` (optionally
850
- swizzled). Port of `lowerWebgl2SkinningAbi` +
851
- `lowerStructuredBoneLoad` in `scripts/packageTr2WebglEffect.js:826-902`.
852
- 2. `lowerBlendIndicesToFloatAttribute` — package-time rewrite of
853
- `uvec4`/`ivec4`/etc. `in_BLENDINDICES<n>` vertex inputs to `vec<N>`, part of
854
- the same pass (`packageTr2WebglEffect.js:840-843`).
855
-
856
- **Reference-only helpers** (mirror HLSLcc's non-WebGL2 SSBO/UAV/atomic/compute
857
- lowering, useful for documentation, debugging, and any future non-WebGL2
858
- backend, but must never be emitted as final WebGL2 output):
859
- 3. `structuredLoadComponent(bufName, structIndex, byteOffset, component, destType)`
860
- 4. `structuredStoreComponent(bufName, structIndex, byteOffset, component, value, srcType)`
861
- 5. `rawLoadComponent(bufName, byteOffset, component, destType)`
862
- 6. `rawStoreComponent(bufName, byteOffset, component, value, srcType)`
863
- 7. `imageLoadTyped(uav, addr, returnType)`
864
- 8. `imageStoreTyped(uav, addr, value, returnType)`
865
- 9. `atomicUavOrTgsmOp(func, dest, addr, src, compare?, returnsPrevious?)`
866
-
867
- **Detection/refusal helpers** (must run before emission; there is no GLSL ES
868
- 3.00 lowering to fall back to):
869
- 10. `refuseComputeOnlyOpcode(opcodeName)` — for `sync`, `store_structured`
870
- (non-skinning), `store_raw`, `store_uav_typed`, `ld_uav_typed`,
871
- `atomic_iadd`, `imm_atomic_iadd`, `imm_atomic_exch`, `atomic_umax`,
872
- `atomic_umin`, and `bufinfo` should it ever appear.
873
- 11. `refuseNonSkinningStructuredLoad(resourceName)` — for `ld_structured` /
874
- `ld_raw` uses that are not the vertex-stage `BoneTransforms` skinning
875
- pattern (e.g. `LightBuffer`/`LightIndexBuffer` pixel-stage reads).
876
-
877
- ---
878
-
879
- *Sources consulted: `vendor/HLSLcc/src/toGLSLInstruction.cpp`,
880
- `toGLSLOperand.cpp`, `toGLSLDeclaration.cpp`, `HLSLccToolkit.cpp`,
881
- `internal_includes/languages.h`; `vendor/HLSLcc/CARBONENGINEJS-FORK.md`;
882
- `../shaderdiscovery/TRANSPILING-GAPS.md`; `../shaderdiscovery/AGENT-FINDINGS/decisions/005-`,
883
- `016-`, `028-cewg-*`, `014-`/`015-`/`016-`/`017-joint-matrix-*`;
884
- `../shaderdiscovery/src/core/transpiler/gles/Dx11GlesDraftTranspiler.js` (hints
885
- only); `scripts/packageTr2WebglEffect.js`;
886
- `../shaderdiscovery/artifacts/dx11-instruction-coverage.json` (per-instruction
887
- stage ground truth, used to correct several "compute-only" stage-reality
888
- claims); `../shaderdiscovery/artifacts/ab-shader-set/work/skinned_fxdirectionalv5/*.es300.glsl`
889
- (actual generated fixture, used to correct the `ld_structured` cb3 worked
890
- example).*
1
+ # DXBC → GLSL ES 3.00 Lowering Spec: Memory-Structured Family
2
+
3
+ Status: Evolving
4
+ Scope: `@carbonenginejs/runtime-resource/formats/webgl` structured-memory lowering
5
+ Audience: Shader translator maintainers and reviewers
6
+ Summary: Defines bounded WebGL2 adaptations for DXBC structured-memory operations.
7
+
8
+ Family key: `memory-structured`
9
+ Target: GLSL ES 3.00 (WebGL2), vertex + pixel stages only (no compute, no SSBOs).
10
+ Register model: every DXBC register is stored by the emitter as a `float` `vec4`; all
11
+ integer/unsigned reads and writes go through `floatBitsToInt` / `floatBitsToUint` /
12
+ `intBitsToFloat` / `uintBitsToFloat` at the use site, mirroring HLSLcc's own
13
+ `GetBitcastOp` (`vendor/HLSLcc/src/toGLSLOperand.cpp:327-353`) and
14
+ `AddOpAssignToDestWithMask` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:28-153`)
15
+ machinery, which HLSLcc itself falls back to whenever static data-type analysis is
16
+ unavailable (exactly the reflection-stripped situation this fork runs in per
17
+ `vendor/HLSLcc/CARBONENGINEJS-FORK.md:34-52`).
18
+
19
+ Corpus counts (450k-instruction sweep, 1611 EVE Online DX11 effects):
20
+
21
+ | Opcode | Count | Stage reality |
22
+ |---|---:|---|
23
+ | `ld_structured` | 4014 | vs (BoneTransforms skinning) + ps (LightBuffer/LightIndexBuffer-style tbuffer reads) |
24
+ | `store_structured` | 930 | compute-only in D3D11 (requires a UAV write target); **not observed as reachable in vs/ps stages actually shipped to WebGL2** — see per-opcode note |
25
+ | `store_uav_typed` | 642 | compute-only in practice for this corpus (UAV write) |
26
+ | `sync` | 183 | compute-only (`sync` only has meaning with `dcl_thread_group`) |
27
+ | `ld_raw` | 54 | no confirmed vs/ps corpus example found (see per-opcode note); same SSBO problem as `ld_structured` if it does appear in vs/ps |
28
+ | `store_raw` | 54 | compute-only in practice (RWByteAddressBuffer write) |
29
+ | `atomic_iadd` | 27 | **confirmed present in a pixel shader** (`lensflareoccludert.sm_depth`, `stageName: "pixel"` in `dx11-instruction-coverage.json`'s `initialCandidateOpcodeExamples`) — not compute-only, see per-opcode note |
30
+ | `imm_atomic_iadd` | 24 | compute-only (UAV atomic with previous-value return) |
31
+ | `ld_uav_typed` | 18 | compute-only in this corpus — sampled instance is `measureexposure.sm_depth` (tone-mapping luminance compute pass), **not** `lensflareoccludert` (that file's confirmed opcode is `atomic_iadd`/`dcl_unordered_access_view_typed`, per `TRANSPILING-GAPS.md:100-114`); see per-opcode note |
32
+ | `imm_atomic_exch` | 9 | compute-only |
33
+ | `atomic_umax` | 6 | compute-only |
34
+ | `atomic_umin` | 3 | compute-only |
35
+ | `bufinfo` | 0 | not observed in this corpus; specified for completeness only |
36
+
37
+ **Central WebGL2 constraint** (governs every opcode below): GLSL ES 3.00 has **no
38
+ shader storage buffers** (`buffer` blocks require `#version 310 es` or GL 4.3+), **no
39
+ image load/store types** (`image2D`/`imageLoad`/`imageStore` require ES 3.10+), and
40
+ **no atomic-memory built-ins outside compute shaders** (`atomicAdd`/`imageAtomicAdd`
41
+ etc. and `barrier()`/`memoryBarrier()` are ES 3.10 compute-shader built-ins, not part
42
+ of the ES 3.00 vertex/fragment built-in set). Every HLSLcc GLSL template shown below
43
+ is therefore **reference material for what desktop/Vulkan/Metal HLSLcc emits**, not
44
+ directly compilable WebGL2 output. The one opcode with a proven, shipping WebGL2
45
+ lowering is `ld_structured` restricted to the `BoneTransforms` skinning case, via the
46
+ package-time `cb3` rewrite described in its section below.
47
+
48
+ ---
49
+
50
+ ## Shared machinery referenced by this family
51
+
52
+ - `HaveUnsignedTypes(eLang)` and `HaveBitEncodingOps(eLang)`
53
+ (`vendor/HLSLcc/src/internal_includes/languages.h:156-180`) both return `1` for
54
+ every target except `LANG_ES_100`/`LANG_120`. `LANG_ES_300` (this project's
55
+ target) has unsigned integer types and bit-encoding intrinsics
56
+ (`floatBitsToInt`/`floatBitsToUint`/`intBitsToFloat`/`uintBitsToFloat`) available,
57
+ so all the `TO_FLAG_UNSIGNED_INTEGER` / bitcast branches below are live for our
58
+ target.
59
+ - `AddAssignToDest` / `AddAssignPrologue`
60
+ (`vendor/HLSLcc/src/toGLSLInstruction.cpp:155-171`) write the destination operand,
61
+ the write mask, `= `, and the correct number of constructor/bitcast open-parens;
62
+ `AddAssignPrologue` closes them and appends `;\n`. This is the generic
63
+ "assign-with-implicit-bitcast" pattern the emitter must reproduce for `ld_structured`,
64
+ `ld_raw`, `ld_uav_typed`, and `bufinfo`.
65
+ - `_sat` (saturate) is applied **generically, after the main switch**, only to
66
+ operand 0 (`vendor/HLSLcc/src/toGLSLInstruction.cpp:4821-4844`): it re-clamps
67
+ `dest = clamp(dest, 0.0, 1.0)` (with an Adreno `min(max(dest,0.0),1.0)` workaround
68
+ path gated behind `#ifdef UNITY_ADRENO_ES3`, since `eTargetLanguage == LANG_ES_300`
69
+ triggers the workaround branch). This only matters for opcodes that write a
70
+ register destination — i.e. `ld_structured`, `ld_raw`, `ld_uav_typed`, `bufinfo`,
71
+ `imm_atomic_*` (previous-value destination). Store/`sync`/non-`imm_` atomics have
72
+ no float destination and DXBC does not encode `_sat` for them in practice.
73
+ - Structured/raw buffer declaration (`DeclareBufferVariable`,
74
+ `vendor/HLSLcc/src/toGLSLDeclaration.cpp:1027-1092`) is the HLSLcc reference
75
+ declaration these instructions index into:
76
+ ```glsl
77
+ struct t0_type { uint[<stride/4>] value; };
78
+ layout(std430, binding = N) readonly buffer t0 { t0_type t0_buf[]; };
79
+ ```
80
+ (raw buffers use `uint t0_buf[]` / `int t0_buf[]` directly, no wrapper struct,
81
+ selected by `HaveUnsignedTypes`). This declaration is unusable in GLSL ES 3.00 —
82
+ documented here only because the per-instruction `t0_buf[...]` indexing expression
83
+ it feeds is the exact text pattern the package-time `cb3` rewrite (see
84
+ `ld_structured`) pattern-matches against.
85
+
86
+ ---
87
+
88
+ ## `ld_structured` (4014 instances) — highest priority in this family
89
+
90
+ ### Semantics
91
+ Loads a structured-buffer element: given a structure index (operand 1) and a
92
+ byte offset within the structure (operand 2), reads one or more 32-bit components
93
+ from resource operand 3 (a `t#`/`u#` structured buffer) into the destination
94
+ register, per D3D11 `ld_structured` semantics (index-then-byte-offset addressing
95
+ into an array of fixed-stride structures).
96
+
97
+ ### GLSL lowering — A. HLSLcc reference (desktop/Vulkan/Metal SSBO path)
98
+ `TranslateShaderStorageLoad`, case `OPCODE_LD_STRUCTURED`
99
+ (`vendor/HLSLcc/src/toGLSLInstruction.cpp:1585-1683`, dispatched at
100
+ `toGLSLInstruction.cpp:4256-4264`). Operands: `psDest=asOperands[0]`,
101
+ `psSrcAddr=asOperands[1]` (struct index), `psSrcByteOff=asOperands[2]` (byte
102
+ offset), `psSrc=asOperands[3]` (resource).
103
+
104
+ For each destination component `c` present in the destination write mask:
105
+ ```glsl
106
+ <bitcast_open><resourceName>_buf[<structIndexExpr>].value[(<byteOffExpr> >> 2u) + <swz>u]<bitcast_close>
107
+ ```
108
+ where `<swz>` is `psSrc->aui32Swizzle[c]` if the resource operand carries an
109
+ explicit swizzle (`OPERAND_4_COMPONENT_SWIZZLE_MODE`), else `c` itself
110
+ (`toGLSLInstruction.cpp:1673`) — i.e. the *resource* operand's own swizzle can
111
+ remap which dword of the structure element component `c` reads from. All such
112
+ per-component expressions are joined by `AddAssignToDest`/constructor into:
113
+ ```glsl
114
+ dest.mask = <ctor>(comp0, comp1, ...);
115
+ ```
116
+ (`toGLSLInstruction.cpp:1623-1682`).
117
+
118
+ `<bitcast_open>`/`<bitcast_close>` depend on `destDataType = psDest->GetDataType()`
119
+ (`toGLSLInstruction.cpp:1642-1654`):
120
+ - `SVT_FLOAT` → `uintBitsToFloat(...)` (since `HaveBitEncodingOps` is true for ES
121
+ 3.00); the code falls back to `float(...)` only for pre-bit-encoding targets.
122
+ - `SVT_INT`/`SVT_INT16`/`SVT_INT12` → `int(...)`.
123
+ - `SVT_UINT` → no wrapper at all (`addedBitcast` stays `0`) because the backing
124
+ `t0_type.value` array element type is always `uint` — "always uint array atm"
125
+ (`toGLSLInstruction.cpp:1641`).
126
+
127
+ The struct-index operand is translated with **both** `TO_FLAG_UNSIGNED_INTEGER |
128
+ TO_FLAG_INTEGER` set simultaneously (`toGLSLInstruction.cpp:1664`) — reproduce
129
+ this by picking whichever the index operand's own declared type implies (uint by
130
+ default on ES 3.00). The byte-offset operand's flag (`srcOffFlag`) is
131
+ `TO_FLAG_UNSIGNED_INTEGER` unless the target lacks unsigned types or the operand's
132
+ own `SVT_INT`/`SVT_INT16`/`SVT_INT12` type forces `TO_FLAG_INTEGER`
133
+ (`toGLSLInstruction.cpp:1618-1621`); when unsigned, both the `>> 2` and the `+
134
+ <swz>` component addend get a trailing `u` suffix (`toGLSLInstruction.cpp:1669-1675`).
135
+
136
+ ### GLSL lowering — B. WebGL2 `cb3` joint-matrix rewrite contract (the shipping path)
137
+ This is the **only** `ld_structured` lowering this emitter must actually produce
138
+ runnable WebGL2 GLSL for, restricted to the `BoneTransforms` skinning case
139
+ (vertex stage). It is a two-stage pipeline:
140
+
141
+ 1. Emit HLSLcc's reference SSBO GLSL exactly as in section A (this project's
142
+ HLSLcc fork already tolerates missing `RDEF` bindings for
143
+ `dcl_resource_structured` by falling back to the DXBC declaration's encoded
144
+ stride — see `CARBONENGINEJS-FORK.md:41-44` and
145
+ `TRANSPILING-GAPS.md:51-54`), producing text of the exact shape:
146
+ ```glsl
147
+ struct t0_type { uint[1] value; };
148
+ layout(std430, binding = 0) readonly buffer t0 { t0_type t0_buf[]; };
149
+ ...
150
+ dest = vec4(uintBitsToFloat(t0_buf[idx].value[(0 >> 2) + 0]),
151
+ uintBitsToFloat(t0_buf[idx].value[(0 >> 2) + 1]),
152
+ uintBitsToFloat(t0_buf[idx].value[(0 >> 2) + 2]),
153
+ uintBitsToFloat(t0_buf[idx].value[(0 >> 2) + 3]));
154
+ ```
155
+ Verified against an actual generated fixture
156
+ (`../shaderdiscovery/artifacts/ab-shader-set/work/skinned_fxdirectionalv5/skinned_fxdirectionalv5.sm_hi.dxbc_29291f5662ed6781.vertex.es300.glsl:60-64`):
157
+ all four components of one row share the **same** byte offset (row 0 uses
158
+ `0`, row 1 uses `16`, row 2 uses `32` — i.e. the row's base byte offset, not
159
+ a per-component offset of `0/4/8/12`), differing only in the trailing
160
+ `+ 0/1/2/3` dword index, and **without** a `u` suffix on either the shift or
161
+ the addend. The missing `u` is not a formatting nit: in this corpus the
162
+ byte-offset operand is declared `SVT_INT` (not `SVT_UINT`), so
163
+ `srcOffFlag` resolves to `TO_FLAG_INTEGER`
164
+ (`toGLSLInstruction.cpp:1618-1621`), which suppresses the `u` suffix
165
+ entirely (see `printImmediate32`, `toGLSLOperand.cpp:371-387` vs. `388-395`).
166
+ This also matters functionally: `lowerStructuredBoneLoad`'s regex
167
+ (`packageTr2WebglEffect.js:875`) matches literal `(\d+)\s*>>\s*2\s*\)` with
168
+ no `u` tolerance, and its 4-component agreement check requires all four
169
+ loads to share one `byteOffset` — a per-component-varying offset of
170
+ `0/4/8/12` would both fail to match the regex *and* fail the "same
171
+ byteOffset" agreement check, silently no-opping the rewrite. The previous
172
+ worked example here was internally inconsistent with the very rewrite
173
+ contract it was illustrating.
174
+ 2. A package-time rewrite pass over the generated GLSL text, performing the
175
+ steps below **in this exact order**.
176
+
177
+ > **Superseded (2026-08-02).** This pass no longer exists. The emitter now
178
+ > declares vertex-stage structured buffers as `std140` UBOs when it emits the
179
+ > shader, with real bindings, rather than rewriting text afterwards — see
180
+ > `DxbcGlslEmitter.js`, the `dcl_resource_structured` vertex branch. The
181
+ > regex functions it describes were deleted from
182
+ > `scripts/packageTr2WebglEffect.js`, so the line citations below point at
183
+ > nothing. The section is kept because it records *why* each rewrite was
184
+ > needed, which the ccpwgl runtime ABI still constrains; read it as history,
185
+ > not as a description of the code.
186
+ 1. Strip the `#ifdef GL_ARB_shader_storage_buffer_object` /
187
+ `GL_ARB_shader_image_load_store` extension guard blocks (regexes at
188
+ `packageTr2WebglEffect.js:832-833`).
189
+ 2. Strip the `struct t0_type { uint[1] value; };` declaration (regex at
190
+ `packageTr2WebglEffect.js:834`, matches only the 1-word/4-byte stride
191
+ shape).
192
+ 3. Strip the `layout(std430, binding = 0) readonly buffer t0 { t0_type
193
+ t0_buf[]; };` declaration (regex at `packageTr2WebglEffect.js:835`).
194
+ 4. Grow the vertex stage's `ConstantBuffer3` (`cb3`) declaration to
195
+ `vec4 data[max(existingSize, 200)]` (`packageTr2WebglEffect.js:836-839`) —
196
+ this is where `JointMat` lives at `cb3.data[26..199]` per the ccpwgl runtime
197
+ ABI (`cb3[26..199]`, 58 joints × 12 floats = 696 floats,
198
+ `AGENT-FINDINGS/decisions/016-cewg-skinning-abi-lowering-for-ccpwgl-2026-06-30.md`
199
+ and `015-joint-matrix-jointmat-findings-ccpwgl-runtime-truth.md`).
200
+ 5. Convert `uvec4`/`ivec4` (or `uvec2/3`, `ivec2/3`) `in_BLENDINDICES<n>` vertex
201
+ inputs to plain `vec<N>` (regex at `packageTr2WebglEffect.js:840-843`) —
202
+ ccpwgl binds GR2 mesh blend indices as float attributes via
203
+ `vertexAttribPointer`, not `vertexAttribIPointer`
204
+ (`AGENT-FINDINGS/decisions/028-cewg-skinned-blend-index-abi-lowering.md`).
205
+ 6. Strip the DX11 global bone-offset add HLSLcc emits when combining
206
+ `in_BLENDINDICES0` with a `cb3.data[26]` offset uniform (two regex forms at
207
+ `packageTr2WebglEffect.js:845-852`, covering both the scalar `int(...) +
208
+ floatBitsToInt(cb3.data[26].x)` shape and the vector `(i)uvec/ivec(...) +
209
+ floatBitsToInt(cb3.data[26].<swz>)` shape) — ccpwgl's `cb3.data[26]` slot is
210
+ reused for `JointMat`, not the native bone-ring-buffer offset, so this add
211
+ must be removed entirely rather than merely rewired.
212
+ 7. Rewrite each 4-component `t0_buf[...]` row-load expression
213
+ (`vec4(t0_buf[idx].value[(byteOff>>2)+0], ...)`, all 4 components required,
214
+ `lowerStructuredBoneLoad`) into:
215
+ ```glsl
216
+ cb3.data[26 + (<idx>) * 3 + <row>]
217
+ ```
218
+ where `<row> = floor(byteOff / 16)` and must be in `{0,1,2}` (a `Float4x3`
219
+ bone matrix is 3 `vec4` rows); if any of the 4 component sub-expressions
220
+ disagree on `idx`/`byteOff`, or `row` falls outside `0..2`, or the swizzle
221
+ is anything but the identity `xyzw`/`.xyzw` is stripped when default,
222
+ **the rewrite silently no-ops and leaves the un-lowered `t0_buf` text in
223
+ place** (`packageTr2WebglEffect.js:886,890,895,898` all `return match`) —
224
+ this is a silent-failure edge case the implementing engineer must guard
225
+ against (add a diagnostic) rather than trust to fail loudly.
226
+ 8. If any rewrite happened, inject a `// Carbon WebGL: BoneTransforms lowered to cb3
227
+ JointMat rows.` marker comment after `#version 300 es`
228
+ (`packageTr2WebglEffect.js:859-861`).
229
+
230
+ ### GLSL lowering — C. `ld_structured` in pixel stage / non-skinning resources
231
+ The corpus also uses `ld_structured` on `t#` "packed tbuffer" resources unrelated
232
+ to skinning — e.g. `LightBuffer`/`LightIndexBuffer` reads in pixel shaders such as
233
+ `decalcylindricv5.sm_depth` (`AGENT-FINDINGS/decisions/005-structured-resource-only-opcodes-2026-06-26.md`).
234
+ **No `cb3`-style *functional* ABI rewrite exists for these** — they hit the same
235
+ SSBO-unavailability wall as section A. The emitter's default path lowers them to
236
+ pixel `usampler2D` data textures (see `DxbcGlslEmitter.js` `dcl_resource_structured`
237
+ pixel branch), which compiles but consumes a texture unit each; on real drivers
238
+ the `_depth` quad variants overflow `MAX_TEXTURE_IMAGE_UNITS`(16).
239
+
240
+ **Superseded (2026-08-02) — the lights fit, and are kept.** The two light
241
+ buffers now lower to a single packed `RGBA32UI` data texture rather than one
242
+ texture each, which frees two units, and the `Detail1/2/3Map` textures merge into
243
+ one array texture, which frees one or two more. Both `.sm_depth` quad variants
244
+ land at or under 16 with lighting intact: `unpackedskinned_quaddetailv5` at 15,
245
+ `unpackedskinned_quadheatdetailv5` at 16. See
246
+ `/docs/contracts/webgl2-texture-budget.md`. Dropping the lights is still
247
+ available as `--stub-light-resources` for isolating a lighting problem, but it is
248
+ no longer the answer to the budget. The paragraph below describes that opt-in
249
+ path and remains accurate.
250
+
251
+ **Earlier resolution (2026-07-08) — stub, not rewrite.** Since Carbon WebGL does not
252
+ support this tiled lighting, the packager can DROP it instead of lowering it. Run
253
+ `packageTr2WebglEffect.js --stub-light-resources`: it resolves the light resource
254
+ names (`LightBuffer`, `LightIndexBuffer`, `LightProfileArray`) to `t#` registers
255
+ from the Carbon `.sm` reflection (RDEF is stripped, so names live only there —
256
+ and the registers vary per permutation, so this is name-driven, not fixed to
257
+ sb11/sb12/s13) and passes them to `emitGlsl` as `stubResourceRegisters`. The
258
+ emitter then drops their decl+binding and lowers reads to `uintBitsToFloat(0u)`
259
+ (structured) / `vec4(0.0)` (sampled) — zeroing the per-tile light count makes the
260
+ light loop dead. The packager also strips those `resource` bindings from the
261
+ manifest JSON (`stripLightResourcesFromManifest`) so the Carbon WebGL runtime does not
262
+ synthesize a texture def (glType 0 → "Invalid shader texture definition") for the
263
+ now-undeclared light buffers. Opt-in, default off; every other package is
264
+ unchanged. A functional light constant-buffer path remains possible but was not
265
+ built.
266
+
267
+ Tested by `test/glsl-emitter.test.js` (synthetic pixel shaders with a structured
268
+ buffer / sampler2DArray: declared by default, dropped + no binding when the
269
+ register is in `stubResourceRegisters`, and only listed registers dropped) and
270
+ `test/stub-light-resources.test.js` (the packager's `resolveStubLightRegisters`
271
+ name→register resolution and `stripLightResourcesFromManifest` manifest filter,
272
+ in `scripts/stubLightResources.js`).
273
+
274
+ ### Type rules
275
+ - Struct index operand: read as int/uint (both flags set in HLSLcc; pick uint by
276
+ default for ES 3.00).
277
+ - Byte-offset operand: uint unless the operand's own declared type is signed int.
278
+ - Result component type follows the *destination* register's inferred type
279
+ (float → `uintBitsToFloat`, int → `int(...)`, uint → passthrough), **not** the
280
+ source resource's declared return type — this is purely dest-driven, matching
281
+ the "everything is `float vec4`, bitcast at use" register model this project
282
+ already commits to.
283
+ - This is a data-movement instruction, not a comparison — it does not produce a
284
+ 0xFFFFFFFF/0 mask.
285
+
286
+ ### Helpers needed
287
+ - `structuredLoadComponent(bufName, structIndex, byteOffset, component, destType)`
288
+ — reference-only (HLSLcc SSBO shape), needed if the emitter ever targets a
289
+ non-WebGL2 backend or documents the pre-rewrite intermediate form.
290
+ - `lowerBoneTransformsToCb3` (package-time text pass; port of
291
+ `lowerWebgl2SkinningAbi` + `lowerStructuredBoneLoad`).
292
+ - `lowerBlendIndicesToFloatAttribute` (package-time text pass, part of the same
293
+ rewrite; port of the `in_BLENDINDICES` regex).
294
+ - `refuseNonSkinningStructuredLoad` (detection helper for section C).
295
+
296
+ ### Edge cases
297
+ - NaN/inf: none introduced by the load itself; `uintBitsToFloat` is a pure
298
+ bit-reinterpret, so any NaN bit pattern already in the buffer round-trips as
299
+ NaN.
300
+ - The resource-operand swizzle indirection (`psSrc->aui32Swizzle[component]`)
301
+ means component `c` of the destination is not guaranteed to read structure
302
+ dword `c` — verify this against real corpus DXBC before assuming identity
303
+ swizzle always holds.
304
+ - The row/index-agreement check in `lowerStructuredBoneLoad` requires **all 4**
305
+ components to share the same `idx`/`byteOffset`; a destination write mask
306
+ narrower than `.xyzw` (e.g. `.xy`) will not match the 4-load regex and will
307
+ silently fail to rewrite — this is plausible for shaders that only need part of
308
+ a bone row and needs explicit test coverage.
309
+ - `bSaturate` on `ld_structured` is legal per the generic post-switch handling
310
+ but is not expected to appear in real bone/light-buffer loads; still must be
311
+ implemented for correctness if the corpus is ever re-scanned with `_sat`
312
+ detection.
313
+
314
+ ### WebGL2 notes
315
+ - SSBOs (`buffer` blocks) do not exist in GLSL ES 3.00 at all; section A's
316
+ output is fundamentally uncompilable in WebGL2 and must never reach the
317
+ final package unless rewritten by section B.
318
+ - `layout(std430, binding=N)` is likewise unavailable in ES 3.00 (`std430` and UBO
319
+ binding indices exist, but SSBO binding does not).
320
+
321
+ ### Confidence
322
+ **High** for the `BoneTransforms`/`cb3` path (validated end-to-end:
323
+ `336/336` and `240/240` WebGL2 program links per
324
+ `TRANSPILING-GAPS.md:60-63` and `016-cewg-skinning-abi-lowering-for-ccpwgl-2026-06-30.md`).
325
+ **Low** for non-skinning `ld_structured` in pixel stages — no validated WebGL2
326
+ lowering exists; treat as blocked pending a decision.
327
+
328
+ ---
329
+
330
+ ## `store_structured` (930 instances)
331
+
332
+ ### Semantics
333
+ Writes one or more 32-bit components of a structured-buffer element addressed by
334
+ a structure index and byte offset, per D3D11 `store_structured` (the inverse of
335
+ `ld_structured`; only valid against a UAV, since D3D11 `structured buffer`
336
+ read-only resources cannot be written).
337
+
338
+ ### GLSL lowering
339
+ `TranslateShaderStorageStore`, case `OPCODE_STORE_STRUCTURED`
340
+ (`vendor/HLSLcc/src/toGLSLInstruction.cpp:1500-1583`, dispatched at
341
+ `toGLSLInstruction.cpp:4348-4357`). Operands: `psDest=asOperands[0]` (the UAV
342
+ resource, carrying the write mask via `OPERAND_4_COMPONENT_MASK_MODE`),
343
+ `psDestAddr=asOperands[1]` (struct index), `psDestByteOff=asOperands[2]` (byte
344
+ offset), `psSrc=asOperands[3]` (value to store).
345
+
346
+ For each component present in `psDest->ui32CompMask`:
347
+ ```glsl
348
+ <name>_buf[<structIndexExpr>].value[(<byteOffExpr> >> 2u) + <comp>u] = <uint-or-int-cast>(src.<swz-or-x>);
349
+ ```
350
+ (`toGLSLInstruction.cpp:1534-1582`). `dstOffFlag` follows the same
351
+ unsigned-unless-signed-typed rule as `ld_structured`. The source cast (`srcFlag`)
352
+ is `TO_FLAG_UNSIGNED_INTEGER` by default, flipped to `TO_FLAG_INTEGER` only when
353
+ `DeclareRWStructuredBufferTemplateTypeAsInteger` reports the target buffer as a
354
+ single-`int`-typed `RWStructuredBuffer<int4>` (the "avoid calling the wrong
355
+ `AtomicMin` overload" special case, `toGLSLDeclaration.cpp:998-1025`) — otherwise
356
+ the destination array element type is always `uint` (`toGLSLInstruction.cpp:1569-1572`).
357
+ Source components are consumed left-to-right via an incrementing `srcComponent`
358
+ counter if the source operand has more than one swizzle element, else always
359
+ `.x` (`toGLSLInstruction.cpp:1575-1578`).
360
+
361
+ ### Type rules
362
+ - Destination index/byte-offset: same int/uint rule as `ld_structured`.
363
+ - Source value: bitcast to uint (default) or int (special RWStructuredBuffer<int>
364
+ case) before storing — never stored as float, consistent with "backing array is
365
+ always uint".
366
+
367
+ ### Helpers needed
368
+ - `structuredStoreComponent(bufName, structIndex, byteOffset, component, value,
369
+ srcType)` — reference-only; no WebGL2 target exists for this opcode (see below).
370
+
371
+ ### Edge cases
372
+ - No destination register write, so `_sat` never applies (DXBC does not attach
373
+ `_sat` to store instructions).
374
+ - Per-component write masking must exactly follow `psDest->ui32CompMask`
375
+ (`OPERAND_4_COMPONENT_MASK_MODE`), not the source operand's own mask.
376
+
377
+ ### WebGL2 notes
378
+ `store_structured` requires a writable UAV, which in turn requires an SSBO
379
+ (`buffer`, not `readonly buffer`) — completely unavailable in GLSL ES 3.00. No
380
+ package-time rewrite analogous to the `cb3` skinning path exists for writes
381
+ (there is no ccpwgl uniform target that plausibly receives a per-invocation
382
+ compute-style scatter write). **This opcode is out of scope for the WebGL2
383
+ emitter.** The 930 corpus instances should be treated as evidence this opcode
384
+ occurs in DX11 stages that are not shipped to the current WebGL2 vs/ps target
385
+ (the sampled corpus instance is `createhistograms.sm_depth`, tagged
386
+ `stageName: "geometry"` in `dx11-instruction-coverage.json` but much more
387
+ plausibly a mislabeled compute shader given the histogram-building workload
388
+ and its co-occurring `sync`/`dcl_thread_group`-shaped instruction in the same
389
+ file — see the `atomic_iadd` section for the one confirmed **non**-compute
390
+ counterexample in this family, `lensflareoccludert`, which is a pixel shader,
391
+ not compute); the emitter should detect and refuse rather than attempt
392
+ emission.
393
+
394
+ ### Confidence
395
+ **Medium** on the HLSLcc reference lowering itself (directly read from source);
396
+ **high** on the WebGL2-scope conclusion (no counter-evidence of a vs/ps
397
+ `store_structured` shipping shader was found in the decision corpus, and the
398
+ project's own draft transpiler already special-cases the sibling UAV write
399
+ opcode `store_uav_typed` as a target blocker — see that section).
400
+
401
+ ---
402
+
403
+ ## `store_uav_typed` (642 instances)
404
+
405
+ ### Semantics
406
+ Writes a full-precision (or format-converted) texel/element to a typed UAV
407
+ (`RWTexture*`/`RWBuffer`) at an integer address, per D3D11 `store_uav_typed`.
408
+
409
+ ### GLSL lowering
410
+ Case `OPCODE_STORE_UAV_TYPED` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:4359-4415`):
411
+ ```glsl
412
+ imageStore(<uavName>, <addr-expr-by-dimension>, <value-cast-by-return-type>);
413
+ ```
414
+ The address component mask and any `TO_AUTO_EXPAND_TO_VEC{2,3,4}` flag are chosen
415
+ from the UAV's reflected `REFLECT_RESOURCE_DIMENSION_*`
416
+ (`toGLSLInstruction.cpp:4382-4407`): 1D/Buffer → `.x` only; 2D/1DArray/2DMS →
417
+ `.xy` with `TO_AUTO_EXPAND_TO_VEC2`; 2DArray/3D/2DMSArray/Cube → `.xyz` with
418
+ `TO_AUTO_EXPAND_TO_VEC3`; CubeArray → `TO_AUTO_EXPAND_TO_VEC4`. The stored value
419
+ is translated with `ResourceReturnTypeToFlag(psRes->ui32ReturnType)` — i.e. cast
420
+ to match the UAV's declared return type (float/int/uint).
421
+
422
+ ### Type rules
423
+ Value operand bitcast is driven entirely by the UAV's reflected return type
424
+ (`RETURN_TYPE_FLOAT`/`SINT`/`UINT`/`UNORM`/`SNORM`), not by any DXBC instruction
425
+ flag — this requires resource-binding reflection, which per
426
+ `CARBONENGINEJS-FORK.md:34-39` is frequently stripped in this project's shipped
427
+ DXBC and falls back to register-stable naming only (no return-type recovery
428
+ implied by that fallback).
429
+
430
+ ### Helpers needed
431
+ None for the WebGL2 emitter proper — see WebGL2 notes. Reference-only helper:
432
+ `imageStoreTyped(uav, addr, value, returnType)`.
433
+
434
+ ### Edge cases
435
+ - `imageStore` swizzle/expansion must match the UAV's declared dimensionality
436
+ exactly, or GLSL will reject a texel with the wrong component count.
437
+ - Missing `RDEF` reflection (this project's normal stripped-DXBC case) leaves the
438
+ return type unrecoverable, which independently blocks this opcode even before
439
+ the SSBO/image-type gap is considered.
440
+
441
+ ### WebGL2 notes
442
+ `image2D`/`imageBuffer` UAV types and `imageStore` are GLSL ES 3.10+ built-ins,
443
+ not part of ES 3.00. **Fully out of scope for the WebGL2 emitter.** This matches
444
+ the project's own draft-transpiler decision, which already lists
445
+ `store_uav_typed` as a hard `TARGET_BLOCKER_OPCODES` entry
446
+ (`../shaderdiscovery/src/core/transpiler/gles/Dx11GlesDraftTranspiler.js:130`) and
447
+ the `TRANSPILING-GAPS.md:100-114` "UAV and atomic path... blocked for current
448
+ WebGL2 target" decision. The emitter must detect `dcl_unordered_access_view_*`
449
+ declarations plus this opcode and refuse the stage (or the whole effect) with an
450
+ explicit diagnostic, never attempt best-effort emission.
451
+
452
+ ### Confidence
453
+ **High** — corroborated independently by HLSLcc source, the draft transpiler's
454
+ explicit blocker list, and the shaderdiscovery decision log.
455
+
456
+ ---
457
+
458
+ ## `sync` (183 instances)
459
+
460
+ ### Semantics
461
+ A compute-shader thread-group synchronization barrier. DXBC encodes which memory
462
+ domains/threads to synchronize via `ui32SyncFlags`: `SYNC_THREAD_GROUP_SHARED_MEMORY`,
463
+ `SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GROUP`/`_GLOBAL`, and `SYNC_THREADS_IN_GROUP`.
464
+ It only has defined meaning inside a compute shader with a `dcl_thread_group` size.
465
+
466
+ ### GLSL lowering
467
+ Case `OPCODE_SYNC` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:3958-3984`):
468
+ ```glsl
469
+ if (flags & SYNC_THREAD_GROUP_SHARED_MEMORY) memoryBarrierShared();
470
+ if (flags & (SYNC_UNORDERED_ACCESS_VIEW_MEMORY_GROUP|_GLOBAL)) memoryBarrier();
471
+ if (flags & SYNC_THREADS_IN_GROUP) barrier();
472
+ ```
473
+ Each condition independently emits its statement (not mutually exclusive; a
474
+ single `sync` instruction can emit all three lines).
475
+
476
+ ### Type rules
477
+ N/A — no operands, no data type.
478
+
479
+ ### Helpers needed
480
+ None — this opcode has no GLSL ES 3.00 equivalent to helper-wrap; see WebGL2
481
+ notes.
482
+
483
+ ### Edge cases
484
+ None beyond the flag decoding itself (a bitmask, not an enum — must check all
485
+ three bits independently, not `switch`/`else if`).
486
+
487
+ ### WebGL2 notes
488
+ `barrier()`, `memoryBarrier()`, and `memoryBarrierShared()` are **compute-shader-
489
+ only** built-ins in GLSL ES (introduced with ES 3.10 compute shaders); they do
490
+ not exist in the ES 3.00 vertex/fragment built-in set at all, and vertex/fragment
491
+ shaders have no thread-group concept regardless of GLSL version. **Fully out of
492
+ scope for the WebGL2 emitter.** Detect `sync` (and its precondition,
493
+ `dcl_thread_group`) and refuse the stage.
494
+
495
+ ### Confidence
496
+ **High** — the DXBC semantics and the compute-only nature of `barrier`/
497
+ `memoryBarrier` in GLSL ES are unambiguous.
498
+
499
+ ---
500
+
501
+ ## `ld_raw` (54 instances)
502
+
503
+ ### Semantics
504
+ Reads one or more 32-bit components from a raw (`ByteAddressBuffer`) resource at
505
+ a byte offset, per D3D11 `ld_raw` — same addressing model as `ld_structured` but
506
+ without a structure index (flat byte-addressed array).
507
+
508
+ ### GLSL lowering
509
+ Same function as `ld_structured`, `TranslateShaderStorageLoad`, case
510
+ `OPCODE_LD_RAW` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:1602-1606`, dispatched
511
+ at `toGLSLInstruction.cpp:4416-4425`). Operands: `psDest=asOperands[0]`,
512
+ `psSrcByteOff=asOperands[1]`, `psSrc=asOperands[2]` — no `psSrcAddr`, so the
513
+ `TranslateShaderStorageLoad` body skips the `[<idx>].value` indirection
514
+ entirely (`toGLSLInstruction.cpp:1661-1666`, gated on `if (psSrcAddr)`), producing:
515
+ ```glsl
516
+ <bitcast_open><name>_buf[(<byteOffExpr> >> 2u) + <comp>u]<bitcast_close>
517
+ ```
518
+ against a flat `uint`/`int` array (`vendor/HLSLcc/src/toGLSLDeclaration.cpp:1078-1088`,
519
+ `isRaw` branch: `uint <name>_buf[];` or `int <name>_buf[];` chosen by
520
+ `HaveUnsignedTypes`), not a `_type` struct array.
521
+
522
+ ### Type rules
523
+ Identical bitcast-by-destination-type rule as `ld_structured` (float →
524
+ `uintBitsToFloat`, int → `int(...)`, uint → passthrough).
525
+
526
+ ### Helpers needed
527
+ - `rawLoadComponent(bufName, byteOffset, component, destType)` — reference-only;
528
+ see WebGL2 notes for scope.
529
+
530
+ ### Edge cases
531
+ Same swizzle-indirection caveat as `ld_structured` (component addressing follows
532
+ the resource operand's own swizzle if present).
533
+
534
+ ### WebGL2 notes
535
+ Same SSBO unavailability as `ld_structured` section A/C. No `cb3`-style rewrite
536
+ is known or defined for raw-buffer reads — the `BoneTransforms` rewrite is
537
+ specific to the structured-buffer `t0_buf[idx].value[...]` shape, not the flat
538
+ `t0_buf[...]` raw shape. **Out of scope for the WebGL2 emitter** unless/until a
539
+ specific raw-buffer resource is proven to need a package-time ABI rewrite
540
+ analogous to skinning.
541
+
542
+ The corpus table's "vs/ps" stage claim was overreach: the only sampled
543
+ instance in `dx11-instruction-coverage.json`'s `allOpcodeExamples` is
544
+ `graphics\effect.dx11\managed\space\specialfx\particles\gpu\emit.sm_depth`
545
+ tagged `stageName: "geometry"`, paired with the sibling `store_raw` opcode in
546
+ the exact same file/technique (also tagged `"geometry"`) — a GPU particle
547
+ emission pass, which is much more plausibly a compute-style workload than a
548
+ genuine vertex/pixel `ByteAddressBuffer` read. Do not assume `ld_raw` is
549
+ `vs/ps`-reachable without a concrete counter-example (unlike `atomic_iadd`,
550
+ which has one — see that section).
551
+
552
+ ### Confidence
553
+ **Medium** — HLSLcc source lowering is directly read and clear, but no shipping
554
+ corpus evidence of `ld_raw` reaching a WebGL2-validated vs/ps program was found
555
+ (only 54 instances total, none flagged in the skinning/JointMat decision
556
+ trail); the sole sampled corpus example is paired with `store_raw` in a
557
+ likely-compute GPU-particle-emission shader, not a vs/ps stage.
558
+
559
+ ---
560
+
561
+ ## `store_raw` (54 instances)
562
+
563
+ ### Semantics
564
+ Writes one or more 32-bit components to a raw UAV (`RWByteAddressBuffer`) at a
565
+ byte offset, per D3D11 `store_raw` — inverse of `ld_raw`.
566
+
567
+ ### GLSL lowering
568
+ Same function as `store_structured`, `TranslateShaderStorageStore`, case
569
+ `OPCODE_STORE_RAW` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:1519-1523`,
570
+ dispatched at `toGLSLInstruction.cpp:4338-4346`). Operands: `psDest=asOperands[0]`,
571
+ `psDestByteOff=asOperands[1]`, `psSrc=asOperands[2]` — no `psDestAddr`, so no
572
+ `[<idx>].value` indirection (`toGLSLInstruction.cpp:1546-1551`, gated on `if
573
+ (psDestAddr)`):
574
+ ```glsl
575
+ <name>_buf[(<byteOffExpr> >> 2u) + <comp>u] = <uint-or-int-cast>(src.<swz-or-x>);
576
+ ```
577
+
578
+ ### Type rules
579
+ Same as `store_structured`: uint by default, int only for the special
580
+ single-int `RWStructuredBuffer<int4>`-shaped buffer detection (which does not
581
+ actually apply to raw buffers in practice, since that check is structured-buffer
582
+ specific, but the code path is shared).
583
+
584
+ ### Helpers needed
585
+ - `rawStoreComponent(bufName, byteOffset, component, value, srcType)` —
586
+ reference-only.
587
+
588
+ ### Edge cases
589
+ No destination register, so `_sat` never applies.
590
+
591
+ ### WebGL2 notes
592
+ Requires a writable UAV/SSBO — unavailable in ES 3.00. **Out of scope for the
593
+ WebGL2 emitter**; treat identically to `store_structured` (detect and refuse).
594
+
595
+ ### Confidence
596
+ **Medium** — same reasoning as `ld_raw`; low corpus volume (54) with no
597
+ known vs/ps shipping path.
598
+
599
+ ---
600
+
601
+ ## `atomic_iadd` (27) / `imm_atomic_iadd` (24) / `imm_atomic_exch` (9) / `atomic_umax` (6) / `atomic_umin` (3)
602
+
603
+ Grouped: all five are handled by the single `TranslateAtomicMemOp` function and
604
+ differ only in GLSL function name and whether a previous-value destination
605
+ exists.
606
+
607
+ ### Semantics
608
+ - `atomic_iadd` / `imm_atomic_iadd`: atomically add a value to a UAV or
609
+ groupshared (TGSM) memory location; the `imm_` form additionally returns the
610
+ pre-add value into a destination register, the non-`imm_` form discards it.
611
+ - `imm_atomic_exch`: atomically replace the memory location's value and return
612
+ the previous value (exchange has no non-`imm_` counterpart in DXBC).
613
+ - `atomic_umax` / `atomic_umin`: atomically store `max`/`min` of the current
614
+ value and the source value (unsigned comparison), discarding the previous
615
+ value (no `imm_` variants observed in this corpus, though DXBC defines
616
+ `imm_atomic_umax`/`umin` too).
617
+
618
+ ### GLSL lowering
619
+ `TranslateAtomicMemOp` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:1685-2087`,
620
+ dispatched at `toGLSLInstruction.cpp:4428-4450`). Per-opcode operand layout and
621
+ GLSL function name selected at the top of the function
622
+ (`toGLSLInstruction.cpp:1700-1935`):
623
+
624
+ | Opcode | `func` | operands (dest addr order) |
625
+ |---|---|---|
626
+ | `imm_atomic_iadd` | `"Add"` | `[0]=previousValue, [1]=dest, [2]=destAddr, [3]=src` |
627
+ | `atomic_iadd` | `"Add"` | `[0]=dest, [1]=destAddr, [2]=src` (no previousValue) |
628
+ | `imm_atomic_exch` | `"Exchange"` | `[0]=previousValue, [1]=dest, [2]=destAddr, [3]=src` |
629
+ | `atomic_umin` | `"Min"` | `[0]=dest, [1]=destAddr, [2]=src` |
630
+ | `atomic_umax` | `"Max"` | `[0]=dest, [1]=destAddr, [2]=src` |
631
+
632
+ Resource-kind detection (`toGLSLInstruction.cpp:1968-2011`): if `dest` is not
633
+ `OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY`, look up its UAV binding.
634
+ `RTYPE_UAV_RWTYPED` → `isUint = (returnType == RETURN_TYPE_UINT)` and derive
635
+ `texDim` (1/2/3) from the UAV's resource dimension; `RTYPE_UAV_RWSTRUCTURED` →
636
+ `isUint=false, ui32DstDataTypeFlag |= TO_FLAG_INTEGER` only if
637
+ `DeclareRWStructuredBufferTemplateTypeAsInteger` says so; TGSM defaults to
638
+ `isUint=true, texDim=0`. Then:
639
+ ```glsl
640
+ ui32DataTypeFlag = isUint ? (TO_FLAG_UNSIGNED_INTEGER|TO_AUTO_BITCAST_TO_UINT)
641
+ : (TO_FLAG_INTEGER|TO_AUTO_BITCAST_TO_INT);
642
+ ```
643
+ (`toGLSLInstruction.cpp:2013-2016`). If `previousValue` exists, it's assigned via
644
+ `AddAssignToDest(previousValue, isUint?SVT_UINT:SVT_INT, 1, ...)` **before** the
645
+ atomic call text is emitted (`toGLSLInstruction.cpp:2018-2019`). Final emitted
646
+ shape (`toGLSLInstruction.cpp:2021-2086`):
647
+ - Typed-UAV (image) form (`texDim>0`):
648
+ ```glsl
649
+ [prev =] imageAtomic<Func>(<uavName>, <addr>.<xy|xyz|x>, <src-cast>);
650
+ ```
651
+ - Buffer/TGSM form (`texDim==0`):
652
+ ```glsl
653
+ [prev =] atomic<Func>(<name>[_buf][<addr> >> 2u], <src-cast>);
654
+ ```
655
+ where `<name>_buf[<addr>]` gains an extra `.value[<addrY> >> 2u]` indirection
656
+ if the destAddr operand carries **two** swizzle components (X and Y) — "structured
657
+ buf if we have both x & y swizzles" (`toGLSLInstruction.cpp:2055-2062`) — vs. a
658
+ raw buffer's single-component addressing.
659
+ - No trailing `;` is appended when `previousValue` is set (the assignment prologue
660
+ handles closing parens/semicolon via `AddAssignPrologue`); otherwise the line
661
+ is terminated with `;\n` directly (`toGLSLInstruction.cpp:2081-2086`).
662
+
663
+ ### Type rules
664
+ - Both `compare`/`src` (when present) are cast via the same
665
+ `ui32DataTypeFlag` (`TO_AUTO_BITCAST_TO_UINT` or `_INT`), i.e. bitcast from the
666
+ underlying float-vec4 register storage to whichever integer type the target
667
+ memory location actually holds.
668
+ - `previousValue` destination type is `SVT_UINT` or `SVT_INT` matching `isUint`,
669
+ never float directly (though the register itself is stored as float and
670
+ bitcast at the destination-write site per this project's register model).
671
+ - These do **not** produce DXBC comparison masks; `atomic_umin`/`umax` compare
672
+ internally to select which value to store, but the DXBC/GLSL result is the
673
+ stored/previous *value*, not a boolean or 0xFFFFFFFF/0 mask.
674
+
675
+ ### Helpers needed
676
+ None implementable for WebGL2 (see below). Reference-only helpers if ever
677
+ targeting ES 3.10+/desktop: `atomicUavOrTgsmOp(func, dest, addr, src,
678
+ [compare], [returnsPrevious])`.
679
+
680
+ ### Edge cases
681
+ - `atomic_umin`/`atomic_umax` are unsigned-only per DXBC (`imin`/`imax` variants
682
+ exist separately for signed); do not conflate with `atomic_imin`/`imax`.
683
+ - The 1-vs-2-swizzle-component destAddr distinction (structured vs. raw
684
+ addressing within the same atomic path) must be preserved if this is ever
685
+ ported.
686
+
687
+ ### WebGL2 notes
688
+ `atomicAdd`/`atomicMin`/`atomicMax`/`atomicExchange`/`atomicCompSwap` (buffer/
689
+ shared-memory atomics) and `imageAtomicAdd` etc. (image atomics) are GLSL ES
690
+ 3.10+ compute-shader-only built-ins; none exist in ES 3.00 vertex/fragment
691
+ shaders, regardless of which D3D11 stage the source instruction came from.
692
+ **Fully out of scope for the WebGL2 emitter.** Detect any `atomic_*`/
693
+ `imm_atomic_*` opcode and refuse the stage.
694
+
695
+ Do **not** rely on "these only occur in compute shaders" as the reason for
696
+ skipping this check in the pixel-stage emitter: `dx11-instruction-coverage.json`
697
+ (`initialCandidateOpcodeExamples`) records a concrete `atomic_iadd` instance in
698
+ `graphics\effect.dx11\managed\space\specialfx\lensflares\lensflareoccludert.sm_depth`
699
+ tagged `stageIndex: 1` / `stageName: "pixel"` — an actual D3D11 pixel shader
700
+ (D3D11.1 permits UAV access from pixel shaders; a lens-flare occlusion query
701
+ written into a UAV counter from the pixel stage is a plausible, ordinary
702
+ technique, not a mislabeled compute pass). The other four sampled corpus
703
+ instances of this opcode group (`atomic_iadd` again in `createhistograms.sm_depth`,
704
+ `imm_atomic_iadd` in `clear.sm_depth`, `imm_atomic_exch`/`atomic_umax`/
705
+ `atomic_umin` in `computelightlists.sm_depth`) are all tagged `stageName:
706
+ "geometry"` by the same tool, but those files' names (histogram building,
707
+ particle-buffer clear, light-list culling) are classic GPGPU compute-shader
708
+ workloads, and the coverage tool has no `"compute"` stage label at all in its
709
+ vocabulary (only `vertex`/`pixel`/`geometry`/`hull`) — those four are much
710
+ better explained as compute shaders whose slot the tool mislabels than as
711
+ genuine geometry shaders (a real DX11 geometry shader cannot declare
712
+ `dcl_thread_group`/`sync`, which the *sibling* `createhistograms.sm_depth`
713
+ example for the `sync` opcode does, and DXBC does not permit that in a
714
+ geometry stage). The `lensflareoccludert` pixel-stage instance is not
715
+ explained away by that reasoning and must be treated as real: the emitter's
716
+ **ps-stage translation path itself** needs the detect-and-refuse check, not
717
+ just a pre-filter that assumes this opcode class never survives into a vs/ps
718
+ compile.
719
+
720
+ `TRANSPILING-GAPS.md:100-114` ("UAV and atomic path... blocked for current
721
+ WebGL2 target... `atomic_iadd`: 6... Affects the `lensflareoccludert` path" —
722
+ note that count was measured on a narrower earlier corpus slice than this
723
+ family's 450k-instruction sweep, which shows 27) already names
724
+ `lensflareoccludert` as the blocked case; it does not itself claim the
725
+ instruction is compute-only, and the per-instruction corpus scan confirms it
726
+ is not.
727
+
728
+ ### Confidence
729
+ **High** for the DXBC/GLSL semantics (read directly from source) and for the
730
+ WebGL2-out-of-scope conclusion (ES 3.00 has no atomics in any stage, so the
731
+ `lensflareoccludert` pixel-shader counterexample does not change the outcome).
732
+ **Medium** on the blanket "compute-only" framing carried over from the
733
+ project's decision log — confirmed false for at least the `lensflareoccludert`
734
+ `atomic_iadd` instance, which is a pixel shader; the emitter must not assume
735
+ this opcode family is filtered out before reaching ps-stage translation.
736
+
737
+ ---
738
+
739
+ ## `ld_uav_typed` (18 instances)
740
+
741
+ ### Semantics
742
+ Reads a texel/element from a typed UAV (`RWTexture*`/`RWBuffer`) at an integer
743
+ address, per D3D11 `ld_uav_typed` — the read counterpart of `store_uav_typed`.
744
+
745
+ ### GLSL lowering
746
+ Case `OPCODE_LD_UAV_TYPED` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:4266-4336`).
747
+ Operands: `psDest=asOperands[0]`, `psSrcAddr=asOperands[1]`,
748
+ `psSrc=asOperands[2]` (UAV resource). Address component mask derived from
749
+ `psInst->eResDim` the same way as `store_uav_typed` (1/2/3 components,
750
+ `toGLSLInstruction.cpp:4281-4300`). Source data type resolved from the UAV's
751
+ reflected return type (`RETURN_TYPE_FLOAT/SINT/UINT/SNORM/UNORM`,
752
+ `toGLSLInstruction.cpp:4302-4325`, with `UNORM`/`SNORM` both mapping to
753
+ `SVT_FLOAT`). Emitted form:
754
+ ```glsl
755
+ dest.mask = imageLoad(<uavName>, <addr-by-dimension>)<swizzle-with-mask>;
756
+ ```
757
+ using `AddAssignToDest`/`AddAssignPrologue` for the destination, and
758
+ `TranslateOperandSwizzleWithMask` to apply the resource operand's own swizzle to
759
+ the 4-component `imageLoad` result restricted to the destination write mask
760
+ (`toGLSLInstruction.cpp:4327-4335`).
761
+
762
+ ### Type rules
763
+ Destination component type follows the UAV's reflected return type, not a fixed
764
+ uint-array convention (unlike `ld_structured`/`ld_raw`) — `imageLoad` always
765
+ returns a 4-component vector of the sampler/image's declared base type
766
+ (`ivec4`/`uvec4`/`vec4`), then swizzled down to the requested mask.
767
+
768
+ ### Helpers needed
769
+ None for WebGL2 (see below). Reference-only: `imageLoadTyped(uav, addr,
770
+ returnType)`.
771
+
772
+ ### Edge cases
773
+ Requires resource-binding reflection (return type, dimension) exactly like
774
+ `store_uav_typed` — doubly blocked when `RDEF` is stripped, independent of the
775
+ image-type gap below.
776
+
777
+ ### WebGL2 notes
778
+ `imageLoad` and image sampler types (`image2D` etc.) are ES 3.10+-only.
779
+ **Fully out of scope for the WebGL2 emitter.** Matches the draft transpiler's
780
+ explicit blocker (`Dx11GlesDraftTranspiler.js:129`) and the
781
+ `TRANSPILING-GAPS.md` UAV decision. With only 18 corpus instances (vs.
782
+ `store_uav_typed`'s 642), this is very likely the read side of a small set of
783
+ compute/UAV effects rather than any vs/ps-reachable code path — but the
784
+ concrete sampled instance (`dx11-instruction-coverage.json`, `allOpcodeExamples`)
785
+ is `graphics\effect.dx11\managed\space\postprocess\measureexposure.sm_depth`
786
+ (a tone-mapping average-luminance compute pass), **not** `lensflareoccludert`.
787
+ `lensflareoccludert` is confirmed by `TRANSPILING-GAPS.md:100-114` to carry
788
+ `dcl_unordered_access_view_typed`/`atomic_iadd` (and per this family's own
789
+ corpus scan, `atomic_iadd` there is a **pixel**-stage instance — see that
790
+ section); do not conflate the two effects or assume `ld_uav_typed`'s
791
+ compute-only reasoning transfers to `atomic_iadd`.
792
+
793
+ ### Confidence
794
+ **High** — same corroboration as `store_uav_typed`, corrected to cite the
795
+ right sampled effect (`measureexposure.sm_depth`, not `lensflareoccludert`).
796
+
797
+ ---
798
+
799
+ ## `bufinfo` (0 instances — specified for completeness only)
800
+
801
+ ### Semantics
802
+ Returns the element count of a structured/raw/typed buffer resource, per D3D11
803
+ `bufinfo` (used for bounds-checking dynamic buffer accesses).
804
+
805
+ ### GLSL lowering
806
+ Case `OPCODE_BUFINFO` (`vendor/HLSLcc/src/toGLSLInstruction.cpp:4753-4766`):
807
+ ```glsl
808
+ dest = <ctor>(<resourceName>_buf.length());
809
+ ```
810
+ via `AddAssignToDest(dest, SVT_INT, 1, ...)` then
811
+ `<resourceName>_buf.length()` then `AddAssignPrologue`.
812
+
813
+ ### Type rules
814
+ Result is always `SVT_INT` (a signed count), regardless of the buffer's element
815
+ type.
816
+
817
+ ### Helpers needed
818
+ None — zero corpus instances; no helper is justified without evidence of use.
819
+
820
+ ### Edge cases
821
+ N/A — not observed in this corpus.
822
+
823
+ ### WebGL2 notes
824
+ `.length()` on a GLSL array returns a compile-time constant for fixed-size
825
+ arrays (legal in ES 3.00) but is a **run-time** query only for SSBO
826
+ runtime-sized arrays (`buffer T { ... x[]; }`), which do not exist in ES 3.00.
827
+ Since this opcode's only real use (bounds-checking a structured/raw buffer) is
828
+ tied to the same SSBO-only resource kinds as `ld_structured`/`ld_raw`, it would
829
+ be out of scope for the same reason if it appeared. Zero corpus instances means
830
+ no immediate action item, but the emitter should still recognize and refuse the
831
+ opcode defensively rather than silently mis-translate.
832
+
833
+ ### Confidence
834
+ **Low** — zero real-world evidence in this corpus; semantics and lowering are
835
+ read directly from HLSLcc source but entirely unvalidated against any actual
836
+ shader.
837
+
838
+ ---
839
+
840
+ ## Helpers summary
841
+
842
+ Helpers the memory-structured family needs the emitter to provide (grouped by
843
+ whether they produce runnable WebGL2 output or are reference/detection-only):
844
+
845
+ **Shipping (WebGL2-runnable) helpers:**
846
+ 1. `lowerBoneTransformsToCb3` — package-time text-rewrite pass that removes the
847
+ HLSLcc `t0` SSBO declaration, grows `cb3.data[]` to at least 200 `vec4`s,
848
+ strips the native bone-ring-buffer offset add, and rewrites each 4-component
849
+ `t0_buf[idx].value[...]` row load to `cb3.data[26 + idx*3 + row]` (optionally
850
+ swizzled). Port of `lowerWebgl2SkinningAbi` +
851
+ `lowerStructuredBoneLoad` in `scripts/packageTr2WebglEffect.js:826-902`.
852
+ 2. `lowerBlendIndicesToFloatAttribute` — package-time rewrite of
853
+ `uvec4`/`ivec4`/etc. `in_BLENDINDICES<n>` vertex inputs to `vec<N>`, part of
854
+ the same pass (`packageTr2WebglEffect.js:840-843`).
855
+
856
+ **Reference-only helpers** (mirror HLSLcc's non-WebGL2 SSBO/UAV/atomic/compute
857
+ lowering, useful for documentation, debugging, and any future non-WebGL2
858
+ backend, but must never be emitted as final WebGL2 output):
859
+ 3. `structuredLoadComponent(bufName, structIndex, byteOffset, component, destType)`
860
+ 4. `structuredStoreComponent(bufName, structIndex, byteOffset, component, value, srcType)`
861
+ 5. `rawLoadComponent(bufName, byteOffset, component, destType)`
862
+ 6. `rawStoreComponent(bufName, byteOffset, component, value, srcType)`
863
+ 7. `imageLoadTyped(uav, addr, returnType)`
864
+ 8. `imageStoreTyped(uav, addr, value, returnType)`
865
+ 9. `atomicUavOrTgsmOp(func, dest, addr, src, compare?, returnsPrevious?)`
866
+
867
+ **Detection/refusal helpers** (must run before emission; there is no GLSL ES
868
+ 3.00 lowering to fall back to):
869
+ 10. `refuseComputeOnlyOpcode(opcodeName)` — for `sync`, `store_structured`
870
+ (non-skinning), `store_raw`, `store_uav_typed`, `ld_uav_typed`,
871
+ `atomic_iadd`, `imm_atomic_iadd`, `imm_atomic_exch`, `atomic_umax`,
872
+ `atomic_umin`, and `bufinfo` should it ever appear.
873
+ 11. `refuseNonSkinningStructuredLoad(resourceName)` — for `ld_structured` /
874
+ `ld_raw` uses that are not the vertex-stage `BoneTransforms` skinning
875
+ pattern (e.g. `LightBuffer`/`LightIndexBuffer` pixel-stage reads).
876
+
877
+ ---
878
+
879
+ *Sources consulted: `vendor/HLSLcc/src/toGLSLInstruction.cpp`,
880
+ `toGLSLOperand.cpp`, `toGLSLDeclaration.cpp`, `HLSLccToolkit.cpp`,
881
+ `internal_includes/languages.h`; `vendor/HLSLcc/CARBONENGINEJS-FORK.md`;
882
+ `../shaderdiscovery/TRANSPILING-GAPS.md`; `../shaderdiscovery/AGENT-FINDINGS/decisions/005-`,
883
+ `016-`, `028-cewg-*`, `014-`/`015-`/`016-`/`017-joint-matrix-*`;
884
+ `../shaderdiscovery/src/core/transpiler/gles/Dx11GlesDraftTranspiler.js` (hints
885
+ only); `scripts/packageTr2WebglEffect.js`;
886
+ `../shaderdiscovery/artifacts/dx11-instruction-coverage.json` (per-instruction
887
+ stage ground truth, used to correct several "compute-only" stage-reality
888
+ claims); `../shaderdiscovery/artifacts/ab-shader-set/work/skinned_fxdirectionalv5/*.es300.glsl`
889
+ (actual generated fixture, used to correct the `ld_structured` cb3 worked
890
+ example).*