@carbonenginejs/runtime-resource 0.12.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/README.md +21 -0
  2. package/dist/CjsResMan.js +393 -29
  3. package/dist/CjsResMan.js.map +1 -1
  4. package/dist/format/carbonEffect/CjsCarbonEffectReader.js +165 -118
  5. package/dist/format/carbonEffect/CjsCarbonEffectReader.js.map +1 -1
  6. package/dist/format/carbonEffect/buildCarbonEffectContainer.js +182 -0
  7. package/dist/format/carbonEffect/buildCarbonEffectContainer.js.map +1 -0
  8. package/dist/format/carbonEffect/carbonEffectBackendBlock.js +7 -113
  9. package/dist/format/carbonEffect/carbonEffectBackendBlock.js.map +1 -1
  10. package/dist/format/carbonEffect/carbonEffectRecords.js +258 -66
  11. package/dist/format/carbonEffect/carbonEffectRecords.js.map +1 -1
  12. package/dist/format/carbonEffect/carbonEffectResourceTransform.js +197 -0
  13. package/dist/format/carbonEffect/carbonEffectResourceTransform.js.map +1 -0
  14. package/dist/format/effect/effectBodyInventory.js +146 -0
  15. package/dist/format/effect/effectBodyInventory.js.map +1 -0
  16. package/dist/format/effect/effectPermutationGraph.js +13 -13
  17. package/dist/format/effect/effectPermutationGraph.js.map +1 -1
  18. package/dist/format/index.js +4 -4
  19. package/dist/formats/black/CjsBlackFormat.js +19 -1
  20. package/dist/formats/black/CjsBlackFormat.js.map +1 -1
  21. package/dist/formats/bnk/CjsBnkFormat.js +28 -9
  22. package/dist/formats/bnk/CjsBnkFormat.js.map +1 -1
  23. package/dist/formats/bnk/core/busNodes.js +252 -0
  24. package/dist/formats/bnk/core/busNodes.js.map +1 -0
  25. package/dist/formats/bnk/core/effectNodes.js +147 -0
  26. package/dist/formats/bnk/core/effectNodes.js.map +1 -0
  27. package/dist/formats/bnk/core/eventAction.js +135 -24
  28. package/dist/formats/bnk/core/eventAction.js.map +1 -1
  29. package/dist/formats/bnk/core/globalSettings.js +215 -0
  30. package/dist/formats/bnk/core/globalSettings.js.map +1 -0
  31. package/dist/formats/bnk/core/helpers.js +37 -5
  32. package/dist/formats/bnk/core/helpers.js.map +1 -1
  33. package/dist/formats/bnk/core/musicNodes.js +43 -19
  34. package/dist/formats/bnk/core/musicNodes.js.map +1 -1
  35. package/dist/formats/bnk/core/nodeBase.js +28 -7
  36. package/dist/formats/bnk/core/nodeBase.js.map +1 -1
  37. package/dist/formats/dds/core/helpers.js +40 -1
  38. package/dist/formats/dds/core/helpers.js.map +1 -1
  39. package/dist/formats/dxbc/CjsDxbcFormat.js +45 -0
  40. package/dist/formats/dxbc/CjsDxbcFormat.js.map +1 -1
  41. package/dist/formats/dxbc/core/disassemble.js +240 -0
  42. package/dist/formats/dxbc/core/disassemble.js.map +1 -0
  43. package/dist/formats/gltf/CjsGltfFormat.js +1 -39
  44. package/dist/formats/gltf/CjsGltfFormat.js.map +1 -1
  45. package/dist/formats/gltf/core/helpers.js +1 -21
  46. package/dist/formats/gltf/core/helpers.js.map +1 -1
  47. package/dist/formats/hlsl/CjsHlslFormat.js +0 -21
  48. package/dist/formats/hlsl/CjsHlslFormat.js.map +1 -1
  49. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js +118 -49
  50. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js.map +1 -1
  51. package/dist/formats/hlsl/core/detailMapFamily.js +128 -0
  52. package/dist/formats/hlsl/core/detailMapFamily.js.map +1 -0
  53. package/dist/formats/hlsl/core/localLightFamily.js +133 -0
  54. package/dist/formats/hlsl/core/localLightFamily.js.map +1 -0
  55. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js +79 -73
  56. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js.map +1 -1
  57. package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js +8 -1
  58. package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js.map +1 -1
  59. package/dist/formats/hlsl/core/tr2/shader/HlslEffectDescription.js +11 -691
  60. package/dist/formats/hlsl/core/tr2/shader/HlslEffectDescription.js.map +1 -1
  61. package/dist/formats/index.js +4 -2
  62. package/dist/formats/index.js.map +1 -1
  63. package/dist/formats/obj/CjsObjFormat.js +1 -22
  64. package/dist/formats/obj/CjsObjFormat.js.map +1 -1
  65. package/dist/formats/obj/core/helpers.js +1 -21
  66. package/dist/formats/obj/core/helpers.js.map +1 -1
  67. package/dist/formats/pickle/CjsPickleFormat.js +214 -0
  68. package/dist/formats/pickle/CjsPickleFormat.js.map +1 -0
  69. package/dist/formats/pickle/core/CjsPickleProtocol0Reader.js +551 -0
  70. package/dist/formats/pickle/core/CjsPickleProtocol0Reader.js.map +1 -0
  71. package/dist/formats/pickle/index.js +2 -0
  72. package/dist/formats/pickle/index.js.map +1 -0
  73. package/dist/formats/red/core/redGraph.js +11 -8
  74. package/dist/formats/red/core/redGraph.js.map +1 -1
  75. package/dist/formats/stl/CjsStlFormat.js +1 -46
  76. package/dist/formats/stl/CjsStlFormat.js.map +1 -1
  77. package/dist/formats/stl/core/helpers.js +1 -21
  78. package/dist/formats/stl/core/helpers.js.map +1 -1
  79. package/dist/formats/webgl/CjsWebglFormat.js +33 -55
  80. package/dist/formats/webgl/CjsWebglFormat.js.map +1 -1
  81. package/dist/formats/webgl/core/buildGlslEffectContainer.js +70 -0
  82. package/dist/formats/webgl/core/buildGlslEffectContainer.js.map +1 -0
  83. package/dist/formats/webgl/core/effectPackage.js +216 -108
  84. package/dist/formats/webgl/core/effectPackage.js.map +1 -1
  85. package/dist/formats/webgl/core/errors.js +1 -1
  86. package/dist/formats/webgl/core/errors.js.map +1 -1
  87. package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js +166 -24
  88. package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js.map +1 -1
  89. package/dist/formats/webgl/core/glsl/DxbcGlslOperandFormatter.js +63 -5
  90. package/dist/formats/webgl/core/glsl/DxbcGlslOperandFormatter.js.map +1 -1
  91. package/dist/formats/webgl/core/glsl/packedLightFixups.js +98 -0
  92. package/dist/formats/webgl/core/glsl/packedLightFixups.js.map +1 -0
  93. package/dist/formats/webgl/core/glslBackendBlock.js +552 -0
  94. package/dist/formats/webgl/core/glslBackendBlock.js.map +1 -0
  95. package/dist/formats/webgl/core/glslBackendBodySet.js +243 -0
  96. package/dist/formats/webgl/core/glslBackendBodySet.js.map +1 -0
  97. package/dist/formats/webgl/core/{cewgCompleteness.js → glslEffectCompleteness.js} +12 -8
  98. package/dist/formats/webgl/core/glslEffectCompleteness.js.map +1 -0
  99. package/dist/formats/webgl/core/helpers.js +33 -172
  100. package/dist/formats/webgl/core/helpers.js.map +1 -1
  101. package/dist/formats/webgl/core/inspectGlslEffectContainer.js +122 -0
  102. package/dist/formats/webgl/core/inspectGlslEffectContainer.js.map +1 -0
  103. package/dist/formats/webgl/core/readGlslEffectContainer.js +249 -0
  104. package/dist/formats/webgl/core/readGlslEffectContainer.js.map +1 -0
  105. package/dist/formats/webgpu/CjsWebgpuFormat.js +15 -15
  106. package/dist/formats/webgpu/CjsWebgpuFormat.js.map +1 -1
  107. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js +37 -145
  108. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js.map +1 -1
  109. package/dist/formats/webgpu/core/{cewgpu/CewgpuContainer.js → carbonWebgpu/CarbonWebgpuContainer.js} +13 -27
  110. package/dist/formats/webgpu/core/carbonWebgpu/CarbonWebgpuContainer.js.map +1 -0
  111. package/dist/formats/webgpu/core/{cewgpu → carbonWebgpu}/containerViews.js +1 -1
  112. package/dist/formats/webgpu/core/carbonWebgpu/containerViews.js.map +1 -0
  113. package/dist/formats/webgpu/core/{cewgpu → carbonWebgpu}/validateContainer.js +4 -4
  114. package/dist/formats/webgpu/core/carbonWebgpu/validateContainer.js.map +1 -0
  115. package/dist/formats/webgpu/core/effectBackendBodySet.js +18 -18
  116. package/dist/formats/webgpu/core/effectBackendBodySet.js.map +1 -1
  117. package/dist/formats/webgpu/core/errors.js +1 -1
  118. package/dist/formats/webgpu/core/errors.js.map +1 -1
  119. package/dist/formats/webgpu/core/helpers.js +22 -22
  120. package/dist/formats/webgpu/core/helpers.js.map +1 -1
  121. package/dist/formats/webgpu/core/packageEffect.js +11 -15
  122. package/dist/formats/webgpu/core/packageEffect.js.map +1 -1
  123. package/dist/formats/webgpu/core/packageEffectSelection.js +1 -1
  124. package/dist/formats/webgpu/core/packageEffectSelection.js.map +1 -1
  125. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js +23 -23
  126. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js.map +1 -1
  127. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js +6 -6
  128. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js.map +1 -1
  129. package/dist/formats/yaml/CjsYamlFormat.js +3 -3
  130. package/dist/formats/yaml/CjsYamlFormat.js.map +1 -1
  131. package/dist/formats/yaml/core/CjsYamlReader.js +4 -6
  132. package/dist/formats/yaml/core/CjsYamlReader.js.map +1 -1
  133. package/dist/formats/yaml/core/helpers.js +27 -1
  134. package/dist/formats/yaml/core/helpers.js.map +1 -1
  135. package/dist/index.js +2 -0
  136. package/dist/index.js.map +1 -1
  137. package/dist/resource/CjsLoadingObject.js +19 -0
  138. package/dist/resource/CjsLoadingObject.js.map +1 -0
  139. package/dist/resource/CjsResource.js +4 -0
  140. package/dist/resource/CjsResource.js.map +1 -1
  141. package/dist/resource/ResourceHandlerMode.js +14 -0
  142. package/dist/resource/ResourceHandlerMode.js.map +1 -0
  143. package/dist/resource/index.js +2 -0
  144. package/dist/resource/index.js.map +1 -1
  145. package/dist/resource/shader/Tr2EffectRes.js +118 -213
  146. package/dist/resource/shader/Tr2EffectRes.js.map +1 -1
  147. package/dist/resource/shader/Tr2Shader.js +37 -15
  148. package/dist/resource/shader/Tr2Shader.js.map +1 -1
  149. package/dist/resource/shader/reflection/Tr2EffectConstant.js +37 -29
  150. package/dist/resource/shader/reflection/Tr2EffectConstant.js.map +1 -1
  151. package/dist/resource/shader/reflection/Tr2EffectDescription.js +39 -34
  152. package/dist/resource/shader/reflection/Tr2EffectDescription.js.map +1 -1
  153. package/dist/resource/shader/reflection/Tr2EffectLibrary.js +51 -30
  154. package/dist/resource/shader/reflection/Tr2EffectLibrary.js.map +1 -1
  155. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js +45 -25
  156. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js.map +1 -1
  157. package/dist/resource/shader/reflection/Tr2EffectResource.js +51 -17
  158. package/dist/resource/shader/reflection/Tr2EffectResource.js.map +1 -1
  159. package/dist/resource/shader/reflection/Tr2EffectStageInput.js +219 -88
  160. package/dist/resource/shader/reflection/Tr2EffectStageInput.js.map +1 -1
  161. package/dist/resource/shader/reflection/Tr2EffectTechnique.js +28 -18
  162. package/dist/resource/shader/reflection/Tr2EffectTechnique.js.map +1 -1
  163. package/dist/resource/shader/reflection/Tr2Pass.js +96 -39
  164. package/dist/resource/shader/reflection/Tr2Pass.js.map +1 -1
  165. package/dist/resource/shader/reflection/carbonRecordFields.js +159 -0
  166. package/dist/resource/shader/reflection/carbonRecordFields.js.map +1 -0
  167. package/dist/resource/shader/reflection/shaderStage.js +22 -0
  168. package/dist/resource/shader/reflection/shaderStage.js.map +1 -0
  169. package/dist/resource/shader/sampler/Tr2SamplerSetup.js +68 -10
  170. package/dist/resource/shader/sampler/Tr2SamplerSetup.js.map +1 -1
  171. package/dist/worker/CjsResManMainThreadLoader.js +9 -7
  172. package/dist/worker/CjsResManMainThreadLoader.js.map +1 -1
  173. package/dist/worker/CjsResManWorker.js +5 -4
  174. package/dist/worker/CjsResManWorker.js.map +1 -1
  175. package/dist/worker/CjsResManWorkerLoader.js +9 -6
  176. package/dist/worker/CjsResManWorkerLoader.js.map +1 -1
  177. package/docs/README.md +2 -2
  178. package/docs/architecture.md +12 -3
  179. package/docs/concepts/resource-lifecycle.md +9 -8
  180. package/docs/concepts/shader-resource-model.md +14 -17
  181. package/docs/concepts/writing-an-engine-adapter.md +1 -1
  182. package/docs/formats/README.md +4 -2
  183. package/docs/formats/carbon-effect-container.md +127 -26
  184. package/docs/formats/dxbc/architecture.md +1 -1
  185. package/docs/formats/dxbc/reference/api.md +28 -0
  186. package/docs/formats/hlsl/README.md +3 -3
  187. package/docs/formats/hlsl/architecture.md +8 -9
  188. package/docs/formats/hlsl/guides/hydrating-json-output.md +3 -5
  189. package/docs/formats/hlsl/guides/reading-effects.md +5 -1
  190. package/docs/formats/hlsl/reference/advanced-analysis.md +20 -25
  191. package/docs/formats/hlsl/reference/api.md +0 -7
  192. package/docs/formats/hlsl/reference/json-graph.md +1 -4
  193. package/docs/formats/pickle.md +82 -0
  194. package/docs/formats/webgl/README.md +71 -13
  195. package/docs/formats/webgl/architecture.md +16 -17
  196. package/docs/formats/webgl/carbon-constant-layouts.md +3 -3
  197. package/docs/formats/webgl/decl-io.md +3 -3
  198. package/docs/formats/webgl/memory-structured.md +27 -8
  199. package/docs/formats/webgl/texture-sample.md +1 -1
  200. package/docs/formats/webgpu/README.md +3 -3
  201. package/docs/formats/webgpu/architecture.md +5 -6
  202. package/docs/formats/webgpu/formats/{cewgpu.md → carbon-webgpu.md} +10 -11
  203. package/docs/formats/webgpu/guides/effect-packaging.md +6 -8
  204. package/docs/formats/webgpu/reference/api.md +11 -12
  205. package/docs/formats/webgpu/reference/wgsl-compatibility.md +11 -8
  206. package/docs/formats/wwise.md +63 -2
  207. package/docs/reference/classes/core.md +10 -0
  208. package/docs/reference/classes/formats.md +40 -48
  209. package/docs/reference/classes/resources.md +2 -2
  210. package/docs/reference/queues.md +88 -4
  211. package/docs/reference/workers.md +7 -0
  212. package/package.json +61 -61
  213. package/dist/format/carbonEffect/carbonDescriptionFromPortable.js +0 -372
  214. package/dist/format/carbonEffect/carbonDescriptionFromPortable.js.map +0 -1
  215. package/dist/format/effect/effectReflectionPackage.js +0 -636
  216. package/dist/format/effect/effectReflectionPackage.js.map +0 -1
  217. package/dist/formats/hlsl/core/HlslReader.js +0 -16
  218. package/dist/formats/hlsl/core/HlslReader.js.map +0 -1
  219. package/dist/formats/hlsl/core/portableReflection.js +0 -848
  220. package/dist/formats/hlsl/core/portableReflection.js.map +0 -1
  221. package/dist/formats/hlsl/portable.js +0 -2
  222. package/dist/formats/hlsl/portable.js.map +0 -1
  223. package/dist/formats/webgl/core/cewg/CewgPackage.js +0 -340
  224. package/dist/formats/webgl/core/cewg/CewgPackage.js.map +0 -1
  225. package/dist/formats/webgl/core/cewg/CewgPackageBuilder.js +0 -104
  226. package/dist/formats/webgl/core/cewg/CewgPackageBuilder.js.map +0 -1
  227. package/dist/formats/webgl/core/cewg/binary.js +0 -19
  228. package/dist/formats/webgl/core/cewg/binary.js.map +0 -1
  229. package/dist/formats/webgl/core/cewgCompleteness.js.map +0 -1
  230. package/dist/formats/webgl/core/effectPackageValidation.js +0 -331
  231. package/dist/formats/webgl/core/effectPackageValidation.js.map +0 -1
  232. package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js.map +0 -1
  233. package/dist/formats/webgpu/core/cewgpu/containerViews.js.map +0 -1
  234. package/dist/formats/webgpu/core/cewgpu/validateContainer.js.map +0 -1
  235. package/dist/resource/shader/portable.js +0 -33
  236. package/dist/resource/shader/portable.js.map +0 -1
  237. package/docs/formats/hlsl/reference/portable-reflection.md +0 -141
  238. package/docs/formats/webgl/effect-reflection.md +0 -127
