@forgeax/engine-render 0.1.33 → 0.1.35

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 +1062 -127
  2. package/dist/assembly/backend-contract.d.ts +2 -0
  3. package/dist/assembly/backend-contract.d.ts.map +1 -1
  4. package/dist/assembly/barrel-distortion-inspection.d.ts +3 -0
  5. package/dist/assembly/barrel-distortion-inspection.d.ts.map +1 -0
  6. package/dist/assembly/canvas-draw-guard.d.ts +3 -0
  7. package/dist/assembly/canvas-draw-guard.d.ts.map +1 -0
  8. package/dist/assembly/cloud-shadow-entry.d.ts +4 -0
  9. package/dist/assembly/cloud-shadow-entry.d.ts.map +1 -0
  10. package/dist/assembly/device-feature-admission.d.ts +2 -2
  11. package/dist/assembly/dynamic-geometry-host.d.ts +4 -1
  12. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
  13. package/dist/assembly/dynamic-geometry-runtime.d.ts +1 -1
  14. package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -1
  15. package/dist/assembly/factory.d.ts +1 -1
  16. package/dist/assembly/factory.d.ts.map +1 -1
  17. package/dist/assembly/host-contract.d.ts +6 -5
  18. package/dist/assembly/host-contract.d.ts.map +1 -1
  19. package/dist/assembly/material/assembly.d.ts.map +1 -1
  20. package/dist/assembly/material/pipeline-helpers.d.ts +5 -0
  21. package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -1
  22. package/dist/assembly/material-shader-policy.d.ts +16 -2
  23. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  24. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -1
  25. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
  26. package/dist/assembly/recovery/renderer-recover.d.ts +2 -0
  27. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  28. package/dist/assembly/render-feature-post-processes.d.ts +6 -0
  29. package/dist/assembly/render-feature-post-processes.d.ts.map +1 -0
  30. package/dist/assembly/render-target-host.d.ts +5 -0
  31. package/dist/assembly/render-target-host.d.ts.map +1 -1
  32. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  33. package/dist/assembly/renderer-feature-inspection.d.ts +1 -1
  34. package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -1
  35. package/dist/assembly/renderer-frame-transaction.d.ts +12 -0
  36. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  37. package/dist/assembly/renderer-inspection.d.ts +11 -0
  38. package/dist/assembly/renderer-inspection.d.ts.map +1 -1
  39. package/dist/assembly/webgpu-pbr-ready.d.ts +1 -0
  40. package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -1
  41. package/dist/assembly/webgpu-ready-per-pass.d.ts +39 -0
  42. package/dist/assembly/webgpu-ready-per-pass.d.ts.map +1 -0
  43. package/dist/assembly/webgpu-ready.d.ts +1 -1
  44. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  45. package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -1
  46. package/dist/assembly/webgpu-renderer-observation-owner.d.ts +35 -0
  47. package/dist/assembly/webgpu-renderer-observation-owner.d.ts.map +1 -0
  48. package/dist/assembly/webgpu-renderer.d.ts +2 -1
  49. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  50. package/dist/authoring.mjs +2 -4
  51. package/dist/authoring.mjs.map +1 -1
  52. package/dist/barrel-distortion.d.ts +47 -0
  53. package/dist/barrel-distortion.d.ts.map +1 -0
  54. package/dist/chunk-3XUBWRY2.mjs +1842 -0
  55. package/dist/chunk-3XUBWRY2.mjs.map +1 -0
  56. package/dist/{chunk-OO3DRYK7.mjs → chunk-G6HDNGY7.mjs} +2400 -1767
  57. package/dist/chunk-G6HDNGY7.mjs.map +1 -0
  58. package/dist/{chunk-ZDQWGQW7.mjs → chunk-LM5IE7GF.mjs} +4 -4
  59. package/dist/{chunk-ZDQWGQW7.mjs.map → chunk-LM5IE7GF.mjs.map} +1 -1
  60. package/dist/{chunk-VOZWDM3L.mjs → chunk-LNIUIKQ7.mjs} +1514 -498
  61. package/dist/chunk-LNIUIKQ7.mjs.map +1 -0
  62. package/dist/chunk-MXTWKZO4.mjs +3268 -0
  63. package/dist/chunk-MXTWKZO4.mjs.map +1 -0
  64. package/dist/chunk-MZ4C7EHF.mjs +6942 -0
  65. package/dist/chunk-MZ4C7EHF.mjs.map +1 -0
  66. package/dist/chunk-RIPAEGI6.mjs +48 -0
  67. package/dist/chunk-RIPAEGI6.mjs.map +1 -0
  68. package/dist/{chunk-LEAXOMYJ.mjs → chunk-SGPUJW3F.mjs} +117 -5
  69. package/dist/chunk-SGPUJW3F.mjs.map +1 -0
  70. package/dist/{chunk-PA5DRJNI.mjs → chunk-X2KA6WHM.mjs} +29 -4
  71. package/dist/chunk-X2KA6WHM.mjs.map +1 -0
  72. package/dist/{chunk-BLXGONR2.mjs → chunk-XSFB7XXQ.mjs} +51 -4
  73. package/dist/chunk-XSFB7XXQ.mjs.map +1 -0
  74. package/dist/cloud/density.d.ts +55 -0
  75. package/dist/cloud/density.d.ts.map +1 -0
  76. package/dist/cloud/extract.d.ts +25 -0
  77. package/dist/cloud/extract.d.ts.map +1 -0
  78. package/dist/cloud/feature.d.ts +57 -0
  79. package/dist/cloud/feature.d.ts.map +1 -0
  80. package/dist/cloud/frame-context.d.ts +8 -0
  81. package/dist/cloud/frame-context.d.ts.map +1 -0
  82. package/dist/cloud/inspection.d.ts +59 -0
  83. package/dist/cloud/inspection.d.ts.map +1 -0
  84. package/dist/cloud/optics.d.ts +56 -0
  85. package/dist/cloud/optics.d.ts.map +1 -0
  86. package/dist/cloud/parameters.d.ts +73 -0
  87. package/dist/cloud/parameters.d.ts.map +1 -0
  88. package/dist/cloud/resources.d.ts +60 -0
  89. package/dist/cloud/resources.d.ts.map +1 -0
  90. package/dist/cloud/shadow.d.ts +42 -0
  91. package/dist/cloud/shadow.d.ts.map +1 -0
  92. package/dist/cloud/temporal.d.ts +74 -0
  93. package/dist/cloud/temporal.d.ts.map +1 -0
  94. package/dist/components/barrel-distortion.d.ts +13 -0
  95. package/dist/components/barrel-distortion.d.ts.map +1 -0
  96. package/dist/components/cloud-layer.d.ts +29 -0
  97. package/dist/components/cloud-layer.d.ts.map +1 -0
  98. package/dist/components/index.d.ts +2 -0
  99. package/dist/components/index.d.ts.map +1 -1
  100. package/dist/components/instances.d.ts +20 -6
  101. package/dist/components/instances.d.ts.map +1 -1
  102. package/dist/construct-renderer.d.ts +1 -1
  103. package/dist/construct-renderer.d.ts.map +1 -1
  104. package/dist/construct-renderer.mjs +8406 -5785
  105. package/dist/construct-renderer.mjs.map +1 -1
  106. package/dist/device/gpu-residency.d.ts +19 -19
  107. package/dist/device/gpu-residency.d.ts.map +1 -1
  108. package/dist/dynamic-geometry.d.ts +5 -1
  109. package/dist/dynamic-geometry.d.ts.map +1 -1
  110. package/dist/errors/cloud.d.ts +59 -0
  111. package/dist/errors/cloud.d.ts.map +1 -0
  112. package/dist/errors/render.d.ts +20 -5
  113. package/dist/errors/render.d.ts.map +1 -1
  114. package/dist/examples/single-layer-medium-surface.d.ts +47 -0
  115. package/dist/examples/single-layer-medium-surface.d.ts.map +1 -0
  116. package/dist/extract/camera.d.ts.map +1 -1
  117. package/dist/extract/contracts.d.ts +5 -1
  118. package/dist/extract/contracts.d.ts.map +1 -1
  119. package/dist/extract/material-context.d.ts +7 -0
  120. package/dist/extract/material-context.d.ts.map +1 -0
  121. package/dist/features/barrel-distortion.d.ts +29 -0
  122. package/dist/features/barrel-distortion.d.ts.map +1 -0
  123. package/dist/features/error-reporting.d.ts +6 -2
  124. package/dist/features/error-reporting.d.ts.map +1 -1
  125. package/dist/features/host.d.ts +15 -0
  126. package/dist/features/host.d.ts.map +1 -1
  127. package/dist/features/plan.d.ts +10 -0
  128. package/dist/features/plan.d.ts.map +1 -1
  129. package/dist/features/render-graph-contribution.d.ts +5 -0
  130. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  131. package/dist/features/render-graph-raster.d.ts +1 -0
  132. package/dist/features/render-graph-raster.d.ts.map +1 -1
  133. package/dist/features/targets.d.ts +2 -0
  134. package/dist/features/targets.d.ts.map +1 -1
  135. package/dist/features/types.d.ts +48 -0
  136. package/dist/features/types.d.ts.map +1 -1
  137. package/dist/fullscreen-post-process-pass.d.ts +15 -7
  138. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  139. package/dist/glyph-text-layout-system.d.ts +1 -1
  140. package/dist/glyph-text-layout-system.d.ts.map +1 -1
  141. package/dist/gpu-dirty-ranges.d.ts +11 -0
  142. package/dist/gpu-dirty-ranges.d.ts.map +1 -0
  143. package/dist/gpu-driven/batch-topology.d.ts +17 -0
  144. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  145. package/dist/gpu-driven/material-artifacts.d.ts +6 -0
  146. package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
  147. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  148. package/dist/gpu-driven/production-raster-material.d.ts +178 -0
  149. package/dist/gpu-driven/production-raster-material.d.ts.map +1 -0
  150. package/dist/gpu-driven/production-raster.d.ts +47 -3
  151. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  152. package/dist/gpu-driven/resource-allocation.d.ts +31 -0
  153. package/dist/gpu-driven/resource-allocation.d.ts.map +1 -0
  154. package/dist/gpu-driven/shadow-ownership.d.ts +14 -0
  155. package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -0
  156. package/dist/gpu-driven/view-gpu.d.ts +26 -2
  157. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  158. package/dist/gpu-scene.d.ts +5 -0
  159. package/dist/gpu-scene.d.ts.map +1 -1
  160. package/dist/hdrp-buffers.d.ts +2 -2
  161. package/dist/hdrp-buffers.d.ts.map +1 -1
  162. package/dist/ibl/frame-resources.d.ts.map +1 -1
  163. package/dist/ibl/skylight-bind-group.d.ts +14 -5
  164. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  165. package/dist/index.d.ts +30 -7
  166. package/dist/index.d.ts.map +1 -1
  167. package/dist/index.mjs +780 -9
  168. package/dist/index.mjs.map +1 -1
  169. package/dist/inspection-types.d.ts +135 -1
  170. package/dist/inspection-types.d.ts.map +1 -1
  171. package/dist/instance-buffer-cache.d.ts +4 -4
  172. package/dist/instance-buffer-cache.d.ts.map +1 -1
  173. package/dist/instances.d.ts +47 -100
  174. package/dist/instances.d.ts.map +1 -1
  175. package/dist/internal.mjs +7 -8
  176. package/dist/internal.mjs.map +1 -1
  177. package/dist/pbr-pipeline.d.ts +22 -4
  178. package/dist/pbr-pipeline.d.ts.map +1 -1
  179. package/dist/pipeline/single-layer-medium-passes.d.ts +73 -0
  180. package/dist/pipeline/single-layer-medium-passes.d.ts.map +1 -0
  181. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  182. package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
  183. package/dist/pipeline/standard-output/lut-gpu.d.ts +2 -2
  184. package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -1
  185. package/dist/pipeline/standard-output/resources.d.ts +2 -2
  186. package/dist/pipeline/standard-output/resources.d.ts.map +1 -1
  187. package/dist/pipeline/standard-output/types.d.ts +4 -2
  188. package/dist/pipeline/standard-output/types.d.ts.map +1 -1
  189. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  190. package/dist/pipeline/standard-post.d.ts.map +1 -1
  191. package/dist/pipeline-builder.d.ts +1 -1
  192. package/dist/pipeline-builder.d.ts.map +1 -1
  193. package/dist/plugin.d.ts.map +1 -1
  194. package/dist/prepare/prepared-graphics-resolver.d.ts +2 -0
  195. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  196. package/dist/publication/contract.d.ts +77 -0
  197. package/dist/publication/contract.d.ts.map +1 -0
  198. package/dist/publication/dependencies.d.ts +7 -0
  199. package/dist/publication/dependencies.d.ts.map +1 -0
  200. package/dist/publication/prepare-geometry.d.ts +9 -0
  201. package/dist/publication/prepare-geometry.d.ts.map +1 -0
  202. package/dist/publication/programs.d.ts +5 -0
  203. package/dist/publication/programs.d.ts.map +1 -0
  204. package/dist/publication/publisher.d.ts +33 -0
  205. package/dist/publication/publisher.d.ts.map +1 -0
  206. package/dist/publication/receiver.d.ts +29 -0
  207. package/dist/publication/receiver.d.ts.map +1 -0
  208. package/dist/publication/resource-scope.d.ts +18 -0
  209. package/dist/publication/resource-scope.d.ts.map +1 -0
  210. package/dist/publication/targets.d.ts +33 -0
  211. package/dist/publication/targets.d.ts.map +1 -0
  212. package/dist/publication/video.d.ts +5 -0
  213. package/dist/publication/video.d.ts.map +1 -0
  214. package/dist/record/barrel-distortion-frame.d.ts +15 -0
  215. package/dist/record/barrel-distortion-frame.d.ts.map +1 -0
  216. package/dist/record/dynamic-geometry-consumption.d.ts +10 -2
  217. package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -1
  218. package/dist/record/fold-buckets.d.ts +95 -0
  219. package/dist/record/fold-buckets.d.ts.map +1 -0
  220. package/dist/record/fold-instance-buffer.d.ts +6 -0
  221. package/dist/record/fold-instance-buffer.d.ts.map +1 -0
  222. package/dist/record/frame-lighting.d.ts +1 -0
  223. package/dist/record/frame-lighting.d.ts.map +1 -1
  224. package/dist/record/frame-snapshot.d.ts +43 -9
  225. package/dist/record/frame-snapshot.d.ts.map +1 -1
  226. package/dist/record/frame.d.ts +15 -65
  227. package/dist/record/frame.d.ts.map +1 -1
  228. package/dist/record/gpu-draw-receipts.d.ts +11 -0
  229. package/dist/record/gpu-draw-receipts.d.ts.map +1 -0
  230. package/dist/record/gpu-timing.d.ts +8 -0
  231. package/dist/record/gpu-timing.d.ts.map +1 -1
  232. package/dist/record/helpers.d.ts +2 -2
  233. package/dist/record/helpers.d.ts.map +1 -1
  234. package/dist/record/main-pass-geometry.d.ts +22 -6
  235. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  236. package/dist/record/main-pass-material.d.ts +14 -10
  237. package/dist/record/main-pass-material.d.ts.map +1 -1
  238. package/dist/record/main-pass-sprite-draws.d.ts +3 -16
  239. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  240. package/dist/record/main-pass.d.ts +13 -2
  241. package/dist/record/main-pass.d.ts.map +1 -1
  242. package/dist/record/motion-blur-receipt.d.ts +11 -0
  243. package/dist/record/motion-blur-receipt.d.ts.map +1 -0
  244. package/dist/record/prepared-material-bindings.d.ts +3 -3
  245. package/dist/record/prepared-material-bindings.d.ts.map +1 -1
  246. package/dist/record/probe-blend-buffer.d.ts +16 -0
  247. package/dist/record/probe-blend-buffer.d.ts.map +1 -0
  248. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  249. package/dist/record/render-context.d.ts +24 -5
  250. package/dist/record/render-context.d.ts.map +1 -1
  251. package/dist/record/shadow-pass.d.ts +2 -0
  252. package/dist/record/shadow-pass.d.ts.map +1 -1
  253. package/dist/record/skybox-post-pass.d.ts +1 -1
  254. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  255. package/dist/record/sprite-instance-buffer.d.ts +31 -0
  256. package/dist/record/sprite-instance-buffer.d.ts.map +1 -0
  257. package/dist/record/target-capture-graph.d.ts +19 -0
  258. package/dist/record/target-capture-graph.d.ts.map +1 -0
  259. package/dist/record/typed-frame-graph.d.ts +24 -15
  260. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  261. package/dist/record/view-ubo.d.ts +11 -3
  262. package/dist/record/view-ubo.d.ts.map +1 -1
  263. package/dist/record/volume-params.d.ts +9 -0
  264. package/dist/record/volume-params.d.ts.map +1 -0
  265. package/dist/recovery/render-system-candidate.d.ts +14 -4
  266. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  267. package/dist/reflection/record-owner.d.ts.map +1 -1
  268. package/dist/render-contract.d.ts +46 -5
  269. package/dist/render-contract.d.ts.map +1 -1
  270. package/dist/render-graph-primitives.d.ts.map +1 -1
  271. package/dist/render-pipeline.d.ts +103 -2
  272. package/dist/render-pipeline.d.ts.map +1 -1
  273. package/dist/render-system-extract-tail.d.ts +3 -2
  274. package/dist/render-system-extract-tail.d.ts.map +1 -1
  275. package/dist/render-system-extract.d.ts +18 -9
  276. package/dist/render-system-extract.d.ts.map +1 -1
  277. package/dist/render-system-presentation.d.ts +23 -0
  278. package/dist/render-system-presentation.d.ts.map +1 -0
  279. package/dist/render-system-producer-roots.d.ts +10 -0
  280. package/dist/render-system-producer-roots.d.ts.map +1 -0
  281. package/dist/render-system-projections.d.ts +3 -2
  282. package/dist/render-system-projections.d.ts.map +1 -1
  283. package/dist/render-system-residency.d.ts +12 -0
  284. package/dist/render-system-residency.d.ts.map +1 -0
  285. package/dist/render-system.d.ts +22 -21
  286. package/dist/render-system.d.ts.map +1 -1
  287. package/dist/scene/probe-blend.d.ts +27 -0
  288. package/dist/scene/probe-blend.d.ts.map +1 -1
  289. package/dist/scene/render-scene.d.ts +18 -7
  290. package/dist/scene/render-scene.d.ts.map +1 -1
  291. package/dist/scene/render-source.d.ts +42 -0
  292. package/dist/scene/render-source.d.ts.map +1 -0
  293. package/dist/scene/source-systems.d.ts +6 -0
  294. package/dist/scene/source-systems.d.ts.map +1 -0
  295. package/dist/ssao-buffers.d.ts +9 -0
  296. package/dist/ssao-buffers.d.ts.map +1 -1
  297. package/dist/surface/dynamic-input.d.ts +133 -0
  298. package/dist/surface/dynamic-input.d.ts.map +1 -0
  299. package/dist/surface/gpu-driven.d.ts +41 -0
  300. package/dist/surface/gpu-driven.d.ts.map +1 -0
  301. package/dist/surface/submission-observation.d.ts +136 -0
  302. package/dist/surface/submission-observation.d.ts.map +1 -0
  303. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  304. package/dist/systems/skin-palette-types.d.ts +10 -0
  305. package/dist/systems/skin-palette-types.d.ts.map +1 -1
  306. package/dist/systems/transparent-dispatch.d.ts +2 -2
  307. package/dist/systems/transparent-dispatch.d.ts.map +1 -1
  308. package/dist/systems/transparent-sort-config.d.ts +2 -1
  309. package/dist/systems/transparent-sort-config.d.ts.map +1 -1
  310. package/dist/temporal/gpu.d.ts +13 -1
  311. package/dist/temporal/gpu.d.ts.map +1 -1
  312. package/dist/temporal/index.mjs +6 -7
  313. package/dist/temporal/inspection.d.ts.map +1 -1
  314. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  315. package/dist/temporal/target-coverage-attachment.d.ts.map +1 -1
  316. package/dist/transmission/single-layer-medium.d.ts +89 -0
  317. package/dist/transmission/single-layer-medium.d.ts.map +1 -0
  318. package/dist/typed-render-graph-primitives.d.ts +14 -2
  319. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  320. package/dist/volume/component.d.ts +9 -4
  321. package/dist/volume/component.d.ts.map +1 -1
  322. package/dist/volume/extract.d.ts +1 -1
  323. package/dist/volume/extract.d.ts.map +1 -1
  324. package/dist/volume/inspection.d.ts +2 -0
  325. package/dist/volume/inspection.d.ts.map +1 -1
  326. package/dist/volume/passes.d.ts +4 -1
  327. package/dist/volume/passes.d.ts.map +1 -1
  328. package/dist/volume/resources.d.ts.map +1 -1
  329. package/package.json +22 -21
  330. package/src/__tests__/atmosphere-daylight.dawn.test.ts +72 -0
  331. package/src/__tests__/auto-exposure-transaction.integration.test.ts +4 -9
  332. package/src/__tests__/barrel-distortion-lkg.integration.test.ts +353 -0
  333. package/src/__tests__/barrel-distortion.dawn.test.ts +41 -0
  334. package/src/__tests__/barrel-distortion.unit.test.ts +364 -0
  335. package/src/__tests__/construct-renderer-identity.unit.test.ts +49 -0
  336. package/src/__tests__/detached-recovery-graph.unit.test.ts +277 -0
  337. package/src/__tests__/device-feature-admission.unit.test.ts +3 -2
  338. package/src/__tests__/device-loss-fanout.unit.test.ts +36 -1
  339. package/src/__tests__/docs-gate-green.unit.test.ts +112 -1
  340. package/src/__tests__/factory-contract.integration.test.ts +295 -45
  341. package/src/__tests__/feature-depth-input.dawn.test.ts +129 -2
  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-material-artifacts.unit.test.ts +143 -0
  347. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +5 -6
  348. package/src/__tests__/gpu-driven-production.integration.test.ts +482 -3
  349. package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +6 -5
  350. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +94 -6
  351. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +18 -2
  352. package/src/__tests__/gpu-driven-view.browser.test.ts +5 -8
  353. package/src/__tests__/gpu-driven-view.dawn.test.ts +6 -2
  354. package/src/__tests__/gpu-driven-view.unit.test.ts +1 -1
  355. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +1 -0
  356. package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -4
  357. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +1 -0
  358. package/src/__tests__/gpu-scene.unit.test.ts +119 -9
  359. package/src/__tests__/instance-allocation-failure.unit.test.ts +57 -0
  360. package/src/__tests__/instances-culling.integration.test.ts +10 -36
  361. package/src/__tests__/instances-store.unit.test.ts +97 -305
  362. package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
  363. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +4 -1
  364. package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +145 -0
  365. package/src/__tests__/material-shader-prewarm.unit.test.ts +59 -0
  366. package/src/__tests__/material-snapshot-mutation.integration.test.ts +39 -1
  367. package/src/__tests__/materials-standard-contract.unit.test.ts +0 -1
  368. package/src/__tests__/morph-culling-reentry.dawn.test.ts +5 -5
  369. package/src/__tests__/morph-visual-evidence.integration.test.ts +8 -7
  370. package/src/__tests__/motion-blur-compute.dawn.test.ts +13 -7
  371. package/src/__tests__/motion-blur.pixel.integration.test.ts +2 -2
  372. package/src/__tests__/nineslice-handle-surface.unit.test.ts +4 -4
  373. package/src/__tests__/pbr-view-layout.unit.test.ts +9 -0
  374. package/src/__tests__/physical-clearcoat.integration.test.ts +66 -8
  375. package/src/__tests__/physical-material-budget.unit.test.ts +40 -0
  376. package/src/__tests__/prepared-graphics-capability.unit.test.ts +66 -2
  377. package/src/__tests__/probe-bindings.unit.test.ts +582 -1
  378. package/src/__tests__/probe-scene-dirty.integration.test.ts +28 -1
  379. package/src/__tests__/receipt-observation-retirement.unit.test.ts +65 -0
  380. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +69 -0
  381. package/src/__tests__/render-error-code-owner.test-d.ts +8 -0
  382. package/src/__tests__/render-feature-diagnostics.unit.test.ts +15 -2
  383. package/src/__tests__/render-feature-error-reporting.unit.test.ts +21 -1
  384. package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +10 -1
  385. package/src/__tests__/render-feature-retry.unit.test.ts +12 -1
  386. package/src/__tests__/render-scene-mixed-updates.integration.test.ts +145 -19
  387. package/src/__tests__/render-scene-projection.unit.test.ts +36 -11
  388. package/src/__tests__/render-scene-temporal-retry.unit.test.ts +20 -20
  389. package/src/__tests__/render-target-probe-soak.perf.test.ts +2 -2
  390. package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +3 -3
  391. package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +5 -2
  392. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +42 -8
  393. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +16 -0
  394. package/src/__tests__/renderer-frame-transaction.integration.test.ts +26 -10
  395. package/src/__tests__/resource-allocation.unit.test.ts +123 -0
  396. package/src/__tests__/single-layer-medium-passes.unit.test.ts +275 -0
  397. package/src/__tests__/skin-motion-regression.unit.test.ts +70 -1
  398. package/src/__tests__/skin-palette-recycling.dawn.test.ts +97 -0
  399. package/src/__tests__/skinned-shadow-caster.test.ts +15 -0
  400. package/src/__tests__/ssr-carrier-public-contract.unit.test.ts +21 -0
  401. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +4 -4
  402. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +2 -2
  403. package/src/__tests__/ssr-gpu-dispatch.ts +1 -1
  404. package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +6 -2
  405. package/src/__tests__/standard-output-chain-contract.unit.test.ts +28 -0
  406. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +46 -1
  407. package/src/__tests__/standard-pipeline.integration.test.ts +182 -1
  408. package/src/__tests__/surface-dynamic-buffer-lifecycle.unit.test.ts +506 -0
  409. package/src/__tests__/surface-submission-observation.unit.test.ts +812 -0
  410. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +32 -0
  411. package/src/__tests__/temporal-performance.integration.test.ts +3 -3
  412. package/src/__tests__/visibility-instances.integration.test.ts +7 -19
  413. package/src/__tests__/visibility-producer-matrix.integration.test.ts +9 -20
  414. package/src/__tests__/volume-optics-oracle.unit.test.ts +4 -2
  415. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +30 -20
  416. package/src/__tests__/volumetric-fog-errors.unit.test.ts +4 -3
  417. package/src/__tests__/volumetric-fog-multiple.unit.test.ts +55 -0
  418. package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +1 -1
  419. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +113 -9
  420. package/src/__tests__/volumetric-fog-topology.unit.test.ts +5 -3
  421. package/src/assembly/backend-contract.ts +30 -0
  422. package/src/assembly/barrel-distortion-inspection.ts +22 -0
  423. package/src/assembly/canvas-draw-guard.ts +11 -0
  424. package/src/assembly/cloud-shadow-entry.ts +22 -0
  425. package/src/assembly/device-feature-admission.ts +2 -2
  426. package/src/assembly/dynamic-geometry-host.ts +65 -37
  427. package/src/assembly/dynamic-geometry-runtime.ts +7 -1
  428. package/src/assembly/factory.ts +2 -30
  429. package/src/assembly/host-contract.ts +8 -5
  430. package/src/assembly/material/assembly.ts +25 -13
  431. package/src/assembly/material/pipeline-helpers.ts +53 -12
  432. package/src/assembly/material-shader-policy.ts +55 -0
  433. package/src/assembly/material-shader-prewarm.ts +21 -10
  434. package/src/assembly/recovery/device-loss-fanout.ts +13 -0
  435. package/src/assembly/recovery/renderer-recover.ts +50 -18
  436. package/src/assembly/render-feature-post-processes.ts +54 -0
  437. package/src/assembly/render-target-host.ts +57 -47
  438. package/src/assembly/renderer-facade.ts +7 -2
  439. package/src/assembly/renderer-feature-inspection.ts +8 -2
  440. package/src/assembly/renderer-frame-transaction.ts +23 -0
  441. package/src/assembly/renderer-inspection.ts +28 -0
  442. package/src/assembly/skin-palette-owner.ts +1 -1
  443. package/src/assembly/temporal-post-process-prewarm.ts +3 -3
  444. package/src/assembly/webgpu-pbr-ready.ts +9 -1
  445. package/src/assembly/webgpu-ready-per-pass.ts +104 -0
  446. package/src/assembly/webgpu-ready.ts +116 -62
  447. package/src/assembly/webgpu-renderer-bootstrap.ts +0 -12
  448. package/src/assembly/webgpu-renderer-observation-owner.ts +222 -0
  449. package/src/assembly/webgpu-renderer.ts +303 -313
  450. package/src/assembly/webgpu-vertex-layouts.ts +1 -1
  451. package/src/barrel-distortion.ts +247 -0
  452. package/src/cloud/__tests__/cloud-density-parity.dawn.test.ts +173 -0
  453. package/src/cloud/__tests__/cloud-layer-framebuffer-y.dawn.test.ts +265 -0
  454. package/src/cloud/__tests__/cloud-layer.browser.test.ts +432 -0
  455. package/src/cloud/__tests__/cloud-layer.unit.test.ts +634 -0
  456. package/src/cloud/density.ts +600 -0
  457. package/src/cloud/extract.ts +110 -0
  458. package/src/cloud/feature.ts +1810 -0
  459. package/src/cloud/frame-context.ts +42 -0
  460. package/src/cloud/inspection.ts +130 -0
  461. package/src/cloud/optics.ts +285 -0
  462. package/src/cloud/parameters.ts +255 -0
  463. package/src/cloud/resources.ts +122 -0
  464. package/src/cloud/shadow.ts +167 -0
  465. package/src/cloud/temporal.ts +226 -0
  466. package/src/components/barrel-distortion.ts +46 -0
  467. package/src/components/cloud-layer.ts +47 -0
  468. package/src/components/index.ts +2 -0
  469. package/src/components/instances.ts +18 -45
  470. package/src/components/sprite-instances.ts +1 -1
  471. package/src/construct-renderer.ts +1 -1
  472. package/src/device/gpu-residency.ts +26 -25
  473. package/src/dynamic-geometry.ts +12 -1
  474. package/src/errors/cloud.ts +103 -0
  475. package/src/errors/render.ts +47 -8
  476. package/src/examples/single-layer-medium-surface.ts +59 -0
  477. package/src/extract/camera.ts +15 -1
  478. package/src/extract/contracts.ts +5 -0
  479. package/src/extract/material-context.ts +21 -0
  480. package/src/features/barrel-distortion.ts +172 -0
  481. package/src/features/error-reporting.ts +26 -6
  482. package/src/features/host.ts +128 -19
  483. package/src/features/plan.ts +10 -0
  484. package/src/features/render-graph-contribution.ts +9 -0
  485. package/src/features/render-graph-raster.ts +45 -3
  486. package/src/features/targets.ts +66 -0
  487. package/src/features/types.ts +51 -0
  488. package/src/fullscreen-post-process-pass.ts +41 -9
  489. package/src/glyph-text-layout-system.ts +2 -2
  490. package/src/gpu-dirty-ranges.ts +48 -0
  491. package/src/gpu-driven/batch-topology.ts +53 -0
  492. package/src/gpu-driven/material-artifacts.ts +33 -25
  493. package/src/gpu-driven/prepared-draw.ts +12 -4
  494. package/src/gpu-driven/production-raster-material.ts +1894 -0
  495. package/src/gpu-driven/production-raster.ts +825 -784
  496. package/src/gpu-driven/resource-allocation.ts +142 -0
  497. package/src/gpu-driven/shadow-ownership.ts +71 -0
  498. package/src/gpu-driven/view-gpu.ts +164 -10
  499. package/src/gpu-scene.ts +75 -84
  500. package/src/hdrp-buffers.ts +4 -3
  501. package/src/ibl/frame-resources.ts +0 -1
  502. package/src/ibl/skylight-bind-group.ts +62 -32
  503. package/src/index.ts +205 -9
  504. package/src/inspection-types.ts +162 -0
  505. package/src/instance-buffer-cache.ts +4 -4
  506. package/src/instances.ts +152 -569
  507. package/src/pbr-pipeline.ts +230 -30
  508. package/src/pipeline/single-layer-medium-passes.ts +267 -0
  509. package/src/pipeline/standard-forward-lane.ts +313 -57
  510. package/src/pipeline/standard-output/graph.ts +8 -2
  511. package/src/pipeline/standard-output/lut-gpu.ts +3 -3
  512. package/src/pipeline/standard-output/resources.ts +2 -2
  513. package/src/pipeline/standard-output/types.ts +12 -1
  514. package/src/pipeline/standard-pipeline.ts +291 -18
  515. package/src/pipeline/standard-post.ts +90 -24
  516. package/src/pipeline-builder.ts +1 -1
  517. package/src/plugin.ts +4 -0
  518. package/src/points-lines/__tests__/points-lines.bench.ts +17 -8
  519. package/src/prepare/prepared-graphics-resolver.ts +2 -0
  520. package/src/publication/__tests__/derived-geometry.integration.test.ts +177 -0
  521. package/src/publication/__tests__/features.integration.test.ts +122 -0
  522. package/src/publication/__tests__/programs.unit.test.ts +25 -0
  523. package/src/publication/__tests__/publication.integration.test.ts +370 -0
  524. package/src/publication/__tests__/targets.integration.test.ts +120 -0
  525. package/src/publication/__tests__/video.browser.test.ts +50 -0
  526. package/src/publication/contract.ts +104 -0
  527. package/src/publication/dependencies.ts +54 -0
  528. package/src/publication/prepare-geometry.ts +94 -0
  529. package/src/publication/programs.ts +45 -0
  530. package/src/publication/publisher.ts +618 -0
  531. package/src/publication/receiver.ts +297 -0
  532. package/src/publication/resource-scope.ts +31 -0
  533. package/src/publication/targets.ts +182 -0
  534. package/src/publication/video.ts +33 -0
  535. package/src/record/__tests__/main-pass-geometry.unit.test.ts +30 -0
  536. package/src/record/__tests__/material-bg-cache.unit.test.ts +15 -1
  537. package/src/record/__tests__/sprite-material-abi.unit.test.ts +9 -11
  538. package/src/record/__tests__/temporal-payload.unit.test.ts +1 -1
  539. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +11 -0
  540. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -0
  541. package/src/record/__tests__/video-texture-view.unit.test.ts +9 -9
  542. package/src/record/__tests__/view-ubo-layout.unit.test.ts +4 -3
  543. package/src/record/barrel-distortion-frame.ts +110 -0
  544. package/src/record/dynamic-geometry-consumption.ts +39 -9
  545. package/src/record/fold-buckets.ts +855 -0
  546. package/src/record/fold-instance-buffer.ts +51 -0
  547. package/src/record/frame-lighting.ts +36 -2
  548. package/src/record/frame-snapshot.ts +45 -9
  549. package/src/record/frame.ts +258 -1105
  550. package/src/record/gpu-draw-receipts.ts +38 -0
  551. package/src/record/gpu-timing.ts +48 -5
  552. package/src/record/helpers.ts +2 -2
  553. package/src/record/main-pass-geometry.ts +272 -84
  554. package/src/record/main-pass-material.ts +102 -36
  555. package/src/record/main-pass-sprite-draws.ts +13 -217
  556. package/src/record/main-pass.ts +164 -27
  557. package/src/record/motion-blur-receipt.ts +91 -0
  558. package/src/record/prepared-material-bindings.ts +11 -6
  559. package/src/record/probe-blend-buffer.ts +180 -0
  560. package/src/record/recovery-pipeline.ts +35 -3
  561. package/src/record/render-context.ts +41 -5
  562. package/src/record/shadow-pass.ts +30 -4
  563. package/src/record/skybox-post-pass.ts +28 -4
  564. package/src/record/sprite-instance-buffer.ts +155 -0
  565. package/src/record/target-capture-graph.ts +258 -0
  566. package/src/record/typed-frame-graph.ts +776 -340
  567. package/src/record/view-ubo.ts +27 -4
  568. package/src/record/volume-params.ts +225 -0
  569. package/src/recovery/render-system-candidate.ts +243 -50
  570. package/src/reflection/record-owner.ts +7 -3
  571. package/src/render-contract.ts +54 -5
  572. package/src/render-graph-primitives.ts +7 -1
  573. package/src/render-pipeline.ts +130 -0
  574. package/src/render-system-extract-tail.ts +90 -78
  575. package/src/render-system-extract.ts +76 -76
  576. package/src/render-system-presentation.ts +56 -0
  577. package/src/render-system-producer-roots.ts +96 -0
  578. package/src/render-system-projections.ts +3 -2
  579. package/src/render-system-residency.ts +159 -0
  580. package/src/render-system.ts +421 -408
  581. package/src/scene/probe-blend.ts +96 -4
  582. package/src/scene/render-scene.ts +416 -337
  583. package/src/scene/render-source.ts +90 -0
  584. package/src/scene/source-systems.ts +33 -0
  585. package/src/ssao-buffers.ts +95 -2
  586. package/src/surface/__tests__/dynamic-input.unit.test.ts +374 -0
  587. package/src/surface/__tests__/gpu-driven.unit.test.ts +87 -0
  588. package/src/surface/dynamic-input.ts +658 -0
  589. package/src/surface/gpu-driven.ts +147 -0
  590. package/src/surface/submission-observation.ts +517 -0
  591. package/src/systems/skin-palette-allocator.ts +5 -1
  592. package/src/systems/skin-palette-types.ts +11 -0
  593. package/src/systems/transparent-dispatch.ts +2 -2
  594. package/src/systems/transparent-sort-config.ts +3 -1
  595. package/src/temporal/gpu.ts +101 -17
  596. package/src/temporal/inspection.ts +7 -1
  597. package/src/temporal/standard-scene-data.ts +5 -0
  598. package/src/temporal/target-coverage-attachment.ts +4 -0
  599. package/src/transmission/__tests__/single-layer-medium.unit.test.ts +149 -0
  600. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +12 -11
  601. package/src/transmission/single-layer-medium.ts +322 -0
  602. package/src/typed-render-graph-primitives.ts +70 -2
  603. package/src/volume/component.ts +10 -9
  604. package/src/volume/extract.ts +11 -5
  605. package/src/volume/inspection.ts +3 -0
  606. package/src/volume/passes.ts +87 -4
  607. package/src/volume/resources.ts +2 -1
  608. package/dist/chunk-3RTCJTH4.mjs +0 -1610
  609. package/dist/chunk-3RTCJTH4.mjs.map +0 -1
  610. package/dist/chunk-BLXGONR2.mjs.map +0 -1
  611. package/dist/chunk-ILQ5MEZF.mjs +0 -32
  612. package/dist/chunk-ILQ5MEZF.mjs.map +0 -1
  613. package/dist/chunk-LEAXOMYJ.mjs.map +0 -1
  614. package/dist/chunk-NB2S7ZRX.mjs +0 -1783
  615. package/dist/chunk-NB2S7ZRX.mjs.map +0 -1
  616. package/dist/chunk-NNQL7CB3.mjs +0 -1713
  617. package/dist/chunk-NNQL7CB3.mjs.map +0 -1
  618. package/dist/chunk-OO3DRYK7.mjs.map +0 -1
  619. package/dist/chunk-PA5DRJNI.mjs.map +0 -1
  620. package/dist/chunk-PLAXOOX5.mjs +0 -1907
  621. package/dist/chunk-PLAXOOX5.mjs.map +0 -1
  622. package/dist/chunk-RXZ3RGOU.mjs +0 -53
  623. package/dist/chunk-RXZ3RGOU.mjs.map +0 -1
  624. package/dist/chunk-VOZWDM3L.mjs.map +0 -1
