@forgeax/engine-render 0.1.20 → 0.1.23

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 (645) hide show
  1. package/README.md +365 -29
  2. package/dist/assembly/backend-contract.d.ts +13 -0
  3. package/dist/assembly/backend-contract.d.ts.map +1 -1
  4. package/dist/assembly/device-feature-admission.d.ts +18 -0
  5. package/dist/assembly/device-feature-admission.d.ts.map +1 -0
  6. package/dist/assembly/directional-shadow-inspection.d.ts +38 -0
  7. package/dist/assembly/directional-shadow-inspection.d.ts.map +1 -0
  8. package/dist/assembly/factory.d.ts +30 -19
  9. package/dist/assembly/factory.d.ts.map +1 -1
  10. package/dist/assembly/host-contract.d.ts +19 -0
  11. package/dist/assembly/host-contract.d.ts.map +1 -1
  12. package/dist/assembly/render-target-host.d.ts +46 -0
  13. package/dist/assembly/render-target-host.d.ts.map +1 -0
  14. package/dist/assets/render-asset-port.d.ts.map +1 -1
  15. package/dist/authoring.mjs +4 -1
  16. package/dist/authoring.mjs.map +1 -1
  17. package/dist/capture/cube-views.d.ts +21 -0
  18. package/dist/capture/cube-views.d.ts.map +1 -0
  19. package/dist/capture/scheduler.d.ts +54 -0
  20. package/dist/capture/scheduler.d.ts.map +1 -0
  21. package/dist/{chunk-PCHBPYML.mjs → chunk-4L3P6DT5.mjs} +2 -2
  22. package/dist/chunk-4L3P6DT5.mjs.map +1 -0
  23. package/dist/{chunk-VFPLTKCU.mjs → chunk-4NPUASZR.mjs} +15 -4
  24. package/dist/chunk-4NPUASZR.mjs.map +1 -0
  25. package/dist/chunk-DIYW3M5D.mjs +5085 -0
  26. package/dist/chunk-DIYW3M5D.mjs.map +1 -0
  27. package/dist/chunk-FBC4R44J.mjs +1003 -0
  28. package/dist/chunk-FBC4R44J.mjs.map +1 -0
  29. package/dist/{chunk-CDTWTHZR.mjs → chunk-FF7K3FSO.mjs} +474 -168
  30. package/dist/chunk-FF7K3FSO.mjs.map +1 -0
  31. package/dist/chunk-HZUPSYW7.mjs +1104 -0
  32. package/dist/chunk-HZUPSYW7.mjs.map +1 -0
  33. package/dist/{chunk-TBQJOK2L.mjs → chunk-QODA5UKU.mjs} +321 -269
  34. package/dist/chunk-QODA5UKU.mjs.map +1 -0
  35. package/dist/chunk-YALPS44A.mjs +296 -0
  36. package/dist/chunk-YALPS44A.mjs.map +1 -0
  37. package/dist/{chunk-4XINDLAY.mjs → chunk-ZUWKAZ2I.mjs} +1963 -2178
  38. package/dist/chunk-ZUWKAZ2I.mjs.map +1 -0
  39. package/dist/cluster-binner.d.ts +5 -0
  40. package/dist/cluster-binner.d.ts.map +1 -1
  41. package/dist/components/camera.d.ts +1 -0
  42. package/dist/components/camera.d.ts.map +1 -1
  43. package/dist/components/cube-camera.d.ts +20 -0
  44. package/dist/components/cube-camera.d.ts.map +1 -0
  45. package/dist/components/directional-light.d.ts +11 -2
  46. package/dist/components/directional-light.d.ts.map +1 -1
  47. package/dist/components/directional-shadow-filter.d.ts +25 -0
  48. package/dist/components/directional-shadow-filter.d.ts.map +1 -0
  49. package/dist/components/index.d.ts +5 -0
  50. package/dist/components/index.d.ts.map +1 -1
  51. package/dist/components/light-helpers.d.ts +4 -0
  52. package/dist/components/light-helpers.d.ts.map +1 -1
  53. package/dist/components/light-probe.d.ts +9 -0
  54. package/dist/components/light-probe.d.ts.map +1 -0
  55. package/dist/components/rect-area-light.d.ts +12 -0
  56. package/dist/components/rect-area-light.d.ts.map +1 -0
  57. package/dist/components/reflection-probe.d.ts +17 -0
  58. package/dist/components/reflection-probe.d.ts.map +1 -0
  59. package/dist/components/scene-instance.d.ts +4 -0
  60. package/dist/components/scene-instance.d.ts.map +1 -1
  61. package/dist/components/spot-light.d.ts +3 -0
  62. package/dist/components/spot-light.d.ts.map +1 -1
  63. package/dist/construct-renderer.mjs +14836 -9620
  64. package/dist/construct-renderer.mjs.map +1 -1
  65. package/dist/device/gpu-residency.d.ts +8 -1
  66. package/dist/device/gpu-residency.d.ts.map +1 -1
  67. package/dist/device/lifecycle-transaction.d.ts +2 -0
  68. package/dist/device/lifecycle-transaction.d.ts.map +1 -0
  69. package/dist/environment/frame.d.ts.map +1 -1
  70. package/dist/environment/lifecycle.d.ts +5 -0
  71. package/dist/environment/lifecycle.d.ts.map +1 -1
  72. package/dist/errors/gpu-driven.d.ts +26 -0
  73. package/dist/errors/gpu-driven.d.ts.map +1 -0
  74. package/dist/errors/index.d.ts +1 -0
  75. package/dist/errors/index.d.ts.map +1 -1
  76. package/dist/errors/recover.d.ts +9 -1
  77. package/dist/errors/recover.d.ts.map +1 -1
  78. package/dist/errors/render.d.ts +166 -93
  79. package/dist/errors/render.d.ts.map +1 -1
  80. package/dist/extract/directional-shadow-projection.d.ts +23 -0
  81. package/dist/extract/directional-shadow-projection.d.ts.map +1 -0
  82. package/dist/extract/environment.d.ts +14 -0
  83. package/dist/extract/environment.d.ts.map +1 -1
  84. package/dist/extract/gpu-driven.d.ts +50 -0
  85. package/dist/extract/gpu-driven.d.ts.map +1 -0
  86. package/dist/extract/visibility.d.ts.map +1 -1
  87. package/dist/features/fullscreen.d.ts.map +1 -1
  88. package/dist/features/targets.d.ts.map +1 -1
  89. package/dist/features/types.d.ts +10 -0
  90. package/dist/features/types.d.ts.map +1 -1
  91. package/dist/fullscreen-post-process-pass.d.ts +13 -2
  92. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  93. package/dist/gpu-driven/batch-topology.d.ts +34 -0
  94. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  95. package/dist/gpu-driven/prepared-draw.d.ts +63 -0
  96. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -0
  97. package/dist/gpu-driven/production-raster.d.ts +97 -2
  98. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  99. package/dist/gpu-driven/view-gpu.d.ts +64 -1
  100. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  101. package/dist/gpu-scene-schema.d.ts +14 -0
  102. package/dist/gpu-scene-schema.d.ts.map +1 -1
  103. package/dist/gpu-scene.d.ts +8 -1
  104. package/dist/gpu-scene.d.ts.map +1 -1
  105. package/dist/gpu-usage.d.ts +0 -1
  106. package/dist/gpu-usage.d.ts.map +1 -1
  107. package/dist/hdrp-buffers.d.ts +21 -19
  108. package/dist/hdrp-buffers.d.ts.map +1 -1
  109. package/dist/ibl/IblPipelineCache.d.ts +4 -0
  110. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  111. package/dist/ibl/kernel-cache.d.ts +36 -0
  112. package/dist/ibl/kernel-cache.d.ts.map +1 -0
  113. package/dist/index.d.ts +39 -3
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.mjs +91 -6
  116. package/dist/index.mjs.map +1 -1
  117. package/dist/inspection-types.d.ts +214 -1
  118. package/dist/inspection-types.d.ts.map +1 -1
  119. package/dist/instances-derived-bounds.d.ts +50 -0
  120. package/dist/instances-derived-bounds.d.ts.map +1 -0
  121. package/dist/internal.d.ts +9 -0
  122. package/dist/internal.d.ts.map +1 -1
  123. package/dist/internal.mjs +23 -2
  124. package/dist/internal.mjs.map +1 -1
  125. package/dist/light-buffer-layout.d.ts +58 -140
  126. package/dist/light-buffer-layout.d.ts.map +1 -1
  127. package/dist/materials.d.ts.map +1 -1
  128. package/dist/mesh-material-bindings.d.ts +54 -0
  129. package/dist/mesh-material-bindings.d.ts.map +1 -1
  130. package/dist/pbr-pipeline.d.ts +38 -14
  131. package/dist/pbr-pipeline.d.ts.map +1 -1
  132. package/dist/pipeline/standard-forward-lane.d.ts +2 -1
  133. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  134. package/dist/pipeline/standard-lighting/errors.d.ts +4 -0
  135. package/dist/pipeline/standard-lighting/errors.d.ts.map +1 -0
  136. package/dist/pipeline/standard-lighting/graph.d.ts +24 -0
  137. package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -0
  138. package/dist/pipeline/standard-lighting/inspection.d.ts +38 -0
  139. package/dist/pipeline/standard-lighting/inspection.d.ts.map +1 -0
  140. package/dist/pipeline/standard-lighting/layout.d.ts +28 -0
  141. package/dist/pipeline/standard-lighting/layout.d.ts.map +1 -0
  142. package/dist/pipeline/standard-lighting/light-frame.d.ts +33 -0
  143. package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -0
  144. package/dist/pipeline/standard-lighting/membership.d.ts +14 -0
  145. package/dist/pipeline/standard-lighting/membership.d.ts.map +1 -0
  146. package/dist/pipeline/standard-lighting/prepare.d.ts +16 -0
  147. package/dist/pipeline/standard-lighting/prepare.d.ts.map +1 -0
  148. package/dist/pipeline/standard-lighting/topology.d.ts +45 -0
  149. package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -0
  150. package/dist/pipeline/standard-lighting/transport.d.ts +36 -0
  151. package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -0
  152. package/dist/pipeline/standard-pipeline.d.ts +4 -13
  153. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  154. package/dist/pipeline/standard-post.d.ts.map +1 -1
  155. package/dist/pipeline/standard-profile.d.ts +5 -26
  156. package/dist/pipeline/standard-profile.d.ts.map +1 -1
  157. package/dist/pipeline-builder.d.ts +2 -0
  158. package/dist/pipeline-builder.d.ts.map +1 -1
  159. package/dist/pipeline-spec.d.ts +40 -7
  160. package/dist/pipeline-spec.d.ts.map +1 -1
  161. package/dist/plugin.d.ts.map +1 -1
  162. package/dist/point-shadow-inspection.d.ts +21 -0
  163. package/dist/point-shadow-inspection.d.ts.map +1 -0
  164. package/dist/prepare/extended-lighting/ltc-resources.d.ts +31 -0
  165. package/dist/prepare/extended-lighting/ltc-resources.d.ts.map +1 -0
  166. package/dist/prepare/extended-lighting/resources.d.ts +72 -0
  167. package/dist/prepare/extended-lighting/resources.d.ts.map +1 -0
  168. package/dist/prepare/extended-lighting/spot-modifiers.d.ts +42 -0
  169. package/dist/prepare/extended-lighting/spot-modifiers.d.ts.map +1 -0
  170. package/dist/prepare/extended-lighting/state.d.ts +24 -0
  171. package/dist/prepare/extended-lighting/state.d.ts.map +1 -0
  172. package/dist/record/frame-lighting.d.ts +33 -36
  173. package/dist/record/frame-lighting.d.ts.map +1 -1
  174. package/dist/record/frame-snapshot.d.ts +78 -24
  175. package/dist/record/frame-snapshot.d.ts.map +1 -1
  176. package/dist/record/frame-targets.d.ts +5 -0
  177. package/dist/record/frame-targets.d.ts.map +1 -1
  178. package/dist/record/frame.d.ts +16 -6
  179. package/dist/record/frame.d.ts.map +1 -1
  180. package/dist/record/gpu-pass-timing/contract.d.ts +96 -0
  181. package/dist/record/gpu-pass-timing/contract.d.ts.map +1 -0
  182. package/dist/record/gpu-pass-timing/errors.d.ts +18 -0
  183. package/dist/record/gpu-pass-timing/errors.d.ts.map +1 -0
  184. package/dist/record/gpu-pass-timing/index.d.ts +8 -0
  185. package/dist/record/gpu-pass-timing/index.d.ts.map +1 -0
  186. package/dist/record/gpu-pass-timing/parser.d.ts +11 -0
  187. package/dist/record/gpu-pass-timing/parser.d.ts.map +1 -0
  188. package/dist/record/gpu-pass-timing/session.d.ts +43 -0
  189. package/dist/record/gpu-pass-timing/session.d.ts.map +1 -0
  190. package/dist/record/gpu-timing.d.ts +20 -3
  191. package/dist/record/gpu-timing.d.ts.map +1 -1
  192. package/dist/record/helpers.d.ts +0 -20
  193. package/dist/record/helpers.d.ts.map +1 -1
  194. package/dist/record/main-pass-geometry.d.ts +6 -1
  195. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  196. package/dist/record/main-pass-material.d.ts +25 -2
  197. package/dist/record/main-pass-material.d.ts.map +1 -1
  198. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  199. package/dist/record/main-pass.d.ts +2 -0
  200. package/dist/record/main-pass.d.ts.map +1 -1
  201. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  202. package/dist/record/render-context.d.ts +98 -6
  203. package/dist/record/render-context.d.ts.map +1 -1
  204. package/dist/record/shadow-pass.d.ts.map +1 -1
  205. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  206. package/dist/record/typed-frame-graph.d.ts +119 -4
  207. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  208. package/dist/record/view-ubo.d.ts +7 -5
  209. package/dist/record/view-ubo.d.ts.map +1 -1
  210. package/dist/reflection/filter.d.ts +22 -0
  211. package/dist/reflection/filter.d.ts.map +1 -0
  212. package/dist/reflection/gpu-table.d.ts +25 -0
  213. package/dist/reflection/gpu-table.d.ts.map +1 -0
  214. package/dist/reflection/inspection.d.ts +10 -0
  215. package/dist/reflection/inspection.d.ts.map +1 -0
  216. package/dist/reflection/projection.d.ts +140 -0
  217. package/dist/reflection/projection.d.ts.map +1 -0
  218. package/dist/reflection/record-owner.d.ts +60 -0
  219. package/dist/reflection/record-owner.d.ts.map +1 -0
  220. package/dist/render-contract.d.ts +62 -14
  221. package/dist/render-contract.d.ts.map +1 -1
  222. package/dist/render-graph-primitives.d.ts +1 -0
  223. package/dist/render-graph-primitives.d.ts.map +1 -1
  224. package/dist/render-pipeline.d.ts +49 -2
  225. package/dist/render-pipeline.d.ts.map +1 -1
  226. package/dist/render-system-extract.d.ts +160 -31
  227. package/dist/render-system-extract.d.ts.map +1 -1
  228. package/dist/render-system.d.ts +28 -2
  229. package/dist/render-system.d.ts.map +1 -1
  230. package/dist/renderer.d.ts +43 -0
  231. package/dist/renderer.d.ts.map +1 -0
  232. package/dist/scene/probe-blend-record.d.ts +22 -0
  233. package/dist/scene/probe-blend-record.d.ts.map +1 -0
  234. package/dist/scene/probe-blend.d.ts +142 -0
  235. package/dist/scene/probe-blend.d.ts.map +1 -0
  236. package/dist/scene/render-scene-types.d.ts +0 -6
  237. package/dist/scene/render-scene-types.d.ts.map +1 -1
  238. package/dist/scene/render-scene.d.ts +117 -9
  239. package/dist/scene/render-scene.d.ts.map +1 -1
  240. package/dist/scene/visibility/bounds.d.ts +20 -0
  241. package/dist/scene/visibility/bounds.d.ts.map +1 -0
  242. package/dist/scene/visibility/budget.d.ts +16 -0
  243. package/dist/scene/visibility/budget.d.ts.map +1 -0
  244. package/dist/scene/visibility/facet.d.ts +73 -0
  245. package/dist/scene/visibility/facet.d.ts.map +1 -0
  246. package/dist/scene/visibility/gpu-frame-timing.d.ts +41 -0
  247. package/dist/scene/visibility/gpu-frame-timing.d.ts.map +1 -0
  248. package/dist/scene/visibility/gpu-lod.d.ts +46 -0
  249. package/dist/scene/visibility/gpu-lod.d.ts.map +1 -0
  250. package/dist/scene/visibility/inspection.d.ts +132 -0
  251. package/dist/scene/visibility/inspection.d.ts.map +1 -0
  252. package/dist/scene/visibility/lod-selector.d.ts +25 -0
  253. package/dist/scene/visibility/lod-selector.d.ts.map +1 -0
  254. package/dist/scene/visibility/occlusion-confidence.d.ts +81 -0
  255. package/dist/scene/visibility/occlusion-confidence.d.ts.map +1 -0
  256. package/dist/scene/visibility/occlusion-pass.d.ts +57 -0
  257. package/dist/scene/visibility/occlusion-pass.d.ts.map +1 -0
  258. package/dist/scene/visibility/occlusion-query-pool.d.ts +57 -0
  259. package/dist/scene/visibility/occlusion-query-pool.d.ts.map +1 -0
  260. package/dist/scene/visibility/occlusion-runtime.d.ts +96 -0
  261. package/dist/scene/visibility/occlusion-runtime.d.ts.map +1 -0
  262. package/dist/scene/visibility/types.d.ts +34 -0
  263. package/dist/scene/visibility/types.d.ts.map +1 -0
  264. package/dist/shadow-atlas.d.ts +3 -3
  265. package/dist/shadow-atlas.d.ts.map +1 -1
  266. package/dist/ssao-buffers.d.ts +3 -0
  267. package/dist/ssao-buffers.d.ts.map +1 -1
  268. package/dist/ssr/admission.d.ts +122 -0
  269. package/dist/ssr/admission.d.ts.map +1 -0
  270. package/dist/ssr/composition.d.ts +32 -0
  271. package/dist/ssr/composition.d.ts.map +1 -0
  272. package/dist/ssr/errors.d.ts +39 -0
  273. package/dist/ssr/errors.d.ts.map +1 -0
  274. package/dist/ssr/identity.d.ts +13 -0
  275. package/dist/ssr/identity.d.ts.map +1 -0
  276. package/dist/systems/skin-palette-allocator.d.ts +1 -1
  277. package/dist/targets/contracts.d.ts +66 -0
  278. package/dist/targets/contracts.d.ts.map +1 -0
  279. package/dist/targets/graph-projection.d.ts +29 -0
  280. package/dist/targets/graph-projection.d.ts.map +1 -0
  281. package/dist/targets/material-source.d.ts +34 -0
  282. package/dist/targets/material-source.d.ts.map +1 -0
  283. package/dist/targets/owner.d.ts +32 -0
  284. package/dist/targets/owner.d.ts.map +1 -0
  285. package/dist/targets/physical.d.ts +18 -0
  286. package/dist/targets/physical.d.ts.map +1 -0
  287. package/dist/targets/readback.d.ts +39 -0
  288. package/dist/targets/readback.d.ts.map +1 -0
  289. package/dist/temporal/frame-coordinator.d.ts +13 -0
  290. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  291. package/dist/temporal/frame.d.ts +2 -0
  292. package/dist/temporal/frame.d.ts.map +1 -1
  293. package/dist/temporal/index.mjs +6 -5
  294. package/dist/temporal/view.d.ts +7 -0
  295. package/dist/temporal/view.d.ts.map +1 -1
  296. package/dist/tilemap-chunk-extract-system.d.ts +1 -1
  297. package/dist/typed-render-graph-primitives.d.ts +5 -1
  298. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  299. package/dist/typed-shadow-passes.d.ts +1 -1
  300. package/dist/typed-shadow-passes.d.ts.map +1 -1
  301. package/dist/volume/passes.d.ts +9 -5
  302. package/dist/volume/passes.d.ts.map +1 -1
  303. package/package.json +20 -20
  304. package/src/__tests__/bloom-exact-zero.unit.test.ts +48 -19
  305. package/src/__tests__/bloom-lifecycle.integration.test.ts +41 -13
  306. package/src/__tests__/camera-target-consumption.unit.test.ts +53 -0
  307. package/src/__tests__/cluster-binner.unit.test.ts +35 -0
  308. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +12 -2
  309. package/src/__tests__/cube-camera-scheduler.unit.test.ts +70 -0
  310. package/src/__tests__/cube-camera-single-submit.perf.test.ts +75 -0
  311. package/src/__tests__/cube-camera-view-matrices.unit.test.ts +41 -0
  312. package/src/__tests__/cube-camera-vocabulary.unit.test.ts +47 -0
  313. package/src/__tests__/cube-camera.integration.test.ts +42 -0
  314. package/src/__tests__/detached-inspection.integration.test.ts +6 -0
  315. package/src/__tests__/device-feature-admission.unit.test.ts +85 -0
  316. package/src/__tests__/direct-light-snapshot.unit.test.ts +62 -6
  317. package/src/__tests__/docs-gate-green.unit.test.ts +38 -0
  318. package/src/__tests__/environment-generation.integration.test.ts +16 -0
  319. package/src/__tests__/evidence-identity.ts +13 -0
  320. package/src/__tests__/evidence-identity.unit.test.ts +24 -0
  321. package/src/__tests__/extended-lighting-recovery.browser.test.ts +14 -0
  322. package/src/__tests__/extended-lighting-recovery.dawn.test.ts +14 -0
  323. package/src/__tests__/extended-lighting-recovery.integration.test.ts +81 -0
  324. package/src/__tests__/extended-lighting-resources.unit.test.ts +51 -0
  325. package/src/__tests__/extended-lighting-zero-feature.integration.test.ts +52 -0
  326. package/src/__tests__/factory-contract.integration.test.ts +288 -29
  327. package/src/__tests__/fog-component.unit.test.ts +67 -0
  328. package/src/__tests__/fog.integration.test.ts +86 -0
  329. package/src/__tests__/frame-observation.contract.test.ts +15 -0
  330. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +37 -0
  331. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +146 -0
  332. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +10 -8
  333. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +18 -1
  334. package/src/__tests__/gpu-driven-production.integration.test.ts +579 -17
  335. package/src/__tests__/gpu-driven-scaling.unit.test.ts +66 -2
  336. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +26 -1
  337. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +101 -6
  338. package/src/__tests__/gpu-driven-view.browser.test.ts +3 -3
  339. package/src/__tests__/gpu-driven-view.dawn.test.ts +3 -3
  340. package/src/__tests__/gpu-driven-view.unit.test.ts +50 -0
  341. package/src/__tests__/gpu-lod-evidence.ts +188 -0
  342. package/src/__tests__/gpu-lod-fallback.integration.test.ts +10 -0
  343. package/src/__tests__/gpu-lod-indirect.integration.test.ts +40 -0
  344. package/src/__tests__/gpu-lod-parity.dawn.test.ts +29 -0
  345. package/src/__tests__/gpu-lod-readiness.integration.test.ts +52 -0
  346. package/src/__tests__/gpu-lod-schema.unit.test.ts +66 -0
  347. package/src/__tests__/gpu-lod-selector.unit.test.ts +28 -0
  348. package/src/__tests__/gpu-lod-silhouette.browser.test.ts +13 -0
  349. package/src/__tests__/gpu-pass-timing-browser-runner.ts +281 -0
  350. package/src/__tests__/gpu-pass-timing-coexistence.integration.test.ts +80 -0
  351. package/src/__tests__/gpu-pass-timing-contract.unit.test.ts +243 -0
  352. package/src/__tests__/gpu-pass-timing-exact-zero.unit.test.ts +131 -0
  353. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +230 -0
  354. package/src/__tests__/gpu-pass-timing-observation.unit.test.ts +233 -0
  355. package/src/__tests__/gpu-pass-timing-session.unit.test.ts +438 -0
  356. package/src/__tests__/gpu-pass-timing.browser.test.ts +26 -0
  357. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +125 -0
  358. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +66 -1
  359. package/src/__tests__/gpu-scene.dawn.test.ts +8 -1
  360. package/src/__tests__/gpu-scene.unit.test.ts +49 -3
  361. package/src/__tests__/hdrp-group2-visibility.unit.test.ts +24 -0
  362. package/src/__tests__/instances-culling.integration.test.ts +94 -0
  363. package/src/__tests__/instances-derived-bounds.unit.test.ts +118 -0
  364. package/src/__tests__/light-buffer-layout.unit.test.ts +113 -3
  365. package/src/__tests__/light-inspection.unit.test.ts +55 -0
  366. package/src/__tests__/light-resource-error.unit.test.ts +43 -0
  367. package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +109 -0
  368. package/src/__tests__/lod-bounds.unit.test.ts +42 -0
  369. package/src/__tests__/lod-cpu-lane.integration.test.ts +55 -0
  370. package/src/__tests__/lod-cpu-no-change.perf.test.ts +19 -0
  371. package/src/__tests__/lod-gpu-frame-timing.unit.test.ts +49 -0
  372. package/src/__tests__/lod-multi-view.integration.test.ts +63 -0
  373. package/src/__tests__/lod-occlusion-device-loss.integration.test.ts +27 -0
  374. package/src/__tests__/lod-occlusion-final-decision.integration.test.ts +47 -0
  375. package/src/__tests__/lod-occlusion-inspection.integration.test.ts +217 -0
  376. package/src/__tests__/lod-occlusion-inspection.schema.test-d.ts +14 -0
  377. package/src/__tests__/lod-occlusion-inspection.unit.test.ts +72 -0
  378. package/src/__tests__/lod-occlusion-visual-evidence.ts +91 -0
  379. package/src/__tests__/lod-occlusion.browser.test.ts +15 -0
  380. package/src/__tests__/lod-occlusion.dawn.test.ts +20 -0
  381. package/src/__tests__/lod-selector.profile.test.ts +35 -0
  382. package/src/__tests__/lod-selector.unit.test.ts +68 -0
  383. package/src/__tests__/lod-visibility-identity.unit.test.ts +198 -0
  384. package/src/__tests__/lod-visibility-lifecycle.integration.test.ts +132 -0
  385. package/src/__tests__/ltc-resources.unit.test.ts +32 -0
  386. package/src/__tests__/material-contract-inventory.unit.test.ts +17 -0
  387. package/src/__tests__/material-observation.integration.test.ts +101 -0
  388. package/src/__tests__/occlusion-confidence.unit.test.ts +168 -0
  389. package/src/__tests__/occlusion-invalidation.integration.test.ts +37 -0
  390. package/src/__tests__/occlusion-pass-routing.unit.test.ts +16 -0
  391. package/src/__tests__/occlusion-pass.dawn.test.ts +20 -0
  392. package/src/__tests__/occlusion-pass.integration.test.ts +43 -0
  393. package/src/__tests__/occlusion-pass.rhi-null.test.ts +22 -0
  394. package/src/__tests__/occlusion-query-pool.rhi-null.test.ts +21 -0
  395. package/src/__tests__/occlusion-query-pool.unit.test.ts +95 -0
  396. package/src/__tests__/occlusion-query-ticket.integration.test.ts +47 -0
  397. package/src/__tests__/occlusion-query.dawn.test.ts +16 -0
  398. package/src/__tests__/occlusion-query.evidence.ts +178 -0
  399. package/src/__tests__/occlusion-query.rhi-null.test.ts +31 -0
  400. package/src/__tests__/occlusion-runtime.unit.test.ts +120 -0
  401. package/src/__tests__/pbr-view-layout.unit.test.ts +16 -0
  402. package/src/__tests__/point-shadow-inspection.unit.test.ts +60 -0
  403. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +196 -0
  404. package/src/__tests__/probe-bindings.unit.test.ts +200 -0
  405. package/src/__tests__/probe-blend-numeric.unit.test.ts +67 -0
  406. package/src/__tests__/probe-blend-oracle.unit.test.ts +136 -0
  407. package/src/__tests__/probe-capacity.integration.test.ts +97 -0
  408. package/src/__tests__/probe-recovery.integration.test.ts +104 -0
  409. package/src/__tests__/probe-scene-dirty.integration.test.ts +211 -0
  410. package/src/__tests__/profiler-phase-catalog.test.ts +4 -1
  411. package/src/__tests__/profiler-phase-token.test.ts +1 -0
  412. package/src/__tests__/public-surface.test-d.ts +10 -0
  413. package/src/__tests__/rect-area-boundary.unit.test.ts +24 -0
  414. package/src/__tests__/reflection-fallback-graph.unit.test.ts +67 -0
  415. package/src/__tests__/reflection-fallback-inspection.unit.test.ts +77 -0
  416. package/src/__tests__/reflection-fallback-lifecycle.integration.test.ts +21 -0
  417. package/src/__tests__/reflection-fallback-projection.unit.test.ts +90 -0
  418. package/src/__tests__/reflection-fallback-source.unit.test.ts +32 -0
  419. package/src/__tests__/reflection-probe-authoring.unit.test.ts +71 -0
  420. package/src/__tests__/reflection-probe-filter.unit.test.ts +54 -0
  421. package/src/__tests__/reflection-probe-ibl-topology.unit.test.ts +45 -0
  422. package/src/__tests__/reflection-probe-owner-placement.unit.test.ts +39 -0
  423. package/src/__tests__/reflection-probe-pass-encode.integration.test.ts +101 -0
  424. package/src/__tests__/reflection-probe-performance.perf.test.ts +60 -0
  425. package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +120 -0
  426. package/src/__tests__/reflection-probe-recovery.integration.test.ts +55 -0
  427. package/src/__tests__/reflection-probe-selection.unit.test.ts +46 -0
  428. package/src/__tests__/render-error-code-owner.test-d.ts +19 -3
  429. package/src/__tests__/render-feature-docs.unit.test.ts +12 -7
  430. package/src/__tests__/render-graph-fallback.integration.test.ts +24 -0
  431. package/src/__tests__/render-perf/extended-lighting.bench.test.ts +66 -0
  432. package/src/__tests__/render-scene-projection.unit.test.ts +447 -18
  433. package/src/__tests__/render-target-admission.unit.test.ts +80 -0
  434. package/src/__tests__/render-target-graph-projection.unit.test.ts +102 -0
  435. package/src/__tests__/render-target-inspection.unit.test.ts +52 -0
  436. package/src/__tests__/render-target-material-source.test-d.ts +30 -0
  437. package/src/__tests__/render-target-material-source.unit.test.ts +97 -0
  438. package/src/__tests__/render-target-owner-placement.unit.test.ts +203 -0
  439. package/src/__tests__/render-target-owner.unit.test.ts +122 -0
  440. package/src/__tests__/render-target-probe-soak.perf.test.ts +94 -0
  441. package/src/__tests__/render-target-public-schema.json +107 -0
  442. package/src/__tests__/render-target-public-schema.unit.test.ts +91 -0
  443. package/src/__tests__/render-target-public-surface.test-d.ts +83 -0
  444. package/src/__tests__/render-target-readback.integration.test.ts +90 -0
  445. package/src/__tests__/render-target-recovery.integration.test.ts +85 -0
  446. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +124 -0
  447. package/src/__tests__/renderer-lifecycle.integration.test.ts +14 -0
  448. package/src/__tests__/shader-stage-surface.unit.test.ts +19 -4
  449. package/src/__tests__/skin-motion-regression.unit.test.ts +154 -0
  450. package/src/__tests__/skinned-shadow-caster.test.ts +13 -4
  451. package/src/__tests__/spot-modifier-projection.unit.test.ts +70 -0
  452. package/src/__tests__/ssr-admission-matrix.integration.test.ts +206 -0
  453. package/src/__tests__/ssr-composition.unit.test.ts +46 -0
  454. package/src/__tests__/ssr-fallback-zero-work.bench.test.ts +94 -0
  455. package/src/__tests__/ssr-recovery-errors.unit.test.ts +58 -0
  456. package/src/__tests__/standard-cluster-capability.integration.test.ts +63 -0
  457. package/src/__tests__/standard-cluster-transport.unit.test.ts +121 -0
  458. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +299 -0
  459. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +15 -5
  460. package/src/__tests__/standard-lighting-inspection.integration.test.ts +138 -0
  461. package/src/__tests__/standard-lighting-ssot.unit.test.ts +18 -0
  462. package/src/__tests__/standard-lighting-write.unit.test.ts +441 -0
  463. package/src/__tests__/standard-lighting.integration.test.ts +67 -0
  464. package/src/__tests__/standard-pipeline.dawn.test.ts +206 -5
  465. package/src/__tests__/standard-pipeline.integration.test.ts +181 -150
  466. package/src/__tests__/standard-probe-binding.unit.test.ts +25 -0
  467. package/src/__tests__/standard-profile-oracle.unit.test.ts +23 -38
  468. package/src/__tests__/temporal-fallback-generation.integration.test.ts +16 -0
  469. package/src/__tests__/temporal-view.unit.test.ts +3 -3
  470. package/src/__tests__/transmission-prewarm.unit.test.ts +69 -0
  471. package/src/__tests__/typed-pipeline-topology.unit.test.ts +395 -14
  472. package/src/__tests__/urp-pipeline-targets.unit.test.ts +2 -2
  473. package/src/__tests__/visibility-budget.integration.test.ts +140 -0
  474. package/src/__tests__/visibility-budget.unit.test.ts +70 -0
  475. package/src/__tests__/visibility-extract.integration.test.ts +11 -6
  476. package/src/__tests__/visibility-resolver.unit.test.ts +15 -0
  477. package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +6 -3
  478. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +31 -28
  479. package/src/assembly/backend-contract.ts +13 -0
  480. package/src/assembly/device-feature-admission.ts +80 -0
  481. package/src/assembly/directional-shadow-inspection.ts +122 -0
  482. package/src/assembly/factory.ts +1411 -366
  483. package/src/assembly/host-contract.ts +35 -0
  484. package/src/assembly/render-target-host.ts +847 -0
  485. package/src/assets/asset-decoders.ts +1 -1
  486. package/src/assets/render-asset-port.ts +7 -1
  487. package/src/capture/cube-views.ts +69 -0
  488. package/src/capture/scheduler.ts +265 -0
  489. package/src/cluster-binner.ts +31 -0
  490. package/src/components/__tests__/lighting-authoring.unit.test.ts +39 -0
  491. package/src/components/__tests__/rect-area-light.unit.test.ts +33 -0
  492. package/src/components/__tests__/schema-contract.unit.test.ts +12 -0
  493. package/src/components/__tests__/schema-parity.unit.test.ts +9 -0
  494. package/src/components/camera.ts +4 -0
  495. package/src/components/cube-camera.ts +52 -0
  496. package/src/components/directional-light.ts +22 -2
  497. package/src/components/directional-shadow-filter.ts +57 -0
  498. package/src/components/index.ts +5 -0
  499. package/src/components/light-helpers.ts +133 -8
  500. package/src/components/light-probe.ts +13 -0
  501. package/src/components/rect-area-light.ts +15 -0
  502. package/src/components/reflection-probe.ts +49 -0
  503. package/src/components/scene-instance.ts +6 -0
  504. package/src/components/spot-light.ts +3 -0
  505. package/src/device/__tests__/extended-lighting-recovery.unit.test.ts +24 -0
  506. package/src/device/gpu-residency.ts +140 -9
  507. package/src/device/lifecycle-transaction.ts +12 -0
  508. package/src/environment/frame.ts +6 -24
  509. package/src/environment/lifecycle.ts +20 -1
  510. package/src/errors/gpu-driven.ts +92 -0
  511. package/src/errors/index.ts +1 -0
  512. package/src/errors/recover.ts +32 -0
  513. package/src/errors/render.ts +341 -143
  514. package/src/extract/camera.ts +3 -3
  515. package/src/extract/directional-shadow-projection.ts +61 -0
  516. package/src/extract/environment.ts +57 -0
  517. package/src/extract/gpu-driven.ts +108 -0
  518. package/src/extract/visibility.ts +2 -1
  519. package/src/features/fullscreen.ts +3 -0
  520. package/src/features/targets.ts +4 -1
  521. package/src/features/types.ts +10 -0
  522. package/src/fullscreen-post-process-pass.ts +43 -2
  523. package/src/gpu-driven/batch-topology.ts +207 -28
  524. package/src/gpu-driven/prepared-draw.ts +208 -0
  525. package/src/gpu-driven/production-raster.ts +953 -43
  526. package/src/gpu-driven/view-gpu.ts +411 -29
  527. package/src/gpu-scene-schema.ts +13 -0
  528. package/src/gpu-scene.ts +140 -64
  529. package/src/gpu-usage.ts +0 -1
  530. package/src/hdrp-buffers.ts +169 -63
  531. package/src/ibl/IblPipelineCache.ts +11 -0
  532. package/src/ibl/kernel-cache.ts +88 -0
  533. package/src/index.ts +222 -0
  534. package/src/inspection-types.ts +276 -1
  535. package/src/instances-derived-bounds.ts +171 -0
  536. package/src/internal.ts +38 -0
  537. package/src/light-buffer-layout.ts +155 -310
  538. package/src/materials.ts +16 -2
  539. package/src/mesh-material-bindings.ts +132 -0
  540. package/src/pbr-pipeline.ts +255 -60
  541. package/src/pipeline/standard-forward-lane.ts +93 -16
  542. package/src/pipeline/standard-lighting/errors.ts +6 -0
  543. package/src/pipeline/standard-lighting/graph.ts +158 -0
  544. package/src/pipeline/standard-lighting/inspection.ts +88 -0
  545. package/src/pipeline/standard-lighting/layout.ts +31 -0
  546. package/src/pipeline/standard-lighting/light-frame.ts +35 -0
  547. package/src/pipeline/standard-lighting/membership.ts +43 -0
  548. package/src/pipeline/standard-lighting/prepare.ts +37 -0
  549. package/src/pipeline/standard-lighting/topology.ts +120 -0
  550. package/src/pipeline/standard-lighting/transport.ts +78 -0
  551. package/src/pipeline/standard-pipeline.ts +124 -193
  552. package/src/pipeline/standard-post.ts +26 -7
  553. package/src/pipeline/standard-profile.ts +17 -47
  554. package/src/pipeline-builder.ts +5 -2
  555. package/src/pipeline-spec.ts +95 -21
  556. package/src/plugin.ts +4 -0
  557. package/src/point-shadow-inspection.ts +55 -0
  558. package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +56 -0
  559. package/src/prepare/extended-lighting/ltc-resources.ts +136 -0
  560. package/src/prepare/extended-lighting/resources.ts +389 -0
  561. package/src/prepare/extended-lighting/spot-modifiers.ts +194 -0
  562. package/src/prepare/extended-lighting/state.ts +92 -0
  563. package/src/record/__tests__/gpu-timing.unit.test.ts +177 -0
  564. package/src/record/__tests__/temporal-payload.unit.test.ts +64 -1
  565. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +73 -0
  566. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +20 -1
  567. package/src/record/__tests__/view-ubo-layout.unit.test.ts +40 -0
  568. package/src/record/frame-lighting.ts +591 -452
  569. package/src/record/frame-snapshot.ts +92 -39
  570. package/src/record/frame-targets.ts +17 -0
  571. package/src/record/frame.ts +662 -158
  572. package/src/record/gpu-pass-timing/contract.ts +180 -0
  573. package/src/record/gpu-pass-timing/errors.ts +34 -0
  574. package/src/record/gpu-pass-timing/index.ts +37 -0
  575. package/src/record/gpu-pass-timing/parser.ts +97 -0
  576. package/src/record/gpu-pass-timing/session.ts +938 -0
  577. package/src/record/gpu-timing.ts +112 -39
  578. package/src/record/helpers.ts +0 -58
  579. package/src/record/main-pass-geometry.ts +324 -119
  580. package/src/record/main-pass-material.ts +103 -10
  581. package/src/record/main-pass-sprite-draws.ts +99 -103
  582. package/src/record/main-pass.ts +71 -15
  583. package/src/record/mesh-ssbo.ts +47 -36
  584. package/src/record/render-context.ts +136 -3
  585. package/src/record/shadow-pass.ts +83 -68
  586. package/src/record/skybox-post-pass.ts +20 -4
  587. package/src/record/typed-frame-graph.ts +1034 -43
  588. package/src/record/view-ubo.ts +72 -47
  589. package/src/reflection/filter.ts +81 -0
  590. package/src/reflection/gpu-table.ts +44 -0
  591. package/src/reflection/inspection.ts +67 -0
  592. package/src/reflection/projection.ts +476 -0
  593. package/src/reflection/record-owner.ts +1283 -0
  594. package/src/render-contract.ts +93 -10
  595. package/src/render-graph-primitives.ts +9 -4
  596. package/src/render-pipeline.ts +122 -2
  597. package/src/render-system-extract.ts +1041 -225
  598. package/src/render-system.ts +1310 -85
  599. package/src/renderer.ts +222 -0
  600. package/src/scene/__tests__/changed-block-summary.candidate.test.ts +36 -0
  601. package/src/scene/probe-blend-record.ts +54 -0
  602. package/src/scene/probe-blend.ts +813 -0
  603. package/src/scene/render-scene-types.ts +0 -7
  604. package/src/scene/render-scene.ts +942 -171
  605. package/src/scene/visibility/bounds.ts +57 -0
  606. package/src/scene/visibility/budget.ts +40 -0
  607. package/src/scene/visibility/facet.ts +400 -0
  608. package/src/scene/visibility/gpu-frame-timing.ts +101 -0
  609. package/src/scene/visibility/gpu-lod.ts +119 -0
  610. package/src/scene/visibility/inspection.ts +313 -0
  611. package/src/scene/visibility/lod-selector.ts +84 -0
  612. package/src/scene/visibility/occlusion-confidence.ts +478 -0
  613. package/src/scene/visibility/occlusion-pass.ts +165 -0
  614. package/src/scene/visibility/occlusion-query-pool.ts +178 -0
  615. package/src/scene/visibility/occlusion-runtime.ts +672 -0
  616. package/src/scene/visibility/types.ts +50 -0
  617. package/src/shadow-atlas.ts +5 -5
  618. package/src/ssao-buffers.ts +9 -2
  619. package/src/ssr/admission.ts +451 -0
  620. package/src/ssr/composition.ts +73 -0
  621. package/src/ssr/errors.ts +76 -0
  622. package/src/ssr/identity.ts +12 -0
  623. package/src/systems/skin-palette-allocator.ts +1 -1
  624. package/src/targets/contracts.ts +182 -0
  625. package/src/targets/graph-projection.ts +169 -0
  626. package/src/targets/material-source.ts +143 -0
  627. package/src/targets/owner.ts +214 -0
  628. package/src/targets/physical.ts +18 -0
  629. package/src/targets/readback.ts +153 -0
  630. package/src/temporal/frame-coordinator.ts +26 -0
  631. package/src/temporal/frame.ts +14 -1
  632. package/src/temporal/view.ts +11 -0
  633. package/src/tilemap-chunk-extract-system.ts +4 -4
  634. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +59 -20
  635. package/src/typed-render-graph-primitives.ts +53 -3
  636. package/src/typed-shadow-passes.ts +46 -40
  637. package/src/volume/passes.ts +61 -58
  638. package/dist/.tsbuildinfo +0 -1
  639. package/dist/chunk-4XINDLAY.mjs.map +0 -1
  640. package/dist/chunk-CDTWTHZR.mjs.map +0 -1
  641. package/dist/chunk-M2MP4ZOU.mjs +0 -73
  642. package/dist/chunk-M2MP4ZOU.mjs.map +0 -1
  643. package/dist/chunk-PCHBPYML.mjs.map +0 -1
  644. package/dist/chunk-TBQJOK2L.mjs.map +0 -1
  645. package/dist/chunk-VFPLTKCU.mjs.map +0 -1
