@forgeax/engine-render 0.1.32 → 0.1.34

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 (624) hide show
  1. package/README.md +1374 -142
  2. package/dist/assembly/analytic-fog-registration.d.ts +3 -0
  3. package/dist/assembly/analytic-fog-registration.d.ts.map +1 -0
  4. package/dist/assembly/backend-contract.d.ts +2 -0
  5. package/dist/assembly/backend-contract.d.ts.map +1 -1
  6. package/dist/assembly/barrel-distortion-inspection.d.ts +3 -0
  7. package/dist/assembly/barrel-distortion-inspection.d.ts.map +1 -0
  8. package/dist/assembly/canvas-draw-guard.d.ts +3 -0
  9. package/dist/assembly/canvas-draw-guard.d.ts.map +1 -0
  10. package/dist/assembly/cloud-shadow-entry.d.ts +4 -0
  11. package/dist/assembly/cloud-shadow-entry.d.ts.map +1 -0
  12. package/dist/assembly/dynamic-geometry-host.d.ts +4 -1
  13. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
  14. package/dist/assembly/dynamic-geometry-runtime.d.ts +1 -1
  15. package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -1
  16. package/dist/assembly/factory.d.ts +1 -1
  17. package/dist/assembly/factory.d.ts.map +1 -1
  18. package/dist/assembly/fxaa-registration.d.ts +4 -0
  19. package/dist/assembly/fxaa-registration.d.ts.map +1 -0
  20. package/dist/assembly/host-contract.d.ts +3 -3
  21. package/dist/assembly/host-contract.d.ts.map +1 -1
  22. package/dist/assembly/lod-observation.d.ts +4 -0
  23. package/dist/assembly/lod-observation.d.ts.map +1 -0
  24. package/dist/assembly/material/assembly.d.ts.map +1 -1
  25. package/dist/assembly/material/pipeline-helpers.d.ts +5 -0
  26. package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -1
  27. package/dist/assembly/material-shader-policy.d.ts +16 -2
  28. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  29. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -1
  30. package/dist/assembly/motion-blur-registration.d.ts +3 -0
  31. package/dist/assembly/motion-blur-registration.d.ts.map +1 -0
  32. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
  33. package/dist/assembly/recovery/renderer-recover.d.ts +2 -0
  34. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  35. package/dist/assembly/render-feature-post-processes.d.ts +6 -0
  36. package/dist/assembly/render-feature-post-processes.d.ts.map +1 -0
  37. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  38. package/dist/assembly/renderer-feature-inspection.d.ts +1 -1
  39. package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -1
  40. package/dist/assembly/renderer-frame-transaction.d.ts +12 -0
  41. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  42. package/dist/assembly/renderer-inspection.d.ts +11 -0
  43. package/dist/assembly/renderer-inspection.d.ts.map +1 -1
  44. package/dist/assembly/temporal-post-process-prewarm.d.ts +13 -0
  45. package/dist/assembly/temporal-post-process-prewarm.d.ts.map +1 -0
  46. package/dist/assembly/webgpu-pbr-ready.d.ts +1 -0
  47. package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -1
  48. package/dist/assembly/webgpu-ready-contract.d.ts +4 -3
  49. package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -1
  50. package/dist/assembly/webgpu-ready-per-pass.d.ts +39 -0
  51. package/dist/assembly/webgpu-ready-per-pass.d.ts.map +1 -0
  52. package/dist/assembly/webgpu-ready.d.ts +2 -2
  53. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  54. package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -1
  55. package/dist/assembly/webgpu-renderer-observation-owner.d.ts +35 -0
  56. package/dist/assembly/webgpu-renderer-observation-owner.d.ts.map +1 -0
  57. package/dist/assembly/webgpu-renderer.d.ts +1 -1
  58. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  59. package/dist/authoring.mjs +1 -1
  60. package/dist/barrel-distortion.d.ts +47 -0
  61. package/dist/barrel-distortion.d.ts.map +1 -0
  62. package/dist/bloom-admission.d.ts +13 -0
  63. package/dist/bloom-admission.d.ts.map +1 -0
  64. package/dist/{chunk-PA5DRJNI.mjs → chunk-ELJFJ2BU.mjs} +2 -2
  65. package/dist/chunk-ELJFJ2BU.mjs.map +1 -0
  66. package/dist/{chunk-JCIRYQ4V.mjs → chunk-HEB564WF.mjs} +10 -4
  67. package/dist/chunk-HEB564WF.mjs.map +1 -0
  68. package/dist/{chunk-JRIPCMYC.mjs → chunk-MKDIT2EM.mjs} +1584 -375
  69. package/dist/chunk-MKDIT2EM.mjs.map +1 -0
  70. package/dist/{chunk-S665NJNF.mjs → chunk-OCDLEI6I.mjs} +3 -3
  71. package/dist/{chunk-S665NJNF.mjs.map → chunk-OCDLEI6I.mjs.map} +1 -1
  72. package/dist/chunk-ODZWR6JQ.mjs +4927 -0
  73. package/dist/chunk-ODZWR6JQ.mjs.map +1 -0
  74. package/dist/{chunk-DRR7D3KX.mjs → chunk-SHFUFJ5N.mjs} +192 -18
  75. package/dist/chunk-SHFUFJ5N.mjs.map +1 -0
  76. package/dist/{chunk-4NNZQCL3.mjs → chunk-UI5SFODX.mjs} +223 -277
  77. package/dist/chunk-UI5SFODX.mjs.map +1 -0
  78. package/dist/{chunk-GWCEMDVR.mjs → chunk-UINHLCZA.mjs} +787 -80
  79. package/dist/chunk-UINHLCZA.mjs.map +1 -0
  80. package/dist/{chunk-6TFBXLRJ.mjs → chunk-WEX45HKH.mjs} +222 -337
  81. package/dist/chunk-WEX45HKH.mjs.map +1 -0
  82. package/dist/cloud/density.d.ts +55 -0
  83. package/dist/cloud/density.d.ts.map +1 -0
  84. package/dist/cloud/extract.d.ts +25 -0
  85. package/dist/cloud/extract.d.ts.map +1 -0
  86. package/dist/cloud/feature.d.ts +57 -0
  87. package/dist/cloud/feature.d.ts.map +1 -0
  88. package/dist/cloud/frame-context.d.ts +8 -0
  89. package/dist/cloud/frame-context.d.ts.map +1 -0
  90. package/dist/cloud/inspection.d.ts +59 -0
  91. package/dist/cloud/inspection.d.ts.map +1 -0
  92. package/dist/cloud/optics.d.ts +56 -0
  93. package/dist/cloud/optics.d.ts.map +1 -0
  94. package/dist/cloud/parameters.d.ts +73 -0
  95. package/dist/cloud/parameters.d.ts.map +1 -0
  96. package/dist/cloud/resources.d.ts +60 -0
  97. package/dist/cloud/resources.d.ts.map +1 -0
  98. package/dist/cloud/shadow.d.ts +42 -0
  99. package/dist/cloud/shadow.d.ts.map +1 -0
  100. package/dist/cloud/temporal.d.ts +74 -0
  101. package/dist/cloud/temporal.d.ts.map +1 -0
  102. package/dist/components/atmosphere.d.ts +2 -0
  103. package/dist/components/atmosphere.d.ts.map +1 -1
  104. package/dist/components/barrel-distortion.d.ts +13 -0
  105. package/dist/components/barrel-distortion.d.ts.map +1 -0
  106. package/dist/components/camera.d.ts +20 -4
  107. package/dist/components/camera.d.ts.map +1 -1
  108. package/dist/components/cloud-layer.d.ts +29 -0
  109. package/dist/components/cloud-layer.d.ts.map +1 -0
  110. package/dist/components/index.d.ts +2 -0
  111. package/dist/components/index.d.ts.map +1 -1
  112. package/dist/components/instances.d.ts +20 -6
  113. package/dist/components/instances.d.ts.map +1 -1
  114. package/dist/components/mesh-filter.d.ts +6 -5
  115. package/dist/components/mesh-filter.d.ts.map +1 -1
  116. package/dist/components/motion-blur.d.ts +1 -0
  117. package/dist/components/motion-blur.d.ts.map +1 -1
  118. package/dist/construct-renderer.d.ts +1 -1
  119. package/dist/construct-renderer.d.ts.map +1 -1
  120. package/dist/construct-renderer.mjs +9751 -3528
  121. package/dist/construct-renderer.mjs.map +1 -1
  122. package/dist/device/gpu-residency.d.ts +6 -0
  123. package/dist/device/gpu-residency.d.ts.map +1 -1
  124. package/dist/dynamic-geometry.d.ts +5 -1
  125. package/dist/dynamic-geometry.d.ts.map +1 -1
  126. package/dist/environment/analytic-fog.d.ts +6 -0
  127. package/dist/environment/analytic-fog.d.ts.map +1 -0
  128. package/dist/errors/cloud.d.ts +59 -0
  129. package/dist/errors/cloud.d.ts.map +1 -0
  130. package/dist/errors/render.d.ts +23 -5
  131. package/dist/errors/render.d.ts.map +1 -1
  132. package/dist/examples/single-layer-medium-surface.d.ts +47 -0
  133. package/dist/examples/single-layer-medium-surface.d.ts.map +1 -0
  134. package/dist/extract/camera.d.ts.map +1 -1
  135. package/dist/extract/contracts.d.ts +12 -1
  136. package/dist/extract/contracts.d.ts.map +1 -1
  137. package/dist/extract/environment.d.ts +2 -0
  138. package/dist/extract/environment.d.ts.map +1 -1
  139. package/dist/features/barrel-distortion.d.ts +29 -0
  140. package/dist/features/barrel-distortion.d.ts.map +1 -0
  141. package/dist/features/error-reporting.d.ts +17 -0
  142. package/dist/features/error-reporting.d.ts.map +1 -0
  143. package/dist/features/host.d.ts +15 -0
  144. package/dist/features/host.d.ts.map +1 -1
  145. package/dist/features/motion-blur/motion-blur-feature.d.ts +20 -2
  146. package/dist/features/motion-blur/motion-blur-feature.d.ts.map +1 -1
  147. package/dist/features/motion-blur/motion-blur-params.d.ts +21 -0
  148. package/dist/features/motion-blur/motion-blur-params.d.ts.map +1 -1
  149. package/dist/features/motion-blur/motion-blur-runtime.d.ts +54 -0
  150. package/dist/features/motion-blur/motion-blur-runtime.d.ts.map +1 -0
  151. package/dist/features/plan.d.ts +15 -4
  152. package/dist/features/plan.d.ts.map +1 -1
  153. package/dist/features/prepared-gpu-work.d.ts +9 -5
  154. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  155. package/dist/features/render-graph-compute.d.ts.map +1 -1
  156. package/dist/features/render-graph-contribution.d.ts +5 -0
  157. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  158. package/dist/features/render-graph-raster.d.ts +1 -0
  159. package/dist/features/render-graph-raster.d.ts.map +1 -1
  160. package/dist/features/targets.d.ts +2 -0
  161. package/dist/features/targets.d.ts.map +1 -1
  162. package/dist/features/types.d.ts +44 -0
  163. package/dist/features/types.d.ts.map +1 -1
  164. package/dist/fullscreen-post-process-pass.d.ts +15 -7
  165. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  166. package/dist/gpu-dirty-ranges.d.ts +11 -0
  167. package/dist/gpu-dirty-ranges.d.ts.map +1 -0
  168. package/dist/gpu-driven/batch-topology.d.ts +17 -0
  169. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  170. package/dist/gpu-driven/material-artifacts.d.ts +6 -0
  171. package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
  172. package/dist/gpu-driven/prepared-draw.d.ts +7 -0
  173. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  174. package/dist/gpu-driven/production-raster-material.d.ts +178 -0
  175. package/dist/gpu-driven/production-raster-material.d.ts.map +1 -0
  176. package/dist/gpu-driven/production-raster.d.ts +50 -4
  177. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  178. package/dist/gpu-driven/resource-allocation.d.ts +31 -0
  179. package/dist/gpu-driven/resource-allocation.d.ts.map +1 -0
  180. package/dist/gpu-driven/shadow-ownership.d.ts +14 -0
  181. package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -0
  182. package/dist/gpu-driven/view-gpu.d.ts +35 -2
  183. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  184. package/dist/gpu-scene.d.ts +11 -1
  185. package/dist/gpu-scene.d.ts.map +1 -1
  186. package/dist/hdrp-buffers.d.ts +2 -2
  187. package/dist/hdrp-buffers.d.ts.map +1 -1
  188. package/dist/ibl/skylight-bind-group.d.ts +10 -1
  189. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  190. package/dist/index.d.ts +31 -8
  191. package/dist/index.d.ts.map +1 -1
  192. package/dist/index.mjs +258 -8
  193. package/dist/index.mjs.map +1 -1
  194. package/dist/inspection-types.d.ts +178 -3
  195. package/dist/inspection-types.d.ts.map +1 -1
  196. package/dist/instance-buffer-cache.d.ts +4 -4
  197. package/dist/instance-buffer-cache.d.ts.map +1 -1
  198. package/dist/instances.d.ts +49 -94
  199. package/dist/instances.d.ts.map +1 -1
  200. package/dist/internal.mjs +7 -7
  201. package/dist/pbr-pipeline.d.ts +19 -1
  202. package/dist/pbr-pipeline.d.ts.map +1 -1
  203. package/dist/pipeline/single-layer-medium-passes.d.ts +73 -0
  204. package/dist/pipeline/single-layer-medium-passes.d.ts.map +1 -0
  205. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  206. package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
  207. package/dist/pipeline/standard-output/types.d.ts +4 -2
  208. package/dist/pipeline/standard-output/types.d.ts.map +1 -1
  209. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  210. package/dist/pipeline/standard-post.d.ts +28 -19
  211. package/dist/pipeline/standard-post.d.ts.map +1 -1
  212. package/dist/pipeline-builder.d.ts +1 -1
  213. package/dist/pipeline-builder.d.ts.map +1 -1
  214. package/dist/pipeline-spec.d.ts +2 -2
  215. package/dist/plugin.d.ts.map +1 -1
  216. package/dist/prepare/prepared-graphics-resolver.d.ts +2 -0
  217. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  218. package/dist/record/barrel-distortion-frame.d.ts +15 -0
  219. package/dist/record/barrel-distortion-frame.d.ts.map +1 -0
  220. package/dist/record/dynamic-geometry-consumption.d.ts +10 -2
  221. package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -1
  222. package/dist/record/fold-buckets.d.ts +95 -0
  223. package/dist/record/fold-buckets.d.ts.map +1 -0
  224. package/dist/record/fold-instance-buffer.d.ts +6 -0
  225. package/dist/record/fold-instance-buffer.d.ts.map +1 -0
  226. package/dist/record/frame-lighting.d.ts +1 -0
  227. package/dist/record/frame-lighting.d.ts.map +1 -1
  228. package/dist/record/frame-snapshot.d.ts +40 -3
  229. package/dist/record/frame-snapshot.d.ts.map +1 -1
  230. package/dist/record/frame-targets.d.ts.map +1 -1
  231. package/dist/record/frame.d.ts +14 -62
  232. package/dist/record/frame.d.ts.map +1 -1
  233. package/dist/record/gpu-draw-receipts.d.ts +11 -0
  234. package/dist/record/gpu-draw-receipts.d.ts.map +1 -0
  235. package/dist/record/gpu-timing.d.ts +8 -0
  236. package/dist/record/gpu-timing.d.ts.map +1 -1
  237. package/dist/record/main-pass-geometry.d.ts +21 -5
  238. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  239. package/dist/record/main-pass-material.d.ts +7 -1
  240. package/dist/record/main-pass-material.d.ts.map +1 -1
  241. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  242. package/dist/record/main-pass.d.ts +13 -2
  243. package/dist/record/main-pass.d.ts.map +1 -1
  244. package/dist/record/mesh-ssbo.d.ts +6 -5
  245. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  246. package/dist/record/motion-blur-receipt.d.ts +11 -0
  247. package/dist/record/motion-blur-receipt.d.ts.map +1 -0
  248. package/dist/record/probe-blend-buffer.d.ts +16 -0
  249. package/dist/record/probe-blend-buffer.d.ts.map +1 -0
  250. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  251. package/dist/record/render-context.d.ts +34 -31
  252. package/dist/record/render-context.d.ts.map +1 -1
  253. package/dist/record/shadow-pass.d.ts +2 -0
  254. package/dist/record/shadow-pass.d.ts.map +1 -1
  255. package/dist/record/skybox-post-pass.d.ts +6 -4
  256. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  257. package/dist/record/typed-frame-graph.d.ts +22 -2
  258. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  259. package/dist/record/view-ubo.d.ts +11 -3
  260. package/dist/record/view-ubo.d.ts.map +1 -1
  261. package/dist/record/volume-params.d.ts +9 -0
  262. package/dist/record/volume-params.d.ts.map +1 -0
  263. package/dist/recovery/render-system-candidate.d.ts +10 -0
  264. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  265. package/dist/reflection/record-owner.d.ts.map +1 -1
  266. package/dist/render-contract.d.ts +66 -8
  267. package/dist/render-contract.d.ts.map +1 -1
  268. package/dist/render-graph-primitives.d.ts.map +1 -1
  269. package/dist/render-pipeline.d.ts +106 -3
  270. package/dist/render-pipeline.d.ts.map +1 -1
  271. package/dist/render-system-extract-tail.d.ts +3 -2
  272. package/dist/render-system-extract-tail.d.ts.map +1 -1
  273. package/dist/render-system-extract.d.ts +24 -10
  274. package/dist/render-system-extract.d.ts.map +1 -1
  275. package/dist/render-system-presentation.d.ts +23 -0
  276. package/dist/render-system-presentation.d.ts.map +1 -0
  277. package/dist/render-system-producer-roots.d.ts +10 -0
  278. package/dist/render-system-producer-roots.d.ts.map +1 -0
  279. package/dist/render-system-residency.d.ts +12 -0
  280. package/dist/render-system-residency.d.ts.map +1 -0
  281. package/dist/render-system.d.ts +26 -15
  282. package/dist/render-system.d.ts.map +1 -1
  283. package/dist/scene/probe-blend.d.ts +27 -0
  284. package/dist/scene/probe-blend.d.ts.map +1 -1
  285. package/dist/scene/render-scene.d.ts +14 -5
  286. package/dist/scene/render-scene.d.ts.map +1 -1
  287. package/dist/ssao-buffers.d.ts +9 -0
  288. package/dist/ssao-buffers.d.ts.map +1 -1
  289. package/dist/surface/dynamic-input.d.ts +133 -0
  290. package/dist/surface/dynamic-input.d.ts.map +1 -0
  291. package/dist/surface/gpu-driven.d.ts +41 -0
  292. package/dist/surface/gpu-driven.d.ts.map +1 -0
  293. package/dist/surface/submission-observation.d.ts +136 -0
  294. package/dist/surface/submission-observation.d.ts.map +1 -0
  295. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  296. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  297. package/dist/temporal/frame.d.ts +7 -0
  298. package/dist/temporal/frame.d.ts.map +1 -1
  299. package/dist/temporal/gpu.d.ts +13 -1
  300. package/dist/temporal/gpu.d.ts.map +1 -1
  301. package/dist/temporal/index.mjs +5 -5
  302. package/dist/temporal/inspection.d.ts.map +1 -1
  303. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  304. package/dist/temporal/target-coverage-attachment.d.ts.map +1 -1
  305. package/dist/temporal/view.d.ts +6 -1
  306. package/dist/temporal/view.d.ts.map +1 -1
  307. package/dist/transmission/single-layer-medium.d.ts +89 -0
  308. package/dist/transmission/single-layer-medium.d.ts.map +1 -0
  309. package/dist/typed-render-graph-primitives.d.ts +24 -9
  310. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  311. package/dist/volume/component.d.ts +9 -5
  312. package/dist/volume/component.d.ts.map +1 -1
  313. package/dist/volume/extract.d.ts +1 -1
  314. package/dist/volume/extract.d.ts.map +1 -1
  315. package/dist/volume/inspection.d.ts +2 -0
  316. package/dist/volume/inspection.d.ts.map +1 -1
  317. package/dist/volume/passes.d.ts +4 -1
  318. package/dist/volume/passes.d.ts.map +1 -1
  319. package/dist/volume/resources.d.ts.map +1 -1
  320. package/dist/volume/temporal.d.ts +3 -1
  321. package/dist/volume/temporal.d.ts.map +1 -1
  322. package/package.json +22 -21
  323. package/src/__tests__/analytic-fog-graph.unit.test.ts +199 -0
  324. package/src/__tests__/analytic-fog-optics.unit.test.ts +38 -0
  325. package/src/__tests__/atmosphere-daylight.dawn.test.ts +72 -0
  326. package/src/__tests__/barrel-distortion-lkg.integration.test.ts +353 -0
  327. package/src/__tests__/barrel-distortion.dawn.test.ts +41 -0
  328. package/src/__tests__/barrel-distortion.unit.test.ts +364 -0
  329. package/src/__tests__/bloom-exact-zero.unit.test.ts +112 -18
  330. package/src/__tests__/bloom-lifecycle.integration.test.ts +17 -4
  331. package/src/__tests__/construct-renderer-identity.unit.test.ts +49 -0
  332. package/src/__tests__/detached-recovery-graph.unit.test.ts +277 -0
  333. package/src/__tests__/device-loss-fanout.unit.test.ts +36 -1
  334. package/src/__tests__/docs-gate-green.unit.test.ts +112 -1
  335. package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +20 -0
  336. package/src/__tests__/environment-fog-lanes.integration.test.ts +14 -0
  337. package/src/__tests__/environment-selection.unit.test.ts +12 -0
  338. package/src/__tests__/factory-contract.integration.test.ts +626 -113
  339. package/src/__tests__/feature-depth-input.dawn.test.ts +129 -2
  340. package/src/__tests__/frame-camera-selection.unit.test.ts +39 -1
  341. package/src/__tests__/frame-plan-contract.unit.test.ts +2 -0
  342. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +14 -3
  343. package/src/__tests__/gpu-dirty-ranges.unit.test.ts +22 -0
  344. package/src/__tests__/gpu-driven-baseline.characterization.test.ts +16 -3
  345. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +12 -0
  346. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +2 -1
  347. package/src/__tests__/gpu-driven-material-artifacts.unit.test.ts +143 -0
  348. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +2 -2
  349. package/src/__tests__/gpu-driven-production.integration.test.ts +482 -3
  350. package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +7 -6
  351. package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +1 -1
  352. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +103 -4
  353. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +18 -2
  354. package/src/__tests__/gpu-driven-view.browser.test.ts +5 -8
  355. package/src/__tests__/gpu-driven-view.dawn.test.ts +6 -2
  356. package/src/__tests__/gpu-driven-view.unit.test.ts +1 -1
  357. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +1 -0
  358. package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -4
  359. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +1 -0
  360. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +12 -0
  361. package/src/__tests__/gpu-scene.unit.test.ts +210 -9
  362. package/src/__tests__/instance-allocation-failure.unit.test.ts +57 -0
  363. package/src/__tests__/instances-culling.integration.test.ts +10 -36
  364. package/src/__tests__/instances-store.unit.test.ts +105 -238
  365. package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
  366. package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +146 -0
  367. package/src/__tests__/material-shader-prewarm.unit.test.ts +59 -0
  368. package/src/__tests__/material-snapshot-mutation.integration.test.ts +39 -1
  369. package/src/__tests__/mesh-submission-lifetime.unit.test.ts +31 -0
  370. package/src/__tests__/morph-culling-reentry.dawn.test.ts +5 -5
  371. package/src/__tests__/morph-visual-evidence.integration.test.ts +8 -7
  372. package/src/__tests__/motion-blur-compute.dawn.test.ts +589 -0
  373. package/src/__tests__/motion-blur.pixel.integration.test.ts +2 -2
  374. package/src/__tests__/nineslice-handle-surface.unit.test.ts +4 -4
  375. package/src/__tests__/pbr-view-layout.unit.test.ts +9 -0
  376. package/src/__tests__/physical-clearcoat.integration.test.ts +58 -0
  377. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +21 -0
  378. package/src/__tests__/prepared-graphics-capability.unit.test.ts +66 -2
  379. package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +86 -0
  380. package/src/__tests__/probe-bindings.unit.test.ts +582 -1
  381. package/src/__tests__/probe-scene-dirty.integration.test.ts +28 -1
  382. package/src/__tests__/public-surface.test-d.ts +8 -0
  383. package/src/__tests__/receipt-observation-retirement.unit.test.ts +65 -0
  384. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +69 -0
  385. package/src/__tests__/render-error-code-owner.test-d.ts +9 -0
  386. package/src/__tests__/render-feature-diagnostics.unit.test.ts +15 -2
  387. package/src/__tests__/render-feature-error-reporting.unit.test.ts +44 -0
  388. package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +10 -1
  389. package/src/__tests__/render-feature-retry.unit.test.ts +12 -1
  390. package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +23 -0
  391. package/src/__tests__/render-scene-mixed-updates.integration.test.ts +145 -19
  392. package/src/__tests__/render-scene-projection.unit.test.ts +36 -11
  393. package/src/__tests__/render-scene-temporal-retry.unit.test.ts +37 -6
  394. package/src/__tests__/render-target-probe-soak.perf.test.ts +2 -2
  395. package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +3 -3
  396. package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +5 -2
  397. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +41 -7
  398. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +16 -0
  399. package/src/__tests__/renderer-frame-transaction.integration.test.ts +33 -11
  400. package/src/__tests__/resource-allocation.unit.test.ts +123 -0
  401. package/src/__tests__/shader-manifest-fixture.ts +4 -4
  402. package/src/__tests__/single-layer-medium-passes.unit.test.ts +275 -0
  403. package/src/__tests__/skin-motion-regression.unit.test.ts +70 -1
  404. package/src/__tests__/skin-palette-recycling.dawn.test.ts +97 -0
  405. package/src/__tests__/skinned-shadow-caster.test.ts +17 -0
  406. package/src/__tests__/ssr-carrier-public-contract.unit.test.ts +21 -0
  407. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +4 -4
  408. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +2 -2
  409. package/src/__tests__/ssr-gpu-dispatch.ts +1 -1
  410. package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +6 -2
  411. package/src/__tests__/standard-lighting-write.unit.test.ts +2 -1
  412. package/src/__tests__/standard-output-chain-contract.unit.test.ts +28 -0
  413. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +46 -1
  414. package/src/__tests__/standard-pipeline.dawn.test.ts +1 -1
  415. package/src/__tests__/standard-pipeline.integration.test.ts +200 -9
  416. package/src/__tests__/surface-dynamic-buffer-lifecycle.unit.test.ts +506 -0
  417. package/src/__tests__/surface-submission-observation.unit.test.ts +812 -0
  418. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +32 -0
  419. package/src/__tests__/temporal-performance.integration.test.ts +3 -3
  420. package/src/__tests__/temporal-reset-matrix.unit.test.ts +10 -0
  421. package/src/__tests__/temporal-view.unit.test.ts +2 -1
  422. package/src/__tests__/typed-pipeline-topology.unit.test.ts +20 -4
  423. package/src/__tests__/visibility-instances.integration.test.ts +7 -19
  424. package/src/__tests__/visibility-producer-matrix.integration.test.ts +9 -20
  425. package/src/__tests__/volume-optics-oracle.unit.test.ts +7 -3
  426. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +189 -15
  427. package/src/__tests__/volumetric-fog-errors.unit.test.ts +4 -3
  428. package/src/__tests__/volumetric-fog-multiple.unit.test.ts +55 -0
  429. package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +1 -1
  430. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +558 -68
  431. package/src/__tests__/volumetric-fog-temporal.unit.test.ts +27 -0
  432. package/src/__tests__/volumetric-fog-topology.unit.test.ts +23 -5
  433. package/src/assembly/analytic-fog-registration.ts +14 -0
  434. package/src/assembly/backend-contract.ts +30 -0
  435. package/src/assembly/barrel-distortion-inspection.ts +22 -0
  436. package/src/assembly/canvas-draw-guard.ts +11 -0
  437. package/src/assembly/cloud-shadow-entry.ts +22 -0
  438. package/src/assembly/dynamic-geometry-host.ts +65 -37
  439. package/src/assembly/dynamic-geometry-runtime.ts +7 -1
  440. package/src/assembly/factory.ts +2 -30
  441. package/src/assembly/fxaa-registration.ts +17 -0
  442. package/src/assembly/host-contract.ts +3 -3
  443. package/src/assembly/lod-observation.ts +12 -0
  444. package/src/assembly/material/assembly.ts +25 -13
  445. package/src/assembly/material/pipeline-helpers.ts +53 -12
  446. package/src/assembly/material-shader-policy.ts +55 -0
  447. package/src/assembly/material-shader-prewarm.ts +21 -10
  448. package/src/assembly/motion-blur-registration.ts +17 -0
  449. package/src/assembly/recovery/device-loss-fanout.ts +13 -0
  450. package/src/assembly/recovery/renderer-recover.ts +41 -18
  451. package/src/assembly/render-feature-post-processes.ts +54 -0
  452. package/src/assembly/renderer-facade.ts +6 -1
  453. package/src/assembly/renderer-feature-inspection.ts +8 -2
  454. package/src/assembly/renderer-frame-transaction.ts +23 -0
  455. package/src/assembly/renderer-inspection.ts +28 -0
  456. package/src/assembly/skin-palette-owner.ts +1 -1
  457. package/src/assembly/temporal-post-process-prewarm.ts +40 -0
  458. package/src/assembly/webgpu-pbr-ready.ts +9 -1
  459. package/src/assembly/webgpu-ready-contract.ts +8 -3
  460. package/src/assembly/webgpu-ready-per-pass.ts +104 -0
  461. package/src/assembly/webgpu-ready.ts +296 -306
  462. package/src/assembly/webgpu-renderer-bootstrap.ts +0 -12
  463. package/src/assembly/webgpu-renderer-observation-owner.ts +222 -0
  464. package/src/assembly/webgpu-renderer.ts +228 -281
  465. package/src/assembly/webgpu-vertex-layouts.ts +1 -1
  466. package/src/barrel-distortion.ts +247 -0
  467. package/src/bloom-admission.ts +21 -0
  468. package/src/cloud/__tests__/cloud-density-parity.dawn.test.ts +173 -0
  469. package/src/cloud/__tests__/cloud-layer-framebuffer-y.dawn.test.ts +265 -0
  470. package/src/cloud/__tests__/cloud-layer.browser.test.ts +432 -0
  471. package/src/cloud/__tests__/cloud-layer.unit.test.ts +634 -0
  472. package/src/cloud/density.ts +600 -0
  473. package/src/cloud/extract.ts +110 -0
  474. package/src/cloud/feature.ts +1810 -0
  475. package/src/cloud/frame-context.ts +42 -0
  476. package/src/cloud/inspection.ts +130 -0
  477. package/src/cloud/optics.ts +285 -0
  478. package/src/cloud/parameters.ts +255 -0
  479. package/src/cloud/resources.ts +122 -0
  480. package/src/cloud/shadow.ts +167 -0
  481. package/src/cloud/temporal.ts +226 -0
  482. package/src/components/__tests__/motion-blur.unit.test.ts +1 -0
  483. package/src/components/atmosphere.ts +4 -0
  484. package/src/components/barrel-distortion.ts +46 -0
  485. package/src/components/camera.ts +66 -10
  486. package/src/components/cloud-layer.ts +47 -0
  487. package/src/components/index.ts +2 -0
  488. package/src/components/instances.ts +18 -45
  489. package/src/components/mesh-filter.ts +11 -9
  490. package/src/components/motion-blur.ts +1 -0
  491. package/src/components/sprite-instances.ts +1 -1
  492. package/src/construct-renderer.ts +1 -1
  493. package/src/device/gpu-residency.ts +56 -12
  494. package/src/dynamic-geometry.ts +12 -1
  495. package/src/environment/analytic-fog.ts +44 -0
  496. package/src/environment/background.ts +2 -2
  497. package/src/errors/cloud.ts +103 -0
  498. package/src/errors/render.ts +58 -8
  499. package/src/examples/single-layer-medium-surface.ts +59 -0
  500. package/src/extract/camera.ts +35 -8
  501. package/src/extract/contracts.ts +12 -0
  502. package/src/extract/environment.ts +4 -0
  503. package/src/features/barrel-distortion.ts +172 -0
  504. package/src/features/error-reporting.ts +98 -0
  505. package/src/features/host.ts +138 -24
  506. package/src/features/motion-blur/__tests__/motion-blur-feature.unit.test.ts +111 -1
  507. package/src/features/motion-blur/__tests__/motion-blur-params.unit.test.ts +58 -3
  508. package/src/features/motion-blur/motion-blur-feature.ts +1519 -15
  509. package/src/features/motion-blur/motion-blur-params.ts +70 -2
  510. package/src/features/motion-blur/motion-blur-runtime.ts +176 -0
  511. package/src/features/plan.ts +33 -8
  512. package/src/features/prepared-gpu-work.ts +86 -35
  513. package/src/features/render-graph-compute.ts +13 -5
  514. package/src/features/render-graph-contribution.ts +11 -0
  515. package/src/features/render-graph-raster.ts +45 -3
  516. package/src/features/targets.ts +66 -0
  517. package/src/features/types.ts +47 -0
  518. package/src/fullscreen-post-process-pass.ts +41 -9
  519. package/src/gpu-dirty-ranges.ts +48 -0
  520. package/src/gpu-driven/batch-topology.ts +53 -0
  521. package/src/gpu-driven/material-artifacts.ts +35 -32
  522. package/src/gpu-driven/prepared-draw.ts +27 -14
  523. package/src/gpu-driven/production-raster-material.ts +1894 -0
  524. package/src/gpu-driven/production-raster.ts +904 -787
  525. package/src/gpu-driven/resource-allocation.ts +142 -0
  526. package/src/gpu-driven/shadow-ownership.ts +71 -0
  527. package/src/gpu-driven/view-gpu.ts +181 -10
  528. package/src/gpu-scene.ts +259 -101
  529. package/src/hdrp-buffers.ts +4 -3
  530. package/src/ibl/skylight-bind-group.ts +49 -7
  531. package/src/index.ts +228 -9
  532. package/src/inspection-types.ts +222 -2
  533. package/src/instance-buffer-cache.ts +4 -4
  534. package/src/instances.ts +151 -492
  535. package/src/pbr-pipeline.ts +219 -13
  536. package/src/pipeline/single-layer-medium-passes.ts +267 -0
  537. package/src/pipeline/standard-forward-lane.ts +313 -57
  538. package/src/pipeline/standard-output/graph.ts +8 -2
  539. package/src/pipeline/standard-output/types.ts +12 -1
  540. package/src/pipeline/standard-pipeline.ts +291 -18
  541. package/src/pipeline/standard-post.ts +397 -102
  542. package/src/pipeline-builder.ts +1 -1
  543. package/src/pipeline-spec.ts +5 -5
  544. package/src/plugin.ts +4 -0
  545. package/src/points-lines/__tests__/points-lines.bench.ts +17 -8
  546. package/src/points-lines/__tests__/view-ubo.unit.test.ts +2 -1
  547. package/src/prepare/prepared-graphics-resolver.ts +2 -0
  548. package/src/record/__tests__/main-pass-geometry.unit.test.ts +30 -0
  549. package/src/record/__tests__/sprite-material-abi.unit.test.ts +1 -1
  550. package/src/record/__tests__/temporal-payload.unit.test.ts +3 -2
  551. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +11 -0
  552. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -0
  553. package/src/record/__tests__/view-ubo-layout.unit.test.ts +4 -3
  554. package/src/record/barrel-distortion-frame.ts +110 -0
  555. package/src/record/dynamic-geometry-consumption.ts +39 -9
  556. package/src/record/fold-buckets.ts +852 -0
  557. package/src/record/fold-instance-buffer.ts +51 -0
  558. package/src/record/frame-lighting.ts +36 -2
  559. package/src/record/frame-snapshot.ts +44 -4
  560. package/src/record/frame-targets.ts +10 -6
  561. package/src/record/frame.ts +373 -1099
  562. package/src/record/gpu-draw-receipts.ts +38 -0
  563. package/src/record/gpu-timing.ts +48 -5
  564. package/src/record/main-pass-geometry.ts +281 -83
  565. package/src/record/main-pass-material.ts +66 -4
  566. package/src/record/main-pass-sprite-draws.ts +13 -66
  567. package/src/record/main-pass.ts +156 -20
  568. package/src/record/mesh-ssbo.ts +43 -6
  569. package/src/record/motion-blur-receipt.ts +91 -0
  570. package/src/record/probe-blend-buffer.ts +180 -0
  571. package/src/record/recovery-pipeline.ts +35 -3
  572. package/src/record/render-context.ts +51 -31
  573. package/src/record/shadow-pass.ts +36 -2
  574. package/src/record/skybox-post-pass.ts +150 -200
  575. package/src/record/typed-frame-graph.ts +824 -44
  576. package/src/record/view-ubo.ts +26 -3
  577. package/src/record/volume-params.ts +225 -0
  578. package/src/recovery/render-system-candidate.ts +257 -52
  579. package/src/reflection/record-owner.ts +7 -3
  580. package/src/render-contract.ts +71 -8
  581. package/src/render-graph-primitives.ts +7 -1
  582. package/src/render-pipeline.ts +136 -1
  583. package/src/render-system-extract-tail.ts +72 -60
  584. package/src/render-system-extract.ts +117 -87
  585. package/src/render-system-presentation.ts +56 -0
  586. package/src/render-system-producer-roots.ts +96 -0
  587. package/src/render-system-residency.ts +159 -0
  588. package/src/render-system.ts +601 -482
  589. package/src/scene/probe-blend.ts +96 -4
  590. package/src/scene/render-scene.ts +431 -281
  591. package/src/ssao-buffers.ts +95 -2
  592. package/src/surface/__tests__/dynamic-input.unit.test.ts +374 -0
  593. package/src/surface/__tests__/gpu-driven.unit.test.ts +87 -0
  594. package/src/surface/dynamic-input.ts +658 -0
  595. package/src/surface/gpu-driven.ts +147 -0
  596. package/src/surface/submission-observation.ts +517 -0
  597. package/src/systems/skin-palette-allocator.ts +4 -1
  598. package/src/temporal/__tests__/temporal-frame-transaction.unit.test.ts +36 -0
  599. package/src/temporal/frame-coordinator.ts +8 -1
  600. package/src/temporal/frame.ts +20 -0
  601. package/src/temporal/gpu.ts +101 -17
  602. package/src/temporal/inspection.ts +7 -1
  603. package/src/temporal/standard-scene-data.ts +5 -0
  604. package/src/temporal/target-coverage-attachment.ts +4 -0
  605. package/src/temporal/view.ts +13 -2
  606. package/src/transmission/__tests__/single-layer-medium.unit.test.ts +149 -0
  607. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +12 -11
  608. package/src/transmission/single-layer-medium.ts +322 -0
  609. package/src/typed-render-graph-primitives.ts +191 -50
  610. package/src/volume/component.ts +10 -10
  611. package/src/volume/extract.ts +11 -5
  612. package/src/volume/inspection.ts +3 -0
  613. package/src/volume/passes.ts +95 -8
  614. package/src/volume/resources.ts +2 -1
  615. package/src/volume/temporal.ts +11 -0
  616. package/dist/chunk-4NNZQCL3.mjs.map +0 -1
  617. package/dist/chunk-6TFBXLRJ.mjs.map +0 -1
  618. package/dist/chunk-DRR7D3KX.mjs.map +0 -1
  619. package/dist/chunk-GWCEMDVR.mjs.map +0 -1
  620. package/dist/chunk-JCIRYQ4V.mjs.map +0 -1
  621. package/dist/chunk-JRIPCMYC.mjs.map +0 -1
  622. package/dist/chunk-LULCA3OF.mjs +0 -1713
  623. package/dist/chunk-LULCA3OF.mjs.map +0 -1
  624. package/dist/chunk-PA5DRJNI.mjs.map +0 -1
