@forgeax/engine-render 0.1.7 → 0.1.20

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 (777) hide show
  1. package/README.md +140 -2
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/assembly/backend-contract.d.ts +12 -0
  4. package/dist/assembly/backend-contract.d.ts.map +1 -1
  5. package/dist/assembly/factory.d.ts +26 -5
  6. package/dist/assembly/factory.d.ts.map +1 -1
  7. package/dist/assembly/host-contract.d.ts +4 -0
  8. package/dist/assembly/host-contract.d.ts.map +1 -1
  9. package/dist/assembly/renderer-frame-transaction.d.ts +39 -0
  10. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -0
  11. package/dist/assembly/renderer-inspection.d.ts +22 -0
  12. package/dist/assembly/renderer-inspection.d.ts.map +1 -0
  13. package/dist/assembly/renderer-lifecycle.d.ts +4 -0
  14. package/dist/assembly/renderer-lifecycle.d.ts.map +1 -0
  15. package/dist/authoring.mjs +4 -2
  16. package/dist/authoring.mjs.map +1 -1
  17. package/dist/chunk-4XINDLAY.mjs +9404 -0
  18. package/dist/chunk-4XINDLAY.mjs.map +1 -0
  19. package/dist/chunk-CDTWTHZR.mjs +752 -0
  20. package/dist/chunk-CDTWTHZR.mjs.map +1 -0
  21. package/dist/chunk-FJ6P52EE.mjs +46 -0
  22. package/dist/chunk-FJ6P52EE.mjs.map +1 -0
  23. package/dist/chunk-JDWARUOI.mjs +44 -0
  24. package/dist/chunk-JDWARUOI.mjs.map +1 -0
  25. package/dist/chunk-M2MP4ZOU.mjs +73 -0
  26. package/dist/chunk-M2MP4ZOU.mjs.map +1 -0
  27. package/dist/chunk-PCHBPYML.mjs +385 -0
  28. package/dist/chunk-PCHBPYML.mjs.map +1 -0
  29. package/dist/chunk-RXZ3RGOU.mjs +53 -0
  30. package/dist/chunk-RXZ3RGOU.mjs.map +1 -0
  31. package/dist/{chunk-YEJIUW7D.mjs → chunk-TBQJOK2L.mjs} +356 -543
  32. package/dist/chunk-TBQJOK2L.mjs.map +1 -0
  33. package/dist/{chunk-CQXZC5XI.mjs → chunk-VFPLTKCU.mjs} +79 -69
  34. package/dist/chunk-VFPLTKCU.mjs.map +1 -0
  35. package/dist/components/atmosphere.d.ts +9 -0
  36. package/dist/components/atmosphere.d.ts.map +1 -0
  37. package/dist/components/camera.d.ts +2 -1
  38. package/dist/components/camera.d.ts.map +1 -1
  39. package/dist/components/fog.d.ts +8 -0
  40. package/dist/components/fog.d.ts.map +1 -0
  41. package/dist/components/index.d.ts +3 -0
  42. package/dist/components/index.d.ts.map +1 -1
  43. package/dist/components/light-helpers.d.ts.map +1 -1
  44. package/dist/components/motion-blur.d.ts +7 -0
  45. package/dist/components/motion-blur.d.ts.map +1 -0
  46. package/dist/components/spot-light.d.ts +12 -0
  47. package/dist/components/spot-light.d.ts.map +1 -1
  48. package/dist/construct-renderer.d.ts +2 -2
  49. package/dist/construct-renderer.d.ts.map +1 -1
  50. package/dist/construct-renderer.mjs +11146 -14507
  51. package/dist/construct-renderer.mjs.map +1 -1
  52. package/dist/device/device-scope.d.ts +4 -0
  53. package/dist/device/device-scope.d.ts.map +1 -1
  54. package/dist/device/gpu-residency.d.ts +19 -2
  55. package/dist/device/gpu-residency.d.ts.map +1 -1
  56. package/dist/environment/frame.d.ts +60 -0
  57. package/dist/environment/frame.d.ts.map +1 -0
  58. package/dist/environment/generation.d.ts +21 -0
  59. package/dist/environment/generation.d.ts.map +1 -0
  60. package/dist/environment/graph.d.ts +4 -0
  61. package/dist/environment/graph.d.ts.map +1 -0
  62. package/dist/environment/inspection.d.ts +48 -0
  63. package/dist/environment/inspection.d.ts.map +1 -0
  64. package/dist/environment/lifecycle.d.ts +39 -0
  65. package/dist/environment/lifecycle.d.ts.map +1 -0
  66. package/dist/environment/signature.d.ts +25 -0
  67. package/dist/environment/signature.d.ts.map +1 -0
  68. package/dist/errors/render.d.ts +204 -1
  69. package/dist/errors/render.d.ts.map +1 -1
  70. package/dist/extract/camera.d.ts +5 -0
  71. package/dist/extract/camera.d.ts.map +1 -0
  72. package/dist/extract/environment.d.ts +74 -0
  73. package/dist/extract/environment.d.ts.map +1 -0
  74. package/dist/extract/visibility.d.ts +1 -1
  75. package/dist/extract/visibility.d.ts.map +1 -1
  76. package/dist/features/host.d.ts +9 -2
  77. package/dist/features/host.d.ts.map +1 -1
  78. package/dist/features/motion-blur/motion-blur-feature.d.ts +16 -0
  79. package/dist/features/motion-blur/motion-blur-feature.d.ts.map +1 -0
  80. package/dist/features/motion-blur/motion-blur-params.d.ts +25 -0
  81. package/dist/features/motion-blur/motion-blur-params.d.ts.map +1 -0
  82. package/dist/features/plan.d.ts +17 -2
  83. package/dist/features/plan.d.ts.map +1 -1
  84. package/dist/features/prepared-gpu-work.d.ts +3 -1
  85. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  86. package/dist/features/prepared-graphics.d.ts +2 -1
  87. package/dist/features/prepared-graphics.d.ts.map +1 -1
  88. package/dist/features/render-graph-raster.d.ts +2 -1
  89. package/dist/features/render-graph-raster.d.ts.map +1 -1
  90. package/dist/features/targets.d.ts +3 -0
  91. package/dist/features/targets.d.ts.map +1 -1
  92. package/dist/features/types.d.ts +12 -0
  93. package/dist/features/types.d.ts.map +1 -1
  94. package/dist/fullscreen-post-process-pass.d.ts +7 -1
  95. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  96. package/dist/gpu-scene.d.ts +2 -0
  97. package/dist/gpu-scene.d.ts.map +1 -1
  98. package/dist/gpu-usage.d.ts +2 -0
  99. package/dist/gpu-usage.d.ts.map +1 -1
  100. package/dist/ibl/skylight-bind-group.d.ts +5 -6
  101. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  102. package/dist/index.d.ts +24 -7
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.mjs +6 -4
  105. package/dist/index.mjs.map +1 -1
  106. package/dist/inspection-types.d.ts +43 -1
  107. package/dist/inspection-types.d.ts.map +1 -1
  108. package/dist/internal.d.ts +2 -0
  109. package/dist/internal.d.ts.map +1 -1
  110. package/dist/internal.mjs +2 -0
  111. package/dist/light-buffer-layout.d.ts +20 -12
  112. package/dist/light-buffer-layout.d.ts.map +1 -1
  113. package/dist/materials.d.ts +22 -0
  114. package/dist/materials.d.ts.map +1 -1
  115. package/dist/pbr-pipeline.d.ts +16 -9
  116. package/dist/pbr-pipeline.d.ts.map +1 -1
  117. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  118. package/dist/pipeline/standard-pipeline.d.ts +3 -0
  119. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  120. package/dist/pipeline/standard-post.d.ts +48 -0
  121. package/dist/pipeline/standard-post.d.ts.map +1 -0
  122. package/dist/pipeline/standard-profile.d.ts +12 -2
  123. package/dist/pipeline/standard-profile.d.ts.map +1 -1
  124. package/dist/pipeline-builder.d.ts +18 -0
  125. package/dist/pipeline-builder.d.ts.map +1 -1
  126. package/dist/pipeline-spec.d.ts.map +1 -1
  127. package/dist/plugin.d.ts.map +1 -1
  128. package/dist/record/frame-lighting.d.ts +2 -0
  129. package/dist/record/frame-lighting.d.ts.map +1 -1
  130. package/dist/record/frame-snapshot.d.ts +137 -6
  131. package/dist/record/frame-snapshot.d.ts.map +1 -1
  132. package/dist/record/frame-targets.d.ts.map +1 -1
  133. package/dist/record/frame.d.ts +6 -3
  134. package/dist/record/frame.d.ts.map +1 -1
  135. package/dist/record/gpu-timing.d.ts +32 -0
  136. package/dist/record/gpu-timing.d.ts.map +1 -0
  137. package/dist/record/main-pass-geometry.d.ts +11 -0
  138. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  139. package/dist/record/main-pass-material.d.ts +9 -6
  140. package/dist/record/main-pass-material.d.ts.map +1 -1
  141. package/dist/record/main-pass-sprite-draws.d.ts +8 -1
  142. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  143. package/dist/record/main-pass.d.ts +2 -0
  144. package/dist/record/main-pass.d.ts.map +1 -1
  145. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  146. package/dist/record/render-context.d.ts +81 -19
  147. package/dist/record/render-context.d.ts.map +1 -1
  148. package/dist/record/shadow-pass.d.ts.map +1 -1
  149. package/dist/record/skybox-post-pass.d.ts +1 -11
  150. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  151. package/dist/record/typed-frame-graph.d.ts +36 -4
  152. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  153. package/dist/record/view-ubo.d.ts +4 -1
  154. package/dist/record/view-ubo.d.ts.map +1 -1
  155. package/dist/render-contract.d.ts +157 -8
  156. package/dist/render-contract.d.ts.map +1 -1
  157. package/dist/render-data.d.ts +10 -6
  158. package/dist/render-data.d.ts.map +1 -1
  159. package/dist/render-graph-primitives.d.ts.map +1 -1
  160. package/dist/render-pipeline.d.ts +86 -6
  161. package/dist/render-pipeline.d.ts.map +1 -1
  162. package/dist/render-system-extract.d.ts +84 -8
  163. package/dist/render-system-extract.d.ts.map +1 -1
  164. package/dist/render-system.d.ts +30 -5
  165. package/dist/render-system.d.ts.map +1 -1
  166. package/dist/renderer-inspect.d.ts +29 -0
  167. package/dist/renderer-inspect.d.ts.map +1 -0
  168. package/dist/scene/render-scene.d.ts +36 -2
  169. package/dist/scene/render-scene.d.ts.map +1 -1
  170. package/dist/temporal/coverage.d.ts +42 -0
  171. package/dist/temporal/coverage.d.ts.map +1 -0
  172. package/dist/temporal/frame-coordinator.d.ts +99 -0
  173. package/dist/temporal/frame-coordinator.d.ts.map +1 -0
  174. package/dist/temporal/frame.d.ts +44 -0
  175. package/dist/temporal/frame.d.ts.map +1 -0
  176. package/dist/temporal/gpu.d.ts +39 -0
  177. package/dist/temporal/gpu.d.ts.map +1 -0
  178. package/dist/temporal/history.d.ts +37 -0
  179. package/dist/temporal/history.d.ts.map +1 -0
  180. package/dist/temporal/index.d.ts +7 -0
  181. package/dist/temporal/index.d.ts.map +1 -0
  182. package/dist/temporal/index.mjs +8 -0
  183. package/dist/temporal/index.mjs.map +1 -0
  184. package/dist/temporal/inspection.d.ts +36 -0
  185. package/dist/temporal/inspection.d.ts.map +1 -0
  186. package/dist/temporal/scene-data-catalog.d.ts +48 -0
  187. package/dist/temporal/scene-data-catalog.d.ts.map +1 -0
  188. package/dist/temporal/scene-data.d.ts +43 -0
  189. package/dist/temporal/scene-data.d.ts.map +1 -0
  190. package/dist/temporal/standard-scene-data.d.ts +61 -0
  191. package/dist/temporal/standard-scene-data.d.ts.map +1 -0
  192. package/dist/temporal/taa-history-resources.d.ts +22 -0
  193. package/dist/temporal/taa-history-resources.d.ts.map +1 -0
  194. package/dist/temporal/taa-history-store.d.ts +27 -0
  195. package/dist/temporal/taa-history-store.d.ts.map +1 -0
  196. package/dist/temporal/taa-resolve.d.ts +15 -0
  197. package/dist/temporal/taa-resolve.d.ts.map +1 -0
  198. package/dist/temporal/temporal-view.d.ts +40 -0
  199. package/dist/temporal/temporal-view.d.ts.map +1 -0
  200. package/dist/temporal/transaction.d.ts +9 -0
  201. package/dist/temporal/transaction.d.ts.map +1 -0
  202. package/dist/temporal/view.d.ts +56 -0
  203. package/dist/temporal/view.d.ts.map +1 -0
  204. package/dist/transmission/backdrop.d.ts +147 -0
  205. package/dist/transmission/backdrop.d.ts.map +1 -0
  206. package/dist/transmission/inspection.d.ts +57 -0
  207. package/dist/transmission/inspection.d.ts.map +1 -0
  208. package/dist/transmission/oracle.d.ts +42 -0
  209. package/dist/transmission/oracle.d.ts.map +1 -0
  210. package/dist/transmission/projection.d.ts +37 -0
  211. package/dist/transmission/projection.d.ts.map +1 -0
  212. package/dist/typed-render-graph-primitives.d.ts +30 -2
  213. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  214. package/dist/typed-shadow-atlas.d.ts +10 -0
  215. package/dist/typed-shadow-atlas.d.ts.map +1 -0
  216. package/dist/typed-shadow-passes.d.ts.map +1 -1
  217. package/dist/volume/capability.d.ts +57 -0
  218. package/dist/volume/capability.d.ts.map +1 -0
  219. package/dist/volume/component.d.ts +61 -0
  220. package/dist/volume/component.d.ts.map +1 -0
  221. package/dist/volume/extract.d.ts +11 -0
  222. package/dist/volume/extract.d.ts.map +1 -0
  223. package/dist/volume/inspection.d.ts +38 -0
  224. package/dist/volume/inspection.d.ts.map +1 -0
  225. package/dist/volume/passes.d.ts +38 -0
  226. package/dist/volume/passes.d.ts.map +1 -0
  227. package/dist/volume/recovery.d.ts +38 -0
  228. package/dist/volume/recovery.d.ts.map +1 -0
  229. package/dist/volume/resources.d.ts +57 -0
  230. package/dist/volume/resources.d.ts.map +1 -0
  231. package/dist/volume/temporal.d.ts +35 -0
  232. package/dist/volume/temporal.d.ts.map +1 -0
  233. package/package.json +24 -19
  234. package/src/__tests__/ai-user-cold-start.integration.test.ts +59 -0
  235. package/src/__tests__/ai-user-public-surface.test-d.ts +45 -0
  236. package/src/__tests__/bloom-exact-zero.unit.test.ts +202 -0
  237. package/src/__tests__/bloom-lifecycle.integration.test.ts +125 -0
  238. package/src/__tests__/construct-renderer-identity.unit.test.ts +4 -3
  239. package/src/__tests__/detached-inspection.integration.test.ts +177 -0
  240. package/src/__tests__/environment-fog-lanes.integration.test.ts +219 -0
  241. package/src/__tests__/environment-generation.integration.test.ts +141 -0
  242. package/src/__tests__/environment-selection.unit.test.ts +245 -0
  243. package/src/__tests__/factory-contract.integration.test.ts +1380 -9
  244. package/src/__tests__/frame-plan-contract.unit.test.ts +90 -0
  245. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +7 -0
  246. package/src/__tests__/gpu-driven-production.integration.test.ts +74 -22
  247. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +37 -0
  248. package/src/__tests__/gpu-scene.unit.test.ts +10 -0
  249. package/src/__tests__/injection-kind-owner.test-d.ts +2 -1
  250. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +14 -14
  251. package/src/__tests__/material-contract-inventory.unit.test.ts +62 -0
  252. package/src/__tests__/material-cooked-projection.unit.test.ts +18 -146
  253. package/src/__tests__/materials-transmission-validation.unit.test.ts +36 -0
  254. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
  255. package/src/__tests__/morph-feature-plan.unit.test.ts +7 -0
  256. package/src/__tests__/motion-blur.pixel.integration.test.ts +121 -0
  257. package/src/__tests__/public-surface.test-d.ts +15 -1
  258. package/src/__tests__/render-error-code-owner.test-d.ts +14 -0
  259. package/src/__tests__/render-error-detail-ssot.test-d.ts +11 -2
  260. package/src/__tests__/render-error-exhaustive.test-d.ts +3 -0
  261. package/src/__tests__/render-feature-errors.unit.test.ts +33 -3
  262. package/src/__tests__/render-feature-gpu-work.integration.test.ts +44 -0
  263. package/src/__tests__/render-feature-plan.test-d.ts +22 -89
  264. package/src/__tests__/render-scene-projection.unit.test.ts +28 -0
  265. package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +26 -0
  266. package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +21 -0
  267. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +423 -1
  268. package/src/__tests__/renderer-frame-transaction.integration.test.ts +405 -0
  269. package/src/__tests__/renderer-lifecycle.integration.test.ts +22 -0
  270. package/src/__tests__/scene-data-catalog.unit.test.ts +59 -0
  271. package/src/__tests__/scene-data-consumer.integration.test.ts +67 -0
  272. package/src/__tests__/scene-data-contract.test-d.ts +35 -0
  273. package/src/__tests__/scene-data-inspection.unit.test.ts +41 -0
  274. package/src/__tests__/scene-data-public-surface.test-d.ts +34 -0
  275. package/src/__tests__/shader-manifest-fixture.ts +111 -0
  276. package/src/__tests__/skinned-shadow-caster.test.ts +123 -0
  277. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +24 -3
  278. package/src/__tests__/standard-pipeline.dawn.test.ts +15 -0
  279. package/src/__tests__/standard-pipeline.integration.test.ts +543 -1
  280. package/src/__tests__/standard-profile-oracle.unit.test.ts +12 -2
  281. package/src/__tests__/standard-temporal-producer.integration.test.ts +35 -0
  282. package/src/__tests__/surface-configuration.unit.test.ts +211 -12
  283. package/src/__tests__/taa-transaction.integration.test.ts +30 -0
  284. package/src/__tests__/temporal-admission.integration.test.ts +40 -0
  285. package/src/__tests__/temporal-coverage.unit.test.ts +35 -0
  286. package/src/__tests__/temporal-demand.unit.test.ts +44 -0
  287. package/src/__tests__/temporal-frame-coordinator.unit.test.ts +64 -0
  288. package/src/__tests__/temporal-graph-topology.unit.test.ts +51 -0
  289. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +170 -0
  290. package/src/__tests__/temporal-performance.integration.test.ts +615 -0
  291. package/src/__tests__/temporal-reset-matrix.unit.test.ts +97 -0
  292. package/src/__tests__/temporal-transaction.integration.test.ts +60 -0
  293. package/src/__tests__/temporal-view.unit.test.ts +152 -0
  294. package/src/__tests__/transmission-prewarm.unit.test.ts +93 -0
  295. package/src/__tests__/typed-pipeline-topology.unit.test.ts +230 -1
  296. package/src/__tests__/urp-pipeline-targets.unit.test.ts +25 -1
  297. package/src/__tests__/urp-pipeline.unit.test.ts +4 -3
  298. package/src/__tests__/visibility-extract.integration.test.ts +27 -1
  299. package/src/__tests__/visibility-producer-matrix.integration.test.ts +10 -0
  300. package/src/__tests__/volume-optics-oracle.unit.test.ts +85 -0
  301. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +655 -0
  302. package/src/__tests__/volumetric-fog-component.unit.test.ts +71 -0
  303. package/src/__tests__/volumetric-fog-composite-stage-readback.dawn.test.ts +376 -0
  304. package/src/__tests__/volumetric-fog-errors.unit.test.ts +51 -0
  305. package/src/__tests__/volumetric-fog-inspection.unit.test.ts +77 -0
  306. package/src/__tests__/volumetric-fog-light-capability.unit.test.ts +19 -0
  307. package/src/__tests__/volumetric-fog-light-consumers.integration.test.ts +18 -0
  308. package/src/__tests__/volumetric-fog-light-errors.unit.test.ts +17 -0
  309. package/src/__tests__/volumetric-fog-light-topology.unit.test.ts +21 -0
  310. package/src/__tests__/volumetric-fog-light-transaction.integration.test.ts +53 -0
  311. package/src/__tests__/volumetric-fog-m9-watcher.integration.test.ts +27 -0
  312. package/src/__tests__/volumetric-fog-point-light-owner.integration.test.ts +44 -0
  313. package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +49 -0
  314. package/src/__tests__/volumetric-fog-point-spot-contract.unit.test.ts +28 -0
  315. package/src/__tests__/volumetric-fog-point-spot-lkg.integration.test.ts +65 -0
  316. package/src/__tests__/volumetric-fog-point-spot-selection.integration.test.ts +49 -0
  317. package/src/__tests__/volumetric-fog-point-spot-temporal.unit.test.ts +55 -0
  318. package/src/__tests__/volumetric-fog-projector-contract.unit.test.ts +22 -0
  319. package/src/__tests__/volumetric-fog-projector-inspection.unit.test.ts +12 -0
  320. package/src/__tests__/volumetric-fog-projector-surface-volume.integration.test.ts +11 -0
  321. package/src/__tests__/volumetric-fog-projector-transaction.integration.test.ts +26 -0
  322. package/src/__tests__/volumetric-fog-recovery.integration.test.ts +47 -0
  323. package/src/__tests__/volumetric-fog-selected-light-resolution.integration.test.ts +63 -0
  324. package/src/__tests__/volumetric-fog-spot-closure.integration.test.ts +26 -0
  325. package/src/__tests__/volumetric-fog-spot-contract.test-d.ts +21 -0
  326. package/src/__tests__/volumetric-fog-spot-contract.unit.test.ts +27 -0
  327. package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +14 -0
  328. package/src/__tests__/volumetric-fog-spot-optics.integration.test.ts +29 -0
  329. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +550 -0
  330. package/src/__tests__/volumetric-fog-temporal-selected-light.unit.test.ts +33 -0
  331. package/src/__tests__/volumetric-fog-temporal.unit.test.ts +45 -0
  332. package/src/__tests__/volumetric-fog-topology.unit.test.ts +237 -0
  333. package/src/__tests__/volumetric-fog-transparent.integration.test.ts +27 -0
  334. package/src/assembly/backend-contract.ts +47 -1
  335. package/src/assembly/factory.ts +1480 -505
  336. package/src/assembly/host-contract.ts +4 -0
  337. package/src/assembly/renderer-frame-transaction.ts +75 -0
  338. package/src/assembly/renderer-inspection.ts +112 -0
  339. package/src/assembly/renderer-lifecycle.ts +6 -0
  340. package/src/components/__tests__/motion-blur.unit.test.ts +14 -0
  341. package/src/components/__tests__/schema-contract.unit.test.ts +6 -1
  342. package/src/components/atmosphere.ts +10 -0
  343. package/src/components/camera.ts +2 -1
  344. package/src/components/fog.ts +9 -0
  345. package/src/components/index.ts +3 -0
  346. package/src/components/light-helpers.ts +4 -0
  347. package/src/components/motion-blur.ts +8 -0
  348. package/src/components/spot-light.ts +22 -3
  349. package/src/construct-renderer.ts +40 -3
  350. package/src/device/device-scope.ts +15 -1
  351. package/src/device/gpu-residency.ts +168 -58
  352. package/src/environment/frame.ts +185 -0
  353. package/src/environment/generation.ts +23 -0
  354. package/src/environment/graph.ts +9 -0
  355. package/src/environment/inspection.ts +87 -0
  356. package/src/environment/lifecycle.ts +250 -0
  357. package/src/environment/signature.ts +55 -0
  358. package/src/errors/render.ts +404 -1
  359. package/src/extract/camera.ts +78 -0
  360. package/src/extract/environment.ts +171 -0
  361. package/src/extract/visibility.ts +38 -19
  362. package/src/features/host.ts +36 -5
  363. package/src/features/motion-blur/__tests__/motion-blur-feature.unit.test.ts +67 -0
  364. package/src/features/motion-blur/__tests__/motion-blur-params.unit.test.ts +53 -0
  365. package/src/features/motion-blur/motion-blur-feature.ts +119 -0
  366. package/src/features/motion-blur/motion-blur-params.ts +89 -0
  367. package/src/features/plan.ts +32 -3
  368. package/src/features/prepared-gpu-work.ts +21 -6
  369. package/src/features/prepared-graphics.ts +2 -1
  370. package/src/features/render-graph-raster.ts +2 -1
  371. package/src/features/targets.ts +24 -2
  372. package/src/features/types.ts +13 -0
  373. package/src/fullscreen-post-process-pass.ts +34 -4
  374. package/src/gpu-scene.ts +24 -0
  375. package/src/gpu-usage.ts +2 -0
  376. package/src/ibl/skylight-bind-group.ts +30 -38
  377. package/src/index.ts +104 -7
  378. package/src/inspection-types.ts +48 -0
  379. package/src/internal.ts +5 -0
  380. package/src/light-buffer-layout.ts +59 -26
  381. package/src/materials.ts +119 -24
  382. package/src/pbr-pipeline.ts +91 -29
  383. package/src/pipeline/standard-forward-lane.ts +178 -101
  384. package/src/pipeline/standard-pipeline.ts +176 -105
  385. package/src/pipeline/standard-post.ts +373 -0
  386. package/src/pipeline/standard-profile.ts +26 -3
  387. package/src/pipeline-builder.ts +102 -12
  388. package/src/pipeline-spec.ts +9 -0
  389. package/src/plugin.ts +2 -0
  390. package/src/record/__tests__/fullscreen-temporal-routing.unit.test.ts +25 -0
  391. package/src/record/__tests__/main-pass-geometry.unit.test.ts +47 -0
  392. package/src/record/__tests__/material-bg-cache.unit.test.ts +12 -1
  393. package/src/record/__tests__/material-pipeline-pending-cache.unit.test.ts +22 -0
  394. package/src/record/__tests__/sprite-material-abi.unit.test.ts +75 -0
  395. package/src/record/__tests__/standard-pbr-ubo-layout.unit.test.ts +67 -0
  396. package/src/record/__tests__/temporal-payload.unit.test.ts +164 -0
  397. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +127 -0
  398. package/src/record/frame-lighting.ts +103 -89
  399. package/src/record/frame-snapshot.ts +170 -6
  400. package/src/record/frame-targets.ts +21 -8
  401. package/src/record/frame.ts +1014 -7
  402. package/src/record/gpu-timing.ts +224 -0
  403. package/src/record/main-pass-geometry.ts +86 -40
  404. package/src/record/main-pass-material.ts +120 -68
  405. package/src/record/main-pass-sprite-draws.ts +78 -101
  406. package/src/record/main-pass.ts +77 -4
  407. package/src/record/mesh-ssbo.ts +22 -3
  408. package/src/record/render-context.ts +194 -23
  409. package/src/record/shadow-pass.ts +197 -58
  410. package/src/record/skybox-post-pass.ts +97 -41
  411. package/src/record/typed-frame-graph.ts +704 -30
  412. package/src/record/view-ubo.ts +52 -4
  413. package/src/render-contract.ts +181 -6
  414. package/src/render-data.ts +50 -22
  415. package/src/render-graph-primitives.ts +18 -1
  416. package/src/render-pipeline.ts +144 -27
  417. package/src/render-system-extract.ts +522 -72
  418. package/src/render-system.ts +561 -41
  419. package/src/renderer-inspect.ts +66 -0
  420. package/src/scene/render-scene.ts +252 -7
  421. package/src/temporal/__tests__/taa-history-resources.unit.test.ts +45 -0
  422. package/src/temporal/coverage.ts +76 -0
  423. package/src/temporal/frame-coordinator.ts +311 -0
  424. package/src/temporal/frame.ts +128 -0
  425. package/src/temporal/gpu.ts +287 -0
  426. package/src/temporal/history.ts +92 -0
  427. package/src/temporal/index.ts +6 -0
  428. package/src/temporal/inspection.ts +97 -0
  429. package/src/temporal/scene-data-catalog.ts +200 -0
  430. package/src/temporal/scene-data.ts +149 -0
  431. package/src/temporal/standard-scene-data.ts +182 -0
  432. package/src/temporal/taa-history-resources.ts +128 -0
  433. package/src/temporal/taa-history-store.ts +79 -0
  434. package/src/temporal/taa-resolve.ts +34 -0
  435. package/src/temporal/temporal-view.ts +169 -0
  436. package/src/temporal/transaction.ts +17 -0
  437. package/src/temporal/view.ts +161 -0
  438. package/src/transmission/__tests__/capability.unit.test.ts +83 -0
  439. package/src/transmission/__tests__/composition.integration.test.ts +161 -0
  440. package/src/transmission/__tests__/inspection.unit.test.ts +241 -0
  441. package/src/transmission/__tests__/lifecycle.integration.test.ts +96 -0
  442. package/src/transmission/__tests__/oracle.unit.test.ts +131 -0
  443. package/src/transmission/__tests__/projection.unit.test.ts +71 -0
  444. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +437 -0
  445. package/src/transmission/__tests__/transmission-backdrop-topology.unit.test.ts +109 -0
  446. package/src/transmission/__tests__/transmission-backdrop.dawn.test.ts +105 -0
  447. package/src/transmission/backdrop.ts +572 -0
  448. package/src/transmission/inspection.ts +190 -0
  449. package/src/transmission/oracle.ts +141 -0
  450. package/src/transmission/projection.ts +83 -0
  451. package/src/typed-render-graph-primitives.ts +194 -11
  452. package/src/typed-shadow-atlas.ts +17 -0
  453. package/src/typed-shadow-passes.ts +6 -5
  454. package/src/volume/capability.ts +173 -0
  455. package/src/volume/component.ts +110 -0
  456. package/src/volume/extract.ts +21 -0
  457. package/src/volume/inspection.ts +78 -0
  458. package/src/volume/passes.ts +680 -0
  459. package/src/volume/recovery.ts +86 -0
  460. package/src/volume/resources.ts +236 -0
  461. package/src/volume/temporal.ts +89 -0
  462. package/dist/__tests__/authoring-path.unit.test.d.ts +0 -2
  463. package/dist/__tests__/authoring-path.unit.test.d.ts.map +0 -1
  464. package/dist/__tests__/backend-selection-rhi-capture.test-d.d.ts +0 -2
  465. package/dist/__tests__/backend-selection-rhi-capture.test-d.d.ts.map +0 -1
  466. package/dist/__tests__/callback-inference.test-d.d.ts +0 -2
  467. package/dist/__tests__/callback-inference.test-d.d.ts.map +0 -1
  468. package/dist/__tests__/cluster-binner.test.d.ts +0 -2
  469. package/dist/__tests__/cluster-binner.test.d.ts.map +0 -1
  470. package/dist/__tests__/cluster-grid-default-surface.unit.test.d.ts +0 -2
  471. package/dist/__tests__/cluster-grid-default-surface.unit.test.d.ts.map +0 -1
  472. package/dist/__tests__/construct-renderer-identity.unit.test.d.ts +0 -2
  473. package/dist/__tests__/construct-renderer-identity.unit.test.d.ts.map +0 -1
  474. package/dist/__tests__/device-scope-lifecycle.unit.test.d.ts +0 -2
  475. package/dist/__tests__/device-scope-lifecycle.unit.test.d.ts.map +0 -1
  476. package/dist/__tests__/device-scope-stale-matrix.unit.test.d.ts +0 -2
  477. package/dist/__tests__/device-scope-stale-matrix.unit.test.d.ts.map +0 -1
  478. package/dist/__tests__/direct-extract-consumer-contract.test-d.d.ts +0 -2
  479. package/dist/__tests__/direct-extract-consumer-contract.test-d.d.ts.map +0 -1
  480. package/dist/__tests__/direct-light-snapshot.unit.test.d.ts +0 -2
  481. package/dist/__tests__/direct-light-snapshot.unit.test.d.ts.map +0 -1
  482. package/dist/__tests__/factory-contract.integration.test.d.ts +0 -2
  483. package/dist/__tests__/factory-contract.integration.test.d.ts.map +0 -1
  484. package/dist/__tests__/fallback-row-stride-surface.unit.test.d.ts +0 -2
  485. package/dist/__tests__/fallback-row-stride-surface.unit.test.d.ts.map +0 -1
  486. package/dist/__tests__/frame-observation.contract.test.d.ts +0 -2
  487. package/dist/__tests__/frame-observation.contract.test.d.ts.map +0 -1
  488. package/dist/__tests__/fullscreen-feature-plan.unit.test.d.ts +0 -2
  489. package/dist/__tests__/fullscreen-feature-plan.unit.test.d.ts.map +0 -1
  490. package/dist/__tests__/glyph-text-layout-recovery.unit.test.d.ts +0 -2
  491. package/dist/__tests__/glyph-text-layout-recovery.unit.test.d.ts.map +0 -1
  492. package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts +0 -6
  493. package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts.map +0 -1
  494. package/dist/__tests__/gpu-driven-indirect-raster.browser.test.d.ts +0 -2
  495. package/dist/__tests__/gpu-driven-indirect-raster.browser.test.d.ts.map +0 -1
  496. package/dist/__tests__/gpu-driven-indirect-raster.dawn.test.d.ts +0 -2
  497. package/dist/__tests__/gpu-driven-indirect-raster.dawn.test.d.ts.map +0 -1
  498. package/dist/__tests__/gpu-driven-production.integration.test.d.ts +0 -2
  499. package/dist/__tests__/gpu-driven-production.integration.test.d.ts.map +0 -1
  500. package/dist/__tests__/gpu-driven-scaling.unit.test.d.ts +0 -2
  501. package/dist/__tests__/gpu-driven-scaling.unit.test.d.ts.map +0 -1
  502. package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts +0 -18
  503. package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts.map +0 -1
  504. package/dist/__tests__/gpu-driven-view-graph.integration.test.d.ts +0 -2
  505. package/dist/__tests__/gpu-driven-view-graph.integration.test.d.ts.map +0 -1
  506. package/dist/__tests__/gpu-driven-view-reference.d.ts +0 -15
  507. package/dist/__tests__/gpu-driven-view-reference.d.ts.map +0 -1
  508. package/dist/__tests__/gpu-driven-view.browser.test.d.ts +0 -2
  509. package/dist/__tests__/gpu-driven-view.browser.test.d.ts.map +0 -1
  510. package/dist/__tests__/gpu-driven-view.dawn.test.d.ts +0 -2
  511. package/dist/__tests__/gpu-driven-view.dawn.test.d.ts.map +0 -1
  512. package/dist/__tests__/gpu-driven-view.unit.test.d.ts +0 -2
  513. package/dist/__tests__/gpu-driven-view.unit.test.d.ts.map +0 -1
  514. package/dist/__tests__/gpu-resource-store-stride.unit.test.d.ts +0 -2
  515. package/dist/__tests__/gpu-resource-store-stride.unit.test.d.ts.map +0 -1
  516. package/dist/__tests__/gpu-scene-render-graph-gpu.d.ts +0 -7
  517. package/dist/__tests__/gpu-scene-render-graph-gpu.d.ts.map +0 -1
  518. package/dist/__tests__/gpu-scene-render-graph.browser.test.d.ts +0 -2
  519. package/dist/__tests__/gpu-scene-render-graph.browser.test.d.ts.map +0 -1
  520. package/dist/__tests__/gpu-scene-render-graph.dawn.test.d.ts +0 -2
  521. package/dist/__tests__/gpu-scene-render-graph.dawn.test.d.ts.map +0 -1
  522. package/dist/__tests__/gpu-scene-schema.unit.test.d.ts +0 -2
  523. package/dist/__tests__/gpu-scene-schema.unit.test.d.ts.map +0 -1
  524. package/dist/__tests__/gpu-scene.dawn.test.d.ts +0 -2
  525. package/dist/__tests__/gpu-scene.dawn.test.d.ts.map +0 -1
  526. package/dist/__tests__/gpu-scene.unit.test.d.ts +0 -2
  527. package/dist/__tests__/gpu-scene.unit.test.d.ts.map +0 -1
  528. package/dist/__tests__/ibl/face-uniforms.unit.test.d.ts +0 -2
  529. package/dist/__tests__/ibl/face-uniforms.unit.test.d.ts.map +0 -1
  530. package/dist/__tests__/infinite-grid-no-vertex-probe.unit.test.d.ts +0 -2
  531. package/dist/__tests__/infinite-grid-no-vertex-probe.unit.test.d.ts.map +0 -1
  532. package/dist/__tests__/injection-kind-owner.test-d.d.ts +0 -2
  533. package/dist/__tests__/injection-kind-owner.test-d.d.ts.map +0 -1
  534. package/dist/__tests__/ktx2-basis-gpu-consumer.dawn.test.d.ts +0 -2
  535. package/dist/__tests__/ktx2-basis-gpu-consumer.dawn.test.d.ts.map +0 -1
  536. package/dist/__tests__/light-buffer-layout.unit.test.d.ts +0 -2
  537. package/dist/__tests__/light-buffer-layout.unit.test.d.ts.map +0 -1
  538. package/dist/__tests__/material-contract-inventory.unit.test.d.ts +0 -2
  539. package/dist/__tests__/material-contract-inventory.unit.test.d.ts.map +0 -1
  540. package/dist/__tests__/material-cooked-projection.unit.test.d.ts +0 -2
  541. package/dist/__tests__/material-cooked-projection.unit.test.d.ts.map +0 -1
  542. package/dist/__tests__/material-snapshot-mutation.integration.test.d.ts +0 -2
  543. package/dist/__tests__/material-snapshot-mutation.integration.test.d.ts.map +0 -1
  544. package/dist/__tests__/material-static-dynamic-route.unit.test.d.ts +0 -2
  545. package/dist/__tests__/material-static-dynamic-route.unit.test.d.ts.map +0 -1
  546. package/dist/__tests__/material-stride-surface.unit.test.d.ts +0 -2
  547. package/dist/__tests__/material-stride-surface.unit.test.d.ts.map +0 -1
  548. package/dist/__tests__/mesh-buffer-usage-surface.unit.test.d.ts +0 -2
  549. package/dist/__tests__/mesh-buffer-usage-surface.unit.test.d.ts.map +0 -1
  550. package/dist/__tests__/mesh-material-bindings.unit.test.d.ts +0 -2
  551. package/dist/__tests__/mesh-material-bindings.unit.test.d.ts.map +0 -1
  552. package/dist/__tests__/mesh-stride-surface.unit.test.d.ts +0 -2
  553. package/dist/__tests__/mesh-stride-surface.unit.test.d.ts.map +0 -1
  554. package/dist/__tests__/morph-culling-reentry.dawn.test.d.ts +0 -2
  555. package/dist/__tests__/morph-culling-reentry.dawn.test.d.ts.map +0 -1
  556. package/dist/__tests__/morph-feature-gpu-work.integration.test.d.ts +0 -2
  557. package/dist/__tests__/morph-feature-gpu-work.integration.test.d.ts.map +0 -1
  558. package/dist/__tests__/morph-feature-plan.unit.test.d.ts +0 -2
  559. package/dist/__tests__/morph-feature-plan.unit.test.d.ts.map +0 -1
  560. package/dist/__tests__/morph-visual-evidence.integration.test.d.ts +0 -2
  561. package/dist/__tests__/morph-visual-evidence.integration.test.d.ts.map +0 -1
  562. package/dist/__tests__/nineslice-handle-surface.unit.test.d.ts +0 -2
  563. package/dist/__tests__/nineslice-handle-surface.unit.test.d.ts.map +0 -1
  564. package/dist/__tests__/post-process-error-policy-owner.unit.test.d.ts +0 -2
  565. package/dist/__tests__/post-process-error-policy-owner.unit.test.d.ts.map +0 -1
  566. package/dist/__tests__/prepared-buffer-kind-owner.test-d.d.ts +0 -2
  567. package/dist/__tests__/prepared-buffer-kind-owner.test-d.d.ts.map +0 -1
  568. package/dist/__tests__/prepared-graphics-capability.unit.test.d.ts +0 -2
  569. package/dist/__tests__/prepared-graphics-capability.unit.test.d.ts.map +0 -1
  570. package/dist/__tests__/prepared-graphics-generation.unit.test.d.ts +0 -2
  571. package/dist/__tests__/prepared-graphics-generation.unit.test.d.ts.map +0 -1
  572. package/dist/__tests__/prepared-graphics-lifecycle.unit.test.d.ts +0 -2
  573. package/dist/__tests__/prepared-graphics-lifecycle.unit.test.d.ts.map +0 -1
  574. package/dist/__tests__/prepared-graphics-pipeline-warmup.unit.test.d.ts +0 -2
  575. package/dist/__tests__/prepared-graphics-pipeline-warmup.unit.test.d.ts.map +0 -1
  576. package/dist/__tests__/prepared-graphics-resolver.unit.test.d.ts +0 -2
  577. package/dist/__tests__/prepared-graphics-resolver.unit.test.d.ts.map +0 -1
  578. package/dist/__tests__/prepared-graphics-snapshot.unit.test.d.ts +0 -2
  579. package/dist/__tests__/prepared-graphics-snapshot.unit.test.d.ts.map +0 -1
  580. package/dist/__tests__/prepared-graphics-store.unit.test.d.ts +0 -2
  581. package/dist/__tests__/prepared-graphics-store.unit.test.d.ts.map +0 -1
  582. package/dist/__tests__/prepared-graphics-validation.unit.test.d.ts +0 -2
  583. package/dist/__tests__/prepared-graphics-validation.unit.test.d.ts.map +0 -1
  584. package/dist/__tests__/profiler-default-off.test.d.ts +0 -2
  585. package/dist/__tests__/profiler-default-off.test.d.ts.map +0 -1
  586. package/dist/__tests__/profiler-phase-catalog.test.d.ts +0 -2
  587. package/dist/__tests__/profiler-phase-catalog.test.d.ts.map +0 -1
  588. package/dist/__tests__/profiler-phase-integration.test.d.ts +0 -2
  589. package/dist/__tests__/profiler-phase-integration.test.d.ts.map +0 -1
  590. package/dist/__tests__/profiler-phase-token.test.d.ts +0 -2
  591. package/dist/__tests__/profiler-phase-token.test.d.ts.map +0 -1
  592. package/dist/__tests__/public-surface.test-d.d.ts +0 -2
  593. package/dist/__tests__/public-surface.test-d.d.ts.map +0 -1
  594. package/dist/__tests__/recover-error-policy-owner.unit.test.d.ts +0 -2
  595. package/dist/__tests__/recover-error-policy-owner.unit.test.d.ts.map +0 -1
  596. package/dist/__tests__/render-error-code-owner.test-d.d.ts +0 -2
  597. package/dist/__tests__/render-error-code-owner.test-d.d.ts.map +0 -1
  598. package/dist/__tests__/render-error-detail-ssot.test-d.d.ts +0 -2
  599. package/dist/__tests__/render-error-detail-ssot.test-d.d.ts.map +0 -1
  600. package/dist/__tests__/render-error-exhaustive.test-d.d.ts +0 -2
  601. package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +0 -1
  602. package/dist/__tests__/render-feature-assembly.test-d.d.ts +0 -2
  603. package/dist/__tests__/render-feature-assembly.test-d.d.ts.map +0 -1
  604. package/dist/__tests__/render-feature-context.test-d.d.ts +0 -2
  605. package/dist/__tests__/render-feature-context.test-d.d.ts.map +0 -1
  606. package/dist/__tests__/render-feature-contract.test-d.d.ts +0 -2
  607. package/dist/__tests__/render-feature-contract.test-d.d.ts.map +0 -1
  608. package/dist/__tests__/render-feature-diagnostics.unit.test.d.ts +0 -2
  609. package/dist/__tests__/render-feature-diagnostics.unit.test.d.ts.map +0 -1
  610. package/dist/__tests__/render-feature-docs.unit.test.d.ts +0 -2
  611. package/dist/__tests__/render-feature-docs.unit.test.d.ts.map +0 -1
  612. package/dist/__tests__/render-feature-errors.unit.test.d.ts +0 -2
  613. package/dist/__tests__/render-feature-errors.unit.test.d.ts.map +0 -1
  614. package/dist/__tests__/render-feature-gpu-work.integration.test.d.ts +0 -2
  615. package/dist/__tests__/render-feature-gpu-work.integration.test.d.ts.map +0 -1
  616. package/dist/__tests__/render-feature-plan.test-d.d.ts +0 -2
  617. package/dist/__tests__/render-feature-plan.test-d.d.ts.map +0 -1
  618. package/dist/__tests__/render-feature-plan.unit.test.d.ts +0 -2
  619. package/dist/__tests__/render-feature-plan.unit.test.d.ts.map +0 -1
  620. package/dist/__tests__/render-feature-prepared-diagnostics.unit.test.d.ts +0 -2
  621. package/dist/__tests__/render-feature-prepared-diagnostics.unit.test.d.ts.map +0 -1
  622. package/dist/__tests__/render-feature-prepared-errors.test-d.d.ts +0 -2
  623. package/dist/__tests__/render-feature-prepared-errors.test-d.d.ts.map +0 -1
  624. package/dist/__tests__/render-feature-prepared-graphics-docs.unit.test.d.ts +0 -2
  625. package/dist/__tests__/render-feature-prepared-graphics-docs.unit.test.d.ts.map +0 -1
  626. package/dist/__tests__/render-feature-prepared-graphics-isolation.unit.test.d.ts +0 -2
  627. package/dist/__tests__/render-feature-prepared-graphics-isolation.unit.test.d.ts.map +0 -1
  628. package/dist/__tests__/render-feature-prepared-graphics-negative.test-d.d.ts +0 -2
  629. package/dist/__tests__/render-feature-prepared-graphics-negative.test-d.d.ts.map +0 -1
  630. package/dist/__tests__/render-feature-prepared-graphics.test-d.d.ts +0 -2
  631. package/dist/__tests__/render-feature-prepared-graphics.test-d.d.ts.map +0 -1
  632. package/dist/__tests__/render-feature-prepared-graphics.unit.test.d.ts +0 -2
  633. package/dist/__tests__/render-feature-prepared-graphics.unit.test.d.ts.map +0 -1
  634. package/dist/__tests__/render-feature-public-surface.test-d.d.ts +0 -2
  635. package/dist/__tests__/render-feature-public-surface.test-d.d.ts.map +0 -1
  636. package/dist/__tests__/render-feature-retry.unit.test.d.ts +0 -2
  637. package/dist/__tests__/render-feature-retry.unit.test.d.ts.map +0 -1
  638. package/dist/__tests__/render-feature-scheduling.unit.test.d.ts +0 -2
  639. package/dist/__tests__/render-feature-scheduling.unit.test.d.ts.map +0 -1
  640. package/dist/__tests__/render-feature-stage-failed-policy-owner.unit.test.d.ts +0 -2
  641. package/dist/__tests__/render-feature-stage-failed-policy-owner.unit.test.d.ts.map +0 -1
  642. package/dist/__tests__/render-feature-stage-fault-recovery.unit.test.d.ts +0 -2
  643. package/dist/__tests__/render-feature-stage-fault-recovery.unit.test.d.ts.map +0 -1
  644. package/dist/__tests__/render-feature-visibility-context.test-d.d.ts +0 -2
  645. package/dist/__tests__/render-feature-visibility-context.test-d.d.ts.map +0 -1
  646. package/dist/__tests__/render-feature-visibility-context.unit.test.d.ts +0 -2
  647. package/dist/__tests__/render-feature-visibility-context.unit.test.d.ts.map +0 -1
  648. package/dist/__tests__/render-feature-zero-work.unit.test.d.ts +0 -2
  649. package/dist/__tests__/render-feature-zero-work.unit.test.d.ts.map +0 -1
  650. package/dist/__tests__/render-graph-compute-raster-gpu.d.ts +0 -7
  651. package/dist/__tests__/render-graph-compute-raster-gpu.d.ts.map +0 -1
  652. package/dist/__tests__/render-graph-compute-raster.browser.test.d.ts +0 -2
  653. package/dist/__tests__/render-graph-compute-raster.browser.test.d.ts.map +0 -1
  654. package/dist/__tests__/render-graph-compute-raster.dawn.test.d.ts +0 -2
  655. package/dist/__tests__/render-graph-compute-raster.dawn.test.d.ts.map +0 -1
  656. package/dist/__tests__/render-graph-hzb-gpu.d.ts +0 -7
  657. package/dist/__tests__/render-graph-hzb-gpu.d.ts.map +0 -1
  658. package/dist/__tests__/render-graph-hzb.browser.test.d.ts +0 -2
  659. package/dist/__tests__/render-graph-hzb.browser.test.d.ts.map +0 -1
  660. package/dist/__tests__/render-graph-hzb.dawn.test.d.ts +0 -2
  661. package/dist/__tests__/render-graph-hzb.dawn.test.d.ts.map +0 -1
  662. package/dist/__tests__/render-graph-primitives.unit.test.d.ts +0 -2
  663. package/dist/__tests__/render-graph-primitives.unit.test.d.ts.map +0 -1
  664. package/dist/__tests__/render-graph-storage-indirect-gpu.d.ts +0 -12
  665. package/dist/__tests__/render-graph-storage-indirect-gpu.d.ts.map +0 -1
  666. package/dist/__tests__/render-graph-storage-indirect.browser.test.d.ts +0 -2
  667. package/dist/__tests__/render-graph-storage-indirect.browser.test.d.ts.map +0 -1
  668. package/dist/__tests__/render-graph-storage-indirect.dawn.test.d.ts +0 -2
  669. package/dist/__tests__/render-graph-storage-indirect.dawn.test.d.ts.map +0 -1
  670. package/dist/__tests__/render-scene-projection.unit.test.d.ts +0 -2
  671. package/dist/__tests__/render-scene-projection.unit.test.d.ts.map +0 -1
  672. package/dist/__tests__/renderer-factory-material-contract.unit.test.d.ts +0 -2
  673. package/dist/__tests__/renderer-factory-material-contract.unit.test.d.ts.map +0 -1
  674. package/dist/__tests__/renderer-lifecycle.integration.test.d.ts +0 -2
  675. package/dist/__tests__/renderer-lifecycle.integration.test.d.ts.map +0 -1
  676. package/dist/__tests__/rhi-backend-package-boundary.unit.test.d.ts +0 -2
  677. package/dist/__tests__/rhi-backend-package-boundary.unit.test.d.ts.map +0 -1
  678. package/dist/__tests__/scriptable-pack-consumer.test-d.d.ts +0 -2
  679. package/dist/__tests__/scriptable-pack-consumer.test-d.d.ts.map +0 -1
  680. package/dist/__tests__/shader-stage-surface.unit.test.d.ts +0 -2
  681. package/dist/__tests__/shader-stage-surface.unit.test.d.ts.map +0 -1
  682. package/dist/__tests__/ssao-config.unit.test.d.ts +0 -2
  683. package/dist/__tests__/ssao-config.unit.test.d.ts.map +0 -1
  684. package/dist/__tests__/ssao-kernel-surface.unit.test.d.ts +0 -2
  685. package/dist/__tests__/ssao-kernel-surface.unit.test.d.ts.map +0 -1
  686. package/dist/__tests__/standard-clustered-pipeline.unit.test.d.ts +0 -2
  687. package/dist/__tests__/standard-clustered-pipeline.unit.test.d.ts.map +0 -1
  688. package/dist/__tests__/standard-descriptors.test-d.d.ts +0 -2
  689. package/dist/__tests__/standard-descriptors.test-d.d.ts.map +0 -1
  690. package/dist/__tests__/standard-pipeline.dawn.test.d.ts +0 -2
  691. package/dist/__tests__/standard-pipeline.dawn.test.d.ts.map +0 -1
  692. package/dist/__tests__/standard-pipeline.integration.test.d.ts +0 -2
  693. package/dist/__tests__/standard-pipeline.integration.test.d.ts.map +0 -1
  694. package/dist/__tests__/standard-profile-oracle.unit.test.d.ts +0 -2
  695. package/dist/__tests__/standard-profile-oracle.unit.test.d.ts.map +0 -1
  696. package/dist/__tests__/surface-configuration.unit.test.d.ts +0 -2
  697. package/dist/__tests__/surface-configuration.unit.test.d.ts.map +0 -1
  698. package/dist/__tests__/texture-usage-surface.unit.test.d.ts +0 -2
  699. package/dist/__tests__/texture-usage-surface.unit.test.d.ts.map +0 -1
  700. package/dist/__tests__/typed-pipeline-topology.unit.test.d.ts +0 -2
  701. package/dist/__tests__/typed-pipeline-topology.unit.test.d.ts.map +0 -1
  702. package/dist/__tests__/urp-pipeline-targets.unit.test.d.ts +0 -2
  703. package/dist/__tests__/urp-pipeline-targets.unit.test.d.ts.map +0 -1
  704. package/dist/__tests__/urp-pipeline.unit.test.d.ts +0 -2
  705. package/dist/__tests__/urp-pipeline.unit.test.d.ts.map +0 -1
  706. package/dist/__tests__/vertex-color-pipeline.unit.test.d.ts +0 -2
  707. package/dist/__tests__/vertex-color-pipeline.unit.test.d.ts.map +0 -1
  708. package/dist/__tests__/vertex-layout-consumer.unit.test.d.ts +0 -2
  709. package/dist/__tests__/vertex-layout-consumer.unit.test.d.ts.map +0 -1
  710. package/dist/__tests__/visibility-diagnostics.integration.test.d.ts +0 -2
  711. package/dist/__tests__/visibility-diagnostics.integration.test.d.ts.map +0 -1
  712. package/dist/__tests__/visibility-extract.integration.test.d.ts +0 -2
  713. package/dist/__tests__/visibility-extract.integration.test.d.ts.map +0 -1
  714. package/dist/__tests__/visibility-extract.perf.test.d.ts +0 -2
  715. package/dist/__tests__/visibility-extract.perf.test.d.ts.map +0 -1
  716. package/dist/__tests__/visibility-feature-dedup.integration.test.d.ts +0 -2
  717. package/dist/__tests__/visibility-feature-dedup.integration.test.d.ts.map +0 -1
  718. package/dist/__tests__/visibility-hidden-report.unit.test.d.ts +0 -2
  719. package/dist/__tests__/visibility-hidden-report.unit.test.d.ts.map +0 -1
  720. package/dist/__tests__/visibility-instances.integration.test.d.ts +0 -2
  721. package/dist/__tests__/visibility-instances.integration.test.d.ts.map +0 -1
  722. package/dist/__tests__/visibility-multi-world.integration.test.d.ts +0 -2
  723. package/dist/__tests__/visibility-multi-world.integration.test.d.ts.map +0 -1
  724. package/dist/__tests__/visibility-producer-matrix.integration.test.d.ts +0 -2
  725. package/dist/__tests__/visibility-producer-matrix.integration.test.d.ts.map +0 -1
  726. package/dist/__tests__/visibility-producer-matrix.unit.test.d.ts +0 -2
  727. package/dist/__tests__/visibility-producer-matrix.unit.test.d.ts.map +0 -1
  728. package/dist/__tests__/visibility-public-surface.test-d.d.ts +0 -2
  729. package/dist/__tests__/visibility-public-surface.test-d.d.ts.map +0 -1
  730. package/dist/__tests__/visibility-resolver.unit.test.d.ts +0 -2
  731. package/dist/__tests__/visibility-resolver.unit.test.d.ts.map +0 -1
  732. package/dist/__tests__/visibility-resource-short-circuit.unit.test.d.ts +0 -2
  733. package/dist/__tests__/visibility-resource-short-circuit.unit.test.d.ts.map +0 -1
  734. package/dist/__tests__/visibility-snapshot-contract.unit.test.d.ts +0 -2
  735. package/dist/__tests__/visibility-snapshot-contract.unit.test.d.ts.map +0 -1
  736. package/dist/__tests__/vocabulary.unit.test.d.ts +0 -2
  737. package/dist/__tests__/vocabulary.unit.test.d.ts.map +0 -1
  738. package/dist/chunk-CQXZC5XI.mjs.map +0 -1
  739. package/dist/chunk-HCOGPHKS.mjs +0 -93
  740. package/dist/chunk-HCOGPHKS.mjs.map +0 -1
  741. package/dist/chunk-YEJIUW7D.mjs.map +0 -1
  742. package/dist/components/__tests__/points-lines-admission.unit.test.d.ts +0 -2
  743. package/dist/components/__tests__/points-lines-admission.unit.test.d.ts.map +0 -1
  744. package/dist/components/__tests__/points-lines-schema.unit.test.d.ts +0 -2
  745. package/dist/components/__tests__/points-lines-schema.unit.test.d.ts.map +0 -1
  746. package/dist/components/__tests__/schema-contract.unit.test.d.ts +0 -2
  747. package/dist/components/__tests__/schema-contract.unit.test.d.ts.map +0 -1
  748. package/dist/components/__tests__/schema-parity.unit.test.d.ts +0 -2
  749. package/dist/components/__tests__/schema-parity.unit.test.d.ts.map +0 -1
  750. package/dist/components/__tests__/visibility-schema.unit.test.d.ts +0 -2
  751. package/dist/components/__tests__/visibility-schema.unit.test.d.ts.map +0 -1
  752. package/dist/points-lines/__tests__/bounds.unit.test.d.ts +0 -2
  753. package/dist/points-lines/__tests__/bounds.unit.test.d.ts.map +0 -1
  754. package/dist/points-lines/__tests__/cache-recovery.unit.test.d.ts +0 -2
  755. package/dist/points-lines/__tests__/cache-recovery.unit.test.d.ts.map +0 -1
  756. package/dist/points-lines/__tests__/inspection-lanes.integration.test.d.ts +0 -2
  757. package/dist/points-lines/__tests__/inspection-lanes.integration.test.d.ts.map +0 -1
  758. package/dist/points-lines/__tests__/inspection.unit.test.d.ts +0 -2
  759. package/dist/points-lines/__tests__/inspection.unit.test.d.ts.map +0 -1
  760. package/dist/points-lines/__tests__/lane-structural.integration.test.d.ts +0 -2
  761. package/dist/points-lines/__tests__/lane-structural.integration.test.d.ts.map +0 -1
  762. package/dist/points-lines/__tests__/points-lines.bench.d.ts +0 -2
  763. package/dist/points-lines/__tests__/points-lines.bench.d.ts.map +0 -1
  764. package/dist/points-lines/__tests__/raster-lane.integration.test.d.ts +0 -2
  765. package/dist/points-lines/__tests__/raster-lane.integration.test.d.ts.map +0 -1
  766. package/dist/points-lines/__tests__/snapshot.unit.test.d.ts +0 -2
  767. package/dist/points-lines/__tests__/snapshot.unit.test.d.ts.map +0 -1
  768. package/dist/points-lines/__tests__/view-ubo.unit.test.d.ts +0 -2
  769. package/dist/points-lines/__tests__/view-ubo.unit.test.d.ts.map +0 -1
  770. package/dist/record/__tests__/material-bg-cache.unit.test.d.ts +0 -2
  771. package/dist/record/__tests__/material-bg-cache.unit.test.d.ts.map +0 -1
  772. package/dist/record/__tests__/material-slot-plan.unit.test.d.ts +0 -2
  773. package/dist/record/__tests__/material-slot-plan.unit.test.d.ts.map +0 -1
  774. package/dist/record/__tests__/typed-frame-graph-plan.unit.test.d.ts +0 -2
  775. package/dist/record/__tests__/typed-frame-graph-plan.unit.test.d.ts.map +0 -1
  776. package/dist/record/__tests__/video-texture-view.unit.test.d.ts +0 -2
  777. package/dist/record/__tests__/video-texture-view.unit.test.d.ts.map +0 -1