@@ -0,0 +1,50 @@
1
+ /** Closed view roles used by the renderer-owned visibility facet. */
2
+ export type VisibilityViewRole = 'main' | 'shadow' | 'reflection' | 'probe';
3
+
4
+ export interface ViewKey {
5
+ readonly attachmentId: string;
6
+ readonly cameraEntity: number;
7
+ readonly viewRole: VisibilityViewRole;
8
+ readonly viewGeneration: number;
9
+ }
10
+
11
+ export interface PrimitiveKey {
12
+ readonly attachmentId: string;
13
+ readonly worldGeneration: number;
14
+ readonly primitiveSlot: number;
15
+ readonly slotGeneration: number;
16
+ }
17
+
18
+ export interface VisibilityCandidate {
19
+ readonly level: number;
20
+ readonly confidence: number;
21
+ }
22
+
23
+ export interface VisibilityFacetInspection {
24
+ readonly owner: 'persistent-render-scene';
25
+ readonly activeViews: number;
26
+ readonly facetRows: number;
27
+ }
28
+
29
+ export interface VisibilityFacetInput {
30
+ readonly view: ViewKey;
31
+ readonly primitive: PrimitiveKey;
32
+ readonly epoch: number;
33
+ readonly candidate: VisibilityCandidate;
34
+ }
35
+
36
+ export function viewKeyId(key: ViewKey): string {
37
+ return `${key.attachmentId}:${key.cameraEntity}:${key.viewRole}:${key.viewGeneration}`;
38
+ }
39
+
40
+ export function primitiveKeyId(key: PrimitiveKey): string {
41
+ return `${key.attachmentId}:${key.worldGeneration}:${key.primitiveSlot}:${key.slotGeneration}`;
42
+ }
43
+
44
+ export function viewKey(input: ViewKey): ViewKey {
45
+ return Object.freeze({ ...input });
46
+ }
47
+
48
+ export function primitiveKey(input: PrimitiveKey): PrimitiveKey {
49
+ return Object.freeze({ ...input });
50
+ }
@@ -3,8 +3,8 @@
3
3
  // feat-20260612-point-light-shadows-urp-hdrp M3 / T-M3-1 (plan-strategy §D-1).
