@forgeax/engine-render 0.1.27 → 0.1.29

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 (655) hide show
  1. package/README.md +842 -124
  2. package/dist/assembly/atmosphere-shader-prewarm.d.ts +6 -0
  3. package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -0
  4. package/dist/assembly/dynamic-geometry-host.d.ts +52 -0
  5. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -0
  6. package/dist/assembly/dynamic-geometry-runtime.d.ts +20 -0
  7. package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -0
  8. package/dist/assembly/factory.d.ts +3 -1
  9. package/dist/assembly/factory.d.ts.map +1 -1
  10. package/dist/assembly/gpu-driven-owner.d.ts +10 -0
  11. package/dist/assembly/gpu-driven-owner.d.ts.map +1 -0
  12. package/dist/assembly/host-contract.d.ts +19 -0
  13. package/dist/assembly/host-contract.d.ts.map +1 -1
  14. package/dist/assembly/material/assembly.d.ts +7 -0
  15. package/dist/assembly/material/assembly.d.ts.map +1 -1
  16. package/dist/assembly/material-shader-policy.d.ts +3 -1
  17. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  18. package/dist/assembly/recovery/failure-location.d.ts +12 -0
  19. package/dist/assembly/recovery/failure-location.d.ts.map +1 -0
  20. package/dist/assembly/recovery/renderer-recover.d.ts +6 -4
  21. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  22. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  23. package/dist/assembly/renderer-feature-inspection.d.ts +5 -0
  24. package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -0
  25. package/dist/assembly/renderer-frame-transaction.d.ts +19 -0
  26. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  27. package/dist/assembly/renderer-helpers.d.ts +3 -0
  28. package/dist/assembly/renderer-helpers.d.ts.map +1 -1
  29. package/dist/assembly/renderer-inspection.d.ts +4 -0
  30. package/dist/assembly/renderer-inspection.d.ts.map +1 -1
  31. package/dist/assembly/shader-prewarm-policy.d.ts +6 -0
  32. package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -1
  33. package/dist/assembly/skin-palette-owner.d.ts +8 -0
  34. package/dist/assembly/skin-palette-owner.d.ts.map +1 -0
  35. package/dist/assembly/ssr-shader-prewarm.d.ts +6 -0
  36. package/dist/assembly/ssr-shader-prewarm.d.ts.map +1 -0
  37. package/dist/assembly/webgpu-device-loss.d.ts +15 -0
  38. package/dist/assembly/webgpu-device-loss.d.ts.map +1 -0
  39. package/dist/assembly/webgpu-pbr-ready.d.ts +38 -0
  40. package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -0
  41. package/dist/assembly/webgpu-ready-mipmap.d.ts +11 -0
  42. package/dist/assembly/webgpu-ready-mipmap.d.ts.map +1 -0
  43. package/dist/assembly/webgpu-ready.d.ts +7 -3
  44. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  45. package/dist/assembly/webgpu-renderer-bootstrap.d.ts +10 -0
  46. package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -0
  47. package/dist/assembly/webgpu-renderer-contract.d.ts +62 -0
  48. package/dist/assembly/webgpu-renderer-contract.d.ts.map +1 -0
  49. package/dist/assembly/webgpu-renderer-guards.d.ts +9 -0
  50. package/dist/assembly/webgpu-renderer-guards.d.ts.map +1 -0
  51. package/dist/assembly/webgpu-renderer-observation.d.ts +6 -0
  52. package/dist/assembly/webgpu-renderer-observation.d.ts.map +1 -0
  53. package/dist/assembly/webgpu-renderer-recovery-failure.d.ts +12 -0
  54. package/dist/assembly/webgpu-renderer-recovery-failure.d.ts.map +1 -0
  55. package/dist/assembly/webgpu-renderer.d.ts +7 -141
  56. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  57. package/dist/assembly/webgpu-vertex-layouts.d.ts +28 -84
  58. package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -1
  59. package/dist/authoring.mjs +3 -3
  60. package/dist/{chunk-ADHHVYLW.mjs → chunk-33XZFTQU.mjs} +245 -39
  61. package/dist/chunk-33XZFTQU.mjs.map +1 -0
  62. package/dist/{chunk-GF523LHF.mjs → chunk-BLXGONR2.mjs} +30 -12
  63. package/dist/chunk-BLXGONR2.mjs.map +1 -0
  64. package/dist/{chunk-KM2NOX2I.mjs → chunk-BQVQOQJH.mjs} +958 -254
  65. package/dist/chunk-BQVQOQJH.mjs.map +1 -0
  66. package/dist/{chunk-HKXTW355.mjs → chunk-BTWH7BP4.mjs} +386 -17
  67. package/dist/chunk-BTWH7BP4.mjs.map +1 -0
  68. package/dist/{chunk-TMIASV2N.mjs → chunk-FUODTTIA.mjs} +3934 -676
  69. package/dist/chunk-FUODTTIA.mjs.map +1 -0
  70. package/dist/{chunk-FJ6P52EE.mjs → chunk-ILQ5MEZF.mjs} +3 -17
  71. package/dist/chunk-ILQ5MEZF.mjs.map +1 -0
  72. package/dist/{chunk-E34VL5VI.mjs → chunk-JQTSEI3Z.mjs} +3 -3
  73. package/dist/{chunk-E34VL5VI.mjs.map → chunk-JQTSEI3Z.mjs.map} +1 -1
  74. package/dist/{chunk-GE3SDD72.mjs → chunk-NICXSHAT.mjs} +16 -38
  75. package/dist/chunk-NICXSHAT.mjs.map +1 -0
  76. package/dist/{chunk-ZZ4YQ474.mjs → chunk-NRNXBLIJ.mjs} +577 -7
  77. package/dist/chunk-NRNXBLIJ.mjs.map +1 -0
  78. package/dist/{chunk-JDWARUOI.mjs → chunk-PA5DRJNI.mjs} +17 -3
  79. package/dist/chunk-PA5DRJNI.mjs.map +1 -0
  80. package/dist/{chunk-XDP6XE5V.mjs → chunk-S4DNKQYQ.mjs} +1118 -446
  81. package/dist/chunk-S4DNKQYQ.mjs.map +1 -0
  82. package/dist/components/camera.d.ts +62 -2
  83. package/dist/components/camera.d.ts.map +1 -1
  84. package/dist/components/index.d.ts +1 -0
  85. package/dist/components/index.d.ts.map +1 -1
  86. package/dist/components/instances.d.ts +6 -20
  87. package/dist/components/instances.d.ts.map +1 -1
  88. package/dist/components/scene-instance.d.ts +2 -1
  89. package/dist/components/scene-instance.d.ts.map +1 -1
  90. package/dist/components/screen-space-reflection.d.ts +14 -0
  91. package/dist/components/screen-space-reflection.d.ts.map +1 -0
  92. package/dist/construct-renderer.mjs +13805 -7186
  93. package/dist/construct-renderer.mjs.map +1 -1
  94. package/dist/device/gpu-residency.d.ts +11 -17
  95. package/dist/device/gpu-residency.d.ts.map +1 -1
  96. package/dist/device/mesh-residency-lifetime.d.ts +21 -0
  97. package/dist/device/mesh-residency-lifetime.d.ts.map +1 -0
  98. package/dist/dynamic-geometry.d.ts +125 -0
  99. package/dist/dynamic-geometry.d.ts.map +1 -0
  100. package/dist/environment/background.d.ts +6 -0
  101. package/dist/environment/background.d.ts.map +1 -0
  102. package/dist/errors/gpu-driven.d.ts +6 -3
  103. package/dist/errors/gpu-driven.d.ts.map +1 -1
  104. package/dist/errors/render.d.ts +7 -4
  105. package/dist/errors/render.d.ts.map +1 -1
  106. package/dist/extract/camera.d.ts.map +1 -1
  107. package/dist/extract/gpu-driven.d.ts +33 -3
  108. package/dist/extract/gpu-driven.d.ts.map +1 -1
  109. package/dist/features/host.d.ts +23 -1
  110. package/dist/features/host.d.ts.map +1 -1
  111. package/dist/features/noise-texture.d.ts +17 -0
  112. package/dist/features/noise-texture.d.ts.map +1 -0
  113. package/dist/features/plan.d.ts +83 -2
  114. package/dist/features/plan.d.ts.map +1 -1
  115. package/dist/features/prepared-gpu-work.d.ts +37 -4
  116. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  117. package/dist/features/prepared-graphics-store.d.ts.map +1 -1
  118. package/dist/features/prepared-graphics.d.ts +5 -0
  119. package/dist/features/prepared-graphics.d.ts.map +1 -1
  120. package/dist/features/render-graph-compute.d.ts +5 -3
  121. package/dist/features/render-graph-compute.d.ts.map +1 -1
  122. package/dist/features/render-graph-contribution.d.ts +11 -3
  123. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  124. package/dist/features/render-graph-raster.d.ts +19 -3
  125. package/dist/features/render-graph-raster.d.ts.map +1 -1
  126. package/dist/features/types.d.ts +11 -0
  127. package/dist/features/types.d.ts.map +1 -1
  128. package/dist/fullscreen-post-process-pass.d.ts +2 -0
  129. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  130. package/dist/gpu-driven/batch-topology.d.ts +34 -1
  131. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  132. package/dist/gpu-driven/pbr-program.d.ts +10 -0
  133. package/dist/gpu-driven/pbr-program.d.ts.map +1 -0
  134. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  135. package/dist/gpu-driven/production-raster.d.ts +144 -23
  136. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  137. package/dist/gpu-driven/shadow-views.d.ts +126 -0
  138. package/dist/gpu-driven/shadow-views.d.ts.map +1 -0
  139. package/dist/gpu-driven/view-gpu.d.ts +18 -2
  140. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  141. package/dist/gpu-scene-schema.d.ts +2 -8
  142. package/dist/gpu-scene-schema.d.ts.map +1 -1
  143. package/dist/gpu-scene.d.ts +14 -0
  144. package/dist/gpu-scene.d.ts.map +1 -1
  145. package/dist/gpu-texture-usage.d.ts +1 -0
  146. package/dist/gpu-texture-usage.d.ts.map +1 -1
  147. package/dist/hdrp-buffers.d.ts +6 -0
  148. package/dist/hdrp-buffers.d.ts.map +1 -1
  149. package/dist/ibl/frame-resources.d.ts +8 -0
  150. package/dist/ibl/frame-resources.d.ts.map +1 -0
  151. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  152. package/dist/index.d.ts +21 -8
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.mjs +8 -7
  155. package/dist/index.mjs.map +1 -1
  156. package/dist/inspection-types.d.ts +164 -0
  157. package/dist/inspection-types.d.ts.map +1 -1
  158. package/dist/instances-derived-bounds.d.ts +11 -0
  159. package/dist/instances-derived-bounds.d.ts.map +1 -1
  160. package/dist/instances.d.ts +89 -44
  161. package/dist/instances.d.ts.map +1 -1
  162. package/dist/internal.d.ts +3 -0
  163. package/dist/internal.d.ts.map +1 -1
  164. package/dist/internal.mjs +118 -10
  165. package/dist/internal.mjs.map +1 -1
  166. package/dist/lifecycle.d.ts +2 -1
  167. package/dist/lifecycle.d.ts.map +1 -1
  168. package/dist/material-row.d.ts +5 -0
  169. package/dist/material-row.d.ts.map +1 -0
  170. package/dist/materials.d.ts +15 -0
  171. package/dist/materials.d.ts.map +1 -1
  172. package/dist/pbr-pipeline.d.ts +21 -5
  173. package/dist/pbr-pipeline.d.ts.map +1 -1
  174. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  175. package/dist/pipeline/standard-output/auto-exposure/capability.d.ts +12 -0
  176. package/dist/pipeline/standard-output/auto-exposure/capability.d.ts.map +1 -0
  177. package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts +70 -0
  178. package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts.map +1 -0
  179. package/dist/pipeline/standard-output/auto-exposure/graph.d.ts +75 -0
  180. package/dist/pipeline/standard-output/auto-exposure/graph.d.ts.map +1 -0
  181. package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts +111 -0
  182. package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts.map +1 -0
  183. package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts +16 -0
  184. package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts.map +1 -0
  185. package/dist/pipeline/standard-output/auto-exposure/preset.d.ts +14 -0
  186. package/dist/pipeline/standard-output/auto-exposure/preset.d.ts.map +1 -0
  187. package/dist/pipeline/standard-output/auto-exposure/state.d.ts +46 -0
  188. package/dist/pipeline/standard-output/auto-exposure/state.d.ts.map +1 -0
  189. package/dist/pipeline/standard-output/color-transform.d.ts +27 -0
  190. package/dist/pipeline/standard-output/color-transform.d.ts.map +1 -0
  191. package/dist/pipeline/standard-output/graph.d.ts +15 -0
  192. package/dist/pipeline/standard-output/graph.d.ts.map +1 -0
  193. package/dist/pipeline/standard-output/lut-admission.d.ts +29 -0
  194. package/dist/pipeline/standard-output/lut-admission.d.ts.map +1 -0
  195. package/dist/pipeline/standard-output/lut-gpu.d.ts +48 -0
  196. package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -0
  197. package/dist/pipeline/standard-output/lut-state.d.ts +68 -0
  198. package/dist/pipeline/standard-output/lut-state.d.ts.map +1 -0
  199. package/dist/pipeline/standard-output/resources.d.ts +10 -0
  200. package/dist/pipeline/standard-output/resources.d.ts.map +1 -0
  201. package/dist/pipeline/standard-output/types.d.ts +39 -0
  202. package/dist/pipeline/standard-output/types.d.ts.map +1 -0
  203. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  204. package/dist/pipeline/standard-post.d.ts +2 -2
  205. package/dist/pipeline/standard-post.d.ts.map +1 -1
  206. package/dist/pipeline-builder.d.ts +2 -1
  207. package/dist/pipeline-builder.d.ts.map +1 -1
  208. package/dist/pipeline-spec-types.d.ts +4 -0
  209. package/dist/pipeline-spec-types.d.ts.map +1 -1
  210. package/dist/pipeline-spec.d.ts +1 -9
  211. package/dist/pipeline-spec.d.ts.map +1 -1
  212. package/dist/points-lines/standard-owner.d.ts +1 -1
  213. package/dist/points-lines/standard-owner.d.ts.map +1 -1
  214. package/dist/prepare/prepared-graphics-resolver.d.ts +6 -1
  215. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  216. package/dist/record/dynamic-geometry-consumption.d.ts +15 -0
  217. package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -0
  218. package/dist/record/frame-lighting.d.ts +3 -3
  219. package/dist/record/frame-lighting.d.ts.map +1 -1
  220. package/dist/record/frame-snapshot.d.ts +106 -5
  221. package/dist/record/frame-snapshot.d.ts.map +1 -1
  222. package/dist/record/frame-targets.d.ts.map +1 -1
  223. package/dist/record/frame.d.ts +39 -4
  224. package/dist/record/frame.d.ts.map +1 -1
  225. package/dist/record/main-pass-geometry.d.ts +7 -8
  226. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  227. package/dist/record/main-pass-material.d.ts +38 -1
  228. package/dist/record/main-pass-material.d.ts.map +1 -1
  229. package/dist/record/main-pass-sprite-draws.d.ts +2 -10
  230. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  231. package/dist/record/main-pass.d.ts +12 -2
  232. package/dist/record/main-pass.d.ts.map +1 -1
  233. package/dist/record/mesh-ssbo.d.ts +15 -6
  234. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  235. package/dist/record/prepared-material-bindings.d.ts +19 -0
  236. package/dist/record/prepared-material-bindings.d.ts.map +1 -0
  237. package/dist/record/recovery-pipeline.d.ts +2 -9
  238. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  239. package/dist/record/render-context.d.ts +76 -26
  240. package/dist/record/render-context.d.ts.map +1 -1
  241. package/dist/record/shadow-pass.d.ts +62 -7
  242. package/dist/record/shadow-pass.d.ts.map +1 -1
  243. package/dist/record/skybox-post-pass.d.ts +1 -1
  244. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  245. package/dist/record/typed-frame-graph.d.ts +35 -7
  246. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  247. package/dist/record/view-ubo.d.ts +1 -0
  248. package/dist/record/view-ubo.d.ts.map +1 -1
  249. package/dist/recovery/render-system-candidate.d.ts +10 -2
  250. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  251. package/dist/recovery/render-system-roots.d.ts +11 -0
  252. package/dist/recovery/render-system-roots.d.ts.map +1 -0
  253. package/dist/recovery/types.d.ts +76 -0
  254. package/dist/recovery/types.d.ts.map +1 -0
  255. package/dist/reflection/record-owner.d.ts +0 -2
  256. package/dist/reflection/record-owner.d.ts.map +1 -1
  257. package/dist/render-contract.d.ts +185 -30
  258. package/dist/render-contract.d.ts.map +1 -1
  259. package/dist/render-graph-primitives.d.ts +1 -0
  260. package/dist/render-graph-primitives.d.ts.map +1 -1
  261. package/dist/render-pipeline.d.ts +64 -5
  262. package/dist/render-pipeline.d.ts.map +1 -1
  263. package/dist/render-system-extract-tail.d.ts +7 -5
  264. package/dist/render-system-extract-tail.d.ts.map +1 -1
  265. package/dist/render-system-extract.d.ts +101 -41
  266. package/dist/render-system-extract.d.ts.map +1 -1
  267. package/dist/render-system-projections.d.ts +22 -0
  268. package/dist/render-system-projections.d.ts.map +1 -0
  269. package/dist/render-system.d.ts +34 -77
  270. package/dist/render-system.d.ts.map +1 -1
  271. package/dist/scene/render-scene-types.d.ts +14 -7
  272. package/dist/scene/render-scene-types.d.ts.map +1 -1
  273. package/dist/scene/render-scene.d.ts +29 -11
  274. package/dist/scene/render-scene.d.ts.map +1 -1
  275. package/dist/scene/shadow-visibility.d.ts +5 -0
  276. package/dist/scene/shadow-visibility.d.ts.map +1 -0
  277. package/dist/ssr/admission.d.ts +70 -1
  278. package/dist/ssr/admission.d.ts.map +1 -1
  279. package/dist/ssr/compose.d.ts +13 -0
  280. package/dist/ssr/compose.d.ts.map +1 -0
  281. package/dist/ssr/errors.d.ts +35 -0
  282. package/dist/ssr/errors.d.ts.map +1 -1
  283. package/dist/ssr/graph.d.ts +60 -0
  284. package/dist/ssr/graph.d.ts.map +1 -0
  285. package/dist/ssr/history.d.ts +97 -0
  286. package/dist/ssr/history.d.ts.map +1 -0
  287. package/dist/ssr/hiz.d.ts +37 -0
  288. package/dist/ssr/hiz.d.ts.map +1 -0
  289. package/dist/ssr/inspection.d.ts +42 -0
  290. package/dist/ssr/inspection.d.ts.map +1 -0
  291. package/dist/ssr/resources.d.ts +58 -0
  292. package/dist/ssr/resources.d.ts.map +1 -0
  293. package/dist/ssr/temporal.d.ts +38 -0
  294. package/dist/ssr/temporal.d.ts.map +1 -0
  295. package/dist/systems/skin-palette-allocator.d.ts +32 -10
  296. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  297. package/dist/systems/skin-palette-types.d.ts +15 -0
  298. package/dist/systems/skin-palette-types.d.ts.map +1 -1
  299. package/dist/systems/transparent-dispatch.d.ts +9 -0
  300. package/dist/systems/transparent-dispatch.d.ts.map +1 -0
  301. package/dist/targets/material-source.d.ts +7 -0
  302. package/dist/targets/material-source.d.ts.map +1 -1
  303. package/dist/temporal/frame-coordinator.d.ts +7 -0
  304. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  305. package/dist/temporal/gpu.d.ts +8 -0
  306. package/dist/temporal/gpu.d.ts.map +1 -1
  307. package/dist/temporal/history.d.ts.map +1 -1
  308. package/dist/temporal/index.mjs +6 -6
  309. package/dist/temporal/inspection.d.ts.map +1 -1
  310. package/dist/temporal/standard-scene-data.d.ts +2 -1
  311. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  312. package/dist/temporal/temporal-view.d.ts +2 -0
  313. package/dist/temporal/temporal-view.d.ts.map +1 -1
  314. package/dist/typed-render-graph-primitives.d.ts +35 -1
  315. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  316. package/dist/typed-shadow-passes.d.ts +3 -1
  317. package/dist/typed-shadow-passes.d.ts.map +1 -1
  318. package/dist/volume/temporal.d.ts +6 -0
  319. package/dist/volume/temporal.d.ts.map +1 -1
  320. package/package.json +21 -20
  321. package/src/__tests__/auto-exposure-gpu-evidence.ts +291 -0
  322. package/src/__tests__/auto-exposure-inspection.unit.test.ts +80 -0
  323. package/src/__tests__/auto-exposure-transaction.integration.test.ts +99 -0
  324. package/src/__tests__/auto-exposure.browser.test.ts +33 -0
  325. package/src/__tests__/auto-exposure.dawn.test.ts +33 -0
  326. package/src/__tests__/detached-inspection.integration.test.ts +5 -0
  327. package/src/__tests__/device-scope-lifecycle.unit.test.ts +18 -0
  328. package/src/__tests__/device-scope-stale-matrix.unit.test.ts +18 -0
  329. package/src/__tests__/docs-gate-green.unit.test.ts +3 -3
  330. package/src/__tests__/dynamic-geometry-host.unit.test.ts +1123 -0
  331. package/src/__tests__/dynamic-geometry.unit.test.ts +233 -0
  332. package/src/__tests__/factory-contract.integration.test.ts +73 -22
  333. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +19 -0
  334. package/src/__tests__/feature-depth-input.dawn.test.ts +121 -0
  335. package/src/__tests__/feature-noise.unit.test.ts +38 -0
  336. package/src/__tests__/fixtures/taa-resolve-pinned-baseline.wgsl +301 -0
  337. package/src/__tests__/frame-camera-selection.unit.test.ts +131 -0
  338. package/src/__tests__/frame-plan-contract.unit.test.ts +24 -0
  339. package/src/__tests__/frame-targets.unit.test.ts +24 -0
  340. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +3 -0
  341. package/src/__tests__/gpu-driven-baseline.characterization.test.ts +79 -0
  342. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +287 -6
  343. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +122 -142
  344. package/src/__tests__/gpu-driven-indirect-raster.browser.test.ts +10 -1
  345. package/src/__tests__/gpu-driven-lod-cache.unit.test.ts +264 -0
  346. package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +349 -0
  347. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +1417 -0
  348. package/src/__tests__/gpu-driven-pbr.integration.test.ts +21 -0
  349. package/src/__tests__/gpu-driven-production-fixture.ts +96 -0
  350. package/src/__tests__/gpu-driven-production.integration.test.ts +1075 -93
  351. package/src/__tests__/gpu-driven-scaling.unit.test.ts +1 -3
  352. package/src/__tests__/gpu-driven-shadow-views.browser.test.ts +63 -0
  353. package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +294 -0
  354. package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +97 -0
  355. package/src/__tests__/gpu-driven-shadow-views.unit.test.ts +168 -0
  356. package/src/__tests__/gpu-driven-skin-contract.test-d.ts +11 -0
  357. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +72 -20
  358. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +30 -9
  359. package/src/__tests__/gpu-driven-view.unit.test.ts +159 -52
  360. package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -1
  361. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +42 -0
  362. package/src/__tests__/gpu-scene-render-graph-gpu.ts +19 -5
  363. package/src/__tests__/gpu-scene.dawn.test.ts +40 -13
  364. package/src/__tests__/gpu-scene.unit.test.ts +559 -33
  365. package/src/__tests__/instances-culling.integration.test.ts +36 -10
  366. package/src/__tests__/instances-derived-bounds.unit.test.ts +54 -0
  367. package/src/__tests__/instances-store.unit.test.ts +242 -39
  368. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +6 -0
  369. package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +13 -16
  370. package/src/__tests__/lut-lkg-transaction.integration.test.ts +122 -0
  371. package/src/__tests__/material-contract-inventory.unit.test.ts +5 -6
  372. package/src/__tests__/material-dispatch-shader-id.unit.test.ts +51 -0
  373. package/src/__tests__/material-lane-selection.unit.test.ts +60 -0
  374. package/src/__tests__/material-numeric-upload.unit.test.ts +53 -0
  375. package/src/__tests__/material-texture-handles.unit.test.ts +18 -0
  376. package/src/__tests__/material-texture-source.unit.test.ts +87 -0
  377. package/src/__tests__/materials-authoring-validation.unit.test.ts +41 -0
  378. package/src/__tests__/materials-standard-contract.unit.test.ts +52 -2
  379. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +4 -0
  380. package/src/__tests__/mesh-submission-lifetime.unit.test.ts +53 -0
  381. package/src/__tests__/normal-fallback.dawn.test.ts +122 -0
  382. package/src/__tests__/particle-material-input-layout.unit.test.ts +104 -0
  383. package/src/__tests__/physical-clearcoat.integration.test.ts +37 -3
  384. package/src/__tests__/pipeline-texture-specialization.unit.test.ts +138 -0
  385. package/src/__tests__/point-shadow-inspection.unit.test.ts +2 -0
  386. package/src/__tests__/prepared-gpu-driven-pbr-contract.test-d.ts +31 -0
  387. package/src/__tests__/prepared-gpu-driven-pbr-contract.unit.test.ts +39 -0
  388. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +35 -4
  389. package/src/__tests__/prepared-graphics-capability.unit.test.ts +21 -0
  390. package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +6 -2
  391. package/src/__tests__/profiler-phase-catalog.test.ts +5 -0
  392. package/src/__tests__/public-surface.test-d.ts +3 -0
  393. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +30 -12
  394. package/src/__tests__/reflection-fallback-async-publication.unit.test.ts +104 -0
  395. package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +2 -1
  396. package/src/__tests__/render-error-code-owner.test-d.ts +4 -0
  397. package/src/__tests__/render-error-detail-ssot.test-d.ts +22 -0
  398. package/src/__tests__/render-error-exhaustive.test-d.ts +15 -0
  399. package/src/__tests__/render-feature-gpu-work.integration.test.ts +285 -2
  400. package/src/__tests__/render-feature-plan.unit.test.ts +245 -0
  401. package/src/__tests__/render-feature-shadow-order.unit.test.ts +228 -0
  402. package/src/__tests__/render-feature-zero-work.unit.test.ts +35 -1
  403. package/src/__tests__/render-graph-fallback.integration.test.ts +2 -2
  404. package/src/__tests__/render-owner-cohesion.unit.test.ts +55 -0
  405. package/src/__tests__/render-scene-mixed-updates.integration.test.ts +426 -0
  406. package/src/__tests__/render-scene-projection.unit.test.ts +914 -23
  407. package/src/__tests__/render-scene-resource-updates.integration.test.ts +214 -0
  408. package/src/__tests__/render-scene-skin-lifecycle.integration.test.ts +173 -0
  409. package/src/__tests__/render-scene-temporal-retry.unit.test.ts +133 -0
  410. package/src/__tests__/render-target-material-source.test-d.ts +27 -3
  411. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +61 -1
  412. package/src/__tests__/renderer-frame-transaction.integration.test.ts +923 -5
  413. package/src/__tests__/shader-manifest-fixture.ts +39 -0
  414. package/src/__tests__/skin-motion-regression.unit.test.ts +0 -33
  415. package/src/__tests__/skin-palette-persistent.integration.test.ts +28 -0
  416. package/src/__tests__/skin-palette-persistent.unit.test.ts +247 -0
  417. package/src/__tests__/skinned-shadow-caster.test.ts +118 -14
  418. package/src/__tests__/specular-environment-scale.dawn.test.ts +156 -0
  419. package/src/__tests__/ssr-admission-matrix.integration.test.ts +7 -1
  420. package/src/__tests__/ssr-confidence-compose.unit.test.ts +84 -0
  421. package/src/__tests__/ssr-coordinate-oracle.unit.test.ts +28 -0
  422. package/src/__tests__/ssr-format-probe-gating.integration.test.ts +82 -0
  423. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +91 -0
  424. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +112 -0
  425. package/src/__tests__/ssr-gpu-dispatch.ts +946 -0
  426. package/src/__tests__/ssr-graph-topology.unit.test.ts +104 -0
  427. package/src/__tests__/ssr-history-filter.dawn.test.ts +198 -0
  428. package/src/__tests__/ssr-hit-color.dawn.test.ts +273 -0
  429. package/src/__tests__/ssr-hiz-odd-reduction.dawn.test.ts +569 -0
  430. package/src/__tests__/ssr-hiz-rescue.dawn.test.ts +279 -0
  431. package/src/__tests__/ssr-hiz.browser.test.ts +150 -0
  432. package/src/__tests__/ssr-hiz.dawn.test.ts +150 -0
  433. package/src/__tests__/ssr-hiz.unit.test.ts +56 -0
  434. package/src/__tests__/ssr-inspection.unit.test.ts +105 -0
  435. package/src/__tests__/ssr-memory-budget.unit.test.ts +45 -0
  436. package/src/__tests__/ssr-performance-sequence-aggregation.unit.test.ts +15 -0
  437. package/src/__tests__/ssr-public-surface.test-d.ts +37 -0
  438. package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +387 -0
  439. package/src/__tests__/ssr-reflection-mip-reducer.dawn.test.ts +309 -0
  440. package/src/__tests__/ssr-reprojection-probe.ts +90 -0
  441. package/src/__tests__/ssr-roughness-mip.dawn.test.ts +163 -0
  442. package/src/__tests__/ssr-shader-prewarm.unit.test.ts +59 -0
  443. package/src/__tests__/ssr-spatial.integration.test.ts +141 -0
  444. package/src/__tests__/ssr-stable-lattice.dawn.test.ts +475 -0
  445. package/src/__tests__/ssr-temporal-history.unit.test.ts +202 -0
  446. package/src/__tests__/ssr-texel-crossing.dawn.test.ts +448 -0
  447. package/src/__tests__/ssr-zero-work.integration.test.ts +87 -0
  448. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +44 -12
  449. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +29 -0
  450. package/src/__tests__/standard-output-chain-contract.unit.test.ts +99 -0
  451. package/src/__tests__/standard-output-chain.integration.test.ts +73 -0
  452. package/src/__tests__/standard-output-gpu-evidence.ts +205 -0
  453. package/src/__tests__/standard-output.browser.test.ts +15 -0
  454. package/src/__tests__/standard-output.dawn.test.ts +15 -0
  455. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +38 -0
  456. package/src/__tests__/standard-pipeline.integration.test.ts +320 -3
  457. package/src/__tests__/standard-profile-oracle.unit.test.ts +22 -0
  458. package/src/__tests__/standard-surface-lighting.unit.test.ts +41 -0
  459. package/src/__tests__/standard-texture-specialization.dawn.test.ts +141 -0
  460. package/src/__tests__/surface-adjacent-regressions.integration.test.ts +6 -3
  461. package/src/__tests__/taa-coverage-edge.dawn.test.ts +193 -0
  462. package/src/__tests__/taa-history-clipping.dawn.test.ts +328 -0
  463. package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +778 -0
  464. package/src/__tests__/taa-stability-feedback.browser.test.ts +23 -0
  465. package/src/__tests__/taa-stability-feedback.dawn.test.ts +68 -0
  466. package/src/__tests__/taa-stability-feedback.ts +341 -0
  467. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +49 -7
  468. package/src/__tests__/temporal-reset-matrix.unit.test.ts +9 -0
  469. package/src/__tests__/temporal-transaction.integration.test.ts +28 -0
  470. package/src/__tests__/texture-usage-surface.unit.test.ts +4 -2
  471. package/src/__tests__/typed-pipeline-topology.unit.test.ts +109 -4
  472. package/src/__tests__/typed-scene-pass-material-cache.integration.test.ts +126 -0
  473. package/src/__tests__/vertex-layouts-ssot.unit.test.ts +47 -0
  474. package/src/__tests__/visibility-extract.perf.test.ts +20 -8
  475. package/src/__tests__/visibility-instances.integration.test.ts +19 -7
  476. package/src/__tests__/visibility-producer-matrix.integration.test.ts +20 -9
  477. package/src/assembly/atmosphere-shader-prewarm.ts +38 -0
  478. package/src/assembly/dynamic-geometry-host.ts +887 -0
  479. package/src/assembly/dynamic-geometry-runtime.ts +71 -0
  480. package/src/assembly/factory.ts +4 -0
  481. package/src/assembly/gpu-driven-owner.ts +29 -0
  482. package/src/assembly/host-contract.ts +36 -0
  483. package/src/assembly/material/assembly.ts +38 -0
  484. package/src/assembly/material-shader-policy.ts +71 -28
  485. package/src/assembly/recovery/failure-location.ts +62 -0
  486. package/src/assembly/recovery/renderer-recover.ts +19 -17
  487. package/src/assembly/renderer-facade.ts +29 -0
  488. package/src/assembly/renderer-feature-inspection.ts +13 -0
  489. package/src/assembly/renderer-frame-transaction.ts +63 -0
  490. package/src/assembly/renderer-helpers.ts +26 -0
  491. package/src/assembly/renderer-inspection.ts +6 -0
  492. package/src/assembly/shader-prewarm-policy.ts +32 -0
  493. package/src/assembly/skin-palette-owner.ts +21 -0
  494. package/src/assembly/ssr-shader-prewarm.ts +41 -0
  495. package/src/assembly/webgpu-device-loss.ts +75 -0
  496. package/src/assembly/webgpu-pbr-ready.ts +232 -0
  497. package/src/assembly/webgpu-ready-mipmap.ts +26 -0
  498. package/src/assembly/webgpu-ready.ts +142 -121
  499. package/src/assembly/webgpu-renderer-bootstrap.ts +85 -0
  500. package/src/assembly/webgpu-renderer-contract.ts +69 -0
  501. package/src/assembly/webgpu-renderer-guards.ts +32 -0
  502. package/src/assembly/webgpu-renderer-observation.ts +43 -0
  503. package/src/assembly/webgpu-renderer-recovery-failure.ts +62 -0
  504. package/src/assembly/webgpu-renderer.ts +675 -614
  505. package/src/assembly/webgpu-vertex-layouts.ts +233 -87
  506. package/src/components/__tests__/camera-exposure.contract.unit.test.ts +153 -0
  507. package/src/components/camera.ts +287 -7
  508. package/src/components/index.ts +1 -0
  509. package/src/components/instances.ts +45 -18
  510. package/src/components/scene-instance.ts +2 -1
  511. package/src/components/screen-space-reflection.ts +21 -0
  512. package/src/components/sprite-animation.ts +3 -3
  513. package/src/components/sprite-instances.ts +1 -1
  514. package/src/device/gpu-residency.ts +96 -93
  515. package/src/device/mesh-residency-lifetime.ts +73 -0
  516. package/src/dynamic-geometry.ts +736 -0
  517. package/src/environment/background.ts +263 -0
  518. package/src/errors/gpu-driven.ts +39 -5
  519. package/src/errors/render.ts +27 -3
  520. package/src/extract/camera.ts +26 -1
  521. package/src/extract/gpu-driven.ts +185 -10
  522. package/src/features/host.ts +237 -19
  523. package/src/features/noise-texture.ts +91 -0
  524. package/src/features/plan.ts +553 -24
  525. package/src/features/prepared-gpu-work.ts +631 -49
  526. package/src/features/prepared-graphics-store.ts +31 -0
  527. package/src/features/prepared-graphics.ts +5 -0
  528. package/src/features/render-graph-compute.ts +41 -8
  529. package/src/features/render-graph-contribution.ts +141 -3
  530. package/src/features/render-graph-raster.ts +143 -30
  531. package/src/features/types.ts +11 -0
  532. package/src/fullscreen-post-process-pass.ts +3 -0
  533. package/src/gpu-driven/batch-topology.ts +424 -102
  534. package/src/gpu-driven/pbr-program.ts +31 -0
  535. package/src/gpu-driven/prepared-draw.ts +29 -4
  536. package/src/gpu-driven/production-raster.ts +2267 -694
  537. package/src/gpu-driven/shadow-views.ts +539 -0
  538. package/src/gpu-driven/view-gpu.ts +188 -41
  539. package/src/gpu-scene-schema.ts +52 -11
  540. package/src/gpu-scene.ts +186 -99
  541. package/src/gpu-texture-usage.ts +1 -0
  542. package/src/hdrp-buffers.ts +72 -36
  543. package/src/ibl/frame-resources.ts +38 -0
  544. package/src/ibl/skylight-bind-group.ts +5 -4
  545. package/src/index.ts +125 -1
  546. package/src/inspection-types.ts +191 -0
  547. package/src/instances-derived-bounds.ts +258 -34
  548. package/src/instances.ts +459 -150
  549. package/src/internal.ts +22 -0
  550. package/src/lifecycle.ts +2 -0
  551. package/src/material-row.ts +145 -0
  552. package/src/materials.ts +44 -8
  553. package/src/pbr-pipeline.ts +159 -17
  554. package/src/pipeline/standard-forward-lane.ts +52 -24
  555. package/src/pipeline/standard-output/__tests__/lut-admission.unit.test.ts +103 -0
  556. package/src/pipeline/standard-output/auto-exposure/__tests__/capability.unit.test.ts +40 -0
  557. package/src/pipeline/standard-output/auto-exposure/__tests__/gpu.contract.unit.test.ts +125 -0
  558. package/src/pipeline/standard-output/auto-exposure/__tests__/graph.contract.unit.test.ts +94 -0
  559. package/src/pipeline/standard-output/auto-exposure/__tests__/oracle.test.ts +108 -0
  560. package/src/pipeline/standard-output/auto-exposure/__tests__/preset.test.ts +26 -0
  561. package/src/pipeline/standard-output/auto-exposure/capability.ts +38 -0
  562. package/src/pipeline/standard-output/auto-exposure/gpu.ts +567 -0
  563. package/src/pipeline/standard-output/auto-exposure/graph.ts +221 -0
  564. package/src/pipeline/standard-output/auto-exposure/inspection.ts +231 -0
  565. package/src/pipeline/standard-output/auto-exposure/oracle.ts +291 -0
  566. package/src/pipeline/standard-output/auto-exposure/preset.ts +28 -0
  567. package/src/pipeline/standard-output/auto-exposure/state.ts +156 -0
  568. package/src/pipeline/standard-output/color-transform.ts +124 -0
  569. package/src/pipeline/standard-output/graph.ts +139 -0
  570. package/src/pipeline/standard-output/lut-admission.ts +121 -0
  571. package/src/pipeline/standard-output/lut-gpu.ts +399 -0
  572. package/src/pipeline/standard-output/lut-state.ts +176 -0
  573. package/src/pipeline/standard-output/resources.ts +121 -0
  574. package/src/pipeline/standard-output/types.ts +58 -0
  575. package/src/pipeline/standard-pipeline.ts +192 -59
  576. package/src/pipeline/standard-post.ts +186 -27
  577. package/src/pipeline-builder.ts +16 -6
  578. package/src/pipeline-spec-types.ts +4 -0
  579. package/src/pipeline-spec.ts +37 -29
  580. package/src/points-lines/standard-owner.ts +6 -14
  581. package/src/prepare/prepared-graphics-resolver.ts +9 -2
  582. package/src/record/__tests__/material-slot-plan.unit.test.ts +48 -0
  583. package/src/record/__tests__/mesh-ssbo.unit.test.ts +40 -1
  584. package/src/record/__tests__/recovery-pipeline.unit.test.ts +2 -29
  585. package/src/record/__tests__/sprite-material-abi.unit.test.ts +36 -5
  586. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +45 -0
  587. package/src/record/__tests__/view-ubo-layout.unit.test.ts +5 -0
  588. package/src/record/dynamic-geometry-consumption.ts +59 -0
  589. package/src/record/frame-lighting.ts +12 -11
  590. package/src/record/frame-snapshot.ts +133 -23
  591. package/src/record/frame-targets.ts +9 -0
  592. package/src/record/frame.ts +727 -133
  593. package/src/record/main-pass-geometry.ts +162 -98
  594. package/src/record/main-pass-material.ts +115 -26
  595. package/src/record/main-pass-sprite-draws.ts +18 -107
  596. package/src/record/main-pass.ts +78 -8
  597. package/src/record/material-uniforms.ts +1 -1
  598. package/src/record/mesh-ssbo.ts +59 -15
  599. package/src/record/prepared-material-bindings.ts +234 -0
  600. package/src/record/recovery-pipeline.ts +59 -136
  601. package/src/record/render-context.ts +92 -22
  602. package/src/record/shadow-pass.ts +1231 -209
  603. package/src/record/skybox-post-pass.ts +14 -3
  604. package/src/record/typed-frame-graph.ts +1006 -53
  605. package/src/record/view-ubo.ts +11 -0
  606. package/src/recovery/render-system-candidate.ts +90 -66
  607. package/src/recovery/render-system-roots.ts +113 -0
  608. package/src/recovery/types.ts +77 -0
  609. package/src/reflection/record-owner.ts +26 -54
  610. package/src/render-contract.ts +219 -25
  611. package/src/render-graph-primitives.ts +6 -2
  612. package/src/render-pipeline.ts +70 -1
  613. package/src/render-system-extract-tail.ts +409 -160
  614. package/src/render-system-extract.ts +376 -102
  615. package/src/render-system-projections.ts +241 -0
  616. package/src/render-system.ts +692 -791
  617. package/src/scene/render-scene-types.ts +13 -4
  618. package/src/scene/render-scene.ts +569 -355
  619. package/src/scene/shadow-visibility.ts +21 -0
  620. package/src/ssr/admission.ts +380 -14
  621. package/src/ssr/compose.ts +144 -0
  622. package/src/ssr/errors.ts +48 -0
  623. package/src/ssr/graph.ts +777 -0
  624. package/src/ssr/history.ts +436 -0
  625. package/src/ssr/hiz.ts +124 -0
  626. package/src/ssr/inspection.ts +124 -0
  627. package/src/ssr/resources.ts +202 -0
  628. package/src/ssr/temporal.ts +164 -0
  629. package/src/systems/skin-palette-allocator.ts +495 -148
  630. package/src/systems/skin-palette-types.ts +17 -0
  631. package/src/systems/transparent-dispatch.ts +90 -0
  632. package/src/targets/material-source.ts +34 -0
  633. package/src/temporal/coverage.ts +1 -1
  634. package/src/temporal/frame-coordinator.ts +23 -2
  635. package/src/temporal/gpu.ts +74 -12
  636. package/src/temporal/history.ts +3 -1
  637. package/src/temporal/inspection.ts +2 -1
  638. package/src/temporal/standard-scene-data.ts +3 -1
  639. package/src/temporal/temporal-view.ts +3 -0
  640. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +204 -139
  641. package/src/typed-render-graph-primitives.ts +266 -74
  642. package/src/typed-shadow-passes.ts +122 -12
  643. package/src/volume/temporal.ts +10 -0
  644. package/dist/chunk-ADHHVYLW.mjs.map +0 -1
  645. package/dist/chunk-FJ6P52EE.mjs.map +0 -1
  646. package/dist/chunk-GE3SDD72.mjs.map +0 -1
  647. package/dist/chunk-GF523LHF.mjs.map +0 -1
  648. package/dist/chunk-HKXTW355.mjs.map +0 -1
  649. package/dist/chunk-JDWARUOI.mjs.map +0 -1
  650. package/dist/chunk-KM2NOX2I.mjs.map +0 -1
  651. package/dist/chunk-TMIASV2N.mjs.map +0 -1
  652. package/dist/chunk-XDP6XE5V.mjs.map +0 -1
  653. package/dist/chunk-ZZ4YQ474.mjs.map +0 -1
  654. package/src/__tests__/instances-world-ownership.integration.test.ts +0 -62
  655. package/src/scene/__tests__/changed-block-summary.candidate.test.ts +0 -36
