@forgeax/engine-render 0.1.32 → 0.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (624) hide show
  1. package/README.md +1374 -142
  2. package/dist/assembly/analytic-fog-registration.d.ts +3 -0
  3. package/dist/assembly/analytic-fog-registration.d.ts.map +1 -0
  4. package/dist/assembly/backend-contract.d.ts +2 -0
  5. package/dist/assembly/backend-contract.d.ts.map +1 -1
  6. package/dist/assembly/barrel-distortion-inspection.d.ts +3 -0
  7. package/dist/assembly/barrel-distortion-inspection.d.ts.map +1 -0
  8. package/dist/assembly/canvas-draw-guard.d.ts +3 -0
  9. package/dist/assembly/canvas-draw-guard.d.ts.map +1 -0
  10. package/dist/assembly/cloud-shadow-entry.d.ts +4 -0
  11. package/dist/assembly/cloud-shadow-entry.d.ts.map +1 -0
  12. package/dist/assembly/dynamic-geometry-host.d.ts +4 -1
  13. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
  14. package/dist/assembly/dynamic-geometry-runtime.d.ts +1 -1
  15. package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -1
  16. package/dist/assembly/factory.d.ts +1 -1
  17. package/dist/assembly/factory.d.ts.map +1 -1
  18. package/dist/assembly/fxaa-registration.d.ts +4 -0
  19. package/dist/assembly/fxaa-registration.d.ts.map +1 -0
  20. package/dist/assembly/host-contract.d.ts +3 -3
  21. package/dist/assembly/host-contract.d.ts.map +1 -1
  22. package/dist/assembly/lod-observation.d.ts +4 -0
  23. package/dist/assembly/lod-observation.d.ts.map +1 -0
  24. package/dist/assembly/material/assembly.d.ts.map +1 -1
  25. package/dist/assembly/material/pipeline-helpers.d.ts +5 -0
  26. package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -1
  27. package/dist/assembly/material-shader-policy.d.ts +16 -2
  28. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  29. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -1
  30. package/dist/assembly/motion-blur-registration.d.ts +3 -0
  31. package/dist/assembly/motion-blur-registration.d.ts.map +1 -0
  32. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
  33. package/dist/assembly/recovery/renderer-recover.d.ts +2 -0
  34. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  35. package/dist/assembly/render-feature-post-processes.d.ts +6 -0
  36. package/dist/assembly/render-feature-post-processes.d.ts.map +1 -0
  37. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  38. package/dist/assembly/renderer-feature-inspection.d.ts +1 -1
  39. package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -1
  40. package/dist/assembly/renderer-frame-transaction.d.ts +12 -0
  41. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  42. package/dist/assembly/renderer-inspection.d.ts +11 -0
  43. package/dist/assembly/renderer-inspection.d.ts.map +1 -1
  44. package/dist/assembly/temporal-post-process-prewarm.d.ts +13 -0
  45. package/dist/assembly/temporal-post-process-prewarm.d.ts.map +1 -0
  46. package/dist/assembly/webgpu-pbr-ready.d.ts +1 -0
  47. package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -1
  48. package/dist/assembly/webgpu-ready-contract.d.ts +4 -3
  49. package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -1
  50. package/dist/assembly/webgpu-ready-per-pass.d.ts +39 -0
  51. package/dist/assembly/webgpu-ready-per-pass.d.ts.map +1 -0
  52. package/dist/assembly/webgpu-ready.d.ts +2 -2
  53. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  54. package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -1
  55. package/dist/assembly/webgpu-renderer-observation-owner.d.ts +35 -0
  56. package/dist/assembly/webgpu-renderer-observation-owner.d.ts.map +1 -0
  57. package/dist/assembly/webgpu-renderer.d.ts +1 -1
  58. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  59. package/dist/authoring.mjs +1 -1
  60. package/dist/barrel-distortion.d.ts +47 -0
  61. package/dist/barrel-distortion.d.ts.map +1 -0
  62. package/dist/bloom-admission.d.ts +13 -0
  63. package/dist/bloom-admission.d.ts.map +1 -0
  64. package/dist/{chunk-PA5DRJNI.mjs → chunk-ELJFJ2BU.mjs} +2 -2
  65. package/dist/chunk-ELJFJ2BU.mjs.map +1 -0
  66. package/dist/{chunk-JCIRYQ4V.mjs → chunk-HEB564WF.mjs} +10 -4
  67. package/dist/chunk-HEB564WF.mjs.map +1 -0
  68. package/dist/{chunk-JRIPCMYC.mjs → chunk-MKDIT2EM.mjs} +1584 -375
  69. package/dist/chunk-MKDIT2EM.mjs.map +1 -0
  70. package/dist/{chunk-S665NJNF.mjs → chunk-OCDLEI6I.mjs} +3 -3
  71. package/dist/{chunk-S665NJNF.mjs.map → chunk-OCDLEI6I.mjs.map} +1 -1
  72. package/dist/chunk-ODZWR6JQ.mjs +4927 -0
  73. package/dist/chunk-ODZWR6JQ.mjs.map +1 -0
  74. package/dist/{chunk-DRR7D3KX.mjs → chunk-SHFUFJ5N.mjs} +192 -18
  75. package/dist/chunk-SHFUFJ5N.mjs.map +1 -0
  76. package/dist/{chunk-4NNZQCL3.mjs → chunk-UI5SFODX.mjs} +223 -277
  77. package/dist/chunk-UI5SFODX.mjs.map +1 -0
  78. package/dist/{chunk-GWCEMDVR.mjs → chunk-UINHLCZA.mjs} +787 -80
  79. package/dist/chunk-UINHLCZA.mjs.map +1 -0
  80. package/dist/{chunk-6TFBXLRJ.mjs → chunk-WEX45HKH.mjs} +222 -337
  81. package/dist/chunk-WEX45HKH.mjs.map +1 -0
  82. package/dist/cloud/density.d.ts +55 -0
  83. package/dist/cloud/density.d.ts.map +1 -0
  84. package/dist/cloud/extract.d.ts +25 -0
  85. package/dist/cloud/extract.d.ts.map +1 -0
  86. package/dist/cloud/feature.d.ts +57 -0
  87. package/dist/cloud/feature.d.ts.map +1 -0
  88. package/dist/cloud/frame-context.d.ts +8 -0
  89. package/dist/cloud/frame-context.d.ts.map +1 -0
  90. package/dist/cloud/inspection.d.ts +59 -0
  91. package/dist/cloud/inspection.d.ts.map +1 -0
  92. package/dist/cloud/optics.d.ts +56 -0
  93. package/dist/cloud/optics.d.ts.map +1 -0
  94. package/dist/cloud/parameters.d.ts +73 -0
  95. package/dist/cloud/parameters.d.ts.map +1 -0
  96. package/dist/cloud/resources.d.ts +60 -0
  97. package/dist/cloud/resources.d.ts.map +1 -0
  98. package/dist/cloud/shadow.d.ts +42 -0
  99. package/dist/cloud/shadow.d.ts.map +1 -0
  100. package/dist/cloud/temporal.d.ts +74 -0
  101. package/dist/cloud/temporal.d.ts.map +1 -0
  102. package/dist/components/atmosphere.d.ts +2 -0
  103. package/dist/components/atmosphere.d.ts.map +1 -1
  104. package/dist/components/barrel-distortion.d.ts +13 -0
  105. package/dist/components/barrel-distortion.d.ts.map +1 -0
  106. package/dist/components/camera.d.ts +20 -4
  107. package/dist/components/camera.d.ts.map +1 -1
  108. package/dist/components/cloud-layer.d.ts +29 -0
  109. package/dist/components/cloud-layer.d.ts.map +1 -0
  110. package/dist/components/index.d.ts +2 -0
  111. package/dist/components/index.d.ts.map +1 -1
  112. package/dist/components/instances.d.ts +20 -6
  113. package/dist/components/instances.d.ts.map +1 -1
  114. package/dist/components/mesh-filter.d.ts +6 -5
  115. package/dist/components/mesh-filter.d.ts.map +1 -1
  116. package/dist/components/motion-blur.d.ts +1 -0
  117. package/dist/components/motion-blur.d.ts.map +1 -1
  118. package/dist/construct-renderer.d.ts +1 -1
  119. package/dist/construct-renderer.d.ts.map +1 -1
  120. package/dist/construct-renderer.mjs +9751 -3528
  121. package/dist/construct-renderer.mjs.map +1 -1
  122. package/dist/device/gpu-residency.d.ts +6 -0
  123. package/dist/device/gpu-residency.d.ts.map +1 -1
  124. package/dist/dynamic-geometry.d.ts +5 -1
  125. package/dist/dynamic-geometry.d.ts.map +1 -1
  126. package/dist/environment/analytic-fog.d.ts +6 -0
  127. package/dist/environment/analytic-fog.d.ts.map +1 -0
  128. package/dist/errors/cloud.d.ts +59 -0
  129. package/dist/errors/cloud.d.ts.map +1 -0
  130. package/dist/errors/render.d.ts +23 -5
  131. package/dist/errors/render.d.ts.map +1 -1
  132. package/dist/examples/single-layer-medium-surface.d.ts +47 -0
  133. package/dist/examples/single-layer-medium-surface.d.ts.map +1 -0
  134. package/dist/extract/camera.d.ts.map +1 -1
  135. package/dist/extract/contracts.d.ts +12 -1
  136. package/dist/extract/contracts.d.ts.map +1 -1
  137. package/dist/extract/environment.d.ts +2 -0
  138. package/dist/extract/environment.d.ts.map +1 -1
  139. package/dist/features/barrel-distortion.d.ts +29 -0
  140. package/dist/features/barrel-distortion.d.ts.map +1 -0
  141. package/dist/features/error-reporting.d.ts +17 -0
  142. package/dist/features/error-reporting.d.ts.map +1 -0
  143. package/dist/features/host.d.ts +15 -0
  144. package/dist/features/host.d.ts.map +1 -1
  145. package/dist/features/motion-blur/motion-blur-feature.d.ts +20 -2
  146. package/dist/features/motion-blur/motion-blur-feature.d.ts.map +1 -1
  147. package/dist/features/motion-blur/motion-blur-params.d.ts +21 -0
  148. package/dist/features/motion-blur/motion-blur-params.d.ts.map +1 -1
  149. package/dist/features/motion-blur/motion-blur-runtime.d.ts +54 -0
  150. package/dist/features/motion-blur/motion-blur-runtime.d.ts.map +1 -0
  151. package/dist/features/plan.d.ts +15 -4
  152. package/dist/features/plan.d.ts.map +1 -1
  153. package/dist/features/prepared-gpu-work.d.ts +9 -5
  154. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  155. package/dist/features/render-graph-compute.d.ts.map +1 -1
  156. package/dist/features/render-graph-contribution.d.ts +5 -0
  157. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  158. package/dist/features/render-graph-raster.d.ts +1 -0
  159. package/dist/features/render-graph-raster.d.ts.map +1 -1
  160. package/dist/features/targets.d.ts +2 -0
  161. package/dist/features/targets.d.ts.map +1 -1
  162. package/dist/features/types.d.ts +44 -0
  163. package/dist/features/types.d.ts.map +1 -1
  164. package/dist/fullscreen-post-process-pass.d.ts +15 -7
  165. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  166. package/dist/gpu-dirty-ranges.d.ts +11 -0
  167. package/dist/gpu-dirty-ranges.d.ts.map +1 -0
  168. package/dist/gpu-driven/batch-topology.d.ts +17 -0
  169. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  170. package/dist/gpu-driven/material-artifacts.d.ts +6 -0
  171. package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
  172. package/dist/gpu-driven/prepared-draw.d.ts +7 -0
  173. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  174. package/dist/gpu-driven/production-raster-material.d.ts +178 -0
  175. package/dist/gpu-driven/production-raster-material.d.ts.map +1 -0
  176. package/dist/gpu-driven/production-raster.d.ts +50 -4
  177. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  178. package/dist/gpu-driven/resource-allocation.d.ts +31 -0
  179. package/dist/gpu-driven/resource-allocation.d.ts.map +1 -0
  180. package/dist/gpu-driven/shadow-ownership.d.ts +14 -0
  181. package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -0
  182. package/dist/gpu-driven/view-gpu.d.ts +35 -2
  183. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  184. package/dist/gpu-scene.d.ts +11 -1
  185. package/dist/gpu-scene.d.ts.map +1 -1
  186. package/dist/hdrp-buffers.d.ts +2 -2
  187. package/dist/hdrp-buffers.d.ts.map +1 -1
  188. package/dist/ibl/skylight-bind-group.d.ts +10 -1
  189. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  190. package/dist/index.d.ts +31 -8
  191. package/dist/index.d.ts.map +1 -1
  192. package/dist/index.mjs +258 -8
  193. package/dist/index.mjs.map +1 -1
  194. package/dist/inspection-types.d.ts +178 -3
  195. package/dist/inspection-types.d.ts.map +1 -1
  196. package/dist/instance-buffer-cache.d.ts +4 -4
  197. package/dist/instance-buffer-cache.d.ts.map +1 -1
  198. package/dist/instances.d.ts +49 -94
  199. package/dist/instances.d.ts.map +1 -1
  200. package/dist/internal.mjs +7 -7
  201. package/dist/pbr-pipeline.d.ts +19 -1
  202. package/dist/pbr-pipeline.d.ts.map +1 -1
  203. package/dist/pipeline/single-layer-medium-passes.d.ts +73 -0
  204. package/dist/pipeline/single-layer-medium-passes.d.ts.map +1 -0
  205. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  206. package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
  207. package/dist/pipeline/standard-output/types.d.ts +4 -2
  208. package/dist/pipeline/standard-output/types.d.ts.map +1 -1
  209. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  210. package/dist/pipeline/standard-post.d.ts +28 -19
  211. package/dist/pipeline/standard-post.d.ts.map +1 -1
  212. package/dist/pipeline-builder.d.ts +1 -1
  213. package/dist/pipeline-builder.d.ts.map +1 -1
  214. package/dist/pipeline-spec.d.ts +2 -2
  215. package/dist/plugin.d.ts.map +1 -1
  216. package/dist/prepare/prepared-graphics-resolver.d.ts +2 -0
  217. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  218. package/dist/record/barrel-distortion-frame.d.ts +15 -0
  219. package/dist/record/barrel-distortion-frame.d.ts.map +1 -0
  220. package/dist/record/dynamic-geometry-consumption.d.ts +10 -2
  221. package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -1
  222. package/dist/record/fold-buckets.d.ts +95 -0
  223. package/dist/record/fold-buckets.d.ts.map +1 -0
  224. package/dist/record/fold-instance-buffer.d.ts +6 -0
  225. package/dist/record/fold-instance-buffer.d.ts.map +1 -0
  226. package/dist/record/frame-lighting.d.ts +1 -0
  227. package/dist/record/frame-lighting.d.ts.map +1 -1
  228. package/dist/record/frame-snapshot.d.ts +40 -3
  229. package/dist/record/frame-snapshot.d.ts.map +1 -1
  230. package/dist/record/frame-targets.d.ts.map +1 -1
  231. package/dist/record/frame.d.ts +14 -62
  232. package/dist/record/frame.d.ts.map +1 -1
  233. package/dist/record/gpu-draw-receipts.d.ts +11 -0
  234. package/dist/record/gpu-draw-receipts.d.ts.map +1 -0
  235. package/dist/record/gpu-timing.d.ts +8 -0
  236. package/dist/record/gpu-timing.d.ts.map +1 -1
  237. package/dist/record/main-pass-geometry.d.ts +21 -5
  238. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  239. package/dist/record/main-pass-material.d.ts +7 -1
  240. package/dist/record/main-pass-material.d.ts.map +1 -1
  241. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  242. package/dist/record/main-pass.d.ts +13 -2
  243. package/dist/record/main-pass.d.ts.map +1 -1
  244. package/dist/record/mesh-ssbo.d.ts +6 -5
  245. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  246. package/dist/record/motion-blur-receipt.d.ts +11 -0
  247. package/dist/record/motion-blur-receipt.d.ts.map +1 -0
  248. package/dist/record/probe-blend-buffer.d.ts +16 -0
  249. package/dist/record/probe-blend-buffer.d.ts.map +1 -0
  250. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  251. package/dist/record/render-context.d.ts +34 -31
  252. package/dist/record/render-context.d.ts.map +1 -1
  253. package/dist/record/shadow-pass.d.ts +2 -0
  254. package/dist/record/shadow-pass.d.ts.map +1 -1
  255. package/dist/record/skybox-post-pass.d.ts +6 -4
  256. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  257. package/dist/record/typed-frame-graph.d.ts +22 -2
  258. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  259. package/dist/record/view-ubo.d.ts +11 -3
  260. package/dist/record/view-ubo.d.ts.map +1 -1
  261. package/dist/record/volume-params.d.ts +9 -0
  262. package/dist/record/volume-params.d.ts.map +1 -0
  263. package/dist/recovery/render-system-candidate.d.ts +10 -0
  264. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  265. package/dist/reflection/record-owner.d.ts.map +1 -1
  266. package/dist/render-contract.d.ts +66 -8
  267. package/dist/render-contract.d.ts.map +1 -1
  268. package/dist/render-graph-primitives.d.ts.map +1 -1
  269. package/dist/render-pipeline.d.ts +106 -3
  270. package/dist/render-pipeline.d.ts.map +1 -1
  271. package/dist/render-system-extract-tail.d.ts +3 -2
  272. package/dist/render-system-extract-tail.d.ts.map +1 -1
  273. package/dist/render-system-extract.d.ts +24 -10
  274. package/dist/render-system-extract.d.ts.map +1 -1
  275. package/dist/render-system-presentation.d.ts +23 -0
  276. package/dist/render-system-presentation.d.ts.map +1 -0
  277. package/dist/render-system-producer-roots.d.ts +10 -0
  278. package/dist/render-system-producer-roots.d.ts.map +1 -0
  279. package/dist/render-system-residency.d.ts +12 -0
  280. package/dist/render-system-residency.d.ts.map +1 -0
  281. package/dist/render-system.d.ts +26 -15
  282. package/dist/render-system.d.ts.map +1 -1
  283. package/dist/scene/probe-blend.d.ts +27 -0
  284. package/dist/scene/probe-blend.d.ts.map +1 -1
  285. package/dist/scene/render-scene.d.ts +14 -5
  286. package/dist/scene/render-scene.d.ts.map +1 -1
  287. package/dist/ssao-buffers.d.ts +9 -0
  288. package/dist/ssao-buffers.d.ts.map +1 -1
  289. package/dist/surface/dynamic-input.d.ts +133 -0
  290. package/dist/surface/dynamic-input.d.ts.map +1 -0
  291. package/dist/surface/gpu-driven.d.ts +41 -0
  292. package/dist/surface/gpu-driven.d.ts.map +1 -0
  293. package/dist/surface/submission-observation.d.ts +136 -0
  294. package/dist/surface/submission-observation.d.ts.map +1 -0
  295. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  296. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  297. package/dist/temporal/frame.d.ts +7 -0
  298. package/dist/temporal/frame.d.ts.map +1 -1
  299. package/dist/temporal/gpu.d.ts +13 -1
  300. package/dist/temporal/gpu.d.ts.map +1 -1
  301. package/dist/temporal/index.mjs +5 -5
  302. package/dist/temporal/inspection.d.ts.map +1 -1
  303. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  304. package/dist/temporal/target-coverage-attachment.d.ts.map +1 -1
  305. package/dist/temporal/view.d.ts +6 -1
  306. package/dist/temporal/view.d.ts.map +1 -1
  307. package/dist/transmission/single-layer-medium.d.ts +89 -0
  308. package/dist/transmission/single-layer-medium.d.ts.map +1 -0
  309. package/dist/typed-render-graph-primitives.d.ts +24 -9
  310. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  311. package/dist/volume/component.d.ts +9 -5
  312. package/dist/volume/component.d.ts.map +1 -1
  313. package/dist/volume/extract.d.ts +1 -1
  314. package/dist/volume/extract.d.ts.map +1 -1
  315. package/dist/volume/inspection.d.ts +2 -0
  316. package/dist/volume/inspection.d.ts.map +1 -1
  317. package/dist/volume/passes.d.ts +4 -1
  318. package/dist/volume/passes.d.ts.map +1 -1
  319. package/dist/volume/resources.d.ts.map +1 -1
  320. package/dist/volume/temporal.d.ts +3 -1
  321. package/dist/volume/temporal.d.ts.map +1 -1
  322. package/package.json +22 -21
  323. package/src/__tests__/analytic-fog-graph.unit.test.ts +199 -0
  324. package/src/__tests__/analytic-fog-optics.unit.test.ts +38 -0
  325. package/src/__tests__/atmosphere-daylight.dawn.test.ts +72 -0
  326. package/src/__tests__/barrel-distortion-lkg.integration.test.ts +353 -0
  327. package/src/__tests__/barrel-distortion.dawn.test.ts +41 -0
  328. package/src/__tests__/barrel-distortion.unit.test.ts +364 -0
  329. package/src/__tests__/bloom-exact-zero.unit.test.ts +112 -18
  330. package/src/__tests__/bloom-lifecycle.integration.test.ts +17 -4
  331. package/src/__tests__/construct-renderer-identity.unit.test.ts +49 -0
  332. package/src/__tests__/detached-recovery-graph.unit.test.ts +277 -0
  333. package/src/__tests__/device-loss-fanout.unit.test.ts +36 -1
  334. package/src/__tests__/docs-gate-green.unit.test.ts +112 -1
  335. package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +20 -0
  336. package/src/__tests__/environment-fog-lanes.integration.test.ts +14 -0
  337. package/src/__tests__/environment-selection.unit.test.ts +12 -0
  338. package/src/__tests__/factory-contract.integration.test.ts +626 -113
  339. package/src/__tests__/feature-depth-input.dawn.test.ts +129 -2
  340. package/src/__tests__/frame-camera-selection.unit.test.ts +39 -1
  341. package/src/__tests__/frame-plan-contract.unit.test.ts +2 -0
  342. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +14 -3
  343. package/src/__tests__/gpu-dirty-ranges.unit.test.ts +22 -0
  344. package/src/__tests__/gpu-driven-baseline.characterization.test.ts +16 -3
  345. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +12 -0
  346. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +2 -1
  347. package/src/__tests__/gpu-driven-material-artifacts.unit.test.ts +143 -0
  348. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +2 -2
  349. package/src/__tests__/gpu-driven-production.integration.test.ts +482 -3
  350. package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +7 -6
  351. package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +1 -1
  352. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +103 -4
  353. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +18 -2
  354. package/src/__tests__/gpu-driven-view.browser.test.ts +5 -8
  355. package/src/__tests__/gpu-driven-view.dawn.test.ts +6 -2
  356. package/src/__tests__/gpu-driven-view.unit.test.ts +1 -1
  357. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +1 -0
  358. package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -4
  359. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +1 -0
  360. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +12 -0
  361. package/src/__tests__/gpu-scene.unit.test.ts +210 -9
  362. package/src/__tests__/instance-allocation-failure.unit.test.ts +57 -0
  363. package/src/__tests__/instances-culling.integration.test.ts +10 -36
  364. package/src/__tests__/instances-store.unit.test.ts +105 -238
  365. package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
  366. package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +146 -0
  367. package/src/__tests__/material-shader-prewarm.unit.test.ts +59 -0
  368. package/src/__tests__/material-snapshot-mutation.integration.test.ts +39 -1
  369. package/src/__tests__/mesh-submission-lifetime.unit.test.ts +31 -0
  370. package/src/__tests__/morph-culling-reentry.dawn.test.ts +5 -5
  371. package/src/__tests__/morph-visual-evidence.integration.test.ts +8 -7
  372. package/src/__tests__/motion-blur-compute.dawn.test.ts +589 -0
  373. package/src/__tests__/motion-blur.pixel.integration.test.ts +2 -2
  374. package/src/__tests__/nineslice-handle-surface.unit.test.ts +4 -4
  375. package/src/__tests__/pbr-view-layout.unit.test.ts +9 -0
  376. package/src/__tests__/physical-clearcoat.integration.test.ts +58 -0
  377. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +21 -0
  378. package/src/__tests__/prepared-graphics-capability.unit.test.ts +66 -2
  379. package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +86 -0
  380. package/src/__tests__/probe-bindings.unit.test.ts +582 -1
  381. package/src/__tests__/probe-scene-dirty.integration.test.ts +28 -1
  382. package/src/__tests__/public-surface.test-d.ts +8 -0
  383. package/src/__tests__/receipt-observation-retirement.unit.test.ts +65 -0
  384. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +69 -0
  385. package/src/__tests__/render-error-code-owner.test-d.ts +9 -0
  386. package/src/__tests__/render-feature-diagnostics.unit.test.ts +15 -2
  387. package/src/__tests__/render-feature-error-reporting.unit.test.ts +44 -0
  388. package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +10 -1
  389. package/src/__tests__/render-feature-retry.unit.test.ts +12 -1
  390. package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +23 -0
  391. package/src/__tests__/render-scene-mixed-updates.integration.test.ts +145 -19
  392. package/src/__tests__/render-scene-projection.unit.test.ts +36 -11
  393. package/src/__tests__/render-scene-temporal-retry.unit.test.ts +37 -6
  394. package/src/__tests__/render-target-probe-soak.perf.test.ts +2 -2
  395. package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +3 -3
  396. package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +5 -2
  397. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +41 -7
  398. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +16 -0
  399. package/src/__tests__/renderer-frame-transaction.integration.test.ts +33 -11
  400. package/src/__tests__/resource-allocation.unit.test.ts +123 -0
  401. package/src/__tests__/shader-manifest-fixture.ts +4 -4
  402. package/src/__tests__/single-layer-medium-passes.unit.test.ts +275 -0
  403. package/src/__tests__/skin-motion-regression.unit.test.ts +70 -1
  404. package/src/__tests__/skin-palette-recycling.dawn.test.ts +97 -0
  405. package/src/__tests__/skinned-shadow-caster.test.ts +17 -0
  406. package/src/__tests__/ssr-carrier-public-contract.unit.test.ts +21 -0
  407. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +4 -4
  408. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +2 -2
  409. package/src/__tests__/ssr-gpu-dispatch.ts +1 -1
  410. package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +6 -2
  411. package/src/__tests__/standard-lighting-write.unit.test.ts +2 -1
  412. package/src/__tests__/standard-output-chain-contract.unit.test.ts +28 -0
  413. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +46 -1
  414. package/src/__tests__/standard-pipeline.dawn.test.ts +1 -1
  415. package/src/__tests__/standard-pipeline.integration.test.ts +200 -9
  416. package/src/__tests__/surface-dynamic-buffer-lifecycle.unit.test.ts +506 -0
  417. package/src/__tests__/surface-submission-observation.unit.test.ts +812 -0
  418. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +32 -0
  419. package/src/__tests__/temporal-performance.integration.test.ts +3 -3
  420. package/src/__tests__/temporal-reset-matrix.unit.test.ts +10 -0
  421. package/src/__tests__/temporal-view.unit.test.ts +2 -1
  422. package/src/__tests__/typed-pipeline-topology.unit.test.ts +20 -4
  423. package/src/__tests__/visibility-instances.integration.test.ts +7 -19
  424. package/src/__tests__/visibility-producer-matrix.integration.test.ts +9 -20
  425. package/src/__tests__/volume-optics-oracle.unit.test.ts +7 -3
  426. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +189 -15
  427. package/src/__tests__/volumetric-fog-errors.unit.test.ts +4 -3
  428. package/src/__tests__/volumetric-fog-multiple.unit.test.ts +55 -0
  429. package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +1 -1
  430. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +558 -68
  431. package/src/__tests__/volumetric-fog-temporal.unit.test.ts +27 -0
  432. package/src/__tests__/volumetric-fog-topology.unit.test.ts +23 -5
  433. package/src/assembly/analytic-fog-registration.ts +14 -0
  434. package/src/assembly/backend-contract.ts +30 -0
  435. package/src/assembly/barrel-distortion-inspection.ts +22 -0
  436. package/src/assembly/canvas-draw-guard.ts +11 -0
  437. package/src/assembly/cloud-shadow-entry.ts +22 -0
  438. package/src/assembly/dynamic-geometry-host.ts +65 -37
  439. package/src/assembly/dynamic-geometry-runtime.ts +7 -1
  440. package/src/assembly/factory.ts +2 -30
  441. package/src/assembly/fxaa-registration.ts +17 -0
  442. package/src/assembly/host-contract.ts +3 -3
  443. package/src/assembly/lod-observation.ts +12 -0
  444. package/src/assembly/material/assembly.ts +25 -13
  445. package/src/assembly/material/pipeline-helpers.ts +53 -12
  446. package/src/assembly/material-shader-policy.ts +55 -0
  447. package/src/assembly/material-shader-prewarm.ts +21 -10
  448. package/src/assembly/motion-blur-registration.ts +17 -0
  449. package/src/assembly/recovery/device-loss-fanout.ts +13 -0
  450. package/src/assembly/recovery/renderer-recover.ts +41 -18
  451. package/src/assembly/render-feature-post-processes.ts +54 -0
  452. package/src/assembly/renderer-facade.ts +6 -1
  453. package/src/assembly/renderer-feature-inspection.ts +8 -2
  454. package/src/assembly/renderer-frame-transaction.ts +23 -0
  455. package/src/assembly/renderer-inspection.ts +28 -0
  456. package/src/assembly/skin-palette-owner.ts +1 -1
  457. package/src/assembly/temporal-post-process-prewarm.ts +40 -0
  458. package/src/assembly/webgpu-pbr-ready.ts +9 -1
  459. package/src/assembly/webgpu-ready-contract.ts +8 -3
  460. package/src/assembly/webgpu-ready-per-pass.ts +104 -0
  461. package/src/assembly/webgpu-ready.ts +296 -306
  462. package/src/assembly/webgpu-renderer-bootstrap.ts +0 -12
  463. package/src/assembly/webgpu-renderer-observation-owner.ts +222 -0
  464. package/src/assembly/webgpu-renderer.ts +228 -281
  465. package/src/assembly/webgpu-vertex-layouts.ts +1 -1
  466. package/src/barrel-distortion.ts +247 -0
  467. package/src/bloom-admission.ts +21 -0
  468. package/src/cloud/__tests__/cloud-density-parity.dawn.test.ts +173 -0
  469. package/src/cloud/__tests__/cloud-layer-framebuffer-y.dawn.test.ts +265 -0
  470. package/src/cloud/__tests__/cloud-layer.browser.test.ts +432 -0
  471. package/src/cloud/__tests__/cloud-layer.unit.test.ts +634 -0
  472. package/src/cloud/density.ts +600 -0
  473. package/src/cloud/extract.ts +110 -0
  474. package/src/cloud/feature.ts +1810 -0
  475. package/src/cloud/frame-context.ts +42 -0
  476. package/src/cloud/inspection.ts +130 -0
  477. package/src/cloud/optics.ts +285 -0
  478. package/src/cloud/parameters.ts +255 -0
  479. package/src/cloud/resources.ts +122 -0
  480. package/src/cloud/shadow.ts +167 -0
  481. package/src/cloud/temporal.ts +226 -0
  482. package/src/components/__tests__/motion-blur.unit.test.ts +1 -0
  483. package/src/components/atmosphere.ts +4 -0
  484. package/src/components/barrel-distortion.ts +46 -0
  485. package/src/components/camera.ts +66 -10
  486. package/src/components/cloud-layer.ts +47 -0
  487. package/src/components/index.ts +2 -0
  488. package/src/components/instances.ts +18 -45
  489. package/src/components/mesh-filter.ts +11 -9
  490. package/src/components/motion-blur.ts +1 -0
  491. package/src/components/sprite-instances.ts +1 -1
  492. package/src/construct-renderer.ts +1 -1
  493. package/src/device/gpu-residency.ts +56 -12
  494. package/src/dynamic-geometry.ts +12 -1
  495. package/src/environment/analytic-fog.ts +44 -0
  496. package/src/environment/background.ts +2 -2
  497. package/src/errors/cloud.ts +103 -0
  498. package/src/errors/render.ts +58 -8
  499. package/src/examples/single-layer-medium-surface.ts +59 -0
  500. package/src/extract/camera.ts +35 -8
  501. package/src/extract/contracts.ts +12 -0
  502. package/src/extract/environment.ts +4 -0
  503. package/src/features/barrel-distortion.ts +172 -0
  504. package/src/features/error-reporting.ts +98 -0
  505. package/src/features/host.ts +138 -24
  506. package/src/features/motion-blur/__tests__/motion-blur-feature.unit.test.ts +111 -1
  507. package/src/features/motion-blur/__tests__/motion-blur-params.unit.test.ts +58 -3
  508. package/src/features/motion-blur/motion-blur-feature.ts +1519 -15
  509. package/src/features/motion-blur/motion-blur-params.ts +70 -2
  510. package/src/features/motion-blur/motion-blur-runtime.ts +176 -0
  511. package/src/features/plan.ts +33 -8
  512. package/src/features/prepared-gpu-work.ts +86 -35
  513. package/src/features/render-graph-compute.ts +13 -5
  514. package/src/features/render-graph-contribution.ts +11 -0
  515. package/src/features/render-graph-raster.ts +45 -3
  516. package/src/features/targets.ts +66 -0
  517. package/src/features/types.ts +47 -0
  518. package/src/fullscreen-post-process-pass.ts +41 -9
  519. package/src/gpu-dirty-ranges.ts +48 -0
  520. package/src/gpu-driven/batch-topology.ts +53 -0
  521. package/src/gpu-driven/material-artifacts.ts +35 -32
  522. package/src/gpu-driven/prepared-draw.ts +27 -14
  523. package/src/gpu-driven/production-raster-material.ts +1894 -0
  524. package/src/gpu-driven/production-raster.ts +904 -787
  525. package/src/gpu-driven/resource-allocation.ts +142 -0
  526. package/src/gpu-driven/shadow-ownership.ts +71 -0
  527. package/src/gpu-driven/view-gpu.ts +181 -10
  528. package/src/gpu-scene.ts +259 -101
  529. package/src/hdrp-buffers.ts +4 -3
  530. package/src/ibl/skylight-bind-group.ts +49 -7
  531. package/src/index.ts +228 -9
  532. package/src/inspection-types.ts +222 -2
  533. package/src/instance-buffer-cache.ts +4 -4
  534. package/src/instances.ts +151 -492
  535. package/src/pbr-pipeline.ts +219 -13
  536. package/src/pipeline/single-layer-medium-passes.ts +267 -0
  537. package/src/pipeline/standard-forward-lane.ts +313 -57
  538. package/src/pipeline/standard-output/graph.ts +8 -2
  539. package/src/pipeline/standard-output/types.ts +12 -1
  540. package/src/pipeline/standard-pipeline.ts +291 -18
  541. package/src/pipeline/standard-post.ts +397 -102
  542. package/src/pipeline-builder.ts +1 -1
  543. package/src/pipeline-spec.ts +5 -5
  544. package/src/plugin.ts +4 -0
  545. package/src/points-lines/__tests__/points-lines.bench.ts +17 -8
  546. package/src/points-lines/__tests__/view-ubo.unit.test.ts +2 -1
  547. package/src/prepare/prepared-graphics-resolver.ts +2 -0
  548. package/src/record/__tests__/main-pass-geometry.unit.test.ts +30 -0
  549. package/src/record/__tests__/sprite-material-abi.unit.test.ts +1 -1
  550. package/src/record/__tests__/temporal-payload.unit.test.ts +3 -2
  551. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +11 -0
  552. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -0
  553. package/src/record/__tests__/view-ubo-layout.unit.test.ts +4 -3
  554. package/src/record/barrel-distortion-frame.ts +110 -0
  555. package/src/record/dynamic-geometry-consumption.ts +39 -9
  556. package/src/record/fold-buckets.ts +852 -0
  557. package/src/record/fold-instance-buffer.ts +51 -0
  558. package/src/record/frame-lighting.ts +36 -2
  559. package/src/record/frame-snapshot.ts +44 -4
  560. package/src/record/frame-targets.ts +10 -6
  561. package/src/record/frame.ts +373 -1099
  562. package/src/record/gpu-draw-receipts.ts +38 -0
  563. package/src/record/gpu-timing.ts +48 -5
  564. package/src/record/main-pass-geometry.ts +281 -83
  565. package/src/record/main-pass-material.ts +66 -4
  566. package/src/record/main-pass-sprite-draws.ts +13 -66
  567. package/src/record/main-pass.ts +156 -20
  568. package/src/record/mesh-ssbo.ts +43 -6
  569. package/src/record/motion-blur-receipt.ts +91 -0
  570. package/src/record/probe-blend-buffer.ts +180 -0
  571. package/src/record/recovery-pipeline.ts +35 -3
  572. package/src/record/render-context.ts +51 -31
  573. package/src/record/shadow-pass.ts +36 -2
  574. package/src/record/skybox-post-pass.ts +150 -200
  575. package/src/record/typed-frame-graph.ts +824 -44
  576. package/src/record/view-ubo.ts +26 -3
  577. package/src/record/volume-params.ts +225 -0
  578. package/src/recovery/render-system-candidate.ts +257 -52
  579. package/src/reflection/record-owner.ts +7 -3
  580. package/src/render-contract.ts +71 -8
  581. package/src/render-graph-primitives.ts +7 -1
  582. package/src/render-pipeline.ts +136 -1
  583. package/src/render-system-extract-tail.ts +72 -60
  584. package/src/render-system-extract.ts +117 -87
  585. package/src/render-system-presentation.ts +56 -0
  586. package/src/render-system-producer-roots.ts +96 -0
  587. package/src/render-system-residency.ts +159 -0
  588. package/src/render-system.ts +601 -482
  589. package/src/scene/probe-blend.ts +96 -4
  590. package/src/scene/render-scene.ts +431 -281
  591. package/src/ssao-buffers.ts +95 -2
  592. package/src/surface/__tests__/dynamic-input.unit.test.ts +374 -0
  593. package/src/surface/__tests__/gpu-driven.unit.test.ts +87 -0
  594. package/src/surface/dynamic-input.ts +658 -0
  595. package/src/surface/gpu-driven.ts +147 -0
  596. package/src/surface/submission-observation.ts +517 -0
  597. package/src/systems/skin-palette-allocator.ts +4 -1
  598. package/src/temporal/__tests__/temporal-frame-transaction.unit.test.ts +36 -0
  599. package/src/temporal/frame-coordinator.ts +8 -1
  600. package/src/temporal/frame.ts +20 -0
  601. package/src/temporal/gpu.ts +101 -17
  602. package/src/temporal/inspection.ts +7 -1
  603. package/src/temporal/standard-scene-data.ts +5 -0
  604. package/src/temporal/target-coverage-attachment.ts +4 -0
  605. package/src/temporal/view.ts +13 -2
  606. package/src/transmission/__tests__/single-layer-medium.unit.test.ts +149 -0
  607. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +12 -11
  608. package/src/transmission/single-layer-medium.ts +322 -0
  609. package/src/typed-render-graph-primitives.ts +191 -50
  610. package/src/volume/component.ts +10 -10
  611. package/src/volume/extract.ts +11 -5
  612. package/src/volume/inspection.ts +3 -0
  613. package/src/volume/passes.ts +95 -8
  614. package/src/volume/resources.ts +2 -1
  615. package/src/volume/temporal.ts +11 -0
  616. package/dist/chunk-4NNZQCL3.mjs.map +0 -1
  617. package/dist/chunk-6TFBXLRJ.mjs.map +0 -1
  618. package/dist/chunk-DRR7D3KX.mjs.map +0 -1
  619. package/dist/chunk-GWCEMDVR.mjs.map +0 -1
  620. package/dist/chunk-JCIRYQ4V.mjs.map +0 -1
  621. package/dist/chunk-JRIPCMYC.mjs.map +0 -1
  622. package/dist/chunk-LULCA3OF.mjs +0 -1713
  623. package/dist/chunk-LULCA3OF.mjs.map +0 -1
  624. package/dist/chunk-PA5DRJNI.mjs.map +0 -1
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { createTemporalFrameTransaction } from '../frame';
3
+
4
+ function frame(frameId: number, resetReason?: 'camera-cut') {
5
+ return {
6
+ frameId,
7
+ currentViewProjection: new Float32Array(16),
8
+ jitter: [0, 0] as [number, number],
9
+ viewport: { width: 64, height: 64 },
10
+ cameraPosition: [frameId, 0, 0] as [number, number, number],
11
+ ...(resetReason === undefined ? {} : { resetReason }),
12
+ };
13
+ }
14
+
15
+ describe('renderer temporal candidate lifecycle', () => {
16
+ it('projects a rejected submit while retaining the accepted baseline', () => {
17
+ const transaction = createTemporalFrameTransaction({ deviceEpoch: 4 });
18
+ transaction.stage(frame(1));
19
+ expect(transaction.commit({ accepted: true }).ok).toBe(true);
20
+ const baseline = transaction.snapshot();
21
+
22
+ transaction.stage(frame(2));
23
+ expect(transaction.commit({ accepted: false, reason: 'queue-submit-failed' }).ok).toBe(false);
24
+ expect(transaction.snapshot()).toEqual(baseline);
25
+ expect(transaction.inspect()).toEqual({
26
+ accepted: true,
27
+ staged: false,
28
+ lastFailure: 'queue-submit-failed',
29
+ });
30
+
31
+ transaction.stage(frame(3));
32
+ expect(transaction.commit({ accepted: true }).ok).toBe(true);
33
+ expect(transaction.inspect()).toEqual({ accepted: true, staged: false });
34
+ expect(transaction.snapshot().frameId).toBe(3);
35
+ });
36
+ });
@@ -1,4 +1,5 @@
1
1
  import { err, ok, type Result } from '@forgeax/engine-types';