4
4
  //
5
5
  // Lazy-allocated `texture_depth_cube_array` (depth32float) + per-face 2D
6
- // TextureView cache. One layer per shadow-casting PointLight (cap = 4 enforced
7
- // by the PointLightShadow ECS component cardinality). Total texel footprint at
6
+ // TextureView cache. One layer per admitted shadow-casting PointLight; the
7
+ // renderer owns the bounded atlas admission policy. Total texel footprint at
8
8
  // the 512x512 default = 4 layers x 6 faces x 512 x 512 x 4 B = 24 MiB.
9
9
  //
10
10
  // Why a separate module (vs inlining into createRenderer / pipelineState):
@@ -53,19 +53,19 @@ import { GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING } from './gpu-t
53
53
  export interface ShadowAtlasOptions {
54
54
  /** Per-face square size in pixels. Default 512 (PointLightShadow.mapSize default). */
55
55
  readonly faceSize?: number;
56
- /** Number of cube layers. Default 4 (= PointLightShadow cardinality cap). */
56
+ /** Number of cube layers. Default is the renderer atlas admission capacity. */
57
57
  readonly layers?: number;
58
58
  }
59
59
 
60
60
  /** Default per-face square size in pixels. Mirrors PointLightShadow.mapSize default. */
61
61
  export const SHADOW_ATLAS_DEFAULT_FACE_SIZE = 512;