@@ -25,10 +25,19 @@ import { executeRendererFrameTransaction } from '../assembly/renderer-frame-tran
25
25
  import { type RenderError, RenderFeatureStageFailedError } from '../errors/render';
26
26
  import {
27
27
  getRenderFeaturePlanExecutionProjection,
28
+ hasSubmissionSensitiveFeatures,
28
29
  type RenderFeaturePlanExecution,
29
30
  } from '../features/host';
30
- import { type RenderFeaturePlannedFrame, renderFeaturePlanSignature } from '../features/plan';
31
- import { projectRenderFeaturePlans as projectPlanExecutions } from '../features/render-graph-contribution';
31
+ import {
32
+ type RenderFeaturePlannedFrame,
33
+ renderFeaturePlanSignature,
34
+ renderFeaturePlanSignatureEvidenceMatches,
35
+ } from '../features/plan';
36
+ import {
37
+ createRenderFeatureProjectionState,
38
+ projectRenderFeaturePlans as projectPlanExecutions,
39
+ projectRenderFeatureShadows,
40
+ } from '../features/render-graph-contribution';
32
41
  import type {
33
42
  RenderFeatureGraphBindingsResolution,
34
43
  RenderFeatureGraphTargetResolver,
@@ -40,19 +49,32 @@ import {
40
49
  createFullscreenBindGroup,
41
50
  isTemporalFullscreenBinding,
42
51
  postProcessShaderEntrySignature,
43
- postProcessShaderModuleLabel,
44
52
  } from '../fullscreen-post-process-pass';
45
53
  import type { PreparedGpuDrivenFrame } from '../gpu-driven/production-raster';
46
54
  import {
47
55
  GPU_TEXTURE_USAGE_COPY_DST,
48
56
  GPU_TEXTURE_USAGE_COPY_SRC,
49
57
  GPU_TEXTURE_USAGE_RENDER_ATTACHMENT,
58
+ GPU_TEXTURE_USAGE_STORAGE_BINDING,
50
59
  GPU_TEXTURE_USAGE_TEXTURE_BINDING,
51
60
  } from '../gpu-texture-usage';
61
+ import { GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_UNIFORM } from '../gpu-usage';
62
+ import { createStandardSurfaceLightingBindGroup } from '../hdrp-buffers';
63
+ import type { RenderFeatureGraphInspection } from '../inspection-types';
52
64
  import {
53
65
  type StandardTopologyInputValue,
54
66
  standardLightingTopologySignature,
55
67
  } from '../pipeline/standard-lighting/topology';
68
+ import {
69
+ importAutoExposureGraphResources,
70
+ retireAutoExposureGpuResources,
71
+ } from '../pipeline/standard-output/auto-exposure/gpu';
72
+ import { commitAutoExposureSubmission } from '../pipeline/standard-output/auto-exposure/state';
73
+ import { retireStandardLutGpuResources } from '../pipeline/standard-output/lut-gpu';
74
+ import {
75
+ commitStandardLutCandidate,
76
+ resetStandardLutState,
77
+ } from '../pipeline/standard-output/lut-state';
56
78
  import { resolveVolumetricFogProfile, STANDARD_PIPELINE_ID } from '../pipeline/standard-profile';
57
79
  import type { CameraSnapshot } from '../render-contract';
58
80
  import type {
@@ -62,7 +84,7 @@ import type {
62
84
  } from '../render-pipeline';
63
85
  import type { ExtractedLights, ExtractedVolumetricFog } from '../render-system-extract';
64
86
  import { SHADOW_ATLAS_DEFAULT_FACE_SIZE, SHADOW_ATLAS_DEFAULT_LAYERS } from '../shadow-atlas';
65
- import type { SsrAdmissionResult } from '../ssr/admission';
87
+ import type { SsrAdmissionResult, SsrSpatialAdmission } from '../ssr/admission';
66
88
  import type { RenderTarget } from '../targets/contracts';
67
89
  import type { RenderTargetPhysical } from '../targets/physical';
68
90
  import {
@@ -72,6 +94,7 @@ import {
72
94
  hasPendingTemporalGpuSubmit,
73
95
  retireTemporalGpuState,
74
96
  retireTemporalGpuStateAfterFence,
97
+ TEMPORAL_HISTORY_FORMATS,
75
98
  temporalReadIndex,
76
99
  temporalWriteIndex,
77
100
  } from '../temporal/gpu';
@@ -80,7 +103,12 @@ import type { TransmissionDemand } from '../transmission/projection';
80
103
  import { hasVolumetricFogCapability } from '../volume/capability';
81
104
  import { deriveVolumetricFogExtent, deriveVolumetricFogResolvedExtent } from '../volume/resources';
82
105
  import { buildPerFrameBindGroups } from './frame-lighting';
83
- import { getTextureIdentity, type RenderFrameState } from './frame-snapshot';
106
+ import {
107
+ type GraphTargetPassReplayPrefixReceipt,
108
+ type GraphTargetPassReplayReceipt,
109
+ getTextureIdentity,
110
+ type RenderFrameState,
111
+ } from './frame-snapshot';
84
112
  import type { GpuPassTimingPassIdentity } from './gpu-pass-timing/contract.js';
85
113
  import type { GpuPassTimingCapture, GpuPassTimingSession } from './gpu-pass-timing/session.js';
86
114
  import type { GpuTimingCapture } from './gpu-timing';
@@ -199,13 +227,14 @@ export function commitReflectionFallbackGraph(
199
227
  function importTemporalHistoryTarget(
200
228
  graph: RenderGraphBuilder<RenderPipelineFrame>,
201
229
  label: string,
202
- kind: 'color' | 'temporal',
230
+ kind: 'color' | 'temporal' | 'stability',
203
231
  role: 'read' | 'write',
204
232
  ): import('../render-pipeline').RenderPipelineTarget {
233
+ const format = TEMPORAL_HISTORY_FORMATS[kind];
205
234
  const texture = graph.importTexture(
206
235
  label,
207
236
  {
208
- format: 'rgba16float',
237
+ format,
209
238
  size: 'surface',
210
239
  usage:
211
240
  GPU_TEXTURE_USAGE_COPY_SRC |
@@ -223,7 +252,7 @@ function importTemporalHistoryTarget(
223
252
  frame.targetH,
224
253
  );
225
254
  const index = role === 'read' ? temporalReadIndex(state) : temporalWriteIndex(state);
226
- return (kind === 'color' ? state.color[index] : state.temporal[index]).texture;
255
+ return state[kind][index].texture;
227
256
  },
228
257
  );
229
258
  if (!texture.ok) throw texture.error;
@@ -240,11 +269,11 @@ function importTemporalHistoryTarget(
240
269
  frame.targetH,
241
270
  );
242
271
  const index = role === 'read' ? temporalReadIndex(state) : temporalWriteIndex(state);
243
- return (kind === 'color' ? state.color[index] : state.temporal[index]).view;
272
+ return state[kind][index].view;
244
273
  },
245
274
  );
246
275
  if (!view.ok) throw view.error;
247
- return { texture: texture.value, view: view.value, format: 'rgba16float', sampleCount: 1 };
276
+ return { texture: texture.value, view: view.value, format, sampleCount: 1 };
248
277
  }
249
278
 
250
279
  function importTemporalHistoryTargets(
@@ -254,6 +283,18 @@ function importTemporalHistoryTargets(
254
283
  > {
255
284
  return {
256
285
  currentColor: importTemporalHistoryTarget(graph, 'taa-history-current-color', 'color', 'write'),
286
+ currentStability: importTemporalHistoryTarget(
287
+ graph,
288
+ 'taa-history-current-stability',
289
+ 'stability',
290
+ 'write',
291
+ ),
292
+ previousStability: importTemporalHistoryTarget(
293
+ graph,
294
+ 'taa-history-previous-stability',
295
+ 'stability',
296
+ 'read',
297
+ ),
257
298
  previousColor: importTemporalHistoryTarget(
258
299
  graph,
259
300
  'taa-history-previous-color',
@@ -275,6 +316,110 @@ function importTemporalHistoryTargets(
275
316
  };
276
317
  }
277
318
 
319
+ function importSsrHistoryTarget(
320
+ graph: RenderGraphBuilder<RenderPipelineFrame>,
321
+ label: string,
322
+ role: 'read' | 'write',
323
+ surface = false,
324
+ ): import('../render-pipeline').RenderPipelineTarget {
325
+ const texture = graph.importTexture(
326
+ label,
327
+ {
328
+ format: surface ? 'rgba8unorm' : 'rgba16float',
329
+ size: 'half-surface',
330
+ usage:
331
+ GPU_TEXTURE_USAGE_COPY_SRC |
332
+ GPU_TEXTURE_USAGE_COPY_DST |
333
+ GPU_TEXTURE_USAGE_RENDER_ATTACHMENT |
334
+ GPU_TEXTURE_USAGE_STORAGE_BINDING |
335
+ GPU_TEXTURE_USAGE_TEXTURE_BINDING,
336
+ domain: 'linear-hdr',
337
+ },
338
+ (frame) => {
339
+ const internal = frame as import('./render-context')._InternalRenderPipelineContext;
340
+ const owner = internal.frameState.ssrHistoryOwner;
341
+ const candidate = internal.frameState.ssrHistoryCandidate;
342
+ if (owner === undefined || candidate === undefined) {
343
+ throw new RhiError({
344
+ code: 'rhi-not-available',
345
+ expected: 'SSR history candidate to be staged before graph execution',
346
+ hint: 'begin the SSR history candidate before executing the admitted graph',
347
+ });
348
+ }
349
+ const slot =
350
+ role === 'read'
351
+ ? (candidate.readSlot ?? (candidate.writeSlot === 0 ? 1 : 0))
352
+ : candidate.writeSlot;
353
+ return surface
354
+ ? owner.resources.slots[slot].surfaceTexture
355
+ : owner.resources.slots[slot].texture;
356
+ },
357
+ );
358
+ if (!texture.ok) throw texture.error;
359
+ const view = graph.importView(
360
+ texture.value,
361
+ { label: `${label}.view`, dimension: '2d' },
362
+ (frame) => {
363
+ const internal = frame as import('./render-context')._InternalRenderPipelineContext;
364
+ const owner = internal.frameState.ssrHistoryOwner;
365
+ const candidate = internal.frameState.ssrHistoryCandidate;
366
+ if (owner === undefined || candidate === undefined) {
367
+ throw new RhiError({
368
+ code: 'rhi-not-available',
369
+ expected: 'SSR history candidate to be staged before graph execution',
370
+ hint: 'begin the SSR history candidate before executing the admitted graph',
371
+ });
372
+ }
373
+ const slot =
374
+ role === 'read'
375
+ ? (candidate.readSlot ?? (candidate.writeSlot === 0 ? 1 : 0))
376
+ : candidate.writeSlot;
377
+ return surface ? owner.resources.slots[slot].surfaceView : owner.resources.slots[slot].view;
378
+ },
379
+ );
380
+ if (!view.ok) throw view.error;
381
+ return {
382
+ texture: texture.value,
383
+ view: view.value,
384
+ format: surface ? 'rgba8unorm' : 'rgba16float',
385
+ sampleCount: 1,
386
+ domain: 'linear-hdr',
387
+ };
388
+ }
389
+
390
+ function importSsrHistoryTargets(
391
+ graph: RenderGraphBuilder<RenderPipelineFrame>,
392
+ ): NonNullable<
393
+ import('../render-pipeline').RenderPipelineBuildContext<RenderPipelineFrame>['ssrHistory']
394
+ > {
395
+ return {
396
+ previous: importSsrHistoryTarget(graph, 'ssr-history-previous', 'read'),
397
+ output: importSsrHistoryTarget(graph, 'ssr-history-output', 'write'),
398
+ previousSurface: importSsrHistoryTarget(graph, 'ssr-history-previous-surface', 'read', true),
399
+ outputSurface: importSsrHistoryTarget(graph, 'ssr-history-output-surface', 'write', true),
400
+ params: (() => {
401
+ const imported = graph.importBuffer(
402
+ 'ssr-history-params',
403
+ { size: 32, usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST },
404
+ (frame) => {
405
+ const internal = frame as import('./render-context')._InternalRenderPipelineContext;
406
+ const owner = internal.frameState.ssrHistoryOwner;
407
+ if (owner === undefined) {
408
+ throw new RhiError({
409
+ code: 'rhi-not-available',
410
+ expected: 'SSR history owner to be staged before graph execution',
411
+ hint: 'create the renderer-owned SSR history before executing the admitted graph',
412
+ });
413
+ }
414
+ return owner.resources.paramsBuffer;
415
+ },
416
+ );
417
+ if (!imported.ok) throw imported.error;
418
+ return imported.value;
419
+ })(),
420
+ };
421
+ }
422
+
278
423
  export function shouldUseTemporalFrameTransaction(
279
424
  antialias: CameraSnapshot['antialias'],
280
425
  temporalDemand = false,
@@ -539,7 +684,6 @@ function addCubeCaptureGraphPasses(
539
684
  const captureResolveView = captureResolve?.ok ? captureResolve.value : null;
540
685
  const captureContext = {
541
686
  ...internal,
542
- gpuDrivenEntityKeys: new Set<number>(),
543
687
  camera: current().faceCamera,
544
688
  // Cube faces are raw scene captures, not display-camera HDR output.
545
689
  // Select the PSO against the actual target attachment so an LDR
@@ -788,6 +932,57 @@ export interface RenderFeatureGraphRuntimeState {
788
932
  >;
789
933
  }
790
934
 
935
+ /** The exact fields that define one validated feature-plan collection. */
936
+ export interface RenderFeaturePlanRevisionEntry {
937
+ readonly featureIdentity: string;
938
+ readonly generation: number;
939
+ readonly signature: string;
940
+ readonly order: number;
941
+ }
942
+
943
+ /** Compact graph-owner state for a completely validated feature-plan set. */
944
+ export interface RenderFeaturePlanRevisionState {
945
+ readonly revision: number;
946
+ readonly entries: readonly RenderFeaturePlanRevisionEntry[];
947
+ }
948
+
949
+ /**
950
+ * Advance the plan revision only when the complete canonical plan collection
951
+ * changes. Equality is an exact field comparison; the canonical signatures
952
+ * have already been recomputed by validation before this helper is called.
953
+ */
954
+ export function updateRenderFeaturePlanRevision(
955
+ previous: RenderFeaturePlanRevisionState,
956
+ plans: readonly RenderFeaturePlannedFrame[],
957
+ executions: readonly RenderFeaturePlanExecution[],
958
+ ): RenderFeaturePlanRevisionState {
959
+ if (
960
+ plans.length === previous.entries.length &&
961
+ plans.every((planned, index) => {
962
+ const prior = previous.entries[index];
963
+ return (
964
+ prior !== undefined &&
965
+ prior.featureIdentity === planned.featureIdentity &&
966
+ prior.generation === planned.generation &&
967
+ prior.signature === planned.signature &&
968
+ prior.order === (executions[index]?.order ?? -1)
969
+ );
970
+ })
971
+ ) {
972
+ return previous;
973
+ }
974
+ const entries = plans.map((planned, index) => ({
975
+ featureIdentity: planned.featureIdentity,
976
+ generation: planned.generation,
977
+ signature: planned.signature,
978
+ order: executions[index]?.order ?? -1,
979
+ }));
980
+ return {
981
+ revision: previous.revision + 1,
982
+ entries: Object.freeze(entries),
983
+ };
984
+ }
985
+
791
986
  /** A frame-local graph candidate that is committed only after compilation. */
792
987
  export interface RenderFeatureGraphCandidate {
793
988
  readonly plans: readonly RenderFeaturePlannedFrame[];
@@ -796,16 +991,22 @@ export interface RenderFeatureGraphCandidate {
796
991
  import('../fullscreen-post-process-pass').PostProcessShaderEntry
797
992
  >;
798
993
  /**
799
- * Physical prepared resources are imported into the compiled graph. A new
800
- * prepared batch therefore needs a new graph even when the declarative plan
801
- * signature is unchanged; otherwise queue retirement can destroy the buffer
802
- * still referenced by the memoized graph.
994
+ * Physical transient graphics resources are imported into the compiled
995
+ * graph. Their lease therefore needs a new graph even when the declarative
996
+ * plan signature is unchanged; otherwise queue retirement could destroy a
997
+ * buffer still referenced by the memoized graph. Retired GPU-work leases are
998
+ * kept in the completion batch but are not imported by this candidate and do
999
+ * not require a graph revision on their own.
803
1000
  */
804
1001
  readonly preparedResourceKey?: string;
805
1002
  /** Release candidate-owned prepared resources when graph promotion fails. */
806
1003
  readonly onRejected?: () => void;
807
1004
  /** Publish candidate-owned runtime declarations only after graph promotion. */
808
1005
  readonly onAccepted?: () => void;
1006
+ /** Consume producer frame state only after the command buffer is submitted. */
1007
+ readonly onSubmitted?: () => void;
1008
+ /** Discard producer frame state after graph execution/submission aborts. */
1009
+ readonly onAborted?: () => void;
809
1010
  /** Discard candidate-only resources when a base graph is kept as fallback. */
810
1011
  readonly onAbandoned?: () => void;
811
1012
  }
@@ -813,6 +1014,139 @@ export interface RenderFeatureGraphCandidate {
813
1014
  const featureGraphStates = new WeakMap<RenderSystemInternals, RenderFeatureGraphRuntimeState>();
814
1015
  const graphDeviceGenerations = new WeakMap<object, number>();
815
1016
 
1017
+ interface MutableRenderFeatureGraphInspection {
1018
+ planRevisionState: RenderFeaturePlanRevisionState;
1019
+ signatureValidationCalls: number;
1020
+ signatureValidationBytes: number;
1021
+ signatureValidationChars: number;
1022
+ signatureValidationCpuMs: number;
1023
+ signatureValidationCacheHits: number;
1024
+ signatureValidationCacheMisses: number;
1025
+ ensureCalls: number;
1026
+ reuseHits: number;
1027
+ rebuildAttempts: number;
1028
+ compileAttempts: number;
1029
+ compileSuccesses: number;
1030
+ compileFailures: number;
1031
+ validationFailures: number;
1032
+ buildFailures: number;
1033
+ compileCpuMs: number;
1034
+ candidatesWithPreparedBatches: number;
1035
+ preparedKeyChanges: number;
1036
+ accepted: number;
1037
+ rejected: number;
1038
+ abandoned: number;
1039
+ lastPlanSignature: string;
1040
+ lastTopologyKey: string | undefined;
1041
+ lastPreparedResourceKey: string | undefined;
1042
+ preparedKeyObserved: boolean;
1043
+ }
1044
+
1045
+ const featureGraphInspections = new WeakMap<
1046
+ RenderSystemInternals,
1047
+ MutableRenderFeatureGraphInspection
1048
+ >();
1049
+
1050
+ function mutableFeatureGraphInspection(
1051
+ internals: RenderSystemInternals,
1052
+ ): MutableRenderFeatureGraphInspection {
1053
+ const existing = featureGraphInspections.get(internals);
1054
+ if (existing !== undefined) return existing;
1055
+ const created: MutableRenderFeatureGraphInspection = {
1056
+ planRevisionState: { revision: 0, entries: [] },
1057
+ signatureValidationCalls: 0,
1058
+ signatureValidationBytes: 0,
1059
+ signatureValidationChars: 0,
1060
+ signatureValidationCpuMs: 0,
1061
+ signatureValidationCacheHits: 0,
1062
+ signatureValidationCacheMisses: 0,
1063
+ ensureCalls: 0,
1064
+ reuseHits: 0,
1065
+ rebuildAttempts: 0,
1066
+ compileAttempts: 0,
1067
+ compileSuccesses: 0,
1068
+ compileFailures: 0,
1069
+ validationFailures: 0,
1070
+ buildFailures: 0,
1071
+ compileCpuMs: 0,
1072
+ candidatesWithPreparedBatches: 0,
1073
+ preparedKeyChanges: 0,
1074
+ accepted: 0,
1075
+ rejected: 0,
1076
+ abandoned: 0,
1077
+ lastPlanSignature: '',
1078
+ lastTopologyKey: undefined,
1079
+ lastPreparedResourceKey: undefined,
1080
+ preparedKeyObserved: false,
1081
+ };
1082
+ featureGraphInspections.set(internals, created);
1083
+ return created;
1084
+ }
1085
+
1086
+ export function getRenderFeatureGraphInspection(
1087
+ internals: RenderSystemInternals,
1088
+ ): RenderFeatureGraphInspection {
1089
+ const metrics = mutableFeatureGraphInspection(internals);
1090
+ return Object.freeze({
1091
+ planRevision: metrics.planRevisionState.revision,
1092
+ signatureValidationCalls: metrics.signatureValidationCalls,
1093
+ signatureValidationBytes: metrics.signatureValidationBytes,
1094
+ signatureValidationChars: metrics.signatureValidationChars,
1095
+ signatureValidationCpuMs: metrics.signatureValidationCpuMs,
1096
+ signatureValidationCacheHits: metrics.signatureValidationCacheHits,
1097
+ signatureValidationCacheMisses: metrics.signatureValidationCacheMisses,
1098
+ ensureCalls: metrics.ensureCalls,
1099
+ reuseHits: metrics.reuseHits,
1100
+ rebuildAttempts: metrics.rebuildAttempts,
1101
+ compileAttempts: metrics.compileAttempts,
1102
+ compileSuccesses: metrics.compileSuccesses,
1103
+ compileFailures: metrics.compileFailures,
1104
+ validationFailures: metrics.validationFailures,
1105
+ buildFailures: metrics.buildFailures,
1106
+ compileCpuMs: metrics.compileCpuMs,
1107
+ candidatesWithPreparedBatches: metrics.candidatesWithPreparedBatches,
1108
+ preparedKeyChanges: metrics.preparedKeyChanges,
1109
+ accepted: metrics.accepted,
1110
+ rejected: metrics.rejected,
1111
+ abandoned: metrics.abandoned,
1112
+ last: Object.freeze({
1113
+ planSignature: metrics.lastPlanSignature,
1114
+ topologyKey: metrics.lastTopologyKey,
1115
+ preparedResourceKey: metrics.lastPreparedResourceKey,
1116
+ }),
1117
+ });
1118
+ }
1119
+
1120
+ export function recordRenderFeatureCandidate(
1121
+ internals: RenderSystemInternals,
1122
+ candidate: RenderFeatureGraphCandidate,
1123
+ ): void {
1124
+ const metrics = mutableFeatureGraphInspection(internals);
1125
+ if (
1126
+ metrics.preparedKeyObserved &&
1127
+ metrics.lastPreparedResourceKey !== candidate.preparedResourceKey
1128
+ ) {
1129
+ metrics.preparedKeyChanges += 1;
1130
+ }
1131
+ metrics.preparedKeyObserved = true;
1132
+ metrics.lastPreparedResourceKey = candidate.preparedResourceKey;
1133
+ if (candidate.preparedResourceKey !== undefined) {
1134
+ metrics.candidatesWithPreparedBatches += 1;
1135
+ }
1136
+ }
1137
+
1138
+ function recordRenderFeatureCandidateEvent(
1139
+ internals: RenderSystemInternals,
1140
+ candidate: RenderFeatureGraphCandidate,
1141
+ event: 'accepted' | 'rejected' | 'abandoned',
1142
+ ): void {
1143
+ const metrics = mutableFeatureGraphInspection(internals);
1144
+ metrics[event] += 1;
1145
+ if (event === 'accepted') candidate.onAccepted?.();
1146
+ else if (event === 'rejected') candidate.onRejected?.();
1147
+ else candidate.onAbandoned?.();
1148
+ }
1149
+
816
1150
  interface TimingInstrumentationState {
817
1151
  readonly instrumentation: RenderGraphPassInstrumentation<RenderPipelineFrame>;
818
1152
  setCapture(capture: GpuPassTimingCapture | undefined): void;
@@ -831,6 +1165,11 @@ type CompiledGraphTargetAccess = CompiledRenderGraph<RenderPipelineFrame> & {
831
1165
  readonly getColorTargetTexture: (name: string) => Texture | undefined;
832
1166
  };
833
1167
 
1168
+ type ResolvedDepthPassExecution =
1169
+ import('@forgeax/engine-render-graph').RenderGraphPassExecution & {
1170
+ readonly resolvedDepthStencilAttachmentView?: TextureView;
1171
+ };
1172
+
834
1173
  export function getRenderFeatureGraphState(
835
1174
  internals: RenderSystemInternals,
836
1175
  ): RenderFeatureGraphRuntimeState {
@@ -900,15 +1239,46 @@ export function resolvePostProcessChainAdmission(
900
1239
 
901
1240
  function validateRenderFeaturePlans(
902
1241
  plans: readonly RenderFeaturePlannedFrame[],
1242
+ metrics?: MutableRenderFeatureGraphInspection,
903
1243
  ): Result<readonly RenderFeaturePlanExecution[], RenderError> {
904
1244
  const identities = new Set<string>();
905
1245
  const projected: RenderFeaturePlanExecution[] = [];
906
1246
  let previousOrder = -1;
907
1247
  for (const [order, planned] of plans.entries()) {
908
1248
  const execution = getRenderFeaturePlanExecutionProjection(planned);
1249
+ const signatureMetrics =
1250
+ metrics === undefined ? undefined : { calls: 0, typedArrayBytes: 0, outputChars: 0 };
1251
+ const started =
1252
+ metrics === undefined
1253
+ ? 0
1254
+ : typeof performance === 'undefined'
1255
+ ? Date.now()
1256
+ : performance.now();
1257
+ // The host records detached structural evidence, but graph admission must
1258
+ // check it again so a mutation between the two synchronous stages cannot
1259
+ // smuggle a stale plan into the compiled graph. Only candidates without
1260
+ // evidence fall back to the canonical serializer.
1261
+ const signatureCacheHit = renderFeaturePlanSignatureEvidenceMatches(
1262
+ planned.plan,
1263
+ planned.signature,
1264
+ );
1265
+ if (metrics !== undefined) {
1266
+ if (signatureCacheHit) metrics.signatureValidationCacheHits += 1;
1267
+ else metrics.signatureValidationCacheMisses += 1;
1268
+ }
1269
+ const recomputedSignature = signatureCacheHit
1270
+ ? planned.signature
1271
+ : renderFeaturePlanSignature(planned.plan, signatureMetrics);
1272
+ if (metrics !== undefined) {
1273
+ const now = typeof performance === 'undefined' ? Date.now() : performance.now();
1274
+ metrics.signatureValidationCalls += signatureMetrics?.calls ?? 0;
1275
+ metrics.signatureValidationBytes += signatureMetrics?.typedArrayBytes ?? 0;
1276
+ metrics.signatureValidationChars += signatureMetrics?.outputChars ?? 0;
1277
+ metrics.signatureValidationCpuMs += now - started;
1278
+ }
909
1279
  if (
910
1280
  identities.has(planned.featureIdentity) ||
911
- planned.signature !== renderFeaturePlanSignature(planned.plan) ||
1281
+ planned.signature !== recomputedSignature ||
912
1282
  execution === undefined ||
913
1283
  execution.featureIdentity !== planned.featureIdentity ||
914
1284
  execution.order <= previousOrder
@@ -931,7 +1301,7 @@ function commitFeatureCandidate(
931
1301
  // Publish renderer-owned declarations only after their device resources have
932
1302
  // been admitted. If the acceptance hook throws, the previously accepted
933
1303
  // graph/state pair remains visible to the caller.
934
- candidate.onAccepted?.();
1304
+ recordRenderFeatureCandidateEvent(internals, candidate, 'accepted');
935
1305
  const state = getRenderFeatureGraphState(internals);
936
1306
  state.plans = candidate.plans;
937
1307
  state.fullscreenEffects = candidate.fullscreenEffects;
@@ -949,10 +1319,12 @@ function topologyOf(
949
1319
  shadowMapSize: number | undefined,
950
1320
  gpuDriven: PreparedGpuDrivenFrame | undefined,
951
1321
  featureGraphCandidate: RenderFeatureGraphCandidate | undefined,
1322
+ featurePlanRevision: number,
952
1323
  cubeCaptureState: CubeCaptureGraphState | undefined,
953
1324
  transmissionDemand: TransmissionDemand | undefined,
954
1325
  volumetricFog: ExtractedVolumetricFog | undefined,
955
1326
  standardLighting: StandardTopologyInputValue | undefined,
1327
+ ssr: SsrSpatialAdmission | undefined,
956
1328
  ): Result<RenderPipelineTopology, RhiError> {
957
1329
  const cascadeCount = Math.max(1, Math.min(4, Math.round(lights.cascadeCount ?? 1))) as
958
1330
  | 1
@@ -1008,12 +1380,15 @@ function topologyOf(
1008
1380
  : volumetricFog?.lightKind === undefined
1009
1381
  ? lights.directional
1010
1382
  : undefined;
1383
+ const preparedLut =
1384
+ frameState.pendingStandardLutGpuResources ?? frameState.standardLutGpuResources;
1011
1385
  return ok({
1012
1386
  pipelineId: STANDARD_PIPELINE_ID,
1013
1387
  standardProfile: internals.standardProfile,
1014
1388
  config,
1015
1389
  clearOnly,
1016
1390
  reflectionFallback: { enabled: frameState.reflectionFallbackDemand === true },
1391
+ ssr,
1017
1392
  surface: {
1018
1393
  width,
1019
1394
  height,
@@ -1026,6 +1401,27 @@ function topologyOf(
1026
1401
  antialias: camera.antialias,
1027
1402
  bloom: camera.bloom,
1028
1403
  },
1404
+ atmosphere: !clearOnly && frameState.environmentFrame?.source.kind === 'atmosphere',
1405
+ output: {
1406
+ autoExposure:
1407
+ !clearOnly &&
1408
+ camera.output?.exposure.kind === 'auto' &&
1409
+ (frameState.pendingAutoExposureGpuResources ?? frameState.autoExposureGpuResources) !==
1410
+ undefined,
1411
+ whiteBalance:
1412
+ !clearOnly &&
1413
+ camera.output !== undefined &&
1414
+ (camera.output.temperature !== 6504 || camera.output.tint !== 0),
1415
+ temperature: camera.output?.temperature ?? 6504,
1416
+ tint: camera.output?.tint ?? 0,
1417
+ colorLut:
1418
+ !clearOnly &&
1419
+ (camera.output?.colorLutStrength ?? 0) > 0 &&
1420
+ (frameState.pendingStandardLutGpuResources ?? frameState.standardLutGpuResources) !==
1421
+ undefined,
1422
+ colorLutStrength: camera.output?.colorLutStrength ?? 0,
1423
+ ...(preparedLut === undefined ? {} : { lutSourceKey: preparedLut.sourceKey }),
1424
+ },
1029
1425
  temporal: {
1030
1426
  taa: camera.antialias === 'taa',
1031
1427
  motionBlur: isMotionBlurTemporalDemand(camera.motionBlur),
@@ -1093,9 +1489,7 @@ function topologyOf(
1093
1489
  maxColorAttachments: internals.device.caps.maxColorAttachments,
1094
1490
  },
1095
1491
  featureTopologySignature: JSON.stringify({
1096
- plans: renderFeatureGraphPlanSignature(
1097
- featureGraphCandidate?.plans ?? getRenderFeatureGraphState(internals).plans,
1098
- ),
1492
+ planRevision: featurePlanRevision,
1099
1493
  fullscreenEffects: featurePostEffectDeclarations,
1100
1494
  preparedResourceKey: featureGraphCandidate?.preparedResourceKey ?? '',
1101
1495
  cubeCaptureSlots: cubeCaptureState?.work.length ?? 0,
@@ -1264,7 +1658,9 @@ function resolveTargetBindings(
1264
1658
  frame.runtime as RenderSystemInternals,
1265
1659
  frame.frameState,
1266
1660
  frame.pipelineState,
1267
- frame.validated.length > 0,
1661
+ // This callback is reached only for an actual feature draw. A scene
1662
+ // containing only particles still needs the pipeline's View/light ABI.
1663
+ true,
1268
1664
  frame.bindGroupCounts,
1269
1665
  {
1270
1666
  directionalShadow: frame.frameState.currentDirectionalShadowView ?? undefined,
@@ -1351,23 +1747,41 @@ export function ensureCompiledFrameGraph(
1351
1747
  volumeTopologyCandidate = false,
1352
1748
  standardLighting?: StandardTopologyInputValue,
1353
1749
  onRecoveryError?: (error: unknown) => void,
1750
+ ssr?: SsrSpatialAdmission,
1354
1751
  ): CompiledRenderGraph<RenderPipelineFrame> | null {
1752
+ const featureMetrics = mutableFeatureGraphInspection(internals);
1753
+ featureMetrics.ensureCalls += 1;
1355
1754
  const requestedStandardLightingSignature = standardLightingTopologySignature(standardLighting);
1356
1755
  const lastKnownGood = (): CompiledRenderGraph<RenderPipelineFrame> | null =>
1357
1756
  frameState.compiledFrameGraph !== null &&
1757
+ !hasSubmissionSensitiveFeatures(getRenderFeatureGraphState(internals).plans) &&
1358
1758
  frameState.standardLightingGraphSignature === requestedStandardLightingSignature
1359
1759
  ? frameState.compiledFrameGraph
1360
1760
  : null;
1361
1761
  const surfaceWidth = Math.max(1, width);
1362
1762
  const surfaceHeight = Math.max(1, height);
1363
1763
  const featurePlans = featureGraphCandidate?.plans ?? getRenderFeatureGraphState(internals).plans;
1364
- const projectedFeaturePlans = validateRenderFeaturePlans(featurePlans);
1764
+ const projectedFeaturePlans = validateRenderFeaturePlans(featurePlans, featureMetrics);
1365
1765
  if (!projectedFeaturePlans.ok) {
1766
+ featureMetrics.validationFailures += 1;
1366
1767
  onRecoveryError?.(projectedFeaturePlans.error);
1367
1768
  reportRenderFeatureGraphError(internals, projectedFeaturePlans.error);
1368
- featureGraphCandidate?.onRejected?.();
1769
+ if (featureGraphCandidate !== undefined) {
1770
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
1771
+ }
1369
1772
  return volumeTopologyCandidate ? null : lastKnownGood();
1370
1773
  }
1774
+ const nextPlanRevision = updateRenderFeaturePlanRevision(
1775
+ featureMetrics.planRevisionState,
1776
+ featurePlans,
1777
+ projectedFeaturePlans.value,
1778
+ );
1779
+ if (nextPlanRevision !== featureMetrics.planRevisionState) {
1780
+ featureMetrics.lastPlanSignature = nextPlanRevision.entries
1781
+ .map((entry) => `${entry.featureIdentity}:${entry.signature}`)
1782
+ .join('|');
1783
+ featureMetrics.planRevisionState = nextPlanRevision;
1784
+ }
1371
1785
  const topologyResult = topologyOf(
1372
1786
  internals,
1373
1787
  frameState,
@@ -1380,15 +1794,20 @@ export function ensureCompiledFrameGraph(
1380
1794
  shadowMapSize,
1381
1795
  gpuDriven,
1382
1796
  featureGraphCandidate,
1797
+ featureMetrics.planRevisionState.revision,
1383
1798
  cubeCaptureState,
1384
1799
  transmissionDemand,
1385
1800
  volumetricFog,
1386
1801
  standardLighting,
1802
+ ssr,
1387
1803
  );
1388
1804
  if (!topologyResult.ok) {
1805
+ featureMetrics.buildFailures += 1;
1389
1806
  onRecoveryError?.(topologyResult.error);
1390
1807
  internals.errorRegistry.fire(topologyResult.error);
1391
- featureGraphCandidate?.onRejected?.();
1808
+ if (featureGraphCandidate !== undefined) {
1809
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
1810
+ }
1392
1811
  return volumeTopologyCandidate ? null : lastKnownGood();
1393
1812
  }
1394
1813
  const requestedPostEffects = topologyResult.value.config?.postEffects ?? [];
@@ -1417,7 +1836,7 @@ export function ensureCompiledFrameGraph(
1417
1836
  getPipeline(
1418
1837
  identity,
1419
1838
  built.bindGroupLayout,
1420
- topologyResult.value.surface.storageFormat as GPUTextureFormat,
1839
+ [topologyResult.value.surface.storageFormat as GPUTextureFormat],
1421
1840
  entry,
1422
1841
  ) !== null
1423
1842
  );
@@ -1428,7 +1847,9 @@ export function ensureCompiledFrameGraph(
1428
1847
  // Do not publish a candidate whose fullscreen chain is only partially
1429
1848
  // ready. Keep the accepted graph (including its prior post-effect chain)
1430
1849
  // and retry the same feature candidate on the next frame.
1431
- featureGraphCandidate?.onRejected?.();
1850
+ if (featureGraphCandidate !== undefined) {
1851
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
1852
+ }
1432
1853
  return acceptedLkg;
1433
1854
  }
1434
1855
  const topology = postProcessAdmission.pending
@@ -1442,6 +1863,12 @@ export function ensureCompiledFrameGraph(
1442
1863
  : topologyResult.value;
1443
1864
  const key = JSON.stringify({
1444
1865
  topology,
1866
+ // Observation domains are an explicit per-frame graph demand. They are
1867
+ // part of the cache identity so a request made after the ordinary graph
1868
+ // was compiled can admit the matching tone/output capture boundaries on
1869
+ // that receipt without forcing every unobserved frame onto the split
1870
+ // output path.
1871
+ observationCaptureDomains: [...(internals.observationCaptureDomains ?? [])].sort(),
1445
1872
  // GPU-driven residency can become available after the first record pass
1446
1873
  // (custom mesh handles are resolved into the per-frame projection). The
1447
1874
  // topology signature alone is intentionally stable across that lifecycle,
@@ -1462,11 +1889,13 @@ export function ensureCompiledFrameGraph(
1462
1889
  ]),
1463
1890
  });
1464
1891
  if (frameState.compiledFrameGraph !== null && frameState.compiledFrameGraphTopologyKey === key) {
1892
+ featureMetrics.reuseHits += 1;
1893
+ featureMetrics.lastTopologyKey = key;
1465
1894
  frameState.standardLightingGraphSignature = requestedStandardLightingSignature;
1466
1895
  if (featureGraphCandidate !== undefined && !postProcessAdmission.pending) {
1467
1896
  commitFeatureCandidate(internals, featureGraphCandidate);
1468
1897
  } else if (featureGraphCandidate !== undefined) {
1469
- featureGraphCandidate.onAbandoned?.();
1898
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'abandoned');
1470
1899
  }
1471
1900
  // A candidate flag only describes a possible topology replacement. When
1472
1901
  // the derived key is unchanged there is no replacement to stage: reuse the
@@ -1475,17 +1904,109 @@ export function ensureCompiledFrameGraph(
1475
1904
  return frameState.compiledFrameGraph;
1476
1905
  }
1477
1906
 
1907
+ featureMetrics.rebuildAttempts += 1;
1908
+ featureMetrics.lastTopologyKey = key;
1909
+ const discardPendingStandardOutput = (): void => {
1910
+ if (frameState.pendingAutoExposureGpuResources !== undefined) {
1911
+ retireAutoExposureGpuResources(frameState.pendingAutoExposureGpuResources);
1912
+ frameState.pendingAutoExposureGpuResources = undefined;
1913
+ }
1914
+ if (frameState.pendingStandardLutGpuResources !== undefined) {
1915
+ retireStandardLutGpuResources(frameState.pendingStandardLutGpuResources);
1916
+ frameState.pendingStandardLutGpuResources = undefined;
1917
+ }
1918
+ };
1478
1919
  const builder = new RenderGraphBuilder<RenderPipelineFrame>();
1920
+ const autoExposureResources =
1921
+ topology.output?.autoExposure === true ? importAutoExposureGraphResources(builder) : undefined;
1922
+ if (autoExposureResources !== undefined && !autoExposureResources.ok) {
1923
+ internals.errorRegistry.fire(autoExposureResources.error);
1924
+ featureGraphCandidate?.onRejected?.();
1925
+ discardPendingStandardOutput();
1926
+ return lastKnownGood();
1927
+ }
1928
+ const lutResources =
1929
+ topology.output?.colorLut === true
1930
+ ? (() => {
1931
+ const prepared =
1932
+ frameState.pendingStandardLutGpuResources ?? frameState.standardLutGpuResources;
1933
+ if (prepared === undefined) return undefined;
1934
+ const texture = builder.importTexture(
1935
+ 'standard-color-lut-source',
1936
+ {
1937
+ format: 'rgba16float',
1938
+ size: {
1939
+ width: prepared.size,
1940
+ height: prepared.size,
1941
+ depthOrArrayLayers: prepared.size,
1942
+ },
1943
+ dimension: '3d',
1944
+ usage: GPU_TEXTURE_USAGE_TEXTURE_BINDING,
1945
+ },
1946
+ (frame) =>
1947
+ (
1948
+ (frame as _InternalRenderPipelineContext).frameState
1949
+ .pendingStandardLutGpuResources ??
1950
+ (frame as _InternalRenderPipelineContext).frameState.standardLutGpuResources
1951
+ )?.texture ?? prepared.texture,
1952
+ );
1953
+ if (!texture.ok) return texture;
1954
+ const view = builder.importView(
1955
+ texture.value,
1956
+ { label: 'standard-color-lut-source.view', dimension: '3d' },
1957
+ (frame) =>
1958
+ (
1959
+ (frame as _InternalRenderPipelineContext).frameState
1960
+ .pendingStandardLutGpuResources ??
1961
+ (frame as _InternalRenderPipelineContext).frameState.standardLutGpuResources
1962
+ )?.view ?? prepared.view,
1963
+ );
1964
+ if (!view.ok) return view;
1965
+ return ok({ texture: texture.value, view: view.value, prepared });
1966
+ })()
1967
+ : undefined;
1968
+ if (lutResources !== undefined && !lutResources.ok) {
1969
+ internals.errorRegistry.fire(lutResources.error);
1970
+ featureGraphCandidate?.onRejected?.();
1971
+ discardPendingStandardOutput();
1972
+ return lastKnownGood();
1973
+ }
1479
1974
  const taaHistory = camera.antialias === 'taa' ? importTemporalHistoryTargets(builder) : undefined;
1975
+ const ssrHistory = ssr?.status === 'admitted' ? importSsrHistoryTargets(builder) : undefined;
1480
1976
  let projectedFeatures = false;
1977
+ const featureProjection = createRenderFeatureProjectionState();
1978
+ let featureShadows:
1979
+ | ReturnType<typeof projectRenderFeatureShadows<RenderPipelineFrame>>
1980
+ | undefined;
1481
1981
  let projectedGpuDriven = false;
1482
1982
  const built = frameState.activePipeline.build(
1483
1983
  {
1484
1984
  graph: builder,
1985
+ observationCaptureDomains: internals.observationCaptureDomains,
1986
+ ...(autoExposureResources?.ok === true
1987
+ ? { standardOutput: { autoExposure: autoExposureResources.value } }
1988
+ : {}),
1989
+ ...(lutResources?.ok === true
1990
+ ? {
1991
+ standardOutput: {
1992
+ ...(autoExposureResources?.ok === true
1993
+ ? { autoExposure: autoExposureResources.value }
1994
+ : {}),
1995
+ colorLut: {
1996
+ view: lutResources.value.view,
1997
+ sampler: lutResources.value.prepared.sampler,
1998
+ strength: lutResources.value.prepared.strength,
1999
+ bindGroupLayout: lutResources.value.prepared.bindGroupLayout,
2000
+ bindGroup: lutResources.value.prepared.bindGroup,
2001
+ },
2002
+ },
2003
+ }
2004
+ : {}),
1485
2005
  ...(standardLighting === undefined ? {} : { standardLighting }),
1486
2006
  capabilities: { rgba16floatRenderable: internals.device.caps.rgba16floatRenderable },
1487
2007
  ...(occlusion === undefined ? {} : { occlusion }),
1488
2008
  ...(taaHistory === undefined ? {} : { taaHistory }),
2009
+ ...(ssrHistory === undefined ? {} : { ssrHistory }),
1489
2010
  encodeTransmissionMip: ({ pass, resources, source }) => {
1490
2011
  const sourceView = resources.textureView(source);
1491
2012
  if (!sourceView.ok) throw sourceView.error;
@@ -1497,14 +2018,53 @@ export function ensureCompiledFrameGraph(
1497
2018
  projectedGpuDriven = true;
1498
2019
  return gpuDriven.project(builder, target.format, target.sampleCount);
1499
2020
  },
1500
- contributeFeatures: (targets, semanticTargets = [], namedTargets = {}) => {
2021
+ ...(gpuDriven?.projectShadow === undefined
2022
+ ? {}
2023
+ : {
2024
+ projectGpuDrivenShadow: (identity) =>
2025
+ gpuDriven.projectShadow?.(builder, identity) ?? ok(undefined),
2026
+ }),
2027
+ contributeShadowFeatures: () =>
2028
+ (featureShadows ??= projectRenderFeatureShadows(
2029
+ builder,
2030
+ projectedFeaturePlans.value,
2031
+ featureProjection,
2032
+ )),
2033
+ contributeFeatures: (
2034
+ targets,
2035
+ semanticTargets = [],
2036
+ namedTargets = {},
2037
+ standardSurfaceAccesses = [],
2038
+ ) => {
1501
2039
  if (projectedFeatures) return ok(undefined);
1502
2040
  projectedFeatures = true;
1503
2041
  if (projectedFeaturePlans.value.length === 0) return ok(undefined);
1504
2042
  const resolveTarget = targetResolver(targets, semanticTargets, namedTargets);
1505
2043
  const projected = projectPlanExecutions(builder, projectedFeaturePlans.value, {
2044
+ state: featureProjection,
1506
2045
  resolveTarget,
2046
+ standardSurfaceAccesses,
1507
2047
  resolveBindings: (input) => resolveTargetBindings(input, featurePostProcessEntries),
2048
+ resolveStandardLighting: (frame, pipeline) => {
2049
+ const context = frame as import('./render-context')._InternalRenderPipelineContext;
2050
+ if (context.standardLighting?.kind !== 'clustered') return undefined;
2051
+ const layout = (
2052
+ pipeline as RenderPipeline & {
2053
+ getBindGroupLayout?: (index: number) => BindGroupLayout;
2054
+ }
2055
+ ).getBindGroupLayout?.(2);
2056
+ if (layout === undefined) return undefined;
2057
+ return (
2058
+ createStandardSurfaceLightingBindGroup(
2059
+ context.runtime,
2060
+ layout,
2061
+ context.standardLighting.prepared.layout.grid,
2062
+ context.hdrpSsaoBlurredView === undefined
2063
+ ? { enabled: false }
2064
+ : { enabled: true, ssaoBlurredView: context.hdrpSsaoBlurredView },
2065
+ ) ?? undefined
2066
+ );
2067
+ },
1508
2068
  reportError: (error) => reportRenderFeatureGraphError(internals, error),
1509
2069
  });
1510
2070
  return projected.ok ? ok(undefined) : projected;
@@ -1525,19 +2085,33 @@ export function ensureCompiledFrameGraph(
1525
2085
  topology,
1526
2086
  );
1527
2087
  if (!built.ok) {
2088
+ featureMetrics.buildFailures += 1;
1528
2089
  onRecoveryError?.(built.error);
1529
2090
  internals.errorRegistry.fire(built.error);
1530
2091
  featureGraphCandidate?.onRejected?.();
2092
+ discardPendingStandardOutput();
2093
+ if (featureGraphCandidate !== undefined) {
2094
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
2095
+ }
1531
2096
  return volumeTopologyCandidate ? null : lastKnownGood();
1532
2097
  }
2098
+ featureMetrics.compileAttempts += 1;
2099
+ const compileStarted = typeof performance === 'undefined' ? Date.now() : performance.now();
1533
2100
  const compiled = builder.compile({
1534
2101
  device: internals.device,
1535
2102
  surfaceSize: { width: surfaceWidth, height: surfaceHeight },
1536
2103
  });
2104
+ featureMetrics.compileCpuMs +=
2105
+ (typeof performance === 'undefined' ? Date.now() : performance.now()) - compileStarted;
1537
2106
  if (!compiled.ok) {
2107
+ featureMetrics.compileFailures += 1;
1538
2108
  onRecoveryError?.(compiled.error);
1539
2109
  internals.errorRegistry.fire(compiled.error);
1540
2110
  featureGraphCandidate?.onRejected?.();
2111
+ discardPendingStandardOutput();
2112
+ if (featureGraphCandidate !== undefined) {
2113
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'rejected');
2114
+ }
1541
2115
  return lastKnownGood();
1542
2116
  }
1543
2117
  const previous = frameState.compiledFrameGraph;
@@ -1552,21 +2126,31 @@ export function ensureCompiledFrameGraph(
1552
2126
  frameState.compiledFrameGraphGeneration += 1;
1553
2127
  const compiledTargets = compiled.value as CompiledGraphTargetAccess;
1554
2128
  frameState.graphGeneration += 1;
2129
+ featureMetrics.compileSuccesses += 1;
1555
2130
  frameState.perFrameGraph = {
1556
2131
  getColorTargetDescriptor: (name) => compiledTargets.getColorTargetDescriptor(name),
1557
2132
  getColorTargetView: (name) => compiledTargets.getColorTargetView(name),
1558
2133
  getColorTargetTexture: (name) => compiledTargets.getColorTargetTexture(name),
1559
2134
  graphGeneration: frameState.graphGeneration,
1560
2135
  };
2136
+ // View buffer replacement is safe to retire once the compiled graph owns
2137
+ // the new imports. Shadow cache publication remains submit-transactional
2138
+ // and is committed only by executeCompiledFrameGraph's onSubmitted hook.
2139
+ if (gpuDriven?._commitGpuResourceReplacement !== undefined) {
2140
+ gpuDriven._commitGpuResourceReplacement();
2141
+ } else {
2142
+ // Synthetic/internal prepared projections predating the split own no
2143
+ // shadow cache; retain their original graph-promotion callback.
2144
+ gpuDriven?._commitResourceReplacement();
2145
+ }
1561
2146
  const deviceGeneration = internals.deviceScope?.generation;
1562
2147
  if (deviceGeneration !== undefined) {
1563
2148
  graphDeviceGenerations.set(compiled.value, deviceGeneration);
1564
2149
  }
1565
- gpuDriven?._commitResourceReplacement();
1566
2150
  if (featureGraphCandidate !== undefined && !postProcessAdmission.pending) {
1567
2151
  commitFeatureCandidate(internals, featureGraphCandidate);
1568
2152
  } else if (featureGraphCandidate !== undefined) {
1569
- featureGraphCandidate.onAbandoned?.();
2153
+ recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'abandoned');
1570
2154
  }
1571
2155
  if (!volumeTopologyCandidate && previous !== null) retire(frameState, previous);
1572
2156
  return compiled.value;
@@ -1578,22 +2162,22 @@ export function executeCompiledFrameGraph(
1578
2162
  frame: RenderPipelineFrame,
1579
2163
  encoder: RhiCommandEncoder,
1580
2164
  runPass?: import('@forgeax/engine-render-graph').RenderGraphPassRunner,
1581
- frameHooks?: {
1582
- readonly afterGraphExecute?: () => Result<void, RhiError>;
1583
- readonly onSubmitted?: () => void;
1584
- readonly onAborted?: () => void;
1585
- },
2165
+ frameHooks?:
2166
+ | {
2167
+ readonly afterGraphExecute?: () => Result<void, RhiError>;
2168
+ readonly onSubmitted?: () => void;
2169
+ readonly onAborted?: () => void;
2170
+ }
2171
+ // Keep the pre-submit-hook shape accepted for internal callers that have
2172
+ // not yet migrated to the typed frame hooks object.
2173
+ | (() => void),
1586
2174
  readbackFaces?: readonly number[],
1587
2175
  onSubmitted?: (completed: Promise<unknown>) => void,
1588
2176
  timingCapture?: GpuTimingCapture,
1589
2177
  ): boolean {
2178
+ const legacySubmitCommit = typeof frameHooks === 'function' ? frameHooks : undefined;
2179
+ const resolvedFrameHooks = typeof frameHooks === 'function' ? undefined : frameHooks;
1590
2180
  const graph = frameState.compiledFrameGraph;
1591
- const invalidatePostProcessModule = (id: string): void => {
1592
- const entry = internals.lookupPostProcess?.(id);
1593
- if (entry !== undefined) {
1594
- internals.invalidateShaderModule?.(postProcessShaderModuleLabel(id, entry.source));
1595
- }
1596
- };
1597
2181
  const rejectTemporalFrame = (): void => {
1598
2182
  if (frameState.temporalFrameInput === undefined) return;
1599
2183
  frameState.temporalFrameTransaction.commit({ accepted: false });
@@ -1601,6 +2185,8 @@ export function executeCompiledFrameGraph(
1601
2185
  };
1602
2186
  if (graph === null) {
1603
2187
  timingCapture?.discard();
2188
+ frameState.pendingAutoExposureState = undefined;
2189
+ frameState.pendingStandardLutState = undefined;
1604
2190
  settleVolumetricFogGraphCandidate(frameState, false);
1605
2191
  rejectTemporalFrame();
1606
2192
  return false;
@@ -1671,14 +2257,327 @@ export function executeCompiledFrameGraph(
1671
2257
  const transaction = executeRendererFrameTransaction<PreparedCommand>({
1672
2258
  build: () => ({ ok: true, value: { command: undefined as never } }),
1673
2259
  execute: () => {
1674
- const executed = graph.execute(frame, runPass, graphInstrumentation);
1675
- timingInstrumentationState?.setCapture(undefined);
1676
- if (!executed.ok) {
1677
- passTimingCapture?.abort({ code: executed.error.code });
1678
- internals.errorRegistry.fire(executed.error);
1679
- return { ok: false, stage: 'execute' };
2260
+ const graphCapture = frameState.graphTargetCapture;
2261
+ const replayPassNames = graphCapture?.replayPassNames;
2262
+ const unsupportedCaptureKind =
2263
+ graphCapture !== undefined &&
2264
+ graphCapture.kind !== undefined &&
2265
+ graphCapture.kind !== 'target-copy' &&
2266
+ graphCapture.kind !== 'pass-replay';
2267
+ if (unsupportedCaptureKind || replayPassNames !== undefined) {
2268
+ if (graphCapture === undefined) {
2269
+ return { ok: false, stage: 'execute' };
2270
+ }
2271
+ const passNames = graph.inspect().passes.map((pass) => pass.name);
2272
+ const passNameCounts = new Map<string, number>();
2273
+ for (const passName of passNames) {
2274
+ passNameCounts.set(passName, (passNameCounts.get(passName) ?? 0) + 1);
2275
+ }
2276
+ const passNameSet = new Set(passNames);
2277
+ const selectedReplayPassNames = replayPassNames ?? [];
2278
+ const requestedPassNames = new Set(selectedReplayPassNames);
2279
+ const passReplayCapture = graphCapture.kind === 'pass-replay' ? graphCapture : undefined;
2280
+ const onPassEncoded = passReplayCapture?.onPassEncoded;
2281
+ const onPrefixEncoded = passReplayCapture?.onPrefixEncoded;
2282
+ const depthAttachmentPassName = passReplayCapture?.depthAttachmentPassName;
2283
+ const observeAfterPassName = passReplayCapture?.observeAfterPassName;
2284
+ let replayValidationFailed = false;
2285
+ const duplicatePassName = selectedReplayPassNames.find(
2286
+ (passName, index) => selectedReplayPassNames.indexOf(passName) !== index,
2287
+ );
2288
+ const invalidPassName = selectedReplayPassNames.find(
2289
+ (passName) => !passNameSet.has(passName),
2290
+ );
2291
+ const nonUniquePassName = selectedReplayPassNames.find(
2292
+ (passName) => passNameCounts.get(passName) !== 1,
2293
+ );
2294
+ const replayFailure = (reason: string): void => {
2295
+ replayValidationFailed = true;
2296
+ internals.errorRegistry.fire(
2297
+ new RhiError({
2298
+ code: 'webgpu-runtime-error',
2299
+ expected:
2300
+ 'the requested graph target replay pass selection is valid and executes once',
2301
+ hint: 'inspect replayPassNames against the compiled graph pass roster',
2302
+ detail: {
2303
+ error: {
2304
+ code: 'graph-target-capture-failed',
2305
+ message: JSON.stringify({
2306
+ target: graphCapture.kind === 'pass-replay' ? undefined : graphCapture.name,
2307
+ replayPassNames,
2308
+ availablePassNames: passNames,
2309
+ reason,
2310
+ }),
2311
+ },
2312
+ },
2313
+ }),
2314
+ );
2315
+ };
2316
+ if (unsupportedCaptureKind) {
2317
+ replayFailure('capture-kind-unknown');
2318
+ return { ok: false, stage: 'execute' };
2319
+ }
2320
+ if (replayPassNames === undefined) {
2321
+ replayFailure('replay-pass-selection-missing');
2322
+ return { ok: false, stage: 'execute' };
2323
+ }
2324
+ if (replayPassNames.length === 0) {
2325
+ replayFailure('replay-pass-selection-empty');
2326
+ return { ok: false, stage: 'execute' };
2327
+ }
2328
+ if (duplicatePassName !== undefined) {
2329
+ replayFailure(`replay-pass-selection-duplicate:${duplicatePassName}`);
2330
+ return { ok: false, stage: 'execute' };
2331
+ }
2332
+ if (invalidPassName !== undefined) {
2333
+ replayFailure(`replay-pass-selection-unknown:${invalidPassName}`);
2334
+ return { ok: false, stage: 'execute' };
2335
+ }
2336
+ if (nonUniquePassName !== undefined) {
2337
+ replayFailure(`replay-pass-selection-non-unique:${nonUniquePassName}`);
2338
+ return { ok: false, stage: 'execute' };
2339
+ }
2340
+
2341
+ if (onPrefixEncoded !== undefined) {
2342
+ if (onPassEncoded !== undefined) {
2343
+ replayFailure('pass-replay-prefix-callback-conflict');
2344
+ return { ok: false, stage: 'execute' };
2345
+ }
2346
+ if (depthAttachmentPassName === undefined) {
2347
+ replayFailure('pass-replay-depth-attachment-name-missing');
2348
+ return { ok: false, stage: 'execute' };
2349
+ }
2350
+ if (observeAfterPassName === undefined) {
2351
+ replayFailure('pass-replay-observe-boundary-name-missing');
2352
+ return { ok: false, stage: 'execute' };
2353
+ }
2354
+ const firstNonPrefixPass = selectedReplayPassNames.find(
2355
+ (passName, index) => passName !== passNames[index],
2356
+ );
2357
+ if (
2358
+ firstNonPrefixPass !== undefined ||
2359
+ selectedReplayPassNames.length > passNames.length
2360
+ ) {
2361
+ replayFailure('pass-replay-selection-not-prefix');
2362
+ return { ok: false, stage: 'execute' };
2363
+ }
2364
+ if (selectedReplayPassNames.at(-1) !== observeAfterPassName) {
2365
+ replayFailure('pass-replay-observe-boundary-not-last');
2366
+ return { ok: false, stage: 'execute' };
2367
+ }
2368
+ const anchorIndex = selectedReplayPassNames.indexOf(depthAttachmentPassName);
2369
+ if (anchorIndex < 0 || anchorIndex > selectedReplayPassNames.length - 1) {
2370
+ replayFailure('pass-replay-depth-attachment-not-in-prefix');
2371
+ return { ok: false, stage: 'execute' };
2372
+ }
2373
+ }
2374
+
2375
+ let replayTarget:
2376
+ | {
2377
+ readonly name: string;
2378
+ readonly view: TextureView;
2379
+ readonly texture: Texture;
2380
+ readonly textureIdentity: number;
2381
+ readonly graphGeneration: number;
2382
+ }
2383
+ | undefined;
2384
+ if (onPassEncoded !== undefined || onPrefixEncoded !== undefined) {
2385
+ const targetName = passReplayCapture?.targetName;
2386
+ const graphAccess = frameState.perFrameGraph;
2387
+ const targetView =
2388
+ targetName === undefined ? undefined : graphAccess?.getColorTargetView(targetName);
2389
+ const targetTexture =
2390
+ targetName === undefined ? undefined : graphAccess?.getColorTargetTexture(targetName);
2391
+ const targetDescriptor =
2392
+ targetName === undefined
2393
+ ? undefined
2394
+ : graphAccess?.getColorTargetDescriptor(targetName);
2395
+ if (targetName === undefined) {
2396
+ replayFailure('pass-replay-target-name-missing');
2397
+ return { ok: false, stage: 'execute' };
2398
+ }
2399
+ if (graphAccess === undefined || graphAccess === null) {
2400
+ replayFailure(`pass-replay-target-missing:${targetName}`);
2401
+ return { ok: false, stage: 'execute' };
2402
+ }
2403
+ if (targetView === undefined || targetTexture === undefined) {
2404
+ replayFailure(`pass-replay-target-missing:${targetName}`);
2405
+ return { ok: false, stage: 'execute' };
2406
+ }
2407
+ if (targetDescriptor !== undefined && targetDescriptor.texture !== targetTexture) {
2408
+ replayFailure(`pass-replay-target-identity-mismatch:${targetName}`);
2409
+ return { ok: false, stage: 'execute' };
2410
+ }
2411
+ replayTarget = {
2412
+ name: targetName,
2413
+ view: targetView,
2414
+ texture: targetTexture,
2415
+ textureIdentity: getTextureIdentity(targetTexture),
2416
+ graphGeneration: graphAccess.graphGeneration,
2417
+ };
2418
+ }
2419
+
2420
+ const executed = graph.execute(frame, runPass, graphInstrumentation);
2421
+ if (!executed.ok) {
2422
+ passTimingCapture?.abort({ code: executed.error.code });
2423
+ internals.errorRegistry.fire(executed.error);
2424
+ return { ok: false, stage: 'execute' };
2425
+ }
2426
+ const replayedPassNames = new Set<string>();
2427
+ let prefixCallbackCount = 0;
2428
+ let depthAnchorReceipt:
2429
+ | {
2430
+ readonly pass: { name: string; executionIndex: number };
2431
+ readonly view: TextureView;
2432
+ }
2433
+ | undefined;
2434
+ const replayRunPass: import('@forgeax/engine-render-graph').RenderGraphPassRunner = (
2435
+ pass,
2436
+ encode,
2437
+ ) => {
2438
+ if (!requestedPassNames.has(pass.name)) return;
2439
+ if (replayedPassNames.has(pass.name)) {
2440
+ replayFailure(`replay-pass-selection-multiple-hit:${pass.name}`);
2441
+ return;
2442
+ }
2443
+ replayedPassNames.add(pass.name);
2444
+ const depthView = (pass as ResolvedDepthPassExecution).resolvedDepthStencilAttachmentView;
2445
+ if (onPrefixEncoded !== undefined) {
2446
+ if (pass.name === depthAttachmentPassName) {
2447
+ if (depthView === undefined) {
2448
+ replayFailure(`pass-replay-depth-attachment-missing:${pass.name}`);
2449
+ return;
2450
+ }
2451
+ const target = replayTarget;
2452
+ if (target === undefined || depthView !== target.view) {
2453
+ replayFailure(`pass-replay-attachment-target-mismatch:${pass.name}`);
2454
+ return;
2455
+ }
2456
+ if (depthAnchorReceipt !== undefined) {
2457
+ replayFailure(`pass-replay-depth-attachment-multiple-hit:${pass.name}`);
2458
+ return;
2459
+ }
2460
+ depthAnchorReceipt = {
2461
+ pass: { name: pass.name, executionIndex: pass.executionIndex },
2462
+ view: depthView,
2463
+ };
2464
+ }
2465
+ let encoded = false;
2466
+ const encodePrefix = () => {
2467
+ if (encoded) {
2468
+ replayFailure(`pass-replay-encode-multiple-hit:${pass.name}`);
2469
+ return;
2470
+ }
2471
+ encode();
2472
+ encoded = true;
2473
+ if (replayValidationFailed || pass.name !== observeAfterPassName) return;
2474
+ const target = replayTarget;
2475
+ const anchor = depthAnchorReceipt;
2476
+ if (target === undefined || anchor === undefined) {
2477
+ replayFailure('pass-replay-depth-attachment-anchor-missing-at-boundary');
2478
+ return;
2479
+ }
2480
+ const receipt: GraphTargetPassReplayPrefixReceipt = {
2481
+ passName: pass.name,
2482
+ executionIndex: pass.executionIndex,
2483
+ resolvedDepthStencilAttachmentView: anchor.view,
2484
+ graphGeneration: target.graphGeneration,
2485
+ targetName: target.name,
2486
+ targetView: target.view,
2487
+ targetTexture: target.texture,
2488
+ targetTextureIdentity: target.textureIdentity,
2489
+ replayPassNames: [...selectedReplayPassNames],
2490
+ depthAttachmentPassName: anchor.pass.name,
2491
+ observeAfterPassName: pass.name,
2492
+ depthAttachmentExecutionIndex: anchor.pass.executionIndex,
2493
+ };
2494
+ try {
2495
+ onPrefixEncoded(receipt);
2496
+ prefixCallbackCount += 1;
2497
+ } catch (cause) {
2498
+ replayFailure(
2499
+ `pass-replay-prefix-callback-threw:${pass.name}:${cause instanceof Error ? cause.message : String(cause)}`,
2500
+ );
2501
+ }
2502
+ };
2503
+ if (runPass === undefined) encodePrefix();
2504
+ else runPass(pass, encodePrefix);
2505
+ if (!encoded && !replayValidationFailed) {
2506
+ replayFailure(`pass-replay-encode-not-called:${pass.name}`);
2507
+ }
2508
+ return;
2509
+ }
2510
+ if (onPassEncoded === undefined) {
2511
+ if (runPass === undefined) encode();
2512
+ else runPass(pass, encode);
2513
+ return;
2514
+ }
2515
+ const target = replayTarget;
2516
+ if (target === undefined || depthView === undefined) {
2517
+ replayFailure(`pass-replay-depth-attachment-missing:${pass.name}`);
2518
+ return;
2519
+ }
2520
+ if (depthView !== target.view) {
2521
+ replayFailure(`pass-replay-attachment-target-mismatch:${pass.name}`);
2522
+ return;
2523
+ }
2524
+ let encoded = false;
2525
+ const encodeAndObserve = () => {
2526
+ if (encoded) {
2527
+ replayFailure(`pass-replay-encode-multiple-hit:${pass.name}`);
2528
+ return;
2529
+ }
2530
+ encode();
2531
+ encoded = true;
2532
+ if (replayValidationFailed) return;
2533
+ const receipt: GraphTargetPassReplayReceipt = {
2534
+ passName: pass.name,
2535
+ executionIndex: pass.executionIndex,
2536
+ resolvedDepthStencilAttachmentView: depthView,
2537
+ graphGeneration: target.graphGeneration,
2538
+ targetName: target.name,
2539
+ targetView: target.view,
2540
+ targetTexture: target.texture,
2541
+ targetTextureIdentity: target.textureIdentity,
2542
+ };
2543
+ try {
2544
+ onPassEncoded(receipt);
2545
+ } catch (cause) {
2546
+ replayFailure(
2547
+ `pass-replay-callback-threw:${pass.name}:${cause instanceof Error ? cause.message : String(cause)}`,
2548
+ );
2549
+ }
2550
+ };
2551
+ if (runPass === undefined) encodeAndObserve();
2552
+ else runPass(pass, encodeAndObserve);
2553
+ if (!encoded && !replayValidationFailed) {
2554
+ replayFailure(`pass-replay-encode-not-called:${pass.name}`);
2555
+ }
2556
+ };
2557
+ const replayed = graph.execute(frame, replayRunPass);
2558
+ if (!replayed.ok) {
2559
+ internals.errorRegistry.fire(replayed.error);
2560
+ return { ok: false, stage: 'execute' };
2561
+ }
2562
+ if (replayValidationFailed) return { ok: false, stage: 'execute' };
2563
+ if (replayedPassNames.size !== requestedPassNames.size) {
2564
+ replayFailure('replay-pass-selection-zero-hit');
2565
+ return { ok: false, stage: 'execute' };
2566
+ }
2567
+ if (onPrefixEncoded !== undefined && prefixCallbackCount !== 1) {
2568
+ replayFailure('pass-replay-prefix-callback-count-invalid');
2569
+ return { ok: false, stage: 'execute' };
2570
+ }
2571
+ } else {
2572
+ const executed = graph.execute(frame, runPass, graphInstrumentation);
2573
+ if (!executed.ok) {
2574
+ passTimingCapture?.abort({ code: executed.error.code });
2575
+ internals.errorRegistry.fire(executed.error);
2576
+ return { ok: false, stage: 'execute' };
2577
+ }
1680
2578
  }
1681
- const afterGraphExecute = frameHooks?.afterGraphExecute;
2579
+ timingInstrumentationState?.setCapture(undefined);
2580
+ const afterGraphExecute = resolvedFrameHooks?.afterGraphExecute;
1682
2581
  if (afterGraphExecute !== undefined) {
1683
2582
  const recorded = afterGraphExecute();
1684
2583
  if (!recorded.ok) {
@@ -1691,7 +2590,7 @@ export function executeCompiledFrameGraph(
1691
2590
  finish: () => {
1692
2591
  const graphCapture = frameState.graphTargetCapture;
1693
2592
  frameState.graphTargetCapture = undefined;
1694
- if (graphCapture !== undefined) {
2593
+ if (graphCapture !== undefined && graphCapture.kind !== 'pass-replay') {
1695
2594
  const graphAccess = frameState.perFrameGraph;
1696
2595
  const graphTexture = graphAccess?.getColorTargetTexture(graphCapture.name);
1697
2596
  const descriptor = graphAccess?.getColorTargetDescriptor(graphCapture.name);
@@ -1910,7 +2809,8 @@ export function executeCompiledFrameGraph(
1910
2809
  },
1911
2810
  }),
1912
2811
  commit: () => {
1913
- frameHooks?.onSubmitted?.();
2812
+ resolvedFrameHooks?.onSubmitted?.();
2813
+ legacySubmitCommit?.();
1914
2814
  if (frameState.temporalFrameInput !== undefined) {
1915
2815
  const temporal = frameState.temporalFrameTransaction.commit({ accepted: true });
1916
2816
  if (temporal.ok) {
@@ -1938,6 +2838,46 @@ export function executeCompiledFrameGraph(
1938
2838
  ?.perPassResources.commitBloomFrameReceipts?.(frameState.bloomFrameReceipts);
1939
2839
  frameState.bloomFrameReceipts = undefined;
1940
2840
  }
2841
+ const pendingAuto = frameState.pendingAutoExposureGpuResources;
2842
+ if (pendingAuto !== undefined) {
2843
+ const previousAuto = frameState.autoExposureGpuResources;
2844
+ frameState.autoExposureGpuResources = pendingAuto;
2845
+ frameState.pendingAutoExposureGpuResources = undefined;
2846
+ if (previousAuto !== undefined && previousAuto !== pendingAuto) {
2847
+ internals.device.queue
2848
+ .onSubmittedWorkDone()
2849
+ .then(() => retireAutoExposureGpuResources(previousAuto))
2850
+ .catch(() => undefined);
2851
+ }
2852
+ }
2853
+ const pendingAutoState = frameState.pendingAutoExposureState;
2854
+ if (pendingAutoState !== undefined) {
2855
+ frameState.autoExposureState = commitAutoExposureSubmission(
2856
+ pendingAutoState.state,
2857
+ pendingAutoState,
2858
+ );
2859
+ frameState.pendingAutoExposureState = undefined;
2860
+ }
2861
+ const pendingLut = frameState.pendingStandardLutGpuResources;
2862
+ if (pendingLut !== undefined) {
2863
+ const previousLut = frameState.standardLutGpuResources;
2864
+ frameState.standardLutGpuResources = pendingLut;
2865
+ frameState.pendingStandardLutGpuResources = undefined;
2866
+ if (previousLut !== undefined && previousLut !== pendingLut) {
2867
+ retireStandardLutGpuResources(previousLut);
2868
+ }
2869
+ }
2870
+ const pendingLutState = frameState.pendingStandardLutState;
2871
+ if (pendingLutState !== undefined) {
2872
+ frameState.standardLutState =
2873
+ pendingLutState.remove || pendingLutState.candidate === undefined
2874
+ ? resetStandardLutState(pendingLutState.state, 'resource-removed', {
2875
+ targetGeneration: pendingLutState.targetGeneration,
2876
+ deviceEpoch: pendingLutState.deviceEpoch,
2877
+ })
2878
+ : commitStandardLutCandidate(pendingLutState.state, pendingLutState.candidate);
2879
+ frameState.pendingStandardLutState = undefined;
2880
+ }
1941
2881
  const stagedGpuState = frameState.temporalGpuState;
1942
2882
  if (stagedGpuState !== undefined) {
1943
2883
  if (commitTemporalGpuSubmit(stagedGpuState)) {
@@ -1945,7 +2885,6 @@ export function executeCompiledFrameGraph(
1945
2885
  frameState.activeTemporalGpuState = stagedGpuState;
1946
2886
  if (previousGpuState !== undefined && previousGpuState !== stagedGpuState) {
1947
2887
  internals.clearPostProcessPipelineCache?.('forgeax.taa-resolve');
1948
- invalidatePostProcessModule('forgeax.taa-resolve');
1949
2888
  retireTemporalGpuStateAfterFence(
1950
2889
  previousGpuState,
1951
2890
  internals.device.queue,
@@ -1975,7 +2914,8 @@ export function executeCompiledFrameGraph(
1975
2914
  }
1976
2915
  if (stagedTemporalCommit.kind === 'off') {
1977
2916
  internals.clearPostProcessPipelineCache?.('forgeax.taa-resolve');
1978
- invalidatePostProcessModule('forgeax.taa-resolve');
2917
+ // Retire frame-sized resources, not the device-owned prewarmed
2918
+ // shader. The next TAA frame must not start an async cold compile.
1979
2919
  const activeGpuState = frameState.activeTemporalGpuState;
1980
2920
  if (activeGpuState !== undefined) {
1981
2921
  retireTemporalGpuStateAfterFence(
@@ -2000,15 +2940,28 @@ export function executeCompiledFrameGraph(
2000
2940
  frameState.environmentLifecycle?.publish(frameState.environmentGeneration);
2001
2941
  frameState.environmentGeneration = undefined;
2002
2942
  }
2943
+ frameState.pendingAtmospherePublish?.();
2944
+ frameState.pendingAtmospherePublish = undefined;
2003
2945
  onSubmitted?.(internals.device.queue.onSubmittedWorkDone());
2004
2946
  },
2005
2947
  abort: (failure) => {
2006
- frameHooks?.onAborted?.();
2948
+ frameState.pendingAtmospherePublish = undefined;
2949
+ resolvedFrameHooks?.onAborted?.();
2007
2950
  passTimingCapture?.abort({ code: failure.stage });
2008
2951
  timingHost.gpuPassTimingCapture = undefined;
2009
2952
  timingHost.gpuPassTimingSubmittedWork = undefined;
2010
2953
  timingCapture?.discard();
2011
2954
  frameState.bloomFrameReceipts = undefined;
2955
+ frameState.pendingAutoExposureState = undefined;
2956
+ frameState.pendingStandardLutState = undefined;
2957
+ if (frameState.pendingAutoExposureGpuResources !== undefined) {
2958
+ retireAutoExposureGpuResources(frameState.pendingAutoExposureGpuResources);
2959
+ frameState.pendingAutoExposureGpuResources = undefined;
2960
+ }
2961
+ if (frameState.pendingStandardLutGpuResources !== undefined) {
2962
+ retireStandardLutGpuResources(frameState.pendingStandardLutGpuResources);
2963
+ frameState.pendingStandardLutGpuResources = undefined;
2964
+ }
2012
2965
  if (frameState.environmentGeneration !== undefined) {
2013
2966
  frameState.environmentLifecycle?.recordStageFailure(
2014
2967
  failure.stage,