@woosh/meep-engine 3.47.0 → 3.49.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 (619) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/editor/particles/effect/ParticleReferenceSimulation.d.ts +1 -2
  3. package/editor/particles/effect/ParticleReferenceSimulation.d.ts.map +1 -1
  4. package/editor/particles/effect/ParticleReferenceSimulation.js +2 -4
  5. package/editor/view/particles/effect/ParticleGraphEditorView.js +1 -1
  6. package/package.json +1 -1
  7. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +27 -11
  8. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  9. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +11 -0
  10. package/src/engine/graphics/ecs/particles/ParticleEffect.d.ts +2 -2
  11. package/src/engine/graphics/ecs/particles/ParticleEffect.d.ts.map +1 -1
  12. package/src/engine/graphics/ecs/particles/ParticleEffect.js +7 -7
  13. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.d.ts +8 -0
  14. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.d.ts.map +1 -1
  15. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.js +11 -3
  16. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationUpgrader_1_2.d.ts +26 -0
  17. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationUpgrader_1_2.d.ts.map +1 -0
  18. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationUpgrader_1_2.js +91 -0
  19. package/src/engine/graphics3/GPUParticleEmitterSystem.js +2 -2
  20. package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -1
  21. package/src/engine/graphics3/GraphicsEngine.js +8 -1
  22. package/src/engine/graphics3/ParticipatingMedia.d.ts +84 -1
  23. package/src/engine/graphics3/ParticipatingMedia.d.ts.map +1 -1
  24. package/src/engine/graphics3/ParticipatingMedia.js +139 -5
  25. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.d.ts +6 -2
  26. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.d.ts.map +1 -1
  27. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.js +29 -4
  28. package/src/engine/graphics3/ParticipatingMediaSerializationUpgrader_0_1.d.ts +10 -0
  29. package/src/engine/graphics3/ParticipatingMediaSerializationUpgrader_0_1.d.ts.map +1 -0
  30. package/src/engine/graphics3/ParticipatingMediaSerializationUpgrader_0_1.js +38 -0
  31. package/src/engine/graphics3/ParticipatingMediaSystem.d.ts.map +1 -1
  32. package/src/engine/graphics3/ParticipatingMediaSystem.js +46 -3
  33. package/src/engine/network/NetworkSession.d.ts +16 -5
  34. package/src/engine/network/NetworkSession.d.ts.map +1 -1
  35. package/src/engine/network/NetworkSession.js +104 -7
  36. package/src/engine/network/README.md +27 -1
  37. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts +40 -18
  38. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
  39. package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +180 -190
  40. package/src/engine/network/replication/Replicator.d.ts +15 -4
  41. package/src/engine/network/replication/Replicator.d.ts.map +1 -1
  42. package/src/engine/network/replication/Replicator.js +50 -16
  43. package/src/engine/network/state/RecordArrivalRing.d.ts +57 -0
  44. package/src/engine/network/state/RecordArrivalRing.d.ts.map +1 -0
  45. package/src/engine/network/state/RecordArrivalRing.js +144 -0
  46. package/src/engine/physics/cloth/MEASUREMENTS.md +354 -0
  47. package/src/engine/physics/cloth/PLAN.md +85 -5
  48. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +0 -12
  49. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
  50. package/src/engine/physics/cloth/ecs/ClothInstance.js +0 -13
  51. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +2 -1
  52. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
  53. package/src/engine/physics/cloth/ecs/ClothSystem.js +7 -1
  54. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
  55. package/src/engine/physics/cloth/ecs/ClothWorld.js +4 -15
  56. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts +2 -1
  57. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts.map +1 -1
  58. package/src/engine/physics/cloth/ecs/WorkerClothSystem.js +6 -1
  59. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -1
  60. package/src/engine/physics/cloth/ecs/cloth_build_rows.js +20 -10
  61. package/src/engine/physics/cloth/playground/collide.html +5 -3
  62. package/src/engine/physics/cloth/playground/collide_main.js +14 -13
  63. package/src/engine/physics/cloth/solver/ClothState.d.ts +30 -2
  64. package/src/engine/physics/cloth/solver/ClothState.d.ts.map +1 -1
  65. package/src/engine/physics/cloth/solver/ClothState.js +62 -4
  66. package/src/engine/physics/cloth/solver/cloth_self_find.d.ts +11 -3
  67. package/src/engine/physics/cloth/solver/cloth_self_find.d.ts.map +1 -1
  68. package/src/engine/physics/cloth/solver/cloth_self_find.js +52 -11
  69. package/src/engine/physics/cloth/solver/cloth_self_radius.d.ts +42 -0
  70. package/src/engine/physics/cloth/solver/cloth_self_radius.d.ts.map +1 -0
  71. package/src/engine/physics/cloth/solver/cloth_self_radius.js +64 -0
  72. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +99 -24
  73. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
  74. package/src/engine/physics/cloth/solver/cloth_solver_constants.js +102 -25
  75. package/src/engine/physics/cloth/solver/cloth_step.d.ts +5 -1
  76. package/src/engine/physics/cloth/solver/cloth_step.d.ts.map +1 -1
  77. package/src/engine/physics/cloth/solver/cloth_step.js +13 -3
  78. package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts +0 -1
  79. package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts.map +1 -1
  80. package/src/engine/physics/cloth/solver/cloth_step_instance.js +12 -14
  81. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts +23 -4
  82. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts.map +1 -1
  83. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.js +36 -8
  84. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.d.ts.map +1 -1
  85. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.js +4 -2
  86. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  87. package/src/shade/device/mock/SoftwareGPUDevice.d.ts +15 -0
  88. package/src/shade/device/mock/SoftwareGPUDevice.d.ts.map +1 -1
  89. package/src/shade/device/mock/SoftwareGPUDevice.js +21 -0
  90. package/src/shade/device/pipeline/ComputePipelineManager.d.ts +20 -0
  91. package/src/shade/device/pipeline/ComputePipelineManager.d.ts.map +1 -1
  92. package/src/shade/device/pipeline/ComputePipelineManager.js +80 -6
  93. package/src/shade/device/pipeline/PipelineEnquiry.d.ts +52 -0
  94. package/src/shade/device/pipeline/PipelineEnquiry.d.ts.map +1 -0
  95. package/src/shade/device/pipeline/PipelineEnquiry.js +63 -0
  96. package/src/shade/device/pipeline/RenderPipelineManager.d.ts +21 -0
  97. package/src/shade/device/pipeline/RenderPipelineManager.d.ts.map +1 -1
  98. package/src/shade/device/pipeline/RenderPipelineManager.js +84 -4
  99. package/src/shade/playground/contact_shadows/ContactShadowLab.d.ts +137 -0
  100. package/src/shade/playground/contact_shadows/ContactShadowLab.d.ts.map +1 -0
  101. package/src/shade/playground/contact_shadows/ContactShadowLab.js +654 -0
  102. package/src/shade/playground/contact_shadows/README.md +126 -0
  103. package/src/shade/playground/contact_shadows/index.html +191 -0
  104. package/src/shade/playground/contact_shadows/main.d.ts +2 -0
  105. package/src/shade/playground/contact_shadows/main.d.ts.map +1 -0
  106. package/src/shade/playground/contact_shadows/main.js +888 -0
  107. package/src/shade/playground/contact_shadows/scenes.d.ts +48 -0
  108. package/src/shade/playground/contact_shadows/scenes.d.ts.map +1 -0
  109. package/src/shade/playground/contact_shadows/scenes.js +620 -0
  110. package/src/shade/playground/contact_shadows/shader_contact_lab_composite.d.ts +36 -0
  111. package/src/shade/playground/contact_shadows/shader_contact_lab_composite.d.ts.map +1 -0
  112. package/src/shade/playground/contact_shadows/shader_contact_lab_composite.js +245 -0
  113. package/src/shade/playground/contact_shadows/shader_contact_lab_reference.d.ts +15 -0
  114. package/src/shade/playground/contact_shadows/shader_contact_lab_reference.d.ts.map +1 -0
  115. package/src/shade/playground/contact_shadows/shader_contact_lab_reference.js +120 -0
  116. package/src/shade/playground/contact_shadows/shader_contact_lab_stats.d.ts +55 -0
  117. package/src/shade/playground/contact_shadows/shader_contact_lab_stats.d.ts.map +1 -0
  118. package/src/shade/playground/contact_shadows/shader_contact_lab_stats.js +216 -0
  119. package/src/shade/playground/contact_shadows/shader_contact_lab_view.d.ts +20 -0
  120. package/src/shade/playground/contact_shadows/shader_contact_lab_view.d.ts.map +1 -0
  121. package/src/shade/playground/contact_shadows/shader_contact_lab_view.js +114 -0
  122. package/src/shade/playground/contact_shadows/shader_contact_shadow_march.d.ts +11 -0
  123. package/src/shade/playground/contact_shadows/shader_contact_shadow_march.d.ts.map +1 -0
  124. package/src/shade/playground/contact_shadows/shader_contact_shadow_march.js +90 -0
  125. package/src/shade/playground/contact_shadows/textures.d.ts +16 -0
  126. package/src/shade/playground/contact_shadows/textures.d.ts.map +1 -0
  127. package/src/shade/playground/contact_shadows/textures.js +123 -0
  128. package/src/shade/playground/particle_ecs/README.md +1 -1
  129. package/src/shade/playground/particle_ecs/effects/flame.json +1932 -1881
  130. package/src/shade/playground/particle_ecs/effects/smoke.json +99 -1
  131. package/src/shade/playground/particle_ecs/effects/soot.json +152 -145
  132. package/src/shade/playground/tonemap_sdr/README.md +90 -0
  133. package/src/shade/playground/tonemap_sdr/ToneMapCompare.d.ts +105 -0
  134. package/src/shade/playground/tonemap_sdr/ToneMapCompare.d.ts.map +1 -0
  135. package/src/shade/playground/tonemap_sdr/ToneMapCompare.js +380 -0
  136. package/src/shade/playground/tonemap_sdr/chunk_tonemap_gt7_sdr_reference.d.ts +32 -0
  137. package/src/shade/playground/tonemap_sdr/chunk_tonemap_gt7_sdr_reference.d.ts.map +1 -0
  138. package/src/shade/playground/tonemap_sdr/chunk_tonemap_gt7_sdr_reference.js +104 -0
  139. package/src/shade/playground/tonemap_sdr/index.html +90 -0
  140. package/src/shade/playground/tonemap_sdr/main.d.ts +2 -0
  141. package/src/shade/playground/tonemap_sdr/main.d.ts.map +1 -0
  142. package/src/shade/playground/tonemap_sdr/main.js +306 -0
  143. package/src/shade/playground/tonemap_sdr/scenes.d.ts +26 -0
  144. package/src/shade/playground/tonemap_sdr/scenes.d.ts.map +1 -0
  145. package/src/shade/playground/tonemap_sdr/scenes.js +404 -0
  146. package/src/shade/playground/volumetrics_froxel/main.d.ts.map +1 -1
  147. package/src/shade/playground/volumetrics_froxel/main.js +85 -4
  148. package/src/shade/playground/water_refraction/README.md +26 -18
  149. package/src/shade/playground/water_refraction/index.html +1 -1
  150. package/src/shade/renderer/Renderer.d.ts +35 -3
  151. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  152. package/src/shade/renderer/Renderer.js +208 -12
  153. package/src/shade/renderer/ShadeToneMappingMode.d.ts +14 -0
  154. package/src/shade/renderer/ShadeToneMappingMode.d.ts.map +1 -0
  155. package/src/shade/renderer/ShadeToneMappingMode.js +30 -0
  156. package/src/shade/renderer/animation/GPUAnimationManager.d.ts +9 -0
  157. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  158. package/src/shade/renderer/animation/GPUAnimationManager.js +14 -0
  159. package/src/shade/renderer/animation/skinning/shader_meshlet_apply_skinning.d.ts.map +1 -1
  160. package/src/shade/renderer/animation/skinning/shader_meshlet_apply_skinning.js +5 -3
  161. package/src/shade/renderer/buffer/table/GPUDatabase.d.ts +7 -0
  162. package/src/shade/renderer/buffer/table/GPUDatabase.d.ts.map +1 -1
  163. package/src/shade/renderer/buffer/table/GPUDatabase.js +12 -0
  164. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts +8 -0
  165. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts.map +1 -1
  166. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.js +12 -0
  167. package/src/shade/renderer/camera/GPUCameraContext.d.ts.map +1 -1
  168. package/src/shade/renderer/camera/GPUCameraContext.js +19 -8
  169. package/src/shade/renderer/deferred/chunk_read_gBuffer_material.d.ts.map +1 -1
  170. package/src/shade/renderer/deferred/chunk_read_gBuffer_material.js +4 -0
  171. package/src/shade/renderer/deferred/chunk_shade_standard_material_direct.d.ts.map +1 -1
  172. package/src/shade/renderer/deferred/chunk_shade_standard_material_direct.js +4 -0
  173. package/src/shade/renderer/deferred/chunk_shade_standard_material_direct_restir.d.ts.map +1 -1
  174. package/src/shade/renderer/deferred/chunk_shade_standard_material_direct_restir.js +4 -0
  175. package/src/shade/renderer/deferred/shader_deferred_main.d.ts.map +1 -1
  176. package/src/shade/renderer/deferred/shader_deferred_main.js +3 -0
  177. package/src/shade/renderer/deferred/shader_tonemap_HDR.d.ts +6 -0
  178. package/src/shade/renderer/deferred/shader_tonemap_HDR.d.ts.map +1 -1
  179. package/src/shade/renderer/deferred/shader_tonemap_HDR.js +13 -3
  180. package/src/shade/renderer/deferred/shader_tonemap_HDR_none.d.ts +9 -0
  181. package/src/shade/renderer/deferred/shader_tonemap_HDR_none.d.ts.map +1 -0
  182. package/src/shade/renderer/deferred/shader_tonemap_HDR_none.js +63 -0
  183. package/src/shade/renderer/deferred/shader_tonemap_HDR_pbr_neutral.d.ts +10 -0
  184. package/src/shade/renderer/deferred/shader_tonemap_HDR_pbr_neutral.d.ts.map +1 -0
  185. package/src/shade/renderer/deferred/shader_tonemap_HDR_pbr_neutral.js +87 -0
  186. package/src/shade/renderer/deferred/shader_tonemap_LDR.d.ts +6 -0
  187. package/src/shade/renderer/deferred/shader_tonemap_LDR.d.ts.map +1 -1
  188. package/src/shade/renderer/deferred/shader_tonemap_LDR.js +23 -5
  189. package/src/shade/renderer/deferred/shader_tonemap_LDR_none.d.ts +9 -0
  190. package/src/shade/renderer/deferred/shader_tonemap_LDR_none.d.ts.map +1 -0
  191. package/src/shade/renderer/deferred/shader_tonemap_LDR_none.js +68 -0
  192. package/src/shade/renderer/deferred/shader_tonemap_LDR_pbr_neutral.d.ts +10 -0
  193. package/src/shade/renderer/deferred/shader_tonemap_LDR_pbr_neutral.d.ts.map +1 -0
  194. package/src/shade/renderer/deferred/shader_tonemap_LDR_pbr_neutral.js +73 -0
  195. package/src/shade/renderer/geometry/meshlet/MeshletBatch.d.ts.map +1 -1
  196. package/src/shade/renderer/geometry/meshlet/MeshletBatch.js +58 -9
  197. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +35 -25
  198. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  199. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +97 -196
  200. package/src/shade/renderer/geometry/sdf/shader_build_sdf.d.ts.map +1 -1
  201. package/src/shade/renderer/geometry/sdf/shader_build_sdf.js +121 -129
  202. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.d.ts.map +1 -1
  203. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.js +3 -0
  204. package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.d.ts.map +1 -1
  205. package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.js +4 -0
  206. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_indirect_lighting.d.ts.map +1 -1
  207. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_indirect_lighting.js +4 -0
  208. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.d.ts.map +1 -1
  209. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.js +3 -0
  210. package/src/shade/renderer/global_illumination/lpv/deprecated/probe_trace_compute_shader_code.d.ts.map +1 -1
  211. package/src/shade/renderer/global_illumination/lpv/deprecated/probe_trace_compute_shader_code.js +4 -0
  212. package/src/shade/renderer/global_illumination/probe/octahedral/graph_sample_light_probes.d.ts.map +1 -1
  213. package/src/shade/renderer/global_illumination/probe/octahedral/graph_sample_light_probes.js +4 -0
  214. package/src/shade/renderer/global_illumination/shader_resolve_indirect_lighting.d.ts.map +1 -1
  215. package/src/shade/renderer/global_illumination/shader_resolve_indirect_lighting.js +4 -0
  216. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +131 -1
  217. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_delta.d.ts +7 -0
  218. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_delta.d.ts.map +1 -1
  219. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_delta.js +45 -38
  220. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_determine_range.d.ts +2 -0
  221. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_determine_range.d.ts.map +1 -1
  222. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_determine_range.js +81 -79
  223. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_find_split.d.ts +2 -0
  224. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_find_split.d.ts.map +1 -1
  225. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_find_split.js +5 -3
  226. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_internal_children.d.ts +21 -0
  227. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_internal_children.d.ts.map +1 -0
  228. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_internal_children.js +42 -0
  229. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_morton_code.d.ts +19 -0
  230. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_morton_code.d.ts.map +1 -0
  231. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_morton_code.js +44 -0
  232. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.d.ts +23 -5
  233. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.d.ts.map +1 -1
  234. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.js +139 -23
  235. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_build_single_workgroup.d.ts +25 -0
  236. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_build_single_workgroup.d.ts.map +1 -0
  237. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_build_single_workgroup.js +179 -0
  238. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.d.ts +2 -2
  239. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.d.ts.map +1 -1
  240. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.js +7 -16
  241. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.d.ts +3 -4
  242. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.d.ts.map +1 -1
  243. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.js +6 -23
  244. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts +11 -0
  245. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts.map +1 -1
  246. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.js +14 -3
  247. package/src/shade/renderer/gpu_primitive/indirect/build_indirect_draw_command.d.ts +10 -0
  248. package/src/shade/renderer/gpu_primitive/indirect/build_indirect_draw_command.d.ts.map +1 -1
  249. package/src/shade/renderer/gpu_primitive/indirect/build_indirect_draw_command.js +13 -3
  250. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.d.ts +10 -0
  251. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.d.ts.map +1 -1
  252. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.js +14 -0
  253. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_bitonic_comparator.d.ts +26 -0
  254. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_bitonic_comparator.d.ts.map +1 -0
  255. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_bitonic_comparator.js +52 -0
  256. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_bitonic_workgroup.d.ts +32 -0
  257. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_bitonic_workgroup.d.ts.map +1 -0
  258. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_bitonic_workgroup.js +89 -0
  259. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts +10 -0
  260. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts.map +1 -1
  261. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.js +20 -1
  262. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +10 -0
  263. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  264. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +294 -281
  265. package/src/shade/renderer/light/GPULightCollection.d.ts +8 -0
  266. package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
  267. package/src/shade/renderer/light/GPULightCollection.js +18 -1
  268. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.d.ts +8 -0
  269. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.d.ts.map +1 -1
  270. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.js +234 -218
  271. package/src/shade/renderer/light/cluster/graph_assign_clsuters.d.ts +7 -0
  272. package/src/shade/renderer/light/cluster/graph_assign_clsuters.d.ts.map +1 -1
  273. package/src/shade/renderer/light/cluster/graph_assign_clsuters.js +13 -0
  274. package/src/shade/renderer/light/cluster/graph_build_light_clusters.d.ts +7 -0
  275. package/src/shade/renderer/light/cluster/graph_build_light_clusters.d.ts.map +1 -1
  276. package/src/shade/renderer/light/cluster/graph_build_light_clusters.js +16 -2
  277. package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts +10 -0
  278. package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts.map +1 -1
  279. package/src/shade/renderer/light/environment/execute_filter_environment_map.js +13 -0
  280. package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
  281. package/src/shade/renderer/lightmap/GPULightMap.js +3 -0
  282. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.d.ts.map +1 -1
  283. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.js +4 -0
  284. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.d.ts.map +1 -1
  285. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.js +4 -0
  286. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  287. package/src/shade/renderer/material/GPUMaterialManager.d.ts +16 -4
  288. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  289. package/src/shade/renderer/material/GPUMaterialManager.js +38 -8
  290. package/src/shade/renderer/particles/DESIGN.md +49 -7
  291. package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +453 -0
  292. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts +5 -1
  293. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -1
  294. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +13 -5
  295. package/src/shade/renderer/particles/data/particle_emitter_record.js +2 -2
  296. package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.d.ts.map +1 -1
  297. package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.js +12 -0
  298. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.d.ts +60 -0
  299. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.d.ts.map +1 -1
  300. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.js +156 -0
  301. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +2 -2
  302. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
  303. package/src/shade/renderer/particles/runtime/ParticleEmitter.js +8 -8
  304. package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.d.ts.map +1 -1
  305. package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.js +6 -4
  306. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts +7 -6
  307. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts.map +1 -1
  308. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.js +91 -58
  309. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.d.ts.map +1 -1
  310. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.js +10 -0
  311. package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.d.ts.map +1 -1
  312. package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.js +5 -1
  313. package/src/shade/renderer/path_tracer/graph_path_trace_scene.d.ts.map +1 -1
  314. package/src/shade/renderer/path_tracer/graph_path_trace_scene.js +6 -0
  315. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.d.ts +7 -0
  316. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.d.ts.map +1 -1
  317. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.js +18 -0
  318. package/src/shade/renderer/postprocess/denoise/LigmaDenoiser.d.ts.map +1 -1
  319. package/src/shade/renderer/postprocess/denoise/LigmaDenoiser.js +1 -0
  320. package/src/shade/renderer/postprocess/denoise/shader_temporal_reprojection_luma.d.ts.map +1 -1
  321. package/src/shade/renderer/postprocess/denoise/shader_temporal_reprojection_luma.js +6 -4
  322. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.d.ts +11 -0
  323. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.d.ts.map +1 -1
  324. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.js +16 -0
  325. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +10 -0
  326. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
  327. package/src/shade/renderer/postprocess/gtao/GTAO.js +16 -0
  328. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
  329. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +32 -10
  330. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  331. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.d.ts.map +1 -1
  332. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.js +3 -0
  333. package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.d.ts.map +1 -1
  334. package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.js +6 -3
  335. package/src/shade/renderer/postprocess/taa/TAA.d.ts +12 -0
  336. package/src/shade/renderer/postprocess/taa/TAA.d.ts.map +1 -1
  337. package/src/shade/renderer/postprocess/taa/TAA.js +15 -0
  338. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.d.ts +7 -0
  339. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.d.ts.map +1 -1
  340. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.js +13 -0
  341. package/src/shade/renderer/rasterize/TemplatePassDescriptor.d.ts +31 -3
  342. package/src/shade/renderer/rasterize/TemplatePassDescriptor.d.ts.map +1 -1
  343. package/src/shade/renderer/rasterize/TemplatePassDescriptor.js +79 -4
  344. package/src/shade/renderer/rasterize/bucket/graph_meshlets_rasterize_by_bucket.d.ts +17 -0
  345. package/src/shade/renderer/rasterize/bucket/graph_meshlets_rasterize_by_bucket.d.ts.map +1 -1
  346. package/src/shade/renderer/rasterize/bucket/graph_meshlets_rasterize_by_bucket.js +35 -4
  347. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.d.ts +7 -0
  348. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.d.ts.map +1 -1
  349. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.js +182 -169
  350. package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_meshes_classify_buckets.d.ts +10 -0
  351. package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_meshes_classify_buckets.d.ts.map +1 -1
  352. package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_meshes_classify_buckets.js +20 -1
  353. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_extract_meshlets_from_bucket.d.ts +10 -0
  354. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_extract_meshlets_from_bucket.d.ts.map +1 -1
  355. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_extract_meshlets_from_bucket.js +18 -1
  356. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.d.ts +10 -0
  357. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.d.ts.map +1 -1
  358. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.js +20 -1
  359. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.d.ts +7 -0
  360. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.d.ts.map +1 -1
  361. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.js +187 -170
  362. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.d.ts +10 -0
  363. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.d.ts.map +1 -1
  364. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.js +19 -1
  365. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts +13 -0
  366. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts.map +1 -1
  367. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.js +25 -1
  368. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.d.ts +10 -0
  369. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.d.ts.map +1 -1
  370. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.js +20 -0
  371. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.d.ts.map +1 -1
  372. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.js +6 -0
  373. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.d.ts.map +1 -1
  374. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.js +6 -0
  375. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +3 -2
  376. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -1
  377. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +3 -2
  378. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts +7 -2
  379. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  380. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +12 -4
  381. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_brick4.d.ts.map +1 -1
  382. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_brick4.js +4 -3
  383. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_ibl.d.ts.map +1 -1
  384. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_ibl.js +4 -3
  385. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts +2 -1
  386. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts.map +1 -1
  387. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +11 -3
  388. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments.d.ts.map +1 -1
  389. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments.js +4 -3
  390. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.d.ts.map +1 -1
  391. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.js +4 -3
  392. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts +15 -0
  393. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  394. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +35 -2
  395. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts +14 -0
  396. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  397. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +32 -4
  398. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts +17 -0
  399. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
  400. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +34 -4
  401. package/src/shade/renderer/restir/di/chunk_restir_resolve.d.ts +5 -3
  402. package/src/shade/renderer/restir/di/chunk_restir_resolve.d.ts.map +1 -1
  403. package/src/shade/renderer/restir/di/chunk_restir_resolve.js +8 -3
  404. package/src/shade/renderer/restir/di/graph_restir_di.d.ts.map +1 -1
  405. package/src/shade/renderer/restir/di/graph_restir_di.js +1 -0
  406. package/src/shade/renderer/restir/di/shader_restir_di_resolve.d.ts.map +1 -1
  407. package/src/shade/renderer/restir/di/shader_restir_di_resolve.js +5 -1
  408. package/src/shade/renderer/restir/reservoir/shader.d.ts.map +1 -1
  409. package/src/shade/renderer/restir/reservoir/shader.js +7 -4
  410. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts +7 -0
  411. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts.map +1 -1
  412. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.js +13 -1
  413. package/src/shade/renderer/scene/GPUSceneContext.d.ts +10 -0
  414. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  415. package/src/shade/renderer/scene/GPUSceneContext.js +19 -0
  416. package/src/shade/renderer/scene/SceneManager.d.ts +9 -0
  417. package/src/shade/renderer/scene/SceneManager.d.ts.map +1 -1
  418. package/src/shade/renderer/scene/SceneManager.js +11 -0
  419. package/src/shade/renderer/scene/hierarchy/graph_nodes_update_transform_hierarchy.d.ts +11 -0
  420. package/src/shade/renderer/scene/hierarchy/graph_nodes_update_transform_hierarchy.d.ts.map +1 -1
  421. package/src/shade/renderer/scene/hierarchy/graph_nodes_update_transform_hierarchy.js +17 -0
  422. package/src/shade/renderer/scene/hierarchy/graph_scene_update_transform_hierarchy.d.ts +11 -0
  423. package/src/shade/renderer/scene/hierarchy/graph_scene_update_transform_hierarchy.d.ts.map +1 -1
  424. package/src/shade/renderer/scene/hierarchy/graph_scene_update_transform_hierarchy.js +18 -1
  425. package/src/shade/renderer/shader/ComputeShader.d.ts +12 -0
  426. package/src/shade/renderer/shader/ComputeShader.d.ts.map +1 -1
  427. package/src/shade/renderer/shader/ComputeShader.js +53 -0
  428. package/src/shade/renderer/shader/ImageShader.d.ts +27 -8
  429. package/src/shade/renderer/shader/ImageShader.d.ts.map +1 -1
  430. package/src/shade/renderer/shader/ImageShader.js +70 -1
  431. package/src/shade/renderer/shader/ShaderDescriptor.d.ts +21 -0
  432. package/src/shade/renderer/shader/ShaderDescriptor.d.ts.map +1 -1
  433. package/src/shade/renderer/shader/ShaderDescriptor.js +71 -1
  434. package/src/shade/renderer/shader/chunk/camera/chunk_project_view_position_from_depth.d.ts +17 -0
  435. package/src/shade/renderer/shader/chunk/camera/chunk_project_view_position_from_depth.d.ts.map +1 -0
  436. package/src/shade/renderer/shader/chunk/camera/chunk_project_view_position_from_depth.js +45 -0
  437. package/src/shade/renderer/shader/chunk/camera/chunk_screen_coordinate_to_view.js +3 -3
  438. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.d.ts +1 -1
  439. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.js +62 -62
  440. package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +318 -0
  441. package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +237 -0
  442. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_PAPER_WHITE_NITS.d.ts +17 -0
  443. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_PAPER_WHITE_NITS.d.ts.map +1 -0
  444. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_PAPER_WHITE_NITS.js +22 -0
  445. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_khronos_pbr.d.ts +14 -1
  446. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_khronos_pbr.d.ts.map +1 -1
  447. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_khronos_pbr.js +28 -15
  448. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.d.ts +12 -0
  449. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.d.ts.map +1 -1
  450. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.js +128 -110
  451. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_offset_ray_from_depth.d.ts +20 -0
  452. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_offset_ray_from_depth.d.ts.map +1 -0
  453. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_offset_ray_from_depth.js +33 -0
  454. package/src/shade/renderer/shader/chunk/light/chunk_compute_multiscattering_specular.d.ts +4 -0
  455. package/src/shade/renderer/shader/chunk/light/chunk_compute_multiscattering_specular.d.ts.map +1 -1
  456. package/src/shade/renderer/shader/chunk/light/chunk_compute_multiscattering_specular.js +12 -1
  457. package/src/shade/renderer/shader/chunk/material/MATERIAL_SHADING_INFO_STRUCT.d.ts.map +1 -1
  458. package/src/shade/renderer/shader/chunk/material/MATERIAL_SHADING_INFO_STRUCT.js +7 -0
  459. package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +108 -0
  460. package/src/shade/renderer/shader/chunk/material/chunk_compute_indirect_specular.d.ts.map +1 -1
  461. package/src/shade/renderer/shader/chunk/material/chunk_compute_indirect_specular.js +23 -4
  462. package/src/shade/renderer/shader/chunk/material/chunk_re_direct_multiscatter.d.ts +18 -0
  463. package/src/shade/renderer/shader/chunk/material/chunk_re_direct_multiscatter.d.ts.map +1 -0
  464. package/src/shade/renderer/shader/chunk/material/chunk_re_direct_multiscatter.js +56 -0
  465. package/src/shade/renderer/shader/chunk/material/chunk_re_indirect_specular_physical.d.ts.map +1 -1
  466. package/src/shade/renderer/shader/chunk/material/chunk_re_indirect_specular_physical.js +2 -0
  467. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_linear_ray.d.ts +43 -0
  468. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_linear_ray.d.ts.map +1 -0
  469. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_linear_ray.js +319 -0
  470. package/src/shade/renderer/shader/compiler/CodeChunk.d.ts +33 -0
  471. package/src/shade/renderer/shader/compiler/CodeChunk.d.ts.map +1 -1
  472. package/src/shade/renderer/shader/compiler/CodeChunk.js +140 -0
  473. package/src/shade/renderer/shader/compiler/CodeChunkChoice.d.ts +74 -0
  474. package/src/shade/renderer/shader/compiler/CodeChunkChoice.d.ts.map +1 -0
  475. package/src/shade/renderer/shader/compiler/CodeChunkChoice.js +166 -0
  476. package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +680 -0
  477. package/src/shade/renderer/shader/compiler/ShaderSwitch.d.ts +86 -0
  478. package/src/shade/renderer/shader/compiler/ShaderSwitch.d.ts.map +1 -0
  479. package/src/shade/renderer/shader/compiler/ShaderSwitch.js +178 -0
  480. package/src/shade/renderer/shader/compiler/ShaderSwitchSet.d.ts +67 -0
  481. package/src/shade/renderer/shader/compiler/ShaderSwitchSet.d.ts.map +1 -0
  482. package/src/shade/renderer/shader/compiler/ShaderSwitchSet.js +205 -0
  483. package/src/shade/renderer/shadow/contact/CONTACT_SHADOW_WAVE_SIZE.d.ts +14 -0
  484. package/src/shade/renderer/shadow/contact/CONTACT_SHADOW_WAVE_SIZE.d.ts.map +1 -0
  485. package/src/shade/renderer/shadow/contact/CONTACT_SHADOW_WAVE_SIZE.js +13 -0
  486. package/src/shade/renderer/shadow/contact/ContactShadowDispatchList.d.ts +65 -0
  487. package/src/shade/renderer/shadow/contact/ContactShadowDispatchList.d.ts.map +1 -0
  488. package/src/shade/renderer/shadow/contact/ContactShadowDispatchList.js +252 -0
  489. package/src/shade/renderer/shadow/contact/FINDINGS.md +413 -0
  490. package/src/shade/renderer/shadow/contact/chunk_contact_shadow_wavefront_extents.d.ts +29 -0
  491. package/src/shade/renderer/shadow/contact/chunk_contact_shadow_wavefront_extents.d.ts.map +1 -0
  492. package/src/shade/renderer/shadow/contact/chunk_contact_shadow_wavefront_extents.js +105 -0
  493. package/src/shade/renderer/shadow/contact/graph_contact_shadow_linear.d.ts +29 -0
  494. package/src/shade/renderer/shadow/contact/graph_contact_shadow_linear.d.ts.map +1 -0
  495. package/src/shade/renderer/shadow/contact/graph_contact_shadow_linear.js +39 -0
  496. package/src/shade/renderer/shadow/contact/graph_contact_shadow_wavefront.d.ts +29 -0
  497. package/src/shade/renderer/shadow/contact/graph_contact_shadow_wavefront.d.ts.map +1 -0
  498. package/src/shade/renderer/shadow/contact/graph_contact_shadow_wavefront.js +92 -0
  499. package/src/shade/renderer/shadow/contact/shader_contact_shadow_linear.d.ts +29 -0
  500. package/src/shade/renderer/shadow/contact/shader_contact_shadow_linear.d.ts.map +1 -0
  501. package/src/shade/renderer/shadow/contact/shader_contact_shadow_linear.js +128 -0
  502. package/src/shade/renderer/shadow/contact/shader_contact_shadow_wavefront.d.ts +52 -0
  503. package/src/shade/renderer/shadow/contact/shader_contact_shadow_wavefront.d.ts.map +1 -0
  504. package/src/shade/renderer/shadow/contact/shader_contact_shadow_wavefront.js +358 -0
  505. package/src/shade/renderer/shadow/map/ShadowDirectionalTechnique.d.ts +10 -0
  506. package/src/shade/renderer/shadow/map/ShadowDirectionalTechnique.d.ts.map +1 -0
  507. package/src/shade/renderer/shadow/map/ShadowDirectionalTechnique.js +12 -0
  508. package/src/shade/renderer/shadow/map/shader/SHADOW_DIRECTIONAL_TECHNIQUE.d.ts +13 -0
  509. package/src/shade/renderer/shadow/map/shader/SHADOW_DIRECTIONAL_TECHNIQUE.d.ts.map +1 -0
  510. package/src/shade/renderer/shadow/map/shader/SHADOW_DIRECTIONAL_TECHNIQUE.js +14 -0
  511. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.d.ts +14 -8
  512. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.d.ts.map +1 -1
  513. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.js +20 -86
  514. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional_cascades.d.ts +14 -0
  515. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional_cascades.d.ts.map +1 -0
  516. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional_cascades.js +79 -0
  517. package/src/shade/renderer/shadow/ray/shader_ray_shadow_code.d.ts.map +1 -1
  518. package/src/shade/renderer/shadow/ray/shader_ray_shadow_code.js +12 -12
  519. package/src/shade/renderer/shadow/ray/staged/gen/shader_generate_rays.d.ts.map +1 -1
  520. package/src/shade/renderer/shadow/ray/staged/gen/shader_generate_rays.js +153 -151
  521. package/src/shade/renderer/shadow/vsm/shader/chunk_shadowmap_sample_directional_virtual.d.ts +13 -0
  522. package/src/shade/renderer/shadow/vsm/shader/chunk_shadowmap_sample_directional_virtual.d.ts.map +1 -0
  523. package/src/shade/renderer/shadow/vsm/shader/chunk_shadowmap_sample_directional_virtual.js +48 -0
  524. package/src/shade/renderer/texture/GPUTextureContext.d.ts +10 -0
  525. package/src/shade/renderer/texture/GPUTextureContext.d.ts.map +1 -1
  526. package/src/shade/renderer/texture/GPUTextureContext.js +24 -0
  527. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.d.ts +219 -0
  528. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.d.ts.map +1 -0
  529. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.js +671 -0
  530. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.d.ts +26 -2
  531. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.d.ts.map +1 -1
  532. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.js +108 -4
  533. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts +5 -1
  534. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
  535. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +6 -0
  536. package/src/shade/renderer/volumetrics/NOTES.md +50 -0
  537. package/src/shade/renderer/volumetrics/PARTICIPATING_MEDIA_VOLUME_STRUCT.d.ts.map +1 -1
  538. package/src/shade/renderer/volumetrics/PARTICIPATING_MEDIA_VOLUME_STRUCT.js +30 -1
  539. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts +51 -13
  540. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts.map +1 -1
  541. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.js +98 -13
  542. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts +9 -0
  543. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts.map +1 -1
  544. package/src/shade/renderer/volumetrics/SceneVolumetrics.js +78 -68
  545. package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +326 -0
  546. package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.d.ts +7 -4
  547. package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.d.ts.map +1 -1
  548. package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.js +21 -9
  549. package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_uvw_from_clip.d.ts +19 -0
  550. package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_uvw_from_clip.d.ts.map +1 -0
  551. package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_uvw_from_clip.js +53 -0
  552. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts +12 -3
  553. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts.map +1 -1
  554. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.js +85 -48
  555. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts +4 -1
  556. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts.map +1 -1
  557. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.js +4 -1
  558. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +4 -1
  559. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
  560. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +21 -1
  561. package/src/shade/renderer/volumetrics/noise/PARTICIPATING_MEDIA_NOISE.d.ts +98 -0
  562. package/src/shade/renderer/volumetrics/noise/PARTICIPATING_MEDIA_NOISE.d.ts.map +1 -0
  563. package/src/shade/renderer/volumetrics/noise/PARTICIPATING_MEDIA_NOISE.js +115 -0
  564. package/src/shade/renderer/volumetrics/noise/VOLUMETRICS_NOISE_TILE_BAKE_SETTINGS_STRUCT.d.ts +9 -0
  565. package/src/shade/renderer/volumetrics/noise/VOLUMETRICS_NOISE_TILE_BAKE_SETTINGS_STRUCT.d.ts.map +1 -0
  566. package/src/shade/renderer/volumetrics/noise/VOLUMETRICS_NOISE_TILE_BAKE_SETTINGS_STRUCT.js +24 -0
  567. package/src/shade/renderer/volumetrics/noise/bake_volumetrics_noise_tile.d.ts +27 -0
  568. package/src/shade/renderer/volumetrics/noise/bake_volumetrics_noise_tile.d.ts.map +1 -0
  569. package/src/shade/renderer/volumetrics/noise/bake_volumetrics_noise_tile.js +91 -0
  570. package/src/shade/renderer/volumetrics/noise/chunk_participating_media_noise.d.ts +35 -0
  571. package/src/shade/renderer/volumetrics/noise/chunk_participating_media_noise.d.ts.map +1 -0
  572. package/src/shade/renderer/volumetrics/noise/chunk_participating_media_noise.js +92 -0
  573. package/src/shade/renderer/volumetrics/noise/shader_volumetrics_noise_tile_bake.d.ts +10 -0
  574. package/src/shade/renderer/volumetrics/noise/shader_volumetrics_noise_tile_bake.d.ts.map +1 -0
  575. package/src/shade/renderer/volumetrics/noise/shader_volumetrics_noise_tile_bake.js +121 -0
  576. package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.d.ts.map +1 -1
  577. package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.js +38 -3
  578. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts.map +1 -1
  579. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.js +311 -290
  580. package/src/shade/renderer/volumetrics/volumetrics_position_world_to_froxel_uvw.d.ts.map +1 -1
  581. package/src/shade/renderer/volumetrics/volumetrics_position_world_to_froxel_uvw.js +28 -49
  582. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +3 -1
  583. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  584. package/src/shade/renderer/water/GPUWaterRenderer.js +24 -18
  585. package/src/shade/renderer/water/chunk_water_transport.d.ts +0 -1
  586. package/src/shade/renderer/water/chunk_water_transport.d.ts.map +1 -1
  587. package/src/shade/renderer/water/chunk_water_transport.js +0 -17
  588. package/src/shade/renderer/water/shader_water.d.ts +4 -2
  589. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  590. package/src/shade/renderer/water/shader_water.js +27 -16
  591. package/src/shade/renderer/water/shader_water_avboit.d.ts +0 -4
  592. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
  593. package/src/shade/renderer/water/shader_water_avboit.js +0 -63
  594. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
  595. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +43 -9
  596. package/src/engine/network/transport/Channel.d.ts +0 -85
  597. package/src/engine/network/transport/Channel.d.ts.map +0 -1
  598. package/src/engine/network/transport/ReliableCommandPipeline.d.ts +0 -155
  599. package/src/engine/network/transport/ReliableCommandPipeline.d.ts.map +0 -1
  600. package/src/engine/network/transport/fragments/FragmentAssembler.d.ts +0 -105
  601. package/src/engine/network/transport/fragments/FragmentAssembler.d.ts.map +0 -1
  602. package/src/engine/network/transport/fragments/FragmentRetention.d.ts +0 -103
  603. package/src/engine/network/transport/fragments/FragmentRetention.d.ts.map +0 -1
  604. package/src/engine/network/transport/fragments/fragment_send.d.ts +0 -61
  605. package/src/engine/network/transport/fragments/fragment_send.d.ts.map +0 -1
  606. package/src/engine/network/transport/fragments/packet_size.d.ts +0 -99
  607. package/src/engine/network/transport/fragments/packet_size.d.ts.map +0 -1
  608. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_RRTAndODTFit.d.ts +0 -6
  609. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_RRTAndODTFit.d.ts.map +0 -1
  610. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_RRTAndODTFit.js +0 -16
  611. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_aces.d.ts +0 -7
  612. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_aces.d.ts.map +0 -1
  613. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_aces.js +0 -46
  614. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_agx.d.ts +0 -10
  615. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_agx.d.ts.map +0 -1
  616. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_agx.js +0 -127
  617. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_pbr_neutral.d.ts +0 -7
  618. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_pbr_neutral.d.ts.map +0 -1
  619. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_pbr_neutral.js +0 -39