62
- /** Default cube layer count. Mirrors PointLightShadow cardinality cap. */
62
+ /** Default cube layer count. Renderer-owned point-shadow admission capacity. */
63
63
  export const SHADOW_ATLAS_DEFAULT_LAYERS = 4;
64
64
 
65
65
  /**
66
66
  * Cube-array depth atlas for point-light shadows.
67
67
  *
68
- * Shape: `texture_depth_cube_array` (depth32float) with `layers = 4` and per-
68
+ * Shape: `texture_depth_cube_array` (depth32float) with `layers` and per-
69
69
  * face size `faceSize x faceSize`. The cube_array sampling view feeds
70
70
  * `@group(0) @binding(5)` in URP shaders; the per-face 2D view feeds the
71
71
  * shadow-caster render pass attachment for one (layer, face) at a time
@@ -106,6 +106,7 @@ export const SSAO_UNIFORM_BYTES = UNIFORM_BYTES;
106
106
  // recompile across enable/disable.
107
107
 
108
108
  interface SsaoFallbackResources {
109
+ readonly device: RenderSystemRuntime['device'];
109
110
  readonly texture: Texture;
110
111
  readonly view: TextureView;
111
112
  readonly sampler: Sampler;
@@ -126,7 +127,8 @@ export function getOrCreateSsaoFallbackTexture(
126
127
  runtime: RenderSystemRuntime,
127
128
  ): SsaoFallbackResources | null {
128
129
  const cached = fallbackCache.get(runtime);
129
- if (cached !== undefined) return cached;
130
+ if (cached !== undefined && cached.device === runtime.device) return cached;
131
+ if (cached !== undefined) fallbackCache.delete(runtime);
130
132
 
131
133
  const device = runtime.device;
132
134
  const texRes = device.createTexture({
@@ -191,6 +193,7 @@ export function getOrCreateSsaoFallbackTexture(
191
193
  }
192
194
 
193
195
  const resources: SsaoFallbackResources = {
196
+ device,
194
197
  texture: texRes.value,
195
198
  view: viewRes.value,
196
199
  sampler: samplerRes.value,
@@ -205,6 +208,8 @@ export function getOrCreateSsaoFallbackTexture(
205
208
  * Fields are readonly per charter P5: one resource, one owner.
206
209
  */