@@ -4,8 +4,11 @@ import {
4
4
  type GraphResourceResolver,
5
5
  RenderGraphBuilder,
6
6
  RenderGraphError,
7
+ type RenderGraphGenerationAllocationEntry,
8
+ type RenderGraphGenerationAllocationInspection,
7
9
  type RenderGraphPassInstrumentation,
8
10
  type RenderGraphPassInstrumentationScope,
11
+ type RenderGraphResourceAllocationInspection,
9
12
  } from '@forgeax/engine-render-graph';
10
13
  import type {
11
14
  BindGroup,
@@ -22,6 +25,8 @@ import type {
22
25
  import { RhiError } from '@forgeax/engine-rhi';
23
26
  import { err, ok, type Result } from '@forgeax/engine-types';
24
27
  import { executeRendererFrameTransaction } from '../assembly/renderer-frame-transaction';
28
+ import { standardBloomAdmitted } from '../bloom-admission';
29
+ import { CLOUD_LAYER_FEATURE_IDENTITY } from '../cloud/feature';
25
30
  import { type RenderError, RenderFeatureStageFailedError } from '../errors/render';
26
31
  import { depthOfFieldTopology } from '../features/depth-of-field/depth-of-field-feature';
27
32
  import { resolveDepthOfFieldFrameParams } from '../features/depth-of-field/depth-of-field-params';
@@ -30,6 +35,7 @@ import {
30
35
  hasSubmissionSensitiveFeatures,
31
36
  type RenderFeaturePlanExecution,
32
37
  } from '../features/host';
38
+ import { motionBlurTemporalDemand } from '../features/motion-blur/motion-blur-params';
33
39
  import {
34
40
  type RenderFeaturePlannedFrame,
35
41
  renderFeaturePlanSignature,
@@ -93,11 +99,13 @@ import type { RenderTarget } from '../targets/contracts';
93
99
  import type { RenderTargetPhysical } from '../targets/physical';
94
100
  import {
95
101
  abortTemporalGpuSubmit,
102
+ CLOUD_HISTORY_FORMATS,
96
103
  commitTemporalGpuSubmit,
97
104
  getTemporalGpuState,
98
105
  hasPendingTemporalGpuSubmit,
99
106
  retireTemporalGpuState,
100
107
  retireTemporalGpuStateAfterFence,
108
+ stageTemporalGpuSubmit,
101
109
  TEMPORAL_HISTORY_FORMATS,
102
110
  temporalReadIndex,
103
111
  temporalWriteIndex,
@@ -118,6 +126,7 @@ import type { GpuPassTimingPassIdentity } from './gpu-pass-timing/contract.js';
118
126
  import type { GpuPassTimingCapture, GpuPassTimingSession } from './gpu-pass-timing/session.js';
119
127
  import type { GpuTimingCapture } from './gpu-timing';
120
128
  import { encodeMainPass } from './main-pass';
129
+ import { deriveMotionBlurExecutionReceipt } from './motion-blur-receipt';
121
130
  import type {
122
131
  _InternalRenderPipelineContext,
123
132
  PipelineState,
@@ -321,6 +330,125 @@ function importTemporalHistoryTargets(
321
330
  };
322
331
  }
323
332
 
333
+ type CloudHistoryKind = keyof typeof CLOUD_HISTORY_FORMATS;
334
+
335
+ function cloudHistorySurface(
336
+ state: import('../temporal/gpu').TemporalGpuState,
337
+ kind: CloudHistoryKind,
338
+ index: 0 | 1,
339
+ ): { readonly texture: Texture; readonly view: TextureView } {
340
+ if (!state.cloudHistoryEnabled) {
341
+ throw new Error('cloud history target resolved without a cloud-enabled temporal state');
342
+ }
343
+ switch (kind) {
344
+ case 'radiance':
345
+ if (state.cloudRadiance === undefined)
346
+ throw new Error('cloud radiance history is unavailable');
347
+ return state.cloudRadiance[index];
348
+ case 'transmittance':
349
+ if (state.cloudTransmittance === undefined)
350
+ throw new Error('cloud transmittance history is unavailable');
351
+ return state.cloudTransmittance[index];
352
+ case 'depth':
353
+ if (state.cloudDepth === undefined) throw new Error('cloud depth history is unavailable');
354
+ return state.cloudDepth[index];
355
+ }
356
+ }
357
+
358
+ /** Import one renderer-owned cloud history attachment into the typed graph. */
359
+ function importCloudHistoryTarget(
360
+ graph: RenderGraphBuilder<RenderPipelineFrame>,
361
+ label: string,
362
+ kind: CloudHistoryKind,
363
+ role: 'read' | 'write',
364
+ ): import('../render-pipeline').RenderPipelineTarget {
365
+ const format = CLOUD_HISTORY_FORMATS[kind];
366
+ const texture = graph.importTexture(
367
+ label,
368
+ {
369
+ format,
370
+ // Cloud transport/history is intentionally half-resolution. The
371
+ // renderer-owned temporal state allocates the same ceil(width/2) extent
372
+ // and the full-resolution resolve reconstructs scene colour from it.
373
+ size: 'half-surface',
374
+ usage:
375
+ GPU_TEXTURE_USAGE_COPY_SRC |
376
+ GPU_TEXTURE_USAGE_COPY_DST |
377
+ GPU_TEXTURE_USAGE_RENDER_ATTACHMENT |
378
+ GPU_TEXTURE_USAGE_TEXTURE_BINDING,
379
+ domain: 'linear-hdr',
380
+ },
381
+ (frame) => {
382
+ const internal = frame as _InternalRenderPipelineContext;
383
+ const state = getTemporalGpuState(
384
+ internal.frameState,
385
+ internal.runtime.device,
386
+ internal.runtime.deviceScope,
387
+ frame.targetW,
388
+ frame.targetH,
389
+ true,
390
+ );
391
+ const index = role === 'read' ? temporalReadIndex(state) : temporalWriteIndex(state);
392
+ return cloudHistorySurface(state, kind, index).texture;
393
+ },
394
+ );
395
+ if (!texture.ok) throw texture.error;
396
+ const view = graph.importView(
397
+ texture.value,
398
+ { label: `${label}.view`, dimension: '2d' },
399
+ (frame) => {
400
+ const internal = frame as _InternalRenderPipelineContext;
401
+ const state = getTemporalGpuState(
402
+ internal.frameState,
403
+ internal.runtime.device,
404
+ internal.runtime.deviceScope,
405
+ frame.targetW,
406
+ frame.targetH,
407
+ true,
408
+ );
409
+ const index = role === 'read' ? temporalReadIndex(state) : temporalWriteIndex(state);
410
+ return cloudHistorySurface(state, kind, index).view;
411
+ },
412
+ );
413
+ if (!view.ok) throw view.error;
414
+ return { texture: texture.value, view: view.value, format, sampleCount: 1 };
415
+ }
416
+
417
+ function importCloudHistoryTargets(
418
+ graph: RenderGraphBuilder<RenderPipelineFrame>,
419
+ ): NonNullable<
420
+ import('../render-pipeline').RenderPipelineBuildContext<RenderPipelineFrame>['cloudHistory']
421
+ > {
422
+ return {
423
+ currentRadiance: importCloudHistoryTarget(
424
+ graph,
425
+ 'cloud-history-radiance-current',
426
+ 'radiance',
427
+ 'write',
428
+ ),
429
+ previousRadiance: importCloudHistoryTarget(
430
+ graph,
431
+ 'cloud-history-radiance-previous',
432
+ 'radiance',
433
+ 'read',
434
+ ),
435
+ currentTransmittance: importCloudHistoryTarget(
436
+ graph,
437
+ 'cloud-history-transmittance-current',
438
+ 'transmittance',
439
+ 'write',
440
+ ),
441
+ previousTransmittance: importCloudHistoryTarget(
442
+ graph,
443
+ 'cloud-history-transmittance-previous',
444
+ 'transmittance',
445
+ 'read',
446
+ ),
447
+ currentDepth: importCloudHistoryTarget(graph, 'cloud-history-depth-current', 'depth', 'write'),
448
+ previousDepth: importCloudHistoryTarget(graph, 'cloud-history-depth-previous', 'depth', 'read'),
449
+ };
450
+ }
451
+
324
452
  function importSsrHistoryTarget(
325
453
  graph: RenderGraphBuilder<RenderPipelineFrame>,
326
454
  label: string,
@@ -435,7 +563,15 @@ export function shouldUseTemporalFrameTransaction(
435
563
  export function isMotionBlurTemporalDemand(
436
564
  motionBlur: CameraSnapshot['motionBlur'] | undefined,
437
565
  ): boolean {
438
- return motionBlur?.shutterAngle !== undefined && motionBlur.shutterAngle > 0;
566
+ return (
567
+ motionBlur !== undefined &&
568
+ motionBlurTemporalDemand({
569
+ shutterAngle: motionBlur.shutterAngle,
570
+ maxRadiusPixels: motionBlur.maxRadiusPixels,
571
+ sampleCount: motionBlur.sampleCount,
572
+ targetFps: motionBlur.targetFps ?? 60,
573
+ })
574
+ );
439
575
  }
440
576
 
441
577
  /**
@@ -968,6 +1104,7 @@ export interface RenderFeatureGraphRuntimeState {
968
1104
  string,
969
1105
  import('../fullscreen-post-process-pass').PostProcessShaderEntry
970
1106
  >;
1107
+ postProcessIdentities: readonly string[];
971
1108
  }
972
1109
 
973
1110
  /** The exact fields that define one validated feature-plan collection. */
@@ -1028,6 +1165,8 @@ export interface RenderFeatureGraphCandidate {
1028
1165
  string,
1029
1166
  import('../fullscreen-post-process-pass').PostProcessShaderEntry
1030
1167
  >;
1168
+ /** Public Standard post-effect identities; graph-local resources stay internal. */
1169
+ readonly postProcessIdentities?: readonly string[];
1031
1170
  /**
1032
1171
  * Physical transient graphics resources are imported into the compiled
1033
1172
  * graph. Their lease therefore needs a new graph even when the declarative
@@ -1051,6 +1190,216 @@ export interface RenderFeatureGraphCandidate {
1051
1190
 
1052
1191
  const featureGraphStates = new WeakMap<RenderSystemInternals, RenderFeatureGraphRuntimeState>();
1053
1192
  const graphDeviceGenerations = new WeakMap<object, number>();
1193
+ const graphRetirementFailures = new WeakMap<
1194
+ RenderFrameState,
1195
+ Set<CompiledRenderGraph<RenderPipelineFrame>>
1196
+ >();
1197
+
1198
+ type GraphGenerationRole = 'active' | 'candidate' | 'retiring';
1199
+ type GraphGenerationStatus = 'active' | 'retiring' | 'failed' | 'retired';
1200
+
1201
+ interface GraphGenerationAllocationRecord {
1202
+ readonly graph: CompiledRenderGraph<RenderPipelineFrame>;
1203
+ status: GraphGenerationStatus;
1204
+ allocation: RenderGraphResourceAllocationInspection | undefined;
1205
+ }
1206
+
1207
+ /**
1208
+ * One stable allocation owner for a RenderSystem lifetime. Recovery builds a
1209
+ * detached frame state, but that state intentionally shares this owner before
1210
+ * it can publish a candidate into the active RenderSystem. Graph records stay
1211
+ * here across asynchronous retirement so a successful replacement cannot
1212
+ * erase the overlap peak before the next public inspection.
1213
+ */
1214
+ interface GraphGenerationAllocationOwner {
1215
+ readonly records: Map<CompiledRenderGraph<RenderPipelineFrame>, GraphGenerationAllocationRecord>;
1216
+ peakBytes: number;
1217
+ }
1218
+
1219
+ const graphGenerationAllocationOwners = new WeakMap<
1220
+ RenderFrameState,
1221
+ GraphGenerationAllocationOwner
1222
+ >();
1223
+
1224
+ function graphGenerationAllocationOwner(
1225
+ frameState: RenderFrameState,
1226
+ ): GraphGenerationAllocationOwner {
1227
+ const existing = graphGenerationAllocationOwners.get(frameState);
1228
+ if (existing !== undefined) return existing;
1229
+ const created: GraphGenerationAllocationOwner = { records: new Map(), peakBytes: 0 };
1230
+ graphGenerationAllocationOwners.set(frameState, created);
1231
+ return created;
1232
+ }
1233
+
1234
+ function graphRetirementFailureSet(
1235
+ frameState: RenderFrameState,
1236
+ ): Set<CompiledRenderGraph<RenderPipelineFrame>> {
1237
+ const existing = graphRetirementFailures.get(frameState);
1238
+ if (existing !== undefined) return existing;
1239
+ const created = new Set<CompiledRenderGraph<RenderPipelineFrame>>();
1240
+ graphRetirementFailures.set(frameState, created);
1241
+ return created;
1242
+ }
1243
+
1244
+ /** Share the renderer-owned graph ledger with a detached recovery frame state. */
1245
+ export function shareRenderGraphGenerationAllocationOwner(
1246
+ source: RenderFrameState,
1247
+ target: RenderFrameState,
1248
+ ): void {
1249
+ graphGenerationAllocationOwners.set(target, graphGenerationAllocationOwner(source));
1250
+ graphRetirementFailures.set(target, graphRetirementFailureSet(source));
1251
+ }
1252
+
1253
+ function observeGraphGenerationAllocation(frameState: RenderFrameState): void {
1254
+ const owner = graphGenerationAllocationOwner(frameState);
1255
+ let simultaneousBytes = 0;
1256
+ for (const record of owner.records.values()) {
1257
+ if (record.status === 'retired') continue;
1258
+ const allocation = record.graph.inspect().resourceAllocation;
1259
+ record.allocation = allocation;
1260
+ if (allocation !== undefined) {
1261
+ simultaneousBytes += allocation.liveBytes + allocation.pendingRetirementBytes;
1262
+ }
1263
+ }
1264
+ owner.peakBytes = Math.max(owner.peakBytes, simultaneousBytes);
1265
+ }
1266
+
1267
+ function trackGraphGeneration(
1268
+ frameState: RenderFrameState,
1269
+ graph: CompiledRenderGraph<RenderPipelineFrame>,
1270
+ ): void {
1271
+ const owner = graphGenerationAllocationOwner(frameState);
1272
+ const record = owner.records.get(graph);
1273
+ if (record === undefined) {
1274
+ owner.records.set(graph, { graph, status: 'active', allocation: undefined });
1275
+ } else if (record.status === 'retired') {
1276
+ record.status = 'active';
1277
+ }
1278
+ observeGraphGenerationAllocation(frameState);
1279
+ }
1280
+
1281
+ function setGraphGenerationRetiring(
1282
+ frameState: RenderFrameState,
1283
+ graph: CompiledRenderGraph<RenderPipelineFrame>,
1284
+ ): void {
1285
+ const owner = graphGenerationAllocationOwner(frameState);
1286
+ const record = owner.records.get(graph);
1287
+ if (record === undefined) {
1288
+ owner.records.set(graph, { graph, status: 'retiring', allocation: undefined });
1289
+ } else if (record.status !== 'failed') {
1290
+ record.status = 'retiring';
1291
+ }
1292
+ }
1293
+
1294
+ function setGraphGenerationRetirementResult(
1295
+ frameState: RenderFrameState,
1296
+ graph: CompiledRenderGraph<RenderPipelineFrame>,
1297
+ succeeded: boolean,
1298
+ ): void {
1299
+ const owner = graphGenerationAllocationOwner(frameState);
1300
+ const record = owner.records.get(graph);
1301
+ if (record !== undefined) record.status = succeeded ? 'retired' : 'failed';
1302
+ observeGraphGenerationAllocation(frameState);
1303
+ // A successful graph no longer owns any handles. Keep the renderer-level
1304
+ // peak scalar, but release the graph object from the generation map; failed
1305
+ // and pending owners remain retained for recovery inspection.
1306
+ if (succeeded) owner.records.delete(graph);
1307
+ }
1308
+
1309
+ /**
1310
+ * Project the actual RenderSystem graph-generation owners as one detached
1311
+ * allocation receipt. The candidate and fallback graph can overlap before a
1312
+ * submit settles; failed retirement stays visible until the frame owner is
1313
+ * discarded instead of disappearing with the asynchronous Set entry.
1314
+ */
1315
+ export function inspectRenderGraphGenerationAllocation(
1316
+ frameState: RenderFrameState,
1317
+ ): RenderGraphGenerationAllocationInspection {
1318
+ const rolesByGraph = new Map<
1319
+ CompiledRenderGraph<RenderPipelineFrame>,
1320
+ Set<GraphGenerationRole>
1321
+ >();
1322
+ const add = (
1323
+ graph: CompiledRenderGraph<RenderPipelineFrame> | null | undefined,
1324
+ role: GraphGenerationRole,
1325
+ ) => {
1326
+ if (graph === null || graph === undefined) return;
1327
+ const roles = rolesByGraph.get(graph);
1328
+ if (roles === undefined) rolesByGraph.set(graph, new Set([role]));
1329
+ else roles.add(role);
1330
+ };
1331
+ add(frameState.compiledFrameGraph, 'active');
1332
+ add(frameState.volumetricFogCandidateGraph, 'candidate');
1333
+ add(frameState.volumetricFogPreviousGraph, 'retiring');
1334
+ add(frameState.depthOfFieldCandidateGraph, 'candidate');
1335
+ add(frameState.depthOfFieldPreviousGraph, 'retiring');
1336
+ add(frameState.barrelDistortionCandidateGraph, 'candidate');
1337
+ add(frameState.barrelDistortionPreviousGraph, 'retiring');
1338
+ for (const graph of frameState.retiredCompiledFrameGraphs) add(graph, 'retiring');
1339
+ for (const graph of graphRetirementFailureSet(frameState)) add(graph, 'retiring');
1340
+
1341
+ const owner = graphGenerationAllocationOwner(frameState);
1342
+ const entries: RenderGraphGenerationAllocationEntry[] = [];
1343
+ let unavailableGenerationCount = 0;
1344
+ for (const [graph, record] of owner.records) {
1345
+ if (record.status === 'retired') continue;
1346
+ const roles = rolesByGraph.get(graph) ?? new Set<GraphGenerationRole>();
1347
+ if (record.status === 'retiring' || record.status === 'failed') roles.add('retiring');
1348
+ if (roles.size === 0) {
1349
+ // A candidate can be detached from the active state's role fields while
1350
+ // it is still being prepared. Keep its owner record observable until
1351
+ // the publication or cleanup event settles it.
1352
+ roles.add(record.status === 'active' ? 'active' : 'retiring');
1353
+ }
1354
+ // Inspection is a projection of the graph's current ledger. The cached
1355
+ // snapshot is only for event-time peak updates; using it here would make
1356
+ // a successfully fenced graph look live after its retirement started.
1357
+ const allocation = graph.inspect().resourceAllocation;
1358
+ record.allocation = allocation;
1359
+ if (allocation === undefined) {
1360
+ unavailableGenerationCount += 1;
1361
+ continue;
1362
+ }
1363
+ const retirement =
1364
+ record.status === 'failed' ? 'failed' : roles.has('retiring') ? 'pending' : 'active';
1365
+ entries.push({
1366
+ generation: graph.inspect().generation,
1367
+ roles: Object.freeze([...roles]),
1368
+ retirement,
1369
+ allocation,
1370
+ });
1371
+ }
1372
+ entries.sort((left, right) => left.generation - right.generation);
1373
+ const liveBytes = entries.reduce((sum, entry) => sum + entry.allocation.liveBytes, 0);
1374
+ const pendingRetirementBytes = entries.reduce(
1375
+ (sum, entry) => sum + entry.allocation.pendingRetirementBytes,
1376
+ 0,
1377
+ );
1378
+ const peakBytes = Math.max(owner.peakBytes, liveBytes + pendingRetirementBytes);
1379
+ const failedEntries = entries.filter((entry) => entry.retirement === 'failed');
1380
+ return Object.freeze({
1381
+ unit: 'engine-allocation-bytes',
1382
+ physicalResidency: 'unknown',
1383
+ availability:
1384
+ entries.length === 0
1385
+ ? unavailableGenerationCount === 0
1386
+ ? 'complete'
1387
+ : 'unavailable'
1388
+ : unavailableGenerationCount === 0
1389
+ ? 'complete'
1390
+ : 'partial',
1391
+ unavailableGenerationCount,
1392
+ entries: Object.freeze(entries.map((entry) => Object.freeze(entry))),
1393
+ liveBytes,
1394
+ pendingRetirementBytes,
1395
+ peakBytes,
1396
+ failedRetirementCount: failedEntries.length,
1397
+ failedRetirementBytes: failedEntries.reduce(
1398
+ (sum, entry) => sum + entry.allocation.liveBytes + entry.allocation.pendingRetirementBytes,
1399
+ 0,
1400
+ ),
1401
+ });
1402
+ }
1054
1403
 
1055
1404
  interface MutableRenderFeatureGraphInspection {
1056
1405
  planRevisionState: RenderFeaturePlanRevisionState;
@@ -1216,6 +1565,7 @@ export function getRenderFeatureGraphState(
1216
1565
  const created: RenderFeatureGraphRuntimeState = {
1217
1566
  plans: [],
1218
1567
  fullscreenEffects: new Map(),
1568
+ postProcessIdentities: [],
1219
1569
  };
1220
1570
  featureGraphStates.set(internals, created);
1221
1571
  return created;
@@ -1225,6 +1575,7 @@ export function resetRenderFeatureGraphState(internals: RenderSystemInternals):
1225
1575
  const state = getRenderFeatureGraphState(internals);
1226
1576
  state.plans = [];
1227
1577
  state.fullscreenEffects = new Map();
1578
+ state.postProcessIdentities = [];
1228
1579
  }
1229
1580
 
1230
1581
  export function reportRenderFeatureGraphError(
@@ -1343,6 +1694,7 @@ function commitFeatureCandidate(
1343
1694
  const state = getRenderFeatureGraphState(internals);
1344
1695
  state.plans = candidate.plans;
1345
1696
  state.fullscreenEffects = candidate.fullscreenEffects;
1697
+ state.postProcessIdentities = candidate.postProcessIdentities ?? [];
1346
1698
  }
1347
1699
 
1348
1700
  function topologyOf(
@@ -1364,6 +1716,8 @@ function topologyOf(
1364
1716
  standardLighting: StandardTopologyInputValue | undefined,
1365
1717
  ssr: SsrSpatialAdmission | undefined,
1366
1718
  renderExtent: RenderExtent | undefined,
1719
+ surfaceMediumActive: boolean | undefined,
1720
+ analyticFog: boolean,
1367
1721
  ): Result<RenderPipelineTopology, RhiError> {
1368
1722
  const cascadeCount = Math.max(1, Math.min(4, Math.round(lights.cascadeCount ?? 1))) as
1369
1723
  | 1
@@ -1384,10 +1738,9 @@ function topologyOf(
1384
1738
  floatRenderAttachment: internals.device.caps.rgba16floatRenderable,
1385
1739
  });
1386
1740
  if (!surfaceProfile.ok) return surfaceProfile;
1387
- const featurePostEffects = [
1388
- ...(featureGraphCandidate?.fullscreenEffects ??
1389
- getRenderFeatureGraphState(internals).fullscreenEffects),
1390
- ].map(([id]) => id);
1741
+ const featurePostEffects =
1742
+ featureGraphCandidate?.postProcessIdentities ??
1743
+ getRenderFeatureGraphState(internals).postProcessIdentities;
1391
1744
  const featurePostEffectDeclarations = [
1392
1745
  ...(featureGraphCandidate?.fullscreenEffects ??
1393
1746
  getRenderFeatureGraphState(internals).fullscreenEffects),
@@ -1430,6 +1783,7 @@ function topologyOf(
1430
1783
  reflectionFallback: { enabled: frameState.reflectionFallbackDemand === true },
1431
1784
  ssr,
1432
1785
  ...(renderExtent === undefined ? {} : { extent: renderExtent }),
1786
+ analyticFog: !clearOnly && analyticFog,
1433
1787
  surface: {
1434
1788
  width,
1435
1789
  height,
@@ -1440,7 +1794,12 @@ function topologyOf(
1440
1794
  camera: {
1441
1795
  tonemap: camera.tonemap,
1442
1796
  antialias: camera.antialias,
1443
- bloom: camera.bloom,
1797
+ // Bloom intensity is a frame parameter, not a graph identity. Fold
1798
+ // only its zero/non-zero admission into the closed topology switch so
1799
+ // positive intensity changes update the recorder UBO in place.
1800
+ bloom: standardBloomAdmitted(camera) ? 'on' : 'off',
1801
+ bloomIntensity: camera.bloomIntensity,
1802
+ barrelDistortion: !clearOnly && (camera.barrelDistortion?.strength ?? 0) > 0,
1444
1803
  ...(depthOfField === undefined ? {} : { depthOfField }),
1445
1804
  },
1446
1805
  atmosphere: !clearOnly && frameState.environmentFrame?.source.kind === 'atmosphere',
@@ -1494,6 +1853,16 @@ function topologyOf(
1494
1853
  return {
1495
1854
  enabled: true,
1496
1855
  format: volumetricFog.densityAsset.format,
1856
+ additional: (volumetricFog.additional ?? []).flatMap((member) =>
1857
+ member.densityAsset?.shape.viewDimension === '3d'
1858
+ ? [
1859
+ {
1860
+ format: member.densityAsset.format,
1861
+ extent: member.densityAsset.shape.extent,
1862
+ },
1863
+ ]
1864
+ : [],
1865
+ ),
1497
1866
  extent: {
1498
1867
  width: volumetricFog.densityAsset.shape.extent.width,
1499
1868
  height: volumetricFog.densityAsset.shape.extent.height,
@@ -1501,7 +1870,7 @@ function topologyOf(
1501
1870
  },
1502
1871
  froxelExtent: deriveVolumetricFogExtent(
1503
1872
  { width, height },
1504
- volumeProfile.depth,
1873
+ volumeProfile.depth * (1 + (volumetricFog.additional?.length ?? 0)),
1505
1874
  volumeProfile.tileSize,
1506
1875
  ),
1507
1876
  resolvedExtent: deriveVolumetricFogResolvedExtent(
@@ -1542,6 +1911,9 @@ function topologyOf(
1542
1911
  activeCount: 0,
1543
1912
  needsRoughMips: false,
1544
1913
  },
1914
+ ...((surfaceMediumActive ?? gpuDriven?.surfaceSubmission !== undefined)
1915
+ ? { singleLayerMedium: true }
1916
+ : {}),
1545
1917
  });
1546
1918
  }
1547
1919
 
@@ -1566,6 +1938,30 @@ function targetResolver(
1566
1938
  ...(named.resolveTarget === undefined ? {} : { resolveTarget: named.resolveTarget }),
1567
1939
  };
1568
1940
  }
1941
+ if (typeof resource === 'string') {
1942
+ const target = targets.find((candidate) => candidate.name === resource);
1943
+ if (target !== undefined) {
1944
+ return {
1945
+ texture: target.texture,
1946
+ view: target.view,
1947
+ ...(target.resolveTarget === undefined ? {} : { resolveTarget: target.resolveTarget }),
1948
+ };
1949
+ }
1950
+ // Standard lanes expose the depth attachment as a semantic target
1951
+ // without duplicating the public `depth` name on every concrete graph
1952
+ // projection. Keep the string alias stable for feature-authored
1953
+ // fullscreen bindings while preserving the typed target identity.
1954
+ if (resource === 'depth') {
1955
+ const depth = targets.find((candidate) => candidate.kind === 'scene-depth');
1956
+ if (depth !== undefined) {
1957
+ return {
1958
+ texture: depth.texture,
1959
+ view: depth.view,
1960
+ ...(depth.resolveTarget === undefined ? {} : { resolveTarget: depth.resolveTarget }),
1961
+ };
1962
+ }
1963
+ }
1964
+ }
1569
1965
  }
1570
1966
  if (isSceneDataTarget(resource)) {
1571
1967
  const semantic = semanticTargets.find(
@@ -1577,11 +1973,27 @@ function targetResolver(
1577
1973
  : { texture: semantic.texture, view: semantic.view };
1578
1974
  }
1579
1975
  if (!isRenderFeatureTargetHandle(resource)) return undefined;
1976
+ // A typed feature handle is an identity, not a format request. Falling
1977
+ // back to another same-format target can silently bind scene color in
1978
+ // place of a missing cloud history/shadow resource and turn a producer
1979
+ // failure into plausible but incorrect pixels.
1980
+ // Standard lanes expose scene depth as a semantic target without a
1981
+ // duplicated public name, while feature plans use the stable `depth`
1982
+ // handle name. Preserve that one semantic alias here as well as above for
1983
+ // string-authored fullscreen bindings.
1580
1984
  const target = targets.find(
1581
1985
  (candidate) =>
1582
1986
  candidate.kind === resource.kind &&
1583
1987
  candidate.format === resource.format &&
1584
- candidate.sampleCount === resource.sampleCount,
1988
+ candidate.sampleCount === resource.sampleCount &&
1989
+ (resource.name === undefined ||
1990
+ candidate.name === resource.name ||
1991
+ (resource.kind === 'scene-color' &&
1992
+ resource.name === 'color' &&
1993
+ candidate.kind === 'scene-color') ||
1994
+ (resource.kind === 'scene-depth' &&
1995
+ resource.name === 'depth' &&
1996
+ candidate.kind === 'scene-depth')),
1585
1997
  );
1586
1998
  return target === undefined
1587
1999
  ? undefined
@@ -1601,6 +2013,9 @@ function resolveTargetBindings(
1601
2013
  };
1602
2014
  readonly resources: GraphResourceResolver;
1603
2015
  readonly resolveTarget: RenderFeatureGraphTargetResolver;
2016
+ readonly resolveGpuResource?: (
2017
+ name: string,
2018
+ ) => import('../features/prepared-gpu-work').RenderFeatureResolvedGpuBuffer | undefined;
1604
2019
  },
1605
2020
  featurePostProcessEntries?: ReadonlyMap<string, PostProcessShaderEntry>,
1606
2021
  ): RenderFeatureGraphBindingsResolution | undefined {
@@ -1612,6 +2027,136 @@ function resolveTargetBindings(
1612
2027
  const sceneDepth = descriptor?.values.sceneDepth;
1613
2028
  const sceneDepthBinding = descriptor?.values.sceneDepthBinding;
1614
2029
  if (pipeline === undefined) return undefined;
2030
+ if (descriptor?.values.view === true) {
2031
+ const viewBindGroup = buildPerFrameBindGroups(
2032
+ frame.runtime as RenderSystemInternals,
2033
+ frame.frameState,
2034
+ frame.pipelineState,
2035
+ true,
2036
+ frame.bindGroupCounts,
2037
+ {
2038
+ directionalShadow: frame.frameState.currentDirectionalShadowView ?? undefined,
2039
+ spotShadow: frame.frameState.currentSpotShadowView ?? undefined,
2040
+ },
2041
+ true,
2042
+ frame.standardLighting,
2043
+ ).viewBindGroup;
2044
+ return viewBindGroup === null ? undefined : { handle: viewBindGroup, dynamicOffsets: [0, 0] };
2045
+ }
2046
+ // Generic fullscreen producers (for example the CloudLayer scene pass)
2047
+ // share the renderer-owned post-process bind-group contract without opting
2048
+ // into the temporal two-input path below. Their input/depth aliases are
2049
+ // resolved from the active typed graph, so they cannot accidentally sample
2050
+ // a stale surface view or manufacture a second resource owner.
2051
+ if (descriptor?.values.fullscreen === true && !isTemporalFullscreenBinding(descriptor.values)) {
2052
+ const shader = descriptor.values.shader;
2053
+ if (typeof shader !== 'string') return undefined;
2054
+ const entry =
2055
+ featurePostProcessEntries?.get(shader) ?? frame.runtime.lookupPostProcess?.(shader);
2056
+ if (entry === undefined) return undefined;
2057
+ const depthFallback = descriptor.values.depthFallback === true;
2058
+ const depthValue = descriptor.values.depth;
2059
+ const depthTarget =
2060
+ depthFallback || depthValue === undefined
2061
+ ? undefined
2062
+ : input.resolveTarget(depthValue as never);
2063
+ const fullscreen = buildFullscreenPostProcessPass(
2064
+ { device: frame.runtime.device, errorRegistry: frame.runtime.errorRegistry },
2065
+ entry,
2066
+ depthTarget?.resolveTarget !== undefined,
2067
+ );
2068
+ if (fullscreen === null || fullscreen.sampler === null) return undefined;
2069
+ const inputDisabled = descriptor.values.input === false;
2070
+ const inputTarget = inputDisabled
2071
+ ? undefined
2072
+ : input.resolveTarget(descriptor.values.input as never);
2073
+ const inputView =
2074
+ inputTarget === undefined
2075
+ ? { ok: true as const, value: frame.pipelineState.defaultWhiteTextureView }
2076
+ : input.resources.textureView(inputTarget.view);
2077
+ if (!inputView.ok) return undefined;
2078
+ const depthView = depthFallback
2079
+ ? frame.pipelineState.shadowFallbackTextureView
2080
+ : depthTarget === undefined
2081
+ ? undefined
2082
+ : (() => {
2083
+ const depthTexture = input.resources.texture(depthTarget.texture);
2084
+ if (!depthTexture.ok) return undefined;
2085
+ const created = frame.runtime.device.createTextureView(depthTexture.value as Texture, {
2086
+ aspect: 'depth-only',
2087
+ dimension: '2d',
2088
+ });
2089
+ return created.ok ? created.value : undefined;
2090
+ })();
2091
+ if (
2092
+ depthTarget !== undefined &&
2093
+ (depthView === undefined || fullscreen.depthSampler === null)
2094
+ ) {
2095
+ return undefined;
2096
+ }
2097
+ const additionalViews: { readonly binding: number; readonly view: TextureView }[] = [];
2098
+ const additionalNames = descriptor.values.additionalTextures;
2099
+ if (Array.isArray(additionalNames)) {
2100
+ for (const [index, value] of additionalNames.entries()) {
2101
+ if (typeof value !== 'string') return undefined;
2102
+ const target = input.resolveTarget(value);
2103
+ if (target === undefined) return undefined;
2104
+ const view = input.resources.textureView(target.view);
2105
+ if (!view.ok) return undefined;
2106
+ const binding = fullscreen.extraColorBindings[index];
2107
+ if (binding === undefined) return undefined;
2108
+ additionalViews.push({ binding, view: view.value });
2109
+ }
2110
+ }
2111
+ const layout = pipeline.getBindGroupLayout?.(1);
2112
+ if (layout === undefined) return undefined;
2113
+ const paramsBuffer = frame.runtime.getPostProcessParamsBuffer?.(shader);
2114
+ if (entry.params !== undefined && paramsBuffer === undefined) return undefined;
2115
+ if (entry.params !== undefined && paramsBuffer !== undefined) {
2116
+ const data = frame.postProcessParams.get(shader) ?? entry.params.defaultValue;
2117
+ if (data.byteLength !== entry.params.byteSize) return undefined;
2118
+ const written = frame.runtime.device.queue.writeBuffer(paramsBuffer, 0, data);
2119
+ if (!written.ok) return undefined;
2120
+ }
2121
+ const createdBindGroup = createFullscreenBindGroup(
2122
+ frame.runtime.device,
2123
+ layout,
2124
+ inputView.value,
2125
+ fullscreen.sampler,
2126
+ paramsBuffer,
2127
+ depthView ?? null,
2128
+ fullscreen.depthSampler,
2129
+ additionalViews,
2130
+ fullscreen.extraStorageBindings.map((binding, index) => {
2131
+ const names = descriptor.values.storageBuffers;
2132
+ const name = Array.isArray(names) ? names[index] : undefined;
2133
+ if (typeof name !== 'string') throw new Error('fullscreen storage buffer name missing');
2134
+ const buffer = input.resolveGpuResource?.(name);
2135
+ if (buffer === undefined) throw new Error(`fullscreen storage buffer unavailable: ${name}`);
2136
+ return { binding, buffer: buffer.buffer };
2137
+ }),
2138
+ );
2139
+ if (createdBindGroup !== null) {
2140
+ // Cloud history is a producer of the shared temporal ping-pong state even
2141
+ // when TAA is disabled. Stage the write at encode time; execute's finish
2142
+ // guard and submit transaction then give it the same abort/commit/fence
2143
+ // semantics as the TAA resolve.
2144
+ if (shader === 'cloud-layer-resolve') {
2145
+ const state = getTemporalGpuState(
2146
+ frame.frameState,
2147
+ frame.runtime.device,
2148
+ frame.runtime.deviceScope,
2149
+ frame.targetW,
2150
+ frame.targetH,
2151
+ true,
2152
+ );
2153
+ stageTemporalGpuSubmit(state);
2154
+ frame.frameState.temporalGpuState = state;
2155
+ }
2156
+ return createdBindGroup;
2157
+ }
2158
+ return undefined;
2159
+ }
1615
2160
  if (descriptor?.values.fullscreen === true && isTemporalFullscreenBinding(descriptor.values)) {
1616
2161
  const shader = descriptor.values.shader;
1617
2162
  if (typeof shader !== 'string') return undefined;
@@ -1626,11 +2171,9 @@ function resolveTargetBindings(
1626
2171
  );
1627
2172
  if (fullscreen === null || fullscreen.sampler === null) return undefined;
1628
2173
  const inputTarget = input.resolveTarget(descriptor.values.input as never);
1629
- const temporalTarget = input.resolveTarget(descriptor.values.temporal as never);
1630
- if (inputTarget === undefined || temporalTarget === undefined) return undefined;
2174
+ if (inputTarget === undefined) return undefined;
1631
2175
  const inputView = input.resources.textureView(inputTarget.view);
1632
- const temporalView = input.resources.textureView(temporalTarget.view);
1633
- if (!inputView.ok || !temporalView.ok) return undefined;
2176
+ if (!inputView.ok) return undefined;
1634
2177
  const layout = pipeline.getBindGroupLayout?.(1);
1635
2178
  if (layout === undefined) return undefined;
1636
2179
  const depth = input.resolveTarget(descriptor.values.depth as never);
@@ -1649,13 +2192,41 @@ function resolveTargetBindings(
1649
2192
  if (depth !== undefined && (depthView === undefined || fullscreen.depthSampler === null)) {
1650
2193
  return undefined;
1651
2194
  }
2195
+
2196
+ const paramsBuffer = frame.runtime.getPostProcessParamsBuffer?.(shader);
2197
+ if (entry.params !== undefined && paramsBuffer === undefined) return undefined;
2198
+ if (entry.params !== undefined && paramsBuffer !== undefined) {
2199
+ const data = frame.postProcessParams.get(shader) ?? entry.params.defaultValue;
2200
+ if (data.byteLength !== entry.params.byteSize) return undefined;
2201
+ const written = frame.runtime.device.queue.writeBuffer(paramsBuffer, 0, data);
2202
+ if (!written.ok) return undefined;
2203
+ }
2204
+
2205
+ if (!isTemporalFullscreenBinding(descriptor.values)) {
2206
+ return (
2207
+ createFullscreenBindGroup(
2208
+ frame.runtime.device,
2209
+ layout,
2210
+ inputView.value,
2211
+ fullscreen.sampler,
2212
+ paramsBuffer,
2213
+ depthView ?? null,
2214
+ fullscreen.depthSampler,
2215
+ ) ?? undefined
2216
+ );
2217
+ }
2218
+
2219
+ const temporalTarget = input.resolveTarget(descriptor.values.temporal as never);
2220
+ if (temporalTarget === undefined) return undefined;
2221
+ const temporalView = input.resources.textureView(temporalTarget.view);
2222
+ if (!temporalView.ok) return undefined;
1652
2223
  return (
1653
2224
  createFullscreenBindGroup(
1654
2225
  frame.runtime.device,
1655
2226
  layout,
1656
2227
  inputView.value,
1657
2228
  fullscreen.sampler,
1658
- frame.runtime.getPostProcessParamsBuffer?.(shader),
2229
+ paramsBuffer,
1659
2230
  depthView ?? null,
1660
2231
  fullscreen.depthSampler,
1661
2232
  [{ binding: fullscreen.extraColorBindings[0] ?? 5, view: temporalView.value }],
@@ -1733,15 +2304,35 @@ function resolveTargetBindings(
1733
2304
  return created.value;
1734
2305
  }
1735
2306
 
1736
- function retire(
2307
+ export function retire(
1737
2308
  frameState: RenderFrameState,
1738
2309
  graph: CompiledRenderGraph<RenderPipelineFrame>,
1739
2310
  ): void {
2311
+ if (frameState.retiredCompiledFrameGraphs.has(graph)) return;
1740
2312
  frameState.retiredCompiledFrameGraphs.add(graph);
1741
- graph
1742
- .retire()
1743
- .finally(() => frameState.retiredCompiledFrameGraphs.delete(graph))
1744
- .catch(() => undefined);
2313
+ setGraphGenerationRetiring(frameState, graph);
2314
+ const failures = graphRetirementFailureSet(frameState);
2315
+ const retirement = graph.retire();
2316
+ // retire() synchronously moves graph-owned resources to pending before its
2317
+ // queue-fence await. Capture that overlap at the lifecycle event, rather
2318
+ // than waiting for a later inspection call.
2319
+ observeGraphGenerationAllocation(frameState);
2320
+ retirement
2321
+ .then((result) => {
2322
+ if (result.ok) {
2323
+ failures?.delete(graph);
2324
+ setGraphGenerationRetirementResult(frameState, graph, true);
2325
+ } else {
2326
+ failures?.add(graph);
2327
+ setGraphGenerationRetirementResult(frameState, graph, false);
2328
+ }
2329
+ frameState.retiredCompiledFrameGraphs.delete(graph);
2330
+ })
2331
+ .catch(() => {
2332
+ failures?.add(graph);
2333
+ setGraphGenerationRetirementResult(frameState, graph, false);
2334
+ frameState.retiredCompiledFrameGraphs.delete(graph);
2335
+ });
1745
2336
  }
1746
2337
 
1747
2338
  /**
@@ -1818,6 +2409,54 @@ export function settleDepthOfFieldGraphCandidate(
1818
2409
  frameState.depthOfFieldCandidateGraph = null;
1819
2410
  }
1820
2411
 
2412
+ /**
2413
+ * Settle an ordinary graph replacement at the same submit barrier as the
2414
+ * barrel mapping. Resize, camera topology, and post-process enable/disable
2415
+ * all replace the compiled graph; a failed command must restore that graph so
2416
+ * the retained mapping still describes the pixels that are actually shown.
2417
+ */
2418
+ export function settleBarrelDistortionGraphCandidate(
2419
+ frameState: RenderFrameState,
2420
+ submitted: boolean,
2421
+ ): void {
2422
+ const candidate = frameState.barrelDistortionCandidateGraph ?? null;
2423
+ if (candidate === null) return;
2424
+ const previous = frameState.barrelDistortionPreviousGraph ?? null;
2425
+ const sharedWithSpecializedOwner =
2426
+ frameState.depthOfFieldCandidateGraph === candidate ||
2427
+ frameState.volumetricFogCandidateGraph === candidate;
2428
+ if (submitted) {
2429
+ if (!sharedWithSpecializedOwner && previous !== null && previous !== candidate) {
2430
+ retire(frameState, previous);
2431
+ }
2432
+ } else {
2433
+ if (!sharedWithSpecializedOwner) {
2434
+ if (candidate !== previous) retire(frameState, candidate);
2435
+ frameState.compiledFrameGraph = previous;
2436
+ frameState.compiledFrameGraphTopologyKey =
2437
+ frameState.barrelDistortionPreviousGraphKey ?? null;
2438
+ if (frameState.barrelDistortionPreviousPerFrameGraph === undefined) {
2439
+ delete frameState.perFrameGraph;
2440
+ } else {
2441
+ frameState.perFrameGraph = frameState.barrelDistortionPreviousPerFrameGraph;
2442
+ }
2443
+ frameState.graphGeneration = frameState.barrelDistortionPreviousGraphGeneration ?? 0;
2444
+ frameState.compiledFrameGraphGeneration =
2445
+ frameState.barrelDistortionPreviousCompiledGraphGeneration ?? 0;
2446
+ frameState.standardLightingGraphSignature =
2447
+ frameState.barrelDistortionPreviousStandardLightingGraphSignature ?? '';
2448
+ }
2449
+ frameState.barrelDistortionGraphResolution = previous === null ? 'accepted' : 'retained';
2450
+ }
2451
+ frameState.barrelDistortionPreviousGraph = null;
2452
+ frameState.barrelDistortionPreviousGraphKey = null;
2453
+ frameState.barrelDistortionPreviousPerFrameGraph = undefined;
2454
+ frameState.barrelDistortionPreviousGraphGeneration = 0;
2455
+ frameState.barrelDistortionPreviousCompiledGraphGeneration = 0;
2456
+ frameState.barrelDistortionPreviousStandardLightingGraphSignature = '';
2457
+ frameState.barrelDistortionCandidateGraph = null;
2458
+ }
2459
+
1821
2460
  export function ensureCompiledFrameGraph(
1822
2461
  internals: RenderSystemInternals,
1823
2462
  frameState: RenderFrameState,
@@ -1839,7 +2478,11 @@ export function ensureCompiledFrameGraph(
1839
2478
  onRecoveryError?: (error: unknown) => void,
1840
2479
  ssr?: SsrSpatialAdmission,
1841
2480
  renderExtent?: RenderExtent,
2481
+ surfaceMediumActive?: boolean,
2482
+ analyticFog = false,
2483
+ cloudShadowResolution?: number,
1842
2484
  ): CompiledRenderGraph<RenderPipelineFrame> | null {
2485
+ frameState.barrelDistortionGraphResolution = 'retained';
1843
2486
  const featureMetrics = mutableFeatureGraphInspection(internals);
1844
2487
  featureMetrics.ensureCalls += 1;
1845
2488
  const requestedStandardLightingSignature = standardLightingTopologySignature(standardLighting);
@@ -1857,10 +2500,16 @@ export function ensureCompiledFrameGraph(
1857
2500
  : { ...camera, depthOfField: frameDepthOfField };
1858
2501
  const lastKnownGood = (): CompiledRenderGraph<RenderPipelineFrame> | null =>
1859
2502
  frameState.compiledFrameGraph !== null &&
2503
+ frameState.compiledFrameGraphTopologyKey !== null &&
1860
2504
  !hasSubmissionSensitiveFeatures(getRenderFeatureGraphState(internals).plans) &&
1861
2505
  frameState.standardLightingGraphSignature === requestedStandardLightingSignature
1862
2506
  ? frameState.compiledFrameGraph
1863
2507
  : null;
2508
+ const retainLastKnownGood = (): CompiledRenderGraph<RenderPipelineFrame> | null => {
2509
+ const graph = lastKnownGood();
2510
+ if (graph !== null) frameState.barrelDistortionGraphResolution = 'retained';
2511
+ return graph;
2512
+ };
1864
2513
  const surfaceWidth = Math.max(1, width);
1865
2514
  const surfaceHeight = Math.max(1, height);
1866
2515
  const featurePlans = featureGraphCandidate?.plans ?? getRenderFeatureGraphState(internals).plans;
@@ -1872,8 +2521,15 @@ export function ensureCompiledFrameGraph(
1872
2521
  if (featureGraphCandidate !== undefined) {
1873
2522
  recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
1874
2523
  }
1875
- return volumeTopologyCandidate ? null : lastKnownGood();
2524
+ return volumeTopologyCandidate ? null : retainLastKnownGood();
1876
2525
  }
2526
+ const cloudHistoryDemand = projectedFeaturePlans.value.some(
2527
+ (execution) =>
2528
+ execution.featureIdentity === CLOUD_LAYER_FEATURE_IDENTITY &&
2529
+ execution.passes.some(
2530
+ (pass) => pass.name === 'cloud-layer-transport' || pass.name === 'cloud-layer-resolve',
2531
+ ),
2532
+ );
1877
2533
  const nextPlanRevision = updateRenderFeaturePlanRevision(
1878
2534
  featureMetrics.planRevisionState,
1879
2535
  featurePlans,
@@ -1904,6 +2560,8 @@ export function ensureCompiledFrameGraph(
1904
2560
  standardLighting,
1905
2561
  ssr,
1906
2562
  renderExtent,
2563
+ surfaceMediumActive,
2564
+ analyticFog,
1907
2565
  );
1908
2566
  if (!topologyResult.ok) {
1909
2567
  featureMetrics.buildFailures += 1;
@@ -1912,12 +2570,22 @@ export function ensureCompiledFrameGraph(
1912
2570
  if (featureGraphCandidate !== undefined) {
1913
2571
  recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
1914
2572
  }
1915
- return volumeTopologyCandidate ? null : lastKnownGood();
2573
+ return volumeTopologyCandidate ? null : retainLastKnownGood();
1916
2574
  }
1917
2575
  const requestedPostEffects = topologyResult.value.config?.postEffects ?? [];
1918
2576
  const featurePostProcessEntries =
1919
2577
  featureGraphCandidate?.fullscreenEffects ??
1920
2578
  getRenderFeatureGraphState(internals).fullscreenEffects;
2579
+ // Motion Blur's compute plan intentionally has no raster fullscreen shader
2580
+ // declaration, but it is still a post chain consumer: Standard Post owns
2581
+ // the named `motion-input`/`motion-output` targets and must project it after
2582
+ // TAA. Keep the identity in this deferred set alongside declared fullscreen
2583
+ // effects so an early forward/deferred contribution cannot resolve its
2584
+ // scene-data target against an incomplete target map.
2585
+ const fullscreenFeatureIds = new Set([
2586
+ ...featurePostProcessEntries.keys(),
2587
+ 'forgeax.motion-blur',
2588
+ ]);
1921
2589
  const postProcessAdmission = resolvePostProcessChainAdmission(
1922
2590
  requestedPostEffects,
1923
2591
  (identity) => {
@@ -1954,6 +2622,7 @@ export function ensureCompiledFrameGraph(
1954
2622
  if (featureGraphCandidate !== undefined) {
1955
2623
  recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
1956
2624
  }
2625
+ frameState.barrelDistortionGraphResolution = 'retained';
1957
2626
  return acceptedLkg;
1958
2627
  }
1959
2628
  const topology = postProcessAdmission.pending
@@ -1965,8 +2634,19 @@ export function ensureCompiledFrameGraph(
1965
2634
  },
1966
2635
  }
1967
2636
  : topologyResult.value;
2637
+ // Keep Bloom's positive parameter values out of the compiled identity. A
2638
+ // crossing of zero has already changed topology.camera.bloom above; the
2639
+ // remaining values are uploaded by the active recorder without rebuilding
2640
+ // targets or pipelines.
2641
+ const topologyIdentity = {
2642
+ ...topology,
2643
+ camera: {
2644
+ ...topology.camera,
2645
+ bloomIntensity: undefined,
2646
+ },
2647
+ };
1968
2648
  const key = JSON.stringify({
1969
- topology,
2649
+ topology: topologyIdentity,
1970
2650
  // Observation domains are an explicit per-frame graph demand. They are
1971
2651
  // part of the cache identity so a request made after the ordinary graph
1972
2652
  // was compiled can admit the matching tone/output capture boundaries on
@@ -1979,6 +2659,11 @@ export function ensureCompiledFrameGraph(
1979
2659
  // so include the prepared projection's presence to invalidate a cached
1980
2660
  // CPU-only graph when the GPU owner becomes ready, and vice versa.
1981
2661
  gpuDrivenProjectionActive: gpuDriven !== undefined,
2662
+ // Cloud shadow targets are quality-derived graph resources. Include the
2663
+ // resolved texel grid in the cache key so a quality/resize change cannot
2664
+ // reuse a graph compiled for the previous allocation.
2665
+ cloudShadowResolution: cloudShadowResolution ?? 0,
2666
+ cloudHistory: cloudHistoryDemand,
1982
2667
  // Query reservations are frame-local transport state. The projection is
1983
2668
  // renderer-owned and read dynamically by the compiled pass, so a page
1984
2669
  // rotation must not invalidate the graph or its cached bind groups.
@@ -1993,9 +2678,11 @@ export function ensureCompiledFrameGraph(
1993
2678
  ]),
1994
2679
  });
1995
2680
  if (frameState.compiledFrameGraph !== null && frameState.compiledFrameGraphTopologyKey === key) {
2681
+ frameState.barrelDistortionGraphResolution = 'accepted';
1996
2682
  featureMetrics.reuseHits += 1;
1997
2683
  featureMetrics.lastTopologyKey = key;
1998
2684
  frameState.standardLightingGraphSignature = requestedStandardLightingSignature;
2685
+ frameState.pendingCloudHistoryActive = cloudHistoryDemand;
1999
2686
  if (featureGraphCandidate !== undefined && !postProcessAdmission.pending) {
2000
2687
  commitFeatureCandidate(internals, featureGraphCandidate);
2001
2688
  } else if (featureGraphCandidate !== undefined) {
@@ -2025,9 +2712,11 @@ export function ensureCompiledFrameGraph(
2025
2712
  topology.output?.autoExposure === true ? importAutoExposureGraphResources(builder) : undefined;
2026
2713
  if (autoExposureResources !== undefined && !autoExposureResources.ok) {
2027
2714
  internals.errorRegistry.fire(autoExposureResources.error);
2028
- featureGraphCandidate?.onRejected?.();
2029
2715
  discardPendingStandardOutput();
2030
- return lastKnownGood();
2716
+ if (featureGraphCandidate !== undefined) {
2717
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
2718
+ }
2719
+ return retainLastKnownGood();
2031
2720
  }
2032
2721
  const lutResources =
2033
2722
  topology.output?.colorLut === true
@@ -2071,23 +2760,27 @@ export function ensureCompiledFrameGraph(
2071
2760
  : undefined;
2072
2761
  if (lutResources !== undefined && !lutResources.ok) {
2073
2762
  internals.errorRegistry.fire(lutResources.error);
2074
- featureGraphCandidate?.onRejected?.();
2075
2763
  discardPendingStandardOutput();
2076
- return lastKnownGood();
2764
+ if (featureGraphCandidate !== undefined) {
2765
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
2766
+ }
2767
+ return retainLastKnownGood();
2077
2768
  }
2078
2769
  const taaHistory = camera.antialias === 'taa' ? importTemporalHistoryTargets(builder) : undefined;
2770
+ const cloudHistory = cloudHistoryDemand ? importCloudHistoryTargets(builder) : undefined;
2079
2771
  const targetCoverage =
2080
2772
  camera.dynamicResolution === undefined || topology.extent === undefined
2081
2773
  ? undefined
2082
2774
  : createTargetCoverageAttachment(builder, topology.extent);
2083
2775
  if (targetCoverage !== undefined && !targetCoverage.ok) {
2084
2776
  internals.errorRegistry.fire(targetCoverage.error);
2085
- featureGraphCandidate?.onRejected?.();
2086
2777
  discardPendingStandardOutput();
2087
- return lastKnownGood();
2778
+ if (featureGraphCandidate !== undefined) {
2779
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
2780
+ }
2781
+ return retainLastKnownGood();
2088
2782
  }
2089
2783
  const ssrHistory = ssr?.status === 'admitted' ? importSsrHistoryTargets(builder) : undefined;
2090
- let projectedFeatures = false;
2091
2784
  const featureProjection = createRenderFeatureProjectionState();
2092
2785
  let featureShadows:
2093
2786
  | ReturnType<typeof projectRenderFeatureShadows<RenderPipelineFrame>>
@@ -2119,8 +2812,10 @@ export function ensureCompiledFrameGraph(
2119
2812
  : {}),
2120
2813
  ...(standardLighting === undefined ? {} : { standardLighting }),
2121
2814
  capabilities: { rgba16floatRenderable: internals.device.caps.rgba16floatRenderable },
2815
+ ...(cloudShadowResolution === undefined ? {} : { cloudShadowResolution }),
2122
2816
  ...(occlusion === undefined ? {} : { occlusion }),
2123
2817
  ...(taaHistory === undefined ? {} : { taaHistory }),
2818
+ ...(cloudHistory === undefined ? {} : { cloudHistory }),
2124
2819
  ...(targetCoverage?.ok === true ? { targetCoverage: targetCoverage.value } : {}),
2125
2820
  ...(ssrHistory === undefined ? {} : { ssrHistory }),
2126
2821
  encodeTransmissionMip: ({ pass, resources, source }) => {
@@ -2136,6 +2831,7 @@ export function ensureCompiledFrameGraph(
2136
2831
  builder,
2137
2832
  target.format,
2138
2833
  target.sampleCount,
2834
+ () => frameState.surfaceSubmissionObservation,
2139
2835
  target.additionalColorFormats,
2140
2836
  );
2141
2837
  },
@@ -2156,14 +2852,42 @@ export function ensureCompiledFrameGraph(
2156
2852
  semanticTargets = [],
2157
2853
  namedTargets = {},
2158
2854
  standardSurfaceAccesses = [],
2855
+ featureSelectionOrPlacement,
2856
+ passNames = [],
2159
2857
  ) => {
2160
- if (projectedFeatures) return ok(undefined);
2161
- projectedFeatures = true;
2162
- if (projectedFeaturePlans.value.length === 0) return ok(undefined);
2858
+ const selection =
2859
+ featureSelectionOrPlacement !== undefined &&
2860
+ typeof featureSelectionOrPlacement !== 'string'
2861
+ ? featureSelectionOrPlacement
2862
+ : undefined;
2863
+ const placement =
2864
+ typeof featureSelectionOrPlacement === 'string' ? featureSelectionOrPlacement : 'post';
2865
+ const include = selection?.include === undefined ? undefined : new Set(selection.include);
2866
+ const exclude = selection?.exclude === undefined ? undefined : new Set(selection.exclude);
2867
+ // Forward/deferred scene lanes contribute scene plans before the
2868
+ // Standard post chain has created its named input/output targets.
2869
+ // Fullscreen effects (Motion Blur included) must wait for that later
2870
+ // call so their temporal input is resolved after TAA. The shared
2871
+ // projection state below deduplicates only the execution/pass that
2872
+ // actually reached the graph; an empty early projection therefore
2873
+ // cannot reserve the later fullscreen pass.
2874
+ const plans = (
2875
+ Object.keys(namedTargets).length === 0
2876
+ ? projectedFeaturePlans.value.filter(
2877
+ (execution) => !fullscreenFeatureIds.has(execution.featureIdentity),
2878
+ )
2879
+ : projectedFeaturePlans.value
2880
+ )
2881
+ .filter((execution) => (execution.placement ?? 'post') === placement)
2882
+ .filter((execution) => include === undefined || include.has(execution.featureIdentity))
2883
+ .filter((execution) => exclude === undefined || !exclude.has(execution.featureIdentity));
2884
+ if (plans.length === 0) return ok(undefined);
2163
2885
  const resolveTarget = targetResolver(targets, semanticTargets, namedTargets);
2164
- const projected = projectPlanExecutions(builder, projectedFeaturePlans.value, {
2886
+ const projected = projectPlanExecutions(builder, plans, {
2165
2887
  state: featureProjection,
2166
2888
  resolveTarget,
2889
+ placement,
2890
+ ...(passNames.length === 0 ? {} : { passNames: new Set(passNames) }),
2167
2891
  standardSurfaceAccesses,
2168
2892
  resolveBindings: (input) => resolveTargetBindings(input, featurePostProcessEntries),
2169
2893
  resolveStandardLighting: (frame, pipeline) => {
@@ -2188,7 +2912,8 @@ export function ensureCompiledFrameGraph(
2188
2912
  },
2189
2913
  reportError: (error) => reportRenderFeatureGraphError(internals, error),
2190
2914
  });
2191
- return projected.ok ? ok(undefined) : projected;
2915
+ if (!projected.ok) return projected;
2916
+ return ok(undefined);
2192
2917
  },
2193
2918
  contributeCubeCaptures: (environmentCube) => {
2194
2919
  const reflectionProbes = addReflectionProbeGraphPasses(
@@ -2203,6 +2928,20 @@ export function ensureCompiledFrameGraph(
2203
2928
  },
2204
2929
  hasFeature: (identity) =>
2205
2930
  projectedFeaturePlans.value.some((execution) => execution.featureIdentity === identity),
2931
+ hasFeatureWork: (identity, placement) =>
2932
+ projectedFeaturePlans.value.some(
2933
+ (execution) =>
2934
+ execution.featureIdentity === identity &&
2935
+ execution.passes.length > 0 &&
2936
+ (placement === undefined || (execution.placement ?? 'post') === placement),
2937
+ ),
2938
+ hasFeatureRasterWork: (identity, placement) =>
2939
+ projectedFeaturePlans.value.some(
2940
+ (execution) =>
2941
+ execution.featureIdentity === identity &&
2942
+ execution.passes.some((pass) => pass.graphics !== undefined) &&
2943
+ (placement === undefined || (execution.placement ?? 'post') === placement),
2944
+ ),
2206
2945
  },
2207
2946
  topology,
2208
2947
  );
@@ -2210,12 +2949,11 @@ export function ensureCompiledFrameGraph(
2210
2949
  featureMetrics.buildFailures += 1;
2211
2950
  onRecoveryError?.(built.error);
2212
2951
  internals.errorRegistry.fire(built.error);
2213
- featureGraphCandidate?.onRejected?.();
2214
2952
  discardPendingStandardOutput();
2215
2953
  if (featureGraphCandidate !== undefined) {
2216
2954
  recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
2217
2955
  }
2218
- return volumeTopologyCandidate ? null : lastKnownGood();
2956
+ return volumeTopologyCandidate ? null : retainLastKnownGood();
2219
2957
  }
2220
2958
  featureMetrics.compileAttempts += 1;
2221
2959
  const compileStarted = typeof performance === 'undefined' ? Date.now() : performance.now();
@@ -2229,13 +2967,16 @@ export function ensureCompiledFrameGraph(
2229
2967
  featureMetrics.compileFailures += 1;
2230
2968
  onRecoveryError?.(compiled.error);
2231
2969
  internals.errorRegistry.fire(compiled.error);
2232
- featureGraphCandidate?.onRejected?.();
2233
2970
  discardPendingStandardOutput();
2234
2971
  if (featureGraphCandidate !== undefined) {
2235
2972
  recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
2236
2973
  }
2237
- return lastKnownGood();
2974
+ return retainLastKnownGood();
2238
2975
  }
2976
+ // Register the generation before publishing it. The owner observes the
2977
+ // overlap while the previous graph is still live, even if a queue fence
2978
+ // retires that graph before the next public inspect() call.
2979
+ trackGraphGeneration(frameState, compiled.value);
2239
2980
  const previous = frameState.compiledFrameGraph;
2240
2981
  const depthOfFieldReplacement =
2241
2982
  (frameState.depthOfFieldCandidateGraph === null ||
@@ -2263,8 +3004,22 @@ export function ensureCompiledFrameGraph(
2263
3004
  frameState.depthOfFieldPreviousStandardLightingGraphSignature =
2264
3005
  frameState.standardLightingGraphSignature;
2265
3006
  frameState.depthOfFieldCandidateGraph = compiled.value;
3007
+ } else if (compiled.value !== previous) {
3008
+ // Keep the complete accepted graph projection until queue submission.
3009
+ // This covers barrel enable/disable, output resize, and camera topology
3010
+ // changes without introducing a second resource owner.
3011
+ frameState.barrelDistortionPreviousGraph = previous;
3012
+ frameState.barrelDistortionPreviousGraphKey = frameState.compiledFrameGraphTopologyKey;
3013
+ frameState.barrelDistortionPreviousPerFrameGraph = frameState.perFrameGraph;
3014
+ frameState.barrelDistortionPreviousGraphGeneration = frameState.graphGeneration;
3015
+ frameState.barrelDistortionPreviousCompiledGraphGeneration =
3016
+ frameState.compiledFrameGraphGeneration;
3017
+ frameState.barrelDistortionPreviousStandardLightingGraphSignature =
3018
+ frameState.standardLightingGraphSignature;
3019
+ frameState.barrelDistortionCandidateGraph = compiled.value;
2266
3020
  }
2267
3021
  frameState.compiledFrameGraph = compiled.value;
3022
+ frameState.barrelDistortionGraphResolution = 'accepted';
2268
3023
  frameState.compiledFrameGraphTopologyKey = key;
2269
3024
  frameState.standardLightingGraphSignature = requestedStandardLightingSignature;
2270
3025
  frameState.compiledFrameGraphGeneration += 1;
@@ -2296,9 +3051,8 @@ export function ensureCompiledFrameGraph(
2296
3051
  } else if (featureGraphCandidate !== undefined) {
2297
3052
  recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'abandoned');
2298
3053
  }
2299
- if (!volumeTopologyCandidate && !depthOfFieldReplacement && previous !== null) {
2300
- retire(frameState, previous);
2301
- }
3054
+ frameState.pendingCloudHistoryActive = cloudHistoryDemand;
3055
+ // The selected candidate owner retires the previous graph only after submit.
2302
3056
  return compiled.value;
2303
3057
  }
2304
3058
 
@@ -2324,20 +3078,31 @@ export function executeCompiledFrameGraph(
2324
3078
  const legacySubmitCommit = typeof frameHooks === 'function' ? frameHooks : undefined;
2325
3079
  const resolvedFrameHooks = typeof frameHooks === 'function' ? undefined : frameHooks;
2326
3080
  const graph = frameState.compiledFrameGraph;
3081
+ // Recovery keeps the candidate graph for detached inspection, but clears
3082
+ // its topology key before the replacement canvas context is published. It
3083
+ // is evidence only until the first post-recovery draw compiles a new graph.
3084
+ // Never let that retained graph reach record/execute or queue.submit.
3085
+ const detachedGraph = graph !== null && frameState.compiledFrameGraphTopologyKey === null;
2327
3086
  const rejectTemporalFrame = (): void => {
2328
3087
  if (frameState.temporalFrameInput === undefined) return;
2329
3088
  frameState.temporalFrameTransaction.commit({ accepted: false });
2330
3089
  frameState.temporalFrameInput = undefined;
2331
3090
  };
2332
- if (graph === null) {
3091
+ if (graph === null || detachedGraph) {
2333
3092
  timingCapture?.discard();
2334
3093
  frameState.pendingAutoExposureState = undefined;
2335
3094
  frameState.pendingStandardLutState = undefined;
3095
+ settleBarrelDistortionGraphCandidate(frameState, false);
2336
3096
  settleVolumetricFogGraphCandidate(frameState, false);
2337
3097
  settleDepthOfFieldGraphCandidate(frameState, false);
2338
3098
  rejectTemporalFrame();
2339
3099
  return false;
2340
3100
  }
3101
+ const motionBlurExecution = deriveMotionBlurExecutionReceipt(
3102
+ internals,
3103
+ frameState,
3104
+ getRenderFeatureGraphState(internals).plans,
3105
+ );
2341
3106
  const legacyTimingInstrumentation =
2342
3107
  timingCapture === undefined ? undefined : createTimingInstrumentation(timingCapture);
2343
3108
  const timingHost = internals as RenderSystemInternals & {
@@ -2400,6 +3165,7 @@ export function executeCompiledFrameGraph(
2400
3165
  );
2401
3166
  settleVolumetricFogGraphCandidate(frameState, false);
2402
3167
  settleDepthOfFieldGraphCandidate(frameState, false);
3168
+ settleBarrelDistortionGraphCandidate(frameState, false);
2403
3169
  return false;
2404
3170
  }
2405
3171
  const capturedGeneration = deviceScope?.generation;
@@ -2903,8 +3669,8 @@ export function executeCompiledFrameGraph(
2903
3669
  internals.errorRegistry.fire(
2904
3670
  new RhiError({
2905
3671
  code: 'webgpu-runtime-error',
2906
- expected: 'TAA resolve stages a history write before submit',
2907
- hint: 'retry the frame after the temporal resolve pass has encoded successfully',
3672
+ expected: 'an admitted temporal producer stages a history write before submit',
3673
+ hint: 'retry the frame after the TAA or CloudLayer history pass has encoded successfully',
2908
3674
  }),
2909
3675
  );
2910
3676
  return { ok: false, stage: 'finish' };
@@ -2960,6 +3726,11 @@ export function executeCompiledFrameGraph(
2960
3726
  commit: () => {
2961
3727
  resolvedFrameHooks?.onSubmitted?.();
2962
3728
  legacySubmitCommit?.();
3729
+ if (motionBlurExecution === undefined) {
3730
+ delete frameState.motionBlurExecution;
3731
+ } else {
3732
+ frameState.motionBlurExecution = motionBlurExecution;
3733
+ }
2963
3734
  if (frameState.temporalFrameInput !== undefined) {
2964
3735
  const temporal = frameState.temporalFrameTransaction.commit({ accepted: true });
2965
3736
  if (temporal.ok) {
@@ -3053,6 +3824,11 @@ export function executeCompiledFrameGraph(
3053
3824
  }
3054
3825
  }
3055
3826
  const stagedTemporalCommit = frameState.pendingTemporalCommit ?? { kind: 'none' as const };
3827
+ const stagedCloudHistoryActive = frameState.pendingCloudHistoryActive;
3828
+ if (stagedCloudHistoryActive !== undefined) {
3829
+ frameState.cloudHistoryActive = stagedCloudHistoryActive;
3830
+ delete frameState.pendingCloudHistoryActive;
3831
+ }
3056
3832
  if (stagedTemporalCommit.kind !== 'none') {
3057
3833
  frameState.lastSuccessfulTemporalView = stagedTemporalCommit.view;
3058
3834
  }
@@ -3061,7 +3837,7 @@ export function executeCompiledFrameGraph(
3061
3837
  } else if (stagedTemporalCommit.kind === 'off') {
3062
3838
  frameState.successfulTemporalFrameIndex = 0;
3063
3839
  }
3064
- if (stagedTemporalCommit.kind === 'off') {
3840
+ if (stagedTemporalCommit.kind === 'off' && frameState.cloudHistoryActive !== true) {
3065
3841
  internals.clearPostProcessPipelineCache?.('forgeax.taa-resolve');
3066
3842
  // Retire frame-sized resources, not the device-owned prewarmed
3067
3843
  // shader. The next TAA frame must not start an async cold compile.
@@ -3092,6 +3868,7 @@ export function executeCompiledFrameGraph(
3092
3868
  frameState.pendingAtmospherePublish?.();
3093
3869
  frameState.pendingAtmospherePublish = undefined;
3094
3870
  onSubmitted?.(internals.device.queue.onSubmittedWorkDone());
3871
+ settleBarrelDistortionGraphCandidate(frameState, true);
3095
3872
  settleVolumetricFogGraphCandidate(frameState, true);
3096
3873
  settleDepthOfFieldGraphCandidate(frameState, true);
3097
3874
  },
@@ -3127,11 +3904,13 @@ export function executeCompiledFrameGraph(
3127
3904
  }
3128
3905
  frameState.temporalGpuState = undefined;
3129
3906
  }
3907
+ delete frameState.pendingCloudHistoryActive;
3130
3908
  frameState.pendingTemporalCommit = { kind: 'none' };
3131
3909
  if (frameState.environmentGeneration !== undefined) {
3132
3910
  frameState.environmentLifecycle?.discard(frameState.environmentGeneration);
3133
3911
  frameState.environmentGeneration = undefined;
3134
3912
  }
3913
+ settleBarrelDistortionGraphCandidate(frameState, false);
3135
3914
  settleVolumetricFogGraphCandidate(frameState, false);
3136
3915
  settleDepthOfFieldGraphCandidate(frameState, false);
3137
3916
  },
@@ -3139,6 +3918,7 @@ export function executeCompiledFrameGraph(
3139
3918
  // The callbacks above settle the normal transaction path. Keep this guard
3140
3919
  // for synthetic callers and any future early transaction result that does
3141
3920
  // not enter a callback.
3921
+ settleBarrelDistortionGraphCandidate(frameState, transaction.ok);
3142
3922
  settleVolumetricFogGraphCandidate(frameState, transaction.ok);
3143
3923
  settleDepthOfFieldGraphCandidate(frameState, transaction.ok);
3144
3924
  if (!transaction.ok) rejectTemporalFrame();