@@ -1,11 +1,18 @@
1
+ import { addTargetCaptureGraphPasses, type CubeCaptureGraphState } from './target-capture-graph';
2
+
3
+ export type { CubeCaptureGraphState, CubeCaptureGraphWork } from './target-capture-graph';
4
+
1
5
  import { encodeMipmapLevel } from '@forgeax/engine-assets-runtime';
2
6
  import {
3
7
  type CompiledRenderGraph,
4
8
  type GraphResourceResolver,
5
9
  RenderGraphBuilder,
6
10
  RenderGraphError,
11
+ type RenderGraphGenerationAllocationEntry,
12
+ type RenderGraphGenerationAllocationInspection,
7
13
  type RenderGraphPassInstrumentation,
8
14
  type RenderGraphPassInstrumentationScope,
15
+ type RenderGraphResourceAllocationInspection,
9
16
  } from '@forgeax/engine-render-graph';
10
17
  import type {
11
18
  BindGroup,
@@ -23,6 +30,7 @@ import { RhiError } from '@forgeax/engine-rhi';
23
30
  import { err, ok, type Result } from '@forgeax/engine-types';
24
31
  import { executeRendererFrameTransaction } from '../assembly/renderer-frame-transaction';
25
32
  import { standardBloomAdmitted } from '../bloom-admission';
33
+ import { CLOUD_LAYER_FEATURE_IDENTITY } from '../cloud/feature';
26
34
  import { type RenderError, RenderFeatureStageFailedError } from '../errors/render';
27
35
  import { depthOfFieldTopology } from '../features/depth-of-field/depth-of-field-feature';
28
36
  import { resolveDepthOfFieldFrameParams } from '../features/depth-of-field/depth-of-field-params';
@@ -64,11 +72,7 @@ import {
64
72
  } from '../gpu-texture-usage';
65
73
  import { GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_UNIFORM } from '../gpu-usage';
66
74
  import { createStandardSurfaceLightingBindGroup } from '../hdrp-buffers';
67
- import type {
68
- MotionBlurExecutionPass,
69
- MotionBlurExecutionReceipt,
70
- RenderFeatureGraphInspection,
71
- } from '../inspection-types';
75
+ import type { RenderFeatureGraphInspection } from '../inspection-types';
72
76
  import type { RenderExtent } from '../pipeline/render-extent';
73
77
  import {
74
78
  type StandardTopologyInputValue,
@@ -95,15 +99,15 @@ import type {
95
99
  import type { ExtractedLights, ExtractedVolumetricFog } from '../render-system-extract';
96
100
  import { SHADOW_ATLAS_DEFAULT_FACE_SIZE, SHADOW_ATLAS_DEFAULT_LAYERS } from '../shadow-atlas';
97
101
  import type { SsrAdmissionResult, SsrSpatialAdmission } from '../ssr/admission';
98
- import type { RenderTarget } from '../targets/contracts';
99
- import type { RenderTargetPhysical } from '../targets/physical';
100
102
  import {
101
103
  abortTemporalGpuSubmit,
104
+ CLOUD_HISTORY_FORMATS,
102
105
  commitTemporalGpuSubmit,
103
106
  getTemporalGpuState,
104
107
  hasPendingTemporalGpuSubmit,
105
108
  retireTemporalGpuState,
106
109
  retireTemporalGpuStateAfterFence,
110
+ stageTemporalGpuSubmit,
107
111
  TEMPORAL_HISTORY_FORMATS,
108
112
  temporalReadIndex,
109
113
  temporalWriteIndex,
@@ -124,6 +128,7 @@ import type { GpuPassTimingPassIdentity } from './gpu-pass-timing/contract.js';
124
128
  import type { GpuPassTimingCapture, GpuPassTimingSession } from './gpu-pass-timing/session.js';
125
129
  import type { GpuTimingCapture } from './gpu-timing';
126
130
  import { encodeMainPass } from './main-pass';
131
+ import { deriveMotionBlurExecutionReceipt } from './motion-blur-receipt';
127
132
  import type {
128
133
  _InternalRenderPipelineContext,
129
134
  PipelineState,
@@ -327,6 +332,125 @@ function importTemporalHistoryTargets(
327
332
  };
328
333
  }
329
334
 
335
+ type CloudHistoryKind = keyof typeof CLOUD_HISTORY_FORMATS;
336
+
337
+ function cloudHistorySurface(
338
+ state: import('../temporal/gpu').TemporalGpuState,
339
+ kind: CloudHistoryKind,
340
+ index: 0 | 1,
341
+ ): { readonly texture: Texture; readonly view: TextureView } {
342
+ if (!state.cloudHistoryEnabled) {
343
+ throw new Error('cloud history target resolved without a cloud-enabled temporal state');
344
+ }
345
+ switch (kind) {
346
+ case 'radiance':
347
+ if (state.cloudRadiance === undefined)
348
+ throw new Error('cloud radiance history is unavailable');
349
+ return state.cloudRadiance[index];
350
+ case 'transmittance':
351
+ if (state.cloudTransmittance === undefined)
352
+ throw new Error('cloud transmittance history is unavailable');
353
+ return state.cloudTransmittance[index];
354
+ case 'depth':
355
+ if (state.cloudDepth === undefined) throw new Error('cloud depth history is unavailable');
356
+ return state.cloudDepth[index];
357
+ }
358
+ }
359
+
360
+ /** Import one renderer-owned cloud history attachment into the typed graph. */
361
+ function importCloudHistoryTarget(
362
+ graph: RenderGraphBuilder<RenderPipelineFrame>,
363
+ label: string,
364
+ kind: CloudHistoryKind,
365
+ role: 'read' | 'write',
366
+ ): import('../render-pipeline').RenderPipelineTarget {
367
+ const format = CLOUD_HISTORY_FORMATS[kind];
368
+ const texture = graph.importTexture(
369
+ label,
370
+ {
371
+ format,
372
+ // Cloud transport/history is intentionally half-resolution. The
373
+ // renderer-owned temporal state allocates the same ceil(width/2) extent
374
+ // and the full-resolution resolve reconstructs scene colour from it.
375
+ size: 'half-surface',
376
+ usage:
377
+ GPU_TEXTURE_USAGE_COPY_SRC |
378
+ GPU_TEXTURE_USAGE_COPY_DST |
379
+ GPU_TEXTURE_USAGE_RENDER_ATTACHMENT |
380
+ GPU_TEXTURE_USAGE_TEXTURE_BINDING,
381
+ domain: 'linear-hdr',
382
+ },
383
+ (frame) => {
384
+ const internal = frame as _InternalRenderPipelineContext;
385
+ const state = getTemporalGpuState(
386
+ internal.frameState,
387
+ internal.runtime.device,
388
+ internal.runtime.deviceScope,
389
+ frame.targetW,
390
+ frame.targetH,
391
+ true,
392
+ );
393
+ const index = role === 'read' ? temporalReadIndex(state) : temporalWriteIndex(state);
394
+ return cloudHistorySurface(state, kind, index).texture;
395
+ },
396
+ );
397
+ if (!texture.ok) throw texture.error;
398
+ const view = graph.importView(
399
+ texture.value,
400
+ { label: `${label}.view`, dimension: '2d' },
401
+ (frame) => {
402
+ const internal = frame as _InternalRenderPipelineContext;
403
+ const state = getTemporalGpuState(
404
+ internal.frameState,
405
+ internal.runtime.device,
406
+ internal.runtime.deviceScope,
407
+ frame.targetW,
408
+ frame.targetH,
409
+ true,
410
+ );
411
+ const index = role === 'read' ? temporalReadIndex(state) : temporalWriteIndex(state);
412
+ return cloudHistorySurface(state, kind, index).view;
413
+ },
414
+ );
415
+ if (!view.ok) throw view.error;
416
+ return { texture: texture.value, view: view.value, format, sampleCount: 1 };
417
+ }
418
+
419
+ function importCloudHistoryTargets(
420
+ graph: RenderGraphBuilder<RenderPipelineFrame>,
421
+ ): NonNullable<
422
+ import('../render-pipeline').RenderPipelineBuildContext<RenderPipelineFrame>['cloudHistory']
423
+ > {
424
+ return {
425
+ currentRadiance: importCloudHistoryTarget(
426
+ graph,
427
+ 'cloud-history-radiance-current',
428
+ 'radiance',
429
+ 'write',
430
+ ),
431
+ previousRadiance: importCloudHistoryTarget(
432
+ graph,
433
+ 'cloud-history-radiance-previous',
434
+ 'radiance',
435
+ 'read',
436
+ ),
437
+ currentTransmittance: importCloudHistoryTarget(
438
+ graph,
439
+ 'cloud-history-transmittance-current',
440
+ 'transmittance',
441
+ 'write',
442
+ ),
443
+ previousTransmittance: importCloudHistoryTarget(
444
+ graph,
445
+ 'cloud-history-transmittance-previous',
446
+ 'transmittance',
447
+ 'read',
448
+ ),
449
+ currentDepth: importCloudHistoryTarget(graph, 'cloud-history-depth-current', 'depth', 'write'),
450
+ previousDepth: importCloudHistoryTarget(graph, 'cloud-history-depth-previous', 'depth', 'read'),
451
+ };
452
+ }
453
+
330
454
  function importSsrHistoryTarget(
331
455
  graph: RenderGraphBuilder<RenderPipelineFrame>,
332
456
  label: string,
@@ -485,19 +609,6 @@ export function createTemporalGraphRoster(input: {
485
609
  };
486
610
  }
487
611
 
488
- export interface CubeCaptureGraphWork {
489
- readonly target: RenderTarget;
490
- readonly faceIndex: number;
491
- readonly physical: RenderTargetPhysical;
492
- readonly faceCamera: import('../render-contract').CameraSnapshot;
493
- readonly viewBindGroupDynamicOffset: number;
494
- }
495
-
496
- export interface CubeCaptureGraphState {
497
- work: readonly CubeCaptureGraphWork[];
498
- reflectionProbes?: ReflectionProbeGraphState;
499
- }
500
-
501
612
  export interface ReflectionProbeFilterGraphWork {
502
613
  readonly probeIndex: number;
503
614
  readonly faceIndex: number;
@@ -544,195 +655,6 @@ export function boundedReflectionProbeFilterWork(
544
655
  return state.work.slice(0, Math.max(0, state.maxStepsPerFrame));
545
656
  }
546
657
 
547
- function renderTargetMipCount(work: CubeCaptureGraphWork): number {
548
- const { descriptor } = work.physical;
549
- return descriptor.mipLevels === 1
550
- ? 1
551
- : Math.floor(Math.log2(Math.max(descriptor.width, descriptor.height))) + 1;
552
- }
553
-
554
- function addCubeCaptureGraphPasses(
555
- builder: RenderGraphBuilder<RenderPipelineFrame>,
556
- state: CubeCaptureGraphState,
557
- ): Result<void, RenderGraphError> {
558
- for (let index = 0; index < state.work.length; index += 1) {
559
- const slot = index;
560
- const initial = state.work[slot];
561
- if (initial === undefined) continue;
562
- const current = (): CubeCaptureGraphWork => state.work[slot] ?? initial;
563
- const texture = builder.importTexture(
564
- `cube-capture.${slot}.texture`,
565
- {
566
- format: initial.physical.descriptor.format,
567
- size: {
568
- width: initial.physical.descriptor.width,
569
- height: initial.physical.descriptor.height,
570
- depthOrArrayLayers: initial.physical.descriptor.sampleCount === 4 ? 1 : 6,
571
- },
572
- mipLevelCount:
573
- initial.physical.descriptor.sampleCount === 4 ? 1 : renderTargetMipCount(initial),
574
- sampleCount: initial.physical.descriptor.sampleCount,
575
- dimension: '2d',
576
- usage: 0x10 | 0x04 | 0x01,
577
- },
578
- () => current().physical.colorTextures[current().faceIndex] ?? initial.physical.texture,
579
- );
580
- if (!texture.ok) return texture;
581
- const color = builder.importView(
582
- texture.value,
583
- {
584
- label: `cube-capture.${slot}.color`,
585
- dimension: '2d',
586
- baseMipLevel: 0,
587
- mipLevelCount: 1,
588
- baseArrayLayer: initial.physical.descriptor.sampleCount === 4 ? 0 : initial.faceIndex,
589
- arrayLayerCount: 1,
590
- },
591
- () =>
592
- current().physical.faceViews[current().faceIndex] ??
593
- initial.physical.faceViews[initial.faceIndex] ??
594
- initial.physical.view,
595
- );
596
- if (!color.ok) return color;
597
- const depthTexture = builder.importTexture(
598
- `cube-capture.${slot}.depth-texture`,
599
- {
600
- format: 'depth24plus-stencil8',
601
- size: {
602
- width: initial.physical.descriptor.width,
603
- height: initial.physical.descriptor.height,
604
- depthOrArrayLayers: initial.physical.descriptor.sampleCount === 4 ? 1 : 6,
605
- },
606
- mipLevelCount: 1,
607
- sampleCount: initial.physical.descriptor.sampleCount,
608
- dimension: '2d',
609
- usage: 0x10,
610
- },
611
- () => current().physical.depthTextures[current().faceIndex] as Texture,
612
- );
613
- if (!depthTexture.ok) return depthTexture;
614
- const depth = builder.importView(
615
- depthTexture.value,
616
- {
617
- label: `cube-capture.${slot}.depth`,
618
- dimension: '2d',
619
- baseArrayLayer: initial.physical.descriptor.sampleCount === 4 ? 0 : initial.faceIndex,
620
- arrayLayerCount: 1,
621
- },
622
- () => current().physical.depthViews[current().faceIndex] as TextureView,
623
- );
624
- if (!depth.ok) return depth;
625
- const resolveTexture =
626
- initial.physical.descriptor.sampleCount === 4
627
- ? builder.importTexture(
628
- `cube-capture.${slot}.resolve-texture`,
629
- {
630
- format: initial.physical.descriptor.format,
631
- size: {
632
- width: initial.physical.descriptor.width,
633
- height: initial.physical.descriptor.height,
634
- depthOrArrayLayers: 6,
635
- },
636
- mipLevelCount: renderTargetMipCount(initial),
637
- sampleCount: 1,
638
- dimension: '2d',
639
- usage: 0x10 | 0x04 | 0x01,
640
- },
641
- () =>
642
- current().physical.resolveTexture ??
643
- initial.physical.resolveTexture ??
644
- initial.physical.texture,
645
- )
646
- : undefined;
647
- if (resolveTexture !== undefined && !resolveTexture.ok) return resolveTexture;
648
- const resolve =
649
- resolveTexture === undefined
650
- ? undefined
651
- : builder.importView(
652
- resolveTexture.value,
653
- {
654
- label: `cube-capture.${slot}.resolve`,
655
- dimension: '2d',
656
- baseMipLevel: 0,
657
- mipLevelCount: 1,
658
- baseArrayLayer: initial.faceIndex,
659
- arrayLayerCount: 1,
660
- },
661
- () =>
662
- current().physical.resolveFaceViews[current().faceIndex] ??
663
- initial.physical.resolveFaceViews[initial.faceIndex] ??
664
- initial.physical.resolveView,
665
- );
666
- if (resolve !== undefined && !resolve.ok) return resolve;
667
- const added = builder.addRasterPass(`cube-capture-face.${slot}`, {
668
- accesses: [
669
- { resource: color.value, usage: 'color-attachment' },
670
- { resource: depth.value, usage: 'depth-stencil-write' },
671
- ...(resolve === undefined
672
- ? []
673
- : [{ resource: resolve.value, usage: 'color-attachment' as const }]),
674
- ],
675
- colorAttachments: [
676
- {
677
- view: color.value,
678
- ...(resolve === undefined ? {} : { resolveTarget: resolve.value }),
679
- loadOp: 'clear',
680
- storeOp: 'store',
681
- clearValue: () => {
682
- return { r: 0, g: 0, b: 0, a: 1 };
683
- },
684
- },
685
- ],
686
- depthStencilAttachment: {
687
- view: depth.value,
688
- depthClearValue: 1,
689
- depthLoadOp: 'clear',
690
- depthStoreOp: 'store',
691
- stencilClearValue: 0,
692
- stencilLoadOp: 'clear',
693
- stencilStoreOp: 'store',
694
- },
695
- encode: ({ pass, frame, resources }) => {
696
- const internal = frame as _InternalRenderPipelineContext;
697
- const captureColor = resources.textureView(color.value);
698
- if (!captureColor.ok) return;
699
- const captureDepth = resources.textureView(depth.value);
700
- if (!captureDepth.ok) return;
701
- const captureResolve =
702
- resolve === undefined ? undefined : resources.textureView(resolve.value);
703
- if (resolve !== undefined && (captureResolve === undefined || !captureResolve.ok)) return;
704
- const captureResolveView = captureResolve?.ok ? captureResolve.value : null;
705
- const captureContext = {
706
- ...internal,
707
- camera: current().faceCamera,
708
- // Cube faces are raw scene captures, not display-camera HDR output.
709
- // Select the PSO against the actual target attachment so an LDR
710
- // cube target is rendered by a compatible forward pipeline.
711
- tonemapActive: false,
712
- skyboxActive: false,
713
- transparentColorFormat: initial.physical.descriptor.format as GPUTextureFormat,
714
- msaaActive: initial.physical.descriptor.sampleCount === 4,
715
- viewBindGroupDynamicOffset: current().viewBindGroupDynamicOffset,
716
- geometryColorView: captureColor.value,
717
- geometryDepthView: captureDepth.value,
718
- geometryColorResolveView: captureResolveView,
719
- splitLdrSprite: false,
720
- ldrSpritePassView: null,
721
- };
722
- encodeMainPass(captureContext, pass, undefined, {
723
- colorViews: [captureColor.value],
724
- colorFormats: [initial.physical.descriptor.format as GPUTextureFormat],
725
- depthView: captureDepth.value,
726
- passKind: 'forward',
727
- clearColor: [0, 0, 0, 1],
728
- });
729
- },
730
- });
731
- if (!added.ok) return added;
732
- }
733
- return ok(undefined);
734
- }
735
-
736
658
  export function addReflectionProbeGraphPasses(
737
659
  builder: RenderGraphBuilder<RenderPipelineFrame>,
738
660
  state: ReflectionProbeGraphState,
@@ -982,6 +904,7 @@ export interface RenderFeatureGraphRuntimeState {
982
904
  string,
983
905
  import('../fullscreen-post-process-pass').PostProcessShaderEntry
984
906
  >;
907
+ postProcessIdentities: readonly string[];
985
908
  }
986
909
 
987
910
  /** The exact fields that define one validated feature-plan collection. */
@@ -1042,6 +965,8 @@ export interface RenderFeatureGraphCandidate {
1042
965
  string,
1043
966
  import('../fullscreen-post-process-pass').PostProcessShaderEntry
1044
967
  >;
968
+ /** Public Standard post-effect identities; graph-local resources stay internal. */
969
+ readonly postProcessIdentities?: readonly string[];
1045
970
  /**
1046
971
  * Physical transient graphics resources are imported into the compiled
1047
972
  * graph. Their lease therefore needs a new graph even when the declarative
@@ -1065,6 +990,216 @@ export interface RenderFeatureGraphCandidate {
1065
990
 
1066
991
  const featureGraphStates = new WeakMap<RenderSystemInternals, RenderFeatureGraphRuntimeState>();
1067
992
  const graphDeviceGenerations = new WeakMap<object, number>();
993
+ const graphRetirementFailures = new WeakMap<
994
+ RenderFrameState,
995
+ Set<CompiledRenderGraph<RenderPipelineFrame>>
996
+ >();
997
+
998
+ type GraphGenerationRole = 'active' | 'candidate' | 'retiring';
999
+ type GraphGenerationStatus = 'active' | 'retiring' | 'failed' | 'retired';
1000
+
1001
+ interface GraphGenerationAllocationRecord {
1002
+ readonly graph: CompiledRenderGraph<RenderPipelineFrame>;
1003
+ status: GraphGenerationStatus;
1004
+ allocation: RenderGraphResourceAllocationInspection | undefined;
1005
+ }
1006
+
1007
+ /**
1008
+ * One stable allocation owner for a RenderSystem lifetime. Recovery builds a
1009
+ * detached frame state, but that state intentionally shares this owner before
1010
+ * it can publish a candidate into the active RenderSystem. Graph records stay
1011
+ * here across asynchronous retirement so a successful replacement cannot
1012
+ * erase the overlap peak before the next public inspection.
1013
+ */
1014
+ interface GraphGenerationAllocationOwner {
1015
+ readonly records: Map<CompiledRenderGraph<RenderPipelineFrame>, GraphGenerationAllocationRecord>;
1016
+ peakBytes: number;
1017
+ }
1018
+
1019
+ const graphGenerationAllocationOwners = new WeakMap<
1020
+ RenderFrameState,
1021
+ GraphGenerationAllocationOwner
1022
+ >();
1023
+
1024
+ function graphGenerationAllocationOwner(
1025
+ frameState: RenderFrameState,
1026
+ ): GraphGenerationAllocationOwner {
1027
+ const existing = graphGenerationAllocationOwners.get(frameState);
1028
+ if (existing !== undefined) return existing;
1029
+ const created: GraphGenerationAllocationOwner = { records: new Map(), peakBytes: 0 };
1030
+ graphGenerationAllocationOwners.set(frameState, created);
1031
+ return created;
1032
+ }
1033
+
1034
+ function graphRetirementFailureSet(
1035
+ frameState: RenderFrameState,
1036
+ ): Set<CompiledRenderGraph<RenderPipelineFrame>> {
1037
+ const existing = graphRetirementFailures.get(frameState);
1038
+ if (existing !== undefined) return existing;
1039
+ const created = new Set<CompiledRenderGraph<RenderPipelineFrame>>();
1040
+ graphRetirementFailures.set(frameState, created);
1041
+ return created;
1042
+ }
1043
+
1044
+ /** Share the renderer-owned graph ledger with a detached recovery frame state. */
1045
+ export function shareRenderGraphGenerationAllocationOwner(
1046
+ source: RenderFrameState,
1047
+ target: RenderFrameState,
1048
+ ): void {
1049
+ graphGenerationAllocationOwners.set(target, graphGenerationAllocationOwner(source));
1050
+ graphRetirementFailures.set(target, graphRetirementFailureSet(source));
1051
+ }
1052
+
1053
+ function observeGraphGenerationAllocation(frameState: RenderFrameState): void {
1054
+ const owner = graphGenerationAllocationOwner(frameState);
1055
+ let simultaneousBytes = 0;
1056
+ for (const record of owner.records.values()) {
1057
+ if (record.status === 'retired') continue;
1058
+ const allocation = record.graph.inspect().resourceAllocation;
1059
+ record.allocation = allocation;
1060
+ if (allocation !== undefined) {
1061
+ simultaneousBytes += allocation.liveBytes + allocation.pendingRetirementBytes;
1062
+ }
1063
+ }
1064
+ owner.peakBytes = Math.max(owner.peakBytes, simultaneousBytes);
1065
+ }
1066
+
1067
+ function trackGraphGeneration(
1068
+ frameState: RenderFrameState,
1069
+ graph: CompiledRenderGraph<RenderPipelineFrame>,
1070
+ ): void {
1071
+ const owner = graphGenerationAllocationOwner(frameState);
1072
+ const record = owner.records.get(graph);
1073
+ if (record === undefined) {
1074
+ owner.records.set(graph, { graph, status: 'active', allocation: undefined });
1075
+ } else if (record.status === 'retired') {
1076
+ record.status = 'active';
1077
+ }
1078
+ observeGraphGenerationAllocation(frameState);
1079
+ }
1080
+
1081
+ function setGraphGenerationRetiring(
1082
+ frameState: RenderFrameState,
1083
+ graph: CompiledRenderGraph<RenderPipelineFrame>,
1084
+ ): void {
1085
+ const owner = graphGenerationAllocationOwner(frameState);
1086
+ const record = owner.records.get(graph);
1087
+ if (record === undefined) {
1088
+ owner.records.set(graph, { graph, status: 'retiring', allocation: undefined });
1089
+ } else if (record.status !== 'failed') {
1090
+ record.status = 'retiring';
1091
+ }
1092
+ }
1093
+
1094
+ function setGraphGenerationRetirementResult(
1095
+ frameState: RenderFrameState,
1096
+ graph: CompiledRenderGraph<RenderPipelineFrame>,
1097
+ succeeded: boolean,
1098
+ ): void {
1099
+ const owner = graphGenerationAllocationOwner(frameState);
1100
+ const record = owner.records.get(graph);
1101
+ if (record !== undefined) record.status = succeeded ? 'retired' : 'failed';
1102
+ observeGraphGenerationAllocation(frameState);
1103
+ // A successful graph no longer owns any handles. Keep the renderer-level
1104
+ // peak scalar, but release the graph object from the generation map; failed
1105
+ // and pending owners remain retained for recovery inspection.
1106
+ if (succeeded) owner.records.delete(graph);
1107
+ }
1108
+
1109
+ /**
1110
+ * Project the actual RenderSystem graph-generation owners as one detached
1111
+ * allocation receipt. The candidate and fallback graph can overlap before a
1112
+ * submit settles; failed retirement stays visible until the frame owner is
1113
+ * discarded instead of disappearing with the asynchronous Set entry.
1114
+ */
1115
+ export function inspectRenderGraphGenerationAllocation(
1116
+ frameState: RenderFrameState,
1117
+ ): RenderGraphGenerationAllocationInspection {
1118
+ const rolesByGraph = new Map<
1119
+ CompiledRenderGraph<RenderPipelineFrame>,
1120
+ Set<GraphGenerationRole>
1121
+ >();
1122
+ const add = (
1123
+ graph: CompiledRenderGraph<RenderPipelineFrame> | null | undefined,
1124
+ role: GraphGenerationRole,
1125
+ ) => {
1126
+ if (graph === null || graph === undefined) return;
1127
+ const roles = rolesByGraph.get(graph);
1128
+ if (roles === undefined) rolesByGraph.set(graph, new Set([role]));
1129
+ else roles.add(role);
1130
+ };
1131
+ add(frameState.compiledFrameGraph, 'active');
1132
+ add(frameState.volumetricFogCandidateGraph, 'candidate');
1133
+ add(frameState.volumetricFogPreviousGraph, 'retiring');
1134
+ add(frameState.depthOfFieldCandidateGraph, 'candidate');
1135
+ add(frameState.depthOfFieldPreviousGraph, 'retiring');
1136
+ add(frameState.barrelDistortionCandidateGraph, 'candidate');
1137
+ add(frameState.barrelDistortionPreviousGraph, 'retiring');
1138
+ for (const graph of frameState.retiredCompiledFrameGraphs) add(graph, 'retiring');
1139
+ for (const graph of graphRetirementFailureSet(frameState)) add(graph, 'retiring');
1140
+
1141
+ const owner = graphGenerationAllocationOwner(frameState);
1142
+ const entries: RenderGraphGenerationAllocationEntry[] = [];
1143
+ let unavailableGenerationCount = 0;
1144
+ for (const [graph, record] of owner.records) {
1145
+ if (record.status === 'retired') continue;
1146
+ const roles = rolesByGraph.get(graph) ?? new Set<GraphGenerationRole>();
1147
+ if (record.status === 'retiring' || record.status === 'failed') roles.add('retiring');
1148
+ if (roles.size === 0) {
1149
+ // A candidate can be detached from the active state's role fields while
1150
+ // it is still being prepared. Keep its owner record observable until
1151
+ // the publication or cleanup event settles it.
1152
+ roles.add(record.status === 'active' ? 'active' : 'retiring');
1153
+ }
1154
+ // Inspection is a projection of the graph's current ledger. The cached
1155
+ // snapshot is only for event-time peak updates; using it here would make
1156
+ // a successfully fenced graph look live after its retirement started.
1157
+ const allocation = graph.inspect().resourceAllocation;
1158
+ record.allocation = allocation;
1159
+ if (allocation === undefined) {
1160
+ unavailableGenerationCount += 1;
1161
+ continue;
1162
+ }
1163
+ const retirement =
1164
+ record.status === 'failed' ? 'failed' : roles.has('retiring') ? 'pending' : 'active';
1165
+ entries.push({
1166
+ generation: graph.inspect().generation,
1167
+ roles: Object.freeze([...roles]),
1168
+ retirement,
1169
+ allocation,
1170
+ });
1171
+ }
1172
+ entries.sort((left, right) => left.generation - right.generation);
1173
+ const liveBytes = entries.reduce((sum, entry) => sum + entry.allocation.liveBytes, 0);
1174
+ const pendingRetirementBytes = entries.reduce(
1175
+ (sum, entry) => sum + entry.allocation.pendingRetirementBytes,
1176
+ 0,
1177
+ );
1178
+ const peakBytes = Math.max(owner.peakBytes, liveBytes + pendingRetirementBytes);
1179
+ const failedEntries = entries.filter((entry) => entry.retirement === 'failed');
1180
+ return Object.freeze({
1181
+ unit: 'engine-allocation-bytes',
1182
+ physicalResidency: 'unknown',
1183
+ availability:
1184
+ entries.length === 0
1185
+ ? unavailableGenerationCount === 0
1186
+ ? 'complete'
1187
+ : 'unavailable'
1188
+ : unavailableGenerationCount === 0
1189
+ ? 'complete'
1190
+ : 'partial',
1191
+ unavailableGenerationCount,
1192
+ entries: Object.freeze(entries.map((entry) => Object.freeze(entry))),
1193
+ liveBytes,
1194
+ pendingRetirementBytes,
1195
+ peakBytes,
1196
+ failedRetirementCount: failedEntries.length,
1197
+ failedRetirementBytes: failedEntries.reduce(
1198
+ (sum, entry) => sum + entry.allocation.liveBytes + entry.allocation.pendingRetirementBytes,
1199
+ 0,
1200
+ ),
1201
+ });
1202
+ }
1068
1203
 
1069
1204
  interface MutableRenderFeatureGraphInspection {
1070
1205
  planRevisionState: RenderFeaturePlanRevisionState;
@@ -1230,6 +1365,7 @@ export function getRenderFeatureGraphState(
1230
1365
  const created: RenderFeatureGraphRuntimeState = {
1231
1366
  plans: [],
1232
1367
  fullscreenEffects: new Map(),
1368
+ postProcessIdentities: [],
1233
1369
  };
1234
1370
  featureGraphStates.set(internals, created);
1235
1371
  return created;
@@ -1239,6 +1375,7 @@ export function resetRenderFeatureGraphState(internals: RenderSystemInternals):
1239
1375
  const state = getRenderFeatureGraphState(internals);
1240
1376
  state.plans = [];
1241
1377
  state.fullscreenEffects = new Map();
1378
+ state.postProcessIdentities = [];
1242
1379
  }
1243
1380
 
1244
1381
  export function reportRenderFeatureGraphError(
@@ -1357,6 +1494,7 @@ function commitFeatureCandidate(
1357
1494
  const state = getRenderFeatureGraphState(internals);
1358
1495
  state.plans = candidate.plans;
1359
1496
  state.fullscreenEffects = candidate.fullscreenEffects;
1497
+ state.postProcessIdentities = candidate.postProcessIdentities ?? [];
1360
1498
  }
1361
1499
 
1362
1500
  function topologyOf(
@@ -1378,6 +1516,7 @@ function topologyOf(
1378
1516
  standardLighting: StandardTopologyInputValue | undefined,
1379
1517
  ssr: SsrSpatialAdmission | undefined,
1380
1518
  renderExtent: RenderExtent | undefined,
1519
+ surfaceMediumActive: boolean | undefined,
1381
1520
  analyticFog: boolean,
1382
1521
  ): Result<RenderPipelineTopology, RhiError> {
1383
1522
  const cascadeCount = Math.max(1, Math.min(4, Math.round(lights.cascadeCount ?? 1))) as
@@ -1399,10 +1538,9 @@ function topologyOf(
1399
1538
  floatRenderAttachment: internals.device.caps.rgba16floatRenderable,
1400
1539
  });
1401
1540
  if (!surfaceProfile.ok) return surfaceProfile;
1402
- const featurePostEffects = [
1403
- ...(featureGraphCandidate?.fullscreenEffects ??
1404
- getRenderFeatureGraphState(internals).fullscreenEffects),
1405
- ].map(([id]) => id);
1541
+ const featurePostEffects =
1542
+ featureGraphCandidate?.postProcessIdentities ??
1543
+ getRenderFeatureGraphState(internals).postProcessIdentities;
1406
1544
  const featurePostEffectDeclarations = [
1407
1545
  ...(featureGraphCandidate?.fullscreenEffects ??
1408
1546
  getRenderFeatureGraphState(internals).fullscreenEffects),
@@ -1461,6 +1599,7 @@ function topologyOf(
1461
1599
  // positive intensity changes update the recorder UBO in place.
1462
1600
  bloom: standardBloomAdmitted(camera) ? 'on' : 'off',
1463
1601
  bloomIntensity: camera.bloomIntensity,
1602
+ barrelDistortion: !clearOnly && (camera.barrelDistortion?.strength ?? 0) > 0,
1464
1603
  ...(depthOfField === undefined ? {} : { depthOfField }),
1465
1604
  },
1466
1605
  atmosphere: !clearOnly && frameState.environmentFrame?.source.kind === 'atmosphere',
@@ -1514,6 +1653,16 @@ function topologyOf(
1514
1653
  return {
1515
1654
  enabled: true,
1516
1655
  format: volumetricFog.densityAsset.format,
1656
+ additional: (volumetricFog.additional ?? []).flatMap((member) =>
1657
+ member.densityAsset?.shape.viewDimension === '3d'
1658
+ ? [
1659
+ {
1660
+ format: member.densityAsset.format,
1661
+ extent: member.densityAsset.shape.extent,
1662
+ },
1663
+ ]
1664
+ : [],
1665
+ ),
1517
1666
  extent: {
1518
1667
  width: volumetricFog.densityAsset.shape.extent.width,
1519
1668
  height: volumetricFog.densityAsset.shape.extent.height,
@@ -1521,7 +1670,7 @@ function topologyOf(
1521
1670
  },
1522
1671
  froxelExtent: deriveVolumetricFogExtent(
1523
1672
  { width, height },
1524
- volumeProfile.depth,
1673
+ volumeProfile.depth * (1 + (volumetricFog.additional?.length ?? 0)),
1525
1674
  volumeProfile.tileSize,
1526
1675
  ),
1527
1676
  resolvedExtent: deriveVolumetricFogResolvedExtent(
@@ -1562,6 +1711,9 @@ function topologyOf(
1562
1711
  activeCount: 0,
1563
1712
  needsRoughMips: false,
1564
1713
  },
1714
+ ...((surfaceMediumActive ?? gpuDriven?.surfaceSubmission !== undefined)
1715
+ ? { singleLayerMedium: true }
1716
+ : {}),
1565
1717
  });
1566
1718
  }
1567
1719
 
@@ -1586,6 +1738,30 @@ function targetResolver(
1586
1738
  ...(named.resolveTarget === undefined ? {} : { resolveTarget: named.resolveTarget }),
1587
1739
  };
1588
1740
  }
1741
+ if (typeof resource === 'string') {
1742
+ const target = targets.find((candidate) => candidate.name === resource);
1743
+ if (target !== undefined) {
1744
+ return {
1745
+ texture: target.texture,
1746
+ view: target.view,
1747
+ ...(target.resolveTarget === undefined ? {} : { resolveTarget: target.resolveTarget }),
1748
+ };
1749
+ }
1750
+ // Standard lanes expose the depth attachment as a semantic target
1751
+ // without duplicating the public `depth` name on every concrete graph
1752
+ // projection. Keep the string alias stable for feature-authored
1753
+ // fullscreen bindings while preserving the typed target identity.
1754
+ if (resource === 'depth') {
1755
+ const depth = targets.find((candidate) => candidate.kind === 'scene-depth');
1756
+ if (depth !== undefined) {
1757
+ return {
1758
+ texture: depth.texture,
1759
+ view: depth.view,
1760
+ ...(depth.resolveTarget === undefined ? {} : { resolveTarget: depth.resolveTarget }),
1761
+ };
1762
+ }
1763
+ }
1764
+ }
1589
1765
  }
1590
1766
  if (isSceneDataTarget(resource)) {
1591
1767
  const semantic = semanticTargets.find(
@@ -1597,11 +1773,27 @@ function targetResolver(
1597
1773
  : { texture: semantic.texture, view: semantic.view };
1598
1774
  }
1599
1775
  if (!isRenderFeatureTargetHandle(resource)) return undefined;
1776
+ // A typed feature handle is an identity, not a format request. Falling
1777
+ // back to another same-format target can silently bind scene color in
1778
+ // place of a missing cloud history/shadow resource and turn a producer
1779
+ // failure into plausible but incorrect pixels.
1780
+ // Standard lanes expose scene depth as a semantic target without a
1781
+ // duplicated public name, while feature plans use the stable `depth`
1782
+ // handle name. Preserve that one semantic alias here as well as above for
1783
+ // string-authored fullscreen bindings.
1600
1784
  const target = targets.find(
1601
1785
  (candidate) =>
1602
1786
  candidate.kind === resource.kind &&
1603
1787
  candidate.format === resource.format &&
1604
- candidate.sampleCount === resource.sampleCount,
1788
+ candidate.sampleCount === resource.sampleCount &&
1789
+ (resource.name === undefined ||
1790
+ candidate.name === resource.name ||
1791
+ (resource.kind === 'scene-color' &&
1792
+ resource.name === 'color' &&
1793
+ candidate.kind === 'scene-color') ||
1794
+ (resource.kind === 'scene-depth' &&
1795
+ resource.name === 'depth' &&
1796
+ candidate.kind === 'scene-depth')),
1605
1797
  );
1606
1798
  return target === undefined
1607
1799
  ? undefined
@@ -1621,6 +1813,9 @@ function resolveTargetBindings(
1621
1813
  };
1622
1814
  readonly resources: GraphResourceResolver;
1623
1815
  readonly resolveTarget: RenderFeatureGraphTargetResolver;
1816
+ readonly resolveGpuResource?: (
1817
+ name: string,
1818
+ ) => import('../features/prepared-gpu-work').RenderFeatureResolvedGpuBuffer | undefined;
1624
1819
  },
1625
1820
  featurePostProcessEntries?: ReadonlyMap<string, PostProcessShaderEntry>,
1626
1821
  ): RenderFeatureGraphBindingsResolution | undefined {
@@ -1632,6 +1827,136 @@ function resolveTargetBindings(
1632
1827
  const sceneDepth = descriptor?.values.sceneDepth;
1633
1828
  const sceneDepthBinding = descriptor?.values.sceneDepthBinding;
1634
1829
  if (pipeline === undefined) return undefined;
1830
+ if (descriptor?.values.view === true) {
1831
+ const viewBindGroup = buildPerFrameBindGroups(
1832
+ frame.runtime as RenderSystemInternals,
1833
+ frame.frameState,
1834
+ frame.pipelineState,
1835
+ true,
1836
+ frame.bindGroupCounts,
1837
+ {
1838
+ directionalShadow: frame.frameState.currentDirectionalShadowView ?? undefined,
1839
+ spotShadow: frame.frameState.currentSpotShadowView ?? undefined,
1840
+ },
1841
+ true,
1842
+ frame.standardLighting,
1843
+ ).viewBindGroup;
1844
+ return viewBindGroup === null ? undefined : { handle: viewBindGroup, dynamicOffsets: [0, 0] };
1845
+ }
1846
+ // Generic fullscreen producers (for example the CloudLayer scene pass)
1847
+ // share the renderer-owned post-process bind-group contract without opting
1848
+ // into the temporal two-input path below. Their input/depth aliases are
1849
+ // resolved from the active typed graph, so they cannot accidentally sample
1850
+ // a stale surface view or manufacture a second resource owner.
1851
+ if (descriptor?.values.fullscreen === true && !isTemporalFullscreenBinding(descriptor.values)) {
1852
+ const shader = descriptor.values.shader;
1853
+ if (typeof shader !== 'string') return undefined;
1854
+ const entry =
1855
+ featurePostProcessEntries?.get(shader) ?? frame.runtime.lookupPostProcess?.(shader);
1856
+ if (entry === undefined) return undefined;
1857
+ const depthFallback = descriptor.values.depthFallback === true;
1858
+ const depthValue = descriptor.values.depth;
1859
+ const depthTarget =
1860
+ depthFallback || depthValue === undefined
1861
+ ? undefined
1862
+ : input.resolveTarget(depthValue as never);
1863
+ const fullscreen = buildFullscreenPostProcessPass(
1864
+ { device: frame.runtime.device, errorRegistry: frame.runtime.errorRegistry },
1865
+ entry,
1866
+ depthTarget?.resolveTarget !== undefined,
1867
+ );
1868
+ if (fullscreen === null || fullscreen.sampler === null) return undefined;
1869
+ const inputDisabled = descriptor.values.input === false;
1870
+ const inputTarget = inputDisabled
1871
+ ? undefined
1872
+ : input.resolveTarget(descriptor.values.input as never);
1873
+ const inputView =
1874
+ inputTarget === undefined
1875
+ ? { ok: true as const, value: frame.pipelineState.defaultWhiteTextureView }
1876
+ : input.resources.textureView(inputTarget.view);
1877
+ if (!inputView.ok) return undefined;
1878
+ const depthView = depthFallback
1879
+ ? frame.pipelineState.shadowFallbackTextureView
1880
+ : depthTarget === undefined
1881
+ ? undefined
1882
+ : (() => {
1883
+ const depthTexture = input.resources.texture(depthTarget.texture);
1884
+ if (!depthTexture.ok) return undefined;
1885
+ const created = frame.runtime.device.createTextureView(depthTexture.value as Texture, {
1886
+ aspect: 'depth-only',
1887
+ dimension: '2d',
1888
+ });
1889
+ return created.ok ? created.value : undefined;
1890
+ })();
1891
+ if (
1892
+ depthTarget !== undefined &&
1893
+ (depthView === undefined || fullscreen.depthSampler === null)
1894
+ ) {
1895
+ return undefined;
1896
+ }
1897
+ const additionalViews: { readonly binding: number; readonly view: TextureView }[] = [];
1898
+ const additionalNames = descriptor.values.additionalTextures;
1899
+ if (Array.isArray(additionalNames)) {
1900
+ for (const [index, value] of additionalNames.entries()) {
1901
+ if (typeof value !== 'string') return undefined;
1902
+ const target = input.resolveTarget(value);
1903
+ if (target === undefined) return undefined;
1904
+ const view = input.resources.textureView(target.view);
1905
+ if (!view.ok) return undefined;
1906
+ const binding = fullscreen.extraColorBindings[index];
1907
+ if (binding === undefined) return undefined;
1908
+ additionalViews.push({ binding, view: view.value });
1909
+ }
1910
+ }
1911
+ const layout = pipeline.getBindGroupLayout?.(1);
1912
+ if (layout === undefined) return undefined;
1913
+ const paramsBuffer = frame.runtime.getPostProcessParamsBuffer?.(shader);
1914
+ if (entry.params !== undefined && paramsBuffer === undefined) return undefined;
1915
+ if (entry.params !== undefined && paramsBuffer !== undefined) {
1916
+ const data = frame.postProcessParams.get(shader) ?? entry.params.defaultValue;
1917
+ if (data.byteLength !== entry.params.byteSize) return undefined;
1918
+ const written = frame.runtime.device.queue.writeBuffer(paramsBuffer, 0, data);
1919
+ if (!written.ok) return undefined;
1920
+ }
1921
+ const createdBindGroup = createFullscreenBindGroup(
1922
+ frame.runtime.device,
1923
+ layout,
1924
+ inputView.value,
1925
+ fullscreen.sampler,
1926
+ paramsBuffer,
1927
+ depthView ?? null,
1928
+ fullscreen.depthSampler,
1929
+ additionalViews,
1930
+ fullscreen.extraStorageBindings.map((binding, index) => {
1931
+ const names = descriptor.values.storageBuffers;
1932
+ const name = Array.isArray(names) ? names[index] : undefined;
1933
+ if (typeof name !== 'string') throw new Error('fullscreen storage buffer name missing');
1934
+ const buffer = input.resolveGpuResource?.(name);
1935
+ if (buffer === undefined) throw new Error(`fullscreen storage buffer unavailable: ${name}`);
1936
+ return { binding, buffer: buffer.buffer };
1937
+ }),
1938
+ );
1939
+ if (createdBindGroup !== null) {
1940
+ // Cloud history is a producer of the shared temporal ping-pong state even
1941
+ // when TAA is disabled. Stage the write at encode time; execute's finish
1942
+ // guard and submit transaction then give it the same abort/commit/fence
1943
+ // semantics as the TAA resolve.
1944
+ if (shader === 'cloud-layer-resolve') {
1945
+ const state = getTemporalGpuState(
1946
+ frame.frameState,
1947
+ frame.runtime.device,
1948
+ frame.runtime.deviceScope,
1949
+ frame.targetW,
1950
+ frame.targetH,
1951
+ true,
1952
+ );
1953
+ stageTemporalGpuSubmit(state);
1954
+ frame.frameState.temporalGpuState = state;
1955
+ }
1956
+ return createdBindGroup;
1957
+ }
1958
+ return undefined;
1959
+ }
1635
1960
  if (descriptor?.values.fullscreen === true && isTemporalFullscreenBinding(descriptor.values)) {
1636
1961
  const shader = descriptor.values.shader;
1637
1962
  if (typeof shader !== 'string') return undefined;
@@ -1646,11 +1971,9 @@ function resolveTargetBindings(
1646
1971
  );
1647
1972
  if (fullscreen === null || fullscreen.sampler === null) return undefined;
1648
1973
  const inputTarget = input.resolveTarget(descriptor.values.input as never);
1649
- const temporalTarget = input.resolveTarget(descriptor.values.temporal as never);
1650
- if (inputTarget === undefined || temporalTarget === undefined) return undefined;
1974
+ if (inputTarget === undefined) return undefined;
1651
1975
  const inputView = input.resources.textureView(inputTarget.view);
1652
- const temporalView = input.resources.textureView(temporalTarget.view);
1653
- if (!inputView.ok || !temporalView.ok) return undefined;
1976
+ if (!inputView.ok) return undefined;
1654
1977
  const layout = pipeline.getBindGroupLayout?.(1);
1655
1978
  if (layout === undefined) return undefined;
1656
1979
  const depth = input.resolveTarget(descriptor.values.depth as never);
@@ -1669,13 +1992,41 @@ function resolveTargetBindings(
1669
1992
  if (depth !== undefined && (depthView === undefined || fullscreen.depthSampler === null)) {
1670
1993
  return undefined;
1671
1994
  }
1995
+
1996
+ const paramsBuffer = frame.runtime.getPostProcessParamsBuffer?.(shader);
1997
+ if (entry.params !== undefined && paramsBuffer === undefined) return undefined;
1998
+ if (entry.params !== undefined && paramsBuffer !== undefined) {
1999
+ const data = frame.postProcessParams.get(shader) ?? entry.params.defaultValue;
2000
+ if (data.byteLength !== entry.params.byteSize) return undefined;
2001
+ const written = frame.runtime.device.queue.writeBuffer(paramsBuffer, 0, data);
2002
+ if (!written.ok) return undefined;
2003
+ }
2004
+
2005
+ if (!isTemporalFullscreenBinding(descriptor.values)) {
2006
+ return (
2007
+ createFullscreenBindGroup(
2008
+ frame.runtime.device,
2009
+ layout,
2010
+ inputView.value,
2011
+ fullscreen.sampler,
2012
+ paramsBuffer,
2013
+ depthView ?? null,
2014
+ fullscreen.depthSampler,
2015
+ ) ?? undefined
2016
+ );
2017
+ }
2018
+
2019
+ const temporalTarget = input.resolveTarget(descriptor.values.temporal as never);
2020
+ if (temporalTarget === undefined) return undefined;
2021
+ const temporalView = input.resources.textureView(temporalTarget.view);
2022
+ if (!temporalView.ok) return undefined;
1672
2023
  return (
1673
2024
  createFullscreenBindGroup(
1674
2025
  frame.runtime.device,
1675
2026
  layout,
1676
2027
  inputView.value,
1677
2028
  fullscreen.sampler,
1678
- frame.runtime.getPostProcessParamsBuffer?.(shader),
2029
+ paramsBuffer,
1679
2030
  depthView ?? null,
1680
2031
  fullscreen.depthSampler,
1681
2032
  [{ binding: fullscreen.extraColorBindings[0] ?? 5, view: temporalView.value }],
@@ -1753,15 +2104,35 @@ function resolveTargetBindings(
1753
2104
  return created.value;
1754
2105
  }
1755
2106
 
1756
- function retire(
2107
+ export function retire(
1757
2108
  frameState: RenderFrameState,
1758
2109
  graph: CompiledRenderGraph<RenderPipelineFrame>,
1759
2110
  ): void {
2111
+ if (frameState.retiredCompiledFrameGraphs.has(graph)) return;
1760
2112
  frameState.retiredCompiledFrameGraphs.add(graph);
1761
- graph
1762
- .retire()
1763
- .finally(() => frameState.retiredCompiledFrameGraphs.delete(graph))
1764
- .catch(() => undefined);
2113
+ setGraphGenerationRetiring(frameState, graph);
2114
+ const failures = graphRetirementFailureSet(frameState);
2115
+ const retirement = graph.retire();
2116
+ // retire() synchronously moves graph-owned resources to pending before its
2117
+ // queue-fence await. Capture that overlap at the lifecycle event, rather
2118
+ // than waiting for a later inspection call.
2119
+ observeGraphGenerationAllocation(frameState);
2120
+ retirement
2121
+ .then((result) => {
2122
+ if (result.ok) {
2123
+ failures?.delete(graph);
2124
+ setGraphGenerationRetirementResult(frameState, graph, true);
2125
+ } else {
2126
+ failures?.add(graph);
2127
+ setGraphGenerationRetirementResult(frameState, graph, false);
2128
+ }
2129
+ frameState.retiredCompiledFrameGraphs.delete(graph);
2130
+ })
2131
+ .catch(() => {
2132
+ failures?.add(graph);
2133
+ setGraphGenerationRetirementResult(frameState, graph, false);
2134
+ frameState.retiredCompiledFrameGraphs.delete(graph);
2135
+ });
1765
2136
  }
1766
2137
 
1767
2138
  /**
@@ -1838,6 +2209,54 @@ export function settleDepthOfFieldGraphCandidate(
1838
2209
  frameState.depthOfFieldCandidateGraph = null;
1839
2210
  }
1840
2211
 
2212
+ /**
2213
+ * Settle an ordinary graph replacement at the same submit barrier as the
2214
+ * barrel mapping. Resize, camera topology, and post-process enable/disable
2215
+ * all replace the compiled graph; a failed command must restore that graph so
2216
+ * the retained mapping still describes the pixels that are actually shown.
2217
+ */
2218
+ export function settleBarrelDistortionGraphCandidate(
2219
+ frameState: RenderFrameState,
2220
+ submitted: boolean,
2221
+ ): void {
2222
+ const candidate = frameState.barrelDistortionCandidateGraph ?? null;
2223
+ if (candidate === null) return;
2224
+ const previous = frameState.barrelDistortionPreviousGraph ?? null;
2225
+ const sharedWithSpecializedOwner =
2226
+ frameState.depthOfFieldCandidateGraph === candidate ||
2227
+ frameState.volumetricFogCandidateGraph === candidate;
2228
+ if (submitted) {
2229
+ if (!sharedWithSpecializedOwner && previous !== null && previous !== candidate) {
2230
+ retire(frameState, previous);
2231
+ }
2232
+ } else {
2233
+ if (!sharedWithSpecializedOwner) {
2234
+ if (candidate !== previous) retire(frameState, candidate);
2235
+ frameState.compiledFrameGraph = previous;
2236
+ frameState.compiledFrameGraphTopologyKey =
2237
+ frameState.barrelDistortionPreviousGraphKey ?? null;
2238
+ if (frameState.barrelDistortionPreviousPerFrameGraph === undefined) {
2239
+ delete frameState.perFrameGraph;
2240
+ } else {
2241
+ frameState.perFrameGraph = frameState.barrelDistortionPreviousPerFrameGraph;
2242
+ }
2243
+ frameState.graphGeneration = frameState.barrelDistortionPreviousGraphGeneration ?? 0;
2244
+ frameState.compiledFrameGraphGeneration =
2245
+ frameState.barrelDistortionPreviousCompiledGraphGeneration ?? 0;
2246
+ frameState.standardLightingGraphSignature =
2247
+ frameState.barrelDistortionPreviousStandardLightingGraphSignature ?? '';
2248
+ }
2249
+ frameState.barrelDistortionGraphResolution = previous === null ? 'accepted' : 'retained';
2250
+ }
2251
+ frameState.barrelDistortionPreviousGraph = null;
2252
+ frameState.barrelDistortionPreviousGraphKey = null;
2253
+ frameState.barrelDistortionPreviousPerFrameGraph = undefined;
2254
+ frameState.barrelDistortionPreviousGraphGeneration = 0;
2255
+ frameState.barrelDistortionPreviousCompiledGraphGeneration = 0;
2256
+ frameState.barrelDistortionPreviousStandardLightingGraphSignature = '';
2257
+ frameState.barrelDistortionCandidateGraph = null;
2258
+ }
2259
+
1841
2260
  export function ensureCompiledFrameGraph(
1842
2261
  internals: RenderSystemInternals,
1843
2262
  frameState: RenderFrameState,
@@ -1859,8 +2278,11 @@ export function ensureCompiledFrameGraph(
1859
2278
  onRecoveryError?: (error: unknown) => void,
1860
2279
  ssr?: SsrSpatialAdmission,
1861
2280
  renderExtent?: RenderExtent,
2281
+ surfaceMediumActive?: boolean,
1862
2282
  analyticFog = false,
2283
+ cloudShadowResolution?: number,
1863
2284
  ): CompiledRenderGraph<RenderPipelineFrame> | null {
2285
+ frameState.barrelDistortionGraphResolution = 'retained';
1864
2286
  const featureMetrics = mutableFeatureGraphInspection(internals);
1865
2287
  featureMetrics.ensureCalls += 1;
1866
2288
  const requestedStandardLightingSignature = standardLightingTopologySignature(standardLighting);
@@ -1878,10 +2300,16 @@ export function ensureCompiledFrameGraph(
1878
2300
  : { ...camera, depthOfField: frameDepthOfField };
1879
2301
  const lastKnownGood = (): CompiledRenderGraph<RenderPipelineFrame> | null =>
1880
2302
  frameState.compiledFrameGraph !== null &&
2303
+ frameState.compiledFrameGraphTopologyKey !== null &&
1881
2304
  !hasSubmissionSensitiveFeatures(getRenderFeatureGraphState(internals).plans) &&
1882
2305
  frameState.standardLightingGraphSignature === requestedStandardLightingSignature
1883
2306
  ? frameState.compiledFrameGraph
1884
2307
  : null;
2308
+ const retainLastKnownGood = (): CompiledRenderGraph<RenderPipelineFrame> | null => {
2309
+ const graph = lastKnownGood();
2310
+ if (graph !== null) frameState.barrelDistortionGraphResolution = 'retained';
2311
+ return graph;
2312
+ };
1885
2313
  const surfaceWidth = Math.max(1, width);
1886
2314
  const surfaceHeight = Math.max(1, height);
1887
2315
  const featurePlans = featureGraphCandidate?.plans ?? getRenderFeatureGraphState(internals).plans;
@@ -1893,8 +2321,15 @@ export function ensureCompiledFrameGraph(
1893
2321
  if (featureGraphCandidate !== undefined) {
1894
2322
  recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
1895
2323
  }
1896
- return volumeTopologyCandidate ? null : lastKnownGood();
2324
+ return volumeTopologyCandidate ? null : retainLastKnownGood();
1897
2325
  }
2326
+ const cloudHistoryDemand = projectedFeaturePlans.value.some(
2327
+ (execution) =>
2328
+ execution.featureIdentity === CLOUD_LAYER_FEATURE_IDENTITY &&
2329
+ execution.passes.some(
2330
+ (pass) => pass.name === 'cloud-layer-transport' || pass.name === 'cloud-layer-resolve',
2331
+ ),
2332
+ );
1898
2333
  const nextPlanRevision = updateRenderFeaturePlanRevision(
1899
2334
  featureMetrics.planRevisionState,
1900
2335
  featurePlans,
@@ -1925,6 +2360,7 @@ export function ensureCompiledFrameGraph(
1925
2360
  standardLighting,
1926
2361
  ssr,
1927
2362
  renderExtent,
2363
+ surfaceMediumActive,
1928
2364
  analyticFog,
1929
2365
  );
1930
2366
  if (!topologyResult.ok) {
@@ -1934,7 +2370,7 @@ export function ensureCompiledFrameGraph(
1934
2370
  if (featureGraphCandidate !== undefined) {
1935
2371
  recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
1936
2372
  }
1937
- return volumeTopologyCandidate ? null : lastKnownGood();
2373
+ return volumeTopologyCandidate ? null : retainLastKnownGood();
1938
2374
  }
1939
2375
  const requestedPostEffects = topologyResult.value.config?.postEffects ?? [];
1940
2376
  const featurePostProcessEntries =
@@ -1986,6 +2422,7 @@ export function ensureCompiledFrameGraph(
1986
2422
  if (featureGraphCandidate !== undefined) {
1987
2423
  recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
1988
2424
  }
2425
+ frameState.barrelDistortionGraphResolution = 'retained';
1989
2426
  return acceptedLkg;
1990
2427
  }
1991
2428
  const topology = postProcessAdmission.pending
@@ -2022,12 +2459,17 @@ export function ensureCompiledFrameGraph(
2022
2459
  // so include the prepared projection's presence to invalidate a cached
2023
2460
  // CPU-only graph when the GPU owner becomes ready, and vice versa.
2024
2461
  gpuDrivenProjectionActive: gpuDriven !== undefined,
2462
+ // Cloud shadow targets are quality-derived graph resources. Include the
2463
+ // resolved texel grid in the cache key so a quality/resize change cannot
2464
+ // reuse a graph compiled for the previous allocation.
2465
+ cloudShadowResolution: cloudShadowResolution ?? 0,
2466
+ cloudHistory: cloudHistoryDemand,
2025
2467
  // Query reservations are frame-local transport state. The projection is
2026
2468
  // renderer-owned and read dynamically by the compiled pass, so a page
2027
2469
  // rotation must not invalidate the graph or its cached bind groups.
2028
2470
  occlusionQuery:
2029
2471
  occlusion === undefined ? undefined : { active: true, sampleCount: occlusion.sampleCount },
2030
- cubeCaptureSlots: cubeCaptureState?.work.length ?? 0,
2472
+ cubeCaptureSlots: cubeCaptureState?.work.map((work) => work.physical.descriptor),
2031
2473
  reflectionProbeSlots: cubeCaptureState?.reflectionProbes?.work.map((work) => [
2032
2474
  work.probeIndex,
2033
2475
  work.rawCaptureFace ?? -1,
@@ -2036,9 +2478,11 @@ export function ensureCompiledFrameGraph(
2036
2478
  ]),
2037
2479
  });
2038
2480
  if (frameState.compiledFrameGraph !== null && frameState.compiledFrameGraphTopologyKey === key) {
2481
+ frameState.barrelDistortionGraphResolution = 'accepted';
2039
2482
  featureMetrics.reuseHits += 1;
2040
2483
  featureMetrics.lastTopologyKey = key;
2041
2484
  frameState.standardLightingGraphSignature = requestedStandardLightingSignature;
2485
+ frameState.pendingCloudHistoryActive = cloudHistoryDemand;
2042
2486
  if (featureGraphCandidate !== undefined && !postProcessAdmission.pending) {
2043
2487
  commitFeatureCandidate(internals, featureGraphCandidate);
2044
2488
  } else if (featureGraphCandidate !== undefined) {
@@ -2068,9 +2512,11 @@ export function ensureCompiledFrameGraph(
2068
2512
  topology.output?.autoExposure === true ? importAutoExposureGraphResources(builder) : undefined;
2069
2513
  if (autoExposureResources !== undefined && !autoExposureResources.ok) {
2070
2514
  internals.errorRegistry.fire(autoExposureResources.error);
2071
- featureGraphCandidate?.onRejected?.();
2072
2515
  discardPendingStandardOutput();
2073
- return lastKnownGood();
2516
+ if (featureGraphCandidate !== undefined) {
2517
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
2518
+ }
2519
+ return retainLastKnownGood();
2074
2520
  }
2075
2521
  const lutResources =
2076
2522
  topology.output?.colorLut === true
@@ -2114,20 +2560,25 @@ export function ensureCompiledFrameGraph(
2114
2560
  : undefined;
2115
2561
  if (lutResources !== undefined && !lutResources.ok) {
2116
2562
  internals.errorRegistry.fire(lutResources.error);
2117
- featureGraphCandidate?.onRejected?.();
2118
2563
  discardPendingStandardOutput();
2119
- return lastKnownGood();
2564
+ if (featureGraphCandidate !== undefined) {
2565
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
2566
+ }
2567
+ return retainLastKnownGood();
2120
2568
  }
2121
2569
  const taaHistory = camera.antialias === 'taa' ? importTemporalHistoryTargets(builder) : undefined;
2570
+ const cloudHistory = cloudHistoryDemand ? importCloudHistoryTargets(builder) : undefined;
2122
2571
  const targetCoverage =
2123
2572
  camera.dynamicResolution === undefined || topology.extent === undefined
2124
2573
  ? undefined
2125
2574
  : createTargetCoverageAttachment(builder, topology.extent);
2126
2575
  if (targetCoverage !== undefined && !targetCoverage.ok) {
2127
2576
  internals.errorRegistry.fire(targetCoverage.error);
2128
- featureGraphCandidate?.onRejected?.();
2129
2577
  discardPendingStandardOutput();
2130
- return lastKnownGood();
2578
+ if (featureGraphCandidate !== undefined) {
2579
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
2580
+ }
2581
+ return retainLastKnownGood();
2131
2582
  }
2132
2583
  const ssrHistory = ssr?.status === 'admitted' ? importSsrHistoryTargets(builder) : undefined;
2133
2584
  const featureProjection = createRenderFeatureProjectionState();
@@ -2161,8 +2612,10 @@ export function ensureCompiledFrameGraph(
2161
2612
  : {}),
2162
2613
  ...(standardLighting === undefined ? {} : { standardLighting }),
2163
2614
  capabilities: { rgba16floatRenderable: internals.device.caps.rgba16floatRenderable },
2615
+ ...(cloudShadowResolution === undefined ? {} : { cloudShadowResolution }),
2164
2616
  ...(occlusion === undefined ? {} : { occlusion }),
2165
2617
  ...(taaHistory === undefined ? {} : { taaHistory }),
2618
+ ...(cloudHistory === undefined ? {} : { cloudHistory }),
2166
2619
  ...(targetCoverage?.ok === true ? { targetCoverage: targetCoverage.value } : {}),
2167
2620
  ...(ssrHistory === undefined ? {} : { ssrHistory }),
2168
2621
  encodeTransmissionMip: ({ pass, resources, source }) => {
@@ -2178,6 +2631,7 @@ export function ensureCompiledFrameGraph(
2178
2631
  builder,
2179
2632
  target.format,
2180
2633
  target.sampleCount,
2634
+ () => frameState.surfaceSubmissionObservation,
2181
2635
  target.additionalColorFormats,
2182
2636
  );
2183
2637
  },
@@ -2198,23 +2652,42 @@ export function ensureCompiledFrameGraph(
2198
2652
  semanticTargets = [],
2199
2653
  namedTargets = {},
2200
2654
  standardSurfaceAccesses = [],
2655
+ featureSelectionOrPlacement,
2656
+ passNames = [],
2201
2657
  ) => {
2202
- // Forward/deferred scene lanes contribute feature plans before the
2658
+ const selection =
2659
+ featureSelectionOrPlacement !== undefined &&
2660
+ typeof featureSelectionOrPlacement !== 'string'
2661
+ ? featureSelectionOrPlacement
2662
+ : undefined;
2663
+ const placement =
2664
+ typeof featureSelectionOrPlacement === 'string' ? featureSelectionOrPlacement : 'post';
2665
+ const include = selection?.include === undefined ? undefined : new Set(selection.include);
2666
+ const exclude = selection?.exclude === undefined ? undefined : new Set(selection.exclude);
2667
+ // Forward/deferred scene lanes contribute scene plans before the
2203
2668
  // Standard post chain has created its named input/output targets.
2204
2669
  // Fullscreen effects (Motion Blur included) must wait for that later
2205
- // call so their temporal input is resolved after TAA. Other feature
2206
- // plans can still be projected at the forward contribution point.
2207
- const plans =
2670
+ // call so their temporal input is resolved after TAA. The shared
2671
+ // projection state below deduplicates only the execution/pass that
2672
+ // actually reached the graph; an empty early projection therefore
2673
+ // cannot reserve the later fullscreen pass.
2674
+ const plans = (
2208
2675
  Object.keys(namedTargets).length === 0
2209
2676
  ? projectedFeaturePlans.value.filter(
2210
2677
  (execution) => !fullscreenFeatureIds.has(execution.featureIdentity),
2211
2678
  )
2212
- : projectedFeaturePlans.value;
2679
+ : projectedFeaturePlans.value
2680
+ )
2681
+ .filter((execution) => (execution.placement ?? 'post') === placement)
2682
+ .filter((execution) => include === undefined || include.has(execution.featureIdentity))
2683
+ .filter((execution) => exclude === undefined || !exclude.has(execution.featureIdentity));
2213
2684
  if (plans.length === 0) return ok(undefined);
2214
2685
  const resolveTarget = targetResolver(targets, semanticTargets, namedTargets);
2215
2686
  const projected = projectPlanExecutions(builder, plans, {
2216
2687
  state: featureProjection,
2217
2688
  resolveTarget,
2689
+ placement,
2690
+ ...(passNames.length === 0 ? {} : { passNames: new Set(passNames) }),
2218
2691
  standardSurfaceAccesses,
2219
2692
  resolveBindings: (input) => resolveTargetBindings(input, featurePostProcessEntries),
2220
2693
  resolveStandardLighting: (frame, pipeline) => {
@@ -2239,7 +2712,8 @@ export function ensureCompiledFrameGraph(
2239
2712
  },
2240
2713
  reportError: (error) => reportRenderFeatureGraphError(internals, error),
2241
2714
  });
2242
- return projected.ok ? ok(undefined) : projected;
2715
+ if (!projected.ok) return projected;
2716
+ return ok(undefined);
2243
2717
  },
2244
2718
  contributeCubeCaptures: (environmentCube) => {
2245
2719
  const reflectionProbes = addReflectionProbeGraphPasses(
@@ -2250,10 +2724,24 @@ export function ensureCompiledFrameGraph(
2250
2724
  if (!reflectionProbes.ok) return reflectionProbes;
2251
2725
  return cubeCaptureState === undefined
2252
2726
  ? ok(undefined)
2253
- : addCubeCaptureGraphPasses(builder, cubeCaptureState);
2727
+ : addTargetCaptureGraphPasses(builder, cubeCaptureState);
2254
2728
  },
2255
2729
  hasFeature: (identity) =>
2256
2730
  projectedFeaturePlans.value.some((execution) => execution.featureIdentity === identity),
2731
+ hasFeatureWork: (identity, placement) =>
2732
+ projectedFeaturePlans.value.some(
2733
+ (execution) =>
2734
+ execution.featureIdentity === identity &&
2735
+ execution.passes.length > 0 &&
2736
+ (placement === undefined || (execution.placement ?? 'post') === placement),
2737
+ ),
2738
+ hasFeatureRasterWork: (identity, placement) =>
2739
+ projectedFeaturePlans.value.some(
2740
+ (execution) =>
2741
+ execution.featureIdentity === identity &&
2742
+ execution.passes.some((pass) => pass.graphics !== undefined) &&
2743
+ (placement === undefined || (execution.placement ?? 'post') === placement),
2744
+ ),
2257
2745
  },
2258
2746
  topology,
2259
2747
  );
@@ -2261,12 +2749,11 @@ export function ensureCompiledFrameGraph(
2261
2749
  featureMetrics.buildFailures += 1;
2262
2750
  onRecoveryError?.(built.error);
2263
2751
  internals.errorRegistry.fire(built.error);
2264
- featureGraphCandidate?.onRejected?.();
2265
2752
  discardPendingStandardOutput();
2266
2753
  if (featureGraphCandidate !== undefined) {
2267
2754
  recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
2268
2755
  }
2269
- return volumeTopologyCandidate ? null : lastKnownGood();
2756
+ return volumeTopologyCandidate ? null : retainLastKnownGood();
2270
2757
  }
2271
2758
  featureMetrics.compileAttempts += 1;
2272
2759
  const compileStarted = typeof performance === 'undefined' ? Date.now() : performance.now();
@@ -2280,13 +2767,16 @@ export function ensureCompiledFrameGraph(
2280
2767
  featureMetrics.compileFailures += 1;
2281
2768
  onRecoveryError?.(compiled.error);
2282
2769
  internals.errorRegistry.fire(compiled.error);
2283
- featureGraphCandidate?.onRejected?.();
2284
2770
  discardPendingStandardOutput();
2285
2771
  if (featureGraphCandidate !== undefined) {
2286
2772
  recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
2287
2773
  }
2288
- return lastKnownGood();
2774
+ return retainLastKnownGood();
2289
2775
  }
2776
+ // Register the generation before publishing it. The owner observes the
2777
+ // overlap while the previous graph is still live, even if a queue fence
2778
+ // retires that graph before the next public inspect() call.
2779
+ trackGraphGeneration(frameState, compiled.value);
2290
2780
  const previous = frameState.compiledFrameGraph;
2291
2781
  const depthOfFieldReplacement =
2292
2782
  (frameState.depthOfFieldCandidateGraph === null ||
@@ -2314,8 +2804,22 @@ export function ensureCompiledFrameGraph(
2314
2804
  frameState.depthOfFieldPreviousStandardLightingGraphSignature =
2315
2805
  frameState.standardLightingGraphSignature;
2316
2806
  frameState.depthOfFieldCandidateGraph = compiled.value;
2807
+ } else if (compiled.value !== previous) {
2808
+ // Keep the complete accepted graph projection until queue submission.
2809
+ // This covers barrel enable/disable, output resize, and camera topology
2810
+ // changes without introducing a second resource owner.
2811
+ frameState.barrelDistortionPreviousGraph = previous;
2812
+ frameState.barrelDistortionPreviousGraphKey = frameState.compiledFrameGraphTopologyKey;
2813
+ frameState.barrelDistortionPreviousPerFrameGraph = frameState.perFrameGraph;
2814
+ frameState.barrelDistortionPreviousGraphGeneration = frameState.graphGeneration;
2815
+ frameState.barrelDistortionPreviousCompiledGraphGeneration =
2816
+ frameState.compiledFrameGraphGeneration;
2817
+ frameState.barrelDistortionPreviousStandardLightingGraphSignature =
2818
+ frameState.standardLightingGraphSignature;
2819
+ frameState.barrelDistortionCandidateGraph = compiled.value;
2317
2820
  }
2318
2821
  frameState.compiledFrameGraph = compiled.value;
2822
+ frameState.barrelDistortionGraphResolution = 'accepted';
2319
2823
  frameState.compiledFrameGraphTopologyKey = key;
2320
2824
  frameState.standardLightingGraphSignature = requestedStandardLightingSignature;
2321
2825
  frameState.compiledFrameGraphGeneration += 1;
@@ -2347,99 +2851,11 @@ export function ensureCompiledFrameGraph(
2347
2851
  } else if (featureGraphCandidate !== undefined) {
2348
2852
  recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'abandoned');
2349
2853
  }
2350
- if (!volumeTopologyCandidate && !depthOfFieldReplacement && previous !== null) {
2351
- retire(frameState, previous);
2352
- }
2854
+ frameState.pendingCloudHistoryActive = cloudHistoryDemand;
2855
+ // The selected candidate owner retires the previous graph only after submit.
2353
2856
  return compiled.value;
2354
2857
  }
2355
2858
 
2356
- /**
2357
- * Project one accepted Motion Blur compute execution from the same graph and
2358
- * resolved dispatch objects used by the encoder. A plan/capability candidate
2359
- * without both executed passes deliberately returns no receipt.
2360
- */
2361
- function deriveMotionBlurExecutionReceipt(
2362
- internals: RenderSystemInternals,
2363
- frameState: RenderFrameState,
2364
- ): MotionBlurExecutionReceipt | undefined {
2365
- const graph = frameState.compiledFrameGraph;
2366
- const deviceGeneration = internals.deviceScope?.generation;
2367
- if (graph === null || deviceGeneration === undefined) return undefined;
2368
- const info = graph.inspect();
2369
- const graphPasses = new Map(info.passes.map((pass) => [pass.name, pass]));
2370
- const summaryGraphPass = graphPasses.get('motion-blur-tile-summary');
2371
- const reconstructGraphPass = graphPasses.get('motion-blur');
2372
- if (summaryGraphPass?.kind !== 'compute' || reconstructGraphPass?.kind !== 'compute') {
2373
- return undefined;
2374
- }
2375
- const planned = getRenderFeatureGraphState(internals).plans.find(
2376
- (candidate) => candidate.featureIdentity === 'forgeax.motion-blur',
2377
- );
2378
- const execution =
2379
- planned === undefined ? undefined : getRenderFeaturePlanExecutionProjection(planned);
2380
- if (execution === undefined || execution.featureIdentity !== 'forgeax.motion-blur') {
2381
- return undefined;
2382
- }
2383
- const executionPass = (name: 'motion-blur-tile-summary' | 'motion-blur') =>
2384
- execution.passes.find((pass) => pass.name === name);
2385
- const summary = executionPass('motion-blur-tile-summary');
2386
- const reconstruct = executionPass('motion-blur');
2387
- if (
2388
- summary?.gpuCompute === undefined ||
2389
- reconstruct?.gpuCompute === undefined ||
2390
- summary.resolvedGpuCompute === undefined ||
2391
- reconstruct.resolvedGpuCompute === undefined
2392
- ) {
2393
- return undefined;
2394
- }
2395
- const directWorkgroups = (
2396
- pass: NonNullable<typeof summary.resolvedGpuCompute>,
2397
- ): readonly [number, number, number] | undefined => {
2398
- const dispatch = pass.dispatches[0];
2399
- if (dispatch === undefined || !('workgroups' in dispatch)) return undefined;
2400
- return dispatch.workgroups;
2401
- };
2402
- const summaryWorkgroups = directWorkgroups(summary.resolvedGpuCompute);
2403
- const reconstructWorkgroups = directWorkgroups(reconstruct.resolvedGpuCompute);
2404
- if (summaryWorkgroups === undefined || reconstructWorkgroups === undefined) return undefined;
2405
- const outputResource = info.resources.find(
2406
- (resource) =>
2407
- resource.label === 'motion-blurred-color' ||
2408
- reconstructGraphPass.accesses.some(
2409
- (access) => access.resource === resource.label && access.usage === 'storage-write',
2410
- ),
2411
- );
2412
- if (
2413
- outputResource?.descriptor.kind !== 'texture' ||
2414
- outputResource.descriptor.format !== 'rgba16float'
2415
- ) {
2416
- return undefined;
2417
- }
2418
- const summaryReceipt: MotionBlurExecutionPass = {
2419
- name: 'motion-blur-tile-summary',
2420
- kind: 'compute',
2421
- entryPoint: 'tile_summary',
2422
- workgroups: summaryWorkgroups,
2423
- };
2424
- const reconstructReceipt: MotionBlurExecutionPass = {
2425
- name: 'motion-blur',
2426
- kind: 'compute',
2427
- entryPoint: 'reconstruct',
2428
- workgroups: reconstructWorkgroups,
2429
- };
2430
- return Object.freeze({
2431
- frameId: frameState.frameNumber,
2432
- deviceGeneration,
2433
- // RenderGraphBuilder generations are process-global. The renderer's
2434
- // frame-state generation is the identity used by inspection and by the
2435
- // same-frame admission check below, so retain that owner-scoped value.
2436
- graphGeneration: frameState.graphGeneration,
2437
- program: 'motion-blur-compute' as const,
2438
- outputFormat: 'rgba16float' as const,
2439
- passes: [summaryReceipt, reconstructReceipt] as const,
2440
- });
2441
- }
2442
-
2443
2859
  export function executeCompiledFrameGraph(
2444
2860
  internals: RenderSystemInternals,
2445
2861
  frameState: RenderFrameState,
@@ -2462,21 +2878,31 @@ export function executeCompiledFrameGraph(
2462
2878
  const legacySubmitCommit = typeof frameHooks === 'function' ? frameHooks : undefined;
2463
2879
  const resolvedFrameHooks = typeof frameHooks === 'function' ? undefined : frameHooks;
2464
2880
  const graph = frameState.compiledFrameGraph;
2881
+ // Recovery keeps the candidate graph for detached inspection, but clears
2882
+ // its topology key before the replacement canvas context is published. It
2883
+ // is evidence only until the first post-recovery draw compiles a new graph.
2884
+ // Never let that retained graph reach record/execute or queue.submit.
2885
+ const detachedGraph = graph !== null && frameState.compiledFrameGraphTopologyKey === null;
2465
2886
  const rejectTemporalFrame = (): void => {
2466
2887
  if (frameState.temporalFrameInput === undefined) return;
2467
2888
  frameState.temporalFrameTransaction.commit({ accepted: false });
2468
2889
  frameState.temporalFrameInput = undefined;
2469
2890
  };
2470
- if (graph === null) {
2891
+ if (graph === null || detachedGraph) {
2471
2892
  timingCapture?.discard();
2472
2893
  frameState.pendingAutoExposureState = undefined;
2473
2894
  frameState.pendingStandardLutState = undefined;
2895
+ settleBarrelDistortionGraphCandidate(frameState, false);
2474
2896
  settleVolumetricFogGraphCandidate(frameState, false);
2475
2897
  settleDepthOfFieldGraphCandidate(frameState, false);
2476
2898
  rejectTemporalFrame();
2477
2899
  return false;
2478
2900
  }
2479
- const motionBlurExecution = deriveMotionBlurExecutionReceipt(internals, frameState);
2901
+ const motionBlurExecution = deriveMotionBlurExecutionReceipt(
2902
+ internals,
2903
+ frameState,
2904
+ getRenderFeatureGraphState(internals).plans,
2905
+ );
2480
2906
  const legacyTimingInstrumentation =
2481
2907
  timingCapture === undefined ? undefined : createTimingInstrumentation(timingCapture);
2482
2908
  const timingHost = internals as RenderSystemInternals & {
@@ -2539,6 +2965,7 @@ export function executeCompiledFrameGraph(
2539
2965
  );
2540
2966
  settleVolumetricFogGraphCandidate(frameState, false);
2541
2967
  settleDepthOfFieldGraphCandidate(frameState, false);
2968
+ settleBarrelDistortionGraphCandidate(frameState, false);
2542
2969
  return false;
2543
2970
  }
2544
2971
  const capturedGeneration = deviceScope?.generation;
@@ -3042,8 +3469,8 @@ export function executeCompiledFrameGraph(
3042
3469
  internals.errorRegistry.fire(
3043
3470
  new RhiError({
3044
3471
  code: 'webgpu-runtime-error',
3045
- expected: 'TAA resolve stages a history write before submit',
3046
- hint: 'retry the frame after the temporal resolve pass has encoded successfully',
3472
+ expected: 'an admitted temporal producer stages a history write before submit',
3473
+ hint: 'retry the frame after the TAA or CloudLayer history pass has encoded successfully',
3047
3474
  }),
3048
3475
  );
3049
3476
  return { ok: false, stage: 'finish' };
@@ -3197,6 +3624,11 @@ export function executeCompiledFrameGraph(
3197
3624
  }
3198
3625
  }
3199
3626
  const stagedTemporalCommit = frameState.pendingTemporalCommit ?? { kind: 'none' as const };
3627
+ const stagedCloudHistoryActive = frameState.pendingCloudHistoryActive;
3628
+ if (stagedCloudHistoryActive !== undefined) {
3629
+ frameState.cloudHistoryActive = stagedCloudHistoryActive;
3630
+ delete frameState.pendingCloudHistoryActive;
3631
+ }
3200
3632
  if (stagedTemporalCommit.kind !== 'none') {
3201
3633
  frameState.lastSuccessfulTemporalView = stagedTemporalCommit.view;
3202
3634
  }
@@ -3205,7 +3637,7 @@ export function executeCompiledFrameGraph(
3205
3637
  } else if (stagedTemporalCommit.kind === 'off') {
3206
3638
  frameState.successfulTemporalFrameIndex = 0;
3207
3639
  }
3208
- if (stagedTemporalCommit.kind === 'off') {
3640
+ if (stagedTemporalCommit.kind === 'off' && frameState.cloudHistoryActive !== true) {
3209
3641
  internals.clearPostProcessPipelineCache?.('forgeax.taa-resolve');
3210
3642
  // Retire frame-sized resources, not the device-owned prewarmed
3211
3643
  // shader. The next TAA frame must not start an async cold compile.
@@ -3236,6 +3668,7 @@ export function executeCompiledFrameGraph(
3236
3668
  frameState.pendingAtmospherePublish?.();
3237
3669
  frameState.pendingAtmospherePublish = undefined;
3238
3670
  onSubmitted?.(internals.device.queue.onSubmittedWorkDone());
3671
+ settleBarrelDistortionGraphCandidate(frameState, true);
3239
3672
  settleVolumetricFogGraphCandidate(frameState, true);
3240
3673
  settleDepthOfFieldGraphCandidate(frameState, true);
3241
3674
  },
@@ -3271,11 +3704,13 @@ export function executeCompiledFrameGraph(
3271
3704
  }
3272
3705
  frameState.temporalGpuState = undefined;
3273
3706
  }
3707
+ delete frameState.pendingCloudHistoryActive;
3274
3708
  frameState.pendingTemporalCommit = { kind: 'none' };
3275
3709
  if (frameState.environmentGeneration !== undefined) {
3276
3710
  frameState.environmentLifecycle?.discard(frameState.environmentGeneration);
3277
3711
  frameState.environmentGeneration = undefined;
3278
3712
  }
3713
+ settleBarrelDistortionGraphCandidate(frameState, false);
3279
3714
  settleVolumetricFogGraphCandidate(frameState, false);
3280
3715
  settleDepthOfFieldGraphCandidate(frameState, false);
3281
3716
  },
@@ -3283,6 +3718,7 @@ export function executeCompiledFrameGraph(
3283
3718
  // The callbacks above settle the normal transaction path. Keep this guard
3284
3719
  // for synthetic callers and any future early transaction result that does
3285
3720
  // not enter a callback.
3721
+ settleBarrelDistortionGraphCandidate(frameState, transaction.ok);
3286
3722
  settleVolumetricFogGraphCandidate(frameState, transaction.ok);
3287
3723
  settleDepthOfFieldGraphCandidate(frameState, transaction.ok);
3288
3724
  if (!transaction.ok) rejectTemporalFrame();