2
+ import { motionBlurTemporalDemand } from '../features/motion-blur/motion-blur-params';
2
3
  import type { CameraSnapshot } from '../render-contract';
3
4
  import { classifySceneDataCoverage, type SceneDataContributor } from './coverage';
4
5
  import { type TaaHistoryAttempt, TaaHistoryStore } from './taa-history-store';
@@ -301,7 +302,13 @@ export class TemporalFrameCoordinator<T = unknown> {
301
302
  ): TemporalView | undefined {
302
303
  const demanded =
303
304
  camera.antialias === 'taa' ||
304
- (camera.motionBlur?.shutterAngle !== undefined && camera.motionBlur.shutterAngle > 0);
305
+ (camera.motionBlur !== undefined &&
306
+ motionBlurTemporalDemand({
307
+ shutterAngle: camera.motionBlur.shutterAngle,
308
+ maxRadiusPixels: camera.motionBlur.maxRadiusPixels,
309
+ sampleCount: camera.motionBlur.sampleCount,
310
+ targetFps: camera.motionBlur.targetFps ?? 60,
311
+ }));
305
312
  if (!demanded) return undefined;
306
313
  return projectTemporalView({
307
314
  camera,
@@ -40,10 +40,18 @@ export interface TemporalSubmitResult {
40
40
  readonly resetReason?: Exclude<TemporalResetReason, 'none' | 'queue-submit-failed'>;
41
41
  }
42
42
 
43
+ /** Detached candidate/lifecycle facts consumed by Renderer inspection. */
44
+ export interface TemporalFrameTransactionInspection {
45
+ readonly accepted: boolean;
46
+ readonly staged: boolean;
47
+ readonly lastFailure?: 'queue-submit-failed';
48
+ }
49
+
43
50
  export interface TemporalFrameTransaction {
44
51
  stage(input: TemporalFrameInput): void;
45
52
  commit(submit: TemporalSubmitResult): Result<TemporalFrame, TemporalFrameSubmitError>;
46
53
  snapshot(): TemporalFrame;
54
+ inspect(): TemporalFrameTransactionInspection;
47
55
  /** Drop accepted/staged history after a producer or device generation cut. */
48
56
  reset(reason?: Extract<TemporalResetReason, 'signature-change' | 'device-recovery'>): void;
49
57
  }
@@ -85,6 +93,7 @@ export function createTemporalFrameTransaction(options: {
85
93
  let staged: TemporalFrameInput | undefined;
86
94
  let historyEpoch = 0;
87
95
  let pendingResetReason: Exclude<TemporalResetReason, 'none' | 'queue-submit-failed'> | undefined;
96
+ let lastFailure: TemporalFrameTransactionInspection['lastFailure'];
88
97
 
89
98
  return {
90
99
  stage(input) {
@@ -93,9 +102,11 @@ export function createTemporalFrameTransaction(options: {
93
102
  commit(submit) {
94
103
  if (!submit.accepted) {
95
104
  staged = undefined;
105
+ lastFailure = submit.reason ?? 'queue-submit-failed';
96
106
  return err(new TemporalFrameSubmitError(submit.reason ?? 'queue-submit-failed'));
97
107
  }
98
108
  if (staged === undefined) {
109
+ lastFailure = 'queue-submit-failed';
99
110
  return err(new TemporalFrameSubmitError('queue-submit-failed'));
100
111
  }
101
112
 
@@ -121,6 +132,7 @@ export function createTemporalFrameTransaction(options: {
121
132
  accepted = next;
122
133
  staged = undefined;
123
134
  pendingResetReason = undefined;
135
+ lastFailure = undefined;
124
136
  return ok(copyFrame(next));
125
137
  },
126
138
  snapshot() {
@@ -128,6 +140,13 @@ export function createTemporalFrameTransaction(options: {
128
140
  throw new Error('TemporalFrame snapshot is unavailable before commit');
129
141
  return copyFrame(accepted);
130
142
  },
143
+ inspect() {
144
+ return {
145
+ accepted: accepted !== undefined,
146
+ staged: staged !== undefined,
147
+ ...(lastFailure === undefined ? {} : { lastFailure }),
148
+ };
149
+ },
131
150
  reset(reason = 'signature-change') {
132
151
  accepted = undefined;
133
152
  staged = undefined;
@@ -136,6 +155,7 @@ export function createTemporalFrameTransaction(options: {
136
155
  // committed frame prevents stale motion vectors from crossing the
137
156
  // producer/device boundary.
138
157
  pendingResetReason = reason;
158
+ lastFailure = undefined;
139
159
  },
140
160
  };
141
161
  }
@@ -23,6 +23,13 @@ export const TEMPORAL_HISTORY_FORMATS = Object.freeze({
23
23
  stability: 'r8unorm',
24
24
  } as const);
25
25
 
26
+ /** Cloud transport history shares the renderer temporal owner and ping-pong fence. */
27
+ export const CLOUD_HISTORY_FORMATS = Object.freeze({
28
+ radiance: 'rgba16float',
29
+ transmittance: 'rgba16float',
30
+ depth: 'rgba16float',
31
+ } as const);
32
+
26
33
  interface TemporalSurface {
27
34
  readonly texture: Texture;
28
35
  readonly view: TextureView;
@@ -40,6 +47,12 @@ export interface TemporalGpuState {
40
47
  readonly color: [TemporalSurface, TemporalSurface];
41
48
  readonly temporal: [TemporalSurface, TemporalSurface];
42
49
  readonly stability: [TemporalSurface, TemporalSurface];
50
+ /** Whether this candidate owns the optional cloud transport history MRT. */
51
+ readonly cloudHistoryEnabled: boolean;
52
+ /** Previous/current cloud radiance, transmittance and representative depth. */
53
+ readonly cloudRadiance: [TemporalSurface, TemporalSurface] | undefined;
54
+ readonly cloudTransmittance: [TemporalSurface, TemporalSurface] | undefined;
55
+ readonly cloudDepth: [TemporalSurface, TemporalSurface] | undefined;
43
56
  bindGroupLayout: BindGroupLayout | undefined;
44
57
  sampler: Sampler | undefined;
45
58
  temporalSampler: Sampler | undefined;
@@ -81,13 +94,22 @@ function createSurface(
81
94
  return { texture: texture.value, view: view.value };
82
95
  }
83
96
 
97
+ function cloudHistoryExtent(width: number, height: number): { width: number; height: number } {
98
+ return {
99
+ width: Math.max(1, Math.ceil(width / 2)),
100
+ height: Math.max(1, Math.ceil(height / 2)),
101
+ };
102
+ }
103
+
84
104
  function createState(
85
105
  device: RhiDevice,
86
106
  scope: DeviceScope,
87
107
  width: number,
88
108
  height: number,
109
+ cloudHistoryEnabled: boolean,
89
110
  ): TemporalGpuState {
90
111
  const childScope = scope.createChild(`${scope.owner}:temporal`);
112
+ const cloudExtent = cloudHistoryExtent(width, height);
91
113
  try {
92
114
  return {
93
115
  device,
@@ -138,6 +160,65 @@ function createState(
138
160
  TEMPORAL_HISTORY_FORMATS.stability,
139
161
  ),
140
162
  ],
163
+ cloudHistoryEnabled,
164
+ cloudRadiance: cloudHistoryEnabled
165
+ ? [
166
+ createSurface(
167
+ device,
168
+ childScope,
169
+ 'cloud-history-radiance-a',
170
+ cloudExtent.width,
171
+ cloudExtent.height,
172
+ ),
173
+ createSurface(
174
+ device,
175
+ childScope,
176
+ 'cloud-history-radiance-b',
177
+ cloudExtent.width,
178
+ cloudExtent.height,
179
+ ),
180
+ ]
181
+ : undefined,
182
+ cloudTransmittance: cloudHistoryEnabled
183
+ ? [
184
+ createSurface(
185
+ device,
186
+ childScope,
187
+ 'cloud-history-transmittance-a',
188
+ cloudExtent.width,
189
+ cloudExtent.height,
190
+ CLOUD_HISTORY_FORMATS.transmittance,
191
+ ),
192
+ createSurface(
193
+ device,
194
+ childScope,
195
+ 'cloud-history-transmittance-b',
196
+ cloudExtent.width,
197
+ cloudExtent.height,
198
+ CLOUD_HISTORY_FORMATS.transmittance,
199
+ ),
200
+ ]
201
+ : undefined,
202
+ cloudDepth: cloudHistoryEnabled
203
+ ? [
204
+ createSurface(
205
+ device,
206
+ childScope,
207
+ 'cloud-history-depth-a',
208
+ cloudExtent.width,
209
+ cloudExtent.height,
210
+ CLOUD_HISTORY_FORMATS.depth,
211
+ ),
212
+ createSurface(
213
+ device,
214
+ childScope,
215
+ 'cloud-history-depth-b',
216
+ cloudExtent.width,
217
+ cloudExtent.height,
218
+ CLOUD_HISTORY_FORMATS.depth,
219
+ ),
220
+ ]
221
+ : undefined,
141
222
  bindGroupLayout: undefined,
142
223
  sampler: undefined,
143
224
  temporalSampler: undefined,
@@ -180,26 +261,29 @@ export function getTemporalGpuState(
180
261
  scope: DeviceScope,
181
262
  width: number,
182
263
  height: number,
264
+ cloudHistoryRequested = false,
183
265
  ): TemporalGpuState {
266
+ // Graph preparation publishes this demand before any imported target is
267
+ // resolved. That makes the first TAA and cloud pass share one candidate,
268
+ // while a plain TAA frame allocates only its six TAA surfaces.
269
+ const wantsCloudHistory =
270
+ cloudHistoryRequested ||
271
+ frameState.pendingCloudHistoryActive === true ||
272
+ frameState.cloudHistoryActive === true;
273
+ const matches = (state: TemporalGpuState | undefined): state is TemporalGpuState =>
274
+ state !== undefined &&
275
+ state.scope === scope &&
276
+ state.width === width &&
277
+ state.height === height &&
278
+ // Cloud history is a demand-shaped part of the temporal allocation. Keep
279
+ // the two shapes distinct so turning CloudLayer off retires its six HDR
280
+ // surfaces instead of retaining them on an otherwise TAA-only frame.
281
+ state.cloudHistoryEnabled === wantsCloudHistory;
184
282
  const staged = frameState.temporalGpuState;
185
- if (
186
- staged !== undefined &&
187
- staged.scope === scope &&
188
- staged.width === width &&
189
- staged.height === height
190
- ) {
191
- return staged;
192
- }
283
+ if (matches(staged)) return staged;
193
284
  const existing = frameState.activeTemporalGpuState;
194
- if (
195
- existing !== undefined &&
196
- existing.scope === scope &&
197
- existing.width === width &&
198
- existing.height === height
199
- ) {
200
- return existing;
201
- }
202
- const next = createState(device, scope, width, height);
285
+ if (matches(existing)) return existing;
286
+ const next = createState(device, scope, width, height, wantsCloudHistory);
203
287
  frameState.temporalGpuState = next;
204
288
  return next;
205
289
  }
@@ -29,7 +29,13 @@ export interface TemporalInspection {
29
29
  }
30
30
 
31
31
  function gpuBytes(state: TemporalGpuState | undefined): number {
32
- return state === undefined ? 0 : createTemporalCoverage(state.width, state.height).bytes;
32
+ if (state === undefined) return 0;
33
+ const taaBytes = createTemporalCoverage(state.width, state.height).bytes;
34
+ // The cloud owner adds six rgba16float surfaces (8 bytes per pixel) only
35
+ // when the candidate was admitted with cloud history demand. This is derived
36
+ // from the live state shape rather than reported as a fixed cloud estimate.
37
+ const cloudBytes = state.cloudHistoryEnabled ? state.width * state.height * 6 * 8 : 0;
38
+ return taaBytes + cloudBytes;
33
39
  }
34
40
 
35
41
  function gpuResources(state: TemporalGpuState | undefined): number {
@@ -156,6 +156,11 @@ export function addStandardSceneDataPass(
156
156
  color: target,
157
157
  depth,
158
158
  selector: { LightMode: ['Deferred', 'Forward'] },
159
+ // The temporal producer runs before the Surface raw-depth/nearest-layer
160
+ // pair. Keep it on the ordinary opaque Standard lane rather than asking
161
+ // a medium material to assemble a bind group from resources that this
162
+ // pass cannot own yet.
163
+ recordMode: 'opaque',
159
164
  passKind: 'temporal',
160
165
  // The temporal producer re-rasterizes the current scene after the main
161
166
  // color pass has populated depth. Its output is a semantic motion target;
@@ -64,6 +64,10 @@ export function addTargetCoveragePass(
64
64
  colorTargets: [target.coverage],
65
65
  depth: target.depth,
66
66
  selector: { LightMode: ['Deferred', 'Forward'] },
67
+ // Coverage is a generic Standard raster producer. Surface medium pixels
68
+ // are owned by the later paired nearest/color passes and have no raw-depth
69
+ // input at this point, so keep this attachment on the opaque lane.
70
+ recordMode: 'opaque',
67
71
  passKind: 'temporal',
68
72
  clearColor: [0, 0, 0, 0],
69
73
  depthLoadOp: 'clear',
@@ -21,6 +21,7 @@ export type TemporalResetReason =
21
21
  | 'view-switch'
22
22
  | 'environment-change'
23
23
  | 'fog-change'
24
+ | 'time-discontinuity'
24
25
  | 'device-recover'
25
26
  | 'submit-failure';
26
27
 
@@ -43,6 +44,8 @@ export interface TemporalViewInput {
43
44
  readonly previousUnjitteredViewProjection?: ArrayLike<number> | undefined;
44
45
  readonly currentCameraPosition?: readonly [number, number, number] | undefined;
45
46
  readonly previousCameraPosition?: readonly [number, number, number] | undefined;
47
+ /** Monotonic accepted sample time used to bridge failed render attempts. */
48
+ readonly sampleTimeSeconds?: number | undefined;
46
49
  readonly resetReason?: TemporalResetReason | undefined;
47
50
  }
48
51
 
@@ -65,6 +68,7 @@ interface TemporalViewSnapshot {
65
68
  readonly previousUnjitteredViewProjection: ArrayLike<number> | undefined;
66
69
  readonly currentCameraPosition: readonly [number, number, number] | undefined;
67
70
  readonly previousCameraPosition: readonly [number, number, number] | undefined;
71
+ readonly sampleTimeSeconds: number | undefined;
68
72
  readonly resetReason: TemporalResetReason | undefined;
69
73
  }
70
74
 
@@ -83,6 +87,8 @@ export interface TemporalView {
83
87
  /** Camera origins follow the same accepted-submit boundary as the matrices. */
84
88
  readonly currentCameraPosition: readonly [number, number, number] | undefined;
85
89
  readonly previousCameraPosition: readonly [number, number, number] | undefined;
90
+ /** Monotonic accepted sample time used to bridge failed render attempts. */
91
+ readonly sampleTimeSeconds: number | undefined;
86
92
  readonly currentProjection: 'jittered' | 'unjittered';
87
93
  readonly previousProjection: 'unjittered';
88
94
  readonly historyRequired: boolean;
@@ -131,6 +137,7 @@ export function createTemporalView(input: TemporalViewInput): TemporalView {
131
137
  previousUnjitteredViewProjection: input.previousUnjitteredViewProjection,
132
138
  currentCameraPosition: input.currentCameraPosition,
133
139
  previousCameraPosition: input.previousCameraPosition,
140
+ sampleTimeSeconds: input.sampleTimeSeconds,
134
141
  resetReason: input.resetReason,
135
142
  });
136
143
  const taa = input.antialias === 'taa';
@@ -156,6 +163,7 @@ export function createTemporalView(input: TemporalViewInput): TemporalView {
156
163
  previousUnjitteredViewProjection: input.previousUnjitteredViewProjection,
157
164
  currentCameraPosition: input.currentCameraPosition,
158
165
  previousCameraPosition: input.previousCameraPosition,
166
+ sampleTimeSeconds: input.sampleTimeSeconds,
159
167
  currentProjection: taa ? 'jittered' : 'unjittered',
160
168
  previousProjection: 'unjittered',
161
169
  historyRequired: taa,
@@ -167,8 +175,11 @@ export function resolveTemporalReset(
167
175
  previous: TemporalView | undefined,
168
176
  next: TemporalView,
169
177
  ): TemporalResetReason | undefined {
170
- if (next.mode !== 'taa') return undefined;
171
- if (previous === undefined || previous.mode !== 'taa') return 'first-frame';
178
+ // Motion Blur uses the unjittered `off` temporal mode as well as TAA. Keep
179
+ // the reset matrix shared so a camera switch, resize, or recovery cannot
180
+ // leak a previous motion vector merely because antialiasing is disabled.
181
+ if (previous === undefined) return 'first-frame';
182
+ if (next.mode === 'taa' && previous.mode !== 'taa') return 'first-frame';
172
183
  const before = previous.input;
173
184
  const after = next.input;
174
185
  if (before.width !== after.width || before.height !== after.height) return 'resize';
@@ -0,0 +1,149 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ composeSingleLayerMediumColor,
4
+ integrateSingleLayerMedium,
5
+ isBelowSingleLayerMediumPlane,
6
+ resolveSingleLayerMediumBackground,
7
+ } from '../single-layer-medium.js';
8
+
9
+ const coefficients = {
10
+ absorption: [0.2, 0.4, 0.8] as const,
11
+ scattering: [0.1, 0.1, 0.1] as const,
12
+ ior: 1.333,
13
+ phaseG: 0.4,
14
+ };
15
+
16
+ describe('single-layer medium optics', () => {
17
+ it('uses Beer-Lambert transmission and the zero-extinction limit', () => {
18
+ const short = integrateSingleLayerMedium({ coefficients, distanceMeters: 1, cosTheta: 1 });
19
+ const long = integrateSingleLayerMedium({ coefficients, distanceMeters: 2, cosTheta: 1 });
20
+ expect(long.transmittance[0]).toBeLessThan(short.transmittance[0]);
21
+ expect(long.transmittance[1]).toBeLessThan(short.transmittance[1]);
22
+ expect(short.singleScatter[0]).toBeGreaterThan(0);
23
+ const empty = integrateSingleLayerMedium({
24
+ coefficients: { absorption: [0, 0, 0], scattering: [0, 0, 0], ior: 1.333 },
25
+ distanceMeters: 4,
26
+ cosTheta: 1,
27
+ });
28
+ expect(empty.transmittance).toEqual([1, 1, 1]);
29
+ expect(empty.singleScatter).toEqual([0, 0, 0]);
30
+ expect(empty.reflectance).toBeGreaterThan(0);
31
+ expect(empty.reflectance).toBeLessThan(1);
32
+ expect(
33
+ [...empty.transmittance, ...empty.singleScatter, empty.phase].every(Number.isFinite),
34
+ ).toBe(true);
35
+ });
36
+
37
+ it('keeps the zero-extinction limit and small-x RGB oracle continuous', () => {
38
+ const zeroExtinction = integrateSingleLayerMedium({
39
+ coefficients: {
40
+ absorption: [0, 0, 0],
41
+ scattering: [0, 0, 0],
42
+ ior: 1.333,
43
+ },
44
+ distanceMeters: 4,
45
+ cosTheta: 1,
46
+ });
47
+ expect(zeroExtinction.singleScatter).toEqual([0, 0, 0]);
48
+
49
+ const coefficients = {
50
+ absorption: [0, 0.000001, 0.25] as const,
51
+ scattering: [0.000001, 0.000001, 0.5] as const,
52
+ ior: 1.333,
53
+ };
54
+ const distanceMeters = 100000;
55
+ const optics = integrateSingleLayerMedium({
56
+ coefficients,
57
+ distanceMeters,
58
+ maxDistanceMeters: distanceMeters,
59
+ cosTheta: 1,
60
+ });
61
+ const sigmaT = [0.000001, 0.000002, 0.75];
62
+ const expected = sigmaT.map((extinction, index) => {
63
+ const x = extinction * distanceMeters;
64
+ const oneMinusExpNeg = x < 0.001 ? x * (1 - x / 2 + (x * x) / 6) : -Math.expm1(-x);
65
+ return ((coefficients.scattering[index] ?? 0) * oneMinusExpNeg) / extinction;
66
+ });
67
+ expect(optics.distanceMeters).toBe(distanceMeters);
68
+ expect(optics.singleScatter[0]).toBeCloseTo(expected[0] ?? 0, 12);
69
+ expect(optics.singleScatter[1]).toBeCloseTo(expected[1] ?? 0, 12);
70
+ expect(optics.singleScatter[2]).toBeCloseTo(expected[2] ?? 0, 12);
71
+ expect(optics.singleScatter.every(Number.isFinite)).toBe(true);
72
+ });
73
+
74
+ it('allocates Fresnel once and keeps coverage/foam as finite mixing weights', () => {
75
+ const optics = integrateSingleLayerMedium({ coefficients, distanceMeters: 1.5, cosTheta: 0.4 });
76
+ const color = composeSingleLayerMediumColor({
77
+ optics,
78
+ background: [0.2, 0.3, 0.4],
79
+ reflection: [1, 1, 1],
80
+ foamColor: [0.9, 0.9, 0.9],
81
+ coverage: 0.8,
82
+ foam: 0.25,
83
+ });
84
+ expect(color.every(Number.isFinite)).toBe(true);
85
+ expect(color.every((channel) => channel >= 0)).toBe(true);
86
+ expect(
87
+ composeSingleLayerMediumColor({
88
+ optics,
89
+ background: [0, 0, 0],
90
+ reflection: [0, 0, 0],
91
+ coverage: 0,
92
+ }),
93
+ ).toEqual([0, 0, 0]);
94
+ });
95
+
96
+ it('keeps endpoint admission on the undisturbed plane under a strongly tilted ripple', () => {
97
+ const surfacePosition = [0, 0, 0] as const;
98
+ const endpoint = [1, -0.1, 0] as const;
99
+ expect(
100
+ isBelowSingleLayerMediumPlane({
101
+ surfacePosition,
102
+ geometricNormal: [0, 1, 0],
103
+ endpoint,
104
+ }),
105
+ ).toBe(true);
106
+ // This shading normal would classify the same endpoint above water. It is
107
+ // deliberately excluded from the geometric-plane API and WGSL callsite.
108
+ expect(endpoint[0] * 0.995 + endpoint[1] * 0.1).toBeGreaterThan(0);
109
+ });
110
+
111
+ it('keeps refracted color and depth paired and reports conservative fallbacks', () => {
112
+ const base = {
113
+ original: { color: [0.1, 0.2, 0.3] as const, depthMeters: 8, uv: [0.5, 0.5] as const },
114
+ surfaceDepthMeters: 2,
115
+ maxDistanceMeters: 20,
116
+ };
117
+ expect(
118
+ resolveSingleLayerMediumBackground({
119
+ ...base,
120
+ refracted: { color: [0.4, 0.5, 0.6], depthMeters: 7, uv: [0.55, 0.5] },
121
+ }),
122
+ ).toMatchObject({ source: 'refracted', distanceMeters: 5 });
123
+ expect(
124
+ resolveSingleLayerMediumBackground({
125
+ ...base,
126
+ refracted: { color: [0.4, 0.5, 0.6], depthMeters: 1, uv: [0.55, 0.5] },
127
+ }),
128
+ ).toMatchObject({ source: 'unrefracted', reason: 'front', color: base.original.color });
129
+ expect(
130
+ resolveSingleLayerMediumBackground({
131
+ ...base,
132
+ refracted: { color: [0.4, 0.5, 0.6], depthMeters: 7, uv: [1.2, 0.5] },
133
+ }),
134
+ ).toMatchObject({ source: 'unrefracted', reason: 'edge' });
135
+ expect(
136
+ resolveSingleLayerMediumBackground({
137
+ ...base,
138
+ original: { color: [0, 0, 0], depthMeters: Number.NaN, uv: [0.5, 0.5] },
139
+ environment: [0.7, 0.8, 0.9],
140
+ }),
141
+ ).toMatchObject({ source: 'environment', reason: 'sky-miss' });
142
+ expect(
143
+ resolveSingleLayerMediumBackground({
144
+ ...base,
145
+ refracted: { color: [0.4, 0.5, 0.6], depthMeters: 7, uv: [-0.01, 0.5] },
146
+ }),
147
+ ).toMatchObject({ source: 'unrefracted', reason: 'edge', color: base.original.color });
148
+ });
149
+ });
@@ -8,7 +8,7 @@ import {
8
8
  } from '@forgeax/engine-rhi-webgpu';
9
9
  import { Transform } from '@forgeax/engine-scene';
10
10
  import type { TextureAsset } from '@forgeax/engine-types';
11
- import { describe, expect, it } from 'vitest';
11
+ import { afterAll, describe, expect, it } from 'vitest';
12
12
  import {
13
13
  Camera,
14
14
  DirectionalLight,
@@ -32,17 +32,18 @@ const GPU_MAP_MODE_READ = 0x0001;
32
32
  const TEST_REACTIVE = 0;
33
33
  const LIGHTWEIGHT_DAWN = process.env.FORGEAX_DAWN_LIGHTWEIGHT === '1';
34
34
  const ROSTER_SMOKE = process.env.FORGEAX_DAWN_ROSTER_SMOKE === '1';
35
- const requestedSmokeFrames = Number.parseInt(process.env.SMOKE_MIN_FRAMES ?? '300', 10);
35
+ const requestedSmokeFrames = Number.parseInt(process.env.SMOKE_MIN_FRAMES ?? '60', 10);
36
36
  const ROSTER_SMOKE_FRAMES =
37
- Number.isInteger(requestedSmokeFrames) && requestedSmokeFrames > 0 ? requestedSmokeFrames : 300;
37
+ Number.isInteger(requestedSmokeFrames) && requestedSmokeFrames > 0 ? requestedSmokeFrames : 60;
38
38
 
39
39
  const ENGINE_MANIFEST = await (async () => {
40
40
  const { buildEngineShaderManifest } = await import('@forgeax/engine-vite-plugin-shader');
41
41
  return buildEngineShaderManifest();
42
42
  })();
43
- const ENGINE_MANIFEST_URL = `data:application/json,${encodeURIComponent(
44
- JSON.stringify(ENGINE_MANIFEST),
45
- )}`;
43
+ const ENGINE_MANIFEST_URL = URL.createObjectURL(
44
+ new Blob([JSON.stringify(ENGINE_MANIFEST)], { type: 'application/json' }),
45
+ );
46
+ afterAll(() => URL.revokeObjectURL(ENGINE_MANIFEST_URL));
46
47
  const DAWN_BACKEND: RhiBackendPack = {
47
48
  rhi,
48
49
  createShaderModule,
@@ -360,7 +361,7 @@ function luma(pixel: readonly [number, number, number, number]): number {
360
361
  }
361
362
 
362
363
  describe('Standard transmission Dawn ROI', () => {
363
- it('completes a 300-frame transmission stream without renderer errors', async () => {
364
+ it('completes a 60-frame transmission stream without renderer errors', async () => {
364
365
  const result = await renderCase(
365
366
  {
366
367
  name: 'sustained-transmission',
@@ -372,9 +373,9 @@ describe('Standard transmission Dawn ROI', () => {
372
373
  attenuationDistance: 2,
373
374
  renderPath: 'forward',
374
375
  },
375
- 300,
376
+ 60,
376
377
  );
377
- expect(result.framesObserved).toBe(300);
378
+ expect(result.framesObserved).toBe(60);
378
379
  expect(result.errors).toEqual([]);
379
380
  expect(result.observation).toBe(true);
380
381
  expect(result.passNames).toContain('transmission-forward');
@@ -537,7 +538,7 @@ describe('Standard transmission Dawn ROI', () => {
537
538
  }, 120_000);
538
539
 
539
540
  if (ROSTER_SMOKE) {
540
- it('emits the canonical 300-frame Dawn roster receipt', async () => {
541
+ it('emits the canonical 60-frame Dawn roster receipt', async () => {
541
542
  const result = await renderCase(
542
543
  {
543
544
  name: 'roster-stability',
@@ -551,7 +552,7 @@ describe('Standard transmission Dawn ROI', () => {
551
552
  },
552
553
  ROSTER_SMOKE_FRAMES,
553
554
  );
554
- expect(result.framesObserved).toBeGreaterThanOrEqual(300);
555
+ expect(result.framesObserved).toBeGreaterThanOrEqual(60);
555
556
  expect(result.observation).toBe(true);
556
557
  expect(result.errors).toEqual([]);
557
558
  process.stdout.write(`[smoke] frames observed=${result.framesObserved}\n`);