@carbonenginejs/runtime-resource 0.11.1 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/dist/CjsResMan.js +4 -4
  2. package/dist/CjsResMan.js.map +1 -1
  3. package/dist/_virtual/_rollupPluginBabelHelpers.js +1 -4
  4. package/dist/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
  5. package/dist/format/CjsByteReader.js +310 -0
  6. package/dist/format/CjsByteReader.js.map +1 -0
  7. package/dist/format/CjsByteWriter.js +242 -0
  8. package/dist/format/CjsByteWriter.js.map +1 -0
  9. package/dist/format/CjsFormatError.js +41 -0
  10. package/dist/format/CjsFormatError.js.map +1 -0
  11. package/dist/format/CjsStringTable.js +268 -0
  12. package/dist/format/CjsStringTable.js.map +1 -0
  13. package/dist/format/carbonEffect/CjsCarbonEffectReader.js +361 -0
  14. package/dist/format/carbonEffect/CjsCarbonEffectReader.js.map +1 -0
  15. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js +373 -0
  16. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js.map +1 -0
  17. package/dist/format/carbonEffect/carbonDescriptionFromPortable.js +372 -0
  18. package/dist/format/carbonEffect/carbonDescriptionFromPortable.js.map +1 -0
  19. package/dist/format/carbonEffect/carbonEffectBackendBlock.js +427 -0
  20. package/dist/format/carbonEffect/carbonEffectBackendBlock.js.map +1 -0
  21. package/dist/format/carbonEffect/carbonEffectRecords.js +955 -0
  22. package/dist/format/carbonEffect/carbonEffectRecords.js.map +1 -0
  23. package/dist/format/compareUtf8.js +36 -0
  24. package/dist/format/compareUtf8.js.map +1 -0
  25. package/dist/format/index.js +11 -0
  26. package/dist/format/index.js.map +1 -0
  27. package/dist/formats/bnk/CjsBnkFormat.js +18 -2
  28. package/dist/formats/bnk/CjsBnkFormat.js.map +1 -1
  29. package/dist/formats/bnk/core/eventAction.js +305 -0
  30. package/dist/formats/bnk/core/eventAction.js.map +1 -0
  31. package/dist/formats/bnk/core/graph.js +3 -6
  32. package/dist/formats/bnk/core/graph.js.map +1 -1
  33. package/dist/formats/bnk/core/helpers.js +62 -11
  34. package/dist/formats/bnk/core/helpers.js.map +1 -1
  35. package/dist/formats/bnk/core/nodeBase.js +532 -0
  36. package/dist/formats/bnk/core/nodeBase.js.map +1 -0
  37. package/dist/formats/bnk/core/sfxNodes.js +632 -0
  38. package/dist/formats/bnk/core/sfxNodes.js.map +1 -0
  39. package/dist/formats/bnk/core/soundbanksInfo.js +59 -51
  40. package/dist/formats/bnk/core/soundbanksInfo.js.map +1 -1
  41. package/dist/formats/gr2/CjsGr2Format.js +256 -13
  42. package/dist/formats/gr2/CjsGr2Format.js.map +1 -1
  43. package/dist/formats/gr2/core/helpers.js +15 -15
  44. package/dist/formats/gr2/core/helpers.js.map +1 -1
  45. package/dist/formats/gr2/core/json.js +1 -1
  46. package/dist/formats/gr2/core/json.js.map +1 -1
  47. package/dist/formats/gr2/core/targets.js +1 -1
  48. package/dist/formats/gr2/core/targets.js.map +1 -1
  49. package/dist/formats/gr2/index.js +0 -1
  50. package/dist/formats/gr2/index.js.map +1 -1
  51. package/dist/formats/hlsl/core/HlslReader.js +7 -257
  52. package/dist/formats/hlsl/core/HlslReader.js.map +1 -1
  53. package/dist/formats/hlsl/core/analysis.js +1 -1
  54. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js +781 -0
  55. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js.map +1 -0
  56. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js +21 -1
  57. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js.map +1 -1
  58. package/dist/formats/hlsl/index.js +1 -0
  59. package/dist/formats/hlsl/index.js.map +1 -1
  60. package/dist/formats/index.js +3 -6
  61. package/dist/formats/index.js.map +1 -1
  62. package/dist/formats/webgl/core/cewg/CewgPackage.js +2 -1
  63. package/dist/formats/webgl/core/cewg/CewgPackage.js.map +1 -1
  64. package/dist/formats/webgl/core/cewg/binary.js +11 -93
  65. package/dist/formats/webgl/core/cewg/binary.js.map +1 -1
  66. package/dist/formats/webgl/core/effectPackage.js +4 -1
  67. package/dist/formats/webgl/core/effectPackage.js.map +1 -1
  68. package/dist/formats/webgl/core/errors.js +3 -3
  69. package/dist/formats/webgl/core/errors.js.map +1 -1
  70. package/dist/formats/webgpu/CjsWebgpuFormat.js +4 -23
  71. package/dist/formats/webgpu/CjsWebgpuFormat.js.map +1 -1
  72. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js +197 -0
  73. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js.map +1 -0
  74. package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js +368 -0
  75. package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js.map +1 -0
  76. package/dist/formats/webgpu/core/cewgpu/containerViews.js +355 -0
  77. package/dist/formats/webgpu/core/cewgpu/containerViews.js.map +1 -0
  78. package/dist/formats/webgpu/core/cewgpu/validateContainer.js +90 -0
  79. package/dist/formats/webgpu/core/cewgpu/validateContainer.js.map +1 -0
  80. package/dist/formats/webgpu/core/effectBackendBodySet.js +21 -20
  81. package/dist/formats/webgpu/core/effectBackendBodySet.js.map +1 -1
  82. package/dist/formats/webgpu/core/helpers.js +87 -92
  83. package/dist/formats/webgpu/core/helpers.js.map +1 -1
  84. package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js +14 -13
  85. package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js.map +1 -1
  86. package/dist/formats/webgpu/core/ir/inferValueTypes.js +10 -8
  87. package/dist/formats/webgpu/core/ir/inferValueTypes.js.map +1 -1
  88. package/dist/formats/webgpu/core/packageEffect.js +94 -9
  89. package/dist/formats/webgpu/core/packageEffect.js.map +1 -1
  90. package/dist/formats/webgpu/core/packageMetadata.js +10 -1
  91. package/dist/formats/webgpu/core/packageMetadata.js.map +1 -1
  92. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js +17 -15
  93. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js.map +1 -1
  94. package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js +13 -12
  95. package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js.map +1 -1
  96. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js +9 -8
  97. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js.map +1 -1
  98. package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js +23 -22
  99. package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js.map +1 -1
  100. package/dist/formats/webgpu/core/wgsl/selectionPlans.js +46 -44
  101. package/dist/formats/webgpu/core/wgsl/selectionPlans.js.map +1 -1
  102. package/dist/resource/CjsResource.js +700 -684
  103. package/dist/resource/CjsResource.js.map +1 -1
  104. package/dist/resource/Tr2LightProfileRes.js +18 -27
  105. package/dist/resource/Tr2LightProfileRes.js.map +1 -1
  106. package/dist/resource/audio/CjsAudioBufferRes.js +2 -2
  107. package/dist/resource/audio/CjsAudioBufferRes.js.map +1 -1
  108. package/dist/resource/audio/CjsAudioRes.js +2 -2
  109. package/dist/resource/audio/CjsAudioRes.js.map +1 -1
  110. package/dist/resource/geometry/TriGeometryRes.js +605 -586
  111. package/dist/resource/geometry/TriGeometryRes.js.map +1 -1
  112. package/dist/resource/geometry/granny/Tr2GrannyStateRes.js +21 -30
  113. package/dist/resource/geometry/granny/Tr2GrannyStateRes.js.map +1 -1
  114. package/dist/resource/geometry/granny/TriGrannyRes.js +21 -30
  115. package/dist/resource/geometry/granny/TriGrannyRes.js.map +1 -1
  116. package/dist/resource/shader/Tr2EffectRes.js +9 -7
  117. package/dist/resource/shader/Tr2EffectRes.js.map +1 -1
  118. package/dist/resource/shader/Tr2MaterialArea.js +5 -3
  119. package/dist/resource/shader/Tr2MaterialArea.js.map +1 -1
  120. package/dist/resource/shader/Tr2MaterialMesh.js +4 -2
  121. package/dist/resource/shader/Tr2MaterialMesh.js.map +1 -1
  122. package/dist/resource/shader/Tr2MaterialRes.js +5 -3
  123. package/dist/resource/shader/Tr2MaterialRes.js.map +1 -1
  124. package/dist/resource/shader/Tr2Shader.js +26 -21
  125. package/dist/resource/shader/Tr2Shader.js.map +1 -1
  126. package/dist/resource/shader/Tr2ShaderPermutation.js +8 -6
  127. package/dist/resource/shader/Tr2ShaderPermutation.js.map +1 -1
  128. package/dist/resource/shader/reflection/Tr2EffectDefine.js +5 -3
  129. package/dist/resource/shader/reflection/Tr2EffectDefine.js.map +1 -1
  130. package/dist/resource/shader/reflection/Tr2EffectDescription.js +5 -3
  131. package/dist/resource/shader/reflection/Tr2EffectDescription.js.map +1 -1
  132. package/dist/resource/shader/reflection/Tr2EffectLibrary.js +17 -15
  133. package/dist/resource/shader/reflection/Tr2EffectLibrary.js.map +1 -1
  134. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js +11 -9
  135. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js.map +1 -1
  136. package/dist/resource/shader/reflection/Tr2EffectResource.js +8 -6
  137. package/dist/resource/shader/reflection/Tr2EffectResource.js.map +1 -1
  138. package/dist/resource/shader/reflection/Tr2EffectStageInput.js +15 -13
  139. package/dist/resource/shader/reflection/Tr2EffectStageInput.js.map +1 -1
  140. package/dist/resource/shader/reflection/Tr2EffectTechnique.js +7 -5
  141. package/dist/resource/shader/reflection/Tr2EffectTechnique.js.map +1 -1
  142. package/dist/resource/shader/reflection/Tr2Pass.js +11 -9
  143. package/dist/resource/shader/reflection/Tr2Pass.js.map +1 -1
  144. package/dist/resource/shader/sampler/Tr2SamplerSetup.js +8 -6
  145. package/dist/resource/shader/sampler/Tr2SamplerSetup.js.map +1 -1
  146. package/dist/resource/texture/CjsTextureArrayRes.js +400 -411
  147. package/dist/resource/texture/CjsTextureArrayRes.js.map +1 -1
  148. package/dist/resource/texture/Tr2ImageRes.js +99 -95
  149. package/dist/resource/texture/Tr2ImageRes.js.map +1 -1
  150. package/dist/resource/texture/TriTextureRes.js +312 -273
  151. package/dist/resource/texture/TriTextureRes.js.map +1 -1
  152. package/docs/README.md +13 -1
  153. package/docs/architecture.md +3 -3
  154. package/docs/concepts/resource-lifecycle.md +9 -2
  155. package/docs/concepts/shader-resource-model.md +114 -0
  156. package/docs/concepts/writing-an-engine-adapter.md +115 -0
  157. package/docs/formats/README.md +24 -1
  158. package/docs/formats/carbon-effect-container.md +452 -0
  159. package/docs/formats/dxbc/README.md +68 -0
  160. package/docs/formats/dxbc/architecture.md +80 -0
  161. package/docs/formats/dxbc/reference/api.md +77 -0
  162. package/docs/formats/dxbc/reference/classes/README.md +9 -0
  163. package/docs/formats/dxbc/reference/decoded-output.md +122 -0
  164. package/docs/formats/gr2.md +3 -4
  165. package/docs/formats/hlsl/README.md +54 -0
  166. package/docs/formats/hlsl/architecture.md +67 -0
  167. package/docs/formats/hlsl/guides/hydrating-json-output.md +62 -0
  168. package/docs/formats/hlsl/guides/reading-effects.md +64 -0
  169. package/docs/formats/hlsl/reference/advanced-analysis.md +66 -0
  170. package/docs/formats/hlsl/reference/api.md +98 -0
  171. package/docs/formats/hlsl/reference/classes/README.md +11 -0
  172. package/docs/formats/hlsl/reference/json-graph.md +100 -0
  173. package/docs/formats/hlsl/reference/portable-reflection.md +141 -0
  174. package/docs/formats/provenance.md +32 -17
  175. package/docs/formats/webgl/README.md +57 -0
  176. package/docs/formats/webgl/architecture.md +70 -0
  177. package/docs/formats/webgl/carbon-constant-layouts.md +326 -0
  178. package/docs/formats/webgl/decl-io.md +1234 -0
  179. package/docs/formats/webgl/effect-reflection.md +127 -0
  180. package/docs/formats/webgl/memory-structured.md +871 -0
  181. package/docs/formats/webgl/reference/classes/README.md +9 -0
  182. package/docs/formats/webgl/texture-sample.md +964 -0
  183. package/docs/formats/webgpu/README.md +84 -0
  184. package/docs/formats/webgpu/architecture.md +96 -0
  185. package/docs/formats/webgpu/formats/cewgpu.md +216 -0
  186. package/docs/formats/webgpu/guides/effect-packaging.md +191 -0
  187. package/docs/formats/webgpu/reference/api.md +197 -0
  188. package/docs/formats/webgpu/reference/classes/README.md +9 -0
  189. package/docs/formats/webgpu/reference/wgsl-compatibility.md +1543 -0
  190. package/docs/formats/wwise.md +45 -4
  191. package/docs/reference/classes/core.md +80 -0
  192. package/docs/reference/classes/formats.md +24 -34
  193. package/docs/reference/events.md +25 -0
  194. package/docs/reference/motherlode-cache.md +22 -8
  195. package/docs/reference/workers.md +5 -5
  196. package/docs/roadmap.md +61 -41
  197. package/format-notices/bnk/NOTICE +5 -4
  198. package/format-notices/webgl/NOTICE +1 -1
  199. package/package.json +2 -1
  200. package/dist/formats/gr2/core/CjsFormatGr2.js +0 -273
  201. package/dist/formats/gr2/core/CjsFormatGr2.js.map +0 -1
  202. package/dist/formats/webgpu/core/cewgpu/CewgpuPackage.js +0 -415
  203. package/dist/formats/webgpu/core/cewgpu/CewgpuPackage.js.map +0 -1
  204. package/dist/formats/webgpu/core/cewgpu/CewgpuPackageBuilder.js +0 -100
  205. package/dist/formats/webgpu/core/cewgpu/CewgpuPackageBuilder.js.map +0 -1
  206. package/dist/formats/webgpu/core/cewgpu/binary.js +0 -93
  207. package/dist/formats/webgpu/core/cewgpu/binary.js.map +0 -1
  208. package/dist/formats/webgpu/core/cewgpu/tags.js +0 -17
  209. package/dist/formats/webgpu/core/cewgpu/tags.js.map +0 -1
  210. package/dist/formats/webgpu/core/effectPackageValidation.js +0 -1078
  211. package/dist/formats/webgpu/core/effectPackageValidation.js.map +0 -1