@@ -45,6 +45,8 @@ const NON_COMPARISON_TEXTURE_OPCODES = new Set(["sample", "sample_l", "sample_b"
45
45
  * Filtered-sample coordinate mask per resource dimension
46
46
  * (HLSLcc `TranslateTexCoord`, toGLSLInstruction.cpp:985-1031).
47
47
  */
48
+ /** GLSL symbol for the merged detail-map array. */
49
+ const DETAIL_MAP_ARRAY_SYMBOL = "sDetailArrayMap";
48
50
  const COORD_MASK_BY_DIMENSION = {
49
51
  2: "x",
50
52
  3: "xy",
@@ -100,8 +102,24 @@ class DxbcGlslEmitter {
100
102
  // LightProfileArray) when `--stub-light-resources` is set, so the
101
103
  // fragment sampler count drops under MAX_TEXTURE_IMAGE_UNITS.
102
104
  stubResourceRegisters: [],
105
+ // Resource (t#) registers whose declaration is dropped and whose
106
+ // samples lower to `vec4(1.0)` rather than `vec4(0.0)`.
107
+ //
108
+ // Separate from `stubResourceRegisters` because the two are not
109
+ // interchangeable: a stubbed sampler reads as zero, which is right
110
+ // for a term that is added and wrong for one that is multiplied.
111
+ // `LightProfileArray` is multiplied into a light's attenuation, so
112
+ // stubbing it would drive every profiled light to black, whereas
113
+ // 1.0 is exactly what the shader's own no-profile path yields.
114
+ neutralResourceRegisters: [],
103
115
  lightConstantBuffer: null,
104
116
  lightPackedTexture: null,
117
+ // Resource (t#) registers to merge into one `sampler2DArray`, in
118
+ // layer order. Empty by default; the packager fills it from the
119
+ // backend-neutral detail-map recogniser. Merging three 2D textures
120
+ // into one array binding frees two texture units on shaders that
121
+ // sit exactly on WebGL2's 16-unit limit.
122
+ detailMapArrayRegisters: [],
105
123
  ...options.profile
106
124
  };
107
125
  }
@@ -207,6 +225,15 @@ class DxbcGlslEmitter {
207
225
  // declarations/bindings are dropped and every read of them lowers
208
226
  // to `uintBitsToFloat(0u)` (structured) / `vec4(0.0)` (sampled).
209
227
  stubbedResources: new Set(this.profile.stubResourceRegisters || []),
228
+ // Resource (t#) registers dropped to a compile-time *one*: same
229
+ // declaration handling as `stubbedResources`, but reads lower to
230
+ // `vec4(1.0)` so a multiplied term degrades to neutral instead of
231
+ // to black. See `neutralResourceRegisters` on the profile.
232
+ neutralResources: new Set(this.profile.neutralResourceRegisters || []),
233
+ // Resource register -> array layer for the merged detail maps, and
234
+ // whether the one shared array declaration has been emitted yet.
235
+ detailMapArrayLayers: new Map((this.profile.detailMapArrayRegisters || []).map((register, layer) => [register, layer])),
236
+ detailMapArrayDeclared: false,
210
237
  inputMasks: new Map(),
211
238
  outputMasks: new Map(),
212
239
  bindings: [],
@@ -263,7 +290,7 @@ class DxbcGlslEmitter {
263
290
  constantBuffer: slot => state.constantBufferNames.get(slot) || `cb${slot}`,
264
291
  constantBufferMember: this.profile.constantBufferStyle === "std140" ? "data" : "",
265
292
  resource: index => state.resourceNames.get(index) || `t${index}`,
266
- uav: index => state.uavNames.get(index) || `cewgUav${index}`,
293
+ uav: index => state.uavNames.get(index) || `cjsUav${index}`,
267
294
  immediateConstantBuffer: state.icbName
268
295
  }
269
296
  });
@@ -409,7 +436,7 @@ class DxbcGlslEmitter {
409
436
  dataRegister,
410
437
  profileRegister: Number.isInteger(Number(profile.profileRegister)) ? Number(profile.profileRegister) : null,
411
438
  registerIndex: Number.isInteger(Number(profile.registerIndex)) ? Number(profile.registerIndex) : indexRegister,
412
- name: profile.name || "cewgLocalLightTexture",
439
+ name: profile.name || "cjsLocalLightTexture",
413
440
  dataTexelBase: Number.isInteger(Number(profile.dataTexelBase)) ? Number(profile.dataTexelBase) : 131072
414
441
  };
415
442
  }
