@forgeax/engine-render 0.1.28 → 0.1.30

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 (768) hide show
  1. package/README.md +856 -126
  2. package/dist/assembly/atmosphere-shader-prewarm.d.ts +6 -0
  3. package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -0
  4. package/dist/assembly/device-feature-admission.d.ts +4 -3
  5. package/dist/assembly/device-feature-admission.d.ts.map +1 -1
  6. package/dist/assembly/dynamic-geometry-host.d.ts +27 -0
  7. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
  8. package/dist/assembly/dynamic-geometry-runtime.d.ts +20 -0
  9. package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -0
  10. package/dist/assembly/factory.d.ts +3 -1
  11. package/dist/assembly/factory.d.ts.map +1 -1
  12. package/dist/assembly/gpu-driven-owner.d.ts +10 -0
  13. package/dist/assembly/gpu-driven-owner.d.ts.map +1 -0
  14. package/dist/assembly/host-contract.d.ts +14 -0
  15. package/dist/assembly/host-contract.d.ts.map +1 -1
  16. package/dist/assembly/material/assembly.d.ts +31 -1
  17. package/dist/assembly/material/assembly.d.ts.map +1 -1
  18. package/dist/assembly/material/pipeline-helpers.d.ts +24 -0
  19. package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -0
  20. package/dist/assembly/material-shader-policy.d.ts +3 -1
  21. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  22. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
  23. package/dist/assembly/recovery/failure-location.d.ts +12 -0
  24. package/dist/assembly/recovery/failure-location.d.ts.map +1 -0
  25. package/dist/assembly/recovery/recovery-attempt.d.ts +1 -6
  26. package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -1
  27. package/dist/assembly/recovery/renderer-recover.d.ts +7 -4
  28. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  29. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  30. package/dist/assembly/renderer-feature-inspection.d.ts +5 -0
  31. package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -0
  32. package/dist/assembly/renderer-frame-transaction.d.ts +19 -0
  33. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  34. package/dist/assembly/renderer-helpers.d.ts +3 -0
  35. package/dist/assembly/renderer-helpers.d.ts.map +1 -1
  36. package/dist/assembly/renderer-inspection.d.ts +4 -0
  37. package/dist/assembly/renderer-inspection.d.ts.map +1 -1
  38. package/dist/assembly/shader-prewarm-policy.d.ts +7 -1
  39. package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -1
  40. package/dist/assembly/skin-palette-owner.d.ts +8 -0
  41. package/dist/assembly/skin-palette-owner.d.ts.map +1 -0
  42. package/dist/assembly/ssr-shader-prewarm.d.ts +6 -0
  43. package/dist/assembly/ssr-shader-prewarm.d.ts.map +1 -0
  44. package/dist/assembly/webgpu-device-loss.d.ts +15 -0
  45. package/dist/assembly/webgpu-device-loss.d.ts.map +1 -0
  46. package/dist/assembly/webgpu-pbr-ready.d.ts +38 -0
  47. package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -0
  48. package/dist/assembly/webgpu-ready-mipmap.d.ts +11 -0
  49. package/dist/assembly/webgpu-ready-mipmap.d.ts.map +1 -0
  50. package/dist/assembly/webgpu-ready.d.ts +7 -3
  51. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  52. package/dist/assembly/webgpu-renderer-bootstrap.d.ts +10 -0
  53. package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -0
  54. package/dist/assembly/webgpu-renderer-contract.d.ts +62 -0
  55. package/dist/assembly/webgpu-renderer-contract.d.ts.map +1 -0
  56. package/dist/assembly/webgpu-renderer-guards.d.ts +9 -0
  57. package/dist/assembly/webgpu-renderer-guards.d.ts.map +1 -0
  58. package/dist/assembly/webgpu-renderer-observation.d.ts +6 -0
  59. package/dist/assembly/webgpu-renderer-observation.d.ts.map +1 -0
  60. package/dist/assembly/webgpu-renderer-recovery-failure.d.ts +12 -0
  61. package/dist/assembly/webgpu-renderer-recovery-failure.d.ts.map +1 -0
  62. package/dist/assembly/webgpu-renderer.d.ts +7 -141
  63. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  64. package/dist/assembly/webgpu-vertex-layouts.d.ts +28 -84
  65. package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -1
  66. package/dist/authoring.mjs +2 -2
  67. package/dist/capture/scene-projection.d.ts +41 -0
  68. package/dist/capture/scene-projection.d.ts.map +1 -0
  69. package/dist/{chunk-J52NMQKR.mjs → chunk-7MQ3XBSP.mjs} +1098 -844
  70. package/dist/chunk-7MQ3XBSP.mjs.map +1 -0
  71. package/dist/{chunk-GF523LHF.mjs → chunk-BLXGONR2.mjs} +30 -12
  72. package/dist/chunk-BLXGONR2.mjs.map +1 -0
  73. package/dist/{chunk-VSL23LZE.mjs → chunk-C3DDSXYK.mjs} +1188 -463
  74. package/dist/chunk-C3DDSXYK.mjs.map +1 -0
  75. package/dist/{chunk-IQMKJLNU.mjs → chunk-FUUO4UND.mjs} +443 -30
  76. package/dist/chunk-FUUO4UND.mjs.map +1 -0
  77. package/dist/{chunk-J6KACNOH.mjs → chunk-JRBDO2KB.mjs} +617 -7
  78. package/dist/chunk-JRBDO2KB.mjs.map +1 -0
  79. package/dist/{chunk-GE3SDD72.mjs → chunk-MDQJPY3T.mjs} +20 -40
  80. package/dist/chunk-MDQJPY3T.mjs.map +1 -0
  81. package/dist/{chunk-MYJ5RPJO.mjs → chunk-NO4WZCAH.mjs} +3 -3
  82. package/dist/chunk-NO4WZCAH.mjs.map +1 -0
  83. package/dist/{chunk-RBDBZ5R7.mjs → chunk-PA5DRJNI.mjs} +2 -2
  84. package/dist/chunk-PA5DRJNI.mjs.map +1 -0
  85. package/dist/{chunk-MBU4ZVE7.mjs → chunk-PSEQ6QPH.mjs} +4655 -892
  86. package/dist/chunk-PSEQ6QPH.mjs.map +1 -0
  87. package/dist/{chunk-JAASCMXA.mjs → chunk-S5UE2T5P.mjs} +289 -41
  88. package/dist/chunk-S5UE2T5P.mjs.map +1 -0
  89. package/dist/components/camera.d.ts +62 -2
  90. package/dist/components/camera.d.ts.map +1 -1
  91. package/dist/components/directional-light.d.ts +2 -2
  92. package/dist/components/dynamic-resolution.d.ts +15 -0
  93. package/dist/components/dynamic-resolution.d.ts.map +1 -0
  94. package/dist/components/index.d.ts +2 -0
  95. package/dist/components/index.d.ts.map +1 -1
  96. package/dist/components/instances.d.ts +6 -20
  97. package/dist/components/instances.d.ts.map +1 -1
  98. package/dist/components/light-helpers.d.ts.map +1 -1
  99. package/dist/components/reflection-probe.d.ts +1 -0
  100. package/dist/components/reflection-probe.d.ts.map +1 -1
  101. package/dist/components/scene-instance.d.ts +2 -1
  102. package/dist/components/scene-instance.d.ts.map +1 -1
  103. package/dist/components/screen-space-reflection.d.ts +14 -0
  104. package/dist/components/screen-space-reflection.d.ts.map +1 -0
  105. package/dist/components/skylight.d.ts +4 -1
  106. package/dist/components/skylight.d.ts.map +1 -1
  107. package/dist/construct-renderer.mjs +14809 -7570
  108. package/dist/construct-renderer.mjs.map +1 -1
  109. package/dist/device/device-scope.d.ts +2 -0
  110. package/dist/device/device-scope.d.ts.map +1 -1
  111. package/dist/device/gpu-residency.d.ts +3 -14
  112. package/dist/device/gpu-residency.d.ts.map +1 -1
  113. package/dist/dynamic-geometry.d.ts.map +1 -1
  114. package/dist/environment/background.d.ts +7 -0
  115. package/dist/environment/background.d.ts.map +1 -0
  116. package/dist/environment/ibl.d.ts +11 -0
  117. package/dist/environment/ibl.d.ts.map +1 -0
  118. package/dist/environment/storage.d.ts +14 -0
  119. package/dist/environment/storage.d.ts.map +1 -0
  120. package/dist/errors/gpu-driven.d.ts +6 -3
  121. package/dist/errors/gpu-driven.d.ts.map +1 -1
  122. package/dist/errors/render.d.ts +41 -4
  123. package/dist/errors/render.d.ts.map +1 -1
  124. package/dist/extract/camera.d.ts.map +1 -1
  125. package/dist/extract/contracts.d.ts +7 -0
  126. package/dist/extract/contracts.d.ts.map +1 -1
  127. package/dist/extract/gpu-driven.d.ts +50 -3
  128. package/dist/extract/gpu-driven.d.ts.map +1 -1
  129. package/dist/features/host.d.ts +23 -1
  130. package/dist/features/host.d.ts.map +1 -1
  131. package/dist/features/noise-texture.d.ts +17 -0
  132. package/dist/features/noise-texture.d.ts.map +1 -0
  133. package/dist/features/plan.d.ts +83 -2
  134. package/dist/features/plan.d.ts.map +1 -1
  135. package/dist/features/prepared-gpu-work.d.ts +37 -4
  136. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  137. package/dist/features/prepared-graphics-store.d.ts.map +1 -1
  138. package/dist/features/prepared-graphics.d.ts +5 -0
  139. package/dist/features/prepared-graphics.d.ts.map +1 -1
  140. package/dist/features/render-graph-compute.d.ts +5 -3
  141. package/dist/features/render-graph-compute.d.ts.map +1 -1
  142. package/dist/features/render-graph-contribution.d.ts +11 -3
  143. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  144. package/dist/features/render-graph-raster.d.ts +19 -3
  145. package/dist/features/render-graph-raster.d.ts.map +1 -1
  146. package/dist/features/types.d.ts +11 -0
  147. package/dist/features/types.d.ts.map +1 -1
  148. package/dist/fullscreen-post-process-pass.d.ts +2 -0
  149. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  150. package/dist/gpu-driven/batch-topology.d.ts +34 -1
  151. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  152. package/dist/gpu-driven/material-artifacts.d.ts +24 -0
  153. package/dist/gpu-driven/material-artifacts.d.ts.map +1 -0
  154. package/dist/gpu-driven/material-bindings.d.ts +7 -0
  155. package/dist/gpu-driven/material-bindings.d.ts.map +1 -0
  156. package/dist/gpu-driven/pbr-program.d.ts +10 -0
  157. package/dist/gpu-driven/pbr-program.d.ts.map +1 -0
  158. package/dist/gpu-driven/prepared-draw.d.ts +2 -0
  159. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  160. package/dist/gpu-driven/production-raster.d.ts +169 -27
  161. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  162. package/dist/gpu-driven/shadow-views.d.ts +126 -0
  163. package/dist/gpu-driven/shadow-views.d.ts.map +1 -0
  164. package/dist/gpu-driven/view-gpu.d.ts +18 -2
  165. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  166. package/dist/gpu-resource.d.ts +5 -2
  167. package/dist/gpu-resource.d.ts.map +1 -1
  168. package/dist/gpu-scene-schema.d.ts +2 -8
  169. package/dist/gpu-scene-schema.d.ts.map +1 -1
  170. package/dist/gpu-scene.d.ts +14 -0
  171. package/dist/gpu-scene.d.ts.map +1 -1
  172. package/dist/gpu-texture-usage.d.ts +1 -0
  173. package/dist/gpu-texture-usage.d.ts.map +1 -1
  174. package/dist/hdrp-buffers.d.ts +9 -0
  175. package/dist/hdrp-buffers.d.ts.map +1 -1
  176. package/dist/ibl/IblPipelineCache.d.ts +2 -0
  177. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  178. package/dist/ibl/frame-resources.d.ts +8 -0
  179. package/dist/ibl/frame-resources.d.ts.map +1 -0
  180. package/dist/ibl/skylight-bind-group.d.ts +3 -1
  181. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  182. package/dist/index.d.ts +87 -75
  183. package/dist/index.d.ts.map +1 -1
  184. package/dist/index.mjs +10 -8
  185. package/dist/index.mjs.map +1 -1
  186. package/dist/inspection-types.d.ts +180 -1
  187. package/dist/inspection-types.d.ts.map +1 -1
  188. package/dist/instances-derived-bounds.d.ts +11 -0
  189. package/dist/instances-derived-bounds.d.ts.map +1 -1
  190. package/dist/instances.d.ts +89 -44
  191. package/dist/instances.d.ts.map +1 -1
  192. package/dist/internal.d.ts +3 -0
  193. package/dist/internal.d.ts.map +1 -1
  194. package/dist/internal.mjs +117 -9
  195. package/dist/internal.mjs.map +1 -1
  196. package/dist/lifecycle.d.ts +2 -1
  197. package/dist/lifecycle.d.ts.map +1 -1
  198. package/dist/material-row.d.ts +15 -0
  199. package/dist/material-row.d.ts.map +1 -0
  200. package/dist/materials.d.ts +15 -0
  201. package/dist/materials.d.ts.map +1 -1
  202. package/dist/pbr-pipeline.d.ts +23 -5
  203. package/dist/pbr-pipeline.d.ts.map +1 -1
  204. package/dist/pipeline/render-extent.d.ts +51 -0
  205. package/dist/pipeline/render-extent.d.ts.map +1 -0
  206. package/dist/pipeline/standard-build-context.d.ts +7 -0
  207. package/dist/pipeline/standard-build-context.d.ts.map +1 -0
  208. package/dist/pipeline/standard-forward-lane.d.ts +3 -2
  209. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  210. package/dist/pipeline/standard-output/auto-exposure/capability.d.ts +12 -0
  211. package/dist/pipeline/standard-output/auto-exposure/capability.d.ts.map +1 -0
  212. package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts +70 -0
  213. package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts.map +1 -0
  214. package/dist/pipeline/standard-output/auto-exposure/graph.d.ts +75 -0
  215. package/dist/pipeline/standard-output/auto-exposure/graph.d.ts.map +1 -0
  216. package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts +111 -0
  217. package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts.map +1 -0
  218. package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts +16 -0
  219. package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts.map +1 -0
  220. package/dist/pipeline/standard-output/auto-exposure/preset.d.ts +14 -0
  221. package/dist/pipeline/standard-output/auto-exposure/preset.d.ts.map +1 -0
  222. package/dist/pipeline/standard-output/auto-exposure/state.d.ts +46 -0
  223. package/dist/pipeline/standard-output/auto-exposure/state.d.ts.map +1 -0
  224. package/dist/pipeline/standard-output/color-transform.d.ts +27 -0
  225. package/dist/pipeline/standard-output/color-transform.d.ts.map +1 -0
  226. package/dist/pipeline/standard-output/graph.d.ts +15 -0
  227. package/dist/pipeline/standard-output/graph.d.ts.map +1 -0
  228. package/dist/pipeline/standard-output/lut-admission.d.ts +29 -0
  229. package/dist/pipeline/standard-output/lut-admission.d.ts.map +1 -0
  230. package/dist/pipeline/standard-output/lut-gpu.d.ts +48 -0
  231. package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -0
  232. package/dist/pipeline/standard-output/lut-state.d.ts +68 -0
  233. package/dist/pipeline/standard-output/lut-state.d.ts.map +1 -0
  234. package/dist/pipeline/standard-output/resources.d.ts +10 -0
  235. package/dist/pipeline/standard-output/resources.d.ts.map +1 -0
  236. package/dist/pipeline/standard-output/types.d.ts +39 -0
  237. package/dist/pipeline/standard-output/types.d.ts.map +1 -0
  238. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  239. package/dist/pipeline/standard-post.d.ts +2 -2
  240. package/dist/pipeline/standard-post.d.ts.map +1 -1
  241. package/dist/pipeline/standard-profile.d.ts +2 -1
  242. package/dist/pipeline/standard-profile.d.ts.map +1 -1
  243. package/dist/pipeline-builder.d.ts +5 -2
  244. package/dist/pipeline-builder.d.ts.map +1 -1
  245. package/dist/pipeline-spec-types.d.ts +4 -0
  246. package/dist/pipeline-spec-types.d.ts.map +1 -1
  247. package/dist/pipeline-spec.d.ts +1 -9
  248. package/dist/pipeline-spec.d.ts.map +1 -1
  249. package/dist/plugin.d.ts.map +1 -1
  250. package/dist/points-lines/standard-owner.d.ts +1 -1
  251. package/dist/points-lines/standard-owner.d.ts.map +1 -1
  252. package/dist/post-process-errors.d.ts +9 -2
  253. package/dist/post-process-errors.d.ts.map +1 -1
  254. package/dist/prepare/extended-lighting/resources.d.ts +3 -3
  255. package/dist/prepare/prepared-graphics-resolver.d.ts +6 -1
  256. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  257. package/dist/record/dynamic-geometry-consumption.d.ts +15 -0
  258. package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -0
  259. package/dist/record/frame-lighting.d.ts +3 -3
  260. package/dist/record/frame-lighting.d.ts.map +1 -1
  261. package/dist/record/frame-snapshot.d.ts +109 -13
  262. package/dist/record/frame-snapshot.d.ts.map +1 -1
  263. package/dist/record/frame-targets.d.ts.map +1 -1
  264. package/dist/record/frame.d.ts +41 -5
  265. package/dist/record/frame.d.ts.map +1 -1
  266. package/dist/record/main-pass-geometry.d.ts +13 -9
  267. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  268. package/dist/record/main-pass-material.d.ts +43 -3
  269. package/dist/record/main-pass-material.d.ts.map +1 -1
  270. package/dist/record/main-pass-sprite-draws.d.ts +2 -10
  271. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  272. package/dist/record/main-pass.d.ts +14 -2
  273. package/dist/record/main-pass.d.ts.map +1 -1
  274. package/dist/record/mesh-ssbo.d.ts +15 -6
  275. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  276. package/dist/record/prepared-material-bindings.d.ts +19 -0
  277. package/dist/record/prepared-material-bindings.d.ts.map +1 -0
  278. package/dist/record/recovery-pipeline.d.ts +1 -0
  279. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  280. package/dist/record/render-context.d.ts +101 -22
  281. package/dist/record/render-context.d.ts.map +1 -1
  282. package/dist/record/shadow-pass.d.ts +62 -7
  283. package/dist/record/shadow-pass.d.ts.map +1 -1
  284. package/dist/record/skybox-post-pass.d.ts +1 -1
  285. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  286. package/dist/record/typed-frame-graph.d.ts +39 -8
  287. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  288. package/dist/record/view-ubo.d.ts +1 -0
  289. package/dist/record/view-ubo.d.ts.map +1 -1
  290. package/dist/recovery/render-system-candidate.d.ts +10 -2
  291. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  292. package/dist/recovery/render-system-roots.d.ts +11 -0
  293. package/dist/recovery/render-system-roots.d.ts.map +1 -0
  294. package/dist/recovery/types.d.ts +76 -0
  295. package/dist/recovery/types.d.ts.map +1 -0
  296. package/dist/reflection/background.d.ts +14 -0
  297. package/dist/reflection/background.d.ts.map +1 -0
  298. package/dist/reflection/filter.d.ts.map +1 -1
  299. package/dist/reflection/projection.d.ts +1 -0
  300. package/dist/reflection/projection.d.ts.map +1 -1
  301. package/dist/reflection/record-owner.d.ts +4 -4
  302. package/dist/reflection/record-owner.d.ts.map +1 -1
  303. package/dist/render-contract.d.ts +211 -53
  304. package/dist/render-contract.d.ts.map +1 -1
  305. package/dist/render-graph-primitives.d.ts +1 -0
  306. package/dist/render-graph-primitives.d.ts.map +1 -1
  307. package/dist/render-pipeline.d.ts +81 -6
  308. package/dist/render-pipeline.d.ts.map +1 -1
  309. package/dist/render-system-extract-tail.d.ts +15 -6
  310. package/dist/render-system-extract-tail.d.ts.map +1 -1
  311. package/dist/render-system-extract.d.ts +128 -41
  312. package/dist/render-system-extract.d.ts.map +1 -1
  313. package/dist/render-system-projections.d.ts +22 -0
  314. package/dist/render-system-projections.d.ts.map +1 -0
  315. package/dist/render-system.d.ts +27 -77
  316. package/dist/render-system.d.ts.map +1 -1
  317. package/dist/scene/render-scene-types.d.ts +14 -7
  318. package/dist/scene/render-scene-types.d.ts.map +1 -1
  319. package/dist/scene/render-scene.d.ts +31 -11
  320. package/dist/scene/render-scene.d.ts.map +1 -1
  321. package/dist/scene/shadow-visibility.d.ts +5 -0
  322. package/dist/scene/shadow-visibility.d.ts.map +1 -0
  323. package/dist/ssao-config.d.ts +8 -6
  324. package/dist/ssao-config.d.ts.map +1 -1
  325. package/dist/ssr/admission.d.ts +70 -1
  326. package/dist/ssr/admission.d.ts.map +1 -1
  327. package/dist/ssr/compose.d.ts +13 -0
  328. package/dist/ssr/compose.d.ts.map +1 -0
  329. package/dist/ssr/errors.d.ts +35 -0
  330. package/dist/ssr/errors.d.ts.map +1 -1
  331. package/dist/ssr/graph.d.ts +60 -0
  332. package/dist/ssr/graph.d.ts.map +1 -0
  333. package/dist/ssr/history.d.ts +97 -0
  334. package/dist/ssr/history.d.ts.map +1 -0
  335. package/dist/ssr/hiz.d.ts +37 -0
  336. package/dist/ssr/hiz.d.ts.map +1 -0
  337. package/dist/ssr/inspection.d.ts +42 -0
  338. package/dist/ssr/inspection.d.ts.map +1 -0
  339. package/dist/ssr/resources.d.ts +58 -0
  340. package/dist/ssr/resources.d.ts.map +1 -0
  341. package/dist/ssr/temporal.d.ts +38 -0
  342. package/dist/ssr/temporal.d.ts.map +1 -0
  343. package/dist/systems/skin-palette-allocator.d.ts +32 -10
  344. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  345. package/dist/systems/skin-palette-types.d.ts +15 -0
  346. package/dist/systems/skin-palette-types.d.ts.map +1 -1
  347. package/dist/systems/transparent-dispatch.d.ts +9 -0
  348. package/dist/systems/transparent-dispatch.d.ts.map +1 -0
  349. package/dist/targets/material-source.d.ts +7 -0
  350. package/dist/targets/material-source.d.ts.map +1 -1
  351. package/dist/temporal/frame-coordinator.d.ts +7 -0
  352. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  353. package/dist/temporal/gpu.d.ts +8 -0
  354. package/dist/temporal/gpu.d.ts.map +1 -1
  355. package/dist/temporal/history.d.ts.map +1 -1
  356. package/dist/temporal/index.mjs +6 -6
  357. package/dist/temporal/inspection.d.ts.map +1 -1
  358. package/dist/temporal/standard-scene-data.d.ts +4 -2
  359. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  360. package/dist/temporal/target-coverage-attachment.d.ts +15 -0
  361. package/dist/temporal/target-coverage-attachment.d.ts.map +1 -0
  362. package/dist/temporal/temporal-view.d.ts +6 -0
  363. package/dist/temporal/temporal-view.d.ts.map +1 -1
  364. package/dist/temporal/view.d.ts +6 -0
  365. package/dist/temporal/view.d.ts.map +1 -1
  366. package/dist/typed-render-graph-primitives.d.ts +41 -1
  367. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  368. package/dist/typed-shadow-passes.d.ts +3 -1
  369. package/dist/typed-shadow-passes.d.ts.map +1 -1
  370. package/dist/volume/component.d.ts +1 -0
  371. package/dist/volume/component.d.ts.map +1 -1
  372. package/dist/volume/temporal.d.ts +6 -0
  373. package/dist/volume/temporal.d.ts.map +1 -1
  374. package/package.json +21 -20
  375. package/src/__tests__/auto-exposure-gpu-evidence.ts +291 -0
  376. package/src/__tests__/auto-exposure-inspection.unit.test.ts +80 -0
  377. package/src/__tests__/auto-exposure-transaction.integration.test.ts +99 -0
  378. package/src/__tests__/auto-exposure.browser.test.ts +33 -0
  379. package/src/__tests__/auto-exposure.dawn.test.ts +33 -0
  380. package/src/__tests__/capture-scene-projection.unit.test.ts +24 -0
  381. package/src/__tests__/detached-inspection.integration.test.ts +5 -0
  382. package/src/__tests__/device-feature-admission.unit.test.ts +15 -24
  383. package/src/__tests__/device-loss-fanout.unit.test.ts +43 -0
  384. package/src/__tests__/device-scope-lifecycle.unit.test.ts +18 -0
  385. package/src/__tests__/device-scope-stale-matrix.unit.test.ts +18 -0
  386. package/src/__tests__/docs-gate-green.unit.test.ts +3 -3
  387. package/src/__tests__/dynamic-geometry-batch.unit.test.ts +157 -0
  388. package/src/__tests__/dynamic-geometry.unit.test.ts +36 -0
  389. package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +81 -0
  390. package/src/__tests__/factory-contract.integration.test.ts +231 -9
  391. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +19 -0
  392. package/src/__tests__/feature-depth-input.dawn.test.ts +121 -0
  393. package/src/__tests__/feature-noise.unit.test.ts +38 -0
  394. package/src/__tests__/fixtures/taa-resolve-pinned-baseline.wgsl +301 -0
  395. package/src/__tests__/frame-camera-selection.unit.test.ts +131 -0
  396. package/src/__tests__/frame-observation.contract.test.ts +21 -0
  397. package/src/__tests__/frame-plan-contract.unit.test.ts +24 -0
  398. package/src/__tests__/frame-targets.unit.test.ts +24 -0
  399. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +3 -0
  400. package/src/__tests__/gpu-driven-baseline.characterization.test.ts +79 -0
  401. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +307 -7
  402. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +122 -142
  403. package/src/__tests__/gpu-driven-indirect-raster.browser.test.ts +10 -1
  404. package/src/__tests__/gpu-driven-lod-cache.unit.test.ts +261 -0
  405. package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +346 -0
  406. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +1422 -0
  407. package/src/__tests__/gpu-driven-pbr.integration.test.ts +21 -0
  408. package/src/__tests__/gpu-driven-production-fixture.ts +96 -0
  409. package/src/__tests__/gpu-driven-production.integration.test.ts +1076 -106
  410. package/src/__tests__/gpu-driven-scaling.unit.test.ts +1 -3
  411. package/src/__tests__/gpu-driven-shadow-views.browser.test.ts +63 -0
  412. package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +294 -0
  413. package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +97 -0
  414. package/src/__tests__/gpu-driven-shadow-views.unit.test.ts +168 -0
  415. package/src/__tests__/gpu-driven-skin-contract.test-d.ts +11 -0
  416. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +72 -20
  417. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +30 -9
  418. package/src/__tests__/gpu-driven-view.unit.test.ts +160 -52
  419. package/src/__tests__/gpu-pass-timing-browser-runner.ts +15 -0
  420. package/src/__tests__/gpu-pass-timing.browser.test.ts +20 -2
  421. package/src/__tests__/gpu-resource-scope.unit.test.ts +71 -0
  422. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +42 -0
  423. package/src/__tests__/gpu-scene-render-graph-gpu.ts +19 -5
  424. package/src/__tests__/gpu-scene.dawn.test.ts +40 -13
  425. package/src/__tests__/gpu-scene.unit.test.ts +613 -33
  426. package/src/__tests__/instances-culling.integration.test.ts +36 -10
  427. package/src/__tests__/instances-derived-bounds.unit.test.ts +54 -0
  428. package/src/__tests__/instances-store.unit.test.ts +242 -39
  429. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +6 -0
  430. package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +13 -16
  431. package/src/__tests__/lut-lkg-transaction.integration.test.ts +122 -0
  432. package/src/__tests__/material-contract-inventory.unit.test.ts +5 -6
  433. package/src/__tests__/material-dispatch-shader-id.unit.test.ts +51 -0
  434. package/src/__tests__/material-lane-selection.unit.test.ts +60 -0
  435. package/src/__tests__/material-numeric-upload.unit.test.ts +53 -0
  436. package/src/__tests__/material-texture-handles.unit.test.ts +18 -0
  437. package/src/__tests__/material-texture-source.unit.test.ts +87 -0
  438. package/src/__tests__/materials-authoring-validation.unit.test.ts +41 -0
  439. package/src/__tests__/materials-standard-contract.unit.test.ts +57 -3
  440. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +4 -0
  441. package/src/__tests__/mesh-submission-lifetime.unit.test.ts +8 -0
  442. package/src/__tests__/morph-neutral-admission.unit.test.ts +19 -0
  443. package/src/__tests__/normal-fallback.dawn.test.ts +122 -0
  444. package/src/__tests__/particle-material-input-layout.unit.test.ts +104 -0
  445. package/src/__tests__/physical-clearcoat.integration.test.ts +42 -3
  446. package/src/__tests__/pipeline-texture-specialization.unit.test.ts +138 -0
  447. package/src/__tests__/point-shadow-inspection.unit.test.ts +2 -0
  448. package/src/__tests__/post-process-error-policy-owner.unit.test.ts +6 -5
  449. package/src/__tests__/prepared-gpu-driven-pbr-contract.test-d.ts +31 -0
  450. package/src/__tests__/prepared-gpu-driven-pbr-contract.unit.test.ts +39 -0
  451. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +251 -9
  452. package/src/__tests__/prepared-graphics-capability.unit.test.ts +21 -0
  453. package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +6 -2
  454. package/src/__tests__/profiler-phase-catalog.test.ts +5 -0
  455. package/src/__tests__/public-surface.test-d.ts +3 -0
  456. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +30 -12
  457. package/src/__tests__/reflection-fallback-async-publication.unit.test.ts +171 -0
  458. package/src/__tests__/reflection-probe-authoring.unit.test.ts +12 -10
  459. package/src/__tests__/reflection-probe-box.dawn.test.ts +71 -0
  460. package/src/__tests__/reflection-probe-filter.unit.test.ts +9 -0
  461. package/src/__tests__/reflection-probe-orientation.dawn.test.ts +294 -0
  462. package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +5 -14
  463. package/src/__tests__/reflection-probe-updates.unit.test.ts +129 -0
  464. package/src/__tests__/render-error-code-owner.test-d.ts +7 -0
  465. package/src/__tests__/render-error-detail-ssot.test-d.ts +27 -1
  466. package/src/__tests__/render-error-exhaustive.test-d.ts +15 -0
  467. package/src/__tests__/render-extent.unit.test.ts +63 -0
  468. package/src/__tests__/render-feature-gpu-work.integration.test.ts +285 -2
  469. package/src/__tests__/render-feature-plan.unit.test.ts +245 -0
  470. package/src/__tests__/render-feature-shadow-order.unit.test.ts +228 -0
  471. package/src/__tests__/render-feature-zero-work.unit.test.ts +35 -1
  472. package/src/__tests__/render-graph-fallback.integration.test.ts +2 -2
  473. package/src/__tests__/render-owner-cohesion.unit.test.ts +55 -0
  474. package/src/__tests__/render-scene-mixed-updates.integration.test.ts +426 -0
  475. package/src/__tests__/render-scene-projection.unit.test.ts +915 -23
  476. package/src/__tests__/render-scene-resource-updates.integration.test.ts +214 -0
  477. package/src/__tests__/render-scene-skin-lifecycle.integration.test.ts +173 -0
  478. package/src/__tests__/render-scene-temporal-retry.unit.test.ts +133 -0
  479. package/src/__tests__/render-target-material-source.test-d.ts +27 -3
  480. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +233 -9
  481. package/src/__tests__/renderer-frame-transaction.integration.test.ts +923 -5
  482. package/src/__tests__/shader-manifest-fixture.ts +45 -0
  483. package/src/__tests__/skin-motion-regression.unit.test.ts +0 -33
  484. package/src/__tests__/skin-palette-persistent.integration.test.ts +28 -0
  485. package/src/__tests__/skin-palette-persistent.unit.test.ts +247 -0
  486. package/src/__tests__/skinned-shadow-caster.test.ts +125 -14
  487. package/src/__tests__/specular-environment-scale.dawn.test.ts +158 -0
  488. package/src/__tests__/ssao-config.unit.test.ts +7 -0
  489. package/src/__tests__/ssao-surface.dawn.test.ts +252 -0
  490. package/src/__tests__/ssr-admission-matrix.integration.test.ts +7 -1
  491. package/src/__tests__/ssr-confidence-compose.unit.test.ts +84 -0
  492. package/src/__tests__/ssr-coordinate-oracle.unit.test.ts +28 -0
  493. package/src/__tests__/ssr-cube-silhouette.dawn.test.ts +270 -0
  494. package/src/__tests__/ssr-format-probe-gating.integration.test.ts +82 -0
  495. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +99 -0
  496. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +116 -0
  497. package/src/__tests__/ssr-gpu-dispatch.ts +951 -0
  498. package/src/__tests__/ssr-graph-topology.unit.test.ts +104 -0
  499. package/src/__tests__/ssr-history-filter.dawn.test.ts +198 -0
  500. package/src/__tests__/ssr-hit-color.dawn.test.ts +273 -0
  501. package/src/__tests__/ssr-hiz-odd-reduction.dawn.test.ts +569 -0
  502. package/src/__tests__/ssr-hiz-rescue.dawn.test.ts +279 -0
  503. package/src/__tests__/ssr-hiz.browser.test.ts +150 -0
  504. package/src/__tests__/ssr-hiz.dawn.test.ts +150 -0
  505. package/src/__tests__/ssr-hiz.unit.test.ts +56 -0
  506. package/src/__tests__/ssr-inspection.unit.test.ts +105 -0
  507. package/src/__tests__/ssr-memory-budget.unit.test.ts +45 -0
  508. package/src/__tests__/ssr-performance-sequence-aggregation.unit.test.ts +15 -0
  509. package/src/__tests__/ssr-public-surface.test-d.ts +37 -0
  510. package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +387 -0
  511. package/src/__tests__/ssr-reflection-mip-reducer.dawn.test.ts +309 -0
  512. package/src/__tests__/ssr-reprojection-probe.ts +90 -0
  513. package/src/__tests__/ssr-roughness-mip.dawn.test.ts +163 -0
  514. package/src/__tests__/ssr-shader-prewarm.unit.test.ts +59 -0
  515. package/src/__tests__/ssr-spatial.integration.test.ts +141 -0
  516. package/src/__tests__/ssr-stable-lattice.dawn.test.ts +475 -0
  517. package/src/__tests__/ssr-temporal-history.unit.test.ts +202 -0
  518. package/src/__tests__/ssr-texel-crossing.dawn.test.ts +448 -0
  519. package/src/__tests__/ssr-zero-work.integration.test.ts +87 -0
  520. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +84 -12
  521. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +29 -0
  522. package/src/__tests__/standard-output-chain-contract.unit.test.ts +99 -0
  523. package/src/__tests__/standard-output-chain.integration.test.ts +73 -0
  524. package/src/__tests__/standard-output-gpu-evidence.ts +205 -0
  525. package/src/__tests__/standard-output.browser.test.ts +15 -0
  526. package/src/__tests__/standard-output.dawn.test.ts +15 -0
  527. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +275 -0
  528. package/src/__tests__/standard-pipeline.integration.test.ts +320 -3
  529. package/src/__tests__/standard-profile-oracle.unit.test.ts +49 -0
  530. package/src/__tests__/standard-surface-lighting.unit.test.ts +41 -0
  531. package/src/__tests__/standard-texture-specialization.dawn.test.ts +141 -0
  532. package/src/__tests__/surface-adjacent-regressions.integration.test.ts +6 -3
  533. package/src/__tests__/taa-coverage-edge.dawn.test.ts +199 -0
  534. package/src/__tests__/taa-history-clipping.dawn.test.ts +328 -0
  535. package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +782 -0
  536. package/src/__tests__/taa-stability-feedback.browser.test.ts +23 -0
  537. package/src/__tests__/taa-stability-feedback.dawn.test.ts +68 -0
  538. package/src/__tests__/taa-stability-feedback.ts +342 -0
  539. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +49 -7
  540. package/src/__tests__/temporal-reset-matrix.unit.test.ts +9 -0
  541. package/src/__tests__/temporal-transaction.integration.test.ts +28 -0
  542. package/src/__tests__/temporal-view.unit.test.ts +17 -0
  543. package/src/__tests__/texture-usage-surface.unit.test.ts +4 -2
  544. package/src/__tests__/transmission-prewarm.unit.test.ts +57 -0
  545. package/src/__tests__/typed-pipeline-topology.unit.test.ts +109 -4
  546. package/src/__tests__/typed-scene-pass-material-cache.integration.test.ts +126 -0
  547. package/src/__tests__/vertex-layouts-ssot.unit.test.ts +47 -0
  548. package/src/__tests__/visibility-extract.perf.test.ts +20 -8
  549. package/src/__tests__/visibility-instances.integration.test.ts +19 -7
  550. package/src/__tests__/visibility-producer-matrix.integration.test.ts +20 -9
  551. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +62 -13
  552. package/src/__tests__/volumetric-fog-topology.unit.test.ts +7 -7
  553. package/src/assembly/atmosphere-shader-prewarm.ts +39 -0
  554. package/src/assembly/device-feature-admission.ts +9 -4
  555. package/src/assembly/dynamic-geometry-host.ts +284 -163
  556. package/src/assembly/dynamic-geometry-runtime.ts +71 -0
  557. package/src/assembly/factory.ts +4 -0
  558. package/src/assembly/gpu-driven-owner.ts +29 -0
  559. package/src/assembly/host-contract.ts +19 -0
  560. package/src/assembly/material/assembly.ts +383 -1
  561. package/src/assembly/material/pipeline-helpers.ts +100 -0
  562. package/src/assembly/material-shader-policy.ts +110 -46
  563. package/src/assembly/recovery/device-loss-fanout.ts +5 -0
  564. package/src/assembly/recovery/failure-location.ts +62 -0
  565. package/src/assembly/recovery/recovery-attempt.ts +2 -29
  566. package/src/assembly/recovery/renderer-recover.ts +16 -17
  567. package/src/assembly/renderer-facade.ts +34 -1
  568. package/src/assembly/renderer-feature-inspection.ts +13 -0
  569. package/src/assembly/renderer-frame-transaction.ts +63 -0
  570. package/src/assembly/renderer-helpers.ts +26 -0
  571. package/src/assembly/renderer-inspection.ts +6 -0
  572. package/src/assembly/shader-prewarm-policy.ts +46 -1
  573. package/src/assembly/skin-palette-owner.ts +21 -0
  574. package/src/assembly/ssr-shader-prewarm.ts +41 -0
  575. package/src/assembly/webgpu-device-loss.ts +75 -0
  576. package/src/assembly/webgpu-pbr-ready.ts +232 -0
  577. package/src/assembly/webgpu-ready-mipmap.ts +26 -0
  578. package/src/assembly/webgpu-ready.ts +161 -131
  579. package/src/assembly/webgpu-renderer-bootstrap.ts +85 -0
  580. package/src/assembly/webgpu-renderer-contract.ts +69 -0
  581. package/src/assembly/webgpu-renderer-guards.ts +32 -0
  582. package/src/assembly/webgpu-renderer-observation.ts +43 -0
  583. package/src/assembly/webgpu-renderer-recovery-failure.ts +62 -0
  584. package/src/assembly/webgpu-renderer.ts +744 -730
  585. package/src/assembly/webgpu-vertex-layouts.ts +233 -87
  586. package/src/capture/scene-projection.ts +37 -0
  587. package/src/components/__tests__/camera-exposure.contract.unit.test.ts +153 -0
  588. package/src/components/__tests__/dynamic-resolution-schema.unit.test.ts +65 -0
  589. package/src/components/__tests__/light-radius.integration.test.ts +59 -0
  590. package/src/components/camera.ts +287 -7
  591. package/src/components/directional-light.ts +3 -3
  592. package/src/components/dynamic-resolution.ts +67 -0
  593. package/src/components/index.ts +2 -0
  594. package/src/components/instances.ts +45 -18
  595. package/src/components/light-helpers.ts +6 -1
  596. package/src/components/reflection-probe.ts +1 -0
  597. package/src/components/scene-instance.ts +2 -1
  598. package/src/components/screen-space-reflection.ts +21 -0
  599. package/src/components/skylight.ts +4 -1
  600. package/src/components/sprite-animation.ts +2 -2
  601. package/src/components/sprite-instances.ts +1 -1
  602. package/src/device/device-scope.ts +13 -7
  603. package/src/device/gpu-residency.ts +45 -42
  604. package/src/dynamic-geometry.ts +13 -8
  605. package/src/environment/background.ts +281 -0
  606. package/src/environment/ibl.ts +211 -0
  607. package/src/environment/storage.ts +63 -0
  608. package/src/errors/gpu-driven.ts +49 -8
  609. package/src/errors/render.ts +91 -3
  610. package/src/extract/camera.ts +37 -1
  611. package/src/extract/contracts.ts +10 -0
  612. package/src/extract/gpu-driven.ts +240 -10
  613. package/src/features/host.ts +237 -19
  614. package/src/features/noise-texture.ts +91 -0
  615. package/src/features/plan.ts +553 -24
  616. package/src/features/prepared-gpu-work.ts +631 -49
  617. package/src/features/prepared-graphics-store.ts +31 -0
  618. package/src/features/prepared-graphics.ts +5 -0
  619. package/src/features/render-graph-compute.ts +41 -8
  620. package/src/features/render-graph-contribution.ts +141 -3
  621. package/src/features/render-graph-raster.ts +143 -30
  622. package/src/features/types.ts +11 -0
  623. package/src/fullscreen-post-process-pass.ts +3 -0
  624. package/src/gpu-driven/batch-topology.ts +452 -108
  625. package/src/gpu-driven/material-artifacts.ts +179 -0
  626. package/src/gpu-driven/material-bindings.ts +41 -0
  627. package/src/gpu-driven/pbr-program.ts +31 -0
  628. package/src/gpu-driven/prepared-draw.ts +42 -6
  629. package/src/gpu-driven/production-raster.ts +2806 -716
  630. package/src/gpu-driven/shadow-views.ts +539 -0
  631. package/src/gpu-driven/view-gpu.ts +188 -41
  632. package/src/gpu-resource.ts +21 -2
  633. package/src/gpu-scene-schema.ts +61 -11
  634. package/src/gpu-scene.ts +197 -99
  635. package/src/gpu-texture-usage.ts +1 -0
  636. package/src/hdrp-buffers.ts +141 -36
  637. package/src/ibl/IblPipelineCache.ts +5 -0
  638. package/src/ibl/frame-resources.ts +38 -0
  639. package/src/ibl/skylight-bind-group.ts +32 -21
  640. package/src/index.ts +194 -75
  641. package/src/inspection-types.ts +207 -1
  642. package/src/instances-derived-bounds.ts +258 -34
  643. package/src/instances.ts +459 -150
  644. package/src/internal.ts +22 -0
  645. package/src/lifecycle.ts +2 -0
  646. package/src/material-row.ts +194 -0
  647. package/src/materials.ts +44 -8
  648. package/src/pbr-pipeline.ts +172 -19
  649. package/src/pipeline/render-extent.ts +111 -0
  650. package/src/pipeline/standard-build-context.ts +7 -0
  651. package/src/pipeline/standard-forward-lane.ts +74 -29
  652. package/src/pipeline/standard-output/__tests__/lut-admission.unit.test.ts +103 -0
  653. package/src/pipeline/standard-output/auto-exposure/__tests__/capability.unit.test.ts +40 -0
  654. package/src/pipeline/standard-output/auto-exposure/__tests__/gpu.contract.unit.test.ts +125 -0
  655. package/src/pipeline/standard-output/auto-exposure/__tests__/graph.contract.unit.test.ts +94 -0
  656. package/src/pipeline/standard-output/auto-exposure/__tests__/oracle.test.ts +108 -0
  657. package/src/pipeline/standard-output/auto-exposure/__tests__/preset.test.ts +26 -0
  658. package/src/pipeline/standard-output/auto-exposure/capability.ts +38 -0
  659. package/src/pipeline/standard-output/auto-exposure/gpu.ts +567 -0
  660. package/src/pipeline/standard-output/auto-exposure/graph.ts +221 -0
  661. package/src/pipeline/standard-output/auto-exposure/inspection.ts +231 -0
  662. package/src/pipeline/standard-output/auto-exposure/oracle.ts +291 -0
  663. package/src/pipeline/standard-output/auto-exposure/preset.ts +28 -0
  664. package/src/pipeline/standard-output/auto-exposure/state.ts +156 -0
  665. package/src/pipeline/standard-output/color-transform.ts +124 -0
  666. package/src/pipeline/standard-output/graph.ts +139 -0
  667. package/src/pipeline/standard-output/lut-admission.ts +121 -0
  668. package/src/pipeline/standard-output/lut-gpu.ts +399 -0
  669. package/src/pipeline/standard-output/lut-state.ts +176 -0
  670. package/src/pipeline/standard-output/resources.ts +121 -0
  671. package/src/pipeline/standard-output/types.ts +58 -0
  672. package/src/pipeline/standard-pipeline.ts +232 -70
  673. package/src/pipeline/standard-post.ts +190 -27
  674. package/src/pipeline/standard-profile.ts +2 -1
  675. package/src/pipeline-builder.ts +28 -9
  676. package/src/pipeline-spec-types.ts +4 -0
  677. package/src/pipeline-spec.ts +37 -29
  678. package/src/plugin.ts +4 -0
  679. package/src/points-lines/standard-owner.ts +6 -14
  680. package/src/post-process-errors.ts +26 -18
  681. package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +2 -2
  682. package/src/prepare/extended-lighting/resources.ts +3 -3
  683. package/src/prepare/prepared-graphics-resolver.ts +9 -2
  684. package/src/record/__tests__/main-pass-geometry.unit.test.ts +68 -0
  685. package/src/record/__tests__/material-bg-cache.unit.test.ts +45 -0
  686. package/src/record/__tests__/material-slot-plan.unit.test.ts +57 -0
  687. package/src/record/__tests__/mesh-ssbo.unit.test.ts +40 -1
  688. package/src/record/__tests__/recovery-pipeline.unit.test.ts +20 -1
  689. package/src/record/__tests__/sprite-material-abi.unit.test.ts +41 -6
  690. package/src/record/__tests__/temporal-payload.unit.test.ts +2 -0
  691. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +45 -0
  692. package/src/record/__tests__/view-ubo-layout.unit.test.ts +5 -0
  693. package/src/record/dynamic-geometry-consumption.ts +59 -0
  694. package/src/record/frame-lighting.ts +15 -12
  695. package/src/record/frame-snapshot.ts +146 -34
  696. package/src/record/frame-targets.ts +9 -0
  697. package/src/record/frame.ts +838 -170
  698. package/src/record/main-pass-geometry.ts +209 -103
  699. package/src/record/main-pass-material.ts +157 -40
  700. package/src/record/main-pass-sprite-draws.ts +16 -107
  701. package/src/record/main-pass.ts +141 -12
  702. package/src/record/material-uniforms.ts +1 -1
  703. package/src/record/mesh-ssbo.ts +59 -15
  704. package/src/record/prepared-material-bindings.ts +238 -0
  705. package/src/record/recovery-pipeline.ts +69 -90
  706. package/src/record/render-context.ts +123 -24
  707. package/src/record/shadow-pass.ts +1389 -230
  708. package/src/record/skybox-post-pass.ts +14 -3
  709. package/src/record/typed-frame-graph.ts +1064 -47
  710. package/src/record/view-ubo.ts +12 -1
  711. package/src/recovery/render-system-candidate.ts +135 -68
  712. package/src/recovery/render-system-roots.ts +113 -0
  713. package/src/recovery/types.ts +77 -0
  714. package/src/reflection/background.ts +151 -0
  715. package/src/reflection/filter.ts +3 -0
  716. package/src/reflection/projection.ts +16 -6
  717. package/src/reflection/record-owner.ts +308 -140
  718. package/src/render-contract.ts +237 -49
  719. package/src/render-graph-primitives.ts +34 -5
  720. package/src/render-pipeline.ts +90 -2
  721. package/src/render-system-extract-tail.ts +431 -162
  722. package/src/render-system-extract.ts +478 -119
  723. package/src/render-system-projections.ts +241 -0
  724. package/src/render-system.ts +715 -828
  725. package/src/scene/render-scene-types.ts +13 -4
  726. package/src/scene/render-scene.ts +577 -355
  727. package/src/scene/shadow-visibility.ts +21 -0
  728. package/src/ssao-config.ts +25 -8
  729. package/src/ssr/admission.ts +380 -14
  730. package/src/ssr/compose.ts +144 -0
  731. package/src/ssr/errors.ts +48 -0
  732. package/src/ssr/graph.ts +797 -0
  733. package/src/ssr/history.ts +436 -0
  734. package/src/ssr/hiz.ts +124 -0
  735. package/src/ssr/inspection.ts +124 -0
  736. package/src/ssr/resources.ts +202 -0
  737. package/src/ssr/temporal.ts +164 -0
  738. package/src/systems/skin-palette-allocator.ts +495 -148
  739. package/src/systems/skin-palette-types.ts +17 -0
  740. package/src/systems/transparent-dispatch.ts +90 -0
  741. package/src/targets/material-source.ts +34 -0
  742. package/src/temporal/coverage.ts +1 -1
  743. package/src/temporal/frame-coordinator.ts +23 -2
  744. package/src/temporal/gpu.ts +79 -12
  745. package/src/temporal/history.ts +3 -1
  746. package/src/temporal/inspection.ts +2 -1
  747. package/src/temporal/standard-scene-data.ts +10 -2
  748. package/src/temporal/target-coverage-attachment.ts +72 -0
  749. package/src/temporal/temporal-view.ts +11 -2
  750. package/src/temporal/view.ts +12 -2
  751. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +219 -139
  752. package/src/typed-render-graph-primitives.ts +304 -74
  753. package/src/typed-shadow-passes.ts +122 -12
  754. package/src/volume/component.ts +1 -0
  755. package/src/volume/passes.ts +3 -3
  756. package/src/volume/temporal.ts +10 -0
  757. package/dist/chunk-GE3SDD72.mjs.map +0 -1
  758. package/dist/chunk-GF523LHF.mjs.map +0 -1
  759. package/dist/chunk-IQMKJLNU.mjs.map +0 -1
  760. package/dist/chunk-J52NMQKR.mjs.map +0 -1
  761. package/dist/chunk-J6KACNOH.mjs.map +0 -1
  762. package/dist/chunk-JAASCMXA.mjs.map +0 -1
  763. package/dist/chunk-MBU4ZVE7.mjs.map +0 -1
  764. package/dist/chunk-MYJ5RPJO.mjs.map +0 -1
  765. package/dist/chunk-RBDBZ5R7.mjs.map +0 -1
  766. package/dist/chunk-VSL23LZE.mjs.map +0 -1
  767. package/src/__tests__/instances-world-ownership.integration.test.ts +0 -62
  768. package/src/scene/__tests__/changed-block-summary.candidate.test.ts +0 -36