@@ -0,0 +1,1234 @@
1
+ # DXBC -> GLSL ES 3.00 Lowering Spec: `decl-io` Family
2
+
3
+ Status: Evolving
4
+ Scope: `@carbonenginejs/runtime-resource/formats/webgl` declaration and stage-I/O lowering
5
+ Audience: Shader translator maintainers and reviewers
6
+ Summary: Defines the DXBC declaration and I/O rules used by the GLSL emitter.
7
+
8
+ Target: GLSL ES 3.00 (WebGL2), vertex + pixel stages only. No SSBOs, no compute, no
9
+ tessellation/geometry stages (facts for those stages are cited where they explain
10
+ *why* a code path in the authority source is skipped, never as things this emitter
11
+ must implement).
12
+
13
+ Register model for this emitter (differs from stock HLSLcc, stated once here so every
14
+ section below can just say "the float vec4 register file"): every DXBC temp/output
15
+ register is stored as a `vec4` of floats. Instructions that need int or uint semantics
16
+ bitcast at the use site with `floatBitsToInt` / `floatBitsToUint` on read and
17
+ `intBitsToFloat` / `uintBitsToFloat` on write, exactly mirroring the bitcast operator
18
+ names HLSLcc itself uses when it emits its own (non-register-collapsed) integer path
19
+ (`GetBitcastOp`, `vendor/HLSLcc/src/toGLSLOperand.cpp:327-353`). Stock HLSLcc instead
20
+ runs `DataTypeAnalysis` and gives each temp register a native-typed shadow variable
21
+ (`u_xlatN` float, `u_xlatiN` int, `u_xlatuN` uint, ...). This spec calls out every
22
+ place our float-only convention diverges from that stock behavior.
23
+
24
+ Corpus counts (450k-instruction sweep, 1611 EVE Online DX11 effects), used to order
25
+ sections by real-world impact:
26
+
27
+ | Opcode | Count |
28
+ |---|---|
29
+ | `dcl_output` | 15197 |
30
+ | `dcl_input` | 11726 |
31
+ | `dcl_constant_buffer` | 10160 |
32
+ | `dcl_input_ps` | 6995 |
33
+ | `dcl_resource` | 6317 |
34
+ | `dcl_global_flags` | 5848 |
35
+ | `dcl_temps` | 5035 |
36
+ | `dcl_sampler` | 3277 |
37
+ | `dcl_output_siv` | 2825 |
38
+ | `dcl_resource_structured` | 684 |
39
+ | `dcl_input_ps_siv` | 538 |
40
+ | `dcl_input_ps_sgv` | 42 |
41
+ | `dcl_input_sgv` | 18 |
42
+ | `dcl_indexable_temp` | 16 |
43
+ | `dcl_input_siv` | 3 |
44
+ | `customdata` | 159 |
45
+
46
+ All line numbers below refer to `vendor/HLSLcc/src/toGLSLDeclaration.cpp`
47
+ unless another file is named.
48
+
49
+ ---
50
+
51
+ ## Emission order and `#version` boilerplate
52
+
53
+ Authority: `vendor/HLSLcc/src/toGLSL.cpp`.
54
+
55
+ HLSLcc builds the final GLSL text as two bstrings that get concatenated at the very
56
+ end (`toGLSL.cpp:1039` region: `// Concat extensions and glsl for the final shader
57
+ code.`):
58
+
59
+ 1. `extensions` — seeded with the version string (`toGLSL.cpp:617`,
60
+ `GetVersionString(LANG_ES_300)` returns `"#version 300 es\n"` at `toGLSL.cpp:387`),
61
+ then every `#extension ... : require|enable` line added by `RequireExtension` /
62
+ `EnableExtension` calls made while walking declarations and instructions.
63
+ 2. `glsl` — populated in this order:
64
+ a. `AddVersionDependentCode` (`toGLSL.cpp:93`, called at `toGLSL.cpp:647`) emits,
65
+ for pixel shaders on an ES target (`toGLSL.cpp:307-328`):
66
+ ```
67
+ precision highp float;
68
+ precision highp int;
69
+ ```
70
+ (int precision is forced to `highp` "to avoid issues on platforms that
71
+ actually implement mediump" — comment at `toGLSL.cpp:326`). Vertex shaders get
72
+ no default-precision block from this path.
73
+ b. If the shader has any constant buffers or textures, two `#define` blocks for
74
+ `UNITY_LOCATION`/`UNITY_BINDING`/`HLSLCC_ENABLE_UNIFORM_BUFFERS` macros
75
+ (`toGLSL.cpp:667-691`). These are Unity-build plumbing; a from-scratch JS
76
+ emitter does not need the macro indirection, only the concrete
77
+ `layout(std140) uniform ... { vec4 data[N]; } cbN;` text it expands to when the
78
+ macros are defined to `1` (the emitter should hardcode the "enabled" expansion,
79
+ not restate the `#if`/`#else` machinery, since WebGL2 has no runtime toggle for
80
+ it).
81
+ c. `TranslateDeclaration` for every `Declaration` in bytecode order — this is the
82
+ entire `decl-io` family plus every other declaration opcode.
83
+ d. `void main() { ... }` with early-main redirect code, then translated
84
+ instructions.
85
+
86
+ **WebGL2 note**: emit exactly `#version 300 es` as line 1, nothing before it (WebGL2
87
+ rejects any non-comment/non-whitespace token before `#version`). Do not emit the
88
+ `GL_FRAGMENT_PRECISION_HIGH` `#ifdef` dance used for `LANG_ES_100`
89
+ (`toGLSL.cpp:311-320`) — that branch is ES 2.0-only; ES 3.00 always has `highp`
90
+ fragment-shader float support and HLSLcc itself only takes the unconditional
91
+ `precision highp float;` branch for `LANG_ES_300` (`toGLSL.cpp:321-324`).
92
+
93
+ **Confidence: high** — this is a straight read of the concatenation order in
94
+ `toGLSL.cpp`, not a translated instruction whose semantics could be ambiguous.
95
+
96
+ ---
97
+
98
+ ## `dcl_global_flags` (5848)
99
+
100
+ **Semantics**: DXBC global shader flags (`D3D10_SB_GLOBAL_FLAGS`) declared once per
101
+ shader: refactoring-allowed, force-early-depth-stencil, enable double-precision float
102
+ ops, skip-optimization, enable raw/structured buffers, etc. Purely a compiler-hint
103
+ bitfield, not a register declaration.
104
+
105
+ **GLSL lowering**: `toGLSLDeclaration.cpp:2781-2809`. Only two bits produce any GLSL
106
+ text in our target subset:
107
+ - `GLOBAL_FLAG_FORCE_EARLY_DEPTH_STENCIL` on a pixel shader emits
108
+ `layout(early_fragment_tests) in;\n` (`2785-2789`) unconditionally — no target
109
+ language gate in the C++ source.
110
+ - `GLOBAL_FLAG_REFACTORING_ALLOWED` combined with `HavePreciseQualifier` emits four
111
+ `precise <type> u_xlat_precise_<type>;` globals (`2790-2802`) — desktop-only
112
+ qualifier gate, unreachable for `LANG_ES_300`.
113
+ - `GLOBAL_FLAG_ENABLE_DOUBLE_PRECISION_FLOAT_OPS` requires
114
+ `GL_ARB_gpu_shader_fp64` (`2803-2807`) — desktop-only, unreachable for ES targets.
115
+
116
+ All other flag bits (skip-optimization, minimum-precision, enable-raw-and-structured,
117
+ force-early-*, all-resources-bound, etc.) are read by the decoder for correctness of
118
+ other lowering decisions but emit no GLSL text here.
119
+
120
+ **Type rules**: n/a — no operands, no register.
121
+
122
+ **Helpers needed**: none.
123
+
124
+ **Edge cases**: this instruction fires on almost every real shader (5848/1611 files ≈
125
+ one per shader stage), but the `FORCE_EARLY_DEPTH_STENCIL` bit itself is set on only a
126
+ minority of those. **WebGL2 note (important deviation from stock HLSLcc)**:
127
+ `layout(early_fragment_tests) in;` is a GLSL ES 3.10 / desktop-4.20 feature
128
+ (`GL_ARB_shader_image_load_store` era) and is **not part of GLSL ES 3.00** — WebGL2
129
+ will fail to compile a fragment shader containing this qualifier. Since the C++
130
+ source emits it unconditionally whenever the flag bit is set and the stage is a
131
+ pixel shader, this JS emitter must add a language gate stock HLSLcc does not have:
132
+ suppress the `layout(early_fragment_tests) in;` line entirely when targeting ES 3.00
133
+ (early-fragment-tests is a depth-test-ordering optimization hint only; skipping it is
134
+ always semantically safe, just potentially slower).
135
+
136
+ **Confidence: medium** — the flag-to-GLSL mapping is a direct source read (high
137
+ confidence), but whether any EVE shader in the corpus actually sets
138
+ `FORCE_EARLY_DEPTH_STENCIL` (making the WebGL2 gate load-bearing) was not verified
139
+ against the corpus in this pass.
140
+
141
+ ---
142
+
143
+ ## `dcl_temps` (5035)
144
+
145
+ **Semantics**: declares the count of general-purpose temporary registers (`r0..r{N-1}`)
146
+ used by the current shader phase (`D3D10_SB_OPCODE_DCL_TEMPS`, one dword: `ui32NumTemps`).
147
+
148
+ **GLSL lowering (stock HLSLcc)**: `toGLSLDeclaration.cpp:2425-2523`. For each temp
149
+ index `i`, HLSLcc's `DataTypeAnalysis` pass has already recorded which *native* GLSL
150
+ types that register was ever read/written as (`psFloatTempSizes[i]`,
151
+ `psIntTempSizes[i]`, `psUIntTempSizes[i]`, `psBoolTempSizes[i]`, plus 16/12/10-bit
152
+ minimum-precision and `fp64` variants), and HLSLcc declares one shadow variable per
153
+ type actually used, e.g.:
154
+ ```
155
+ vec4 u_xlat0;
156
+ int u_xlati0; // only if r0 was ever read/written as int
157
+ uint u_xlatu0; // only if r0 was ever read/written as uint
158
+ ```
159
+ (prefix macro `HLSLCC_TEMP_PREFIX` = `"u_xlat"`, `vendor/HLSLcc/include/hlslcc.h:127`).
160
+ On Switch targets only, each gets a `= <ctor>(0)` default initializer
161
+ (`2436-2498`) to dodge a false-positive uninitialized-variable compiler warning;
162
+ non-Switch targets declare with no initializer (`2501-2521`).
163
+
164
+ **GLSL lowering (this emitter's float-only register file — required deviation)**:
165
+ because this project does not run `DataTypeAnalysis` (that pass requires full
166
+ def/use dataflow over the instruction stream, ref: project brief), declare exactly
167
+ one `vec4` per temp index, unconditionally:
168
+ ```
169
+ vec4 r0;
170
+ vec4 r1;
171
+ ...
172
+ vec4 r{N-1};
173
+ ```
174
+ Every instruction that consumes `rN` as int/uint wraps the read in
175
+ `floatBitsToInt(rN)` / `floatBitsToUint(rN)`; every instruction that produces an
176
+ int/uint result destined for `rN` wraps the write in `intBitsToFloat(...)` /
177
+ `uintBitsToFloat(...)` before assigning. This is the single largest structural
178
+ deviation from the stock HLSLcc temp-register model in this whole family — flag it
179
+ prominently to the instruction-family authors, since every arithmetic/logic opcode's
180
+ lowering depends on this convention holding for `dcl_temps`-declared registers.
181
+
182
+ **Type rules**: declared type is always `vec4` (float). Actual read/write type is
183
+ determined per-instruction by the consuming/producing opcode, not by this
184
+ declaration.
185
+
186
+ **Helpers needed**: none beyond the four bitcast builtins (already core GLSL ES 3.00,
187
+ not user-defined helpers).
188
+
189
+ **Edge cases**: initializing temps to zero (as HLSLcc does only for Switch) is *not*
190
+ required for GLSL ES 3.00/WebGL2 correctness in general, but this project's
191
+ `vec4(0.0)` fallback convention for signature-only outputs (see `dcl_output` below)
192
+ suggests the same defensive default-init could be applied here too if any code path
193
+ is ever found reading a temp before it is written; no corpus evidence of that has
194
+ been found (`TRANSPILING-GAPS.md` "Already handled" section reports 0 instruction
195
+ blockers). Recommend leaving temps uninitialized (matches non-Switch stock
196
+ behavior) unless a specific shader is found to depend on zero-init.
197
+
198
+ **Confidence: high** for the *stock* semantics (direct source read); **medium** for
199
+ the float-only deviation's completeness, since it depends on every opcode in the
200
+ sibling instruction-lowering families correctly bitcasting at every read/write site —
201
+ a single missed bitcast silently corrupts values without a compile error.
202
+
203
+ ---
204
+
205
+ ## `dcl_indexable_temp` (16)
206
+
207
+ **Semantics**: declares a dynamically-indexable local array of temp registers
208
+ (`x0[i]`-style addressing, as opposed to the flat `r#` file), used when HLSL source
209
+ has to write to computed indices (e.g. unrolled small loops writing into a local
210
+ array). Encodes register index, element count, and per-element component width.
211
+
212
+ **GLSL lowering**: `toGLSLDeclaration.cpp:3085-3092`:
213
+ ```cpp
214
+ bformata(glsl, "vec%d TempArray%d[%d];\n", ui32RegComponentSize, ui32RegIndex, ui32RegCount);
215
+ ```
216
+ i.e. `vec{ComponentSize} TempArray{RegIndex}[{RegCount}];` — component width (1-4)
217
+ comes directly from the DXBC declaration's `sIdxTemp.ui32RegComponentSize`, not from
218
+ signature/DataTypeAnalysis lookups.
219
+
220
+ **Type rules**: always declared as `float`-family vector (`vecN`), never `ivecN`/
221
+ `uvecN`, even in stock HLSLcc — indexable temps are HLSL-source local arrays, whose
222
+ element type is resolved by the DXBC compiler down to float storage before emission
223
+ in every observed case in this authority source. This emitter should mirror that:
224
+ declare `vecN TempArray{RegIndex}[RegCount];` where `N` is exactly the decoded
225
+ `ui32RegComponentSize` (do not force to `vec4` — unlike the flat temp-register file,
226
+ indexable-temp width is a hard DXBC-encoded fact, not a convention this project
227
+ gets to choose). Bitcast at use sites the same way as regular temps if any consumer
228
+ reads/writes it as int/uint.
229
+
230
+ **Helpers needed**: none.
231
+
232
+ **Edge cases**: rare (16 total instructions across the whole corpus) — low priority,
233
+ but get the declared width right since a mismatched vector size is a hard GLSL
234
+ compile error, not a silent bug.
235
+
236
+ **WebGL2 notes**: none beyond standard array declaration syntax, which GLSL ES 3.00
237
+ supports natively.
238
+
239
+ **Confidence: high** — single unconditional code path, no branching on target
240
+ language or shader stage.
241
+
242
+ ---
243
+
244
+ ## `dcl_constant_buffer` (10160)
245
+
246
+ **Semantics**: declares a constant-buffer binding (`cb#`) and its total `vec4`-slot
247
+ count, used by later `mov`/arithmetic instructions that read `cb#[i].component`.
248
+ Authoritative source for constant *names/offsets/layout* is Carbon/Trinity RDEF-derived
249
+ metadata, not this fork (`CARBONENGINEJS-FORK.md`: "Carbon/Trinity metadata remains
250
+ authoritative for ... per-frame and per-object constant buffer layout"); this fork's
251
+ job is only to keep the register-stable `cb#` GLSL symbol emitting correctly when that
252
+ reflection metadata is present *or stripped*.
253
+
254
+ **GLSL lowering — stripped-RDEF fallback (the path this project actually depends on)**:
255
+ `toGLSLDeclaration.cpp:2530-2559`. When `GetConstantBufferFromBindingPoint` returns
256
+ `nullptr` (no RDEF, or RDEF present but missing this specific buffer — the CarbonEngineJS
257
+ fork's stripped-reflection case, `CARBONENGINEJS-FORK.md` "Constant-buffer operands
258
+ emit fallback register access such as `cb3.data[0]`"), HLSLcc emits:
259
+ ```cpp
260
+ bformata(glsl, "layout(std140) uniform %s {\n\tvec4 data[%d];\n} cb%d;\n",
261
+ name, psOperand->aui32ArraySizes[1], ui32BindingPoint);
262
+ ```
263
+ i.e. exactly:
264
+ ```glsl
265
+ layout(std140) uniform ConstantBuffer3 {
266
+ vec4 data[200];
267
+ } cb3;
268
+ ```
269
+ where `ConstantBuffer{N}` is a synthesized block-type name (`name` built at
270
+ `2541-2542`, `sprintf(name, "ConstantBuffer%d", ui32BindingPoint)`), the instance
271
+ name is always `cb{N}` (`ui32BindingPoint`), and the array size is
272
+ `psOperand->aui32ArraySizes[1]` — the DXBC declaration's own encoded slot count, read
273
+ directly off the `dcl_constant_buffer` instruction, independent of any reflection
274
+ data. This `cbN.data[i]` register-stable access convention is the ABI this whole
275
+ project is built to preserve (`CARBONENGINEJS-FORK.md` "Runtime Contract";
276
+ `TRANSPILING-GAPS.md` records the cross-stage `ConstantBuffer0` size-mismatch bug
277
+ this produces when VS/PS declare different slot counts for the same `cb#` — normalize
278
+ to the maximum observed size across stages sharing a binding point before emitting).
279
+ If `UNITY_LOCATION`/binding macros are relevant (`2550-2555`) they add a
280
+ `UNITY_LOCATION(%d)` prefix, which this emitter should expand directly to
281
+ `layout(location = N)` text (WebGL2 has no runtime macro toggle, so skip the
282
+ `#if`/`#else` indirection described above).
283
+
284
+ **GLSL lowering — named/reflected path**: when RDEF metadata for the named cbuffer
285
+ *is* present, HLSLcc instead calls `DeclareUBOConstants`
286
+ (`877-997`, used when `HLSLCC_FLAG_UNIFORM_BUFFER_OBJECT` is set) or
287
+ `DeclareStructConstants` (`1094+`) to emit one named `float`/`vecN`/matrix member per
288
+ reflected constant, inside `uniform {CBufferName} { ... };`. This path is **not** the
289
+ ABI this project keeps stable end-to-end (Carbon metadata is the source of truth for
290
+ per-constant names/offsets, per `CARBONENGINEJS-FORK.md`); treat the fallback
291
+ `cbN.data[i]` form above as the primary target shape for this emitter, and the named
292
+ path as background context only.
293
+
294
+ **Type rules**: every `data[i]` slot is a `vec4` of floats; a `mov` or arithmetic
295
+ instruction reading `cb3.data[5].x` as an int/uint bitcasts with `floatBitsToInt` /
296
+ `floatBitsToUint` exactly like a temp register read (this project's cbuffers are
297
+ `vec4`-slot float storage regardless of the HLSL source's cbuffer member types,
298
+ mirroring how the fallback path above has no member-type information at all).
299
+
300
+ **Helpers needed**: none for the declaration itself.
301
+
302
+ **Edge cases**:
303
+ - Cross-stage size mismatch: VS and PS `dcl_constant_buffer` for the same binding
304
+ point can carry different `aui32ArraySizes[1]` if each stage only reads a prefix of
305
+ the buffer (`TRANSPILING-GAPS.md`, "AB hull test-set blockers" — `boostervolumetric`
306
+ and `planeglow` failed WebGL2 *linking* because of this, not compilation). Emit the
307
+ **max** slot count seen for a given `cb#` across the linked program's stages.
308
+ - Vulkan-subpass-input (`2561-2636`) and `OVR_multiview` (`2638-2664`) special cases
309
+ are Vulkan/Unity-specific and out of scope for this project's DX11→WebGL2 EVE
310
+ corpus. **Correction**: these two are the only special cases actually in the
311
+ `2561-2670` range; the `"$Globals"` name check is a *different* piece of code, not
312
+ located there. There are two distinct `$Globals`-related checks elsewhere in this
313
+ file: (1) `psCBuf->name[0] == '$'` at `2674`, inside the *named/reflected* path
314
+ (`2672-2687`), which chooses `DeclareStructConstants` over `DeclareUBOConstants`
315
+ when `HLSLCC_FLAG_GLOBAL_CONSTS_NEVER_IN_UBO` is also set; and (2) the `"$Globals"`
316
+ string checks inside `DeclareUBOConstants`/`DeclareStructConstants` themselves
317
+ (`883`, `888`, `1105`, `1142`). None of this is reached by the stripped-RDEF
318
+ fallback path this project depends on (that path returns at `2558` before any of
319
+ `2561` onward runs) — so the conclusion ("$Globals is out of scope, handled
320
+ generically by the synthesized `ConstantBufferN` name") still holds, just not for
321
+ the reason/line-range originally cited.
322
+
323
+ **WebGL2 notes**: `layout(std140) uniform` blocks are core GLSL ES 3.00 — no
324
+ extension required. `std140` layout rules (16-byte vec4 alignment, `vec4 data[]`
325
+ array) mean every array element is a full 16 bytes regardless of the HLSL source
326
+ type, which is exactly why the flat `vec4 data[N]` fallback shape is both simple and
327
+ correct for any packed constant layout.
328
+
329
+ **Confidence: high** for the stripped-RDEF fallback shape (this is exactly what
330
+ `CARBONENGINEJS-FORK.md` documents as the load-bearing convention and what
331
+ `toGLSLDeclaration.cpp:2557` literally emits); **medium** on the cross-stage
332
+ normalization requirement, since that is a package/link-time policy documented in
333
+ `TRANSPILING-GAPS.md` as a known bug rather than something already fixed in
334
+ `toGLSLDeclaration.cpp` itself.
335
+
336
+ ---
337
+
338
+ ## `dcl_resource_structured` (684)
339
+
340
+ **Semantics**: declares a structured-buffer shader resource (`t#`) with a known
341
+ per-element byte stride, read via `ld_structured`. In EVE's skinned space-object
342
+ shaders this is `BoneTransforms` (a `Buffer<float4x3>`-shaped structured resource of
343
+ joint matrices), always paired with `ld_structured` in the instruction stream.
344
+
345
+ **GLSL lowering (stock HLSLcc)**: `toGLSLDeclaration.cpp:3680-3684` calls
346
+ `DeclareBufferVariable(..., isRaw=0, isUAV=0, ...)` (`1027-1092`), which emits an
347
+ SSBO. **Correction to the exact emitted text** (verified against `1042-1088`): the
348
+ struct member uses GLSL's `type[size] name;` array-declarator ordering, not
349
+ `type name[size];`, and the buffer *block* name is plain `t0` (`BufName`), not a
350
+ synthesized `t0_type_block_name` — the block's *member* variable is `t0_buf`:
351
+ ```glsl
352
+ struct t0_type {
353
+ uint[3] value; // stride/4, via bformata("...\t%s[%d] value;\n...", typeStr, stride/4)
354
+ };
355
+ layout(std430, binding = N) readonly buffer t0 {
356
+ t0_type t0_buf[];
357
+ };
358
+ ```
359
+ (exact struct/instance naming per `1035-1088`; `readonly` is unconditional for
360
+ non-UAV structured buffers, `1069-1070`; on Switch targets only, the block name gets
361
+ an `hlslcc_readonly` prefix instead, `1073`).
362
+
363
+ **Stripped-RDEF fallback**: `CARBONENGINEJS-FORK.md` — "Structured-buffer
364
+ declarations tolerate missing `RDEF` bindings by using the stride encoded in the DXBC
365
+ declaration extension" — i.e. `psDecl->ui32BufferStride` is read directly off the
366
+ `dcl_resource_structured` instruction's declaration-extension dword rather than off
367
+ reflection metadata, so the struct-element count (`stride/4`) is always available
368
+ even with `RDEF` stripped (decision shard
369
+ `022-dxbc-dcl-resource-declaration-tails.md` covers the sibling `dcl_resource`
370
+ one-word-tail decoding that established this pattern).
371
+
372
+ **Type rules**: SSBO element type is `uint[]` (raw dwords); the consuming
373
+ `ld_structured` instruction bitcasts each loaded dword to float/int/uint as needed
374
+ at the read site (out of this family's scope — instruction family territory).
375
+
376
+ **Helpers needed**: none for the declaration text itself.
377
+
378
+ **Edge cases / WebGL2 notes (hard blocker, not a lowering detail)**: **GLSL ES 3.00
379
+ has no shader storage buffers** — `buffer` blocks require GLSL ES 3.10+ or desktop
380
+ `GL_ARB_shader_storage_buffer_object`. This emitter **must not** emit the SSBO form
381
+ above for a WebGL2 target; it will not compile. The proven working path for this
382
+ project is a **package-time ABI rewrite**, not a change to this opcode's GLSL
383
+ lowering:
384
+ - `CARBONENGINEJS-FORK.md` / `016-cewg-skinning-abi-lowering-for-ccpwgl-2026-06-30.md`
385
+ / `TRANSPILING-GAPS.md`: drop the `t0` SSBO declaration entirely, grow the
386
+ paired `ConstantBuffer3` (`cb3`) to at least `vec4 data[200]`, and rewrite every
387
+ `ld_structured` row load against `t0` into `cb3.data[26 + blendIndex * 3 + row]`
388
+ (the ccpwgl `EveShip2` `JointMat` uniform-block projection, `58` joints × `12`
389
+ floats/joint = `696` floats appended at `cb3[26..199]`).
390
+ - This rewrite is package/runtime-ABI policy for the current ccpwgl compatibility
391
+ target, **not** a native CarbonEngineJS/Trinity representation
392
+ (`016-...md`, "Boundary" section) — a future non-ccpwgl consumer may want a real
393
+ buffer-texture or uniform-array representation of `BoneTransforms` instead of this
394
+ specific `cb3` splice.
395
+ - Validated: `skinned_quadv5.sm_converted_hi` 75/75 shaders translate, 336/336
396
+ WebGL2 programs link after the rewrite; `skinned_quadheatv5` 63/63 / 240/240.
397
+
398
+ **Confidence: high** on both "stock HLSLcc emits an SSBO" and "WebGL2 cannot use
399
+ SSBOs" (directly cited, unambiguous); **medium** on the exact `cb3.data[26 + ...]`
400
+ offset formula for any *other* structured buffer this family's opcode might describe
401
+ in a shader outside the skinned space-object set — this is proven only for
402
+ `BoneTransforms` specifically, not as a general `dcl_resource_structured` policy.
403
+
404
+ ---
405
+
406
+ ## `dcl_resource` (6317)
407
+
408
+ **Semantics**: declares a texture shader-resource-view binding (`t#`) with a resource
409
+ dimension (1D/2D/3D/Cube/arrays/MS variants/buffer) and a 4-nibble per-component
410
+ return type (float/unorm/snorm/int/uint), consumed later by `sample*`/`ld`/`resinfo`
411
+ instructions.
412
+
413
+ **GLSL lowering**: `toGLSLDeclaration.cpp:2689-2758`. Non-Vulkan path:
414
+ 1. Optional `UNITY_LOCATION(%d)` prefix (`2700-2718`) — expand directly to
415
+ `layout(location = N)` for this project (see cbuffer section's note on macro
416
+ indirection).
417
+ 2. `RESOURCE_DIMENSION_BUFFER` → `uniform samplerBuffer`/`isamplerBuffer`/
418
+ `usamplerBuffer` (`2722-2733`, requires `GL_EXT_texture_buffer` on ES targets,
419
+ `1404-1405` inside `GetSamplerType`) — not expected in this project's vertex/pixel
420
+ corpus (texel buffers are rare in EVE effects; treat as low-priority).
421
+ 3. All 2D/1D/Cube/array-of-those dimensions → `TranslateResourceTexture(..., 1)`
422
+ (`1597-1687`), which emits:
423
+ ```glsl
424
+ uniform <precision> <samplerType> t{N};
425
+ ```
426
+ and, only if the resource is a shadow-comparison texture
427
+ (`psDecl->ui32IsShadowTex`), an *additional* `<samplerType>Shadow t{N}_shadow`-style
428
+ binding (`1668-1686`) — this second declaration is **unconditional** on
429
+ `ui32IsShadowTex`/`samplerCanDoShadowCmp` and fires regardless of whether
430
+ `HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS` is set (that flag is not tested anywhere in
431
+ this code path). What the combine flag actually gates is a *separate*, earlier
432
+ block (`1632-1657`, inside `TranslateResourceTexture`): when
433
+ `HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS` is set, HLSLcc emits one *additional*
434
+ `uniform <samplerType> <name>;` per (texture, sampler) pair actually used together
435
+ (name from `TextureSamplerName`, iterating `psDecl->samplersUsed`) — on top of, not
436
+ instead of, the plain `t{N}` declaration at `1659-1666`. Since this project's
437
+ `dcl_sampler` section concludes the combine flag is required for any GLSL-ES
438
+ target, expect these extra per-combo uniforms to be emitted too, and confirm which
439
+ name (`t{N}` vs. the combo name) the consuming `sample`/`sample_c` instruction
440
+ family actually references before assuming plain `t{N}` is the only symbol that
441
+ matters.
442
+ 4. `TEXTURE2DMS`/`TEXTURE3D`/`TEXTURE2DMSARRAY` → `TranslateResourceTexture(..., 0)`
443
+ (same emission, `samplerCanDoShadowCmp=0`, i.e. never a comparison sampler).
444
+
445
+ **Sampler-type selection** (`GetSamplerType`, `1388-1551`): base type name keyed on
446
+ `RESOURCE_DIMENSION` (`sampler2D`, `sampler3D`, `samplerCube`, `sampler2DArray`,
447
+ `samplerCubeArray`, `sampler2DMS`, `sampler2DMSArray`, `sampler1D`/`1DArray`,
448
+ `samplerBuffer`), then prefixed `i`/`u` if the resource's reflected
449
+ `RESOURCE_RETURN_TYPE` is `RETURN_TYPE_SINT`/`RETURN_TYPE_UINT` (`1406-1544`); every
450
+ other return type (`UNORM`/`SNORM`/`FLOAT`/`TYPELESS`) maps to the plain (float)
451
+ sampler. Return type comes from `ResourceBinding::ui32ReturnType`
452
+ (RDEF-reflected) when available; when RDEF is stripped, this fork's
453
+ `dcl_resource` one-word declaration-tail decoding
454
+ (`022-dxbc-dcl-resource-declaration-tails.md`) supplies the same fact from the raw
455
+ DXBC declaration instead: the trailing dword is four 4-bit return-type nibbles,
456
+ preserved as `declarationData.resourceReturnType` — decode return type from there
457
+ when no RDEF binding is found, rather than defaulting to float/unorm blindly.
458
+
459
+ **Texture type 3 == native 3D** (`027-texture-type-3-is-native-3d-not-legacy-atlas.md`):
460
+ DXBC/Carbon resource type `3` (`RESOURCE_DIMENSION_TEXTURE3D`) is an authoritative
461
+ native volume texture, not a legacy packed-2D-atlas convention some older ccpwgl code
462
+ assumed — lower it to `sampler3D`/`texture3D`-family calls, never collapse it into
463
+ `sampler2D`.
464
+
465
+ **Type rules**: the sampler's *component* return type (float vs int vs uint) governs
466
+ which `texture()`/`texelFetch()` overload the consuming instruction must call
467
+ (`vec4`- vs `ivec4`- vs `uvec4`-returning) — declaration-time fact, consumed by the
468
+ `sample`/`ld` instruction family (out of scope here beyond noting the dependency).
469
+
470
+ **Helpers needed**: none for the declaration; sampler precision comes from
471
+ `GetSamplerPrecision` (`1553-1569`) — **not** `highp` by default. The function only
472
+ returns `"highp "` when the resource's reflected precision is explicitly
473
+ `REFLECT_RESOURCE_PRECISION_HIGHP`; the `default`/`UNKNOWN`/`LOWP` case (`1560-1563`,
474
+ the path this project's stripped-RDEF fallback always hits, since there is no
475
+ reflected `ResourceBinding::ePrecision` to consult) returns `EmitLowp(...) ? "lowp "
476
+ : "mediump "`, and `EmitLowp` (`languages.h:69-73`) is `true` only for `LANG_ES_100` —
477
+ so for this project's `LANG_ES_300` target the *actual* default sampler precision
478
+ emitted is **`mediump`**, not `highp`. (WebGL2 does support `highp` samplers in
479
+ fragment shaders, unlike GLSL ES 1.00, but stock HLSLcc does not default to
480
+ requesting it — this project must decide separately whether to request `highp`
481
+ samplers, rather than assuming the stock lowering already does.)
482
+
483
+ **Edge cases**: MSAA resource dimensions (`TEXTURE2DMS`/`TEXTURE2DMSARRAY`) need
484
+ `GL_OES_texture_storage_multisample_2d_array` on ES targets for the array variant
485
+ (`1516-1519`) — WebGL2 exposes multisample textures but **not** `texelFetch` on them
486
+ from a fragment shader in the same way desktop does; treat multisample resources as
487
+ out-of-scope/low-confidence for this corpus unless a specific EVE effect is proven to
488
+ need them. Cube-array (`TEXTURECUBEARRAY`) needs
489
+ `GL_OES_texture_cube_map_array`/`GL_EXT_texture_cube_map_array` (`1608-1619`) — **not
490
+ core in GLSL ES 3.00**, only in 3.20/desktop or via those extensions, which are not
491
+ universally available on WebGL2 (`OES_texture_cube_map_array` is an optional WebGL2
492
+ extension) — flag as a target blocker if any EVE effect requires it without checking
493
+ extension availability at runtime.
494
+
495
+ **WebGL2 notes**: combined texture+sampler objects (GLSL `sampler2D` as a single
496
+ opaque uniform) are exactly WebGL2's own texture model — no separate texture/sampler
497
+ descriptor split is needed for this target (that split only matters for the Vulkan
498
+ backend in `TranslateVulkanResource`, out of scope).
499
+
500
+ **Confidence: high** for 2D/Cube/array dimension mapping (heavily used, directly
501
+ cited); **medium** for MS/cube-array/buffer dimensions (low corpus incidence, GLSL ES
502
+ 3.00 extension availability not verified per-device).
503
+
504
+ ---
505
+
506
+ ## `dcl_sampler` (3277)
507
+
508
+ **Semantics**: declares a sampler-state binding (`s#`) — filter mode, address mode,
509
+ comparison mode, LOD clamp/bias — consumed by `sample`/`sample_c`/`sample_l`
510
+ instructions when combined with a `t#` resource via `dcl_resource`'s
511
+ `samplersUsed` set.
512
+
513
+ **GLSL lowering**: `toGLSLDeclaration.cpp:3348-3365`. For **non-Vulkan** targets (this
514
+ project's WebGL2 target), the entire case body is gated behind
515
+ `if (psContext->IsVulkan()) { ...; break; }` — falling through to `break;` with **no
516
+ GLSL text emitted at all** for OpenGL/GLSL/GLSL-ES targets. The separate sampler
517
+ object is *not* represented as its own GLSL declaration on this target; it is folded
518
+ into the combined `uniform sampler2D t{N};` declaration emitted by `dcl_resource`
519
+ above (`HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS`-style combined texture+sampler model,
520
+ the only model GLSL ES 3.00 supports). The one piece of state this instruction does
521
+ carry into codegen is whether the sampler is a comparison sampler
522
+ (`D3D10_SB_SAMPLER_MODE_COMPARISON`) — that fact reaches
523
+ `TranslateResourceTexture`'s shadow-texture branch (`ui32IsShadowTex`, decided
524
+ upstream at declaration-plan time from which `s#`/`t#` pairs get used together in
525
+ `sample_c`-family instructions, not from this opcode's own GLSL emission path).
526
+
527
+ **Type rules**: n/a — no GLSL variable is produced for this opcode on WebGL2/GLSL
528
+ targets.
529
+
530
+ **Helpers needed**: none.
531
+
532
+ **Edge cases**: DX11 sampler-state fields that have no WebGL2 shader-side
533
+ representation at all (address mode, filter mode, LOD bias/clamp, anisotropy,
534
+ border color) are **runtime binding-state policy**, not shader text — they must be
535
+ applied via `gl.texParameteri`/`gl.samplerParameteri` from Carbon/Trinity sampler
536
+ metadata at draw time, entirely outside this translator
537
+ (`TRANSPILING-GAPS.md`, "Exporter and runtime policy": "WebGL sampler-state
538
+ application" is explicitly listed as deferred/out-of-scope for the DXBC reader and
539
+ draft transpiler).
540
+
541
+ **WebGL2 notes**: this is the single opcode in the family whose stock-HLSLcc-for-our-
542
+ target lowering is "emit nothing" — worth flagging clearly to the implementing
543
+ engineer so they don't go looking for a `s0` GLSL symbol; the register-stable name
544
+ `s0` only shows up in Carbon binding-manifest metadata (`CARBONENGINEJS-FORK.md`:
545
+ "`s0`" is one of the register-stable symbols this fork preserves for
546
+ `Tr2EffectBindingManifest`), not in the GLSL source text itself.
547
+
548
+ **Confidence: high** — the Vulkan/non-Vulkan branch is unconditional and
549
+ unambiguous; the only judgment call is confirming this project always wants
550
+ `HLSLCC_FLAG_COMBINE_TEXTURE_SAMPLERS`-style combined sampling (yes — required for
551
+ any WebGL2/GLSL-ES target, since GLSL ES 3.00 has no separate sampler-object type).
552
+
553
+ ---
554
+
555
+ ## `dcl_input` (11726)
556
+
557
+ **Semantics**: declares a shader-stage input register (vertex-shader per-vertex
558
+ attribute, or any non-pixel stage's plain input) bound to an input-signature (ISGN)
559
+ row: semantic name/index, component mask, component type (float/sint/uint),
560
+ interpolation mode (non-VS stages).
561
+
562
+ **GLSL lowering**: `toGLSLDeclaration.cpp:2061-2186`. Several early-outs
563
+ (`2070-2105`) skip declaration for control-flow/thread-ID-style pseudo-inputs and
564
+ already-declared-as-array registers — none apply to plain vertex-shader attributes,
565
+ the dominant case in this corpus. For a normal vertex input:
566
+ 1. Name: `GetDeclaredInputName` → `inputPrefix + semanticName + semanticIndex`
567
+ (`HLSLCrossCompilerContext.cpp:169-211`); for a vertex shader `inputPrefix =
568
+ "in_"` (`toGLSL.cpp:33`) — i.e. **`in_POSITION0`, `in_NORMAL0`, `in_TEXCOORD3`,
569
+ `in_BLENDINDICES0`**, etc. This `in_<SEMANTIC><index>` naming is the register-
570
+ stable vertex-attribute ABI surface Carbon metadata and package/runtime tooling
571
+ bind against.
572
+ 2. Storage qualifier: `"in"` for any target where `InOutSupported` is true
573
+ (`2121-2127`) — true for `LANG_ES_300` (WebGL2's `in`/`out` keyword model, not the
574
+ legacy `attribute`/`varying` GLSL ES 1.00 keywords).
575
+ 3. Precision: `highp` unless the operand carries a `OPERAND_MIN_PRECISION_*` hint
576
+ (`2129-2159`) — DXBC minimum-precision annotations are rare in this corpus; default
577
+ to `highp` for all vertex attributes absent contrary evidence.
578
+ 4. Component type/count (`DeclareInput`, `232-390`, using the ISGN row's
579
+ `eComponentType` and `GetNumberBitsSet(ui32Mask)` for element count, **not** the
580
+ operand's own write mask) → `float`/`vecN` (`INOUT_COMPONENT_FLOAT32`),
581
+ `int`/`ivecN` (`SINT32`), or `uint`/`uvecN` (`UINT32`) (`265-291`).
582
+ 5. Final text: `in <precision> <type> in_<SEMANTIC><index>;` (`362-388` default
583
+ branch, non-array case — the common case for VS attributes).
584
+
585
+ **Attribute naming / BINORMAL→BITANGENT alias (load-bearing for this project)**:
586
+ HLSLcc derives the attribute name purely from the DXBC ISGN semantic string, which
587
+ for EVE's split-tangent-space vertex format is literally `BINORMAL` (`in_BINORMAL0`)
588
+ — but Carbon/Trinity per-vertex-stream metadata for that exact same GR2 mesh channel
589
+ calls it `BITANGENT`
590
+ (`../shaderdiscovery/knowledge/trinity-metadata/shader-discovery-truths.md:126-133`,
591
+ `../shaderdiscovery/knowledge/carbon-metadata-contract/hlslcc-transpile-spike.md:195-201`).
592
+ This is a **naming alias, not a semantic difference** — same vertex buffer channel,
593
+ two different names used by two different authorities. The proven fix is a
594
+ package-time rewrite, *after* HLSLcc emission, not a change to this opcode's GLSL
595
+ lowering itself: `scripts/packageTr2WebglEffect.js` normalizes every
596
+ `in_BINORMAL{n} -> in_BITANGENT{n}` for a vertex shader whose stage contract
597
+ declares a `BITANGENT` pipeline input, so the runtime's metadata-driven attribute
598
+ binder (which looks up attributes by the Carbon name) finds the symbol it expects.
599
+ Validated: `unpackedskinned_quadv5`/`unpackedskinned_quadheatv5` regenerated with no
600
+ `in_BINORMAL*` symbols remaining, still link (336/336, 80/80 WebGL2 programs).
601
+ **This emitter should keep emitting `in_BINORMALn`** (matching HLSLcc/DXBC ISGN
602
+ truth) **and rely on the package-time rewrite step**, not bake a BINORMAL→BITANGENT
603
+ special case into the opcode lowering itself — the alias is a runtime-ABI fact, not a
604
+ DXBC-to-GLSL translation fact.
605
+
606
+ **Type rules**: component type/count are ISGN facts (declaration-time), independent
607
+ of this project's float-register-file convention for `r#` temps — a vertex attribute
608
+ declared `uvec4` really is a GLSL `uvec4`-typed `in` variable at declaration time (see
609
+ next paragraph for why that is still a problem at the runtime-binding layer).
610
+
611
+ **Helpers needed**: `HandleInputRedirect` (`toGLSLDeclaration.cpp:1689-1806`) is an
612
+ HLSLcc-internal hull/domain-shader phase-input staging mechanism (`phase{N}_Input...`
613
+ temporaries) — out of scope for this project's vertex/pixel-only target; no
614
+ equivalent helper is needed here.
615
+
616
+ **Edge cases — the uvec/ivec attribute problem (required deviation, highest-priority
617
+ item in this family)**: stock HLSLcc, run as designed, declares integer-semantic
618
+ vertex inputs with their true GLSL integer vector type, e.g. `in uvec4
619
+ in_BLENDINDICES0;` for a `BLENDINDICES` stream reflected as `UINT32` component type
620
+ (`DeclareInput`, `270-274`). **This project's runtime (ccpwgl) binds all mesh
621
+ attribute channels, including blend indices, as float vertex attributes via
622
+ `gl.vertexAttribPointer` (not `vertexAttribIPointer`)** — a real `uvec4 in_...`
623
+ declaration either fails to link against that float-typed buffer binding, or links
624
+ but silently produces garbage/invisible geometry
625
+ (`028-cewg-skinned-blend-index-abi-lowering.md`: "Raw CEWG validation can link
626
+ integer attributes, but ccpwgl runtime binding can still fail or produce invisible
627
+ geometry if the source reaches compile as `uvec4`."). The proven, validated fix
628
+ (`028-...md`; `TRANSPILING-GAPS.md` "Ranked helper action plan" documents the general
629
+ version of this class of bug): **lower every integer-component-type `dcl_input`
630
+ vertex attribute to its float-vector equivalent at declaration time** —
631
+ ```glsl
632
+ in vec4 in_BLENDINDICES0; // not uvec4
633
+ ```
634
+ and bitcast at every *use* site instead: any instruction reading `in_BLENDINDICES0`
635
+ as an index wraps it in `floatBitsToUint(in_BLENDINDICES0)` (or, if the actual buffer
636
+ data was uploaded as plain float index values rather than bit-pattern-encoded uints —
637
+ verify per-attribute — a plain `uint(in_BLENDINDICES0.x)` truncating conversion
638
+ instead of a bitcast; the CEWG lowering evidence describes casting at use sites but
639
+ does not pin down which of these two forms every producer used, see Confidence
640
+ below). This is the **general form** of the family-level "every register is a float
641
+ vec4" convention applied specifically to `dcl_input`: unlike the `r#` temp file
642
+ (where float-only storage is this project's own choice), for vertex attributes it is
643
+ required by a concrete runtime constraint (float-only `vertexAttribPointer` binding),
644
+ proven necessary by regression (skinned geometry disappearing) and proven sufficient
645
+ (336/336 and 240/240 WebGL2 programs pass after the rewrite) in the corpus evidence
646
+ cited above.
647
+
648
+ **WebGL2 notes**: GLSL ES 3.00 does support genuine integer vertex attributes
649
+ (`in uvec4`/`in ivec4` with `vertexAttribIPointer`) — the float-only lowering above is
650
+ not a GLSL-ES-3.00 *language* limitation, it is a **runtime binding-layer**
651
+ limitation specific to this project's current ccpwgl consumer. A future
652
+ CarbonEngineJS-native consumer that binds attributes with `vertexAttribIPointer`
653
+ could use the stock HLSLcc `uvec4`/`ivec4` declarations directly and should not
654
+ inherit this workaround by default.
655
+
656
+ **Confidence: medium** — the *requirement* to avoid integer vertex-attribute types is
657
+ high confidence (proven by a specific regression + fix with before/after link
658
+ counts); the *exact* per-attribute cast convention (bitcast-reinterpret vs.
659
+ truncating-convert at the use site) is not nailed down to opcode-level precision in
660
+ the cited evidence and should be confirmed against the actual uploaded vertex-buffer
661
+ encoding for each integer semantic (`BLENDINDICES` specifically is documented;
662
+ generalize cautiously to any other integer-typed vertex semantic found in the corpus).
663
+
664
+ ---
665
+
666
+ ## `dcl_input_ps` (6995)
667
+
668
+ **Semantics**: declares a pixel-shader input register (an interpolated varying from
669
+ the previous stage) bound to an ISGN row, carrying an explicit DXBC interpolation
670
+ mode (`INTERPOLATION_CONSTANT`/`LINEAR`/`LINEAR_CENTROID`/`LINEAR_NOPERSPECTIVE`/etc.)
671
+ that this instruction's own `value.eInterpolation` field encodes (distinct from plain
672
+ `dcl_input`, which has no interpolation-mode payload).
673
+
674
+ **GLSL lowering**: `toGLSLDeclaration.cpp:2217-2423`. Name via `GetDeclaredInputName`
675
+ with `inputPrefix = "vs_"` (when the previous stage is a vertex shader, the common
676
+ case for this corpus — `toGLSL.cpp:59-79`) → **`vs_<SEMANTIC><index>`**, matching the
677
+ vertex shader's `outputPrefix = "vs_"` output name exactly (`toGLSL.cpp:34`), which is
678
+ how HLSLcc keeps VS-output/PS-input varying names paired across the two independently
679
+ compiled GLSL stage sources. Storage qualifier `"in"` (`2228-2231`, `InOutSupported`
680
+ true for ES 300). Interpolation qualifier:
681
+ - integer component type (`UINT32`/`SINT32`) forces `flat ` regardless of the DXBC
682
+ interpolation mode (`2238-2242`) — **GLSL spec requirement**, not a DXBC fact:
683
+ integer varyings must be flat-interpolated in any GLSL version.
684
+ - otherwise, map `psDecl->value.eInterpolation` (`2245-2284`): `INTERPOLATION_CONSTANT`
685
+ → `"flat "`; `LINEAR` → `""`; `LINEAR_CENTROID` → `"centroid "`;
686
+ `LINEAR_NOPERSPECTIVE` → `"noperspective "` **only if** `hasNoPerspective` (true for
687
+ `eTargetLanguage > LANG_ES_310`, i.e. **false for `LANG_ES_300`** — `2225`,
688
+ `2263-2265`); `LINEAR_SAMPLE`/`LINEAR_NOPERSPECTIVE_SAMPLE` → `"sample "`/
689
+ `"noperspective sample "` similarly gated.
690
+ - Precision: same `highp`/`mediump`/`lowp` mapping from `OPERAND_MIN_PRECISION_*` as
691
+ `dcl_input` (`2287-2317`).
692
+ - Final text: `DeclareInput(...)` (`2418`) → same underlying emitter as `dcl_input`,
693
+ producing `<interp>in <precision> <type> vs_<SEMANTIC><index>;`.
694
+
695
+ **Type rules**: identical component-type derivation to `dcl_input` (ISGN
696
+ `eComponentType` → `float`/`int`/`uint` base, `GetNumberBitsSet(mask)` → vector
697
+ width). The **integer-varying-must-be-flat** rule is a hard GLSL requirement in every
698
+ GLSL version, not a WebGL2-specific quirk — always emit `flat` for integer pixel
699
+ inputs regardless of the source DXBC interpolation mode field.
700
+
701
+ **Helpers needed**: none beyond core-language `flat`/`centroid` qualifiers (both core
702
+ in GLSL ES 3.00).
703
+
704
+ **Edge cases — framebuffer-fetch special case**: `2319-2416` handles reading back a
705
+ previously-written render target value (`SV_TargetN` bound both as PS input and PS
706
+ output, gated on `EXT_shader_framebuffer_fetch` + `HLSLCC_FLAG_SHADER_FRAMEBUFFER_FETCH`)
707
+ via `#define vs_SV_TargetN gl_LastFragData[N]` or a `layout(location=N) inout`
708
+ declaration. `GL_EXT_shader_framebuffer_fetch` **is not universally available in
709
+ WebGL2** and this project's target is standard WebGL2 fragment shaders reading only
710
+ their own current-fragment inputs — treat this branch as out of scope / not expected
711
+ to trigger in the EVE corpus; if it ever does, it is a target blocker requiring the
712
+ extension's presence to be verified at runtime, not silently assumed.
713
+
714
+ **WebGL2 notes**: `centroid`/`sample` interpolation qualifiers are core GLSL ES 3.00
715
+ keywords (no extension needed). `noperspective` is **not** — GLSL ES 3.00's spec does
716
+ not include `noperspective` as a keyword at all (it was added later, e.g. via
717
+ `NV_shader_noperspective_interpolation` for ES 3.0/3.1, and core only from ES 3.20);
718
+ WebGL2/ES 3.00 has no standard `noperspective` qualifier. The C++ source's own
719
+ `hasNoPerspective` gate (`eTargetLanguage <= LANG_ES_310 ? 0 : 1`, `2225`) is
720
+ therefore **correct as written, not stale or overly conservative** — it already
721
+ disables `noperspective` for `LANG_ES_300` (and `LANG_ES_310`) and only enables it for
722
+ targets above `LANG_ES_310`. This emitter should simply mirror that gate rather than
723
+ second-guess it: if any EVE pixel shader declares `INTERPOLATION_LINEAR_NOPERSPECTIVE`,
724
+ drop the qualifier for a `LANG_ES_300` target (falls back to perspective-correct
725
+ interpolation, a visible but non-fatal quality difference, vs. a hard compile error
726
+ from an unrecognized qualifier).
727
+
728
+ **Confidence: high** — the name-prefix pairing and integer-flat rule are high
729
+ confidence (direct source read, universal GLSL requirement); the
730
+ `noperspective`-in-ES-3.00 gate was re-checked directly against the GLSL ES 3.00
731
+ language facts (no `noperspective` keyword until ES 3.20 core / the
732
+ `NV_shader_noperspective_interpolation` extension) and confirmed correct as written,
733
+ so this is no longer an open question for this opcode.
734
+
735
+ ---
736
+
737
+ ## `dcl_output` (15197) — highest-frequency opcode in this family
738
+
739
+ **Semantics**: declares a vertex/pixel-shader output register (`o#`) bound to an
740
+ OSGN row (semantic name/index, component mask, component type), or (hull-shader
741
+ control-point phase only, not in this project's scope) routed to `gl_Position`.
742
+
743
+ **GLSL lowering**: `toGLSLDeclaration.cpp:2760-2779` → `AddUserOutput`
744
+ (`594-854`), gated by `OutputNeedsDeclaring` (`HLSLCrossCompilerContext.cpp:279-330`)
745
+ which dedups repeated partial-mask declarations of the same register via an
746
+ `acOutputDeclared` bitmask (a register can legally receive several `dcl_output`
747
+ instructions each covering a different component subset; only undeclared components
748
+ trigger new text).
749
+ - Component type/count: OSGN `eComponentType`/`GetNumberBitsSet(mask)` →
750
+ `float`/`vecN` (`FLOAT32`), `int`/`ivecN` (`SINT32`), `uint`/`uvecN` (`UINT32`)
751
+ (`622-655`) — identical derivation pattern to `dcl_input`.
752
+ - Precision: `highp`/`mediump`/`lowp` from `OPERAND_MIN_PRECISION_*` (`657-689`).
753
+ - **Pixel shader** (`691-795`): special output types first —
754
+ `OPERAND_TYPE_OUTPUT_DEPTH` → plain `gl_FragDepth` (built-in, no declaration
755
+ needed on GL/ES targets, `701-708`; the `EXT_frag_depth` `#define` shim at `703-706`
756
+ is `LANG_ES_100`-only, irrelevant for ES 300 where `gl_FragDepth` is core).
757
+ `OUTPUT_DEPTH_GREATER_EQUAL`/`OUTPUT_DEPTH_LESS_EQUAL` → `GL_ARB_conservative_depth`
758
+ layout qualifiers (`709-723`) — **desktop-only extension, not available in GLSL ES
759
+ 3.00**; if an EVE pixel shader uses conditional depth output, this project must fall
760
+ back to plain unconstrained `gl_FragDepth` writes (drop the `depth_greater`/
761
+ `depth_less` hint — it is a performance hint only, never required for correctness).
762
+ Otherwise (`725-793`, the common `SV_TargetN` case): name =
763
+ `outputPrefix("") + semanticName + renderTargetIndex` i.e. plain `SV_TargetN`, and
764
+ an explicit `layout(location = N) out <precision><type> SV_TargetN;` the first time
765
+ render target `N` is seen (`748-789`). **Correction to the gating fact**: the
766
+ `layout(location=N)` here is *not* gated by `HaveInOutLocationQualifier` — that
767
+ function (`languages.h:102-109`) is `false` for `LANG_ES_300` (only `true` for
768
+ `>=LANG_410` or `LANG_ES_310`). The actual gate at `752-753` is
769
+ `HaveInOutLocationQualifier(...) || HaveLimitedInOutLocationQualifier(...)`, and it
770
+ is `HaveLimitedInOutLocationQualifier` (`languages.h:93-100`, explicitly commented
771
+ "Only on vertex inputs and pixel outputs") that returns `true` for `LANG_ES_300`,
772
+ satisfying the `||` and producing the explicit location. Net behavior is unchanged
773
+ (ES 300 pixel outputs do get `layout(location=N)`), but the responsible function is
774
+ `HaveLimitedInOutLocationQualifier`, not `HaveInOutLocationQualifier` — worth getting
775
+ right since the two gates diverge for other declarations in this same family (see
776
+ `dcl_input`'s vertex-attribute declaration below, which tests
777
+ `HaveInOutLocationQualifier` alone and therefore does *not* get an explicit location
778
+ on `LANG_ES_300`). WebGL2 **requires** explicit `layout(location=N)` for any
779
+ multi-render-target fragment shader (no implicit `gl_FragData[N]` indexing in the
780
+ ES 3.00 core profile; `WriteToFragData` is true only for legacy/ES 100 targets).
781
+ - **Vertex shader** (`796-846`, the common case here since this project has no
782
+ geometry/hull/domain stages): name =
783
+ `outputPrefix("vs_") + semanticName + semanticIndex` → **`vs_<SEMANTIC><index>`**
784
+ (`804`), matching the pixel shader's `vs_`-prefixed input names described above.
785
+ Interpolation: integer types forced `flat` (`810-814`), float types resolved from
786
+ cross-stage dependency data (`GetInterpolationMode`, `815-818`) — in practice this
787
+ project should resolve interpolation per-varying from the **pixel shader's**
788
+ `dcl_input_ps` `value.eInterpolation` for the same semantic, since that is the only
789
+ side that actually encodes an interpolation mode in DXBC (vertex-shader outputs
790
+ carry no interpolation-mode field of their own). `layout(location=N)` from
791
+ `GetVaryingLocation` (`821-825`) — WebGL2 requires **matching** explicit varying
792
+ locations between the two independently compiled VS/PS GLSL programs when using
793
+ explicit locations, or (simpler, and what this project should default to) omit
794
+ `layout(location=...)` for varyings entirely and let the GLSL **linker** match by
795
+ name — WebGL2/GLSL ES 3.00 supports both; matching by name avoids a whole class of
796
+ location-numbering bugs across independently emitted VS/PS sources and is
797
+ recommended here. Final text: `<interp>out <precision><type> vs_<SEMANTIC><index>;`
798
+ (`838`).
799
+ - Early-out: register 0 with legacy `"POS"` semantic name in a vertex shader returns
800
+ without declaring anything (`619-620`) — that register is expected to be routed to
801
+ `gl_Position` via a separate `dcl_output_siv` `NAME_POSITION` declaration elsewhere
802
+ in the same instruction stream (see next section); do not double-declare it.
803
+
804
+ **Type rules**: identical component-type/count derivation to `dcl_input`/
805
+ `dcl_input_ps` (OSGN-driven, not write-mask-driven). The destination write mask on
806
+ the *declaration* itself only ever narrows which components get declared this pass
807
+ (merged across multiple partial declarations via `acOutputDeclared`); it has no
808
+ `_sat` concept — `saturate` only applies to the *instructions* that write the
809
+ register, never to a `dcl_output` declaration itself.
810
+
811
+ **Helpers needed**: `HandleOutputRedirect` — like `HandleInputRedirect`, this is
812
+ HLSLcc's hull-shader phase-output staging machinery; out of scope for this project's
813
+ vertex/pixel-only target.
814
+
815
+ **Edge cases — signature-only vertex outputs**: `023-signature-only-vertex-output-fallback.md`
816
+ documents `starmapnew`/`ubershader3d` variants whose OSGN declares outputs (`o3
817
+ -> COLOR2`, `o4 -> TEXCOORD1`, etc. for `starmapnew`; `o2 -> COLOR1`, `o7 ->
818
+ TEXCOORD4` for `ubershader3d`) with **no corresponding bytecode write** anywhere in
819
+ the instruction stream (audited: `references: 0`). GLSL ES 3.00 does not guarantee
820
+ zero-initialization of `out` varyings, and reading an unwritten varying downstream is
821
+ undefined/implementation-defined — this project's proven, adopted policy is to emit
822
+ an explicit deterministic zero-fill in early-main for any declared-but-never-written
823
+ output: `vs_COLOR2 = vec4(0.0);` before the translated instruction stream runs. Treat
824
+ this as expected, not a parser bug, for any output register with zero write
825
+ references.
826
+
827
+ **WebGL2 notes**: `layout(location=N)` for fragment-shader color outputs is
828
+ mandatory when more than one is declared (no implicit indexing); prefer
829
+ name-based linking (no `layout(location=...)`) for VS→PS varyings specifically,
830
+ per the recommendation above, to avoid cross-stage location-numbering mismatches.
831
+
832
+ **Confidence: high** for the SV_Target/varying declaration shape and naming
833
+ convention (heavily used, directly cited, corpus-validated linking counts exist
834
+ elsewhere in this family for related opcodes); **medium** for the
835
+ "resolve VS-output interpolation from the paired PS-input's `dcl_input_ps` mode"
836
+ policy recommendation, since that is this spec's own synthesis of how to handle
837
+ cross-stage interpolation resolution for a from-scratch JS emitter, not a literal
838
+ restatement of an already-proven CarbonEngineJS-fork behavior.
839
+
840
+ ---
841
+
842
+ ## `dcl_output_siv` (2825)
843
+
844
+ **Semantics**: declares a vertex/geometry/domain-shader output register bound to a
845
+ **system-value** semantic (`SV_Position`, `SV_RenderTargetArrayIndex`,
846
+ `SV_ClipDistance`, `SV_CullDistance`, `SV_ViewportArrayIndex`, `SV_PrimitiveID`, plus
847
+ tessellation-factor system values not relevant to this project's VS/PS-only scope)
848
+ rather than an arbitrary user semantic.
849
+
850
+ **GLSL lowering**: `toGLSLDeclaration.cpp:1899-2058`, switch on
851
+ `psDecl->asOperands[0].eSpecialName`:
852
+ - `NAME_POSITION` → `AddBuiltinOutput(psDecl, 0, "gl_Position")` (`1903-1907`) — the
853
+ **only** system value this project's vertex-shader corpus needs. `gl_Position` is
854
+ built-in in every GLSL version; no declaration text at all is emitted for it (the
855
+ register's read/write sites are simply redirected to the literal string
856
+ `gl_Position` instead of a synthesized varying name — see `AddBuiltinOutput`,
857
+ `413-...`, and note it early-returns immediately for any special name other than
858
+ `NAME_CLIP_DISTANCE`/`NAME_CULL_DISTANCE`, `419-420`, meaning **for `NAME_POSITION`
859
+ specifically this call is a pure no-op**: the redirection to `gl_Position` as an
860
+ operand string happens entirely in the instruction-translation/operand layer, not
861
+ here).
862
+ - `NAME_RENDER_TARGET_ARRAY_INDEX` → `gl_Layer`, requiring
863
+ `GL_AMD_vertex_shader_layer` on a vertex shader (`1908-1928`) — **not standard in
864
+ GLSL ES 3.00/WebGL2**; this is a desktop-GL vendor extension. Multi-layer
865
+ rendering from a vertex shader is out of scope for this project's target; treat as
866
+ a target blocker if encountered, not a silently-degraded feature.
867
+ - `NAME_CLIP_DISTANCE`/`NAME_CULL_DISTANCE` → `gl_ClipDistance`/`gl_CullDistance`
868
+ (`1929-1938`), requiring `GL_EXT_clip_cull_distance` on ES targets
869
+ (`449` inside `AddBuiltinOutput`) — this is a **real, available WebGL2 extension**
870
+ (`EXT_clip_cull_distance`), but is optional/not universally supported; treat as
871
+ conditionally available, verify at runtime before relying on it.
872
+ - `NAME_VIEWPORT_ARRAY_INDEX` → `gl_ViewportIndex` (`1939-1943`) — desktop-only
873
+ (`GL_ARB_shader_viewport_layer_array`/`GL_NV_viewport_array2`), no WebGL2
874
+ equivalent; out of scope.
875
+ - `NAME_PRIMITIVE_ID` → `gl_PrimitiveID` (`1949-1953`) — vertex shaders cannot write
876
+ `SV_PrimitiveID` in D3D (it is a geometry-shader-only output there); this branch is
877
+ reached only for geometry shaders, out of this project's scope.
878
+ - `NAME_VERTEX_ID`/`NAME_INSTANCE_ID`/`NAME_IS_FRONT_FACE` → `ASSERT(0)` (`1944-1963`,
879
+ these are never legal *outputs*, only inputs — DXBC-level invariant, not a lowering
880
+ choice).
881
+ - Tessellation-factor names (`NAME_FINAL_*_TESSFACTOR`, `1964-2047`) → hull-shader-only
882
+ `gl_TessLevelOuter`/`gl_TessLevelInner` array slots; entirely out of this project's
883
+ vertex/pixel-only scope.
884
+
885
+ **Type rules**: `gl_Position` is always `vec4`; the `_siv` declaration itself carries
886
+ no separate component-type fact beyond what the built-in GLSL variable already
887
+ mandates.
888
+
889
+ **Helpers needed**: none — every reachable case in this project's scope
890
+ (`NAME_POSITION`) redirects to a language built-in with zero emitted declaration
891
+ text.
892
+
893
+ **Edge cases**: the DXBC-level invariant that `SV_Position` is always written in a
894
+ vertex shader means this opcode should appear at least once per vertex-shader stage
895
+ in the corpus (2825 occurrences across 1611 files' vertex + pixel-adjacent stages is
896
+ consistent with "roughly one per vertex/domain/geometry stage, most of which are
897
+ plain VS-only stages here").
898
+
899
+ **WebGL2 notes**: of the system values this opcode can carry, only `SV_Position` (no
900
+ extension) and, conditionally, `SV_ClipDistance`/`SV_CullDistance`
901
+ (`EXT_clip_cull_distance`, optional) have any real WebGL2 story; every other branch
902
+ listed above is a target blocker for this project's WebGL2 scope, not a lowering
903
+ detail to implement.
904
+
905
+ **Confidence: high** for `NAME_POSITION` (dominant case, direct source read,
906
+ `TRANSPILING-GAPS.md` "Already handled": "`SV_Position`... [is] handled"); **low**
907
+ for every other branch, since none of them are exercised in this project's proven
908
+ VS/PS-only WebGL2 validation runs.
909
+
910
+ ---
911
+
912
+ ## `dcl_input_ps_siv` (538)
913
+
914
+ **Semantics**: declares a pixel-shader input bound to a system-value semantic
915
+ (`SV_Position` as `gl_FragCoord`, or `SV_RenderTargetArrayIndex` as `gl_Layer`).
916
+
917
+ **GLSL lowering**: `toGLSLDeclaration.cpp:2188-2207`:
918
+ - `NAME_POSITION` → `AddBuiltinInput(psDecl, "gl_FragCoord")` (built-in, no
919
+ declaration text — `gl_FragCoord` is core GLSL ES 3.00), **plus** an early-main
920
+ statement:
921
+ ```glsl
922
+ vec4 hlslcc_FragCoord = vec4(gl_FragCoord.xyz, 1.0/gl_FragCoord.w);
923
+ ```
924
+ (`2195`) — every read of the DXBC `SV_Position` pixel-shader input must be
925
+ redirected to `hlslcc_FragCoord`, **not** raw `gl_FragCoord`: the literal C++
926
+ text takes `gl_FragCoord.xyz` unchanged but replaces the `.w` component with
927
+ `1.0/gl_FragCoord.w`, reconciling a difference between what HLSL's
928
+ `SV_Position.w` and GLSL's `gl_FragCoord.w` each store in the 4th component.
929
+ Treat `hlslcc_FragCoord` as the mandatory redirect target for any
930
+ `SV_Position`-as-input read in a pixel shader, not `gl_FragCoord` directly — do
931
+ not attempt to re-derive or "simplify" the `1.0/gl_FragCoord.w` swap, just
932
+ reproduce the line as emitted.
933
+ - `NAME_RENDER_TARGET_ARRAY_INDEX` → `gl_Layer` (`2198-2201`) — reading back which
934
+ array layer/cubemap face the primitive rasterized into; requires geometry-shader
935
+ layered rendering upstream, out of this project's scope.
936
+ - Anything else → `ASSERT(0)` (`2203-2205`) — DXBC-level invariant, no other system
937
+ value is legal as a pixel-shader `_siv` input.
938
+
939
+ **Type rules**: `gl_FragCoord`/`hlslcc_FragCoord` is always `vec4`.
940
+
941
+ **Helpers needed**: the `hlslcc_FragCoord` early-main redirect line above — declare
942
+ it as a **named helper convention** (a fixed early-main statement emitted whenever a
943
+ `dcl_input_ps_siv NAME_POSITION` is seen), not a callable function, since it is a
944
+ local variable substitution rather than a reusable GLSL function.
945
+
946
+ **Edge cases**: `1.0/gl_FragCoord.w` divides by the fragment's window-space `w`
947
+ reciprocal — if `gl_FragCoord.w` is ever exactly `0.0` (a fragment at infinite
948
+ depth/degenerate clip-space w), this produces `inf`; no corpus evidence this occurs
949
+ in practice (fragments with `w=0` do not typically survive clipping), but note it as
950
+ a theoretical NaN/inf source worth being aware of, not one this project needs to
951
+ guard defensively against absent contrary evidence.
952
+
953
+ **WebGL2 notes**: `gl_FragCoord` is core; no extension needed. `gl_Layer` requires
954
+ geometry-shader support, out of scope for this project.
955
+
956
+ **Confidence: high** for `NAME_POSITION` (directly cited, dominant case — 538
957
+ occurrences is consistent with "most pixel shaders read `SV_Position`"); **low** for
958
+ `gl_Layer` (unreachable without geometry shaders in this project's stage set).
959
+
960
+ ---
961
+
962
+ ## `dcl_input_ps_sgv` (42) / `dcl_input_sgv` (18)
963
+
964
+ **Semantics**: declares a pixel-shader (`_ps_sgv`) or vertex/other-stage (`_sgv`)
965
+ input bound to a **system-generated-value** semantic — the DXBC category for values
966
+ the *rasterizer/assembler* synthesizes rather than values passed through the
967
+ interpolator pipeline: `SV_IsFrontFace`, `SV_SampleIndex`, `SV_VertexID`,
968
+ `SV_InstanceID`, `SV_PrimitiveID`, plus the same position/layer/clip/cull/viewport
969
+ names `_siv` also carries (both opcodes share one switch statement in the source).
970
+
971
+ **GLSL lowering**: `toGLSLDeclaration.cpp:1815-1896` (both opcodes dispatch to this
972
+ single `case` block). The branches relevant to this project's VS/PS-only scope:
973
+ - `NAME_IS_FRONT_FACE` (pixel-shader-only in practice — `SV_IsFrontFace` is a PS
974
+ input) → `gl_FrontFacing`, with an explicit **cast trick**
975
+ (`1856-1869`):
976
+ ```cpp
977
+ if (HaveUnsignedTypes(psContext->psShader->eTargetLanguage))
978
+ AddBuiltinInput(psDecl, "(gl_FrontFacing ? 0xffffffffu : uint(0))");
979
+ else
980
+ AddBuiltinInput(psDecl, "(gl_FrontFacing ? 1 : 0)");
981
+ ```
982
+ The comment explains why: `if(gl_FrontFacing != 0)` failed to compile on Intel HD
983
+ 4000 — no implicit bool↔int conversion on that driver — so HLSLcc always
984
+ materializes the DXBC **comparison-mask convention** explicitly at the point of
985
+ use: `SV_IsFrontFace` in DXBC is an integer that reads as `0xFFFFFFFF` (true) or
986
+ `0x00000000` (false), **not** a GLSL `bool`. For GLSL ES 3.00 (`HaveUnsignedTypes`
987
+ true), every read of this input must produce `0xffffffffu`/`uint(0)`, then get
988
+ bitcast with `uintBitsToFloat(...)` if the register file stores it as a float (per
989
+ this project's register convention) — i.e. the declaration-time substitution text
990
+ for `in_IS_FRONT_FACE`-style reads should literally be
991
+ `(gl_FrontFacing ? uintBitsToFloat(0xffffffffu) : uintBitsToFloat(uint(0)))` when
992
+ materializing it into this project's float register file, or equivalently
993
+ `uintBitsToFloat(gl_FrontFacing ? 0xffffffffu : 0u)` (matches the JS draft
994
+ transpiler's own hypothesis at
995
+ `../shaderdiscovery/src/core/transpiler/gles/Dx11GlesDraftTranspiler.js:1405`:
996
+ `splatScalarExpression("uintBitsToFloat(gl_FrontFacing ? 0xffffffffu : 0u)", count)`
997
+ — cross-checked against `toGLSLDeclaration.cpp:1864-1867` and confirmed
998
+ consistent).
999
+ - `NAME_SAMPLE_INDEX` → `gl_SampleID`, requiring `GL_OES_sample_variables` on ES
1000
+ targets (`1870-1876`) — this **is** a real, available WebGL2 extension
1001
+ (`OES_sample_variables`), but optional; treat as conditionally available.
1002
+ - `NAME_VERTEX_ID` → `gl_VertexID` (`1878-1881`) — core in GLSL ES 3.00 for vertex
1003
+ shaders, no extension needed. `TRANSPILING-GAPS.md` "Already handled":
1004
+ "`SV_VertexID`... [is] handled."
1005
+ - `NAME_INSTANCE_ID` → `gl_InstanceID` (`1851-1854`) — core in GLSL ES 3.00, no
1006
+ extension needed.
1007
+ - `NAME_PRIMITIVE_ID` → `gl_PrimitiveID`/`gl_PrimitiveIDIn` (`1883-1889`) — requires a
1008
+ geometry shader upstream in practice, out of scope.
1009
+ - `NAME_RENDER_TARGET_ARRAY_INDEX`/`NAME_CLIP_DISTANCE`/`NAME_CULL_DISTANCE`/
1010
+ `NAME_VIEWPORT_ARRAY_INDEX` (`1826-1850`) — same extension caveats as the `_siv`
1011
+ output section above.
1012
+ - `default` (`1891-1894`) — falls back to a plain `in vec4 %s;` declaration using the
1013
+ DXBC-provided special-name string directly, for any system-generated value this
1014
+ switch doesn't special-case.
1015
+
1016
+ **Type rules**: `gl_FrontFacing` is GLSL `bool`; every other value in this list is
1017
+ `int` (`gl_VertexID`, `gl_InstanceID`, `gl_SampleID`, `gl_PrimitiveID`). None of these
1018
+ match DXBC's `0xFFFFFFFF`/`0x00000000` mask convention natively except
1019
+ `SV_IsFrontFace`, which is why it alone gets the explicit ternary-to-mask
1020
+ materialization above; `gl_VertexID`/`gl_InstanceID`/etc. are read as plain signed
1021
+ `int` and bitcast with `intBitsToFloat` when stored into this project's float
1022
+ register file (not `uintBitsToFloat` — these are genuinely signed-int builtins in the
1023
+ GLSL spec, unlike the synthesized front-facing mask).
1024
+
1025
+ **Helpers needed**: none beyond the inline ternary-to-mask expression for
1026
+ `SV_IsFrontFace` (a fixed substitution text, not a callable helper function).
1027
+
1028
+ **Edge cases**: `SV_IsFrontFace`'s `0/0xFFFFFFFF` convention is exactly the
1029
+ **comparison-mask convention** called out at the top level of this spec family — even
1030
+ though this is a *declaration*-family opcode rather than an instruction, it is one of
1031
+ the few `decl-io` opcodes where the DXBC 0/0xFFFFFFFF-vs-GLSL-bool boundary has to be
1032
+ crossed at declaration time rather than left to a later comparison instruction.
1033
+
1034
+ **WebGL2 notes**: `gl_VertexID`/`gl_InstanceID`/`gl_FrontFacing` are all core GLSL ES
1035
+ 3.00 (no extension) — the dominant, high-confidence cases for this project.
1036
+ `gl_SampleID` needs an optional extension; primitive-ID/layer/clip/cull/viewport
1037
+ branches need geometry-shader support this project's target does not have.
1038
+
1039
+ **Confidence: high** for `NAME_IS_FRONT_FACE`/`NAME_VERTEX_ID`/`NAME_INSTANCE_ID`
1040
+ (directly cited, corpus-relevant, `TRANSPILING-GAPS.md`-confirmed); **low** for every
1041
+ other branch (geometry-shader-dependent or low corpus incidence: 42+18=60 total
1042
+ instances is small next to the 15k+ `dcl_output` count, and most of that 60 is
1043
+ plausibly `SV_IsFrontFace`/`SV_VertexID` given this corpus is VS/PS-dominated).
1044
+
1045
+ ---
1046
+
1047
+ ## `dcl_input_siv` (3)
1048
+
1049
+ **Semantics**: declares a non-pixel-shader input bound to a system-value semantic
1050
+ (shares the `NAME_*` enum with `dcl_output_siv`/`dcl_input_ps_siv`/`dcl_input_sgv`,
1051
+ but with its own narrow handling).
1052
+
1053
+ **GLSL lowering**: `toGLSLDeclaration.cpp:2209-2215`:
1054
+ ```cpp
1055
+ case OPCODE_DCL_INPUT_SIV:
1056
+ {
1057
+ if (psShader->eShaderType == PIXEL_SHADER && psContext->psDependencies)
1058
+ {
1059
+ psContext->psDependencies->SetInterpolationMode(psDecl->asOperands[0].ui32RegisterNumber, psDecl->value.eInterpolation);
1060
+ }
1061
+ break;
1062
+ }
1063
+ ```
1064
+ This opcode emits **no GLSL text of its own** in this project's scope. Its only
1065
+ effect is recording an interpolation-mode fact for a later `dcl_input_ps`/
1066
+ `dcl_input` declaration of the *same register* to consume when choosing that
1067
+ declaration's interpolation qualifier — and only when the current shader is itself
1068
+ the pixel shader (unreachable in the VS/PS pair unless this specific opcode is
1069
+ emitted for a PS input, which is unusual — `dcl_input_ps`/`dcl_input_ps_siv` normally
1070
+ carry that role for pixel shaders; `dcl_input_siv` proper is more commonly a
1071
+ non-pixel-stage opcode in the wider HLSLcc target matrix, e.g. domain/geometry-shader
1072
+ inputs receiving a previous stage's system value as an ordinary interpolated input).
1073
+
1074
+ **Type rules**: n/a — no declaration text, no operand type conversion here.
1075
+
1076
+ **Helpers needed**: none.
1077
+
1078
+ **Edge cases**: only 3 occurrences in the entire 450k-instruction/1611-file corpus —
1079
+ lowest-frequency opcode in this family by a wide margin. Given this project has no
1080
+ geometry/domain/hull stages, verify what these 3 instances actually are before
1081
+ assuming the pixel-shader branch above is even the relevant one; they may be
1082
+ vertex-shader-side declarations that fall through this `case` doing nothing
1083
+ observable at all (the `if` guard requires `PIXEL_SHADER`, so a vertex-shader
1084
+ occurrence of this opcode is a complete no-op).
1085
+
1086
+ **WebGL2 notes**: none — no GLSL is emitted.
1087
+
1088
+ **Confidence: low** — three instances is too small a sample to be confident which
1089
+ concrete DXBC pattern in the EVE corpus produces this opcode versus its much more
1090
+ common `_ps_siv`/`_sgv`/`_ps_sgv` siblings; implement the no-op/interpolation-mode-
1091
+ recording behavior above as written in the source, but do not assume it is
1092
+ exercised meaningfully by this project's corpus.
1093
+
1094
+ ---
1095
+
1096
+ ## `customdata` (159, encodes an Immediate Constant Buffer)
1097
+
1098
+ **Semantics**: DXBC `customdata` with subtype `ICB` (Immediate Constant Buffer) —
1099
+ an inline array of literal `vec4`-shaped constant data baked directly into the
1100
+ bytecode (as opposed to `dcl_constant_buffer`'s externally-bound `cb#`), addressed
1101
+ either directly by a fixed index or dynamically (`x0[aL]`-style) from instructions
1102
+ later in the stream.
1103
+
1104
+ **GLSL lowering (non-Vulkan, non-Switch — this project's target)**:
1105
+ `toGLSLDeclaration.cpp:3007-3076`. HLSLcc walks `m_ConstantArrayInfo.m_Chunks`
1106
+ (pre-computed groupings of same-component-width, contiguously-accessed ICB slices)
1107
+ and, per chunk:
1108
+ 1. Declares an array: `float ImmCB_{phase}_{chunkFirst}_{rebase}[{size}];` (scalar
1109
+ chunk) or `vec{N} ImmCB_{phase}_{chunkFirst}_{rebase}[{size}];` (`N`-wide chunk)
1110
+ (`3017-3020`).
1111
+ 2. If the target lacks dynamic indexing support, additionally registers a
1112
+ `DeclareDynamicIndexWrapper` (`3022-3029`) — irrelevant here since GLSL ES 3.00
1113
+ (`HaveDynamicIndexing` true for ES 300) supports genuine dynamic array indexing
1114
+ natively; this project should skip the wrapper machinery entirely and always emit
1115
+ a real GLSL array.
1116
+ 3. Populates each array element in **early-main** (not as a `const` initializer list
1117
+ — note this is a *mutable* global-scope-declared, early-main-assigned array in
1118
+ stock HLSLcc, not a `const` array), one assignment per element per component
1119
+ (`3032-3074`):
1120
+ ```glsl
1121
+ ImmCB_0_0_0[0] = 1.5;
1122
+ ImmCB_0_0_0[1] = uintBitsToFloat(uint(0x7FC00000u)); // NaN literal, bit-pattern form
1123
+ ```
1124
+ Float literals that are themselves NaN/Inf bit patterns
1125
+ (`fpcheck(val[...])`, `3045-3048`, `3067-3070`) are re-encoded as
1126
+ `uintBitsToFloat(uint(0x{hex}u))` rather than a literal `nan`/`inf` token (which
1127
+ GLSL cannot parse directly) — **this bit-pattern-preserving encoding is the
1128
+ correct/required approach for any ICB constant that is NaN or ±Inf, and this
1129
+ project's emitter must replicate it**, since a plain decimal float literal cannot
1130
+ represent those bit patterns exactly and GLSL has no `nan`/`inf` literal syntax.
1131
+ 4. Vulkan target (`2975-2990`, background only — not this project's target):
1132
+ `const uvec4 ImmCB_{phase}[] = uvec4[{count}](uvec4(0x..., ...), ...);` — a true
1133
+ `const` array of raw-bit `uvec4`s.
1134
+ 5. Switch target (`2991-3006`, background only): a `const vec4
1135
+ ImmCB_{phase}[]` initialized with a `vec4[{count}]` array constructor whose
1136
+ elements preserve raw bits with `uintBitsToFloat`; this is the
1137
+ shape closest to "a `const vec4 array`" as the family brief describes, and is a
1138
+ **better model for this project's emitter to imitate** than the non-Vulkan/
1139
+ non-Switch mutable-early-main-array path above: declare
1140
+ ```glsl
1141
+ const vec4 ImmCB_{phase}[{count}] = vec4[{count}](
1142
+ vec4(uintBitsToFloat(0x3FC00000u), uintBitsToFloat(0x00000000u), ...),
1143
+ ...
1144
+ );
1145
+ ```
1146
+ at global scope (every raw dword reinterpreted through `uintBitsToFloat`
1147
+ uniformly, whether or not it happens to be a "nice" float, sidestepping the
1148
+ `fpcheck`-conditional branching the CPU-target path uses) — simpler to implement
1149
+ correctly in a from-scratch JS emitter and avoids the early-main mutable-global
1150
+ pattern (which exists in stock HLSLcc mainly to support per-chunk dynamic-array
1151
+ splitting this project doesn't need, since `HaveDynamicIndexing` is true and a
1152
+ single flat `const vec4[]` can be indexed directly).
1153
+
1154
+ **Type rules**: every ICB element is exactly 4 raw dwords; **always reinterpret via
1155
+ `uintBitsToFloat`** rather than trusting a decimal float re-parse of the DXBC literal
1156
+ value, to guarantee exact bit-for-bit reproduction of the original constant
1157
+ (including denormals/NaNs/Infs the HLSL compiler folded in). Consumers needing an
1158
+ int/uint view of an ICB element bitcast again at the use site
1159
+ (`floatBitsToInt`/`floatBitsToUint`) exactly like any other float-register-file read.
1160
+
1161
+ **Helpers needed**: none — `uintBitsToFloat` is core GLSL ES 3.00.
1162
+
1163
+ **Edge cases**: NaN/Inf constants are the primary edge case, handled correctly by
1164
+ the bit-pattern-preserving encoding above — do not let a JS `JSON.stringify`/decimal
1165
+ round-trip of the float value silently normalize a NaN payload or lose an Inf's
1166
+ sign bit; carry the raw `uint32` dword through to the emitted GLSL untouched.
1167
+ Dynamic indexing of the ICB (`x0[aL]`-style reads) is a plain GLSL array-index
1168
+ expression on this project's target (`HaveDynamicIndexing` true for ES 300),
1169
+ needing none of stock HLSLcc's non-dynamic-indexing wrapper-function fallback.
1170
+
1171
+ **WebGL2 notes**: dynamic (non-constant-expression) array indexing of a global
1172
+ array is supported in GLSL ES 3.00's core profile (unlike GLSL ES 1.00, where it was
1173
+ restricted) — this project's target does not need the
1174
+ `DeclareDynamicIndexWrapper` fallback stock HLSLcc carries for older targets.
1175
+
1176
+ **Confidence: medium** — the NaN/Inf bit-pattern-preserving requirement and the
1177
+ "prefer the Switch-shaped `const vec4[]` over the non-Vulkan/non-Switch mutable
1178
+ early-main array" recommendation are both this spec's own synthesis reasoned from
1179
+ reading three different target branches in the same source function, not a literal
1180
+ restatement of a single already-proven CarbonEngineJS-fork code path — validate the
1181
+ `const vec4[]` shape compiles and links correctly on an actual WebGL2 context before
1182
+ treating it as final.
1183
+
1184
+ ---
1185
+
1186
+ ## Helpers summary
1187
+
1188
+ Every helper function/macro/convention this family requires the JS emitter to
1189
+ provide, in one place:
1190
+
1191
+ | Helper | Kind | Used by | Definition |
1192
+ |---|---|---|---|
1193
+ | `floatBitsToInt` | core GLSL ES 3.00 builtin | any int-typed read of a float-register-file value (`dcl_temps`, `dcl_indexable_temp`, `dcl_constant_buffer` fallback slots, `customdata`, integer-lowered `dcl_input`) | built-in, no definition needed |
1194
+ | `floatBitsToUint` | core GLSL ES 3.00 builtin | same as above, uint-typed reads (also the `SV_IsFrontFace` mask materialization, `dcl_input_ps_sgv`/`dcl_input_sgv`) | built-in |
1195
+ | `intBitsToFloat` | core GLSL ES 3.00 builtin | writing an int-typed result into a float register (`gl_VertexID`/`gl_InstanceID`/`gl_PrimitiveID`/`gl_SampleID` materialization into the float register file) | built-in |
1196
+ | `uintBitsToFloat` | core GLSL ES 3.00 builtin | writing a uint-typed result into a float register; ICB literal decoding (`customdata`); `SV_IsFrontFace` mask materialization | built-in |
1197
+ | `hlslcc_FragCoord` early-main redirect | fixed substitution statement, not a function | `dcl_input_ps_siv` (`NAME_POSITION`) | `vec4 hlslcc_FragCoord = vec4(gl_FragCoord.xyz, 1.0/gl_FragCoord.w);` — every `SV_Position`-as-PS-input read must use this name, not raw `gl_FragCoord` |
1198
+ | `SV_IsFrontFace` mask expression | fixed substitution expression, not a function | `dcl_input_ps_sgv`/`dcl_input_sgv` (`NAME_IS_FRONT_FACE`) | `uintBitsToFloat(gl_FrontFacing ? 0xffffffffu : 0u)` materialized wherever the DXBC 0/0xFFFFFFFF mask value is read |
1199
+ | Integer-vertex-attribute float lowering | declaration-shape convention, not a function | `dcl_input` for any `INOUT_COMPONENT_UINT32`/`SINT32` vertex attribute (proven for `BLENDINDICES`; generalize cautiously) | declare `vec4`/`vecN` instead of `uvecN`/`ivecN`; bitcast at every use site |
1200
+ | `in_BINORMALn -> in_BITANGENTn` rename | package-time post-process, not a GLSL-emission-time helper | `dcl_input` split-tangent-space vertex shaders | applied by `scripts/packageTr2WebglEffect.js`-equivalent tooling **after** this family's GLSL text is emitted, keyed off the stage's Carbon metadata contract — do not bake into the opcode lowering itself |
1201
+ | Cross-stage `cbN` size normalization | package/link-time policy, not a per-shader GLSL-emission helper | `dcl_constant_buffer` | emit the **max** `data[]` slot count observed for a given `cb#` across every stage sharing that binding point in one linked program |
1202
+ | `BoneTransforms` SSBO→`cb3` splice | package-time ABI rewrite, not a GLSL-emission-time helper | `dcl_resource_structured` (skinned space-object shaders specifically) | drop the `t0` SSBO decl; grow `cb3` to `vec4 data[200]`; rewrite `ld_structured` row reads to `cb3.data[26 + blendIndex*3 + row]` |
1203
+ | `layout(early_fragment_tests) in;` suppression | emission-time language gate (not present in stock HLSLcc) | `dcl_global_flags` (`FORCE_EARLY_DEPTH_STENCIL`) | drop the qualifier entirely when targeting GLSL ES 3.00 |
1204
+
1205
+ ---
1206
+
1207
+ ## Cross-cutting risks (lowest-confidence areas across this whole family)
1208
+
1209
+ 1. **`dcl_input_siv`** (3 corpus instances) — sample too small to be confident the
1210
+ documented no-op/interpolation-recording behavior is even the path exercised;
1211
+ verify against the actual 3 instances before trusting this section.
1212
+ 2. ~~`noperspective` on `dcl_input_ps`~~ — resolved during review: the C++ source's
1213
+ `hasNoPerspective` gate (`>LANG_ES_310`) is correct, not stale — GLSL ES 3.00
1214
+ genuinely has no `noperspective` keyword, so the gate should be mirrored as-is
1215
+ rather than second-guessed. `sample`/`centroid` remain core ES 3.00 keywords with
1216
+ no gating concern.
1217
+ 3. **Integer-vertex-attribute float-lowering generalization** — proven and validated
1218
+ specifically for `BLENDINDICES`/`uvec4`; whether the same treatment is correct for
1219
+ every other `INOUT_COMPONENT_UINT32`/`SINT32` vertex semantic in the corpus (and
1220
+ whether the use-site cast should be a bit-pattern `floatBitsToUint` reinterpret vs.
1221
+ a value-preserving `uint(...)` truncating convert) has not been separately proven
1222
+ per-semantic.
1223
+ 4. **`customdata` emission shape** — this spec recommends the Switch-target's
1224
+ `const vec4[]` shape over the stock non-Vulkan/non-Switch mutable-early-main-array
1225
+ shape as simpler and sufficient for this project's ES-300-with-dynamic-indexing
1226
+ target; this recommendation is this document's own synthesis, not a literally
1227
+ cited already-proven CarbonEngineJS-fork code path, and should be validated by an
1228
+ actual WebGL2 compile before being treated as settled.
1229
+ 5. **`dcl_global_flags` / `layout(early_fragment_tests) in;`** — whether any EVE
1230
+ effect in the corpus actually sets `FORCE_EARLY_DEPTH_STENCIL` (making the WebGL2
1231
+ suppression gate load-bearing rather than theoretical) was not verified in this
1232
+ pass.
1233
+
1234
+