@@ -0,0 +1,680 @@
1
+ # Shader switches: a proposal (2026-10-16, revision c; stage 1 landed)
2
+
3
+ **Status: stage 1 is on master (2026-10-16; §12). Stages 2 and 3 are open (§6, §11).**
4
+
5
+ §0 records your decisions, §4–§6 the design and its stages, and §12 what stage 1 contains and how it was checked:
6
+ the shade suite (879 files, 6,744 tests), `check-types` at the base's 18, and a device run. Revisions (a) and (b) are
7
+ superseded by your answers.
8
+
9
+ ## 0. Decided so far
10
+
11
+ From your answers of 2026-10-16:
12
+
13
+ - **D1 — a switch is an integer.** A boolean is a switch of two values. A shader depends on several switches. A
14
+ switch is a value, not a chunk, and one switch may be decided at several places in the graph. (Confirmed.)
15
+ - **D2 — a switched shader specializes to a static graph.** A switched shader and an immutable set of switch values
16
+ become an ordinary, fully static `CodeChunk` graph. The shader object caches its variants by the set.
17
+ `GraphicsContext` knows nothing of switches.
18
+ - **D3 — every switch defaults to 0.**
19
+ - **D4 — code first, the kludges fixed after.** In the first stage, alternatives differ in code only. The long-term
20
+ design still has to remove the existing kludges, resources included, and a refactor is fine.
21
+
22
+ From your answers to revision (b):
23
+
24
+ - **D5 — resources: one interface per definition (§5.2).** It is the union of its alternatives' bindings, bound by
25
+ name, with defaults for what a mode lacks. Stage 3's declared bindings are wanted later; they will change the shape
26
+ of a shader class a little.
27
+ - **D6 — no flip machinery.** Pipelines made on first use compile synchronously, as they do today. A flip compiles the
28
+ new variants then, and nothing extra is built for it.
29
+ - **D7 — a variant's label carries its set:** `Shade From G-Buffer [shadow directional technique=Virtual]`.
30
+ - **D8 — no interface declaration.** A choice never inspects its alternatives, and the parent never white-boxes what
31
+ sits behind a switch. The per-value specs are what link every alternative into its real shaders.
32
+ - **D9 — the API is `ShaderSwitch.enum(label, Enum)` and `ShaderSwitch.bool(label)`,** one mechanism underneath. A
33
+ switch's values are its own: the enum's members, or `false`/`true`. Sets, choices, labels and errors use them, and
34
+ errors and labels name the member.
35
+ - *My call, inside D3:* an enum must have a member equal to 0, the default, and one without is refused at
36
+ `enum()`. A bool's default is `false`. If you would rather the first-declared member be the default, it is a
37
+ one-line change.
38
+
39
+ What follows from those:
40
+
41
+ - **Pins are gone.** Their only use was a pair of exports with different bindings. §5 makes one definition serve every
42
+ value, and a pipeline that wants another value passes another set.
43
+ - **The linker is untouched.** `compile()` links static graphs only, and refuses a graph that still holds a choice.
44
+ Revision (a) threaded the set through `compile()`; this revision does not.
45
+
46
+ ## 1. Summary
47
+
48
+ ```js
49
+ // a switch over an engine enum, 0 by default; or ShaderSwitch.bool(label), false by default
50
+ export const ShadowDirectionalTechnique = { Cascades: 0, Virtual: 1 };
51
+ export const SHADOW_DIRECTIONAL_TECHNIQUE = ShaderSwitch.enum('shadow directional technique', ShadowDirectionalTechnique);
52
+
53
+ // where a graph decides it: the export every importer already uses
54
+ export const chunk_shadowmap_sample_directional = CodeChunkChoice.from(SHADOW_DIRECTIONAL_TECHNIQUE);
55
+ chunk_shadowmap_sample_directional.add(ShadowDirectionalTechnique.Cascades, chunk_shadowmap_sample_directional_cascades);
56
+ chunk_shadowmap_sample_directional.add(ShadowDirectionalTechnique.Virtual, chunk_shadowmap_sample_directional_virtual);
57
+
58
+ // an immutable, interned set of values; a switch it does not name is at its default
59
+ const switches = ShaderSwitchSet.EMPTY.with(SHADOW_DIRECTIONAL_TECHNIQUE, ShadowDirectionalTechnique.Virtual);
60
+
61
+ // the graph, specialized: every choice replaced by its selected alternative, so no choice is left
62
+ const static_body = body.specialize(switches);
63
+
64
+ // the shader caches its variants by the set; a variant is an ordinary static shader
65
+ shader_deferred_main.variant(switches).draw({ /* … */ });
66
+ shader_deferred_main.descriptor.switches; // [SHADOW_DIRECTIONAL_TECHNIQUE]
67
+ ```
68
+
69
+ For the case that prompted this, `chunk_shadowmap_sample_directional` becomes a choice between `_cascades` and
70
+ `_virtual`. None of the chunks above it changes, and none of the nine shaders that reach it changes its chunk code.
71
+
72
+ Measured on the real `Shade From G-Buffer` module (§7):
73
+
74
+ - **Cascades** drops exactly the 14 VSM functions and 4 VSM declarations: 96 → 82 functions, 69,917 → 59,892
75
+ characters.
76
+ - **Virtual** drops the 2-function cascade walk: 94 functions.
77
+ - In both, only `shadowmap_sample_directional` changes body.
78
+ - **Tint** (Chrome, RTX 4090) compiles all three with no messages.
79
+ - **Pipeline creation** in Chrome takes 0.81–0.83× (cascades) and 0.66–0.70× (virtual) of the shipped shader's.
80
+ - **Cost of a variant:** specializing plus linking takes 0.075–0.083 ms across runs, the same as linking the shipped
81
+ shader today.
82
+ A repeat `variant(set)` lookup takes 0.1 µs and allocates nothing.
83
+
84
+ The deferred GPU time measured earlier on the same variants (as hand-made twins) was 0.936× for cascades and 0.957×
85
+ for virtual, with byte-identical images.
86
+
87
+ **The long-term shape (§5–§6).** Each switched shader is one definition. It declares every binding any of its
88
+ alternatives reads, and a binding a mode does not have falls back to a declared default. Owners bind by name and
89
+ hand the renderer's set to `variant()`. That removes, in three stages:
90
+
91
+ - the two IBL/Brick4 forward-fragment twins;
92
+ - the paired AVBOIT/MBOIT/water exports and the `use_brick4 ? […] : […]` bind-entry arrays;
93
+ - the `build_deferred_main_shader` and `build_ssr_resolve_shader` factories;
94
+ - the per-mode froxel GI shaders;
95
+ - water's hidden GI dependency;
96
+ - the five owners' branches on `indirect_lighting_mode`.
97
+
98
+ Open questions are in §11.
99
+
100
+ ## 2. What the engine does today
101
+
102
+ There are four ways a shader chooses an implementation today. None of them can be seen in the chunk graph.
103
+
104
+ 1. **A runtime branch on a global choice.** `shadowmap_sample_directional` branches per light on
105
+ `shadowmap.virtual_map != NULL`, though the technique is per renderer (`Renderer.feature_virtual_shadow_maps` →
106
+ `GPUSceneShadowmapContext.use_virtual_shadow_maps`). Both paths are linked into nine shader definitions:
107
+ - deferred heavy and deferred ReSTIR (`shader_deferred_main.js`);
108
+ - AVBOIT draw IBL and Brick4 (`TemplatePassDescriptor`, one pipeline per material template);
109
+ - MBOIT resolve IBL and Brick4;
110
+ - volumetrics build-lighting and build-irradiance;
111
+ - water finalize.
112
+
113
+ The chain is `chunk_shadowmap_sample_directional` → `chunk_shadowmap_get_light_visibility` →
114
+ `chunk_shade_directional_lights` → `chunk_shade_standard_material_direct` (and `_restir`) → the shader bodies.
115
+ Volumetrics and water optics reach it from `chunk_shadowmap_get_light_visibility` directly.
116
+ 2. **A factory parameter at the root.** `build_deferred_main_shader({ label, shading_chunk, with_restir })` builds heavy
117
+ and ReSTIR, and the Renderer picks the export by `feature_restir_di_enabled`. This is the `make_shader({ flag })`
118
+ shape that `no-nested-shader-codegen` rules out. ReSTIR also adds a binding, `tRestirRadiance`.
119
+ 3. **A twin of an intermediate chunk.** `chunk_forward_shade_standard_fragment_ibl` and `_brick4` are 178 and 185 lines
120
+ that differ in 46 lines: the indirect-lighting block, plus comments that have already drifted apart. They feed
121
+ `shader_avboit_draw_ibl` / `_brick4` and `shader_oit_resolve_moments_ibl` / `shader_oit_resolve_moments`.
122
+ The owners pick by `indirect_lighting_mode` (`graph_rasterize_meshes_transparent_avboit.js:538`,
123
+ `graph_rasterize_meshes_transparent_oit.js:150`) and build different bind groups, because Brick4 adds a `brick4`
124
+ storage buffer to the view group.
125
+ 4. **A function filled in at the root.** Water's `finalize_body` calls `water_gi_specular` and `water_gi_irradiance`
126
+ without listing a dependency that defines them. `water_finalize_shader_ibl` / `_brick4` add `chunk_water_gi_ibl` or
127
+ `_brick4` beside it (`shader_water.js:593-597`). Nothing needs a twin, but the dependency is invisible.
128
+
129
+ A switch with its choices gives all four one declared form: pattern 1 in stage 1 (shadows), and patterns 2, 3 and 4
130
+ in stage 2, once one definition can carry every alternative's bindings (§5).
131
+
132
+ The engine already has an *open* interface of the same shape. A material template supplies `material_surface` and
133
+ `material_opacity` to every pass by function name (`ShadeMaterialTemplate.surface` / `.opacity`), as Unreal's materials
134
+ and vertex factories do. Switches are the *closed* counterpart: an engine-owned choice whose values the engine knows.
135
+ Neither replaces the other.
136
+
137
+ ## 3. What other engines do
138
+
139
+ Read from source or primary docs. Unreal was read in the installed UE 5.4 tree. The rest comes from the research brief
140
+ `shader-switch-prior-art.md` in the side-notes folder of this session group, which has the URLs and identifiers; its
141
+ Bevy and gpuweb claims were re-checked against the pages.
142
+
143
+ **Unreal: permutation domains.** `RenderCore/Public/ShaderPermutation.h`. A dimension is a C++ type made by
144
+ `SHADER_PERMUTATION_BOOL` / `_INT` / `_RANGE_INT` / `_SPARSE_INT` / `_ENUM_CLASS`, each carrying a `DefineName`.
145
+ `TShaderPermutationDomain<…>` is their product, and its permutation id is mixed-radix over the dimensions
146
+ (`ToDimensionValueId`). A shader class lists its dimensions. `FDeferredLightPS` (`Renderer/Private/LightRendering.cpp:816`)
147
+ has 14 of them, including `FVirtualShadowMapMask`.
148
+
149
+ - `ShouldCompilePermutation` culls combinations: a directional light × the VSM mask is never compiled.
150
+ - `RemapPermutation` folds combinations together: visualize-culling forces the others off.
151
+ - `ModifyCompilationEnvironment` adds the VSM defines only when that dimension is on.
152
+ - The draw site sets every dimension by hand (`LightRendering.cpp:2804-2833`), then asks the shader map for
153
+ `(shader type, permutation id)`. **The owner knows every dimension.** Nothing derives the domain: the shader class lists it
154
+ and the draw site sets it.
155
+ - Dimensions select `#if` blocks. Dependencies are `#include`s, which can sit inside an `#if`, but nothing declares
156
+ what each value pulls in.
157
+
158
+ **Unreal: vertex factories and materials.** These are the other half of Unreal's answer, and they are an *interface
159
+ slot*. `FVertexFactoryType::ModifyCompilationEnvironment` maps the virtual include path
160
+ `/Engine/Generated/VertexFactory.ush` to `#include "<this factory's file>"` (`RenderCore/Public/VertexFactory.h:418-420`).
161
+ `/Engine/Generated/Material.ush` is mapped to the generated material code (`Engine/Private/Materials/MaterialShared.cpp:3357`).
162
+ Every shader that includes the path gets the implementation the compile supplies, and nothing in between is twinned.
163
+ The shader map is keyed by material × vertex factory × shader type × permutation.
164
+
165
+ Note that Unreal's deferred light shader does not choose its *shadow technique* this way. VSM is projected into a
166
+ screen-space mask beforehand, and the light shader only chooses whether to read the mask.
167
+
168
+ **Unity: keywords.** A pass declares its keyword sets by hand with `#pragma multi_compile` / `shader_feature`
169
+ (`_local`, `_vertex`/`_fragment` suffixes). A plain `#include` drops the pragmas of the file it includes, so a library's
170
+ switches do not reach the shader that uses it. `#include_with_pragmas` exists to let them, and URP's `Lit.shader` uses
171
+ it to pull in whole keyword bundles. That is Unity working its way toward a domain derived from the includes.
172
+
173
+ - Since 2021.2 every declared keyword is local to the shader's `LocalKeywordSpace`. A `GlobalKeyword` is mapped into
174
+ each shader's space by name.
175
+ - Setting a keyword the shader does not declare does nothing. This design does the same: values for other switches
176
+ are ignored.
177
+ - Variants are looked up by closest match, and a missing one falls back silently. We should not copy that.
178
+
179
+ **Bevy and naga_oil.** naga_oil passes one global map of shader defs to every module. Each module's `effective_defs`
180
+ are collected by syntax: every name in an `#if`/`#ifdef`, whether or not its branch is taken, plus the effective defs of
181
+ every import. So a module's domain is derived and transitive. The module is cached by a key made only of those defs
182
+ (`ModuleKey::from_members`). That is this design's `descriptor.switches` and its key, arrived at independently.
183
+
184
+ The code itself is chosen by `#ifdef` inside the text, the conditional-template shape this engine rejects.
185
+
186
+ naga_oil's other route is `virtual fn` in a base module and `override fn base::f` in the top-level shader. A
187
+ `Redirector` then rewrites every call in the final module, so no intermediate module is twinned. But the redirect is
188
+ global, so two call sites cannot get different implementations. That is candidate B (§10).
189
+
190
+ **Bevy 0.20 (released 2026-10-08) has since moved all its shaders to WESL** and removed its own WGSL dialect (checked
191
+ against the release post). The release says nothing about what replaces `virtual fn`/`override fn`.
192
+
193
+ **WESL.** `@if`/`@elif`/`@else` attributes go on declarations, statements, struct members and imports. The conditions
194
+ are names joined with `! && ||`; they carry no values. Features are one flat set for the whole link.
195
+
196
+ - The spec makes an unset feature that is used a link error. wesl-js treats it as false. wesl-rs offers
197
+ `Enable/Disable/Keep/Error` and defaults to `Disable`.
198
+ - A removed branch need not be valid, and only declarations reachable from the root are emitted.
199
+ - A module-parameters proposal (#222, unimplemented) would fork only the declarations that depend on a parameter.
200
+
201
+ **gpuweb / WGSL.** WGSL itself has no conditional compilation, shipped or accepted. The only thread is gpuweb#5140,
202
+ "@if for conditions", which is open in Milestone 3 with no decision (checked). Reviewers there asked for *declared*
203
+ conditions, so that a library can say which conditions it reads. That is the same requirement as here.
204
+
205
+ `override` is pipeline-overridable, but the spec defines a shader as the call closure from the entry point *whatever the
206
+ constants' values*. A function reached only through a false `if` on an override is part of the shader, its bindings stay
207
+ in the shader's interface, and nothing requires the dead branch to be removed. Chrome's Tint keeps it as `if (false)`
208
+ (measured 2026-10-16), and DXC then drops it.
209
+
210
+ This is a second reason an override is the wrong mechanism, beyond hiding the dependency. A Brick4-or-IBL override would
211
+ still need the `brick4` buffer bound in IBL mode.
212
+
213
+ **Slang: link-time `extern`/`export`.** A module declares `extern struct S : IFoo;` or `extern static const int k;`. A
214
+ provider module declares `export struct S : IFoo = Impl;`. The program is composed from the modules chosen
215
+ (`createCompositeComponentType`) and then `link()`ed. Unselected providers never enter the program, and intermediate
216
+ modules compile once.
217
+
218
+ This is the cleanest prior art for choosing deep in a graph without duplication, and it is open, like B. Whether any
219
+ API lists a module's unresolved externs is unverified.
220
+
221
+ **Filament** chooses the shadow technique per View at *run time*. Only the sampler type is a variant; PCF, VSM and
222
+ EVSM are a uniform (`frameUniforms.shadowSamplingType`). That is today's meep, the runtime branch, which costs 4–6 % of
223
+ the deferred pass here.
224
+
225
+ **Godot** reads each specialization constant through an accessor that falls back to a push constant when built as an
226
+ "ubershader". One source then serves both the specialized pipeline and a fallback compiled in the background. We do not
227
+ need that, because `PipelineEnquiry` already compiles off the main thread.
228
+
229
+ **What this design takes, and what it leaves.**
230
+
231
+ - **From Unreal:** a dimension as an object with a closed value set, and a key per shader made of its values. But not
232
+ the owner listing every dimension; here the graph lists them.
233
+ - **From naga_oil:** deriving the domain transitively from the graph, and keying only on it.
234
+ - **From Unity:** values a shader does not use are ignored.
235
+ - **From WESL's spec:** an unset value is an error, not false.
236
+ - **From Slang and vertex factories:** the implementation is linked in *beside* the code that calls it, so nothing in
237
+ between is twinned or rebuilt.
238
+ - **Left out:** conditional text inside a chunk (Unreal's and naga_oil's `#if`, WESL's `@if` on statements), which is
239
+ what `CodeChunk` exists to avoid. Also left out: global redirects (naga_oil overrides) and closest-match fallback
240
+ (Unity).
241
+
242
+ An alternative here is a whole chunk with its own dependencies, which is WESL's `@if` on a declaration, raised to a
243
+ declared, typed choice.
244
+
245
+ ## 4. The design
246
+
247
+ ### 4.1 Pieces
248
+
249
+ | piece | is | API |
250
+ |---|---|---|
251
+ | `ShaderSwitch` | a named choice over an engine enum's members (default: the member equal to 0) or over `false`/`true` (default `false`) | `ShaderSwitch.enum(label, Enum)`, `ShaderSwitch.bool(label)`; `label`, `values`, `default_value`, `is_value(v)`, `name_of(v)` |
252
+ | `ShaderSwitchSet` | immutable, **interned** values for some switches; a switch it does not name is at its default | `EMPTY`, `with(switch, value)`, `get(switch)`, `restricted_to(switches)` |
253
+ | `CodeChunkChoice` | a chunk standing for one alternative per value of one switch | `CodeChunkChoice.from(switch)`, `add(value, chunk)`, `alternative_for(set)` |
254
+ | `CodeChunk.specialize(set)` | the graph with every choice replaced by its selected alternative | returns a static `CodeChunk` |
255
+ | `CodeChunk.collectSwitches()` | every switch the graph decides, through every alternative | `ShaderSwitch[]` |
256
+ | `variant(set)` | on `ShaderDescriptor`, `ImageShader`, `ComputeShader`, and (designed) `TemplatePassDescriptor` | a static shader of the same class |
257
+
258
+ **Interning** makes a set a value: two sets that give every switch the same value are one object, and a switch set to
259
+ 0 is not stored. So:
260
+
261
+ - `a.with(X, 1).with(Y, 2) === a.with(Y, 2).with(X, 1)`;
262
+ - `EMPTY.with(X, X.default_value) === EMPTY`;
263
+ - every cache keys by identity;
264
+ - `toString()` names the members, `shadow directional technique=Virtual`, which is what labels carry (D7);
265
+ - `with` returns `this` when the value is already that, so a renderer re-deriving its set every frame allocates
266
+ nothing until a flag changes.
267
+
268
+ Each set is made once, on first use, and kept for the process, so the table only grows by sets actually used.
269
+
270
+ ### 4.2 Specialization: a switched graph becomes a static one
271
+
272
+ `specialize(set)` walks the graph once, depth-first, with a memo from source chunk to result:
273
+
274
+ - A **choice** becomes the specialization of the alternative `set` selects. An alternative may itself be a choice of
275
+ another switch.
276
+ - A **chunk whose dependencies all come back as themselves** is returned as it is. Everything not above a choice is
277
+ shared with the source graph, and a graph with no choice specializes to itself.
278
+ - **Any other chunk** becomes a new `CodeChunk`: the same text and extensions, with specialized dependencies. If two
279
+ dependencies resolve to the same chunk, it is kept once.
280
+ - **The memo** keeps what two chunks share in the source shared in the result, so a diamond stays a diamond.
281
+
282
+ `compile()` is unchanged, except that it throws, naming the choice, if a choice reaches it. The linker only ever sees
283
+ static graphs.
284
+
285
+ `collectSwitches()` walks every alternative, selected or not, so a switch nested inside one alternative is still in
286
+ the shader's domain.
287
+
288
+ Errors:
289
+
290
+ - a value with no alternative at a choice names the switch and the value;
291
+ - a cycle through any alternative is refused at `add()`;
292
+ - a choice equals only itself, since every choice has the same empty text.
293
+
294
+ ### 4.3 Variants on the shader classes
295
+
296
+ "The shader" caches its variants by the set, as you asked. Every class has the same shape, `variant(set)`:
297
+
298
+ 1. Look `set` up by identity. A frame asking again stops here: 0.1 µs, no allocation.
299
+ 2. Restrict it to the descriptor's `switches`. Sets that agree on this shader's switches get the same variant.
300
+ 3. If the restricted set is `EMPTY`, return `this`. The object a module exports *is* the all-defaults variant, so
301
+ `shader.pipeline` and `descriptor.source` keep meaning what they mean today.
302
+ 4. Otherwise build, once, a static object of the same class from the specialized descriptor.
303
+
304
+ | class | status | variant is |
305
+ |---|---|---|
306
+ | `ShaderDescriptor` | prototyped | `ShaderDescriptor.from({ label: '<label> [<set>]', resources: same object, body: body.specialize(set) })`. `switches` = `body.collectSwitches()`. `source` = the all-zeros variant. |
307
+ | `ImageShader` | prototyped | `new ImageShader({ descriptor: descriptor.variant(set), targets, depthStencil })`, the same targets and depth state as the original. |
308
+ | `ComputeShader` | prototyped | `new ComputeShader(descriptor.variant(set))`. Refused if its `@workgroup_size` differs from the original's: a switch changes what a pass computes, never how it is dispatched. |
309
+ | `TemplatePassDescriptor` | designed | a sibling sharing `label`, `build` and the attachments. `build(template, set)` gets the set and returns `….variant(set).source`; existing builds that ignore the second argument keep working. Its own per-manager × template × cull-mode cache is unchanged. The reads of `.pipeline` for its layout (`graph_rasterize_meshes_transparent_avboit.js:287`, `:593`; `graph_rasterize_meshes_transparent_oit.js:205`, `:353`) becomes a `pipeline_layout` getter, so a sibling needs no pipeline of its own. |
310
+ | water's finalize pipeline | designed | `create_water_finalize_pipeline(set)`, cached the same way. |
311
+
312
+ A few things fall out of variants being whole static objects:
313
+
314
+ - `features` (the `enable` list) and `validate_against_device` are per variant, with no extra work.
315
+ - Labels carry the set, so profiler entries and validation errors name the variant they came from.
316
+
317
+ ### 4.4 Where the set comes from
318
+
319
+ - **Who builds it.** The Renderer, from its feature flags, at the start of `render()`:
320
+
321
+ ```js
322
+ this.#shader_switches = this.#shader_switches
323
+ .with(SHADOW_DIRECTIONAL_TECHNIQUE, this.feature_virtual_shadow_maps ? ShadowDirectionalTechnique.Virtual : ShadowDirectionalTechnique.Cascades)
324
+ .with(INDIRECT_LIGHTING, this.indirect_lighting_mode) // stage 2
325
+ .with(LOCAL_LIGHT_SHADING, this.feature_restir_di_enabled ? LocalLightShading.ReSTIR : LocalLightShading.Clustered); // stage 2
326
+ ```
327
+
328
+ This allocates nothing while the flags are unchanged.
329
+ - **How it reaches owners.** Each owner is handed it the way it is handed `indirect_lighting_mode` today: the deferred
330
+ pass in the Renderer, the volumetrics and transparency graph functions through their options, water through its
331
+ draw call. An owner passes the set opaquely to `variant()`. It never learns which switches its shaders contain, and
332
+ in stage 2 the set replaces the `indirect_lighting_mode` parameter it carries today.
333
+ - **A per-pipeline choice.** An owner asks for it explicitly, with `shader.variant(switches.with(X, v))`, for example a
334
+ debug view that forces cascades.
335
+ - **A forgotten set.** With D3 an owner that forgets to pass the set draws the all-defaults variant, silently. That is the price of
336
+ defaults. The domain is enumerable, so a review can list every shader with non-empty `switches` and check that its
337
+ owner calls `variant`.
338
+
339
+ ### 4.5 Caching and keys
340
+
341
+ - **Shader level:** by set identity, then by the restricted set (§4.3).
342
+ - **GPU pipeline managers: unchanged.** `RenderPipelineManager` and `ComputePipelineManager` key by descriptor, which
343
+ includes the WGSL text. Distinct variants are distinct pipelines. Sets that produce the same text share one: LPV and
344
+ IBL on the transparent paths, or any set differing only in switches a shader does not have.
345
+ - **What affects what.** `descriptor.switches` lists a shader's switches, and `TemplatePassDescriptor.switches_of(template)`
346
+ does the same per template, since a template's own chunks may hold a choice. "Which pipelines does flipping X
347
+ recompile" is the set of shaders whose `switches` include X.
348
+
349
+ ### 4.6 Preparation, and a flip
350
+
351
+ - **Offers.** The Renderer offers `shader.variant(this.#shader_switches).pipeline` and hands the set to the `enquire_*`
352
+ functions, so only the selected variants are prepared.
353
+ - **When the set changes**, nothing extra happens (D6). The first frame that draws with the new set makes its
354
+ variants through `obtain`, synchronously, as a feature turned on after `initialize()` does today. That is about 1 s
355
+ cold for the deferred shader. The shadow records and the shaders flip in that same frame.
356
+ - **Speculative offers.** The domain is enumerable, so a settings screen can offer the other value in the background to
357
+ make flips instant.
358
+
359
+ ### 4.7 Specs
360
+
361
+ - **Existing specs keep passing.** With D3 they test the all-defaults variant, and the whole shade suite (877 files) passes on the
362
+ prototype unchanged.
363
+ - **New specs per switched shader:** "every value validates as WGSL", a plain `for` over `switch.values` inside one test,
364
+ not `.each`.
365
+ - **Chunk-level harnesses** that compile a graph containing a choice call `specialize(set)` first. The linker refuses
366
+ one that does not.
367
+
368
+ ## 5. Resources: the long-term half
369
+
370
+ ### 5.1 What differs between alternatives today
371
+
372
+ | fork | bindings only one mode has | does the resource exist in the other modes? |
373
+ |---|---|---|
374
+ | AVBOIT draw, MBOIT resolve | `brick4` (in the view group) | yes: `Brick4LightMap` creates its buffer in its constructor, per scene |
375
+ | water finalize | `brick4`, `view` (an extra group) | yes |
376
+ | SSR resolve (`build_ssr_resolve_shader` factory) | IBL `tEnvironment`; Brick4 `brick4`, `tSTBN_vec2`; LPV `tEnvironment` + 3 textures, 3 buffers | Brick4 and STBN yes; LPV's only when LPV runs |
377
+ | froxel GI (a module per mode) | Brick4 `brick4`; LPV its atlas and buffers | Brick4 yes; LPV's only when LPV runs |
378
+ | deferred heavy/ReSTIR (factory) | `tRestirRadiance` | only when ReSTIR runs |
379
+
380
+ Mode-specific bindings cannot simply get a group of their own. The raster passes already use groups 0–3 (material,
381
+ scene, view, lights), which is all WebGPU guarantees (`maxBindGroups` defaults to 4, and the device floor does not
382
+ raise it).
383
+
384
+ ### 5.2 Recommended: one interface per definition, the union of its alternatives'
385
+
386
+ A switched shader's resource set declares every binding any of its alternatives reads. Switches then change code only,
387
+ never the interface (D4 holds forever, not just in stage 1). It follows that:
388
+
389
+ - **Layouts don't depend on the set.** The shared groups (`SHADING_LIGHT_RESOURCE_GROUP`, `FORWARD_LIGHT_LAYOUT`) and the
390
+ bind groups built from them stay shared across variants and passes. A layout may list a binding the selected variant
391
+ does not read; WebGPU allows that.
392
+ - **Owners bind by name.** `group.generateBindings(map, encoder)` already does this, and every pass becomes
393
+ "pass what you have". The positional `use_brick4 ? [ … ] : [ … ]` arrays in the AVBOIT and MBOIT owners go.
394
+ - **A binding a mode lacks falls back to its declared default.**
395
+ - Textures already take one: `addTexture(…, BLACK_PIXEL_TEXTURE)` is in use. `tRestirRadiance` defaults to black.
396
+ - Storage buffers have no default parameter today. One convention would be added: `addStorageBuffer(name, type,
397
+ write, default_value)`, with the engine's zero buffer of the type's minimum size. LPV's three buffers need it.
398
+
399
+ **Unmeasured:** whether an unread binding in a layout costs anything at draw time. I know of no reason it would, and
400
+ `SHADING_LIGHT_RESOURCE_GROUP` is already shared by shaders that do not read all of it. Stage 2 would measure the
401
+ transparent pass before and after.
402
+
403
+ ### 5.3 Later, and optional: chunks declare the bindings they read
404
+
405
+ Today `chunk_shade_directional_lights` says in prose that it "relies on the `light_data` storage binding and the shadow
406
+ atlas bindings being present". `chunk_water_gi_brick4` says "Expects `brick4` and `view`". These are implicit
407
+ dependencies, of the same kind as water's hidden GI function.
408
+
409
+ A chunk would declare them through a channel beside `addExtension`: `chunk.requireResource(RESOURCE_BRICK4)`, with the
410
+ resource descriptor exported once. `compile()` collects them over the static graph as it collects extensions, and
411
+ `ShaderDescriptor` checks each against its resource set by name and type. The error names both: "`chunk_brick4_sample_*`
412
+ reads `brick4`, which `AVBOIT / draw` does not bind".
413
+
414
+ With switches, a variant's required set is then known exactly. Migration is chunk by chunk, since an undeclared read
415
+ works as it does today.
416
+
417
+ ### 5.4 Considered and not recommended
418
+
419
+ - **Resources chosen by switch, per variant.** A group, or entries within one, would change with the set, so each
420
+ variant binds only what it reads. Shared groups would stop being shared whenever a variant read less than all of
421
+ one, and owners would build a bind group per variant. It buys nothing measured.
422
+ - **A group per mode.** It does not fit in 4 groups.
423
+
424
+ ## 6. Migration, in three stages
425
+
426
+ Each stage is a set of one-concern commits, red-then-green where it fixes something.
427
+
428
+ ### Stage 1: the mechanism and shadows (code-only)
429
+
430
+ 1. The compiler layer as prototyped: `ShaderSwitch`, `ShaderSwitchSet`, `CodeChunkChoice`, `specialize`,
431
+ `collectSwitches`, and `variant` on `ShaderDescriptor`, `ImageShader` and `ComputeShader`.
432
+ 2. `TemplatePassDescriptor.variant` and `build(template, set)`. `.pipeline` reads for layout become `pipeline_layout`.
433
+ Water's finalize builder takes a set.
434
+ 3. Shadows:
435
+ - add `ShadowDirectionalTechnique { Cascades: 0, Virtual: 1 }` and
436
+ `SHADOW_DIRECTIONAL_TECHNIQUE = ShaderSwitch.enum('shadow directional technique', ShadowDirectionalTechnique)`;
437
+ - split `_cascades` (today's function without the virtual branch) from `_virtual` (record read; NULL → 1.0; else
438
+ `shadowmap_sample_virtual`);
439
+ - make `chunk_shadowmap_sample_directional` the choice. Nothing above it changes.
440
+ 4. The Renderer builds `#shader_switches` and hands it to:
441
+ - the deferred pass;
442
+ - volumetrics build-lighting and build-irradiance;
443
+ - AVBOIT and MBOIT;
444
+ - water;
445
+ - the `enquire_*` functions.
446
+
447
+ 5. Specs: about a dozen that compile these shaders directly gain a loop over the technique.
448
+
449
+ ### Stage 2: resources, and the forks that needed them
450
+
451
+ Every step below is "one definition, one choice, bind by name".
452
+
453
+ - **`INDIRECT_LIGHTING = ShaderSwitch.enum('indirect lighting', ShadeIndirectLightingMode)`**, the enum as it is, with IBL
454
+ as 0.
455
+ - **Forward fragment:** one `chunk_forward_shade_standard_fragment` calling `forward_sample_indirect(…)`. A choice
456
+ provides it: IBL → environment, Brick4 → `brick4_sample_forward_indirect`, LPV → environment. This replaces the
457
+ 178/185-line twins.
458
+ - **AVBOIT and MBOIT:** one draw and one resolve, each with `brick4` in the view group. The `_ibl`/`_brick4` exports and
459
+ the branch on `indirect_lighting_mode` in both owners go.
460
+ - **Water:** one finalize shader. `chunk_water_gi` is a choice, and `finalize_body` lists it as a dependency, so the
461
+ hidden dependency becomes explicit.
462
+ - **SSR resolve:** one shader. `ssr_sample_radiance` (and `ssr_sample_hit_irradiance`) are choices, and
463
+ `build_ssr_resolve_shader` goes.
464
+ - **Froxel GI:** one shader whose per-froxel radiance lookup is a choice; the three modules become three alternatives.
465
+ - **`LOCAL_LIGHT_SHADING = ShaderSwitch.enum('local light shading', LocalLightShading)`**, `{ Clustered: 0, ReSTIR: 1 }`. The deferred body depends on a choice between
466
+ `chunk_shade_standard_material_direct` and `_restir`. `tRestirRadiance` defaults to black.
467
+ `build_deferred_main_shader` and `shader_deferred_main_restir` go. The forward paths keep linking the clustered chunk
468
+ directly, so they never depend on the switch.
469
+ - **The storage-buffer default convention** (§5.2), for LPV's buffers.
470
+ - **Measure:** the transparent pass and SSR before and after, by the paired method.
471
+
472
+ ### Stage 3 (optional): declared bindings
473
+
474
+ `requireResource` and the link-time check (§5.3), then chunk by chunk, starting with the ones that say "relies on" or
475
+ "expects" in prose.
476
+
477
+ ### What goes away, in total
478
+
479
+ - **Duplicate code:**
480
+ - the forward fragment twins;
481
+ - the AVBOIT and MBOIT export pairs;
482
+ - the water finalize pair;
483
+ - two per-mode SSR wrappers and three froxel GI modules, which become alternatives;
484
+ - `shader_deferred_main_restir`.
485
+ - **Factories:** `build_deferred_main_shader` and `build_ssr_resolve_shader`.
486
+ - **Owner branches:** five `switch`/`?:` on `indirect_lighting_mode`, two positional bind-entry pairs, and the
487
+ `indirect_lighting_mode` parameter threaded through those owners.
488
+ - **A runtime branch:** the per-light shadow technique branch, worth 4–6 % of the deferred pass.
489
+
490
+ ## 7. Prototype and measurements
491
+
492
+ ### Specs, 33 new tests
493
+
494
+ - `compiler/ShaderSwitch.spec.js`, 4 tests: an enum's members in declaration order with default 0; a bool's
495
+ `false`/`true` with default `false`; an enum without a 0 member refused; repeated values refused.
496
+ - `compiler/CodeChunkChoice.spec.js`, 16 tests:
497
+ - only the selected closure is linked;
498
+ - an unnamed enum switch is 0, an unnamed bool `false`;
499
+ - an enum with gaps between its values;
500
+ - a choice three chunks deep needs one copy of each chunk above it;
501
+ - a shared chunk stays one chunk;
502
+ - topological order;
503
+ - a static graph is its own specialization;
504
+ - the linker refuses a choice;
505
+ - discovery from above;
506
+ - a switch nested in one alternative, and one with 3 values;
507
+ - an alternative that is a choice;
508
+ - one switch at two places;
509
+ - a missing alternative names the switch and the member;
510
+ - cycles;
511
+ - identity.
512
+ - `compiler/ShaderSwitchSet.spec.js`, 5 tests: interning regardless of order; a default is not stored; a changed
513
+ value; restriction; `toString` names the members.
514
+ - `ShaderDescriptor.spec.js`, 4 tests: domain found two chunks down; `source` is the all-defaults variant; a variant
515
+ links one alternative, has no switches and is labelled `… technique=Virtual`; sets that agree share a variant, and
516
+ the empty set gives the descriptor itself.
517
+ - `ImageShader.spec.js`, 2 tests, and `ComputeShader.spec.js`, 2 tests: the variant's pipeline code and targets, caching,
518
+ `this` for all-defaults, and a variant with another `@workgroup_size` refused.
519
+
520
+ ### The real deferred shader
521
+
522
+ A scratch probe (`scratchpad/probe/shadow_choice.probe.js`, not in the tree) rebuilds the four chunks above
523
+ `chunk_shadowmap_sample_directional` from their real `.text` and dependency lists, with the choice in its place. In the
524
+ engine they would not change at all.
525
+
526
+ | | characters | functions | engine validator | Tint (Chrome, RTX 4090) | vs shipped |
527
+ |---|---|---|---|---|---|
528
+ | shipped (both paths) | 69,917 | 96 | pass | 0 messages | |
529
+ | cascades | 59,892 | 82 | pass | 0 messages | −14 VSM functions, −4 VSM structs/consts; only `shadowmap_sample_directional` changed |
530
+ | virtual | 62,682 | 94 | pass | 0 messages | −`shadowmap_csm_compute_cascade_blended` and its row read, −2 structs; only `shadowmap_sample_directional` changed |
531
+
532
+ - **Link time**, median of 200 interleaved runs: today's `compile()` 0.079 / 0.083 ms in two runs; `specialize` +
533
+ `compile()` of a variant 0.075 / 0.083 ms.
534
+ - **`variant(set)` with a set already seen:** 0.1 µs.
535
+ - **Pipeline creation** (`tint_check.mjs`):
536
+ - **Method.** `createRenderPipelineAsync` with the deferred pass's vertex stage, target and depth state, and
537
+ `layout: 'auto'`. One at a time, rotating order, 8 rounds. A fresh seed constant per compile, so neither Dawn's
538
+ nor the driver's cache serves it.
539
+ - **Results.** Two runs: shipped 1251 / 1225 ms, cascades 1009 / 1017 ms (0.81× / 0.83×), virtual 877 / 810 ms
540
+ (0.70× / 0.66×).
541
+ - **Caveat.** This is wall time on a busy box, not the trace's `InitializeImpl`, so it compares with the earlier
542
+ twins (0.78× / 0.66×) by ratio only.
543
+ - **GPU time was not re-measured.** The 0.936× / 0.957× were measured on twins built by the same removals. The cascades
544
+ variant is that twin. The virtual variant is the twin's branch inverted (`== NULL → 1.0` first).
545
+
546
+ ## 8. Costs
547
+
548
+ | part | size |
549
+ |---|---|
550
+ | prototype as it stands | 3 classes (540 lines with docs); `CodeChunk` +135; `ShaderDescriptor` +67/−1; `ImageShader` +51; `ComputeShader` +49; 6 spec files (504 lines) |
551
+ | rest of stage 1 (estimate) | `TemplatePassDescriptor` (~50), water builder, Renderer set + plumbing to the owners and the enquiries (~100), shadow split (1 file into 3), ~12 specs |
552
+ | stage 2 (estimate) | the steps of §6 stage 2, each mostly deletion. Expected net negative in lines, not counted. |
553
+
554
+ Runtime:
555
+
556
+ - No per-frame allocation once the set is steady.
557
+ - A lookup is 0.1 µs, and a variant costs 0.075–0.083 ms to specialize and link.
558
+ - A flip compiles the affected variants on first use (D6). The old ones stay cached.
559
+
560
+ Risks:
561
+
562
+ - **A forgotten set draws the all-defaults variant silently (§4.4).**
563
+ - **Errors move to first use.** A broken non-zero alternative only shows when a pipeline is made. The per-value specs
564
+ catch it.
565
+
566
+ ## 9. Reuse map
567
+
568
+ | piece | reuses / generalises |
569
+ |---|---|
570
+ | specialization | `CodeChunk`'s own graph and private fields, with the same identity-based dedup the linker relies on. No second linker; `compile()` is untouched. |
571
+ | values | engine enums as they are (`ShadeIndirectLightingMode` unchanged), or a bool. The shape of Unreal's `SHADER_PERMUTATION_ENUM_CLASS`/`_BOOL`, and like its `SPARSE_INT` an enum need not be contiguous. |
572
+ | variant objects | `ShaderDescriptor`, `ImageShader`, `ComputeShader` and `TemplatePassDescriptor` themselves. A variant is one of them, so draw, dispatch, validation and the profiler need nothing new. |
573
+ | GPU pipeline cache | `RenderPipelineManager` / `ComputePipelineManager` unchanged. Text equality separates and shares variants. |
574
+ | preparation | `PipelineEnquiry` and `prepare()` at start-up, offering the selected variants. A flip uses `obtain` as anything made on first use does. |
575
+ | bind by name | `ShaderResourceGroupDescriptor.generateBindings`, and texture `default_value` (`BLACK_PIXEL_TEXTURE`). One new default convention, for storage buffers. |
576
+ | declared bindings (stage 3) | the `addExtension` / `addLanguageExtension` channel: collected at link, the same way. |
577
+ | `ProgrammableStage.constants` | left for what `override` is for: numbers. Not for choosing code. |
578
+
579
+ ## 10. Candidates considered (revision a)
580
+
581
+ - **A: a switch is the choice.** The value is an alternative chunk, and each place where the graph chooses is its own
582
+ switch. It would make the renderer name every place a decision is made. Superseded by D1.
583
+ - **B: open slots filled by whoever builds the shader.** This is Unreal's vertex-factory include, naga_oil's
584
+ `override fn`, Slang's `extern`/`export`, and today's water GI. Nothing can enumerate a slot's implementations, so
585
+ preparation and specs cannot loop over variants. Material templates already are the engine's open interface.
586
+ - **C: a switch decided at choices in the graph.** This is the design above, now with enum and bool values (D1, D9), a
587
+ static graph per variant (D2) and a default (D3).
588
+ - **Rejected before this proposal:** WGSL `override` constants. They hide the dependency, and the spec keeps a function
589
+ reached through a false override `if` in the shader, its bindings included.
590
+
591
+ ## 11. Open questions for Alex
592
+
593
+ The design questions are closed (D1–D9). An enum must have a member equal to 0, a rule Alex kept. Stage 1 landed as
594
+ four one-concern commits:
595
+
596
+ 1. the compiler layer;
597
+ 2. the shader classes' `variant`;
598
+ 3. `TemplatePassDescriptor`'s;
599
+ 4. the shadow switch with the renderer's set and its plumbing.
600
+
601
+ Open: whether stage 2 (§6) follows next, `INDIRECT_LIGHTING` and `LOCAL_LIGHT_SHADING` with the bindings union.
602
+
603
+ ## 12. Stage 1, as landed (2026-10-16)
604
+
605
+ ### What it contains
606
+
607
+ - **The mechanism:**
608
+ - `ShaderSwitch` (`enum` / `bool`), `ShaderSwitchSet` and `CodeChunkChoice`;
609
+ - `CodeChunk.specialize` and `collectSwitches`;
610
+ - `variant(set)` on `ShaderDescriptor`, `ImageShader`, `ComputeShader` and `TemplatePassDescriptor`.
611
+
612
+ A `TemplatePassDescriptor` build is now `(template, switches)`. The four transparent passes that light fragments
613
+ build `….variant(switches)`, and their pipeline labels carry the set. Owners read a template pass's layout through
614
+ `pipeline_layout`, so a variant needs no pipeline of its own.
615
+ - **The shadow switch:**
616
+ - `ShadowDirectionalTechnique { Cascades: 0, Virtual: 1 }`;
617
+ - `SHADOW_DIRECTIONAL_TECHNIQUE = ShaderSwitch.enum('shadow directional technique', ShadowDirectionalTechnique)`;
618
+ - `chunk_shadowmap_sample_directional_cascades` and `chunk_shadowmap_sample_directional_virtual`.
619
+
620
+ `chunk_shadowmap_sample_directional` is now the choice between those two alternatives. No chunk above it changed.
621
+ - **The renderer:**
622
+ - `Renderer.#shader_switches` follows `feature_virtual_shadow_maps`. It is updated in `#draw_to_target`, beside the
623
+ shadow context's own flag, and in `enquirePipelines`, and readable as `renderer.shader_switches`.
624
+ - It is handed to:
625
+ - the deferred pass;
626
+ - `GPUViewVolumetrics.graph_update`, which passes it to froxel irradiance and light scattering;
627
+ - the AVBOIT orchestrator, which passes it to the draw pass and to each side channel's `avboit_prepare` (water);
628
+ - the MBOIT orchestrator, for its resolve pass.
629
+ - **Water:** `create_water_finalize_pipeline_ibl/brick4(switches)`, and `GPUWaterRenderer` caches the result per GI mode
630
+ and set.
631
+
632
+ ### Tests
633
+
634
+ - **`shadow_directional_technique_consumers.spec.js`** covers all nine shaders under both techniques. In each, exactly
635
+ the selected technique's sampling is linked, the other's is absent, and the module passes the WGSL checks. With every
636
+ switch at its default, the result is cascades.
637
+ - **`TemplatePassDescriptor.spec.js`** (3 tests): a variant builds each template for its set; one variant per set, and
638
+ the pass itself for the empty set; a variant shares its original's attachments and layout.
639
+ - **The AVBOIT orchestrator spec** gained a test on the software device: water's optics pipeline, as recorded on the
640
+ device, links the technique of the set the orchestrator was handed. It fails if the orchestrator stops passing the set
641
+ to side channels (checked).
642
+ - **Five chunk-level harnesses now specialize before linking**, which the linker requires:
643
+ - the cascades harness to Cascades;
644
+ - the VSM harness to Virtual — 4 of its 6 tests fail if it is built for Cascades instead (checked);
645
+ - the normal-decode harness and the two water-optics harnesses to the default.
646
+ - **Gates:** the shade suite passes, 879 files and 6,744 tests. `check-types` reads 18 against the base's 18.
647
+
648
+ ### Device check (one-off, headless Chrome, RTX 4090)
649
+
650
+ The stage-1 tree and its base (`03d5816bd`) were served side by side.
651
+
652
+ - **VSM playground (deferred lighting).**
653
+ - Stage 1 makes two shadow-sampling pipelines: `Shade From G-Buffer`, cascades only, offered at start-up; and
654
+ `Shade From G-Buffer [shadow directional technique=Virtual]`, virtual only. No pipeline links both.
655
+ - The base makes one pipeline that links both.
656
+ - **Images.** Stage 1 against the base differs in no more pixels than the base against itself, with the playground's
657
+ movers animating and the filter jittered per frame:
658
+
659
+ | technique | stage 1 vs base | base vs base |
660
+ |---|---|---|
661
+ | cascades | 6.3 % of pixels | 7.4 % |
662
+ | virtual | 5.5 % of pixels | 7.8 % |
663
+
664
+ - **The same playground with a fog volume added from the page (deferred + volumetric lighting).**
665
+ - Stage 1 adds `Volumetrics / build lighting`, cascades only, and `Volumetrics / build lighting [shadow directional
666
+ technique=Virtual]`, virtual only. The base has one, which links both.
667
+ - The fog's per-frame jitter moves most pixels by a level or two, in both comparisons alike:
668
+
669
+ | technique | stage 1 vs base: pixels off by > 8 levels (mean max-channel difference) | base vs base |
670
+ |---|---|---|
671
+ | cascades | 5.97 % (4.32) | 5.70 % (4.33) |
672
+ | virtual | 6.00 % (4.38) | 5.81 % (4.44) |
673
+ - **Console errors.** `vsm: feedback readback failed` appears only after a tab is closed, on the base as on stage 1.
674
+ It is a readback cut short by the close.
675
+
676
+ Not checked on the device:
677
+
678
+ - **water and MBOIT,** whose playground does not expose its renderer for the flip. They are covered on the software
679
+ device and by the consumer spec.
680
+ - **GPU time of the variants.** The 0.936× / 0.957× of revision (a) were measured on equivalent twins.