@@ -433,7 +460,7 @@ class DxbcGlslEmitter {
433
460
  strideBytes: 0,
434
461
  format: "RGBA32UI",
435
462
  width: this.profile.dataTextureWidth,
436
- cewgSemantic: "packedLocalLights",
463
+ cjsSemantic: "packedLocalLights",
437
464
  lightIndexRegister: profile.indexRegister,
438
465
  lightDataRegister: profile.dataRegister,
439
466
  lightProfileRegister: profile.profileRegister,
@@ -453,14 +480,14 @@ class DxbcGlslEmitter {
453
480
  state.lightConstantBufferDeclared = true;
454
481
  const rows = 1 + profile.capacity * 3;
455
482
  state.constantBufferNames.set(profile.registerIndex, profile.name);
456
- state.declarationLines.push(`uniform vec4 ${profile.name}[${rows}];`, `uint cewgLocalLightCount() { return floatBitsToUint(${profile.name}[0].x); }`, "uint cewgLocalLightIndexLoad(int element) {", " uint count = cewgLocalLightCount();", " if (count == 0u) return 0u;", ` const int listBase = ${profile.listBase};`, " if (element < listBase) return uint(listBase);", " int local = element - listBase;", " int node = local >> 1;", " if (node < 0 || uint(node) >= count) return 0u;", " if ((local & 1) == 0) return uint(node + 1);", " uint next = uint(node + 1);", " return next < count ? uint(listBase + int(next) * 2) : 0u;", "}", "vec4 cewgLocalLightRow(int lightIndex, int row) {", " int i = lightIndex - 1;", " if (i < 0 || uint(i) >= cewgLocalLightCount() || row < 0 || row >= 3) return vec4(0.0);", ` return ${profile.name}[1 + i * 3 + row];`, "}");
483
+ state.declarationLines.push(`uniform vec4 ${profile.name}[${rows}];`, `uint cjsLocalLightCount() { return floatBitsToUint(${profile.name}[0].x); }`, "uint cjsLocalLightIndexLoad(int element) {", " uint count = cjsLocalLightCount();", " if (count == 0u) return 0u;", ` const int listBase = ${profile.listBase};`, " if (element < listBase) return uint(listBase);", " int local = element - listBase;", " int node = local >> 1;", " if (node < 0 || uint(node) >= count) return 0u;", " if ((local & 1) == 0) return uint(node + 1);", " uint next = uint(node + 1);", " return next < count ? uint(listBase + int(next) * 2) : 0u;", "}", "vec4 cjsLocalLightRow(int lightIndex, int row) {", " int i = lightIndex - 1;", " if (i < 0 || uint(i) >= cjsLocalLightCount() || row < 0 || row >= 3) return vec4(0.0);", ` return ${profile.name}[1 + i * 3 + row];`, "}");
457
484
  state.bindings.push({
458
485
  kind: "constantBuffer",
459
486
  registerIndex: profile.registerIndex,
460
487
  name: profile.name,
461
488
  sizeInVec4: rows,
462
489
  style: "array",
463
- cewgSemantic: "localLights",
490
+ cjsSemantic: "localLights",
464
491
  capacityLights: profile.capacity,
465
492
  lightIndexRegister: profile.indexRegister,
466
493
  lightDataRegister: profile.dataRegister,
@@ -723,6 +750,18 @@ class DxbcGlslEmitter {
723
750
  // this register lowers to vec4(0.0) in _textureSample.
724
751
  break;
725
752
  }
753
+ if (state.neutralResources.has(register)) {
754
+ // Same as the stub above, but its samples lower to
755
+ // vec4(1.0). Dropping the declaration is what frees the
756
+ // sampler unit; the substitution is what keeps the result
757
+ // correct for a multiplied term.
758
+ //
759
+ // Nothing is recorded as a binding, because nothing is
760
+ // bound. The pass's resource-transform section carries the
761
+ // statement that this resource was dropped deliberately;
762
+ // see `localLightProfileNeutralTransformFor`.
763
+ break;
764
+ }
726
765
  if (declaration.resourceDimension === 1) {
727
766
  // Buffer<> texel buffers (post-processing) become float data textures
728
767
  // read via width-wrapped texelFetch; no samplerBuffer in WebGL2.
@@ -748,6 +787,10 @@ class DxbcGlslEmitter {
748
787
  dimensionName: declaration.resourceDimensionName
749
788
  });
750
789
  }
790
+ if (state.detailMapArrayLayers.has(register)) {
791
+ this._declareDetailArrayMap(state, register, declaration, comparisonSamplers);
792
+ break;
793
+ }
751
794
  const name = this.profile.samplerName(register, state.stageName);
752
795
  state.resourceDimensions.set(register, declaration.resourceDimension);
753
796
  state.resourceNames.set(register, name);
@@ -840,7 +883,7 @@ class DxbcGlslEmitter {
840
883
  } else {
841
884
  // Vertex-stage structured buffers (BoneTransforms) become dedicated
842
885
  // std140 UBOs sized to the profile capacity (Carbon max 69 joints).
843
- const name = `CewgSb${register}`;
886
+ const name = `CjsSb${register}`;
844
887
  const capacity = this.profile.vertexStructuredCapacity;
845
888
  const rows = capacity * Math.ceil(strideDwords / 4);
846
889
  state.structuredBuffers.set(register, {
@@ -882,7 +925,7 @@ class DxbcGlslEmitter {
882
925
  // framebufferTextureLayer.
883
926
  const sliceNames = new Map();
884
927
  for (const slice of plan.slices) {
885
- const name = `cewgUav${register}_s${slice}`;
928
+ const name = `cjsUav${register}_s${slice}`;
886
929
  const location = this._allocateUavOutputLocation(state, register);
887
930
  sliceNames.set(slice, name);
888
931
  state.declarationLines.push(`layout(location = ${location}) out highp vec4 ${name};`);
@@ -898,7 +941,7 @@ class DxbcGlslEmitter {
898
941
  state.uavSliceNames.set(register, sliceNames);
899
942
  break;
900
943
  }
901
- const name = `cewgUav${register}`;
944
+ const name = `cjsUav${register}`;
902
945
  const location = this._allocateUavOutputLocation(state, register);
903
946
  state.uavNames.set(register, name);
904
947
  state.declarationLines.push(`layout(location = ${location}) out highp vec4 ${name};`);
@@ -1103,7 +1146,7 @@ class DxbcGlslEmitter {
1103
1146
 
1104
1147
  /**
1105
1148
  * Emits the shared `gl_FragCoord`-derived thread-id prelude for map-style
1106
- * compute shaders: a `cewgDispatchOrigin` uniform (so a dispatch can be
1149
+ * compute shaders: a `cjsDispatchOrigin` uniform (so a dispatch can be
1107
1150
  * tiled across more than one draw/viewport), then only the intermediate
1108
1151
  * and final locals this shader actually declared inputs for.
1109
1152
  *
@@ -1114,29 +1157,29 @@ class DxbcGlslEmitter {
1114
1157
  if (!used.threadId && !used.groupId && !used.tig && !used.flattened) {
1115
1158
  return;
1116
1159
  }
1117
- state.dispatchOriginUniform = "cewgDispatchOrigin";
1118
- state.declarationLines.push("uniform ivec3 cewgDispatchOrigin;");
1160
+ state.dispatchOriginUniform = "cjsDispatchOrigin";
1161
+ state.declarationLines.push("uniform ivec3 cjsDispatchOrigin;");
1119
1162
  state.bindings.push({
1120
1163
  kind: "dispatchUniform",
1121
- name: "cewgDispatchOrigin"
1164
+ name: "cjsDispatchOrigin"
1122
1165
  });
1123
- state.earlyMainLines.push("ivec3 cewgThreadId = ivec3(ivec2(gl_FragCoord.xy), 0) + cewgDispatchOrigin;");
1166
+ state.earlyMainLines.push("ivec3 cjsThreadId = ivec3(ivec2(gl_FragCoord.xy), 0) + cjsDispatchOrigin;");
1124
1167
  if (used.threadId) {
1125
- state.earlyMainLines.push("vec4 vThreadID = intBitsToFloat(ivec4(cewgThreadId, 0));");
1168
+ state.earlyMainLines.push("vec4 vThreadID = intBitsToFloat(ivec4(cjsThreadId, 0));");
1126
1169
  }
1127
1170
  if (used.groupId || used.tig || used.flattened) {
1128
1171
  const [nx, ny, nz] = this._threadGroupSizeOrThrow(state);
1129
- state.earlyMainLines.push(`ivec3 cewgGroupId = cewgThreadId / ivec3(${nx}, ${ny}, ${nz});`);
1172
+ state.earlyMainLines.push(`ivec3 cjsGroupId = cjsThreadId / ivec3(${nx}, ${ny}, ${nz});`);
1130
1173
  if (used.groupId) {
1131
- state.earlyMainLines.push("vec4 vThreadGroupID = intBitsToFloat(ivec4(cewgGroupId, 0));");
1174
+ state.earlyMainLines.push("vec4 vThreadGroupID = intBitsToFloat(ivec4(cjsGroupId, 0));");
1132
1175
  }
1133
1176
  if (used.tig || used.flattened) {
1134
- state.earlyMainLines.push(`ivec3 cewgTig = cewgThreadId - cewgGroupId * ivec3(${nx}, ${ny}, ${nz});`);
1177
+ state.earlyMainLines.push(`ivec3 cjsTig = cjsThreadId - cjsGroupId * ivec3(${nx}, ${ny}, ${nz});`);
1135
1178
  if (used.tig) {
1136
- state.earlyMainLines.push("vec4 vThreadIDInGroup = intBitsToFloat(ivec4(cewgTig, 0));");
1179
+ state.earlyMainLines.push("vec4 vThreadIDInGroup = intBitsToFloat(ivec4(cjsTig, 0));");
1137
1180
  }
1138
1181
  if (used.flattened) {
1139
- state.earlyMainLines.push(`vec4 vThreadIDInGroupFlattened = intBitsToFloat(ivec4(cewgTig.z*${nx}*${ny} + cewgTig.y*${nx} + cewgTig.x, 0, 0, 0));`);
1182
+ state.earlyMainLines.push(`vec4 vThreadIDInGroupFlattened = intBitsToFloat(ivec4(cjsTig.z*${nx}*${ny} + cjsTig.y*${nx} + cjsTig.x, 0, 0, 0));`);
1140
1183
  }
1141
1184
  }
1142
1185
  }
@@ -1689,6 +1732,82 @@ class DxbcGlslEmitter {
1689
1732
  }
1690
1733
  }
1691
1734
 
1735
+ /**
1736
+ * Declares the merged detail-map array, once, on the first member seen.
1737
+ *
1738
+ * Each member keeps its own entry in `resourceNames` pointing at the shared
1739
+ * array, so every existing reference site resolves to it without knowing a
1740
+ * merge happened. The declared dimension deliberately stays 2D: the layer is
1741
+ * appended to the coordinate at each sample site rather than read out of the
1742
+ * shader's own operand, which has no third component to give.
1743
+ *
1744
+ * @param {object} state Mutable emission state.
1745
+ * @param {number} register Resource register being declared.
1746
+ * @param {object} declaration Decoded resource declaration.
1747
+ * @param {Set<number>|null} comparisonSamplers Comparison samplers, when any.
1748
+ * @private
1749
+ */
1750
+ _declareDetailArrayMap(state, register, declaration, comparisonSamplers) {
1751
+ // A comparison-sampled or non-2D detail map is not the family the
1752
+ // recogniser promised, so refuse rather than emit a wrong declaration.
1753
+ if (comparisonSamplers || declaration.resourceDimension !== 3) {
1754
+ throw new WebglReadError("Detail-map array merging requires plain 2D textures without comparison sampling", {
1755
+ source: state.sourceName,
1756
+ register,
1757
+ dimensionName: declaration.resourceDimensionName
1758
+ });
1759
+ }
1760
+ state.resourceDimensions.set(register, declaration.resourceDimension);
1761
+ state.resourceNames.set(register, DETAIL_MAP_ARRAY_SYMBOL);
1762
+ if (state.detailMapArrayDeclared) return;
1763
+ state.detailMapArrayDeclared = true;
1764
+ state.declarationLines.push(`uniform mediump sampler2DArray ${DETAIL_MAP_ARRAY_SYMBOL};`);
1765
+ state.bindings.push({
1766
+ kind: "resource",
1767
+ registerIndex: register,
1768
+ name: DETAIL_MAP_ARRAY_SYMBOL,
1769
+ samplerType: "sampler2DArray",
1770
+ dimensionName: "texture2darray",
1771
+ arrayLayerCount: state.detailMapArrayLayers.size,
1772
+ mergedFrom: [...state.detailMapArrayLayers.keys()]
1773
+ });
1774
+ }
1775
+
1776
+ /**
1777
+ * Returns the array layer a texture operand maps to, when it was merged.
1778
+ *
1779
+ * @param {object} state Mutable emission state.
1780
+ * @param {object} texOperand Texture resource operand.
1781
+ * @returns {number|null} Layer index, or null when the register was not merged.
1782
+ * @private
1783
+ */
1784
+ _detailMapArrayLayer(state, texOperand) {
1785
+ const layer = state.detailMapArrayLayers.get(texOperand.registerIndex);
1786
+ return layer === undefined ? null : layer;
1787
+ }
1788
+
1789
+ /**
1790
+ * Refuses an operation that cannot be redirected at an array layer.
1791
+ *
1792
+ * Recognising the detail family proves the resources are mergeable; it does
1793
+ * not prove every *use* is. A texel fetch or a size query against a merged
1794
+ * register would silently mean something different once the register became
1795
+ * one layer of an array, so those fail the build instead.
1796
+ *
1797
+ * @param {object} state Mutable emission state.
1798
+ * @param {object} instruction Decoded DXBC instruction.
1799
+ * @param {object} texOperand Texture resource operand.
1800
+ * @private
1801
+ */
1802
+ _rejectDetailArrayMapUse(state, instruction, texOperand) {
1803
+ if (!state.detailMapArrayLayers.has(texOperand.registerIndex)) return;
1804
+ throw new WebglReadError(`Detail map merged into an array is used by ${instruction.opcodeName}, ` + "which this emitter cannot redirect at an array layer", {
1805
+ source: state.sourceName,
1806
+ register: texOperand.registerIndex,
1807
+ opcodeName: instruction.opcodeName
1808
+ });
1809
+ }
1810
+
1692
1811
  /**
1693
1812
  * Resolves and validates the declared dimension of a texture operand.
1694
1813
  *
@@ -1763,9 +1882,24 @@ class DxbcGlslEmitter {
1763
1882
  });
1764
1883
  return;
1765
1884
  }
1885
+ if (state.neutralResources.has(texOperand.registerIndex)) {
1886
+ // Neutralised sampler (LightProfileArray): its declaration was
1887
+ // dropped, and the value it fed is a multiplier, so lower to one.
1888
+ // The shader's own no-profile path leaves attenuation untouched,
1889
+ // which is what multiplying by 1.0 reproduces exactly.
1890
+ const oneSwizzle = [...mask].map(component => texOperand.swizzle ? texOperand.swizzle["xyzw".indexOf(component)] : component).join("");
1891
+ this._assign(state, instruction, `vec4(1.0).${oneSwizzle}`, {
1892
+ saturate: instruction.saturate
1893
+ });
1894
+ return;
1895
+ }
1766
1896
  const dimension = this._resourceDimension(state, instruction, texOperand);
1767
1897
  const coordMask = COORD_MASK_BY_DIMENSION[dimension];
1768
- const coord = this._vecArg(state, coordOperand, coordMask);
1898
+ const detailLayer = this._detailMapArrayLayer(state, texOperand);
1899
+ const baseCoord = this._vecArg(state, coordOperand, coordMask);
1900
+ // A merged detail map keeps its 2D coordinate and gains the layer as a
1901
+ // literal third component; the register it came from is the layer.
1902
+ const coord = detailLayer === null ? baseCoord : `vec3(${baseCoord}, ${detailLayer}.0)`;
1769
1903
  const texName = state.formatter.registerReference(texOperand);
1770
1904
  const returnSwizzle = [...mask].map(component => texOperand.swizzle ? texOperand.swizzle["xyzw".indexOf(component)] : component).join("");
1771
1905
  if (comparisonRefOperandIndex !== null) {
@@ -1832,6 +1966,7 @@ class DxbcGlslEmitter {
1832
1966
  } = this._destMask(state, instruction);
1833
1967
  const coordOperand = instruction.operands[1];
1834
1968
  const texOperand = instruction.operands[2];
1969
+ this._rejectDetailArrayMapUse(state, instruction, texOperand);
1835
1970
  const dimension = this._resourceDimension(state, instruction, texOperand);
1836
1971
  const texName = state.formatter.registerReference(texOperand);
1837
1972
  const returnSwizzle = [...mask].map(component => texOperand.swizzle ? texOperand.swizzle["xyzw".indexOf(component)] : component).join("");
@@ -2491,7 +2626,7 @@ DxbcGlslEmitter.prototype._ldStructured = function _ldStructured(state, instruct
2491
2626
  const texel = `(${info.dataTexelBase} + ((${dword}) >> 2))`;
2492
2627
  value = `uintBitsToFloat(texelFetch(${info.name}, ivec2((${texel}) & ${widthMask}, (${texel}) >> ${widthShift}), 0)[(${dword}) & 3])`;
2493
2628
  } else if (info.kind === "lightIndexCb") {
2494
- value = `uintBitsToFloat(cewgLocalLightIndexLoad(${element}))`;
2629
+ value = `uintBitsToFloat(cjsLocalLightIndexLoad(${element}))`;
2495
2630
  } else if (info.kind === "lightDataCb") {
2496
2631
  if (!aligned) {
2497
2632
  throw new WebglReadError("local-light constant-buffer lowering requires aligned light rows", {
@@ -2499,7 +2634,7 @@ DxbcGlslEmitter.prototype._ldStructured = function _ldStructured(state, instruct
2499
2634
  offset: instruction.offset
2500
2635
  });
2501
2636
  }
2502
- value = `cewgLocalLightRow(${element}, ${offsetDwords / 4}).${channel}`;
2637
+ value = `cjsLocalLightRow(${element}, ${offsetDwords / 4}).${channel}`;
2503
2638
  } else if (info.kind === "ubo") {
2504
2639
  if (aligned) {
2505
2640
  const row = `(${element}) * ${info.strideDwords / 4}${offsetDwords ? ` + ${offsetDwords / 4}` : ""}`;
@@ -2535,6 +2670,7 @@ DxbcGlslEmitter.prototype._resinfo = function _resinfo(state, instruction) {
2535
2670
  as: "int"
2536
2671
  });
2537
2672
  const texOperand = instruction.operands[2];
2673
+ this._rejectDetailArrayMapUse(state, instruction, texOperand);
2538
2674
  const dimension = this._resourceDimension(state, instruction, texOperand);
2539
2675
  const axisCount = dimension === 5 || dimension === 8 ? 3 : 2;
2540
2676
  const returnType = instruction.resinfoReturnTypeName || "float";
@@ -2604,8 +2740,14 @@ DxbcGlslEmitter.prototype._gather4 = function _gather4(state, instruction) {
2604
2740
  offset: instruction.offset
2605
2741
  });
2606
2742
  }
2607
- const helper = this.helpers.require(dimension === 8 ? "hlslcc_textureGather4ArrayEmulated" : "hlslcc_textureGather4Emulated");
2608
- const coord = this._vecArg(state, coordOperand, dimension === 8 ? "xyz" : "xy");
2743
+ // A merged detail map is declared 2D but sampled through an array, so the
2744
+ // array helper and the layered coordinate are selected by the merge rather
2745
+ // than by the declared dimension.
2746
+ const detailLayer = this._detailMapArrayLayer(state, texOperand);
2747
+ const asArray = dimension === 8 || detailLayer !== null;
2748
+ const helper = this.helpers.require(asArray ? "hlslcc_textureGather4ArrayEmulated" : "hlslcc_textureGather4Emulated");
2749
+ const baseCoord = this._vecArg(state, coordOperand, dimension === 8 ? "xyz" : "xy");
2750
+ const coord = detailLayer === null ? baseCoord : `vec3(${baseCoord}, ${detailLayer}.0)`;
2609
2751
  const texName = state.formatter.registerReference(texOperand);
2610
2752
  const channelLetter = this._gather4Channel(samplerOperand);
2611
2753
  const channelIndex = "xyzw".indexOf(channelLetter);