@@ -1,3 +1,5 @@
1
+ import { projectCaptureScene } from './capture/scene-projection';
2
+ import type { RenderSceneBounds } from './scene/render-scene-types';
1
3
  // @forgeax/engine-runtime - RenderSystem main entry (D-S2 three-stage Extract ->
2
4
  // Prepare -> Record + 4-tier error fan-out).
3
5
  //
@@ -16,31 +18,17 @@
16
18
  // .invert` (charter proposition 5: no math reinvention; render-system.test.ts
17
19
  // asserts `/@forgeax\/engine-math/` shows up in render-system.ts source).
18
20
 
19
- import {
20
- type MipmapEncoderWork,
21
- numMipLevels,
22
- resolveAssetHandle,
23
- } from '@forgeax/engine-assets-runtime';
24
- import type { World } from '@forgeax/engine-ecs';
21
+ import { numMipLevels, resolveAssetHandle } from '@forgeax/engine-assets-runtime';
22
+ import { Time, type World } from '@forgeax/engine-ecs';
25
23
  import type { RenderReadLease } from '@forgeax/engine-ecs/projection';
26
24
  import type { RecorderSession } from '@forgeax/engine-profiler';
27
25
  import type { CompiledRenderGraphInfo } from '@forgeax/engine-render-graph';