@@ -4,11 +4,13 @@
4
4
  import { HANDLE_NINESLICE_QUAD, resolveAssetHandle } from '@forgeax/engine-assets-runtime';
5
5
  import type { World } from '@forgeax/engine-ecs';
6
6
  import type { RenderReadLease } from '@forgeax/engine-ecs/projection';
7
+ import { mat4, vec3 } from '@forgeax/engine-math';
7
8
  import {
8
9
  type CurrentFrameObservationLease,
9
10
  createCurrentFrameObservationLease,
10
11
  } from '@forgeax/engine-render-graph';
11
12
  import {
13
+ type Buffer,
12
14
  err,
13
15
  ok,
14
16
  type Result,
@@ -21,14 +23,25 @@ import type { MaterialRenderState, MeshAsset } from '@forgeax/engine-types';
21
23
  import { handleSlot, toShared } from '@forgeax/engine-types';
22
24
  import type { MeshGpuHandles } from '../device/gpu-residency';
23
25
  import { ObservationUnavailableError, type ObservationUnavailableReason } from '../errors/render';
26
+ import type { FogFrame } from '../extract/environment';
24
27
  import type { GpuDrivenProduction, PreparedGpuDrivenFrame } from '../gpu-driven/production-raster';
25
28
  import { GpuBuffer } from '../gpu-resource';
26
- import { GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_VERTEX } from '../gpu-usage';
29
+ import {
30
+ GPU_BUFFER_USAGE_COPY_DST,
31
+ GPU_BUFFER_USAGE_UNIFORM,
32
+ GPU_BUFFER_USAGE_VERTEX,
33
+ } from '../gpu-usage';
27
34
  import { disposeTransientInstanceBuffers } from '../instance-buffer-cache';
28
- import type { CameraSnapshot, RenderRecordPhase } from '../render-contract';
35
+ import type {
36
+ CameraSnapshot,
37
+ RenderRecordPhase,
38
+ SpotLightProjectorFrameContext,
39
+ VolumetricFogFrameContext,
40
+ } from '../render-contract';
29
41
  import type {
30
42
  DispatchEntry,
31
43
  ExtractedLights,
44
+ ExtractedVolumetricFog,
32
45
  MaterialSnapshot,
33
46
  RenderableSnapshot,
34
47
  SkyboxSnapshot,
@@ -40,6 +53,10 @@ import {
40
53
  TRANSPARENT_SORT_MODE_LAYER_Y,
41
54
  TRANSPARENT_SORT_MODE_LAYER_Z,
42
55
  } from '../systems/transparent-sort-config';
56
+ import type { TransmissionDemand } from '../transmission/projection';
57
+ import { hasVolumetricFogCapability } from '../volume/capability';
58
+ import { inspectVolumetricFogResources, VOLUMETRIC_FOG_PARAMS_BYTES } from '../volume/resources';
59
+ import { resolveVolumeTemporalReset, type VolumeTemporalSignature } from '../volume/temporal';
43
60
  import {
44
61
  buildPerFrameBindGroups,
45
62
  prepareFrameLighting,
@@ -184,13 +201,17 @@ export async function observeCurrentFrame(
184
201
  });
185
202
  }
186
203
 
204
+ import { createTemporalView, resolveTemporalReset } from '../temporal/view';
205
+ import { inspectVolumetricFog } from '../volume/inspection';
187
206
  import {
188
207
  acquireSwapChainTarget,
189
208
  graphExecutionPhase,
190
209
  resolveShadowMapSize,
191
210
  resolveSpotShadowMapSize,
192
211
  } from './frame-targets';
212
+ import type { GpuTimingCapture } from './gpu-timing';
193
213
  import {
214
+ computeProjectionMatrix,
194
215
  computeViewMatrix,
195
216
  driveLazyEquirectProjection,
196
217
  selectLazyEquirectHandle,
@@ -205,6 +226,7 @@ import {
205
226
  ensureCompiledFrameGraph,
206
227
  executeCompiledFrameGraph,
207
228
  type RenderFeatureGraphCandidate,
229
+ settleVolumetricFogGraphCandidate,
208
230
  } from './typed-frame-graph';
209
231
  import { writePointsLinesViewUbo, writeViewUbo } from './view-ubo';
210
232
 
@@ -289,6 +311,703 @@ function runRecordProfilePhase<T>(
289
311
  return runner === undefined ? action() : runner(phase, action);
290
312
  }
291
313
 
314
+ const VOLUMETRIC_FOG_PASS_COUNT = 4;
315
+
316
+ type VolumetricFogParamsSlot = 0 | 1;
317
+
318
+ function nextVolumetricFogParamsSlot(frameState: RenderFrameState): VolumetricFogParamsSlot {
319
+ const accepted = frameState.volumetricFogParamsAcceptedSlot;
320
+ const pending = frameState.volumetricFogParamsPendingSlot;
321
+ if (pending !== null && pending !== accepted) return pending;
322
+ return accepted === null ? 0 : accepted === 0 ? 1 : 0;
323
+ }
324
+
325
+ function stageVolumetricFogParams(
326
+ internals: RenderSystemInternals,
327
+ frameState: RenderFrameState,
328
+ params: Float32Array,
329
+ ): Buffer | undefined {
330
+ const slot = nextVolumetricFogParamsSlot(frameState);
331
+ let buffer = frameState.volumetricFogParamsBuffers[slot];
332
+ if (buffer === null) {
333
+ const created = internals.device.createBuffer({
334
+ label: `volumetric-fog-params-${slot}`,
335
+ size: VOLUMETRIC_FOG_PARAMS_BYTES,
336
+ usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
337
+ mappedAtCreation: false,
338
+ });
339
+ if (!created.ok) {
340
+ internals.errorRegistry.fire(created.error);
341
+ return undefined;
342
+ }
343
+ buffer = created.value;
344
+ frameState.volumetricFogParamsBuffers[slot] = buffer;
345
+ }
346
+ const written = internals.device.queue.writeBuffer(buffer, 0, params);
347
+ if (!written.ok) {
348
+ internals.errorRegistry.fire(written.error);
349
+ return undefined;
350
+ }
351
+ frameState.volumetricFogParamsPendingSlot = slot;
352
+ frameState.volumetricFogPendingParams = new Float32Array(params);
353
+ return buffer;
354
+ }
355
+
356
+ /** Retire parameter buffers only after the submit that stopped using them. */
357
+ function retireVolumetricFogParams(
358
+ internals: RenderSystemInternals,
359
+ frameState: RenderFrameState,
360
+ ): void {
361
+ const buffers = frameState.volumetricFogParamsBuffers;
362
+ frameState.volumetricFogParamsBuffers = [null, null];
363
+ frameState.volumetricFogParamsPendingSlot = null;
364
+ frameState.volumetricFogParamsAcceptedSlot = null;
365
+ frameState.volumetricFogAcceptedParams = undefined;
366
+ frameState.volumetricFogPendingParams = undefined;
367
+ const live = buffers.filter((buffer): buffer is Buffer => buffer !== null);
368
+ if (live.length === 0) return;
369
+ const device = internals.device;
370
+ const release = (): void => {
371
+ for (const buffer of live) {
372
+ const destroyed = device.destroyBuffer(buffer);
373
+ if (!destroyed.ok) internals.errorRegistry.fire(destroyed.error);
374
+ }
375
+ };
376
+ try {
377
+ void device.queue.onSubmittedWorkDone().then(release, release);
378
+ } catch {
379
+ release();
380
+ }
381
+ }
382
+
383
+ function volumeRevision(values: readonly number[]): number {
384
+ let hash = 2166136261;
385
+ for (const value of values) {
386
+ const quantized = Number.isFinite(value) ? Math.round(value * 1_000_000) : 0;
387
+ hash = Math.imul(hash ^ quantized, 16777619) >>> 0;
388
+ }
389
+ return hash;
390
+ }
391
+
392
+ function volumeTemporalSignature(
393
+ fog: ExtractedVolumetricFog,
394
+ prepared: VolumetricFogFrameContext,
395
+ camera: CameraSnapshot,
396
+ lights: ExtractedLights,
397
+ width: number,
398
+ height: number,
399
+ ): VolumeTemporalSignature {
400
+ const authored = fog.fog;
401
+ const directional = lights.directional;
402
+ const cameraRevision = volumeRevision([
403
+ ...camera.world,
404
+ camera.fov,
405
+ camera.aspect,
406
+ camera.near,
407
+ camera.far,
408
+ camera.orthoLeft,
409
+ camera.orthoRight,
410
+ camera.orthoBottom,
411
+ camera.orthoTop,
412
+ ]);
413
+ const fogRevision =
414
+ authored === undefined
415
+ ? 0
416
+ : volumeRevision([
417
+ ...authored.bounds.min,
418
+ ...authored.bounds.max,
419
+ ...authored.extinction,
420
+ ...authored.albedo,
421
+ ...authored.emission,
422
+ authored.anisotropy,
423
+ authored.maxDistance,
424
+ ]);
425
+ const selectedSpot =
426
+ fog.spotLightEntity === undefined
427
+ ? fog.lightKind === 'spot'
428
+ ? lights.spot.find((spot) => spot.entity === fog.lightEntity)
429
+ : undefined
430
+ : lights.spot.find((spot) => spot.entity === fog.spotLightEntity);
431
+ const selectedPoint =
432
+ fog.pointLightEntity === undefined
433
+ ? fog.lightKind === 'point'
434
+ ? lights.point.find((point) => point.entity === fog.lightEntity)
435
+ : undefined
436
+ : lights.point.find((point) => point.entity === fog.pointLightEntity);
437
+ const selectedDirectional =
438
+ fog.lightKind === 'directional' && directional?.entity === fog.lightEntity
439
+ ? directional
440
+ : undefined;
441
+ const lightRevision =
442
+ selectedSpot === undefined && selectedPoint === undefined && selectedDirectional === undefined
443
+ ? 0
444
+ : volumeRevision(
445
+ selectedSpot === undefined && selectedPoint === undefined
446
+ ? selectedDirectional === undefined
447
+ ? []
448
+ : [
449
+ ...selectedDirectional.direction,
450
+ ...selectedDirectional.color,
451
+ selectedDirectional.intensity,
452
+ ...(lights.lightViewProj?.flatMap((matrix) => [...matrix]) ?? []),
453
+ ]
454
+ : [
455
+ ...(selectedPoint === undefined
456
+ ? []
457
+ : [
458
+ ...selectedPoint.position,
459
+ ...selectedPoint.color,
460
+ selectedPoint.intensity,
461
+ selectedPoint.invRangeSquared,
462
+ selectedPoint.shadowAtlasLayer ?? -1,
463
+ selectedPoint.shadowNear ?? 0,
464
+ selectedPoint.shadowFar ?? 0,
465
+ ]),
466
+ ...(selectedSpot === undefined
467
+ ? []
468
+ : [
469
+ ...selectedSpot.position,
470
+ ...selectedSpot.direction,
471
+ ...selectedSpot.color,
472
+ selectedSpot.intensity,
473
+ selectedSpot.invRangeSquared,
474
+ selectedSpot.cosInner,
475
+ selectedSpot.cosOuter,
476
+ selectedSpot.depthBias ?? 0.005,
477
+ selectedSpot.normalBias ?? 0.05,
478
+ selectedSpot.pcfKernelSize ?? 3,
479
+ selectedSpot.shadowAtlasTile,
480
+ ...(selectedSpot.lightViewProj === undefined
481
+ ? []
482
+ : [...selectedSpot.lightViewProj]),
483
+ ]),
484
+ ],
485
+ );
486
+ return {
487
+ cameraRevision,
488
+ fogRevision,
489
+ lightRevision,
490
+ densityGeneration: prepared.densityGeneration,
491
+ width,
492
+ height,
493
+ ...(fog.lightKind === undefined ? {} : { lightKind: fog.lightKind }),
494
+ ...(fog.lightEntity === undefined ? {} : { lightEntity: fog.lightEntity }),
495
+ ...(fog.pointLightEntity === undefined ? {} : { pointLightEntity: fog.pointLightEntity }),
496
+ ...(fog.spotLightEntity === undefined ? {} : { spotLightEntity: fog.spotLightEntity }),
497
+ ...(fog.projector === undefined ? {} : { projector: fog.projector }),
498
+ lightShadowRevision: lightRevision,
499
+ };
500
+ }
501
+
502
+ function resolveVolumetricFogHistoryContext(
503
+ frameState: RenderFrameState,
504
+ graph: import('@forgeax/engine-render-graph').CompiledRenderGraph<
505
+ import('../render-pipeline').RenderPipelineFrame
506
+ >,
507
+ fog: ExtractedVolumetricFog,
508
+ prepared: VolumetricFogFrameContext,
509
+ camera: CameraSnapshot,
510
+ lights: ExtractedLights,
511
+ width: number,
512
+ height: number,
513
+ ): { readonly context: VolumetricFogFrameContext; readonly signature: VolumeTemporalSignature } {
514
+ const signature = volumeTemporalSignature(fog, prepared, camera, lights, width, height);
515
+ const acceptedGraph = frameState.volumetricFogHistoryGraph;
516
+ const acceptedSlot = frameState.volumetricFogHistorySlot;
517
+ const previousSignature = frameState.volumetricFogHistorySignature;
518
+ let reset = acceptedGraph !== graph || acceptedSlot === null || previousSignature === null;
519
+ if (!reset && previousSignature !== null) {
520
+ const temporal = frameState.temporalFrame;
521
+ const previousResetReason = temporal?.resetReason;
522
+ const reprojection =
523
+ previousResetReason === 'out-of-screen'
524
+ ? 'out-of-screen'
525
+ : previousResetReason === 'depth-discontinuity'
526
+ ? 'depth-discontinuity'
527
+ : undefined;
528
+ reset = resolveVolumeTemporalReset(previousSignature, signature, {
529
+ taaEnabled: camera.antialias === 'taa',
530
+ ...(reprojection === undefined ? {} : { reprojection }),
531
+ }).reset;
532
+ }
533
+ const historyReadSlot = reset ? null : acceptedSlot;
534
+ const historyWriteSlot = reset || acceptedSlot === null ? 0 : acceptedSlot === 0 ? 1 : 0;
535
+ return {
536
+ context: {
537
+ ...prepared,
538
+ historyReadSlot,
539
+ historyWriteSlot,
540
+ historyValid: historyReadSlot !== null,
541
+ },
542
+ signature,
543
+ };
544
+ }
545
+
546
+ /**
547
+ * Resolve the authored density POD into the renderer-owned GPU resources used
548
+ * by the graph. This is deliberately a pull operation: a missing/stale
549
+ * handle or failed upload produces a structured error and leaves the graph
550
+ * candidate disabled for this frame rather than binding a synthetic density.
551
+ */
552
+ function prepareVolumetricFogFrame(
553
+ internals: RenderSystemInternals,
554
+ frameState: RenderFrameState,
555
+ world: World,
556
+ worlds: readonly World[],
557
+ fog: ExtractedVolumetricFog | undefined,
558
+ lights: ExtractedLights,
559
+ volumeCapability: boolean,
560
+ projector: SpotLightProjectorFrameContext | undefined,
561
+ ): VolumetricFogFrameContext | undefined {
562
+ if (
563
+ !volumeCapability ||
564
+ fog?.status !== 'available' ||
565
+ fog.fog === undefined ||
566
+ fog.densityHandle === undefined ||
567
+ fog.densityAsset === undefined ||
568
+ (fog.lightKind === 'spot'
569
+ ? lights.spot.find((light) => light.entity === fog.lightEntity) === undefined
570
+ : fog.lightKind === 'point'
571
+ ? lights.point.find((light) => light.entity === fog.lightEntity) === undefined
572
+ : lights.directional === undefined)
573
+ ) {
574
+ return undefined;
575
+ }
576
+ const ownerWorld = worlds[fog.worldId ?? 0] ?? world;
577
+ const resident = internals.gpuStore.ensureResident(
578
+ fog.densityHandle,
579
+ fog.densityAsset,
580
+ ownerWorld,
581
+ );
582
+ if (!resident.ok || !('texture' in resident.value)) {
583
+ if (!resident.ok) {
584
+ internals.errorRegistry.fire(
585
+ resident.error instanceof RhiError
586
+ ? resident.error
587
+ : new RhiError({
588
+ code: 'webgpu-runtime-error',
589
+ expected: 'volumetric density residency to resolve a GPU texture',
590
+ hint: 'repair the authored TextureAsset payload and retry the next frame',
591
+ detail: { error: resident.error },
592
+ }),
593
+ );
594
+ }
595
+ return undefined;
596
+ }
597
+ const params = new Float32Array(32);
598
+ params.set(fog.fog.bounds.min, 0);
599
+ params.set(fog.fog.bounds.max, 4);
600
+ params.set(fog.fog.extinction, 8);
601
+ params.set(fog.fog.albedo, 12);
602
+ params.set(fog.fog.emission, 16);
603
+ const selectedPoint =
604
+ fog.pointLightEntity === undefined
605
+ ? undefined
606
+ : lights.point.find((light) => light.entity === fog.pointLightEntity);
607
+ const selectedSpot =
608
+ fog.spotLightEntity === undefined
609
+ ? fog.lightKind === 'spot'
610
+ ? lights.spot.find((light) => light.entity === fog.lightEntity)
611
+ : undefined
612
+ : lights.spot.find((light) => light.entity === fog.spotLightEntity);
613
+ const lightDirection =
614
+ selectedSpot?.direction ?? lights.directional?.direction ?? vec3.create(0, -1, 0);
615
+ const lightColor = selectedSpot?.color ?? lights.directional?.color ?? vec3.create(0, 0, 0);
616
+ if (lightDirection === undefined || lightColor === undefined) return undefined;
617
+ params.set(lightDirection, 20);
618
+ // Reuse the vec4 padding lane as the renderer-owned frame phase for
619
+ // sub-froxel jitter. The temporal resolver consumes it without growing the
620
+ // public fog component or allocating another uniform buffer.
621
+ params[23] = frameState.frameNumber % 1024;
622
+ const selectedSpotIndex =
623
+ selectedSpot === undefined
624
+ ? -1
625
+ : lights.spot.findIndex((light) => light.entity === selectedSpot.entity);
626
+ params.set(lightColor, 24);
627
+ params[19] =
628
+ selectedPoint === undefined
629
+ ? -1
630
+ : lights.point.findIndex((light) => light.entity === selectedPoint.entity);
631
+ params[27] = selectedSpotIndex;
632
+ params[28] = fog.fog.maxDistance;
633
+ params[29] = fog.fog.anisotropy;
634
+ params[30] =
635
+ selectedPoint !== undefined && selectedSpot !== undefined
636
+ ? 3
637
+ : selectedPoint !== undefined
638
+ ? 1
639
+ : selectedSpot !== undefined
640
+ ? 2
641
+ : 0;
642
+ // The temporal shader reads this lane after the record stage resolves the
643
+ // accepted graph/slot. It is overwritten on the next queue-visible frame;
644
+ // the first candidate deliberately starts with history disabled.
645
+ params[31] =
646
+ frameState.temporalFrame !== undefined && frameState.temporalFrame.resetReason === 'none'
647
+ ? 1
648
+ : 0;
649
+
650
+ const paramsBuffer = stageVolumetricFogParams(internals, frameState, params);
651
+ if (paramsBuffer === undefined) return undefined;
652
+ return {
653
+ densityTexture: resident.value.texture.handle,
654
+ densityView: resident.value.view,
655
+ paramsBuffer,
656
+ densityGeneration: resident.value.receipt.generation,
657
+ historyReadSlot: null,
658
+ historyWriteSlot: 0,
659
+ historyValid: false,
660
+ ...(projector === undefined
661
+ ? {}
662
+ : {
663
+ projectorTexture: projector.texture,
664
+ projectorView: projector.view,
665
+ projectorSampler: projector.sampler,
666
+ }),
667
+ };
668
+ }
669
+
670
+ /**
671
+ * Resolve the SpotLight-owned cookie independently of volumetric fog. Surface
672
+ * lighting must keep the authored projector when the fog component is absent;
673
+ * otherwise the view bind group silently falls back to white and changes the
674
+ * direct-light energy on the floor. A fog-selected projector is preferred so
675
+ * the surface and volume lanes consume one identity, with the first authored
676
+ * projector as the deterministic scene fallback.
677
+ */
678
+ function prepareSpotLightProjectorFrame(
679
+ internals: RenderSystemInternals,
680
+ world: World,
681
+ worlds: readonly World[],
682
+ lights: ExtractedLights,
683
+ fog: ExtractedVolumetricFog | undefined,
684
+ ): SpotLightProjectorFrameContext | undefined {
685
+ const preferred =
686
+ fog?.projectorHandle === undefined
687
+ ? undefined
688
+ : lights.spot.find((light) => light.projectorHandle === fog.projectorHandle);
689
+ const source =
690
+ preferred ??
691
+ lights.spot.find(
692
+ (light) =>
693
+ light.projectorHandle !== undefined &&
694
+ light.projectorAsset !== undefined &&
695
+ light.lightViewProj !== undefined,
696
+ );
697
+ if (
698
+ source === undefined ||
699
+ source.projectorHandle === undefined ||
700
+ source.projectorAsset === undefined
701
+ ) {
702
+ return undefined;
703
+ }
704
+ const ownerWorld = worlds[fog?.worldId ?? 0] ?? world;
705
+ const resident = internals.gpuStore.ensureResident(
706
+ source.projectorHandle,
707
+ source.projectorAsset,
708
+ ownerWorld,
709
+ );
710
+ if (!resident.ok || !('texture' in resident.value)) {
711
+ if (!resident.ok) {
712
+ internals.errorRegistry.fire(
713
+ resident.error instanceof RhiError
714
+ ? resident.error
715
+ : new RhiError({
716
+ code: 'webgpu-runtime-error',
717
+ expected: 'SpotLight projector residency to resolve a GPU texture',
718
+ hint: 'repair the authored projector TextureAsset payload and retry the next frame',
719
+ detail: { error: resident.error },
720
+ }),
721
+ );
722
+ }
723
+ return undefined;
724
+ }
725
+ const sampler = internals.device.createSampler({
726
+ label: 'spot-projector-linear-clamp',
727
+ minFilter: 'linear',
728
+ magFilter: 'linear',
729
+ mipmapFilter: 'linear',
730
+ addressModeU: 'clamp-to-edge',
731
+ addressModeV: 'clamp-to-edge',
732
+ addressModeW: 'clamp-to-edge',
733
+ });
734
+ if (!sampler.ok) {
735
+ internals.errorRegistry.fire(sampler.error);
736
+ return undefined;
737
+ }
738
+ return {
739
+ texture: resident.value.texture.handle,
740
+ view: resident.value.view,
741
+ sampler: sampler.value,
742
+ };
743
+ }
744
+
745
+ /** Stage the accepted parameter payload into the writable slot for a degraded
746
+ * source frame. This keeps the accepted GPU buffer immutable until submit
747
+ * succeeds while still allowing the LKG graph to update its history flag. */
748
+ function stageAcceptedVolumetricFogFrame(
749
+ internals: RenderSystemInternals,
750
+ frameState: RenderFrameState,
751
+ accepted: VolumetricFogFrameContext | undefined,
752
+ ): VolumetricFogFrameContext | undefined {
753
+ if (accepted === undefined) return undefined;
754
+ const params = frameState.volumetricFogAcceptedParams;
755
+ if (params === undefined) return accepted;
756
+ const paramsBuffer = stageVolumetricFogParams(internals, frameState, new Float32Array(params));
757
+ if (paramsBuffer === undefined) return accepted;
758
+ return { ...accepted, paramsBuffer };
759
+ }
760
+
761
+ function updateVolumetricFogInspection(
762
+ frameState: RenderFrameState,
763
+ fog: ExtractedVolumetricFog | undefined,
764
+ lights: ExtractedLights,
765
+ prepared: VolumetricFogFrameContext | undefined,
766
+ submitted: boolean,
767
+ volumeCapability: boolean,
768
+ volumeSubmissionAttempted: boolean,
769
+ ): void {
770
+ const resourceFacts =
771
+ frameState.compiledFrameGraph === undefined || frameState.compiledFrameGraph === null
772
+ ? undefined
773
+ : inspectVolumetricFogResources(frameState.compiledFrameGraph.inspect(), {
774
+ parameterBufferCount: frameState.volumetricFogParamsBuffers.filter(
775
+ (buffer) => buffer !== null,
776
+ ).length,
777
+ });
778
+ const graphSampleCount = resourceFacts?.sampleCount ?? 0;
779
+ const graphMemoryBytes = resourceFacts?.totalBytes ?? 0;
780
+ const previous = frameState.volumetricFogInspection;
781
+ const accepted = frameState.volumetricFogAccepted;
782
+ const acceptedLkg =
783
+ accepted === undefined
784
+ ? undefined
785
+ : {
786
+ guid: accepted.guid ?? previous.guid,
787
+ generation: accepted.generation ?? previous.generation,
788
+ digest: accepted.digest ?? previous.digest,
789
+ deviceEpoch: previous.deviceEpoch,
790
+ format: previous.format ?? accepted.densityAsset?.format ?? fog?.densityAsset?.format,
791
+ passCount: previous.passCount > 0 ? previous.passCount : VOLUMETRIC_FOG_PASS_COUNT,
792
+ sampleCount: previous.sampleCount > 0 ? previous.sampleCount : graphSampleCount,
793
+ memoryBytes: previous.memoryBytes > 0 ? previous.memoryBytes : graphMemoryBytes,
794
+ resourceFacts: previous.resourceFacts ?? resourceFacts,
795
+ lightEntity: accepted.lightEntity,
796
+ lightKind: accepted.lightKind,
797
+ pointLightEntity: accepted.pointLightEntity,
798
+ spotLightEntity: accepted.spotLightEntity,
799
+ projector: accepted.projector,
800
+ };
801
+
802
+ const candidateIdentity =
803
+ fog?.guid === undefined || fog.generation === undefined
804
+ ? undefined
805
+ : {
806
+ guid: fog.guid,
807
+ generation: fog.generation,
808
+ digest: fog.digest,
809
+ };
810
+ const selectedLightAvailable =
811
+ fog?.lightKind === 'spot'
812
+ ? lights.spot.some((light) => light.entity === fog.lightEntity)
813
+ : fog?.lightKind === 'point'
814
+ ? lights.point.some(
815
+ (light) => light.entity === (fog.pointLightEntity ?? fog.lightEntity),
816
+ ) &&
817
+ (fog.spotLightEntity === undefined ||
818
+ lights.spot.some((light) => light.entity === fog.spotLightEntity))
819
+ : fog?.lightKind === 'directional'
820
+ ? lights.directional?.entity === fog.lightEntity
821
+ : lights.directional !== undefined;
822
+ const capability = volumeCapability && selectedLightAvailable ? 'available' : 'unavailable';
823
+
824
+ const inspectAccepted = (status: 'accepted' | 'degraded', candidateFailure?: 'submit-failed') => {
825
+ if (
826
+ acceptedLkg === undefined ||
827
+ acceptedLkg.guid === undefined ||
828
+ acceptedLkg.generation === undefined
829
+ ) {
830
+ return false;
831
+ }
832
+ const recovery = {
833
+ guid: acceptedLkg.guid,
834
+ generation: acceptedLkg.generation,
835
+ deviceEpoch: acceptedLkg.deviceEpoch,
836
+ status,
837
+ ...(status === 'degraded' ? { lkgGeneration: acceptedLkg.generation } : {}),
838
+ ...(candidateIdentity === undefined
839
+ ? {}
840
+ : {
841
+ candidateGeneration: candidateIdentity.generation,
842
+ ...(candidateIdentity.digest === undefined
843
+ ? {}
844
+ : { candidateDigest: candidateIdentity.digest }),
845
+ }),
846
+ ...(acceptedLkg.lightEntity === undefined || acceptedLkg.lightKind === undefined
847
+ ? {}
848
+ : {
849
+ selectedLight: {
850
+ entity: acceptedLkg.lightEntity,
851
+ kind: acceptedLkg.lightKind,
852
+ revision: 0,
853
+ shadowTile: -1,
854
+ ...(acceptedLkg.pointLightEntity === undefined
855
+ ? {}
856
+ : { pointLightEntity: acceptedLkg.pointLightEntity }),
857
+ ...(acceptedLkg.spotLightEntity === undefined
858
+ ? {}
859
+ : { spotLightEntity: acceptedLkg.spotLightEntity }),
860
+ ...(acceptedLkg.projector === undefined ? {} : { projector: acceptedLkg.projector }),
861
+ },
862
+ }),
863
+ ...(candidateFailure === undefined ? {} : { candidateFailure }),
864
+ } satisfies import('../volume/recovery').VolumeRecoveryState;
865
+ frameState.volumetricFogInspection = inspectVolumetricFog({
866
+ authored: true,
867
+ capability: 'available',
868
+ degraded: status === 'degraded',
869
+ recovery,
870
+ ...(acceptedLkg.digest === undefined ? {} : { acceptedDigest: acceptedLkg.digest }),
871
+ ...(acceptedLkg.format === undefined ? {} : { format: acceptedLkg.format }),
872
+ passCount: acceptedLkg.passCount,
873
+ sampleCount: acceptedLkg.sampleCount,
874
+ memoryBytes: acceptedLkg.memoryBytes,
875
+ ...(acceptedLkg.resourceFacts === undefined
876
+ ? {}
877
+ : { resourceFacts: acceptedLkg.resourceFacts }),
878
+ });
879
+ return true;
880
+ };
881
+
882
+ if (fog === undefined || fog.status === 'off') {
883
+ // A failed fog-off candidate must not erase the inspection/LKG that is
884
+ // still being rendered. The accepted projection is retired only after a
885
+ // successful no-volume submission below.
886
+ if (!submitted && volumeSubmissionAttempted && inspectAccepted('degraded', 'submit-failed')) {
887
+ return;
888
+ }
889
+ if (!submitted && volumeSubmissionAttempted) return;
890
+ if (!submitted) return;
891
+ frameState.volumetricFogInspection = inspectVolumetricFog({
892
+ authored: false,
893
+ capability: 'available',
894
+ });
895
+ return;
896
+ }
897
+
898
+ const authored = fog.status === 'available' || fog.status === 'degraded';
899
+
900
+ // A successfully submitted available candidate is the only path that may
901
+ // become the new accepted projection. This call runs after graph settlement
902
+ // so the inspection and the graph/context observe the same transaction.
903
+ if (
904
+ submitted &&
905
+ fog.status === 'available' &&
906
+ prepared !== undefined &&
907
+ capability === 'available'
908
+ ) {
909
+ const recovery =
910
+ candidateIdentity === undefined
911
+ ? undefined
912
+ : {
913
+ guid: candidateIdentity.guid,
914
+ generation: candidateIdentity.generation,
915
+ deviceEpoch: previous.deviceEpoch,
916
+ status: 'accepted' as const,
917
+ ...(fog.lightEntity === undefined || fog.lightKind === undefined
918
+ ? {}
919
+ : {
920
+ selectedLight: {
921
+ entity: fog.lightEntity,
922
+ kind: fog.lightKind,
923
+ revision: 0,
924
+ shadowTile:
925
+ lights.spot.find((spot) => spot.entity === fog.spotLightEntity)
926
+ ?.shadowAtlasTile ??
927
+ lights.spot.find((spot) => spot.entity === fog.lightEntity)
928
+ ?.shadowAtlasTile ??
929
+ -1,
930
+ ...(fog.pointLightEntity === undefined
931
+ ? {}
932
+ : { pointLightEntity: fog.pointLightEntity }),
933
+ ...(fog.spotLightEntity === undefined
934
+ ? {}
935
+ : { spotLightEntity: fog.spotLightEntity }),
936
+ ...(fog.projector === undefined ? {} : { projector: fog.projector }),
937
+ },
938
+ }),
939
+ };
940
+ frameState.volumetricFogInspection = inspectVolumetricFog({
941
+ authored: true,
942
+ capability,
943
+ ...(recovery === undefined ? {} : { recovery }),
944
+ ...(candidateIdentity?.digest === undefined
945
+ ? {}
946
+ : { acceptedDigest: candidateIdentity.digest }),
947
+ ...(fog.densityAsset?.format === undefined ? {} : { format: fog.densityAsset.format }),
948
+ passCount: VOLUMETRIC_FOG_PASS_COUNT,
949
+ sampleCount: graphSampleCount,
950
+ memoryBytes: graphMemoryBytes,
951
+ ...(resourceFacts === undefined ? {} : { resourceFacts }),
952
+ });
953
+ return;
954
+ }
955
+
956
+ // A submitted degraded source (or an available source whose residency
957
+ // preparation failed) is rendered through the previously accepted graph.
958
+ // Keep that LKG resource fact visible instead of projecting a zero-resource
959
+ // candidate merely because the current source could not be prepared.
960
+ if (
961
+ submitted &&
962
+ acceptedLkg !== undefined &&
963
+ (fog.status === 'degraded' || prepared === undefined)
964
+ ) {
965
+ if (inspectAccepted('degraded')) return;
966
+ }
967
+
968
+ // Any candidate that reached encode/submit but failed must project the LKG,
969
+ // never the unsubmitted candidate. The candidate identity remains visible so
970
+ // recovery tooling can distinguish the rejected bytes from the frame being
971
+ // rendered.
972
+ if (!submitted && volumeSubmissionAttempted && inspectAccepted('degraded', 'submit-failed')) {
973
+ return;
974
+ }
975
+
976
+ // A degraded source that has no LKG is still a diagnostic, but it must not
977
+ // claim accepted resources. Keep its identity in the candidate stage.
978
+ const candidateRecovery =
979
+ candidateIdentity === undefined
980
+ ? undefined
981
+ : {
982
+ guid: candidateIdentity.guid,
983
+ generation: candidateIdentity.generation,
984
+ deviceEpoch: previous.deviceEpoch,
985
+ status: 'candidate' as const,
986
+ candidateGeneration: candidateIdentity.generation,
987
+ ...(candidateIdentity.digest === undefined
988
+ ? {}
989
+ : { candidateDigest: candidateIdentity.digest }),
990
+ ...(!submitted && volumeSubmissionAttempted
991
+ ? { candidateFailure: 'submit-failed' as const }
992
+ : {}),
993
+ };
994
+ frameState.volumetricFogInspection = inspectVolumetricFog({
995
+ authored,
996
+ capability,
997
+ degraded: fog.status === 'degraded' || capability === 'unavailable',
998
+ ...(candidateRecovery === undefined ? {} : { recovery: candidateRecovery }),
999
+ ...(acceptedLkg?.digest === undefined ? {} : { acceptedDigest: acceptedLkg.digest }),
1000
+ ...(fog.densityAsset?.format === undefined
1001
+ ? acceptedLkg?.format === undefined
1002
+ ? {}
1003
+ : { format: acceptedLkg.format }
1004
+ : { format: fog.densityAsset.format }),
1005
+ passCount: 0,
1006
+ sampleCount: 0,
1007
+ memoryBytes: 0,
1008
+ });
1009
+ }
1010
+
292
1011
  export function recordFrame(
293
1012
  internals: RenderSystemInternals,
294
1013
  world: World,
@@ -304,6 +1023,7 @@ export function recordFrame(
304
1023
  skybox: SkyboxSnapshot | undefined,
305
1024
  skyboxCount: number,
306
1025
  postProcessParams: ReadonlyMap<string, Uint8Array>,
1026
+ fog: FogFrame | undefined,
307
1027
  // feat-20260709-editor-world-partition ENGINE-fix-round2 (defect 2): the
308
1028
  // full worlds[] list passed to `renderer.draw`, indexed by
309
1029
  // RenderableSnapshot.worldId so the record stage resolves each renderable's
@@ -321,6 +1041,12 @@ export function recordFrame(
321
1041
  renderReadLeases?: readonly RenderReadLease[],
322
1042
  featureGraphCandidate?: RenderFeatureGraphCandidate,
323
1043
  pointsLinesOwner?: import('./render-context').PointsLinesRecordOwner,
1044
+ environmentSignature = '',
1045
+ fogSignature = '',
1046
+ environmentReady = true,
1047
+ transmissionDemand?: TransmissionDemand,
1048
+ volumetricFog?: ExtractedVolumetricFog,
1049
+ timingCapture?: GpuTimingCapture,
324
1050
  ): boolean {
325
1051
  // The `try / finally` wrapper advances `frameState.frameNumber` exactly
326
1052
  // once per `recordFrame` invocation regardless of which early-return
@@ -351,6 +1077,11 @@ export function recordFrame(
351
1077
  );
352
1078
  activeCameras = [makeZeroCameraFallbackSnapshot()];
353
1079
  }
1080
+ let submitted = false;
1081
+ let volumeInspectionReady = false;
1082
+ let volumeSubmissionAttempted = false;
1083
+ let preparedVolumetricFogForInspection: VolumetricFogFrameContext | undefined;
1084
+ let volumeCapabilityForInspection = false;
354
1085
  try {
355
1086
  // Case D: N>1 Camera => fire onError, use first hit (D-S7).
356
1087
  if (activeCameras.length > 1) {
@@ -484,6 +1215,64 @@ export function recordFrame(
484
1215
  const effectiveShadowMapSize = resolveShadowMapSize(internals, lights);
485
1216
  const effectiveSpotShadowMapSize = resolveSpotShadowMapSize(internals, lights);
486
1217
  const graphShadowMapSize = effectiveShadowMapSize ?? effectiveSpotShadowMapSize;
1218
+ const volumeCapability = hasVolumetricFogCapability(
1219
+ internals.device.caps,
1220
+ internals.volumetricFogShaders,
1221
+ );
1222
+ volumeInspectionReady = true;
1223
+ volumeCapabilityForInspection = volumeCapability;
1224
+
1225
+ const preparedSpotLightProjector = prepareSpotLightProjectorFrame(
1226
+ internals,
1227
+ world,
1228
+ worlds,
1229
+ lights,
1230
+ volumetricFog,
1231
+ );
1232
+
1233
+ const preparedVolumetricFog = prepareVolumetricFogFrame(
1234
+ internals,
1235
+ frameState,
1236
+ world,
1237
+ worlds,
1238
+ volumetricFog,
1239
+ lights,
1240
+ volumeCapability,
1241
+ preparedSpotLightProjector,
1242
+ );
1243
+ preparedVolumetricFogForInspection = preparedVolumetricFog;
1244
+ // A degraded authored snapshot must keep consuming the last accepted GPU
1245
+ // projection. The current source is still surfaced as a diagnostic, but
1246
+ // it must not replace the graph/context that produced the last good frame.
1247
+ // An absent component is different: a successful no-volume frame retires
1248
+ // the accepted projection and compiles the normal graph.
1249
+ const acceptedFallback =
1250
+ volumeCapability && volumetricFog !== undefined && preparedVolumetricFog === undefined
1251
+ ? frameState.volumetricFogAccepted
1252
+ : undefined;
1253
+ const acceptedFallbackContext =
1254
+ acceptedFallback === undefined
1255
+ ? undefined
1256
+ : stageAcceptedVolumetricFogFrame(
1257
+ internals,
1258
+ frameState,
1259
+ frameState.volumetricFogAcceptedContext,
1260
+ );
1261
+ const graphVolumetricFog = volumeCapability
1262
+ ? preparedVolumetricFog === undefined
1263
+ ? acceptedFallback
1264
+ : volumetricFog
1265
+ : undefined;
1266
+ const baseVolumetricFogContext = preparedVolumetricFog ?? acceptedFallbackContext;
1267
+ // A volume graph replacement is provisional whenever it can affect the
1268
+ // accepted volume projection. This includes authoring degradation (which
1269
+ // consumes the LKG graph), an authored replacement, and an explicit
1270
+ // fog-off transition. Non-volume frames retain the legacy immediate graph
1271
+ // retirement path.
1272
+ const volumeTopologyCandidate =
1273
+ (graphVolumetricFog?.status === 'available' && baseVolumetricFogContext !== undefined) ||
1274
+ (graphVolumetricFog === undefined && frameState.volumetricFogAccepted !== undefined) ||
1275
+ (volumetricFog?.status === 'degraded' && acceptedFallback !== undefined);
487
1276
 
488
1277
  let preparedGpuDriven: PreparedGpuDrivenFrame | undefined;
489
1278
  if (gpuDriven !== undefined) {
@@ -521,6 +1310,10 @@ export function recordFrame(
521
1310
  graphShadowMapSize,
522
1311
  preparedGpuDriven,
523
1312
  featureGraphCandidate,
1313
+ cameras.length === 0,
1314
+ transmissionDemand,
1315
+ graphVolumetricFog,
1316
+ volumeTopologyCandidate,
524
1317
  ),
525
1318
  );
526
1319
  if (preflightGraph === null) return false;
@@ -539,6 +1332,108 @@ export function recordFrame(
539
1332
  const targetW = swapTarget.targetW;
540
1333
  const targetH = swapTarget.targetH;
541
1334
 
1335
+ const temporalView =
1336
+ camera.antialias === 'taa' && environmentReady
1337
+ ? (() => {
1338
+ const created = createTemporalView({
1339
+ antialias: camera.antialias,
1340
+ width: targetW,
1341
+ height: targetH,
1342
+ frameIndex: frameState.successfulTemporalFrameIndex ?? 0,
1343
+ viewIdentity: `camera:${camera.entityKey ?? 0}`,
1344
+ environmentSignature,
1345
+ fogSignature,
1346
+ deviceGeneration: internals.deviceScope.generation,
1347
+ ...(camera.historyVersion === undefined
1348
+ ? {}
1349
+ : { historyVersion: camera.historyVersion }),
1350
+ });
1351
+ const previous = frameState.lastSuccessfulTemporalView;
1352
+ const currentUnjitteredViewProjection = mat4.multiply(
1353
+ mat4.create(),
1354
+ computeProjectionMatrix(camera),
1355
+ computeViewMatrix(camera),
1356
+ );
1357
+ const resetCandidate = createTemporalView({
1358
+ antialias: created.input.antialias,
1359
+ width: created.input.width,
1360
+ height: created.input.height,
1361
+ frameIndex: created.input.frameIndex,
1362
+ historyVersion: created.input.historyVersion,
1363
+ viewIdentity: created.input.viewIdentity,
1364
+ environmentSignature,
1365
+ fogSignature,
1366
+ deviceGeneration: internals.deviceScope.generation,
1367
+ cameraCut: created.input.cameraCut,
1368
+ historyValid: false,
1369
+ currentUnjitteredViewProjection,
1370
+ });
1371
+ const resetReason = resolveTemporalReset(previous, resetCandidate);
1372
+ return createTemporalView({
1373
+ ...resetCandidate.input,
1374
+ frameIndex: resetReason === undefined ? resetCandidate.input.frameIndex : 0,
1375
+ historyValid: previous !== undefined && resetReason === undefined,
1376
+ currentUnjitteredViewProjection,
1377
+ resetReason,
1378
+ ...(resetReason !== undefined ||
1379
+ previous?.currentUnjitteredViewProjection === undefined
1380
+ ? {}
1381
+ : {
1382
+ previousUnjitteredViewProjection: previous.currentUnjitteredViewProjection,
1383
+ }),
1384
+ });
1385
+ })()
1386
+ : undefined;
1387
+ frameState.pendingTemporalCommit =
1388
+ cameras.length === 0 || !environmentReady || internals.deviceScope === undefined
1389
+ ? { kind: 'none' }
1390
+ : temporalView === undefined
1391
+ ? {
1392
+ kind: 'off',
1393
+ view: createTemporalView({
1394
+ antialias: camera.antialias,
1395
+ width: targetW,
1396
+ height: targetH,
1397
+ frameIndex: frameState.successfulTemporalFrameIndex ?? 0,
1398
+ viewIdentity: `camera:${camera.entityKey ?? 0}`,
1399
+ environmentSignature,
1400
+ fogSignature,
1401
+ deviceGeneration: internals.deviceScope.generation,
1402
+ }),
1403
+ }
1404
+ : { kind: 'taa', view: temporalView };
1405
+ const recordCamera: CameraSnapshot =
1406
+ temporalView === undefined ? camera : { ...camera, temporal: temporalView };
1407
+ let activeVolumetricFogContext: VolumetricFogFrameContext | undefined;
1408
+ let activeVolumetricFogSignature: VolumeTemporalSignature | undefined;
1409
+ if (graphVolumetricFog?.status === 'available' && baseVolumetricFogContext !== undefined) {
1410
+ const resolvedHistory = resolveVolumetricFogHistoryContext(
1411
+ frameState,
1412
+ preflightGraph,
1413
+ graphVolumetricFog,
1414
+ baseVolumetricFogContext,
1415
+ camera,
1416
+ lights,
1417
+ targetW,
1418
+ targetH,
1419
+ );
1420
+ activeVolumetricFogContext = resolvedHistory.context;
1421
+ activeVolumetricFogSignature = resolvedHistory.signature;
1422
+ const historyFlag = new Float32Array([activeVolumetricFogContext.historyValid ? 1 : 0]);
1423
+ if (frameState.volumetricFogPendingParams !== undefined) {
1424
+ frameState.volumetricFogPendingParams[31] = historyFlag[0] ?? 0;
1425
+ }
1426
+ const historyFlagWritten = internals.device.queue.writeBuffer(
1427
+ activeVolumetricFogContext.paramsBuffer,
1428
+ 31 * Float32Array.BYTES_PER_ELEMENT,
1429
+ historyFlag,
1430
+ );
1431
+ if (!historyFlagWritten.ok) {
1432
+ internals.errorRegistry.fire(historyFlagWritten.error);
1433
+ return false;
1434
+ }
1435
+ }
1436
+
542
1437
  const depthView: TextureView | null = null;
543
1438
  // feat-20260709 M3 / D-3: clear-color read from the active CameraSnapshot's
544
1439
  // single `clearColor` array field (first-archetype-hit per OOS-2). When the
@@ -583,7 +1478,7 @@ export function recordFrame(
583
1478
  const { tonemapActive, skyboxActive } = resolveSkyboxActive(
584
1479
  internals,
585
1480
  frameState,
586
- camera,
1481
+ recordCamera,
587
1482
  skybox,
588
1483
  );
589
1484
 
@@ -687,7 +1582,18 @@ export function recordFrame(
687
1582
  // feat-20260531-skybox-env-background M2 / w6: gate relaxed from
688
1583
  // `validatedOrdered.length > 0` to include skybox-only frames
689
1584
  // (plan-strategy D-3, R-3).
690
- if (validatedOrdered.length > 0 || skyboxActive) {
1585
+ const currentViewProjection = mat4.create();
1586
+ mat4.multiply(
1587
+ currentViewProjection,
1588
+ computeProjectionMatrix(camera),
1589
+ computeViewMatrix(camera),
1590
+ );
1591
+ if (
1592
+ validatedOrdered.length > 0 ||
1593
+ skyboxActive ||
1594
+ activeVolumetricFogContext !== undefined ||
1595
+ preparedVolumetricFog !== undefined
1596
+ ) {
691
1597
  runRecordProfilePhase(profilePhase, 'record/uploads', () => {
692
1598
  // View UBO + CSM/spot-shadow matrix pack: assembled + uploaded in one
693
1599
  // queue.writeBuffer round-trip. Extracted to view-ubo.ts (M3/w18) so
@@ -699,6 +1605,8 @@ export function recordFrame(
699
1605
  light,
700
1606
  lights,
701
1607
  frameState.spotShadowSnapshots,
1608
+ fog,
1609
+ temporalView,
702
1610
  );
703
1611
  if (pipelineState.pointsLinesViewBuffer !== undefined) {
704
1612
  writePointsLinesViewUbo(
@@ -783,12 +1691,29 @@ export function recordFrame(
783
1691
  // errorRegistry + shader-cache lookups) surfaces (`internals` itself satisfies
784
1692
  // `RenderSystemRuntime` so `runtime: internals` is a zero-cost reference). The
785
1693
  // 0-consumed `skyboxCount` residual is dropped.
1694
+ // Camera-owned temporal controls are projected into the per-frame
1695
+ // post-process parameter map at record time. The extracted ECS map only
1696
+ // carries explicit PostProcessParams components; without this bridge the
1697
+ // builtin motion-blur pass receives its all-zero registration default and
1698
+ // becomes a no-op even though the temporal scene target is populated.
1699
+ const framePostProcessParams = new Map(postProcessParams);
1700
+ if (camera.motionBlur !== undefined) {
1701
+ const motionBlurParams = new Uint8Array(16);
1702
+ const motionBlurView = new DataView(motionBlurParams.buffer);
1703
+ motionBlurView.setFloat32(0, camera.motionBlur.shutterAngle, true);
1704
+ motionBlurView.setFloat32(4, camera.motionBlur.maxRadiusPixels, true);
1705
+ motionBlurView.setUint32(8, camera.motionBlur.sampleCount, true);
1706
+ motionBlurView.setUint32(12, temporalView?.historyValid === true ? 0 : 1, true);
1707
+ framePostProcessParams.set('forgeax.motion-blur', motionBlurParams);
1708
+ }
1709
+
786
1710
  const passCtx: _InternalRenderPipelineContext = {
787
1711
  assets: internals.assets,
788
1712
  world,
789
1713
  gpuDrivenEntityKeys: gpuOwnedEntityKeys,
790
1714
  store: internals.gpuStore,
791
1715
  pipelineState,
1716
+ bloomResources: pipelineState.perPassResources.getBloomResources?.() ?? null,
792
1717
  runtime: internals,
793
1718
  encoder,
794
1719
  view,
@@ -796,7 +1721,7 @@ export function recordFrame(
796
1721
  targetW,
797
1722
  targetH,
798
1723
  currentTexture,
799
- camera,
1724
+ camera: recordCamera,
800
1725
  tonemapActive,
801
1726
  geometryColorView,
802
1727
  geometryDepthView,
@@ -817,7 +1742,16 @@ export function recordFrame(
817
1742
  msaaActive,
818
1743
  geometryColorResolveView,
819
1744
  ldrSpriteColorView,
820
- postProcessParams,
1745
+ postProcessParams: framePostProcessParams,
1746
+ ...(internals.volumetricFogShaders === undefined
1747
+ ? {}
1748
+ : { volumetricFogShaders: internals.volumetricFogShaders }),
1749
+ ...(activeVolumetricFogContext === undefined
1750
+ ? {}
1751
+ : { volumetricFog: activeVolumetricFogContext }),
1752
+ ...(preparedSpotLightProjector === undefined
1753
+ ? {}
1754
+ : { spotLightProjector: preparedSpotLightProjector }),
821
1755
  dispatch: transparentDispatch,
822
1756
  hdrpClusterBindGroup,
823
1757
  hdrpClusterMembershipBindGroup,
@@ -831,7 +1765,21 @@ export function recordFrame(
831
1765
  directionalShadowCacheReuse: directionalShadowCache.reuse,
832
1766
  ...(profilePhase !== undefined ? { profilePhase } : {}),
833
1767
  };
834
- const submitted = runRecordProfilePhase(profilePhase, 'record/graph-execute', () =>
1768
+ const stagedTemporalFrame = {
1769
+ frameId: frameState.frameNumber,
1770
+ currentViewProjection: new Float32Array(currentViewProjection),
1771
+ jitter: [0, 0] as [number, number],
1772
+ viewport: { width: targetW, height: targetH },
1773
+ cameraPosition: [
1774
+ camera.position[0] ?? 0,
1775
+ camera.position[1] ?? 0,
1776
+ camera.position[2] ?? 0,
1777
+ ] as [number, number, number],
1778
+ };
1779
+ frameState.temporalFrameInput = stagedTemporalFrame;
1780
+ frameState.temporalFrameTransaction.stage(stagedTemporalFrame);
1781
+ volumeSubmissionAttempted = volumeTopologyCandidate || activeVolumetricFogContext !== undefined;
1782
+ submitted = runRecordProfilePhase(profilePhase, 'record/graph-execute', () =>
835
1783
  executeCompiledFrameGraph(
836
1784
  internals,
837
1785
  frameState,
@@ -840,6 +1788,7 @@ export function recordFrame(
840
1788
  profilePhase === undefined
841
1789
  ? undefined
842
1790
  : (pass, encode) => profilePhase(graphExecutionPhase(pass.name), encode),
1791
+ timingCapture,
843
1792
  ),
844
1793
  );
845
1794
  if (submitted && !directionalShadowCache.reuse) {
@@ -847,8 +1796,66 @@ export function recordFrame(
847
1796
  ? directionalShadowCache.next
848
1797
  : null;
849
1798
  }
1799
+ if (submitted) {
1800
+ if (graphVolumetricFog === undefined) {
1801
+ frameState.volumetricFogAccepted = undefined;
1802
+ frameState.volumetricFogAcceptedContext = undefined;
1803
+ frameState.volumetricFogHistoryGraph = null;
1804
+ frameState.volumetricFogHistorySlot = null;
1805
+ frameState.volumetricFogHistorySignature = null;
1806
+ retireVolumetricFogParams(internals, frameState);
1807
+ } else if (
1808
+ activeVolumetricFogContext !== undefined &&
1809
+ activeVolumetricFogSignature !== undefined
1810
+ ) {
1811
+ // Temporal history is graph-owned state. Promote the write slot only
1812
+ // after queue submission succeeds; a failed candidate keeps the
1813
+ // accepted graph/context/slot and retries into the same pending slot.
1814
+ frameState.volumetricFogHistoryGraph = preflightGraph;
1815
+ frameState.volumetricFogHistorySlot = activeVolumetricFogContext.historyWriteSlot;
1816
+ frameState.volumetricFogHistorySignature = activeVolumetricFogSignature;
1817
+ if (frameState.volumetricFogParamsPendingSlot !== null) {
1818
+ frameState.volumetricFogParamsAcceptedSlot = frameState.volumetricFogParamsPendingSlot;
1819
+ frameState.volumetricFogAcceptedParams =
1820
+ frameState.volumetricFogPendingParams === undefined
1821
+ ? undefined
1822
+ : new Float32Array(frameState.volumetricFogPendingParams);
1823
+ frameState.volumetricFogParamsPendingSlot = null;
1824
+ frameState.volumetricFogPendingParams = undefined;
1825
+ }
1826
+ if (volumetricFog?.status === 'available' && preparedVolumetricFog !== undefined) {
1827
+ frameState.volumetricFogAccepted = volumetricFog;
1828
+ frameState.volumetricFogAcceptedContext = activeVolumetricFogContext;
1829
+ } else if (acceptedFallback !== undefined) {
1830
+ // The current authored source is degraded, so keep the old accepted
1831
+ // POD while advancing the LKG graph's ping-pong slot.
1832
+ frameState.volumetricFogAcceptedContext = activeVolumetricFogContext;
1833
+ }
1834
+ } else if (volumetricFog?.status === 'available' && preparedVolumetricFog !== undefined) {
1835
+ // Defensive fallback for a volume graph that was accepted before the
1836
+ // temporal context could be resolved; do not manufacture history.
1837
+ frameState.volumetricFogAccepted = volumetricFog;
1838
+ frameState.volumetricFogAcceptedContext = preparedVolumetricFog;
1839
+ }
1840
+ }
850
1841
  return submitted;
851
1842
  } finally {
1843
+ // Volume topology replacements are provisional until the queue submission
1844
+ // succeeds. This runs for every early-return path as well, so a swapchain,
1845
+ // encoder, or graph failure cannot leave a non-submitted candidate as the
1846
+ // next frame's accepted graph.
1847
+ settleVolumetricFogGraphCandidate(frameState, submitted);
1848
+ if (volumeInspectionReady && (submitted || volumeSubmissionAttempted)) {
1849
+ updateVolumetricFogInspection(
1850
+ frameState,
1851
+ volumetricFog,
1852
+ lights,
1853
+ preparedVolumetricFogForInspection,
1854
+ submitted,
1855
+ volumeCapabilityForInspection,
1856
+ volumeSubmissionAttempted,
1857
+ );
1858
+ }
852
1859
  frameState.frameNumber += 1;
853
1860
  // feat-20260608-cluster-lighting M5 / w22: clear HDRP once-per-frame fired
854
1861
  // set so the next frame re-fires if the condition persists.