@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
@@ -31,6 +31,11 @@ describe('TAA history lifecycle', () => {
31
31
  'r8unorm',
32
32
  ]);
33
33
  expect(state).toHaveProperty('stability');
34
+ expect(state).toHaveProperty('cloudRadiance');
35
+ expect(state).toHaveProperty('cloudTransmittance');
36
+ expect(state).toHaveProperty('cloudDepth');
37
+ expect(state.cloudHistoryEnabled).toBe(false);
38
+ expect(state.cloudRadiance).toBeUndefined();
34
39
  const view = createTemporalView({ antialias: 'taa', width: 8, height: 8 });
35
40
  const inspection = inspectTemporal(view, undefined, { active: state });
36
41
  expect(inspection.historyBytes).toBe(34 * 8 * 8);
@@ -39,6 +44,33 @@ describe('TAA history lifecycle', () => {
39
44
  retireTemporalGpuState(state);
40
45
  }
41
46
  });
47
+ it('allocates the cloud MRT only for a cloud-enabled candidate', async () => {
48
+ const device = (await new RhiNullAdapter().requestDevice()).unwrap();
49
+ const textures = vi.spyOn(device, 'createTexture');
50
+ const state = getTemporalGpuState(
51
+ {} as RenderFrameState,
52
+ device,
53
+ DeviceScope.create(23, 'cloud-history-test'),
54
+ 8,
55
+ 8,
56
+ true,
57
+ );
58
+ try {
59
+ expect(state.cloudHistoryEnabled).toBe(true);
60
+ expect(state.cloudRadiance).toBeDefined();
61
+ expect(textures).toHaveBeenCalledTimes(12);
62
+ expect(textures.mock.calls.slice(6).map(([desc]) => desc.label)).toEqual([
63
+ 'cloud-history-radiance-a',
64
+ 'cloud-history-radiance-b',
65
+ 'cloud-history-transmittance-a',
66
+ 'cloud-history-transmittance-b',
67
+ 'cloud-history-depth-a',
68
+ 'cloud-history-depth-b',
69
+ ]);
70
+ } finally {
71
+ retireTemporalGpuState(state);
72
+ }
73
+ });
42
74
  it('filters subpixel color reconstruction without filtering temporal metadata', async () => {
43
75
  const device = (await new RhiNullAdapter().requestDevice()).unwrap();
44
76
  const samplers = vi.spyOn(device, 'createSampler');
@@ -210,7 +210,7 @@ describe('temporal motion blur performance evidence', () => {
210
210
  const evidence = createCorrectnessEvidence(
211
211
  {
212
212
  backend: 'webgpu',
213
- framesObserved: 300,
213
+ framesObserved: 60,
214
214
  temporal: { status: 'stable', historyValid: true, historyAttempt: 'committed', epoch: 300 },
215
215
  temporalTarget: {
216
216
  identity: 'standard-scene-temporal',
@@ -441,7 +441,7 @@ describe('temporal motion blur performance evidence', () => {
441
441
  const assembled = assemblePerformanceAdmissionEvidence(
442
442
  {
443
443
  backend: 'webgpu',
444
- framesObserved: 300,
444
+ framesObserved: 60,
445
445
  errors: [],
446
446
  drawErrors: [],
447
447
  resolution: { width: 1920, height: 1080 },
@@ -543,7 +543,7 @@ describe('temporal motion blur performance evidence', () => {
543
543
  assemblePerformanceAdmissionEvidence(
544
544
  {
545
545
  backend: 'webgpu',
546
- framesObserved: 300,
546
+ framesObserved: 60,
547
547
  errors: [],
548
548
  drawErrors: [],
549
549
  resolution: { width: 1920, height: 1080 },
@@ -37,6 +37,16 @@ describe('TemporalView reset matrix', () => {
37
37
  expect(resolveTemporalReset(previous, next)).toBeUndefined();
38
38
  });
39
39
 
40
+ it('applies camera reset reasons to motion-blur-only off views', () => {
41
+ const previous = createTemporalView({ ...base().input, antialias: 'none' });
42
+ const next = createTemporalView({
43
+ ...previous.input,
44
+ antialias: 'none',
45
+ viewIdentity: 'secondary',
46
+ });
47
+ expect(resolveTemporalReset(previous, next)).toBe('view-switch');
48
+ });
49
+
40
50
  it('treats an explicit off commit as a first-frame boundary when TAA resumes', () => {
41
51
  const previous = base();
42
52
  const off = createTemporalView({
@@ -130,7 +130,8 @@ describe('TemporalView v1', () => {
130
130
  bloom: 'disabled',
131
131
  bloomThreshold: 1,
132
132
  bloomIntensity: 0,
133
- bloomBlurRadius: 0,
133
+ bloomSoftKnee: 0.5,
134
+ bloomScatter: 0.7,
134
135
  clearColor: [0, 0, 0, 1] as const,
135
136
  } as unknown as CameraSnapshot;
136
137
  const light = {
@@ -32,6 +32,10 @@ import { standardTemporalPostOrder } from '../temporal/standard-scene-data';
32
32
 
33
33
  let device: RhiDevice;
34
34
 
35
+ type TopologyOverrides = Omit<Partial<RenderPipelineTopology>, 'camera'> & {
36
+ readonly camera?: Partial<RenderPipelineTopology['camera']>;
37
+ };
38
+
35
39
  beforeAll(async () => {
36
40
  const adapter = await rhi.requestAdapter();
37
41
  if (!adapter.ok) throw adapter.error;
@@ -42,8 +46,9 @@ beforeAll(async () => {
42
46
 
43
47
  function topology(
44
48
  pipelineId: 'forgeax::standard',
45
- overrides: Partial<RenderPipelineTopology> = {},
49
+ overrides: TopologyOverrides = {},
46
50
  ): RenderPipelineTopology {
51
+ const { camera: cameraOverride, ...rest } = overrides;
47
52
  return {
48
53
  pipelineId,
49
54
  config: undefined,
@@ -53,7 +58,13 @@ function topology(
53
58
  storageFormat: 'bgra8unorm',
54
59
  viewFormat: 'bgra8unorm-srgb',
55
60
  },
56
- camera: { tonemap: 'aces-filmic', antialias: 'fxaa', bloom: 'on' },
61
+ camera: {
62
+ tonemap: 'aces-filmic',
63
+ antialias: 'fxaa',
64
+ bloom: 'on',
65
+ bloomIntensity: 1,
66
+ ...cameraOverride,
67
+ },
57
68
  shadow: {
58
69
  directional: { mapSize: 1024, cascadeCount: 4 },
59
70
  spotMapSize: 1024,
@@ -69,7 +80,7 @@ function topology(
69
80
  },
70
81
  featureTopologySignature: 'none',
71
82
  gpuDrivenTopologySignature: '',
72
- ...overrides,
83
+ ...rest,
73
84
  };
74
85
  }
75
86
 
@@ -877,7 +888,12 @@ describe('typed built-in pipeline topology', () => {
877
888
 
878
889
  it('keeps zero-shutter Motion Blur free of temporal target and passes', () => {
879
890
  expect(
880
- isMotionBlurTemporalDemand({ shutterAngle: 0, maxRadiusPixels: 32, sampleCount: 8 }),
891
+ isMotionBlurTemporalDemand({
892
+ shutterAngle: 0,
893
+ maxRadiusPixels: 32,
894
+ sampleCount: 8,
895
+ targetFps: 60,
896
+ }),
881
897
  ).toBe(false);
882
898
  const compiled = compile(
883
899
  standardPipeline,
@@ -6,23 +6,17 @@ import { Instances } from '../components/instances';
6
6
  import { MeshFilter } from '../components/mesh-filter';
7
7
  import { MeshRenderer } from '../components/mesh-renderer';
8
8
  import { Visibility, VisibilityStateValue } from '../components/visibility';
9
- import { InstanceCollectionStore } from '../instances';
10
9
  import { extractFrames } from '../render-system-extract';
11
10
 
12
11
  const IDENTITY = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
13
12
 
14
- function spawnInstance(
15
- world: World,
16
- instanceCollections: InstanceCollectionStore,
17
- state: keyof typeof VisibilityStateValue,
18
- ) {
19
- const collectionId = instanceCollections.create({ transforms: IDENTITY }).unwrap().collectionId;
13
+ function spawnInstance(world: World, state: keyof typeof VisibilityStateValue) {
20
14
  return world
21
15
  .spawn(
22
16
  { component: Transform, data: {} },
23
17
  { component: MeshFilter, data: { assetHandle: HANDLE_CUBE } },
24
18
  { component: MeshRenderer, data: {} },
25
- { component: Instances, data: { collectionId } },
19
+ { component: Instances, data: { transforms: new Float32Array(IDENTITY) } },
26
20
  { component: Visibility, data: { state: VisibilityStateValue[state] } },
27
21
  )
28
22
  .unwrap();
@@ -31,12 +25,9 @@ function spawnInstance(
31
25
  describe('visibility instance producer gate', () => {
32
26
  it('gates an explicit Instances owner before producing a renderable', () => {
33
27
  const world = new World();
34
- const instanceCollections = new InstanceCollectionStore();
35
- const entity = spawnInstance(world, instanceCollections, 'hidden');
28
+ const entity = spawnInstance(world, 'hidden');
36
29
 
37
- const frame = extractFrames([world], 0, undefined, undefined, undefined, {
38
- instanceCollections,
39
- });
30
+ const frame = extractFrames([world], 0);
40
31
 
41
32
  expect(frame.renderables.some((item) => item.entityKey === entity)).toBe(false);
42
33
  expect(frame.dispatch.some((item) => item.entityIndex === 0)).toBe(false);
@@ -44,13 +35,10 @@ describe('visibility instance producer gate', () => {
44
35
 
45
36
  it('keeps visible members of a mixed batch independent from hidden members', () => {
46
37
  const world = new World();
47
- const instanceCollections = new InstanceCollectionStore();
48
- spawnInstance(world, instanceCollections, 'hidden');
49
- const visible = spawnInstance(world, instanceCollections, 'visible');
38
+ spawnInstance(world, 'hidden');
39
+ const visible = spawnInstance(world, 'visible');
50
40
 
51
- const frame = extractFrames([world], 0, undefined, undefined, undefined, {
52
- instanceCollections,
53
- });
41
+ const frame = extractFrames([world], 0);
54
42
 
55
43
  expect(frame.renderables.map((item) => item.entityKey)).toEqual([visible]);
56
44
  expect(frame.dispatch.length).toBe(2);
@@ -11,7 +11,6 @@ import { SpriteInstances } from '../components/sprite-instances';
11
11
  import { TileLayer } from '../components/tile-layer';
12
12
  import { Tilemap } from '../components/tilemap';
13
13
  import { Visibility, VisibilityStateValue } from '../components/visibility';
14
- import { InstanceCollectionStore } from '../instances';
15
14
  import { extractFrames } from '../render-system-extract';
16
15
  import { classifySceneDataCoverage } from '../temporal/coverage';
17
16
  import { tilemapChunkExtractSystem } from '../tilemap-chunk-extract-system';
@@ -20,7 +19,7 @@ const IDENTITY = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
20
19
 
21
20
  type ProducerCase = {
22
21
  readonly name: string;
23
- readonly setup: (world: World, instanceCollections: InstanceCollectionStore) => EntityHandle;
22
+ readonly setup: (world: World) => EntityHandle;
24
23
  };
25
24
 
26
25
  function staticMesh(world: World): EntityHandle {
@@ -61,33 +60,25 @@ function spriteMesh(world: World): EntityHandle {
61
60
  .unwrap();
62
61
  }
63
62
 
64
- function autoFoldInstances(
65
- world: World,
66
- instanceCollections: InstanceCollectionStore,
67
- ): EntityHandle {
68
- const collectionId = instanceCollections.create({ transforms: IDENTITY }).unwrap().collectionId;
63
+ function autoFoldInstances(world: World): EntityHandle {
69
64
  return world
70
65
  .spawn(
71
66
  { component: Transform, data: {} },
72
67
  { component: MeshFilter, data: { assetHandle: HANDLE_CUBE } },
73
68
  { component: MeshRenderer, data: {} },
74
- { component: Instances, data: { collectionId } },
69
+ { component: Instances, data: { transforms: IDENTITY } },
75
70
  { component: Visibility, data: { state: VisibilityStateValue.hidden } },
76
71
  )
77
72
  .unwrap();
78
73
  }
79
74
 
80
- function explicitInstances(
81
- world: World,
82
- instanceCollections: InstanceCollectionStore,
83
- ): EntityHandle {
84
- const collectionId = instanceCollections.create({ transforms: IDENTITY }).unwrap().collectionId;
75
+ function explicitInstances(world: World): EntityHandle {
85
76
  return world
86
77
  .spawn(
87
78
  { component: Transform, data: {} },
88
79
  { component: MeshFilter, data: { assetHandle: HANDLE_CUBE } },
89
80
  { component: MeshRenderer, data: {} },
90
- { component: Instances, data: { collectionId } },
81
+ { component: Instances, data: { transforms: IDENTITY } },
91
82
  { component: Visibility, data: { state: VisibilityStateValue.hidden } },
92
83
  )
93
84
  .unwrap();
@@ -141,20 +132,18 @@ describe('visibility built-in producer matrix', () => {
141
132
 
142
133
  it.each(PRODUCERS)('$name is hidden, visible, then restorable', ({ setup }) => {
143
134
  const world = new World();
144
- const instanceCollections = new InstanceCollectionStore();
145
- const entity = setup(world, instanceCollections);
135
+ const entity = setup(world);
146
136
 
147
- const options = { instanceCollections };
148
- const hidden = extractFrames([world], 0, undefined, undefined, undefined, options);
137
+ const hidden = extractFrames([world], 0);
149
138
  expect(hidden.renderables).toHaveLength(0);
150
139
  expect(hidden.dispatch).toHaveLength(0);
151
140
 
152
141
  world.set(entity, Visibility, { state: VisibilityStateValue.visible }).unwrap();
153
- const visible = extractFrames([world], 0, undefined, undefined, undefined, options);
142
+ const visible = extractFrames([world], 0);
154
143
  expect(visible.visibilitySnapshots[0]?.get(entity)?.effective).toBe('visible');
155
144
 
156
145
  world.set(entity, Visibility, { state: VisibilityStateValue.hidden }).unwrap();
157
- const restored = extractFrames([world], 0, undefined, undefined, undefined, options);
146
+ const restored = extractFrames([world], 0);
158
147
  expect(restored.renderables).toHaveLength(0);
159
148
  expect(restored.dispatch).toHaveLength(0);
160
149
  });
@@ -35,14 +35,18 @@ describe('volumetric optics CPU oracle', () => {
35
35
  it('does not apply density twice to single-scatter radiance', () => {
36
36
  expect(integrateShader).not.toContain('phase * sample.g * sample.r * (1.0 - transmittance)');
37
37
  expect(integrateShader).toContain(
38
- 'let local_scatter = radiance * volume_params.albedo.xyz * phase *',
38
+ 'source += radiance * volume_params.albedo.xyz * phase * VOLUME_LIGHT_PHASE_SCALE * sigma;',
39
+ );
40
+ expect(integrateShader).toContain(
41
+ 'let local_scatter = source * (1.0 - local_transmittance) / max(extinction, EPSILON) +',
39
42
  );
40
- expect(integrateShader).toContain('VOLUME_LIGHT_PHASE_SCALE * (1.0 - local_transmittance) +');
41
43
  });
42
44
 
43
45
  it('converts the shared integrated-light basis once before normalized HG', () => {
44
46
  expect(integrateShader).toContain('const VOLUME_LIGHT_PHASE_SCALE : f32 = FOUR_PI;');
45
- expect(integrateShader).toContain('phase *\n VOLUME_LIGHT_PHASE_SCALE *');
47
+ expect(integrateShader.match(/phase\s*\*\s*VOLUME_LIGHT_PHASE_SCALE\s*\*/g) ?? []).toHaveLength(
48
+ 1,
49
+ );
46
50
  });
47
51
 
48
52
  it('keeps empty density at identity transmittance', () => {
@@ -1,10 +1,11 @@
1
1
  import { execFileSync } from 'node:child_process';
2
2
  import { mkdir, writeFile } from 'node:fs/promises';
3
3
  import { dirname } from 'node:path';
4
- import { World } from '@forgeax/engine-ecs';
4
+ import { Time, World } from '@forgeax/engine-ecs';
5
5
  import { RenderGraphBuilder } from '@forgeax/engine-render-graph';
6
6
  import type {
7
7
  BindGroupLayoutDescriptor,
8
+ Buffer,
8
9
  RenderPipelineDescriptor,
9
10
  RhiAdapter,
10
11
  RhiDevice,
@@ -33,6 +34,7 @@ import {
33
34
  deriveVolumetricFogExtent,
34
35
  deriveVolumetricFogResolvedExtent,
35
36
  inspectVolumetricFogResources,
37
+ VOLUMETRIC_FOG_PARAMS_BYTES,
36
38
  } from '../volume/resources';
37
39
  import { resolveVolumeTemporalReset } from '../volume/temporal';
38
40
 
@@ -62,15 +64,12 @@ class RecoverableNullDevice extends RhiNullDevice {
62
64
  private readonly resolveLost: (info: LostInfo) => void;
63
65
  private readonly lostPromise: Promise<LostInfo>;
64
66
 
65
- constructor() {
67
+ constructor(queue: RhiNullQueue = new RhiNullQueue()) {
66
68
  let resolveLost!: (info: LostInfo) => void;
67
69
  const lostPromise = new Promise<LostInfo>((resolve) => {
68
70
  resolveLost = resolve;
69
71
  });
70
- super(
71
- new RhiNullQueue(),
72
- (bookkeeper, device) => new RhiNullCommandEncoder(bookkeeper, device),
73
- );
72
+ super(queue, (bookkeeper, device) => new RhiNullCommandEncoder(bookkeeper, device));
74
73
  this.resolveLost = resolveLost;
75
74
  this.lostPromise = lostPromise;
76
75
  }
@@ -104,13 +103,44 @@ class RecoverableNullAdapter implements RhiAdapter {
104
103
  readonly limits: Readonly<Record<string, number>> = {};
105
104
  readonly devices: RecoverableNullDevice[] = [];
106
105
 
106
+ constructor(private readonly queueFactory: () => RhiNullQueue = () => new RhiNullQueue()) {}
107
+
107
108
  requestDevice(): Promise<Result<RhiDevice, RhiError>> {
108
- const device = new RecoverableNullDevice();
109
+ const device = new RecoverableNullDevice(this.queueFactory());
109
110
  this.devices.push(device);
110
111
  return Promise.resolve(ok(device));
111
112
  }
112
113
  }
113
114
 
115
+ interface CapturedQueueWrite {
116
+ readonly buffer: Buffer;
117
+ readonly offset: number;
118
+ readonly bytes: Uint8Array;
119
+ }
120
+
121
+ /** Captures the real record-stage volume parameter writes without adding a
122
+ * second render implementation. */
123
+ class RecordingNullQueue extends RhiNullQueue {
124
+ readonly writes: CapturedQueueWrite[] = [];
125
+
126
+ override writeBuffer(
127
+ buffer: Buffer,
128
+ bufferOffset: number,
129
+ data: ArrayBufferView | ArrayBuffer,
130
+ dataOffset?: number,
131
+ size?: number,
132
+ ) {
133
+ const source =
134
+ data instanceof ArrayBuffer
135
+ ? new Uint8Array(data)
136
+ : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
137
+ const start = dataOffset ?? 0;
138
+ const end = size === undefined ? source.byteLength : start + size;
139
+ this.writes.push({ buffer, offset: bufferOffset, bytes: source.slice(start, end) });
140
+ return super.writeBuffer(buffer, bufferOffset, data, dataOffset, size);
141
+ }
142
+ }
143
+
114
144
  function surfaceCanvas(width = 1920, height = 1080): HTMLCanvasElement {
115
145
  return { width, height, getContext: () => null } as unknown as HTMLCanvasElement;
116
146
  }
@@ -336,11 +366,11 @@ describe('volumetric fog budget and recovery contract', () => {
336
366
  label: 'volume-params',
337
367
  kind: 'buffer',
338
368
  origin: 'imported',
339
- descriptor: { kind: 'buffer', size: 128 },
369
+ descriptor: { kind: 'buffer', size: VOLUMETRIC_FOG_PARAMS_BYTES },
340
370
  firstUse: 0,
341
371
  lastUse: 3,
342
372
  derivedUsage: 64,
343
- byteSize: 128,
373
+ byteSize: VOLUMETRIC_FOG_PARAMS_BYTES,
344
374
  },
345
375
  ],
346
376
  } as const;
@@ -349,21 +379,25 @@ describe('volumetric fog budget and recovery contract', () => {
349
379
  expect(facts.sampleCount).toBe(960 * 540 * 64);
350
380
  expect(facts.resolvedPixelCount).toBe(960 * 540);
351
381
  expect(facts.currentBytes).toBe(960 * 540 * 8);
352
- expect(facts.bufferBytes).toBe(128);
382
+ expect(facts.bufferBytes).toBe(VOLUMETRIC_FOG_PARAMS_BYTES);
353
383
  expect(facts.liveResourceCount).toBe(5);
354
384
  expect(facts.historyBytes).toBe(960 * 540 * 8 * 2);
355
385
  expect(facts.physicalResourceCount).toBe(5);
356
- expect(facts.totalBytes).toBe(960 * 540 * 16 * 4 + 960 * 540 * 8 * 3 + 128);
386
+ expect(facts.totalBytes).toBe(
387
+ 960 * 540 * 16 * 4 + 960 * 540 * 8 * 3 + VOLUMETRIC_FOG_PARAMS_BYTES,
388
+ );
357
389
  expect(facts.totalBytes).toBe(
358
390
  facts.currentBytes + facts.historyBytes + facts.scratchBytes + facts.bufferBytes,
359
391
  );
360
392
  expect(facts.totalBytes).toBeLessThanOrEqual(MAX_MEMORY_BYTES);
361
393
 
362
394
  const doubleBuffered = inspectVolumetricFogResources(graph, { parameterBufferCount: 2 });
363
- expect(doubleBuffered.bufferBytes).toBe(256);
395
+ expect(doubleBuffered.bufferBytes).toBe(VOLUMETRIC_FOG_PARAMS_BYTES * 2);
364
396
  expect(doubleBuffered.liveResourceCount).toBe(6);
365
397
  expect(doubleBuffered.physicalResourceCount).toBe(6);
366
- expect(doubleBuffered.totalBytes).toBe(960 * 540 * 16 * 4 + 960 * 540 * 8 * 3 + 256);
398
+ expect(doubleBuffered.totalBytes).toBe(
399
+ 960 * 540 * 16 * 4 + 960 * 540 * 8 * 3 + VOLUMETRIC_FOG_PARAMS_BYTES * 2,
400
+ );
367
401
  expect(doubleBuffered.totalBytes).toBe(
368
402
  doubleBuffered.currentBytes +
369
403
  doubleBuffered.historyBytes +
@@ -372,6 +406,146 @@ describe('volumetric fog budget and recovery contract', () => {
372
406
  );
373
407
  });
374
408
 
409
+ it.each([
410
+ 1, 2,
411
+ ])('uses the canonical World clock for %i owners and bounds real history reuse', async (ownerCount) => {
412
+ const queue = new RecordingNullQueue();
413
+ const adapter = new RecoverableNullAdapter(() => queue);
414
+ const backend = {
415
+ ...nullRhi,
416
+ requestAdapter: async () => ok(adapter),
417
+ };
418
+ const created = await constructRendererHost(
419
+ surfaceCanvas(640, 360),
420
+ { rhi: backend },
421
+ { shaderManifestUrl: VOLUME_MANIFEST },
422
+ );
423
+ expect(created.ok).toBe(true);
424
+ if (!created.ok) return;
425
+ const { renderer } = created.value;
426
+ const world = new World();
427
+ const density = world.allocSharedRef('TextureAsset', densityAsset());
428
+ world
429
+ .spawn(
430
+ { component: Transform, data: { pos: [0, 0, 3] } },
431
+ {
432
+ component: Camera,
433
+ data: {
434
+ ...perspective({ fov: Math.PI / 4, aspect: 16 / 9 }),
435
+ tonemap: TONEMAP_ACES_FILMIC,
436
+ },
437
+ },
438
+ )
439
+ .unwrap();
440
+ const sun = world
441
+ .spawn({
442
+ component: DirectionalLight,
443
+ data: { direction: [-0.4, -0.8, -0.3], castShadow: true },
444
+ })
445
+ .unwrap();
446
+ for (let owner = 0; owner < ownerCount; owner++)
447
+ world
448
+ .spawn({
449
+ component: VolumetricFog,
450
+ data: {
451
+ light: sun,
452
+ density,
453
+ boundsMin: [-1, -1, -1],
454
+ boundsMax: [1, 1, 1],
455
+ extinction: [0.2, 0.2, 0.2],
456
+ albedo: [0.8, 0.8, 0.8],
457
+ emission: [0, 0, 0],
458
+ anisotropy: 0,
459
+ maxDistance: 50,
460
+ },
461
+ })
462
+ .unwrap();
463
+
464
+ const lease = renderer.attach(world);
465
+ expect(lease.ok).toBe(true);
466
+ if (!lease.ok) return;
467
+ const request = {
468
+ leases: [lease.value],
469
+ camera: { lease: lease.value },
470
+ environment: { lease: lease.value },
471
+ } as const;
472
+ const drawAndRead = async (): Promise<{
473
+ readonly history: number;
474
+ readonly worldTime: number;
475
+ readonly delta: number;
476
+ }> => {
477
+ queue.writes.length = 0;
478
+ const frame = renderer.draw(request);
479
+ expect(frame.ok).toBe(true);
480
+ if (!frame.ok) return { history: -1, worldTime: -1, delta: -1 };
481
+ expect((await frame.value.completed).ok).toBe(true);
482
+ const paramsWrite = queue.writes
483
+ .filter(
484
+ (write) => write.offset === 0 && write.bytes.byteLength === VOLUMETRIC_FOG_PARAMS_BYTES,
485
+ )
486
+ .at(-1);
487
+ expect(paramsWrite).toBeDefined();
488
+ if (paramsWrite === undefined) return { history: -1, worldTime: -1, delta: -1 };
489
+ const params = new Float32Array(paramsWrite.bytes.buffer, paramsWrite.bytes.byteOffset);
490
+ expect(params[3]).toBe(ownerCount);
491
+ for (let owner = 0; owner < ownerCount; owner++) {
492
+ expect(params[(owner + 1) * 36 + 32]).toBeCloseTo(world.getResource(Time).elapsed, 5);
493
+ }
494
+ const historyWrite = queue.writes
495
+ .filter(
496
+ (write) =>
497
+ write.buffer === paramsWrite.buffer &&
498
+ write.offset === 31 * Float32Array.BYTES_PER_ELEMENT &&
499
+ write.bytes.byteLength === 3 * Float32Array.BYTES_PER_ELEMENT,
500
+ )
501
+ .at(-1);
502
+ expect(historyWrite).toBeDefined();
503
+ if (historyWrite === undefined) return { history: -1, worldTime: -1, delta: -1 };
504
+ return {
505
+ history: new Float32Array(
506
+ historyWrite.bytes.buffer,
507
+ historyWrite.bytes.byteOffset,
508
+ 1,
509
+ )[0] as number,
510
+ worldTime: new Float32Array(
511
+ historyWrite.bytes.buffer,
512
+ historyWrite.bytes.byteOffset,
513
+ 3,
514
+ )[1] as number,
515
+ delta: new Float32Array(
516
+ historyWrite.bytes.buffer,
517
+ historyWrite.bytes.byteOffset,
518
+ 3,
519
+ )[2] as number,
520
+ };
521
+ };
522
+
523
+ world.update(1 / 60).unwrap();
524
+ const firstTime = world.getResource(Time).elapsed;
525
+ const first = await drawAndRead();
526
+ expect(first.worldTime).toBeCloseTo(firstTime, 5);
527
+ expect(first.history).toBe(0);
528
+
529
+ const stable = await drawAndRead();
530
+ expect(world.getResource(Time).elapsed).toBe(firstTime);
531
+ expect(stable.worldTime).toBeCloseTo(firstTime, 5);
532
+ expect(stable.history).toBe(1);
533
+
534
+ world.update(1 / 60).unwrap();
535
+ const movedTime = world.getResource(Time).elapsed;
536
+ const moved = await drawAndRead();
537
+ expect(moved.worldTime).toBeCloseTo(movedTime, 5);
538
+ expect(moved.worldTime).toBeGreaterThan(first.worldTime);
539
+ expect(moved.history).toBe(1);
540
+ expect(moved.delta).toBeCloseTo(1 / 60, 6);
541
+ expect(stable.delta).toBe(0);
542
+ for (let index = 0; index < 12; index += 1) world.update(1 / 60).unwrap();
543
+ const jumped = await drawAndRead();
544
+ expect(jumped.history).toBe(0);
545
+ expect(jumped.delta).toBeGreaterThan(0.1);
546
+ await renderer.dispose();
547
+ }, 30_000);
548
+
375
549
  it('derives a same-XY resolve descriptor when the packed raw extent is supplied alone', async () => {
376
550
  const adapter = await nullRhi.requestAdapter();
377
551
  expect(adapter.ok).toBe(true);
@@ -395,7 +569,7 @@ describe('volumetric fog budget and recovery contract', () => {
395
569
  expect(resolved?.extent).toEqual({ width: 960, height: 540, depthOrArrayLayers: 1 });
396
570
  });
397
571
 
398
- it('keeps one real Renderer volume generation through 20 resizes, 3 recoveries, and 300 frames', async () => {
572
+ it('keeps one real Renderer volume generation through 20 resizes, 3 recoveries, and 60 frames', async () => {
399
573
  const adapter = new RecoverableNullAdapter();
400
574
  const backend = {
401
575
  ...nullRhi,
@@ -602,7 +776,7 @@ describe('volumetric fog budget and recovery contract', () => {
602
776
  readonly totalBytes: number;
603
777
  readonly physicalResourceCount: number;
604
778
  }> = [];
605
- for (let frame = 0; frame < 300; frame += 1) {
779
+ for (let frame = 0; frame < 60; frame += 1) {
606
780
  const inspection = await draw();
607
781
  const facts = inspection?.volumetricFog?.resourceFacts;
608
782
  expect(facts).toBeDefined();
@@ -1,6 +1,7 @@
1
1
  import type { EntityHandle } from '@forgeax/engine-ecs';
2
2
  import { describe, expect, it } from 'vitest';
3
3
  import { type VolumetricFogAuthoring, validateVolumetricFog } from '../volume/component';
4
+ import { extractVolumetricFog } from '../volume/extract';
4
5
 
5
6
  const base: VolumetricFogAuthoring = {
6
7
  light: 1 as EntityHandle,
@@ -32,12 +33,12 @@ describe('VolumetricFog structured error contract', () => {
32
33
  }
33
34
  });
34
35
 
35
- it('keeps owner conflict as a closed error instead of creating a second volume', () => {
36
- const result = validateVolumetricFog({ ...base, ownerCount: 2 });
36
+ it('keeps volume budget overflow as a closed error', () => {
37
+ const result = extractVolumetricFog(Array.from({ length: 9 }, () => base));
37
38
  expect(result.ok).toBe(false);
38
39
  if (!result.ok) {
39
40
  expect(result.error.code).toBe('volume-owner-conflict');
40
- expect(result.error.detail).toMatchObject({ ownerCount: 2 });
41
+ expect(result.error.detail).toMatchObject({ ownerCount: 9 });
41
42
  }
42
43
  });
43
44