207
210
  export interface SsaoBuffers {
211
+ /** Device identity that owns every handle in this bundle. */
212
+ readonly device: RenderSystemRuntime['device'];
208
213
  /** 64 padded vec4 uniform buffer for hemisphere samples. */
209
214
  readonly kernelBuffer: Buffer;
210
215
  readonly kernelBytes: number;
@@ -229,7 +234,8 @@ const cache = new WeakMap<RenderSystemRuntime, SsaoBuffers>();
229
234
  */
230
235
  export function getOrCreateSsaoBuffers(runtime: RenderSystemRuntime): SsaoBuffers | null {
231
236
  const cached = cache.get(runtime);
232
- if (cached !== undefined) return cached;
237
+ if (cached !== undefined && cached.device === runtime.device) return cached;
238
+ if (cached !== undefined) cache.delete(runtime);
233
239
 
234
240
  const device = runtime.device;
235
241
 
@@ -322,6 +328,7 @@ export function getOrCreateSsaoBuffers(runtime: RenderSystemRuntime): SsaoBuffer
322
328
  }
323
329
 
324
330
  const buffers: SsaoBuffers = {
331
+ device,
325
332
  kernelBuffer: kernelBufferRes.value,
326
333
  kernelBytes: SSAO_KERNEL_SAMPLE_COUNT * BYTES_PER_KERNEL_ELEMENT,
327
334
  noiseTexture: noiseTexRes.value,
@@ -0,0 +1,451 @@
1
+ /**
2
+ * Consumer-only SSR M0 admission.
3
+ *
4
+ * This module consumes detached producer receipts. It owns no capture,
5
+ * device, graph, or recovery handle and therefore cannot repair a missing
6
+ * producer. A complete receipt set is the only route to an admitted result.
7
+ */
8
+
9
+ import {
10
+ type RhiTextureFormatCapabilityReceipt,
11
+ validateR32FloatReceipt,
12
+ } from '@forgeax/engine-rhi';
13
+ import type { ReflectionFallbackReceipt } from '../inspection-types';
14
+ import type { SsrOwnerRecoveryAction } from './errors';
15
+ import type { SsrAdmissionIdentity } from './identity';
16
+
17
+ export const SSR_FORMAT_PROFILE = 'r32float-mip-sampled-storage' as const;
18
+
19
+ export const SSR_FORMAT_STAGES = Object.freeze([
20
+ 'texture-create',
21
+ 'mip-view',
22
+ 'sampled-storage-bind-group',
23
+ 'pipeline-bind',
24
+ 'finish',
25
+ 'submit',
26
+ 'completion',
27
+ 'readback',
28
+ ] as const);
29
+
30
+ export type { SsrAdmissionIdentity } from './identity';
31
+
32
+ export type SsrFormatStage = (typeof SSR_FORMAT_STAGES)[number];
33
+
34
+ /**
35
+ * The admission input is the real renderer owner receipt plus the one
36
+ * integration identity supplied by the host. It deliberately does not
37
+ * recreate submitted/source/LKG boolean flags.
38
+ */
39
+ export type SsrReflectionFallbackReceipt = ReflectionFallbackReceipt & {
40
+ readonly identity: SsrAdmissionIdentity;
41
+ };
42
+
43
+ /** RHI capability receipt with the same host-supplied integration identity. */
44
+ export type SsrFormatReceipt = RhiTextureFormatCapabilityReceipt & {
45
+ readonly identity: SsrAdmissionIdentity;
46
+ };
47
+
48
+ export interface SsrTemporalReceipt {
49
+ readonly identity: SsrAdmissionIdentity;
50
+ readonly successfulSubmit: true;
51
+ readonly generation: number;
52
+ }
53
+
54
+ export interface SsrAdmissionWork {
55
+ readonly attachmentCount: number;
56
+ readonly passCount: number;
57
+ readonly bindingCount: number;
58
+ readonly resourceCount: number;
59
+ readonly historyCount: 0;
60
+ readonly temporalDemand: 0;
61
+ }
62
+
63
+ export interface SsrAdmissionBudget {
64
+ readonly probeExecutions: 0 | 1;
65
+ readonly resetCount: 0;
66
+ readonly rebuildCount: 0;
67
+ }
68
+
69
+ export interface SsrAdmissionFailure {
70
+ readonly code:
71
+ | 'ssr-not-requested'
72
+ | 'ssr-reflection-fallback-unavailable'
73
+ | 'ssr-format-unavailable'
74
+ | 'ssr-temporal-unavailable'
75
+ | 'ssr-receipt-identity-mismatch'
76
+ | 'ssr-receipt-stale';
77
+ readonly expected: string;
78
+ readonly hint: string;
79
+ readonly detail: Readonly<{
80
+ readonly owner: 'producer' | 'format' | 'temporal' | 'consumer';
81
+ /** Owner-directed action an AI consumer can take without parsing hint text. */
82
+ readonly action: SsrOwnerRecoveryAction;
83
+ readonly generation?: number;
84
+ readonly identityField?: keyof SsrAdmissionIdentity;
85
+ }>;
86
+ }
87
+
88
+ export type SsrAdmissionResult =
89
+ | {
90
+ readonly status: 'admitted';
91
+ readonly identity: SsrAdmissionIdentity | undefined;
92
+ readonly generation: number;
93
+ readonly work: SsrAdmissionWork;
94
+ readonly budget: SsrAdmissionBudget;
95
+ readonly failure?: undefined;
96
+ }
97
+ | {
98
+ readonly status: 'fallback-only';
99
+ readonly identity: SsrAdmissionIdentity | undefined;
100
+ readonly generation: number;
101
+ readonly work: SsrAdmissionWork;
102
+ readonly budget: SsrAdmissionBudget;
103
+ readonly failure: SsrAdmissionFailure;
104
+ };
105
+
106
+ export interface SsrAdmissionInput {
107
+ readonly requested: boolean;
108
+ readonly identity: SsrAdmissionIdentity;
109
+ readonly reflectionFallback?: SsrReflectionFallbackReceipt;
110
+ readonly format?: SsrFormatReceipt;
111
+ readonly temporal?: SsrTemporalReceipt;
112
+ readonly previousGeneration?: number;
113
+ }
114
+
115
+ /** Public renderer projection consumed by the parent SSR feature. */
116
+ export interface SsrDependenciesInspection {
117
+ readonly status: 'admitted' | 'fallback-only';
118
+ /** Absent until the host binds an exact source/tree/lock/build identity. */
119
+ readonly identity: SsrAdmissionIdentity | undefined;
120
+ readonly requested: boolean;
121
+ readonly reflectionFallback: ReflectionFallbackReceipt | undefined;
122
+ readonly format: SsrFormatReceipt | undefined;
123
+ readonly temporal: SsrTemporalReceipt | undefined;
124
+ readonly admission: SsrAdmissionResult;
125
+ readonly work: SsrAdmissionWork;
126
+ readonly budget: SsrAdmissionBudget;
127
+ readonly failure: SsrAdmissionFailure | undefined;
128
+ }
129
+
130
+ const ZERO_WORK: SsrAdmissionWork = Object.freeze({
131
+ attachmentCount: 0,
132
+ passCount: 0,
133
+ bindingCount: 0,
134
+ resourceCount: 0,
135
+ historyCount: 0,
136
+ temporalDemand: 0,
137
+ });
138
+
139
+ const ADMITTED_WORK: SsrAdmissionWork = Object.freeze({
140
+ attachmentCount: 1,
141
+ passCount: 1,
142
+ bindingCount: 1,
143
+ resourceCount: 1,
144
+ historyCount: 0,
145
+ temporalDemand: 0,
146
+ });
147
+
148
+ const ZERO_BUDGET: SsrAdmissionBudget = Object.freeze({
149
+ probeExecutions: 0,
150
+ resetCount: 0,
151
+ rebuildCount: 0,
152
+ });
153
+
154
+ const ADMITTED_BUDGET: SsrAdmissionBudget = Object.freeze({
155
+ probeExecutions: 1,
156
+ resetCount: 0,
157
+ rebuildCount: 0,
158
+ });
159
+
160
+ function identityFieldMismatch(
161
+ expected: SsrAdmissionIdentity,
162
+ actual: SsrAdmissionIdentity,
163
+ ): keyof SsrAdmissionIdentity | undefined {
164
+ for (const field of ['sourceHead', 'sourceTree', 'lockSha256', 'buildSha256'] as const) {
165
+ if (expected[field] !== actual[field]) return field;
166
+ }
167
+ return undefined;
168
+ }
169
+
170
+ function blocked(
171
+ identity: SsrAdmissionIdentity | undefined,
172
+ failure: SsrAdmissionFailure,
173
+ generation = 0,
174
+ ): SsrAdmissionResult {
175
+ return Object.freeze({
176
+ status: 'fallback-only',
177
+ identity,
178
+ generation,
179
+ work: ZERO_WORK,
180
+ budget: ZERO_BUDGET,
181
+ failure,
182
+ });
183
+ }
184
+
185
+ function failure(
186
+ code: SsrAdmissionFailure['code'],
187
+ owner: SsrAdmissionFailure['detail']['owner'],
188
+ hint: string,
189
+ detail: Omit<SsrAdmissionFailure['detail'], 'owner' | 'action'> = {},
190
+ ): SsrAdmissionFailure {
191
+ const action: SsrOwnerRecoveryAction = recoveryActionFor(code);
192
+ return Object.freeze({
193
+ code,
194
+ expected: 'all producer, format, and temporal receipts share one admitted identity',
195
+ hint,
196
+ detail: Object.freeze({ owner, action, ...detail }),
197
+ });
198
+ }
199
+
200
+ function recoveryActionFor(code: SsrAdmissionFailure['code']): SsrOwnerRecoveryAction {
201
+ switch (code) {
202
+ case 'ssr-reflection-fallback-unavailable':
203
+ case 'ssr-receipt-identity-mismatch':
204
+ return 'rebuild';
205
+ case 'ssr-not-requested':
206
+ case 'ssr-format-unavailable':
207
+ case 'ssr-temporal-unavailable':
208
+ case 'ssr-receipt-stale':
209
+ return 'retry';
210
+ }
211
+ }
212
+
213
+ function receiptFailure(
214
+ input: SsrAdmissionInput,
215
+ receipt: { readonly identity: SsrAdmissionIdentity },
216
+ owner: SsrAdmissionFailure['detail']['owner'],
217
+ ): SsrAdmissionResult | undefined {
218
+ const field = identityFieldMismatch(input.identity, receipt.identity);
219
+ if (field === undefined) return undefined;
220
+ return blocked(
221
+ input.identity,
222
+ failure('ssr-receipt-identity-mismatch', owner, 'rebuild the owner receipt for this identity', {
223
+ identityField: field,
224
+ }),
225
+ );
226
+ }
227
+
228
+ function validFallback(receipt: SsrReflectionFallbackReceipt | undefined): boolean {
229
+ return (
230
+ receipt !== undefined &&
231
+ receipt.candidateVisible === false &&
232
+ Number.isFinite(receipt.coverage) &&
233
+ receipt.coverage >= 0 &&
234
+ receipt.coverage <= 1 &&
235
+ (receipt.sourceKey === undefined || receipt.sourceKey.length > 0) &&
236
+ (receipt.extent === undefined ||
237
+ (receipt.extent.length === 3 &&
238
+ receipt.extent.every((value) => Number.isFinite(value) && value > 0))) &&
239
+ Number.isInteger(receipt.sourceGeneration) &&
240
+ receipt.sourceGeneration >= 0 &&
241
+ Number.isInteger(receipt.projectionGeneration) &&
242
+ receipt.projectionGeneration >= 0 &&
243
+ Number.isInteger(receipt.deviceGeneration) &&
244
+ receipt.deviceGeneration >= 0 &&
245
+ receipt.brdfSignature === 'standard-pbr-ibl-v1' &&
246
+ ((receipt.source === 'probe' && (receipt.state === 'active' || receipt.state === 'lkg')) ||
247
+ (receipt.source === 'skylight' && (receipt.state === 'active' || receipt.state === 'lkg')) ||
248
+ (receipt.source === 'neutral' && receipt.state === 'neutral'))
249
+ );
250
+ }
251
+
252
+ function validFormat(receipt: SsrFormatReceipt | undefined): boolean {
253
+ if (
254
+ receipt?.profile !== SSR_FORMAT_PROFILE ||
255
+ receipt.verdict !== 'admitted' ||
256
+ receipt.evidence !== 'real' ||
257
+ !Number.isInteger(receipt.deviceGeneration) ||
258
+ receipt.deviceGeneration < 0
259
+ ) {
260
+ return false;
261
+ }
262
+ if (
263
+ !SSR_FORMAT_STAGES.every((stage) =>
264
+ receipt.stages.some((entry) => entry.stage === stage && entry.verdict === 'admitted'),
265
+ )
266
+ ) {
267
+ return false;
268
+ }
269
+ return validateR32FloatReceipt(receipt).ok;
270
+ }
271
+
272
+ function validTemporal(receipt: SsrTemporalReceipt | undefined): boolean {
273
+ return (
274
+ receipt?.successfulSubmit === true &&
275
+ Number.isInteger(receipt.generation) &&
276
+ receipt.generation >= 0
277
+ );
278
+ }
279
+
280
+ export function admitSsrM0(input: SsrAdmissionInput): SsrAdmissionResult {
281
+ if (!input.requested) {
282
+ return blocked(
283
+ input.identity,
284
+ failure('ssr-not-requested', 'consumer', 'request SSR before evaluating M0 admission'),
285
+ );
286
+ }
287
+ if (input.reflectionFallback === undefined) {
288
+ return blocked(
289
+ input.identity,
290
+ failure('ssr-reflection-fallback-unavailable', 'producer', 'use LKG, Skylight, or neutral'),
291
+ );
292
+ }
293
+ const fallbackIdentityFailure = receiptFailure(input, input.reflectionFallback, 'producer');
294
+ if (fallbackIdentityFailure !== undefined) return fallbackIdentityFailure;
295
+ if (!validFallback(input.reflectionFallback)) {
296
+ return blocked(
297
+ input.identity,
298
+ failure(
299
+ 'ssr-reflection-fallback-unavailable',
300
+ 'producer',
301
+ 'inspect and rebuild the producer receipt',
302
+ ),
303
+ input.reflectionFallback.projectionGeneration,
304
+ );
305
+ }
306
+ if (input.format === undefined) {
307
+ return blocked(
308
+ input.identity,
309
+ failure('ssr-format-unavailable', 'format', 'probe the exact format profile for this device'),
310
+ input.reflectionFallback.projectionGeneration,
311
+ );
312
+ }
313
+ const formatIdentityFailure = receiptFailure(input, input.format, 'format');
314
+ if (formatIdentityFailure !== undefined) return formatIdentityFailure;
315
+ if (!validFormat(input.format)) {
316
+ return blocked(
317
+ input.identity,
318
+ failure('ssr-format-unavailable', 'format', 'complete the full r32float profile probe'),
319
+ input.reflectionFallback.projectionGeneration,
320
+ );
321
+ }
322
+ if (input.temporal === undefined) {
323
+ return blocked(
324
+ input.identity,
325
+ failure(
326
+ 'ssr-temporal-unavailable',
327
+ 'temporal',
328
+ 'submit the matching temporal producer receipt',
329
+ ),
330
+ input.reflectionFallback.projectionGeneration,
331
+ );
332
+ }
333
+ const temporalIdentityFailure = receiptFailure(input, input.temporal, 'temporal');
334
+ if (temporalIdentityFailure !== undefined) return temporalIdentityFailure;
335
+ if (!validTemporal(input.temporal)) {
336
+ return blocked(
337
+ input.identity,
338
+ failure('ssr-temporal-unavailable', 'temporal', 'inspect and rebuild the temporal receipt'),
339
+ input.reflectionFallback.projectionGeneration,
340
+ );
341
+ }
342
+ if (
343
+ input.previousGeneration !== undefined &&
344
+ input.previousGeneration !== input.reflectionFallback.projectionGeneration
345
+ ) {
346
+ return blocked(
347
+ input.identity,
348
+ failure(
349
+ 'ssr-receipt-stale',
350
+ 'consumer',
351
+ 'discard stale completion and reinspect the owners',
352
+ {
353
+ generation: input.reflectionFallback.projectionGeneration,
354
+ },
355
+ ),
356
+ input.reflectionFallback.projectionGeneration,
357
+ );
358
+ }
359
+ return Object.freeze({
360
+ status: 'admitted',
361
+ identity: input.identity,
362
+ generation: input.reflectionFallback.projectionGeneration,
363
+ work: ADMITTED_WORK,
364
+ budget: ADMITTED_BUDGET,
365
+ });
366
+ }
367
+
368
+ /**
369
+ * Joins the live owner projections at the renderer boundary. The only casts
370
+ * here add the host identity to receipts that otherwise remain canonical RHI
371
+ * or ReflectionProbe PODs; no producer fact is copied or renamed.
372
+ */
373
+ export function projectSsrDependencies(input: {
374
+ readonly requested: boolean;
375
+ readonly identity: SsrAdmissionIdentity | undefined;
376
+ readonly reflectionFallback: ReflectionFallbackReceipt | undefined;
377
+ readonly format: RhiTextureFormatCapabilityReceipt | undefined;
378
+ readonly temporal: { readonly successfulSubmit: true; readonly generation: number } | undefined;
379
+ readonly previousGeneration?: number;
380
+ }): SsrDependenciesInspection {
381
+ const identity = input.identity;
382
+ const reflectionFallback =
383
+ identity === undefined || input.reflectionFallback?.identity === undefined
384
+ ? undefined
385
+ : (input.reflectionFallback as SsrReflectionFallbackReceipt);
386
+ const format =
387
+ identity === undefined || input.format === undefined
388
+ ? undefined
389
+ : ({ ...input.format, identity } satisfies SsrFormatReceipt);
390
+ const temporal =
391
+ identity === undefined || input.temporal === undefined
392
+ ? undefined
393
+ : ({ identity, ...input.temporal } satisfies SsrTemporalReceipt);
394
+ const admission =
395
+ identity === undefined
396
+ ? blocked(
397
+ undefined,
398
+ failure(
399
+ input.requested ? 'ssr-receipt-identity-mismatch' : 'ssr-not-requested',
400
+ 'consumer',
401
+ input.requested
402
+ ? 'bind the exact source, tree, lock, and build identity before admission'
403
+ : 'request SSR before evaluating M0 admission',
404
+ input.requested ? { identityField: 'sourceHead' } : {},
405
+ ),
406
+ )
407
+ : admitSsrM0({
408
+ requested: input.requested,
409
+ identity,
410
+ ...(reflectionFallback === undefined ? {} : { reflectionFallback }),
411
+ ...(format === undefined ? {} : { format }),
412
+ ...(temporal === undefined ? {} : { temporal }),
413
+ ...(input.previousGeneration === undefined
414
+ ? {}
415
+ : { previousGeneration: input.previousGeneration }),
416
+ });
417
+ return Object.freeze({
418
+ status: admission.status,
419
+ identity,
420
+ requested: input.requested,
421
+ reflectionFallback: input.reflectionFallback,
422
+ format,
423
+ temporal,
424
+ admission,
425
+ work: admission.work,
426
+ budget: admission.budget,
427
+ failure: admission.failure,
428
+ });
429
+ }
430
+
431
+ export function zeroSsrAdmissionWork(): SsrAdmissionWork {
432
+ return ZERO_WORK;
433
+ }
434
+
435
+ /**
436
+ * Projects the admission decision into the existing temporal reset seam.
437
+ * Admission never creates temporal history; only a changed committed
438
+ * producer generation can request a reset through the coordinator.
439
+ */
440
+ export function resolveSsrAdmissionGeneration(
441
+ previousGeneration: number | undefined,
442
+ nextGeneration: number | undefined,
443
+ ): { readonly changed: boolean; readonly generation: number | undefined } {
444
+ return {
445
+ changed:
446
+ previousGeneration !== undefined &&
447
+ nextGeneration !== undefined &&
448
+ previousGeneration !== nextGeneration,
449
+ generation: nextGeneration,
450
+ };
451
+ }
@@ -0,0 +1,73 @@
1
+ import { err, ok, type Result } from '@forgeax/engine-types';
2
+
3
+ /** Linear HDR RGB used by the M0 composition contract. */
4
+ export type SsrReflectionColor = readonly [number, number, number];
5
+
6
+ export interface SsrCompositionInput {
7
+ /** Screen-hit confidence. The parent SSR consumer supplies 0..1. */
8
+ readonly c: number;
9
+ /** The Standard BRDF specular lobe before SSR replacement. */
10
+ readonly baseSpecular: SsrReflectionColor;
11
+ /** The same-source BRDF-projected screen contribution. */
12
+ readonly screenSpecular: SsrReflectionColor;
13
+ /** The committed producer-owned environment contribution. */
14
+ readonly fallbackSpecular: SsrReflectionColor;
15
+ }
16
+
17
+ export interface SsrCompositionInputError {
18
+ readonly code: 'ssr-composition-input-invalid';
19
+ readonly expected: string;
20
+ readonly hint: string;
21
+ readonly detail: Readonly<{
22
+ readonly field: 'c' | 'baseSpecular' | 'screenSpecular' | 'fallbackSpecular';
23
+ }>;
24
+ }
25
+
26
+ export type SsrCompositionResult = Result<SsrReflectionColor, SsrCompositionInputError>;
27
+
28
+ function finiteColor(value: SsrReflectionColor): boolean {
29
+ return value.length === 3 && value.every(Number.isFinite);
30
+ }
31
+
32
+ function invalid(
33
+ field: SsrCompositionInputError['detail']['field'],
34
+ expected: string,
35
+ ): SsrCompositionResult {
36
+ return err({
37
+ code: 'ssr-composition-input-invalid',
38
+ expected,
39
+ hint: 'preserve linear HDR and the bounded SSR confidence before composing',
40
+ detail: Object.freeze({ field }),
41
+ });
42
+ }
43
+
44
+ /**
45
+ * Apply the M0 reflection replacement contract without owning an SSR pass.
46
+ *
47
+ * `c=0` returns the base Standard specular lobe. `c=1` replaces only that
48
+ * lobe with the same-source screen contribution, subtracting the committed
49
+ * environment contribution once. Hi-Z traversal, history and graph wiring
50
+ * remain owned by the parent SSR feature.
51
+ */
52
+ export function composeSsrReflection(input: SsrCompositionInput): SsrCompositionResult {
53
+ if (!Number.isFinite(input.c) || input.c < 0 || input.c > 1) {
54
+ return invalid('c', 'a finite confidence in the inclusive range 0..1');
55
+ }
56
+ for (const [field, value] of [
57
+ ['baseSpecular', input.baseSpecular],
58
+ ['screenSpecular', input.screenSpecular],
59
+ ['fallbackSpecular', input.fallbackSpecular],
60
+ ] as const) {
61
+ if (!finiteColor(value)) {
62
+ return invalid(field, 'three finite linear HDR RGB channels');
63
+ }
64
+ }
65
+ const c = input.c;
66
+ return ok(
67
+ Object.freeze([
68
+ input.baseSpecular[0] + c * (input.screenSpecular[0] - input.fallbackSpecular[0]),
69
+ input.baseSpecular[1] + c * (input.screenSpecular[1] - input.fallbackSpecular[1]),
70
+ input.baseSpecular[2] + c * (input.screenSpecular[2] - input.fallbackSpecular[2]),
71
+ ]) as SsrReflectionColor,
72
+ );
73
+ }