28
- import type {
29
- BindGroupEntry,
30
- BindGroupLayout,
31
- Buffer,
32
- RenderPipeline,
33
- RhiDevice,
34
- } from '@forgeax/engine-rhi';
26
+ import type { BindGroupLayout, Buffer, RenderPipeline, RhiDevice } from '@forgeax/engine-rhi';
35
27
  import { err, ok, type Result, RhiError } from '@forgeax/engine-rhi';
36
28
  import {
37
- derive,
38
29
  deriveTextureLayout,
39
- type Handle,
40
30
  type MaterialAsset,
41
- type MaterialTextureValue,
42
31
  type RenderPipelineAsset,
43
- RenderQueue,
44
32
  type SamplerAsset,
45
33
  type TextureAsset,
46
34
  toShared,
@@ -50,9 +38,8 @@ import {
50
38
  directionalShadowProfileFromQuality,
51
39
  projectDirectionalShadowInspectionSource,
52
40
  } from './assembly/directional-shadow-inspection';
41
+ import { createGpuDrivenOwner } from './assembly/gpu-driven-owner';
53
42
  import { createClusterBinScratch } from './cluster-binner';
54
- import type { DeviceScope, LifecycleResourceSpec } from './device/device-scope';
55
- import type { EnvironmentGeneration } from './environment/generation';
56
43
  import { EnvironmentLifecycle } from './environment/lifecycle';
57
44
  import {
58
45
  type ObservationUnavailableError,
@@ -60,11 +47,11 @@ import {
60
47
  TransmissionCapabilityMissingError,
61
48
  } from './errors/render';
62
49
  import {
63
- type RenderFeatureHost,
64
50
  type RenderFeaturePreparedGraphicsResolverInput,
65
51
  runRenderFeatureFrame,
66
52
  settlePreparedGraphicsCompletion,
67
53
  } from './features/host';
54
+ import { createFeatureNoiseResolver } from './features/noise-texture';
68
55
  import {
69
56
  createRenderFeatureGpuWorkOwner,
70
57
  type RenderFeatureGpuWorkOwner,
@@ -79,22 +66,45 @@ import {
79
66
  postProcessShaderModuleLabel,
80
67
  postProcessShaderPipelineLabel,
81
68
  } from './fullscreen-post-process-pass';
82
- import { GpuDrivenProduction } from './gpu-driven/production-raster';
83
69
  import type {
84
70
  BloomInspection,
85
71
  DirectionalShadowInspection,
86
72
  GpuDrivenProductionInspection,
87
73
  LodOcclusionInspection,
88
- LodOcclusionInspectionRow,
89
- LodOcclusionWorldInspection,
74
+ MaterialTextureSourceInspection,
90
75
  MotionBlurInspection,
91
76
  ReflectionProbeInspection,
92
77
  RenderSceneInspection,
93
78
  SsrDependenciesInspection,
79
+ SsrSpatialInspection,
94
80
  TemporalTargetInspection,
95
81
  TransmissionInspection,
96
82
  } from './inspection-types';
97
83
  import { buildOcclusionProxyVertices } from './occlusion-proxy';
84
+ import {
85
+ AUTO_EXPOSURE_HISTOGRAM_BYTES,
86
+ retireAutoExposureGpuResources,
87
+ writeAutoExposureParameters,
88
+ } from './pipeline/standard-output/auto-exposure/gpu';
89
+ import {
90
+ type AutoExposureInspection,
91
+ createAutoExposureInspection,
92
+ } from './pipeline/standard-output/auto-exposure/inspection';
93
+ import {
94
+ createAutoExposureState,
95
+ resetAutoExposureState,
96
+ } from './pipeline/standard-output/auto-exposure/state';
97
+ import {
98
+ createStandardLutState,
99
+ inspectStandardLutState,
100
+ prepareStandardLutCandidate,
101
+ resetStandardLutState,
102
+ type StandardLutInspection,
103
+ } from './pipeline/standard-output/lut-state';
104
+ import {
105
+ prepareStandardOutputResources,
106
+ resetStandardOutputForDeviceLoss,
107
+ } from './pipeline/standard-output/resources';
98
108
  import type { PointsLinesRetainedSnapshot } from './points-lines/snapshot';
99
109
  import { StandardPointsLinesOwner } from './points-lines/standard-owner';
100
110
  import {
@@ -102,6 +112,35 @@ import {
102
112
  materialSamplerHandles,
103
113
  materialTextureHandles,
104
114
  } from './recovery/render-system-candidate';
115
+
116
+ export { materialTextureHandlesForResidency } from './recovery/render-system-candidate';
117
+
118
+ import {
119
+ createDynamicGeometryFrameBindings,
120
+ resetDynamicGeometryTemporalHistory,
121
+ } from './record/dynamic-geometry-consumption';
122
+ import type {
123
+ RecoveryGraphCandidate,
124
+ RecoveryGraphCandidatePreparation,
125
+ RecoveryGraphCandidateRuntime,
126
+ RecoveryGraphSetupSubmission,
127
+ RecoveryPostProcessResources,
128
+ RecoveryRootBundle,
129
+ RecoveryRootRuntime,
130
+ } from './recovery/types';
131
+
132
+ export type {
133
+ RecoveryGraphCandidate,
134
+ RecoveryGraphCandidatePreparation,
135
+ RecoveryGraphCandidateRuntime,
136
+ RecoveryGraphSetupSubmission,
137
+ RecoveryPointsLinesCandidate,
138
+ RecoveryPostProcessResources,
139
+ RecoveryRootBundle,
140
+ RecoveryRootRuntime,
141
+ } from './recovery/types';
142
+
143
+ import { prepareRenderSystemRecoveryRoots } from './recovery/render-system-roots';
105
144
  import { ReflectionProbeRecordOwner } from './reflection/record-owner';
106
145
  import {
107
146
  OcclusionRenderRuntime,
@@ -113,18 +152,18 @@ import { inspectVolumetricFog } from './volume/inspection';
113
152
  export type { RenderSceneInspection } from './inspection-types';
114
153
 
115
154
  import { createCubeCaptureScheduler } from './capture/scheduler';
116
- import type { GpuResidencyCache } from './device/gpu-residency';
117
155
  import type { EnvironmentInspection } from './environment/inspection';
156
+ import { gpuDrivenShadowDrawKey } from './extract/gpu-driven';
118
157
  import { GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_UNIFORM } from './gpu-usage';
119
158
  import { resetHdrpBuffers } from './hdrp-buffers';
120
- import { assembleMaterialWithSkylightEntries } from './ibl/skylight-bind-group';
159
+ import type { SkylightBindGroupResources } from './ibl/skylight-bind-group';
121
160
  import {
122
161
  disposeInstanceBufferChunks,
123
162
  disposeInstanceBuffers,
124
163
  disposeTransientInstanceBuffers,
125
164
  } from './instance-buffer-cache';
126
165
  import type { InstanceCollectionInspection } from './instances';
127
- import { InstanceProjectionStore } from './instances';
166
+ import { InstanceCollectionStore } from './instances';
128
167
  import {
129
168
  type MeshMaterialBindingObservation,
130
169
  type MeshMaterialBindingPreparationFailure,
@@ -163,12 +202,18 @@ import type {
163
202
  GpuPassTimingSession,
164
203
  } from './record/gpu-pass-timing/session';
165
204
  import type { GpuTimingCapture } from './record/gpu-timing';
166
- import { applyParamSnapshotToUbo, residentTextureView } from './record/main-pass-material';
167
- import type { RecoveryPipelineReadiness } from './record/recovery-pipeline';
205
+ import { resolveMaterialSkylight } from './record/main-pass-material';
206
+ import {
207
+ createPreparedResolverCaches,
208
+ type PreparedResolverCaches,
209
+ preparedMaterialBindings,
210
+ } from './record/prepared-material-bindings';
168
211
  import type { PipelineState, RenderSystemInternals } from './record/render-context';
169
212
  import type { CubeCaptureGraphState } from './record/typed-frame-graph';
170
213
  import {
214
+ getRenderFeatureGraphInspection,
171
215
  type RenderFeatureGraphCandidate,
216
+ recordRenderFeatureCandidate,
172
217
  resetRenderFeatureGraphState,
173
218
  settleVolumetricFogGraphCandidate,
174
219
  } from './record/typed-frame-graph';
@@ -182,27 +227,27 @@ import {
182
227
  } from './render-contract';
183
228
  import type {
184
229
  DispatchEntry,
185
- ExtractedFrame,
186
230
  ExtractedLights,
187
231
  MaterialSnapshot,
188
232
  RenderableSnapshot,
233
+ ShadowCasterMembership,
189
234
  } from './render-system-extract';
190
235
  import { extractFrames } from './render-system-extract';
191
236
  import {
192
- type PersistentGpuDrivenCandidate,
193
- type PersistentRenderCandidateRequest,
194
- PersistentRenderScene,
195
- } from './scene/render-scene';
237
+ canonicalizeWorldComposition,
238
+ createLodWorldInspections,
239
+ } from './render-system-projections';
240
+ import { PersistentRenderScene } from './scene/render-scene';
196
241
  import { inspectLodOcclusion } from './scene/visibility/inspection';
197
242
  import { resolveSsaoParameters } from './ssao-config';
198
- import { projectSsrDependencies, resolveSsrAdmissionGeneration } from './ssr/admission';
199
243
  import {
200
- getTransparentSortConfig,
201
- TRANSPARENT_SORT_MODE_DISTANCE,
202
- TRANSPARENT_SORT_MODE_LAYER_Y,
203
- TRANSPARENT_SORT_MODE_LAYER_YZ,
204
- TRANSPARENT_SORT_MODE_LAYER_Z,
205
- } from './systems/transparent-sort-config';
244
+ projectSsrDependencies,
245
+ resolveSsrAdmissionGeneration,
246
+ type SsrSpatialAdmission,
247
+ zeroSsrAdmissionWork,
248
+ } from './ssr/admission';
249
+ import { projectSsrSpatialInspection } from './ssr/inspection';
250
+ import { sortTransparentDispatch } from './systems/transparent-dispatch';
206
251
  import type { RenderTarget } from './targets/contracts';
207
252
  import { createTemporalFrameTransaction, type TemporalFrame } from './temporal/frame';
208
253
  import { retireTemporalGpuState } from './temporal/gpu';
@@ -219,147 +264,71 @@ import {
219
264
  inspectTransmissionFromAdmission,
220
265
  } from './transmission/inspection';
221
266
 
222
- function canonicalizeWorldComposition(
267
+ const WORLD_ENTITY_KEY_STRIDE = 4294967296;
268
+
269
+ /**
270
+ * Extract emits world/entity keys in the transient `worlds[]` index domain,
271
+ * while PersistentRenderScene owns stable renderer-local World keys for the
272
+ * retained GPU scene. Rebase ShadowCaster ownership at this orchestration
273
+ * boundary before both GPU projection and CPU residual recording consume it.
274
+ */
275
+ function rebaseShadowCasterOwnership(
223
276
  worlds: readonly World[],
224
- owners: Pick<DrawOwnerOptions, 'cameraOwner' | 'resourceOwner'>,
225
- leases: readonly RenderReadLease[] | undefined,
277
+ stableWorldKeys: readonly number[],
278
+ entityKeys: ReadonlySet<number>,
279
+ drawKeys: ReadonlySet<string>,
280
+ membership: readonly ShadowCasterMembership[] | undefined,
226
281
  ): {
227
- readonly worlds: readonly World[];
228
- readonly owners: Pick<DrawOwnerOptions, 'cameraOwner' | 'resourceOwner'>;
229
- readonly leases: readonly RenderReadLease[] | undefined;
282
+ readonly entityKeys: ReadonlySet<number>;
283
+ readonly drawKeys: ReadonlySet<string>;
284
+ readonly membership: readonly ShadowCasterMembership[] | undefined;
230
285
  } {
231
- const order = worlds
232
- .map((world, index) => ({ world, index }))
233
- .sort((left, right) => left.world.identity.localeCompare(right.world.identity));
234
- const indexByOriginal = new Map(order.map((entry, index) => [entry.index, index]));
235
- return {
236
- worlds: Object.freeze(order.map((entry) => entry.world)),
237
- owners: {
238
- cameraOwner: indexByOriginal.get(owners.cameraOwner) ?? owners.cameraOwner,
239
- resourceOwner: indexByOriginal.get(owners.resourceOwner) ?? owners.resourceOwner,
240
- },
241
- leases:
242
- leases === undefined
243
- ? undefined
244
- : Object.freeze(
245
- order.map((entry) => leases[entry.index]).filter((lease) => lease !== undefined),
246
- ),
286
+ const rebaseWorldEntity = (composite: number): number => {
287
+ const transientWorldId = Math.floor(composite / WORLD_ENTITY_KEY_STRIDE);
288
+ if (
289
+ !Number.isInteger(transientWorldId) ||
290
+ transientWorldId < 0 ||
291
+ transientWorldId >= worlds.length
292
+ ) {
293
+ return composite;
294
+ }
295
+ const entityKey = composite - transientWorldId * WORLD_ENTITY_KEY_STRIDE;
296
+ return worldEntityKey(stableWorldKeys[transientWorldId] ?? transientWorldId, entityKey);
247
297
  };
248
- }
249
298
 
250
- interface LodInspectionQuerySummary {
251
- readonly used: number;
252
- readonly capacity: number;
253
- }
254
-
255
- function createLodWorldInspections(
256
- worlds: readonly World[],
257
- renderables: readonly RenderableSnapshot[],
258
- submittedRenderables: readonly RenderableSnapshot[],
259
- camera: CameraSnapshot,
260
- frameId: number,
261
- worldKeys: readonly number[],
262
- slots: ReadonlyMap<number, { readonly slot: number; readonly generation: number }>,
263
- query: LodInspectionQuerySummary,
264
- fallback: LodOcclusionInspection['fallback'],
265
- degradation: LodOcclusionInspection['degradation'],
266
- ): readonly LodOcclusionWorldInspection[] {
267
- // The CPU projection knows which renderables belong to each World, but it
268
- // does not receive the GPU selector/query counters split by World from the
269
- // same submit. Keep these rows available for diagnostics while explicitly
270
- // marking their attribution unavailable; producers must not promote them
271
- // to World-reorder evidence by inferring facts from array order.
272
- const attribution = Object.freeze({
273
- status: 'unavailable' as const,
274
- reason: 'projection-only' as const,
275
- });
276
- const submitted = new Set(submittedRenderables);
277
- const sampleLimit = 64;
278
- const accumulators = worlds.map(() => ({
279
- allCount: 0,
280
- allVisible: 0,
281
- allSamples: [] as RenderableSnapshot[],
282
- lodCount: 0,
283
- lodVisible: 0,
284
- lodSamples: [] as RenderableSnapshot[],
285
- }));
286
- // Keep this projection bounded: the old implementation filtered the full
287
- // 100k renderable list twice per World on every frame and retained another
288
- // Set-backed traversal just to derive the six counters below. One pass is
289
- // enough because the inspection only needs counts, the first row identity,
290
- // and at most 64 detached samples.
291
- for (const renderable of renderables) {
292
- const accumulator = accumulators[renderable.worldId];
293
- if (accumulator === undefined) continue;
294
- const visible = submitted.has(renderable);
295
- accumulator.allCount += 1;
296
- if (visible) accumulator.allVisible += 1;
297
- if (accumulator.allSamples.length < sampleLimit) accumulator.allSamples.push(renderable);
298
- if ((renderable.lods?.length ?? 0) > 0) {
299
- accumulator.lodCount += 1;
300
- if (visible) accumulator.lodVisible += 1;
301
- if (accumulator.lodSamples.length < sampleLimit) accumulator.lodSamples.push(renderable);
302
- }
303
- }
304
- const result: LodOcclusionWorldInspection[] = [];
305
- for (let worldId = 0; worldId < worlds.length; worldId += 1) {
306
- const world = worlds[worldId];
307
- if (world === undefined) continue;
308
- const accumulator = accumulators[worldId];
309
- if (accumulator === undefined || accumulator.allCount === 0) {
310
- result.push(
311
- Object.freeze({ attachmentId: world.identity, rows: Object.freeze([]), attribution }),
312
- );
313
- continue;
314
- }
315
- const usesLod = accumulator.lodCount > 0;
316
- const candidateCount = usesLod ? accumulator.lodCount : accumulator.allCount;
317
- const visible = usesLod ? accumulator.lodVisible : accumulator.allVisible;
318
- const samples = usesLod ? accumulator.lodSamples : accumulator.allSamples;
319
- const first = samples[0];
320
- const slot =
321
- first === undefined
322
- ? undefined
323
- : slots.get(worldEntityKey(worldKeys[first.worldId] ?? first.worldId, first.entityKey));
324
- const row: LodOcclusionInspectionRow = {
325
- root: {
326
- guid: first === undefined ? 'none' : `asset-handle:${first.assetHandle}`,
327
- sourceKey: `render-world:${world.identity}`,
328
- },
329
- view: {
330
- attachmentId: world.identity,
331
- cameraEntity: world === worlds[camera.worldId ?? 0] ? (camera.entityKey ?? 0) : 0,
332
- viewRole: 'main',
333
- viewGeneration: camera.historyVersion ?? 0,
334
- },
335
- slot: { primitiveSlot: slot?.slot ?? 0, slotGeneration: slot?.generation ?? 0 },
336
- generation: frameId,
337
- count: { candidates: candidateCount, visible, occluded: candidateCount - visible },
338
- lodHistogram: [{ level: 0, count: candidateCount }],
339
- queryLatencyUs: { median: 0, p95: 0, last: 0 },
340
- pagePressure: query,
341
- fallback,
342
- degradation,
343
- samples: samples.map((candidate, index) => ({
344
- primitiveSlot:
345
- slots.get(
346
- worldEntityKey(worldKeys[candidate.worldId] ?? candidate.worldId, candidate.entityKey),
347
- )?.slot ?? index,
348
- level: 0,
349
- visible: submitted.has(candidate),
350
- })),
351
- };
352
- result.push(
353
- Object.freeze({
354
- attachmentId: world.identity,
355
- rows: Object.freeze([row]),
356
- attribution,
357
- }),
358
- );
359
- }
360
- return Object.freeze(
361
- result.sort((left, right) => left.attachmentId.localeCompare(right.attachmentId)),
362
- );
299
+ const stableMembership =
300
+ membership === undefined
301
+ ? undefined
302
+ : membership.map((entry) => ({
303
+ ...entry,
304
+ worldEntity: rebaseWorldEntity(entry.worldEntity),
305
+ }));
306
+ const stableDrawKeys =
307
+ stableMembership === undefined
308
+ ? new Set(
309
+ [...drawKeys].map((key) => {
310
+ const parts = key.split(':');
311
+ if (parts.length !== 4) return key;
312
+ const transientWorldEntity = Number(parts[0]);
313
+ if (!Number.isFinite(transientWorldEntity)) return key;
314
+ return `${rebaseWorldEntity(transientWorldEntity)}:${parts[1]}:${parts[2]}:${parts[3]}`;
315
+ }),
316
+ )
317
+ : new Set(
318
+ stableMembership.map((entry) =>
319
+ gpuDrivenShadowDrawKey(
320
+ entry.worldEntity,
321
+ entry.materialHandle,
322
+ entry.drawItemIndex,
323
+ entry.passIndex,
324
+ ),
325
+ ),
326
+ );
327
+ return {
328
+ entityKeys: new Set([...entityKeys].map(rebaseWorldEntity)),
329
+ drawKeys: stableDrawKeys,
330
+ membership: stableMembership,
331
+ };
363
332
  }
364
333
 
365
334
  type TimingSessionHost = RenderSystemInternals & {
@@ -418,12 +387,6 @@ export interface RendererProducerRoot {
418
387
  readonly disabledWork: 'zero' | 'lazy';
419
388
  }
420
389
 
421
- /** Device-bound post-process resources prepared off to the side of the active renderer. */
422
- export interface RecoveryPostProcessResources {
423
- readonly device: RhiDevice;
424
- readonly paramsBuffers: Map<string, Buffer>;
425
- }
426
-
427
390
  const PRODUCER_ROOT_MATRIX: readonly RendererProducerRoot[] = Object.freeze([
428
391
  {
429
392
  kind: 'backend-surface',
@@ -520,129 +483,6 @@ export interface RecoveryProductionEvidence {
520
483
  };
521
484
  }
522
485
 
523
- /**
524
- * Unified transparent-queue sub-sort covering all four
525
- * {@link TransparentSortConfig} modes. Reorders the
526
- * `queue === RenderQueue.Transparent` segment of the dispatch list;
527
- * all other queue segments keep their relative order.
528
- *
529
- * | mode | primary key | secondary key | tertiary key |
530
- * |:--:|:--|:--|:--|
531
- * | 0 (LAYER_Z) | `layer` ASC | `posZ` ASC | `materialHandle` ASC |
532
- * | 1 (LAYER_Y) | `layer` ASC | `-(posY - pivotY * sizeY)` ASC | `materialHandle` ASC |
533
- * | 2 (LAYER_YZ) | `layer` ASC | `(posY - pivotY * sizeY) + yzAlpha * posZ` ASC | `materialHandle` ASC |
534
- * | 3 (DISTANCE) | `-(dist² from camera)` ASC (back-to-front, layer ignored) | — |
535
- *
536
- * The `materialHandle` tertiary key for modes 0/1/2 groups same-material
537
- * entries together whenever the primary+secondary sort values are equal
538
- * (e.g. tilemap tiles in the same row/layer share `posY` in LAYER_Y mode).
539
- * Consecutive same-material groups then collapse into fold buckets in the
540
- * record-stage fold operator, significantly reducing draw call count for
541
- * tilemap-heavy scenes.
542
- *
543
- * `posX/Y/Z` = translation column of the entity's world mat4 (indices 12/13/14).
544
- * `pivotY` = `RenderableSnapshot.material.paramSnapshot.pivotAndSize[1]` (default 0.5).
545
- * `sizeY` = length of the Y-axis column of the world mat4 (indices 4/5/6).
546
- */
547
- function sortTransparentDispatch(
548
- dispatch: DispatchEntry[],
549
- world: World,
550
- cameras: readonly CameraSnapshot[],
551
- renderables: readonly RenderableSnapshot[],
552
- ): DispatchEntry[] {
553
- const cfg = getTransparentSortConfig(world);
554
- const mode = cfg.mode;
555
-
556
- // Indices of Transparent-queue entries within the dispatch list.
557
- const transparentSlots: number[] = [];
558
- for (let i = 0; i < dispatch.length; i++) {
559
- if (dispatch[i]?.queue === RenderQueue.Transparent) transparentSlots.push(i);
560
- }
561
- if (transparentSlots.length <= 1) return dispatch;
562
-
563
- let sortedSlotOrder: number[];
564
-
565
- if (mode === TRANSPARENT_SORT_MODE_DISTANCE) {
566
- const camera = cameras[0];
567
- if (camera === undefined) return dispatch;
568
- const camPos = camera.position;
569
-
570
- // Squared camera distance per entry (negated → ascending = back-to-front).
571
- // D-3: world position = translation column of GlobalTransform.world (m[12,13,14]).
572
- const negDistSq = (entry: DispatchEntry): number => {
573
- const tx = renderables[entry.renderableIndex]?.transform;
574
- if (tx === undefined) return 0;
575
- const w = tx.world;
576
- const dx = (w[12] ?? 0) - (camPos[0] ?? 0);
577
- const dy = (w[13] ?? 0) - (camPos[1] ?? 0);
578
- const dz = (w[14] ?? 0) - (camPos[2] ?? 0);
579
- return -(dx * dx + dy * dy + dz * dz);
580
- };
581
-
582
- sortedSlotOrder = transparentSlots.slice().sort((a, b) => {
583
- const da = negDistSq(dispatch[a] as DispatchEntry);
584
- const db = negDistSq(dispatch[b] as DispatchEntry);
585
- if (da < db) return -1;
586
- if (da > db) return 1;
587
- return 0;
588
- });
589
- } else {
590
- // Modes 0/1/2: primary = layer ASC, secondary = mode-formula ASC.
591
- // sizeY = length of the Y-axis column (col1 = indices 4,5,6) of the
592
- // world mat4; rotation-invariant and handles flipV sign correctly.
593
- const sortVal = (entry: DispatchEntry): number => {
594
- const tx = renderables[entry.renderableIndex]?.transform;
595
- const w = tx?.world;
596
- const posY = (w?.[13] ?? 0) as number;
597
- const posZ = (w?.[14] ?? 0) as number;
598
- if (mode === TRANSPARENT_SORT_MODE_LAYER_Z) return posZ;
599
- const mat = renderables[entry.renderableIndex]?.material;
600
- // feat-20260625-refactor-sprite-as-transparent-mesh M3 / w15: post
601
- // SpriteFieldsSnapshot ablation, pivot lives in paramSnapshot
602
- // .pivotAndSize[0..1] (UBO-aligned vec4 slot 2, plan D-6). Non-sprite
603
- // materials carry paramSnapshot=undefined and fall back to 0.5.
604
- const pivotAndSize = mat?.paramSnapshot?.pivotAndSize as readonly number[] | undefined;
605
- const pivotY = (pivotAndSize?.[1] ?? 0.5) as number;
606
- const wy4 = (w?.[4] ?? 0) as number;
607
- const wy5 = (w?.[5] ?? 1) as number;
608
- const wy6 = (w?.[6] ?? 0) as number;
609
- const sizeY = Math.sqrt(wy4 * wy4 + wy5 * wy5 + wy6 * wy6);
610
- const footY = posY - pivotY * sizeY;
611
- if (mode === TRANSPARENT_SORT_MODE_LAYER_Y) return -footY;
612
- if (mode === TRANSPARENT_SORT_MODE_LAYER_YZ) return footY + cfg.yzAlpha * posZ;
613
- // Defensive fallback for an unknown mode that slips past setTransparentSortConfig.
614
- return posZ;
615
- };
616
-
617
- sortedSlotOrder = transparentSlots.slice().sort((a, b) => {
618
- const da = dispatch[a] as DispatchEntry;
619
- const db = dispatch[b] as DispatchEntry;
620
- const la = da.layer;
621
- const lb = db.layer;
622
- if (la !== lb) return la - lb;
623
- const va = sortVal(da);
624
- const vb = sortVal(db);
625
- if (va < vb) return -1;
626
- if (va > vb) return 1;
627
- // Tertiary tiebreaker: group same-materialHandle entries together so
628
- // fold-eligible consecutive runs form. Entries at equal (layer,
629
- // sortVal) — e.g. tilemap tiles in the same row under LAYER_Y — are
630
- // depth-equivalent; reordering them by material does not change the
631
- // visual result but maximises fold-bucket width.
632
- return da.materialHandle - db.materialHandle;
633
- });
634
- }
635
-
636
- // Scatter the reordered Transparent entries back into their original slots.
637
- const result = dispatch.slice();
638
- for (let k = 0; k < transparentSlots.length; k++) {
639
- const targetSlot = transparentSlots[k] as number;
640
- const sourceSlot = sortedSlotOrder[k] as number;
641
- result[targetSlot] = dispatch[sourceSlot] as DispatchEntry;
642
- }
643
- return result;
644
- }
645
-
646
486
  /**
647
487
  * Engine-internal Extract / Prepare / Record driver; constructed by createRenderer.
648
488
  *
@@ -660,6 +500,9 @@ function sortTransparentDispatch(
660
500
  * whose shader identity is `forgeax::default-standard-pbr`).
661
501
  */
662
502
  export interface RenderSystem {
503
+ bounds(world: World, entity: number): RenderSceneBounds | undefined;
504
+ /** Complete explicit SSR capability preparation before drawing a device generation. */
505
+ initializeSsr(device?: RhiDevice): Promise<void>;
663
506
  /** Returns true only when this invocation reached queue submission. */
664
507
  draw(
665
508
  worlds: readonly World[],
@@ -677,7 +520,7 @@ export interface RenderSystem {
677
520
  /** Prepare a detached graph candidate without entering the frame record path. */
678
521
  prepareRecoveryGraphCandidate(
679
522
  runtime: RecoveryGraphCandidateRuntime,
680
- ): Promise<RecoveryGraphCandidatePreparation>;
523
+ ): RecoveryGraphCandidatePreparation;
681
524
  /** Finish and submit detached candidate setup, never a frame receipt. */
682
525
  submitCandidateSetup(
683
526
  candidate: RecoveryGraphCandidate,
@@ -703,6 +546,8 @@ export interface RenderSystem {
703
546
  readonly pipelineDispatchCounts: {
704
547
  readonly unlit: number;
705
548
  };
549
+ /** Renderer-owned source for explicit large instance collections. */
550
+ readonly instances: InstanceCollectionStore;
706
551
  /** Detached residency and upload facts for explicit instance collections. */
707
552
  readonly instanceCollectionsInspection: readonly InstanceCollectionInspection[];
708
553
  /**
@@ -717,11 +562,17 @@ export interface RenderSystem {
717
562
  readonly reflectionProbes: ReflectionProbeInspection;
718
563
  /** Live producer/RHI/temporal receipts projected through SSR admission. */
719
564
  readonly ssrDependencies: SsrDependenciesInspection;
565
+ /** Live SSR spatial admission and bounded graph/history projection. */
566
+ readonly ssr: SsrSpatialInspection;
720
567
  /** Producer completion fence included in the public FrameReceipt. */
721
568
  readonly reflectionFallbackCompletion: Promise<void> | undefined;
722
569
  /** True while a progressive CubeCamera candidate still owns unfinished faces. */
723
570
  isCubeCapturePending(target: RenderTarget): boolean;
724
571
  readonly environment: EnvironmentInspection;
572
+ /** Detached auto-exposure state published after a successful frame submit. */
573
+ readonly autoExposure: AutoExposureInspection | undefined;
574
+ /** Detached Standard LUT state published after a successful frame submit. */
575
+ readonly standardLut: StandardLutInspection;
725
576
  readonly temporal: TemporalInspection;
726
577
  readonly bloom: BloomInspection;
727
578
  readonly motionBlurInspection: MotionBlurInspection | undefined;
@@ -756,6 +607,10 @@ export interface RenderSystem {
756
607
  readonly lastSuccessfulCameraAntialias: CameraSnapshot['antialias'] | undefined;
757
608
  /** Detached compiled-graph facts used by the renderer inspection owner. */
758
609
  readonly perFrameGraphInfo: CompiledRenderGraphInfo | undefined;
610
+ /** Detached feature signature/compile/lifetime counters. */
611
+ readonly featureGraphInspection: import('./inspection-types').RenderFeatureGraphInspection;
612
+ /** Detached material texture-source probe counters from the last extract. */
613
+ readonly materialTextureSources: MaterialTextureSourceInspection;
759
614
  /** Production-owner recovery facts used by Renderer.inspect(). */
760
615
  readonly recoveryEvidence: RecoveryProductionEvidence;
761
616
  /**
@@ -878,13 +733,6 @@ function makePreparedPipelinePendingError(): RhiError {
878
733
  });
879
734
  }
880
735
 
881
- /** The single Points/Lines owner’s staged device-generation handoff. */
882
- export interface RecoveryPointsLinesCandidate {
883
- createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown>;
884
- publish(): void;
885
- release(): void;
886
- }
887
-
888
736
  function preparationFailureFrom(error: unknown): MeshMaterialBindingPreparationFailure | undefined {
889
737
  if (typeof error !== 'object' || error === null) return undefined;
890
738
  const value = error as {
@@ -912,58 +760,6 @@ function preparationFailureFrom(error: unknown): MeshMaterialBindingPreparationF
912
760
  };
913
761
  }
914
762
 
915
- /** Candidate-only device/runtime inputs used by the recovery prepare seam. */
916
- export interface RecoveryGraphCandidateRuntime {
917
- readonly internals: RenderSystemInternals;
918
- readonly pipelineState: PipelineState;
919
- }
920
-
921
- /** Detached graph state prepared on a replacement device, before publication. */
922
- export interface RecoveryGraphCandidate {
923
- readonly frameState: RenderFrameState;
924
- readonly setupWorks: readonly MipmapEncoderWork[];
925
- readonly device: RhiDevice;
926
- readonly generation: number;
927
- readonly gpuDrivenProduction?: GpuDrivenProduction;
928
- readonly gpuDrivenScene?: PersistentGpuDrivenCandidate;
929
- /** Staged CPU-expanded Points/Lines buffers owned by the same RenderSystem. */
930
- readonly pointsLines?: RecoveryPointsLinesCandidate;
931
- readonly featureHost?: RenderFeatureHost;
932
- readonly featureGpuWork?: RenderFeatureGpuWorkOwner;
933
- readonly featureGraphCandidate?: RenderFeatureGraphCandidate;
934
- readonly postProcessPipelines?: ReadonlyMap<string, RenderPipeline>;
935
- readonly recoveryReadiness: RecoveryPipelineReadiness;
936
- /** Release all candidate roots before publication; safe to call repeatedly. */
937
- readonly release: () => void;
938
- /** Transfer candidate-root ownership to the published RenderSystem. */
939
- readonly markPublished: () => void;
940
- }
941
-
942
- /** Candidate setup completion; completion is bounded by the recovery owner. */
943
- export interface RecoveryGraphSetupSubmission {
944
- readonly completion?: PromiseLike<unknown>;
945
- }
946
-
947
- export type RecoveryGraphCandidatePreparation =
948
- | { readonly kind: 'no-seed' }
949
- | { readonly kind: 'ready'; readonly candidate: RecoveryGraphCandidate }
950
- | { readonly kind: 'failed'; readonly reason: string; readonly cause?: unknown };
951
-
952
- /** Explicit candidate-owned inputs for lifecycle-root preparation. */
953
- export interface RecoveryRootRuntime {
954
- readonly scope: DeviceScope;
955
- readonly device: RhiDevice;
956
- readonly gpuStore: GpuResidencyCache;
957
- readonly graphCandidate?: RecoveryGraphCandidate | undefined;
958
- }
959
-
960
- /** Candidate roots plus the one synchronous owner switch at publication. */
961
- export interface RecoveryRootBundle {
962
- readonly roots: readonly LifecycleResourceSpec<unknown>[];
963
- publish(): void;
964
- discard(): void;
965
- }
966
-
967
763
  /**
968
764
  * Project the producer-owned material and GPU store state into the one
969
765
  * renderer observation surface. This is intentionally recomputed from the
@@ -1088,42 +884,37 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
1088
884
  let releaseProfilerCatalog =
1089
885
  phaseCatalogRegistration?.ok === true ? phaseCatalogRegistration.value : undefined;
1090
886
  let preparedWorlds: readonly World[] = [];
1091
- let lastSubmittedDynamicGeometryBindings = new WeakMap<World, ReadonlyMap<number, number>>();
887
+ const resolveFeatureNoise = createFeatureNoiseResolver({
888
+ scope: internals.deviceScope,
889
+ getDevice: () => internals.device,
890
+ onError: (error) => internals.errorRegistry.fire(error),
891
+ });
892
+ let preparedFeatureSkylightResources: SkylightBindGroupResources | undefined;
893
+ const dynamicGeometryFrames = createDynamicGeometryFrameBindings();
1092
894
  let latestCamera: CameraSnapshot | undefined;
1093
895
  const pointsLinesOwner = new StandardPointsLinesOwner(internals);
1094
- const instanceCollections = new InstanceProjectionStore();
896
+ const instanceCollections = new InstanceCollectionStore();
1095
897
  const reflectionProbeOwner = new ReflectionProbeRecordOwner(internals);
1096
- let ssrFormatReceipt: import('@forgeax/engine-rhi').RhiTextureFormatCapabilityReceipt | undefined;
1097
- let ssrFormatProbeGeneration = -1;
1098
- let ssrFormatProbe: Promise<void> | undefined;
898
+ const ssrFormatReceipts = new WeakMap<
899
+ RhiDevice,
900
+ import('@forgeax/engine-rhi').RhiTextureFormatCapabilityReceipt
901
+ >();
1099
902
  let ssrFallbackGeneration: number | undefined;
1100
- const ensureSsrFormatProbe = (): void => {
1101
- if (internals.ssrIdentity === undefined) return;
1102
- const generation = internals.deviceScope.generation;
903
+ const ensureSsrFormatProbe = async (device = internals.device): Promise<void> => {
1103
904
  if (
1104
- ssrFormatReceipt?.deviceGeneration === generation ||
1105
- ssrFormatProbeGeneration === generation ||
1106
- ssrFormatProbe !== undefined ||
1107
- typeof internals.device.probeTextureFormatCapability !== 'function'
1108
- ) {
905
+ internals.ssrIdentity === undefined ||
906
+ typeof device.probeTextureFormatCapability !== 'function'
907
+ )
1109
908
  return;
909
+ // RHI memoizes the probe per physical device. Retain the detached candidate
910
+ // receipt without publishing it: inspection selects only the active device.
911
+ // A failed/late recovery therefore cannot overwrite the active generation.
912
+ try {
913
+ const result = await device.probeTextureFormatCapability();
914
+ if (result.ok) ssrFormatReceipts.set(device, result.value);
915
+ } catch {
916
+ // Missing capability remains an explicit fallback-only admission.
1110
917
  }
1111
- ssrFormatProbeGeneration = generation;
1112
- const device = internals.device;
1113
- ssrFormatProbe = device
1114
- .probeTextureFormatCapability()
1115
- .then((result) => {
1116
- if (internals.device !== device || internals.deviceScope.generation !== generation) return;
1117
- ssrFormatReceipt = result.ok ? result.value : undefined;
1118
- })
1119
- .catch(() => {
1120
- if (internals.device === device && internals.deviceScope.generation === generation) {
1121
- ssrFormatReceipt = undefined;
1122
- }
1123
- })
1124
- .finally(() => {
1125
- ssrFormatProbe = undefined;
1126
- });
1127
918
  };
1128
919
  const cubeCaptureScheduler = createCubeCaptureScheduler({ maxFacesPerFrame: 1 });
1129
920
  const cubeCaptureState: CubeCaptureGraphState = { work: [] };
@@ -1134,7 +925,6 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
1134
925
  internals.gpuStore.invalidateMesh(handle, preparedWorlds[worldId] ?? worldId);
1135
926
  },
1136
927
  instanceCollections,
1137
- getSkinPaletteAllocator: () => internals.getPipelineState()?.skinPaletteAllocator ?? null,
1138
928
  });
1139
929
  let environmentLifecycle = new EnvironmentLifecycle(internals.deviceScope);
1140
930
  const transmissionAdmission = new TransmissionCandidateAdmission();
@@ -1278,7 +1068,12 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
1278
1068
  }),
1279
1069
  ),
1280
1070
  } satisfies import('./pipeline-builder').PipelineBuilderShaderModuleFactory);
1281
- let gpuDrivenProduction = GpuDrivenProduction.forDevice(internals.device, gpuDrivenShaderFactory);
1071
+ let gpuDrivenRecoveryCount = 0;
1072
+ let gpuDrivenProduction = createGpuDrivenOwner(
1073
+ internals.device,
1074
+ gpuDrivenShaderFactory,
1075
+ gpuDrivenRecoveryCount,
1076
+ );
1282
1077
  let occlusionRuntime: OcclusionRenderRuntime | undefined = new OcclusionRenderRuntime(
1283
1078
  internals.device,
1284
1079
  persistentRenderScene.visibilityFacetStore(),
@@ -1346,6 +1141,13 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
1346
1141
  const frameState: RenderFrameState = {
1347
1142
  frameNumber: 0,
1348
1143
  reflectionFallbackDemand: false,
1144
+ ssrRequested: false,
1145
+ ssrSpatialAdmission: undefined,
1146
+ ssrHistoryOwner: undefined,
1147
+ ssrHistoryCandidate: undefined,
1148
+ ssrTemporalParamsPayload: new Uint8Array(32),
1149
+ ssrLastCameraEntity: undefined,
1150
+ ssrLastHistoryVersion: undefined,
1349
1151
  graphGeneration: 0,
1350
1152
  successfulTemporalFrameIndex: 0,
1351
1153
  lastSuccessfulBloom: 'off',
@@ -1376,6 +1178,14 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
1376
1178
  reflectionFallbackObservationSource: undefined,
1377
1179
  lastSuccessfulCameraAntialias: undefined,
1378
1180
  temporalGpuState: undefined,
1181
+ autoExposureGpuResources: undefined,
1182
+ pendingAutoExposureGpuResources: undefined,
1183
+ autoExposureState: undefined,
1184
+ pendingAutoExposureState: undefined,
1185
+ standardLutGpuResources: undefined,
1186
+ pendingStandardLutGpuResources: undefined,
1187
+ standardLutState: createStandardLutState({ deviceEpoch: internals.deviceScope.generation }),
1188
+ pendingStandardLutState: undefined,
1379
1189
  activeTemporalGpuState: undefined,
1380
1190
  retiringTemporalGpuStates: new Set(),
1381
1191
  lastSuccessfulTemporalView: undefined,
@@ -1385,7 +1195,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
1385
1195
  currentDirectionalShadowView: null,
1386
1196
  currentSpotShadowView: null,
1387
1197
  instanceBuffers: new Map(),
1388
- instanceResidency: new Map(),
1198
+ instanceCollections,
1389
1199
  instanceBufferChunks: new Map(),
1390
1200
  probeBlendBuffers: new Map(),
1391
1201
  probeBlendRecordBufferCapacity: 0,
@@ -1437,7 +1247,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
1437
1247
  // explicit texture/sampler handles resolved to resident GPU resources.
1438
1248
  materialBgAssemblyCache: new Map(),
1439
1249
  // singleton material cache (flat Map<variant, BindGroup>; D-6).
1440
- singletonMaterialCache: new Map(),
1250
+ shadowMaterialBindGroups: new WeakMap(),
1441
1251
  // post-process bind group cache (bloom / fxaa / ssao): identity-keyed
1442
1252
  // WeakMap chain so resize-retired transient targets rebuild automatically.
1443
1253
  postProcessBgCache: new WeakMap(),
@@ -1468,6 +1278,25 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
1468
1278
  // tiles until the first frame with a castShadow spot (AC-03).
1469
1279
  spotShadowSnapshots: [],
1470
1280
  };
1281
+ const resolveSsrDependencies = (requested: boolean): SsrDependenciesInspection => {
1282
+ // Explicit SSR capability preparation belongs to initialization. Inspection
1283
+ // only projects receipts and never starts work or creates image history.
1284
+ const owner = reflectionProbeOwner.inspect();
1285
+ const temporal =
1286
+ frameState.lastSuccessfulTemporalView === undefined
1287
+ ? undefined
1288
+ : ({
1289
+ successfulSubmit: true,
1290
+ generation: frameState.lastSuccessfulTemporalView.input.deviceGeneration,
1291
+ } as const);
1292
+ return projectSsrDependencies({
1293
+ requested,
1294
+ identity: internals.ssrIdentity,
1295
+ reflectionFallback: owner.reflectionFallback,
1296
+ format: ssrFormatReceipts.get(internals.device),
1297
+ temporal,
1298
+ });
1299
+ };
1471
1300
  let directFrameId = 0;
1472
1301
  let submittedFrameCount = 0;
1473
1302
  let lastSubmittedGeneration: number | undefined;
@@ -1475,6 +1304,122 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
1475
1304
  let lastDirectionalShadowCandidate: 'accepted' | 'failed' = 'failed';
1476
1305
  let lastDirectionalShadowError: DirectionalShadowInspectionSource['error'];
1477
1306
 
1307
+ /**
1308
+ * Stage the detached auto/LUT facts beside the GPU candidates already owned
1309
+ * by RenderFrameState. The typed graph promotes both records only after its
1310
+ * single finish/submit transaction succeeds; no side registry or compile
1311
+ * callback can publish a value early.
1312
+ */
1313
+ function stageLiveFeatureState(camera: CameraSnapshot | undefined, deltaTime: number): void {
1314
+ frameState.pendingAutoExposureState = undefined;
1315
+ frameState.pendingStandardLutState = undefined;
1316
+ const output = camera?.output;
1317
+ if (output === undefined) return;
1318
+
1319
+ const publicFrameId =
1320
+ (internals as RenderSystemInternals & { readonly observationFrameId?: number })
1321
+ .observationFrameId ?? frameState.frameNumber;
1322
+ const targetGeneration = Math.max(1, camera?.historyVersion ?? 0);
1323
+ const deviceEpoch = internals.deviceScope.generation;
1324
+
1325
+ if (
1326
+ output.exposure.kind === 'auto' &&
1327
+ (frameState.pendingAutoExposureGpuResources !== undefined ||
1328
+ frameState.autoExposureGpuResources !== undefined)
1329
+ ) {
1330
+ let state = frameState.autoExposureState;
1331
+ if (state === undefined || state.fallback !== output.exposure.fallback) {
1332
+ const created = createAutoExposureState({
1333
+ fallback: output.exposure.fallback,
1334
+ targetGeneration,
1335
+ deviceEpoch,
1336
+ frameId: publicFrameId,
1337
+ });
1338
+ if (!created.ok) {
1339
+ internals.errorRegistry.fire(created.error);
1340
+ return;
1341
+ }
1342
+ state = created.value;
1343
+ } else if (state.targetGeneration !== targetGeneration || state.deviceEpoch !== deviceEpoch) {
1344
+ state = resetAutoExposureState(state, 'camera-change', {
1345
+ targetGeneration,
1346
+ deviceEpoch,
1347
+ });
1348
+ }
1349
+ const resources =
1350
+ frameState.pendingAutoExposureGpuResources ?? frameState.autoExposureGpuResources;
1351
+ if (resources !== undefined) {
1352
+ const parameterWrite = writeAutoExposureParameters(resources, {
1353
+ compensationEv: output.exposure.compensationEv,
1354
+ rangeMinEv: output.exposure.rangeEv[0],
1355
+ rangeMaxEv: output.exposure.rangeEv[1],
1356
+ upRate: output.exposure.rates[0],
1357
+ downRate: output.exposure.rates[1],
1358
+ deltaTime,
1359
+ fallback: output.exposure.fallback,
1360
+ generation: state.targetGeneration,
1361
+ });
1362
+ if (!parameterWrite.ok) {
1363
+ internals.errorRegistry.fire(parameterWrite.error as RhiError);
1364
+ return;
1365
+ }
1366
+ }
1367
+ // The GPU adapt pass owns the numeric candidate. CPU staging carries
1368
+ // only the generation/transaction facts needed for submit publication.
1369
+ frameState.pendingAutoExposureState = Object.freeze({
1370
+ state,
1371
+ generation: state.targetGeneration,
1372
+ deviceEpoch: state.deviceEpoch,
1373
+ frameId: publicFrameId,
1374
+ });
1375
+ }
1376
+
1377
+ let lutState = frameState.standardLutState;
1378
+ if (lutState.targetGeneration !== targetGeneration || lutState.deviceEpoch !== deviceEpoch) {
1379
+ lutState = resetStandardLutState(lutState, 'device-recovered', {
1380
+ targetGeneration,
1381
+ deviceEpoch,
1382
+ });
1383
+ frameState.standardLutState = lutState;
1384
+ }
1385
+ const wantsLut = output.colorLutStrength > 0 && output.colorLut > 0;
1386
+ const lutResources =
1387
+ frameState.pendingStandardLutGpuResources ?? frameState.standardLutGpuResources;
1388
+ if (wantsLut && lutResources !== undefined) {
1389
+ const prepared = prepareStandardLutCandidate(lutState, {
1390
+ // sourceKey is the Catalog-owned identity returned by the same
1391
+ // preparation that built the live LUT bind group; numeric handles do
1392
+ // not cross the inspection boundary.
1393
+ resident: lutResources.sourceKey,
1394
+ sourceKey: lutResources.sourceKey,
1395
+ generation: lutState.targetGeneration,
1396
+ deviceEpoch: lutState.deviceEpoch,
1397
+ frameId: publicFrameId,
1398
+ });
1399
+ if (prepared.ok) {
1400
+ frameState.pendingStandardLutState = Object.freeze({
1401
+ state: lutState,
1402
+ candidate: prepared.value,
1403
+ remove: false,
1404
+ targetGeneration,
1405
+ deviceEpoch,
1406
+ });
1407
+ }
1408
+ } else if (!wantsLut && lutState.resident !== null) {
1409
+ frameState.pendingStandardLutState = Object.freeze({
1410
+ state: lutState,
1411
+ remove: true,
1412
+ targetGeneration,
1413
+ deviceEpoch,
1414
+ });
1415
+ }
1416
+ }
1417
+
1418
+ function discardLiveFeatureState(): void {
1419
+ frameState.pendingAutoExposureState = undefined;
1420
+ frameState.pendingStandardLutState = undefined;
1421
+ }
1422
+
1478
1423
  function beginProfilePhase(session: RecorderSession | undefined, phase: RenderPhase): boolean {
1479
1424
  if (session === undefined) return false;
1480
1425
  try {
@@ -1706,22 +1651,21 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
1706
1651
  const getPostProcessPipeline = (
1707
1652
  id: string,
1708
1653
  bgl: BindGroupLayout,
1709
- colorFormat: GPUTextureFormat,
1654
+ colorFormats: readonly GPUTextureFormat[],
1710
1655
  entryOverride?: PostProcessShaderEntry,
1711
1656
  ): RenderPipeline | null => {
1712
1657
  const entry = entryOverride ?? lookupPostProcess(id);
1713
1658
  if (entry === undefined) return null;
1714
1659
  const signature = postProcessShaderEntrySignature(entry);
1715
- const key = `${id}|${colorFormat}|${signature}`;
1660
+ const key = `${id}|${colorFormats.join(',')}|${signature}`;
1716
1661
  const cached = postProcessPipelineCache.get(key);
1717
1662
  if (cached !== undefined) return cached;
1718
- internals.recoveryColdWorkGuard?.notePipelineColdWork();
1719
1663
  const factory = internals.buildPostProcessPipeline;
1720
1664
  if (factory === undefined) return null;
1721
1665
  const built = factory(
1722
1666
  entry,
1723
1667
  bgl,
1724
- colorFormat,
1668
+ colorFormats,
1725
1669
  postProcessShaderPipelineLabel(id, entry.source),
1726
1670
  );
1727
1671
  if (built === null) return null;
@@ -1892,193 +1836,18 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
1892
1836
  createBindGroup: 0,
1893
1837
  keys: [],
1894
1838
  };
1895
- type PreparedResolverCaches = {
1896
- readonly preparedPipelineIds: WeakMap<object, string>;
1897
- readonly preparedMaterialPipelineShaders: WeakMap<object, string>;
1898
- readonly preparedGroup0Pipelines: WeakSet<object>;
1899
- readonly preparedViewOnlyPipelines: WeakSet<object>;
1900
- readonly preparedRenderMaterialPipelines: WeakSet<object>;
1901
- readonly preparedAssetHandles: WeakMap<World, Map<string, number>>;
1902
- };
1903
- const createPreparedResolverCaches = (): PreparedResolverCaches => ({
1904
- preparedPipelineIds: new WeakMap(),
1905
- preparedMaterialPipelineShaders: new WeakMap(),
1906
- preparedGroup0Pipelines: new WeakSet(),
1907
- preparedViewOnlyPipelines: new WeakSet(),
1908
- preparedRenderMaterialPipelines: new WeakSet(),
1909
- preparedAssetHandles: new WeakMap(),
1910
- });
1911
1839
  const activePreparedResolverCaches = createPreparedResolverCaches();
1912
- const preparedHandle = <Brand extends string>(
1913
- runtime: RenderSystemInternals,
1914
- caches: PreparedResolverCaches,
1915
- world: World,
1916
- guid: string,
1917
- brand: Brand,
1918
- ): Handle<Brand, 'shared'> | undefined => {
1919
- let handles = caches.preparedAssetHandles.get(world);
1920
- if (handles === undefined) {
1921
- handles = new Map();
1922
- caches.preparedAssetHandles.set(world, handles);
1923
- }
1924
- const key = `${brand}:${guid.toLowerCase()}`;
1925
- const cached = handles.get(key);
1926
- if (cached !== undefined) return cached as Handle<Brand, 'shared'>;
1927
- const asset = runtime.assets.lookup(guid);
1928
- if (asset === undefined) return undefined;
1929
- const handle = world.allocSharedRef(brand, asset);
1930
- handles.set(key, handle as number);
1931
- return handle;
1932
- };
1933
- const preparedMaterialBindings = (
1934
- runtime: RenderSystemInternals,
1935
- caches: PreparedResolverCaches,
1936
- worlds: readonly World[],
1937
- materialShaderId: string,
1938
- worldIndex: number,
1939
- materialGuid: string,
1940
- layout: BindGroupLayout,
1941
- ) => {
1942
- const world = worlds[worldIndex];
1943
- const material = runtime.assets.lookup(materialGuid) as MaterialAsset | undefined;
1944
- const pipelineState = runtime.getPipelineState();
1945
- if (world === undefined || material?.kind !== 'material' || pipelineState === null) {
1946
- return err(new Error('prepared material asset is unavailable'));
1947
- }
1948
- const schema = runtime.getParamSchema?.(materialShaderId) ?? [];
1949
- const values = material.values ?? {};
1950
- const derived = derive(schema);
1951
- const fields = [...derived.textureFieldNames];
1952
- let buffer: Buffer | undefined;
1953
- let payloadByteLength = 0;
1954
- if (derived.uboLayout.totalBytes > 0) {
1955
- const payload = new Uint8Array(Math.max(16, derived.uboLayout.totalBytes));
1956
- applyParamSnapshotToUbo(
1957
- payload,
1958
- schema,
1959
- values as Readonly<Record<string, number | readonly number[]>>,
1960
- );
1961
- const created = runtime.device.createBuffer({
1962
- label: `prepared-material:${materialGuid}`,
1963
- size: payload.byteLength,
1964
- usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
1965
- });
1966
- if (!created.ok) return created;
1967
- buffer = created.value;
1968
- payloadByteLength = payload.byteLength;
1969
- const written = runtime.device.queue.writeBuffer(buffer, 0, payload);
1970
- if (!written.ok) {
1971
- runtime.device.destroyBuffer(buffer);
1972
- return written;
1973
- }
1974
- }
1975
- const textureResources: Array<{
1976
- sampler: import('@forgeax/engine-rhi').Sampler;
1977
- view: import('@forgeax/engine-rhi').TextureView;
1978
- }> = [];
1979
- for (let index = 0; index < fields.length; index += 1) {
1980
- const field = fields[index];
1981
- const value = field === undefined ? undefined : values[field];
1982
- const textureValue =
1983
- typeof value === 'object' && value !== null && 'texture' in value
1984
- ? (value as MaterialTextureValue)
1985
- : undefined;
1986
- let sampler = pipelineState.defaultSampler;
1987
- if (textureValue?.sampler !== undefined) {
1988
- const handle = preparedHandle(
1989
- runtime,
1990
- caches,
1991
- world,
1992
- String(textureValue.sampler),
1993
- 'SamplerAsset',
1994
- );
1995
- if (handle !== undefined) {
1996
- const asset = resolveAssetHandle<SamplerAsset>(world, handle);
1997
- if (asset.ok) {
1998
- const resident = runtime.gpuStore.ensureSamplerResident(handle, asset.value, world);
1999
- if (resident.ok) sampler = resident.value;
2000
- }
2001
- }
2002
- }
2003
- let view = pipelineState.defaultWhiteTextureView;
2004
- if (textureValue !== undefined) {
2005
- const handle = preparedHandle(
2006
- runtime,
2007
- caches,
2008
- world,
2009
- String(textureValue.texture),
2010
- 'TextureAsset',
2011
- );
2012
- if (handle !== undefined) {
2013
- view = residentTextureView(world, runtime.gpuStore, runtime, handle) ?? view;
2014
- }
2015
- }
2016
- textureResources.push({ sampler, view });
2017
- }
2018
- const entries: BindGroupEntry[] = [];
2019
- let textureIndex = 0;
2020
- for (let index = 0; index < derived.bglEntries.length; index += 1) {
2021
- const expected = derived.bglEntries[index];
2022
- if (expected?.buffer?.type === 'uniform' && buffer !== undefined) {
2023
- entries.push({
2024
- binding: expected.binding,
2025
- resource: { kind: 'buffer', value: { buffer, size: payloadByteLength } },
2026
- });
2027
- continue;
2028
- }
2029
- const textureResource = textureResources[textureIndex];
2030
- if (
2031
- expected?.sampler?.type === 'filtering' &&
2032
- derived.bglEntries[index + 1]?.texture !== undefined &&
2033
- textureResource !== undefined
2034
- ) {
2035
- entries.push({
2036
- binding: expected.binding,
2037
- resource: { kind: 'sampler', value: textureResource.sampler },
2038
- });
2039
- continue;
2040
- }
2041
- if (expected?.texture !== undefined && textureResource !== undefined) {
2042
- entries.push({
2043
- binding: expected.binding,
2044
- resource: { kind: 'textureView', value: textureResource.view },
2045
- });
2046
- textureIndex += 1;
2047
- continue;
2048
- }
2049
- if (buffer !== undefined) runtime.device.destroyBuffer(buffer);
2050
- return err(new Error('prepared material schema contains unsupported binding kinds'));
2051
- }
2052
- const fallback = pipelineState.skylightFallback;
2053
- if (fallback === null) {
2054
- if (buffer !== undefined) runtime.device.destroyBuffer(buffer);
2055
- return err(new Error('prepared material skylight fallback is unavailable'));
2056
- }
2057
- const completeEntries = assembleMaterialWithSkylightEntries(entries, {
2058
- irradianceView: fallback.irradianceView,
2059
- irradianceSampler: fallback.sampler,
2060
- prefilterView: fallback.prefilterView,
2061
- prefilterSampler: fallback.sampler,
2062
- brdfLutView: fallback.brdfLutView,
2063
- brdfLutSampler: fallback.sampler,
2064
- intensityBuffer: fallback.intensityBuffer,
2065
- });
2066
- const group = runtime.device.createBindGroup({ layout, entries: completeEntries });
2067
- if (!group.ok) {
2068
- if (buffer !== undefined) runtime.device.destroyBuffer(buffer);
2069
- return group;
2070
- }
2071
- if (buffer === undefined) return group;
2072
- return ok({
2073
- handle: group.value,
2074
- dynamicOffsets: [0, 0],
2075
- release: () => runtime.device.destroyBuffer(buffer),
2076
- });
2077
- };
2078
1840
  const lastFrustumStats: { culled: number; total: number } = { culled: 0, total: 0 };
2079
1841
  const lastVisibilityStats: { explicitlyHidden: number } = { explicitlyHidden: 0 };
1842
+ let lastMaterialTextureSources: MaterialTextureSourceInspection = {
1843
+ sourceFieldsVisited: 0,
1844
+ numericSharedRefProbes: 0,
1845
+ sourceCacheHits: 0,
1846
+ sourceCacheMisses: 0,
1847
+ producerRoutes: Object.freeze({}),
1848
+ };
2080
1849
  let lastMeshMaterialBindings: readonly MeshMaterialBindingObservation[] = [];
2081
- let lastMeshMaterialBindingFrame: ExtractedFrame | undefined;
1850
+ let lastMeshMaterialBindingRenderables: readonly RenderableSnapshot[] | undefined;
2082
1851
  const createPreparedResolverFactory =
2083
1852
  (
2084
1853
  runtime: RenderSystemInternals,
@@ -2115,7 +1884,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2115
1884
  const pipeline = runtime.getPostProcessPipeline?.(
2116
1885
  descriptor.shader,
2117
1886
  fullscreen.bindGroupLayout,
2118
- descriptor.colorFormats[0] as GPUTextureFormat,
1887
+ descriptor.colorFormats as readonly GPUTextureFormat[],
2119
1888
  postProcessEntry,
2120
1889
  );
2121
1890
  if (pipeline !== null && pipeline !== undefined) {
@@ -2128,14 +1897,26 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2128
1897
  ? err(makePreparedPipelinePendingError())
2129
1898
  : ok(pipeline);
2130
1899
  }
2131
- const preparedPipeline = runtime.getMaterialShaderPipeline?.(
1900
+ const clusteredParticleMesh =
1901
+ runtime.device.caps.storageBuffer &&
1902
+ (descriptor.shader === 'forgeax::vfx-render.particles.mesh' ||
1903
+ descriptor.shader === 'forgeax::vfx-render.particles.mesh-inputs');
1904
+ const requestedVariantSet = clusteredParticleMesh
1905
+ ? 'CLUSTER_FORWARD_AVAILABLE=true'
1906
+ : undefined;
1907
+ // A colorless prepared program is a depth-only pass. Vertex-only
1908
+ // shadow programs must use the depth policy; resolving them as
1909
+ // forward asks Dawn for a non-existent `fs_main` entry point.
1910
+ const preparedPassKind =
1911
+ descriptor.colorFormats.length === 0 ? 'shadow-caster' : 'forward';
1912
+ const preparedPipelineEntry = runtime.getMaterialShaderPipelineEntry?.(
2132
1913
  descriptor.shader,
2133
1914
  descriptor.colorFormats[0] === 'rgba16float',
2134
1915
  descriptor.renderState,
2135
1916
  descriptor.topology,
2136
1917
  descriptor.indexFormat,
2137
- undefined,
2138
- 'forward',
1918
+ requestedVariantSet,
1919
+ preparedPassKind,
2139
1920
  undefined,
2140
1921
  descriptor.sampleCount ?? 1,
2141
1922
  // Prepared graphics own their declared attachment format. Passing
@@ -2150,7 +1931,41 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2150
1931
  descriptor.vertexLayout,
2151
1932
  undefined,
2152
1933
  input.shaderModuleMode,
1934
+ undefined,
1935
+ undefined,
1936
+ undefined,
1937
+ undefined,
1938
+ undefined,
1939
+ descriptor.particleInputLanes,
2153
1940
  );
1941
+ const preparedPipeline =
1942
+ preparedPipelineEntry === undefined
1943
+ ? (runtime.getMaterialShaderPipeline?.(
1944
+ descriptor.shader,
1945
+ descriptor.colorFormats[0] === 'rgba16float',
1946
+ descriptor.renderState,
1947
+ descriptor.topology,
1948
+ descriptor.indexFormat,
1949
+ requestedVariantSet,
1950
+ preparedPassKind,
1951
+ undefined,
1952
+ descriptor.sampleCount ?? 1,
1953
+ descriptor.colorFormats[0] as GPUTextureFormat,
1954
+ undefined,
1955
+ descriptor.depthFormat === undefined
1956
+ ? null
1957
+ : (descriptor.depthFormat as GPUTextureFormat),
1958
+ descriptor.vertexLayout,
1959
+ undefined,
1960
+ input.shaderModuleMode,
1961
+ undefined,
1962
+ undefined,
1963
+ undefined,
1964
+ undefined,
1965
+ undefined,
1966
+ descriptor.particleInputLanes,
1967
+ ) ?? null)
1968
+ : (preparedPipelineEntry?.pipeline ?? null);
2154
1969
  // A requested material shader is an exact pipeline contract. During
2155
1970
  // async shader warmup, substituting the generic unlit pipeline can
2156
1971
  // mismatch vertex layouts and HDR attachment formats, turning a
@@ -2167,7 +1982,12 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2167
1982
  preparedRenderMaterialPipelines.add(pipeline as object);
2168
1983
  }
2169
1984
  }
2170
- return pipeline === null ? err(makePreparedPipelinePendingError()) : ok(pipeline);
1985
+ return pipeline === null
1986
+ ? err(makePreparedPipelinePendingError())
1987
+ : ok({
1988
+ handle: pipeline,
1989
+ standardLighting: preparedPipelineEntry?.group2Contract === 'cluster',
1990
+ });
2171
1991
  },
2172
1992
  resolveBindings: (descriptor, pipeline) => {
2173
1993
  const materialShaderId = preparedMaterialPipelineShaders.get(pipeline as object);
@@ -2183,6 +2003,14 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2183
2003
  if (bindingContract === 'view-and-scene-depth') {
2184
2004
  return ok(undefined);
2185
2005
  }
2006
+ if (
2007
+ preparedRenderMaterialPipelines.has(pipeline as object) &&
2008
+ descriptor.values.group === 0 &&
2009
+ descriptor.values.sceneDepth === undefined &&
2010
+ descriptor.values.sceneDepthBinding !== undefined
2011
+ ) {
2012
+ return err(new Error('prepared scene-depth pipeline requires a scene target'));
2013
+ }
2186
2014
  if (preparedGroup0Pipelines.has(pipeline as object)) {
2187
2015
  const layout =
2188
2016
  (materialShaderId === undefined
@@ -2236,6 +2064,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2236
2064
  material.world,
2237
2065
  material.guid,
2238
2066
  layout,
2067
+ preparedFeatureSkylightResources,
2239
2068
  );
2240
2069
  }
2241
2070
  return layout === undefined
@@ -2319,23 +2148,20 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2319
2148
  createPreparedResolverCaches,
2320
2149
  });
2321
2150
  return {
2151
+ instances: instanceCollections,
2152
+ initializeSsr: ensureSsrFormatProbe,
2322
2153
  get instanceCollectionsInspection(): readonly InstanceCollectionInspection[] {
2323
- return instanceCollections._inspections(
2324
- frameState.instanceResidency,
2325
- frameState.frameNumber - 1,
2326
- );
2154
+ return instanceCollections._inspections();
2327
2155
  },
2328
2156
  releaseProfilerCatalog(): void {
2329
2157
  releaseProfilerCatalog?.();
2330
2158
  releaseProfilerCatalog = undefined;
2331
2159
  },
2332
2160
  invalidateGeometryHistory(): void {
2333
- frameState.temporalFrameTransaction.reset('signature-change');
2334
- frameState.temporalFrame = undefined;
2335
- frameState.temporalFrameInput = undefined;
2336
- frameState.lastSuccessfulTemporalView = undefined;
2337
- frameState.successfulTemporalFrameIndex = 0;
2338
- frameState.pendingTemporalCommit = { kind: 'none' };
2161
+ resetDynamicGeometryTemporalHistory(frameState);
2162
+ },
2163
+ bounds(world, entity) {
2164
+ return persistentRenderScene.bounds(world, entity);
2339
2165
  },
2340
2166
  get renderScene(): RenderSceneInspection {
2341
2167
  return {
@@ -2350,32 +2176,46 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2350
2176
  return reflectionProbeOwner.inspect();
2351
2177
  },
2352
2178
  get ssrDependencies(): SsrDependenciesInspection {
2353
- ensureSsrFormatProbe();
2354
- const owner = reflectionProbeOwner.inspect();
2355
- const nextGeneration = owner.reflectionFallback.projectionGeneration;
2356
- const reset = resolveSsrAdmissionGeneration(ssrFallbackGeneration, nextGeneration);
2357
- if (reset.changed) {
2358
- frameState.temporalFrameTransaction.reset('signature-change');
2359
- frameState.temporalFrame = undefined;
2360
- frameState.temporalFrameInput = undefined;
2361
- frameState.lastSuccessfulTemporalView = undefined;
2362
- frameState.successfulTemporalFrameIndex = 0;
2363
- frameState.pendingTemporalCommit = { kind: 'none' };
2364
- }
2365
- ssrFallbackGeneration = reset.generation;
2366
- const temporal =
2367
- frameState.lastSuccessfulTemporalView === undefined
2368
- ? undefined
2369
- : ({
2370
- successfulSubmit: true,
2371
- generation: frameState.lastSuccessfulTemporalView.input.deviceGeneration,
2372
- } as const);
2373
- return projectSsrDependencies({
2374
- requested: frameState.reflectionFallbackDemand === true,
2375
- identity: internals.ssrIdentity,
2376
- reflectionFallback: owner.reflectionFallback,
2377
- format: ssrFormatReceipt,
2378
- temporal,
2179
+ return resolveSsrDependencies(frameState.ssrRequested === true);
2180
+ },
2181
+ get ssr(): SsrSpatialInspection {
2182
+ const camera = latestCamera;
2183
+ const lane = internals.standardProfile?.renderPath === 'deferred' ? 'deferred' : 'forward';
2184
+ const admission: SsrSpatialAdmission =
2185
+ frameState.ssrSpatialAdmission ??
2186
+ Object.freeze({
2187
+ status: camera?.screenSpaceReflection === undefined ? 'not-requested' : 'requested',
2188
+ lane,
2189
+ config: camera?.screenSpaceReflection,
2190
+ viewRange: camera === undefined ? 0 : camera.far - camera.near,
2191
+ work: zeroSsrAdmissionWork(),
2192
+ });
2193
+ const admitted = admission.status === 'admitted';
2194
+ const status =
2195
+ admitted && internals.ssrShaders === undefined
2196
+ ? ('structural-only' as const)
2197
+ : admission.status;
2198
+ const graph = frameState.compiledFrameGraph?.inspect();
2199
+ const passRoster =
2200
+ admitted && graph !== undefined
2201
+ ? graph.passes.filter((pass) => pass.name.startsWith('ssr-')).map((pass) => pass.name)
2202
+ : [];
2203
+ const history = frameState.ssrHistoryOwner?.inspect();
2204
+ const dependencies = resolveSsrDependencies(frameState.ssrRequested === true);
2205
+ const fallbackSource = dependencies.reflectionFallback?.source;
2206
+ return projectSsrSpatialInspection(admission, {
2207
+ status,
2208
+ ...(history === undefined
2209
+ ? {}
2210
+ : {
2211
+ history: {
2212
+ state: history.state,
2213
+ bytes: history.activeBytes + history.candidateBytes + history.retiringBytes,
2214
+ resetCount: history.resetCount,
2215
+ },
2216
+ }),
2217
+ passRoster,
2218
+ ...(fallbackSource === undefined ? {} : { fallbackSource }),
2379
2219
  });
2380
2220
  },
2381
2221
  get reflectionFallbackCompletion(): Promise<void> | undefined {
@@ -2387,6 +2227,27 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2387
2227
  get environment(): EnvironmentInspection {
2388
2228
  return environmentLifecycle.inspect();
2389
2229
  },
2230
+ get autoExposure(): AutoExposureInspection | undefined {
2231
+ const camera = latestCamera;
2232
+ const state = frameState.autoExposureState;
2233
+ if (camera?.output?.exposure.kind !== 'auto' || state === undefined) return undefined;
2234
+ const actual = state.acceptedEv > 0 ? state.acceptedEv : null;
2235
+ return createAutoExposureInspection({
2236
+ requested: camera.output.exposure,
2237
+ actual,
2238
+ actualState:
2239
+ actual === null ? (state.receipt.committed ? 'gpu-resident' : 'fallback') : 'accepted',
2240
+ fallback: state.fallback,
2241
+ lastKnownGood: state.lastKnownGood,
2242
+ targetGeneration: state.targetGeneration,
2243
+ reset: state.reset,
2244
+ cost: { histogramBytes: AUTO_EXPOSURE_HISTOGRAM_BYTES, passCount: 3, physicalPassCount: 1 },
2245
+ receipt: state.receipt,
2246
+ });
2247
+ },
2248
+ get standardLut(): StandardLutInspection {
2249
+ return inspectStandardLutState(frameState.standardLutState);
2250
+ },
2390
2251
  get temporal(): TemporalInspection {
2391
2252
  return inspectTemporal(frameState.lastSuccessfulTemporalView, undefined, {
2392
2253
  ...(frameState.activeTemporalGpuState === undefined
@@ -2434,8 +2295,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2434
2295
  if (producer === undefined || target === undefined) {
2435
2296
  return undefined;
2436
2297
  }
2437
- const width = Math.max(1, internals.canvas.width);
2438
- const height = Math.max(1, internals.canvas.height);
2298
+ const descriptor = target.descriptor.kind === 'texture' ? target.descriptor : undefined;
2299
+ const width = descriptor?.width ?? Math.max(1, internals.canvas.width);
2300
+ const height = descriptor?.height ?? Math.max(1, internals.canvas.height);
2439
2301
  return Object.freeze({
2440
2302
  identity: 'standard-scene-temporal',
2441
2303
  producerId: 'forgeax::standard::scene-data',
@@ -2526,6 +2388,14 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2526
2388
  return persistentRenderScene.pointsLinesSnapshots();
2527
2389
  },
2528
2390
  detachScene(world: World): void {
2391
+ const worldId = preparedWorlds.indexOf(world);
2392
+ const allocator = internals.getPipelineState()?.skinPaletteAllocator;
2393
+ if (allocator !== undefined && allocator !== null) {
2394
+ for (const slot of persistentRenderScene.compositionSlots()) {
2395
+ if (slot.worldId === worldId && slot.snapshot.skin !== undefined)
2396
+ allocator.releasePersistentSlice(slot.snapshot.skin.identity);
2397
+ }
2398
+ }
2529
2399
  persistentRenderScene.detach(world);
2530
2400
  },
2531
2401
  draw(
@@ -2543,8 +2413,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2543
2413
  let submitted = false;
2544
2414
  // A failed draw must never leave a previous frame eligible to publish a
2545
2415
  // newly accepted dynamic-geometry candidate.
2546
- lastSubmittedDynamicGeometryBindings = new WeakMap();
2547
- const frameConsumedDynamicGeometryBindings = new WeakMap<World, Map<number, number>>();
2416
+ dynamicGeometryFrames.begin();
2548
2417
  if (profileSession !== undefined && opts.profileFrame === undefined) {
2549
2418
  try {
2550
2419
  ownsProfileFrame = profileSession.beginFrame(++directFrameId).ok;
@@ -2553,11 +2422,12 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2553
2422
  }
2554
2423
  }
2555
2424
  try {
2425
+ internals.recoveryColdWorkGuard?.beginFrame();
2556
2426
  pointsLinesOwner.beginFrame();
2557
2427
  // cameraOwner drives the surfaced cameras + frustum
2558
2428
  // cull; resourceOwner drives skylight/skybox/postProcess + per-world
2559
2429
  // record config.
2560
- const { cameraOwner, resourceOwner } = composition.owners;
2430
+ const { cameraOwner, resourceOwner, cameraEntityKey } = composition.owners;
2561
2431
  // Keep the compiled graph as last-known-good until the candidate topology
2562
2432
  // compiles. The failed candidate frame does not execute it; successful
2563
2433
  // replacement retires it atomically in ensureCompiledFrameGraph.
@@ -2571,37 +2441,20 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2571
2441
  }
2572
2442
  dispatchCounts.unlit = 0;
2573
2443
 
2574
- // feat-20260708-composited-multi-world-rendering M3 / D-2 / m3-i2:
2575
- // extractFrames merges per-world snapshots (renderables + lights from
2576
- // every world, cameras + singleton resources from the owner world),
2577
- // runs read-only extractFrame per world over the already-updated World,
2578
- // resets frame state once, and isolates
2579
- // per-world errors (AC-09).
2580
- // Single-world draw([world], { cameraOwner: 0, resourceOwner: 0 }) is the identity path
2581
- // (worldId=0), byte-for-byte equivalent to the pre-M3 direct
2582
- // extractFrame path (AC-03 regression guarantee).
2583
- //
2584
- // Entry validation (empty worlds / owner out of range) is enforced by
2585
- // the public renderer.draw facade (createRenderer.ts, D-5) before this
2586
- // internal method is reached; the owner world is guaranteed present.
2587
- //
2588
- // feat-20260709-editor-world-partition M1 / w5+w6 (D-3): per-world
2589
- // record configuration (transparent-sort mode, fold buckets, skybox /
2590
- // asset resolution) is read from the resource-owner world — the world
2591
- // that owns skylight / skybox / postProcessParams. w6 sources it from
2592
- // the dedicated resourceOwner index. The variable is named
2593
- // `resourceWorld` so the record-stage reads are self-describing (they
2594
- // are resource-owner reads, not camera reads).
2595
2444
  const resourceWorld = compositionWorlds[resourceOwner] as World;
2596
2445
  const frame = runProfiledRenderPhase(profileSession, 'extract', () => {
2597
2446
  return persistentRenderScene.extractComposition(
2598
2447
  compositionWorlds,
2599
2448
  { cameraOwner, resourceOwner },
2600
2449
  internals.assets.catalogEpoch,
2601
- (request: PersistentRenderCandidateRequest) =>
2450
+ (renderables) =>
2602
2451
  extractFrames(
2603
2452
  compositionWorlds,
2604
- { cameraOwner, resourceOwner },
2453
+ {
2454
+ cameraOwner,
2455
+ resourceOwner,
2456
+ ...(cameraEntityKey === undefined ? {} : { cameraEntityKey }),
2457
+ },
2605
2458
  internals.assets,
2606
2459
  internals.getPipelineState(),
2607
2460
  persistentRenderScene.materialSnapshotCacheStore(),
@@ -2626,13 +2479,11 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2626
2479
  },
2627
2480
  }),
2628
2481
  cull: 'none',
2629
- renderables: request === 'none' ? 'none' : 'full',
2630
- ...(request === 'none' || request === 'full'
2631
- ? {}
2632
- : { renderableEntitiesByWorld: request.entitiesByWorld }),
2633
2482
  retainHidden: true,
2483
+ renderables,
2634
2484
  instanceCollections,
2635
- ...(internals.getMaterialShaderArtifact === undefined
2485
+ ...(!internals.device.caps.storageBuffer ||
2486
+ internals.getMaterialShaderArtifact === undefined
2636
2487
  ? {}
2637
2488
  : { getMaterialShaderArtifact: internals.getMaterialShaderArtifact }),
2638
2489
  },
@@ -2641,7 +2492,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2641
2492
  );
2642
2493
  });
2643
2494
  const {
2644
- cameras,
2495
+ cameras: extractedCameras,
2645
2496
  cubeCameras,
2646
2497
  lights,
2647
2498
  environment,
@@ -2649,6 +2500,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2649
2500
  volumetricFog,
2650
2501
  renderables,
2651
2502
  dispatch,
2503
+ shadowCasterEntityKeys: extractedShadowCasterEntityKeys,
2504
+ shadowCasterDrawKeys: extractedShadowCasterDrawKeys,
2505
+ shadowCasterMembership: extractedShadowCasterMembership,
2652
2506
  skylight,
2653
2507
  skylightCount,
2654
2508
  skybox,
@@ -2658,6 +2512,28 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2658
2512
  visibilityStats,
2659
2513
  postProcessParams,
2660
2514
  } = frame;
2515
+ const cameras = extractedCameras;
2516
+ const preparedOutput = prepareStandardOutputResources(
2517
+ frameState,
2518
+ internals,
2519
+ cameras[0],
2520
+ compositionWorlds[cameraOwner] as World,
2521
+ );
2522
+ if (!preparedOutput.ok) {
2523
+ internals.errorRegistry.fire(preparedOutput.error);
2524
+ return false;
2525
+ }
2526
+ const stableShadowOwnership = rebaseShadowCasterOwnership(
2527
+ compositionWorlds,
2528
+ persistentRenderScene.visibilityWorldKeysFor(compositionWorlds),
2529
+ extractedShadowCasterEntityKeys,
2530
+ extractedShadowCasterDrawKeys,
2531
+ extractedShadowCasterMembership,
2532
+ );
2533
+ const shadowCasterEntityKeys = stableShadowOwnership.entityKeys;
2534
+ const shadowCasterDrawKeys = stableShadowOwnership.drawKeys;
2535
+ const shadowCasterMembership = stableShadowOwnership.membership;
2536
+ const shadowCasterProjection = persistentRenderScene.shadowCasterProjection();
2661
2537
  lastDirectionalShadowLights = lights;
2662
2538
  lastDirectionalShadowCandidate = 'failed';
2663
2539
  lastDirectionalShadowError = lights.directionalShadowError;
@@ -2667,8 +2543,41 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2667
2543
  cameras[0],
2668
2544
  frameState.frameNumber,
2669
2545
  skylight,
2546
+ frame.reflectionProbes?.some((probe) => probe.updateIntent === 'on-change')
2547
+ ? JSON.stringify([
2548
+ persistentRenderScene.reflectionCaptureRevision(),
2549
+ lights.directional,
2550
+ lights.point,
2551
+ lights.spot,
2552
+ lights.rect,
2553
+ skylight,
2554
+ frame.skybox,
2555
+ environment?.environmentSignature,
2556
+ ])
2557
+ : '',
2558
+ frame.skybox,
2670
2559
  );
2671
2560
  latestCamera = cameras[0];
2561
+ frameState.ssrRequested = latestCamera?.screenSpaceReflection !== undefined;
2562
+ // Resolve before a committed fallback change resets accumulation.
2563
+ // Fresh spatial SSR does not require a previous temporal submission.
2564
+ // Inspection uses the read-only projection above and never resets state.
2565
+ const ssrDependencies = resolveSsrDependencies(frameState.ssrRequested);
2566
+ const reset = resolveSsrAdmissionGeneration(
2567
+ ssrFallbackGeneration,
2568
+ ssrDependencies.reflectionFallback?.projectionGeneration,
2569
+ );
2570
+ if (reset.changed) {
2571
+ frameState.temporalFrameTransaction.reset('signature-change');
2572
+ frameState.temporalFrame = undefined;
2573
+ frameState.temporalFrameInput = undefined;
2574
+ frameState.lastSuccessfulTemporalView = undefined;
2575
+ frameState.successfulTemporalFrameIndex = 0;
2576
+ frameState.pendingTemporalCommit = { kind: 'none' };
2577
+ frameState.ssrHistoryOwner?.reset('reflection-generation');
2578
+ frameState.ssrHistoryCandidate = undefined;
2579
+ }
2580
+ ssrFallbackGeneration = reset.generation;
2672
2581
  persistentRenderScene.updateVisibilityFacet(compositionWorlds, latestCamera, renderables);
2673
2582
  let occlusionProjection:
2674
2583
  | import('./scene/visibility/occlusion-runtime').OcclusionFrameProjection
@@ -2940,6 +2849,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2940
2849
  if (temporalDemanded) {
2941
2850
  persistentRenderScene.prepareTemporalFrame(submissionRenderables);
2942
2851
  }
2852
+ frameState.environmentFrame = environment as
2853
+ | import('./environment/frame').SelectedEnvironmentFrame
2854
+ | undefined;
2943
2855
  environmentLifecycle.bindScope(internals.deviceScope);
2944
2856
  if (fogFailure !== undefined) {
2945
2857
  environmentLifecycle.recordSelectionFailure(fogFailure);
@@ -2970,8 +2882,8 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
2970
2882
  updateTransmissionInspection(transmissionAntialias, false);
2971
2883
  return false;
2972
2884
  }
2973
- if (lastMeshMaterialBindingFrame !== frame) {
2974
- lastMeshMaterialBindingFrame = frame;
2885
+ if (lastMeshMaterialBindingRenderables !== renderables) {
2886
+ lastMeshMaterialBindingRenderables = renderables;
2975
2887
  lastMeshMaterialBindings = renderables.map((renderable) => {
2976
2888
  const materialWorld = preparedWorlds[renderable.worldId];
2977
2889
  return projectMeshMaterialBindingObservation({
@@ -3024,6 +2936,15 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3024
2936
  lastFrustumStats.culled = frustumStats.culled;
3025
2937
  lastFrustumStats.total = frustumStats.total;
3026
2938
  lastVisibilityStats.explicitlyHidden = visibilityStats.explicitlyHidden;
2939
+ if (frame.materialTextureSources !== undefined) {
2940
+ lastMaterialTextureSources = Object.freeze({
2941
+ sourceFieldsVisited: frame.materialTextureSources.sourceFieldsVisited,
2942
+ numericSharedRefProbes: frame.materialTextureSources.numericSharedRefProbes,
2943
+ sourceCacheHits: frame.materialTextureSources.sourceCacheHits,
2944
+ sourceCacheMisses: frame.materialTextureSources.sourceCacheMisses,
2945
+ producerRoutes: Object.freeze({ ...frame.materialTextureSources.producerRoutes }),
2946
+ });
2947
+ }
3027
2948
  const featureTargets =
3028
2949
  preparedPipelineState === null || cameras[0] === undefined
3029
2950
  ? []
@@ -3034,6 +2955,11 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3034
2955
  storageBuffer: internals.device.caps.storageBuffer,
3035
2956
  multisample: internals.device.caps.backendKind !== 'wgpu-webgl2',
3036
2957
  });
2958
+ preparedFeatureSkylightResources =
2959
+ preparedPipelineState === null
2960
+ ? undefined
2961
+ : resolveMaterialSkylight(internals, preparedPipelineState, skylight, skylightCount)
2962
+ .skylightResources;
3037
2963
  let featureGraphCandidate: RenderFeatureGraphCandidate | undefined;
3038
2964
  const preparedResourceBatches = runProfiledRenderPhase(profileSession, 'features', () => {
3039
2965
  if (internals.featureHost === undefined) return [];
@@ -3049,6 +2975,28 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3049
2975
  ...(internals.getMaterialShaderBindingContract === undefined
3050
2976
  ? {}
3051
2977
  : { materialShaderBindingContract: internals.getMaterialShaderBindingContract }),
2978
+ ...(preparedPipelineState === null
2979
+ ? {}
2980
+ : {
2981
+ preparedDataInterfaces: {
2982
+ camera: {
2983
+ kind: 'buffer' as const,
2984
+ value: preparedPipelineState.viewUniformBuffer,
2985
+ size: 64,
2986
+ usage: 'uniform' as const,
2987
+ },
2988
+ 'scene-depth': {
2989
+ kind: 'texture-view' as const,
2990
+ value: preparedPipelineState.shadowFallbackTextureView,
2991
+ },
2992
+ get noise() {
2993
+ const value = resolveFeatureNoise(frameState.frameNumber);
2994
+ return value === undefined
2995
+ ? undefined
2996
+ : { kind: 'texture-view' as const, value };
2997
+ },
2998
+ },
2999
+ }),
3052
3000
  createPreparedGraphicsResolver: preparedResolverFactory,
3053
3001
  gpuWork: featureGpuWork,
3054
3002
  });
@@ -3072,10 +3020,11 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3072
3020
  featureGraphCandidate = {
3073
3021
  plans: featureFrame.plans,
3074
3022
  fullscreenEffects: featureFrame.fullscreenEffects,
3075
- ...(featureFrame.preparedResourceBatches.length === 0
3076
- ? {}
3077
- : { preparedResourceKey: `frame-${frameState.frameNumber}` }),
3023
+ ...(featureFrame.requiresPreparedResourceKey
3024
+ ? { preparedResourceKey: `frame-${frameState.frameNumber}` }
3025
+ : {}),
3078
3026
  onRejected: () => {
3027
+ featureFrame.onAborted();
3079
3028
  for (const batch of featureFrame.preparedResourceBatches) {
3080
3029
  const released = batch.release();
3081
3030
  if (!released.ok) {
@@ -3085,8 +3034,15 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3085
3034
  discardPostProcessParamsCandidate(postProcessParamsCandidate.value);
3086
3035
  },
3087
3036
  onAbandoned: () => {
3037
+ featureFrame.onAborted();
3088
3038
  discardPostProcessParamsCandidate(postProcessParamsCandidate.value);
3089
3039
  },
3040
+ onSubmitted: () => {
3041
+ featureFrame.onSubmitted();
3042
+ },
3043
+ onAborted: () => {
3044
+ featureFrame.onAborted();
3045
+ },
3090
3046
  onAccepted: () => {
3091
3047
  for (const [id, previous] of activeFeaturePostProcessEntries) {
3092
3048
  const next = featureFrame.fullscreenEffects.get(id);
@@ -3107,6 +3063,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3107
3063
  activeFeaturePostProcessEntries = featureFrame.fullscreenEffects;
3108
3064
  },
3109
3065
  };
3066
+ recordRenderFeatureCandidate(internals, featureGraphCandidate);
3110
3067
  return featureFrame.preparedResourceBatches;
3111
3068
  });
3112
3069
 
@@ -3184,14 +3141,38 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3184
3141
  if (cameras[0]?.bloom === 'on') {
3185
3142
  internals.getPipelineState()?.perPassResources.ensureBloomResources?.();
3186
3143
  }
3144
+ const liveOutput = cameras[0]?.output;
3145
+ const hasLiveFeatureState =
3146
+ liveOutput !== undefined ||
3147
+ frameState.autoExposureGpuResources !== undefined ||
3148
+ frameState.pendingAutoExposureGpuResources !== undefined ||
3149
+ frameState.autoExposureState !== undefined ||
3150
+ frameState.pendingAutoExposureState !== undefined ||
3151
+ frameState.standardLutGpuResources !== undefined ||
3152
+ frameState.pendingStandardLutGpuResources !== undefined ||
3153
+ frameState.standardLutState.resident !== null ||
3154
+ frameState.pendingStandardLutState !== undefined;
3155
+ if (hasLiveFeatureState) {
3156
+ const cameraWorld = compositionWorlds[cameraOwner] as World;
3157
+ const deltaTime = cameraWorld.getResource(Time).delta;
3158
+ stageLiveFeatureState(
3159
+ cameras[0],
3160
+ Number.isFinite(deltaTime) ? Math.max(0, deltaTime) : 0,
3161
+ );
3162
+ }
3163
+ const probeCaptureScene =
3164
+ shadowCasterProjection !== undefined &&
3165
+ reflectionProbeRecord.graph.work.some((work) => work.rawCaptureFace !== undefined)
3166
+ ? projectCaptureScene(shadowCasterProjection, submissionRenderables, orderedDispatch)
3167
+ : undefined;
3187
3168
  submitted = runProfiledRenderPhase(profileSession, 'record', () =>
3188
3169
  recordFrame(
3189
3170
  internals,
3190
3171
  resourceWorld,
3191
3172
  cameras,
3192
3173
  lights,
3193
- [...submissionRenderables],
3194
- orderedDispatch,
3174
+ probeCaptureScene?.renderables ?? submissionRenderables,
3175
+ probeCaptureScene?.displayDispatch ?? orderedDispatch,
3195
3176
  frameState,
3196
3177
  dispatchCounts,
3197
3178
  bindGroupCounts,
@@ -3221,37 +3202,28 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3221
3202
  scheduler: cubeCaptureScheduler,
3222
3203
  state: cubeCaptureState,
3223
3204
  reflectionProbes: reflectionProbeRecord,
3205
+ ...(probeCaptureScene === undefined
3206
+ ? {}
3207
+ : { captureDispatch: probeCaptureScene.captureDispatch }),
3224
3208
  } satisfies CubeCaptureFrameInput,
3225
3209
  environment?.environmentSignature ?? '',
3226
3210
  environment?.fogSignature ?? '',
3227
3211
  environmentReady && environment !== undefined,
3228
3212
  persistentRenderScene.transmissionTopologyDemand(),
3213
+ shadowCasterEntityKeys,
3214
+ shadowCasterDrawKeys,
3229
3215
  volumetricFog,
3230
3216
  timingCapture,
3231
- (entry) => {
3232
- const renderable = entry.source;
3233
- const world = compositionWorlds[renderable.worldId];
3234
- if (world === undefined) return;
3235
- let bindings = frameConsumedDynamicGeometryBindings.get(world);
3236
- if (bindings === undefined) {
3237
- bindings = new Map();
3238
- frameConsumedDynamicGeometryBindings.set(world, bindings);
3239
- }
3240
- bindings.set(renderable.entityKey, renderable.assetHandle);
3241
- },
3217
+ shadowCasterMembership,
3218
+ shadowCasterProjection,
3219
+ worldKeys,
3220
+ undefined,
3221
+ ssrDependencies,
3222
+ dynamicGeometryFrames.onRenderableDraw(compositionWorlds),
3242
3223
  ),
3243
3224
  );
3244
- if (submitted) {
3245
- lastSubmittedDynamicGeometryBindings = frameConsumedDynamicGeometryBindings;
3246
- }
3247
- if (submitted) {
3248
- try {
3249
- internals.recoveryColdWorkGuard?.finish();
3250
- } catch (cause) {
3251
- submitted = false;
3252
- throw cause;
3253
- }
3254
- }
3225
+ dynamicGeometryFrames.commit(submitted);
3226
+ if (!submitted) discardLiveFeatureState();
3255
3227
  if (submitted && lastLodOcclusionInspection !== undefined) {
3256
3228
  const query = ensureOcclusionRuntime().inspect();
3257
3229
  lastLodOcclusionInspection = {
@@ -3346,6 +3318,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3346
3318
  }
3347
3319
  }
3348
3320
  } catch (err) {
3321
+ discardLiveFeatureState();
3349
3322
  if (!submitted) {
3350
3323
  internals.getPipelineState()?.perPassResources.discardBloomResources?.();
3351
3324
  }
@@ -3359,7 +3332,11 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3359
3332
  }
3360
3333
  : err instanceof RhiError
3361
3334
  ? err
3362
- : { code: 'unknown' as const, message: String(err), name: (err as Error)?.name };
3335
+ : {
3336
+ code: 'unknown' as const,
3337
+ message: String(err),
3338
+ name: (err as Error)?.name,
3339
+ };
3363
3340
  internals.errorRegistry.fire(
3364
3341
  new RhiError({
3365
3342
  code: 'webgpu-runtime-error',
@@ -3369,6 +3346,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3369
3346
  }),
3370
3347
  );
3371
3348
  } finally {
3349
+ internals.recoveryColdWorkGuard?.endFrame();
3372
3350
  if (ownsProfileFrame) {
3373
3351
  try {
3374
3352
  profileSession?.endFrame();
@@ -3379,14 +3357,8 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3379
3357
  }
3380
3358
  return submitted;
3381
3359
  },
3382
- isDynamicGeometryConsumed(
3383
- world: World,
3384
- entity: number,
3385
- meshHandle: number | undefined,
3386
- ): boolean {
3387
- if (!Number.isInteger(entity) || meshHandle === undefined) return false;
3388
- return lastSubmittedDynamicGeometryBindings.get(world)?.get(entity) === meshHandle;
3389
- },
3360
+ isDynamicGeometryConsumed: (world, entity, meshHandle) =>
3361
+ dynamicGeometryFrames.isConsumed(world, entity, meshHandle),
3390
3362
  pipelineDispatchCounts: dispatchCounts,
3391
3363
  observeCurrentFrame(options: FrameObservationOptions) {
3392
3364
  const currentFrameId = frameState.frameNumber - 1;
@@ -3457,6 +3429,12 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3457
3429
  get perFrameGraphInfo(): CompiledRenderGraphInfo | undefined {
3458
3430
  return frameState.compiledFrameGraph?.inspect();
3459
3431
  },
3432
+ get featureGraphInspection(): import('./inspection-types').RenderFeatureGraphInspection {
3433
+ return getRenderFeatureGraphInspection(internals);
3434
+ },
3435
+ get materialTextureSources(): MaterialTextureSourceInspection {
3436
+ return lastMaterialTextureSources;
3437
+ },
3460
3438
  get recoveryEvidence(): RecoveryProductionEvidence {
3461
3439
  const graph = frameState.compiledFrameGraph?.inspect();
3462
3440
  return Object.freeze({
@@ -3486,7 +3464,10 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3486
3464
  ? config
3487
3465
  : {
3488
3466
  clusterGrid: DEFAULT_CLUSTER_GRID,
3489
- ...(profileConfig.ssao ? { ssao: { enabled: true } } : {}),
3467
+ ssao: {
3468
+ enabled: profileConfig.ssao !== false,
3469
+ ...(typeof profileConfig.ssao === 'object' ? profileConfig.ssao : {}),
3470
+ },
3490
3471
  ...config,
3491
3472
  };
3492
3473
  if (resolvedConfig?.clusterGrid !== undefined) {
@@ -3583,6 +3564,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3583
3564
  (internals as TimingSessionHost).gpuPassTimingSubmittedWork = undefined;
3584
3565
  (internals as TimingSessionHost).gpuPassTimingFrameIdentity = undefined;
3585
3566
  (internals as TimingSessionHost).gpuPassTimingBeginReason = undefined;
3567
+ internals.getPipelineState()?.skinPaletteAllocator?.dispose();
3586
3568
  pointsLinesOwner.dispose();
3587
3569
  persistentRenderScene.dispose();
3588
3570
  instanceCollections.dispose();
@@ -3590,6 +3572,10 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3590
3572
  occlusionRuntime?.dispose();
3591
3573
  disposeFeatureGpuWork();
3592
3574
  reflectionProbeOwner.dispose();
3575
+ frameState.ssrHistoryOwner?.dispose();
3576
+ frameState.ssrHistoryOwner = undefined;
3577
+ frameState.ssrHistoryCandidate = undefined;
3578
+ frameState.ssrSpatialAdmission = undefined;
3593
3579
  // Bloom generations are renderer-owned rather than graph-owned. Drain
3594
3580
  // candidate, active, and fence-retiring bundles before the rest of the
3595
3581
  // frame state is torn down; the owner callback is idempotent so the
@@ -3610,6 +3596,19 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3610
3596
  frameState.lastSuccessfulCameraAntialias = undefined;
3611
3597
  frameState.currentDirectionalShadowView = null;
3612
3598
  frameState.currentSpotShadowView = null;
3599
+ if (frameState.pendingAutoExposureGpuResources !== undefined) {
3600
+ retireAutoExposureGpuResources(frameState.pendingAutoExposureGpuResources);
3601
+ frameState.pendingAutoExposureGpuResources = undefined;
3602
+ }
3603
+ if (frameState.autoExposureGpuResources !== undefined) {
3604
+ retireAutoExposureGpuResources(frameState.autoExposureGpuResources);
3605
+ frameState.autoExposureGpuResources = undefined;
3606
+ }
3607
+ frameState.autoExposureState = undefined;
3608
+ frameState.pendingAutoExposureState = undefined;
3609
+ frameState.pendingStandardLutGpuResources = undefined;
3610
+ frameState.standardLutGpuResources = undefined;
3611
+ frameState.pendingStandardLutState = undefined;
3613
3612
  if (frameState.temporalGpuState !== undefined) {
3614
3613
  retireTemporalGpuState(frameState.temporalGpuState);
3615
3614
  frameState.temporalGpuState = undefined;
@@ -3655,7 +3654,6 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3655
3654
  // so destroy failures fire structured errors (unified per-frame +
3656
3655
  // dispose error strategy).
3657
3656
  disposeInstanceBuffers(frameState.instanceBuffers, internals.errorRegistry);
3658
- frameState.instanceResidency?.clear();
3659
3657
  if (frameState.instanceBufferChunks !== undefined) {
3660
3658
  disposeInstanceBufferChunks(frameState.instanceBufferChunks, internals.errorRegistry);
3661
3659
  }
@@ -3696,9 +3694,8 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3696
3694
  // their handles without invoking destroy on a device that can no longer
3697
3695
  // service cleanup; the staged candidate owns the replacement buffers.
3698
3696
  pointsLinesOwner.abandonForDeviceLoss();
3699
- ssrFormatReceipt = undefined;
3700
- ssrFormatProbeGeneration = -1;
3701
- ssrFormatProbe = undefined;
3697
+ resetStandardOutputForDeviceLoss(frameState, internals.deviceScope.generation);
3698
+ ssrFormatReceipts.delete(internals.device);
3702
3699
  ssrFallbackGeneration = undefined;
3703
3700
  transmissionAdmission.markDeviceLost();
3704
3701
  lastTransmissionKey = '';
@@ -3716,13 +3713,11 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3716
3713
  updateTransmissionInspection(lastTransmissionAntialias, false);
3717
3714
  persistentRenderScene.resetGpuForRecover();
3718
3715
  gpuDrivenProduction.dispose();
3719
- // The active renderer still points at the lost device until the caller
3720
- // completes the synchronous publication boundary. Bind the replacement
3721
- // explicitly so the first published draw never routes through an owner
3722
- // recreated against the old physical device.
3723
- gpuDrivenProduction = GpuDrivenProduction.forDevice(
3716
+ gpuDrivenRecoveryCount += 1;
3717
+ gpuDrivenProduction = createGpuDrivenOwner(
3724
3718
  replacementDevice ?? internals.device,
3725
3719
  gpuDrivenShaderFactory,
3720
+ gpuDrivenRecoveryCount,
3726
3721
  );
3727
3722
  occlusionRuntime?.dispose();
3728
3723
  // The replacement device is installed by the host after this hook. Do
@@ -3732,6 +3727,13 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3732
3727
  disposeFeatureGpuWork();
3733
3728
  featureGpuWork = createFeatureGpuWorkOwner(internals);
3734
3729
  reflectionProbeOwner.dispose();
3730
+ frameState.ssrHistoryOwner?.dispose();
3731
+ frameState.ssrHistoryOwner = undefined;
3732
+ frameState.ssrHistoryCandidate = undefined;
3733
+ frameState.ssrSpatialAdmission = undefined;
3734
+ frameState.ssrRequested = false;
3735
+ frameState.ssrLastCameraEntity = undefined;
3736
+ frameState.ssrLastHistoryVersion = undefined;
3735
3737
  // A recovered device cannot retain handles from the lost device. The
3736
3738
  // Bloom owner abandons candidates and retires every committed generation
3737
3739
  // before the new PipelineState is rebuilt.
@@ -3801,7 +3803,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3801
3803
  frameState.retiredCompiledFrameGraphs.clear();
3802
3804
  frameState.instanceBuffers.clear();
3803
3805
  frameState.instanceBufferChunks?.clear();
3804
- frameState.instanceResidency?.clear();
3806
+ instanceCollections._resetResidency();
3805
3807
  delete frameState.probeBlendRecordBuffer;
3806
3808
  frameState.probeBlendRecordBufferCapacity = 0;
3807
3809
  frameState.probeBlendBuffers.clear();
@@ -3818,7 +3820,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3818
3820
  frameState.instancesBgPerEntity.clear();
3819
3821
  frameState.materialBgShared.clear();
3820
3822
  frameState.materialBgAssemblyCache.clear();
3821
- frameState.singletonMaterialCache.clear();
3823
+ frameState.shadowMaterialBindGroups = new WeakMap();
3822
3824
  frameState.postProcessBgCache = new WeakMap();
3823
3825
  // Fullscreen PSOs are cached outside frameState because the normal
3824
3826
  // path reuses them across frames. They still carry opaque handles from
@@ -3844,124 +3846,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
3844
3846
  publishRecoveryPostProcessResources(prepared.value);
3845
3847
  },
3846
3848
  prepareRecoveryRoots(runtime: RecoveryRootRuntime): RecoveryRootBundle {
3847
- // Snapshot only CPU projections/declarations here. The root create
3848
- // callbacks below must never close over a mutable active owner while
3849
- // LifecycleTransaction is awaiting candidate construction.
3850
- const candidateEnvironmentLifecycle = environmentLifecycle.createRecoveryCandidate(
3851
- runtime.scope,
3852
- );
3853
- const roots: LifecycleResourceSpec<unknown>[] = [];
3854
- let candidateEnvironmentGeneration: EnvironmentGeneration | undefined;
3855
- let environmentRootCleaned = false;
3856
- let published = false;
3857
- if (candidateEnvironmentLifecycle.hasRecoveryFrame()) {
3858
- const environmentRoot = candidateEnvironmentLifecycle.createRecoveryRoot(runtime.scope);
3859
- roots.push({
3860
- ...environmentRoot,
3861
- create: async () => {
3862
- const generation = (await environmentRoot.create()) as EnvironmentGeneration;
3863
- candidateEnvironmentGeneration = generation;
3864
- return generation;
3865
- },
3866
- cleanup: (value) => {
3867
- if (environmentRootCleaned) return;
3868
- environmentRootCleaned = true;
3869
- if (candidateEnvironmentGeneration === value) {
3870
- candidateEnvironmentGeneration = undefined;
3871
- }
3872
- if (published) {
3873
- candidateEnvironmentLifecycle.retirePublishedGeneration(
3874
- value as EnvironmentGeneration,
3875
- );
3876
- } else {
3877
- environmentRoot.cleanup(value);
3878
- }
3879
- },
3880
- });
3881
- }
3882
- const graphCandidate = runtime.graphCandidate;
3883
- if (graphCandidate !== undefined) {
3884
- const releaseGraphCandidate = (): void => graphCandidate.release();
3885
- const compiledGraph = graphCandidate.frameState.compiledFrameGraph;
3886
- if (compiledGraph !== null) {
3887
- roots.push({
3888
- kind: 'pipeline',
3889
- create: () => {
3890
- if (!runtime.scope.isAlive()) {
3891
- throw new Error('Recovery graph candidate scope is not active.');
3892
- }
3893
- if (graphCandidate.frameState.compiledFrameGraph !== compiledGraph) {
3894
- throw new Error('Recovery graph candidate was changed before aggregate assembly.');
3895
- }
3896
- return compiledGraph;
3897
- },
3898
- // All graph/feature/GPU-driven candidate resources share one
3899
- // owner. The root is a real release seam, not a marker; the
3900
- // candidate latch makes every root cleanup call harmless after
3901
- // the first one.
3902
- cleanup: releaseGraphCandidate,
3903
- });
3904
- }
3905
- if (graphCandidate.featureHost !== undefined) {
3906
- const featureRoot = graphCandidate.featureHost.createRecoveryRoot(runtime.scope);
3907
- roots.push({ ...featureRoot, cleanup: releaseGraphCandidate });
3908
- }
3909
- if (graphCandidate.featureGpuWork !== undefined) {
3910
- const featureWorkRoot = graphCandidate.featureGpuWork.createRecoveryRoot(runtime.scope);
3911
- roots.push({ ...featureWorkRoot, cleanup: releaseGraphCandidate });
3912
- }
3913
- if (graphCandidate.gpuDrivenProduction !== undefined) {
3914
- const productionRoot = graphCandidate.gpuDrivenProduction.createRecoveryRoot(
3915
- runtime.scope,
3916
- );
3917
- roots.push({ ...productionRoot, cleanup: releaseGraphCandidate });
3918
- }
3919
- if (graphCandidate.gpuDrivenScene?.state !== undefined) {
3920
- const sceneRoot = graphCandidate.gpuDrivenScene.createRecoveryRoot(runtime.scope);
3921
- roots.push({ ...sceneRoot, cleanup: releaseGraphCandidate });
3922
- }
3923
- if (graphCandidate.pointsLines !== undefined) {
3924
- const pointsLinesRoot = graphCandidate.pointsLines.createRecoveryRoot(runtime.scope);
3925
- roots.push({ ...pointsLinesRoot, cleanup: releaseGraphCandidate });
3926
- }
3927
- if (runtime.gpuStore.recoveryResourceCount() > 0) {
3928
- const gpuStoreRoot = runtime.gpuStore.createRecoveryRoot(runtime.scope);
3929
- roots.push({ ...gpuStoreRoot, cleanup: releaseGraphCandidate });
3930
- }
3931
- }
3932
- let discarded = false;
3933
- return {
3934
- roots: Object.freeze(roots),
3935
- publish: () => {
3936
- if (published || discarded) return;
3937
- if (
3938
- candidateEnvironmentGeneration !== undefined &&
3939
- !candidateEnvironmentGeneration.retired
3940
- ) {
3941
- environmentLifecycle.retireActiveForReplacement();
3942
- candidateEnvironmentLifecycle.publish(candidateEnvironmentGeneration);
3943
- }
3944
- published = true;
3945
- environmentLifecycle = candidateEnvironmentLifecycle;
3946
- Object.assign(frameState, { environmentLifecycle });
3947
- },
3948
- discard: () => {
3949
- if (published || discarded) return;
3950
- discarded = true;
3951
- if (candidateEnvironmentGeneration !== undefined) {
3952
- candidateEnvironmentLifecycle.discard(candidateEnvironmentGeneration);
3953
- candidateEnvironmentGeneration = undefined;
3954
- }
3955
- candidateEnvironmentLifecycle.discardRecoveryCandidates();
3956
- // The transaction can fail before it reaches any graph root (for
3957
- // example while the environment root is created). Keep the graph,
3958
- // feature, GPU-driven, and residency resources on the same real
3959
- // candidate owner in that case; later scope/factory cleanup is a
3960
- // harmless repeat through the candidate latch.
3961
- graphCandidate?.release();
3962
- runtime.scope.abandon();
3963
- },
3964
- };
3849
+ return prepareRenderSystemRecoveryRoots(runtime, environmentLifecycle, frameState, (next) => {
3850
+ environmentLifecycle = next;
3851
+ });
3965
3852
  },
3966
3853
  };
3967
3854
  }