@forgeax/engine-render 0.1.33 → 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 (518) hide show
  1. package/README.md +1010 -124
  2. package/dist/assembly/backend-contract.d.ts +2 -0
  3. package/dist/assembly/backend-contract.d.ts.map +1 -1
  4. package/dist/assembly/barrel-distortion-inspection.d.ts +3 -0
  5. package/dist/assembly/barrel-distortion-inspection.d.ts.map +1 -0
  6. package/dist/assembly/canvas-draw-guard.d.ts +3 -0
  7. package/dist/assembly/canvas-draw-guard.d.ts.map +1 -0
  8. package/dist/assembly/cloud-shadow-entry.d.ts +4 -0
  9. package/dist/assembly/cloud-shadow-entry.d.ts.map +1 -0
  10. package/dist/assembly/dynamic-geometry-host.d.ts +4 -1
  11. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
  12. package/dist/assembly/dynamic-geometry-runtime.d.ts +1 -1
  13. package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -1
  14. package/dist/assembly/factory.d.ts +1 -1
  15. package/dist/assembly/factory.d.ts.map +1 -1
  16. package/dist/assembly/host-contract.d.ts +3 -3
  17. package/dist/assembly/host-contract.d.ts.map +1 -1
  18. package/dist/assembly/material/assembly.d.ts.map +1 -1
  19. package/dist/assembly/material/pipeline-helpers.d.ts +5 -0
  20. package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -1
  21. package/dist/assembly/material-shader-policy.d.ts +16 -2
  22. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  23. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -1
  24. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
  25. package/dist/assembly/recovery/renderer-recover.d.ts +2 -0
  26. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  27. package/dist/assembly/render-feature-post-processes.d.ts +6 -0
  28. package/dist/assembly/render-feature-post-processes.d.ts.map +1 -0
  29. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  30. package/dist/assembly/renderer-feature-inspection.d.ts +1 -1
  31. package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -1
  32. package/dist/assembly/renderer-frame-transaction.d.ts +12 -0
  33. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  34. package/dist/assembly/renderer-inspection.d.ts +11 -0
  35. package/dist/assembly/renderer-inspection.d.ts.map +1 -1
  36. package/dist/assembly/webgpu-pbr-ready.d.ts +1 -0
  37. package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -1
  38. package/dist/assembly/webgpu-ready-per-pass.d.ts +39 -0
  39. package/dist/assembly/webgpu-ready-per-pass.d.ts.map +1 -0
  40. package/dist/assembly/webgpu-ready.d.ts +1 -1
  41. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  42. package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -1
  43. package/dist/assembly/webgpu-renderer-observation-owner.d.ts +35 -0
  44. package/dist/assembly/webgpu-renderer-observation-owner.d.ts.map +1 -0
  45. package/dist/assembly/webgpu-renderer.d.ts +1 -1
  46. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  47. package/dist/authoring.mjs +1 -1
  48. package/dist/barrel-distortion.d.ts +47 -0
  49. package/dist/barrel-distortion.d.ts.map +1 -0
  50. package/dist/{chunk-PA5DRJNI.mjs → chunk-ELJFJ2BU.mjs} +2 -2
  51. package/dist/chunk-ELJFJ2BU.mjs.map +1 -0
  52. package/dist/{chunk-ZDQWGQW7.mjs → chunk-HEB564WF.mjs} +4 -4
  53. package/dist/{chunk-ZDQWGQW7.mjs.map → chunk-HEB564WF.mjs.map} +1 -1
  54. package/dist/{chunk-VOZWDM3L.mjs → chunk-MKDIT2EM.mjs} +1307 -188
  55. package/dist/chunk-MKDIT2EM.mjs.map +1 -0
  56. package/dist/{chunk-LEAXOMYJ.mjs → chunk-OCDLEI6I.mjs} +3 -3
  57. package/dist/{chunk-LEAXOMYJ.mjs.map → chunk-OCDLEI6I.mjs.map} +1 -1
  58. package/dist/chunk-ODZWR6JQ.mjs +4927 -0
  59. package/dist/chunk-ODZWR6JQ.mjs.map +1 -0
  60. package/dist/{chunk-3RTCJTH4.mjs → chunk-SHFUFJ5N.mjs} +184 -13
  61. package/dist/chunk-SHFUFJ5N.mjs.map +1 -0
  62. package/dist/{chunk-OO3DRYK7.mjs → chunk-UI5SFODX.mjs} +176 -96
  63. package/dist/chunk-UI5SFODX.mjs.map +1 -0
  64. package/dist/{chunk-PLAXOOX5.mjs → chunk-UINHLCZA.mjs} +561 -79
  65. package/dist/chunk-UINHLCZA.mjs.map +1 -0
  66. package/dist/{chunk-NB2S7ZRX.mjs → chunk-WEX45HKH.mjs} +127 -386
  67. package/dist/chunk-WEX45HKH.mjs.map +1 -0
  68. package/dist/cloud/density.d.ts +55 -0
  69. package/dist/cloud/density.d.ts.map +1 -0
  70. package/dist/cloud/extract.d.ts +25 -0
  71. package/dist/cloud/extract.d.ts.map +1 -0
  72. package/dist/cloud/feature.d.ts +57 -0
  73. package/dist/cloud/feature.d.ts.map +1 -0
  74. package/dist/cloud/frame-context.d.ts +8 -0
  75. package/dist/cloud/frame-context.d.ts.map +1 -0
  76. package/dist/cloud/inspection.d.ts +59 -0
  77. package/dist/cloud/inspection.d.ts.map +1 -0
  78. package/dist/cloud/optics.d.ts +56 -0
  79. package/dist/cloud/optics.d.ts.map +1 -0
  80. package/dist/cloud/parameters.d.ts +73 -0
  81. package/dist/cloud/parameters.d.ts.map +1 -0
  82. package/dist/cloud/resources.d.ts +60 -0
  83. package/dist/cloud/resources.d.ts.map +1 -0
  84. package/dist/cloud/shadow.d.ts +42 -0
  85. package/dist/cloud/shadow.d.ts.map +1 -0
  86. package/dist/cloud/temporal.d.ts +74 -0
  87. package/dist/cloud/temporal.d.ts.map +1 -0
  88. package/dist/components/barrel-distortion.d.ts +13 -0
  89. package/dist/components/barrel-distortion.d.ts.map +1 -0
  90. package/dist/components/cloud-layer.d.ts +29 -0
  91. package/dist/components/cloud-layer.d.ts.map +1 -0
  92. package/dist/components/index.d.ts +2 -0
  93. package/dist/components/index.d.ts.map +1 -1
  94. package/dist/components/instances.d.ts +20 -6
  95. package/dist/components/instances.d.ts.map +1 -1
  96. package/dist/construct-renderer.d.ts +1 -1
  97. package/dist/construct-renderer.d.ts.map +1 -1
  98. package/dist/construct-renderer.mjs +6759 -2875
  99. package/dist/construct-renderer.mjs.map +1 -1
  100. package/dist/dynamic-geometry.d.ts +5 -1
  101. package/dist/dynamic-geometry.d.ts.map +1 -1
  102. package/dist/errors/cloud.d.ts +59 -0
  103. package/dist/errors/cloud.d.ts.map +1 -0
  104. package/dist/errors/render.d.ts +18 -4
  105. package/dist/errors/render.d.ts.map +1 -1
  106. package/dist/examples/single-layer-medium-surface.d.ts +47 -0
  107. package/dist/examples/single-layer-medium-surface.d.ts.map +1 -0
  108. package/dist/extract/camera.d.ts.map +1 -1
  109. package/dist/extract/contracts.d.ts +5 -1
  110. package/dist/extract/contracts.d.ts.map +1 -1
  111. package/dist/features/barrel-distortion.d.ts +29 -0
  112. package/dist/features/barrel-distortion.d.ts.map +1 -0
  113. package/dist/features/error-reporting.d.ts +6 -2
  114. package/dist/features/error-reporting.d.ts.map +1 -1
  115. package/dist/features/host.d.ts +10 -0
  116. package/dist/features/host.d.ts.map +1 -1
  117. package/dist/features/plan.d.ts +8 -0
  118. package/dist/features/plan.d.ts.map +1 -1
  119. package/dist/features/render-graph-contribution.d.ts +5 -0
  120. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  121. package/dist/features/render-graph-raster.d.ts +1 -0
  122. package/dist/features/render-graph-raster.d.ts.map +1 -1
  123. package/dist/features/targets.d.ts +2 -0
  124. package/dist/features/targets.d.ts.map +1 -1
  125. package/dist/features/types.d.ts +44 -0
  126. package/dist/features/types.d.ts.map +1 -1
  127. package/dist/fullscreen-post-process-pass.d.ts +15 -7
  128. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  129. package/dist/gpu-dirty-ranges.d.ts +11 -0
  130. package/dist/gpu-dirty-ranges.d.ts.map +1 -0
  131. package/dist/gpu-driven/batch-topology.d.ts +17 -0
  132. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  133. package/dist/gpu-driven/material-artifacts.d.ts +6 -0
  134. package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
  135. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  136. package/dist/gpu-driven/production-raster-material.d.ts +178 -0
  137. package/dist/gpu-driven/production-raster-material.d.ts.map +1 -0
  138. package/dist/gpu-driven/production-raster.d.ts +47 -3
  139. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  140. package/dist/gpu-driven/resource-allocation.d.ts +31 -0
  141. package/dist/gpu-driven/resource-allocation.d.ts.map +1 -0
  142. package/dist/gpu-driven/shadow-ownership.d.ts +14 -0
  143. package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -0
  144. package/dist/gpu-driven/view-gpu.d.ts +26 -2
  145. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  146. package/dist/gpu-scene.d.ts +5 -0
  147. package/dist/gpu-scene.d.ts.map +1 -1
  148. package/dist/hdrp-buffers.d.ts +2 -2
  149. package/dist/hdrp-buffers.d.ts.map +1 -1
  150. package/dist/ibl/skylight-bind-group.d.ts +10 -1
  151. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  152. package/dist/index.d.ts +27 -7
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.mjs +258 -8
  155. package/dist/index.mjs.map +1 -1
  156. package/dist/inspection-types.d.ts +135 -1
  157. package/dist/inspection-types.d.ts.map +1 -1
  158. package/dist/instance-buffer-cache.d.ts +4 -4
  159. package/dist/instance-buffer-cache.d.ts.map +1 -1
  160. package/dist/instances.d.ts +48 -100
  161. package/dist/instances.d.ts.map +1 -1
  162. package/dist/internal.mjs +7 -7
  163. package/dist/pbr-pipeline.d.ts +19 -1
  164. package/dist/pbr-pipeline.d.ts.map +1 -1
  165. package/dist/pipeline/single-layer-medium-passes.d.ts +73 -0
  166. package/dist/pipeline/single-layer-medium-passes.d.ts.map +1 -0
  167. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  168. package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
  169. package/dist/pipeline/standard-output/types.d.ts +4 -2
  170. package/dist/pipeline/standard-output/types.d.ts.map +1 -1
  171. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  172. package/dist/pipeline/standard-post.d.ts.map +1 -1
  173. package/dist/pipeline-builder.d.ts +1 -1
  174. package/dist/pipeline-builder.d.ts.map +1 -1
  175. package/dist/plugin.d.ts.map +1 -1
  176. package/dist/prepare/prepared-graphics-resolver.d.ts +2 -0
  177. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  178. package/dist/record/barrel-distortion-frame.d.ts +15 -0
  179. package/dist/record/barrel-distortion-frame.d.ts.map +1 -0
  180. package/dist/record/dynamic-geometry-consumption.d.ts +10 -2
  181. package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -1
  182. package/dist/record/fold-buckets.d.ts +95 -0
  183. package/dist/record/fold-buckets.d.ts.map +1 -0
  184. package/dist/record/fold-instance-buffer.d.ts +6 -0
  185. package/dist/record/fold-instance-buffer.d.ts.map +1 -0
  186. package/dist/record/frame-lighting.d.ts +1 -0
  187. package/dist/record/frame-lighting.d.ts.map +1 -1
  188. package/dist/record/frame-snapshot.d.ts +37 -3
  189. package/dist/record/frame-snapshot.d.ts.map +1 -1
  190. package/dist/record/frame.d.ts +10 -61
  191. package/dist/record/frame.d.ts.map +1 -1
  192. package/dist/record/gpu-draw-receipts.d.ts +11 -0
  193. package/dist/record/gpu-draw-receipts.d.ts.map +1 -0
  194. package/dist/record/gpu-timing.d.ts +8 -0
  195. package/dist/record/gpu-timing.d.ts.map +1 -1
  196. package/dist/record/main-pass-geometry.d.ts +21 -5
  197. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  198. package/dist/record/main-pass-material.d.ts +7 -1
  199. package/dist/record/main-pass-material.d.ts.map +1 -1
  200. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  201. package/dist/record/main-pass.d.ts +13 -2
  202. package/dist/record/main-pass.d.ts.map +1 -1
  203. package/dist/record/motion-blur-receipt.d.ts +11 -0
  204. package/dist/record/motion-blur-receipt.d.ts.map +1 -0
  205. package/dist/record/probe-blend-buffer.d.ts +16 -0
  206. package/dist/record/probe-blend-buffer.d.ts.map +1 -0
  207. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  208. package/dist/record/render-context.d.ts +22 -3
  209. package/dist/record/render-context.d.ts.map +1 -1
  210. package/dist/record/shadow-pass.d.ts +2 -0
  211. package/dist/record/shadow-pass.d.ts.map +1 -1
  212. package/dist/record/skybox-post-pass.d.ts +1 -1
  213. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  214. package/dist/record/typed-frame-graph.d.ts +22 -2
  215. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  216. package/dist/record/view-ubo.d.ts +11 -3
  217. package/dist/record/view-ubo.d.ts.map +1 -1
  218. package/dist/record/volume-params.d.ts +9 -0
  219. package/dist/record/volume-params.d.ts.map +1 -0
  220. package/dist/recovery/render-system-candidate.d.ts +10 -0
  221. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  222. package/dist/reflection/record-owner.d.ts.map +1 -1
  223. package/dist/render-contract.d.ts +42 -4
  224. package/dist/render-contract.d.ts.map +1 -1
  225. package/dist/render-graph-primitives.d.ts.map +1 -1
  226. package/dist/render-pipeline.d.ts +103 -2
  227. package/dist/render-pipeline.d.ts.map +1 -1
  228. package/dist/render-system-extract-tail.d.ts +3 -2
  229. package/dist/render-system-extract-tail.d.ts.map +1 -1
  230. package/dist/render-system-extract.d.ts +16 -9
  231. package/dist/render-system-extract.d.ts.map +1 -1
  232. package/dist/render-system-presentation.d.ts +23 -0
  233. package/dist/render-system-presentation.d.ts.map +1 -0
  234. package/dist/render-system-producer-roots.d.ts +10 -0
  235. package/dist/render-system-producer-roots.d.ts.map +1 -0
  236. package/dist/render-system-residency.d.ts +12 -0
  237. package/dist/render-system-residency.d.ts.map +1 -0
  238. package/dist/render-system.d.ts +19 -14
  239. package/dist/render-system.d.ts.map +1 -1
  240. package/dist/scene/probe-blend.d.ts +27 -0
  241. package/dist/scene/probe-blend.d.ts.map +1 -1
  242. package/dist/scene/render-scene.d.ts +12 -5
  243. package/dist/scene/render-scene.d.ts.map +1 -1
  244. package/dist/ssao-buffers.d.ts +9 -0
  245. package/dist/ssao-buffers.d.ts.map +1 -1
  246. package/dist/surface/dynamic-input.d.ts +133 -0
  247. package/dist/surface/dynamic-input.d.ts.map +1 -0
  248. package/dist/surface/gpu-driven.d.ts +41 -0
  249. package/dist/surface/gpu-driven.d.ts.map +1 -0
  250. package/dist/surface/submission-observation.d.ts +136 -0
  251. package/dist/surface/submission-observation.d.ts.map +1 -0
  252. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  253. package/dist/temporal/gpu.d.ts +13 -1
  254. package/dist/temporal/gpu.d.ts.map +1 -1
  255. package/dist/temporal/index.mjs +5 -5
  256. package/dist/temporal/inspection.d.ts.map +1 -1
  257. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  258. package/dist/temporal/target-coverage-attachment.d.ts.map +1 -1
  259. package/dist/transmission/single-layer-medium.d.ts +89 -0
  260. package/dist/transmission/single-layer-medium.d.ts.map +1 -0
  261. package/dist/typed-render-graph-primitives.d.ts +14 -2
  262. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  263. package/dist/volume/component.d.ts +9 -4
  264. package/dist/volume/component.d.ts.map +1 -1
  265. package/dist/volume/extract.d.ts +1 -1
  266. package/dist/volume/extract.d.ts.map +1 -1
  267. package/dist/volume/inspection.d.ts +2 -0
  268. package/dist/volume/inspection.d.ts.map +1 -1
  269. package/dist/volume/passes.d.ts +4 -1
  270. package/dist/volume/passes.d.ts.map +1 -1
  271. package/dist/volume/resources.d.ts.map +1 -1
  272. package/package.json +22 -21
  273. package/src/__tests__/atmosphere-daylight.dawn.test.ts +72 -0
  274. package/src/__tests__/barrel-distortion-lkg.integration.test.ts +353 -0
  275. package/src/__tests__/barrel-distortion.dawn.test.ts +41 -0
  276. package/src/__tests__/barrel-distortion.unit.test.ts +364 -0
  277. package/src/__tests__/construct-renderer-identity.unit.test.ts +49 -0
  278. package/src/__tests__/detached-recovery-graph.unit.test.ts +277 -0
  279. package/src/__tests__/device-loss-fanout.unit.test.ts +36 -1
  280. package/src/__tests__/docs-gate-green.unit.test.ts +112 -1
  281. package/src/__tests__/factory-contract.integration.test.ts +295 -45
  282. package/src/__tests__/feature-depth-input.dawn.test.ts +129 -2
  283. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +14 -3
  284. package/src/__tests__/gpu-dirty-ranges.unit.test.ts +22 -0
  285. package/src/__tests__/gpu-driven-baseline.characterization.test.ts +16 -3
  286. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +12 -0
  287. package/src/__tests__/gpu-driven-material-artifacts.unit.test.ts +143 -0
  288. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +2 -2
  289. package/src/__tests__/gpu-driven-production.integration.test.ts +482 -3
  290. package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +6 -5
  291. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +94 -6
  292. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +18 -2
  293. package/src/__tests__/gpu-driven-view.browser.test.ts +5 -8
  294. package/src/__tests__/gpu-driven-view.dawn.test.ts +6 -2
  295. package/src/__tests__/gpu-driven-view.unit.test.ts +1 -1
  296. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +1 -0
  297. package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -4
  298. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +1 -0
  299. package/src/__tests__/gpu-scene.unit.test.ts +119 -9
  300. package/src/__tests__/instance-allocation-failure.unit.test.ts +57 -0
  301. package/src/__tests__/instances-culling.integration.test.ts +10 -36
  302. package/src/__tests__/instances-store.unit.test.ts +97 -305
  303. package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
  304. package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +146 -0
  305. package/src/__tests__/material-shader-prewarm.unit.test.ts +59 -0
  306. package/src/__tests__/material-snapshot-mutation.integration.test.ts +39 -1
  307. package/src/__tests__/morph-culling-reentry.dawn.test.ts +5 -5
  308. package/src/__tests__/morph-visual-evidence.integration.test.ts +8 -7
  309. package/src/__tests__/motion-blur-compute.dawn.test.ts +13 -7
  310. package/src/__tests__/motion-blur.pixel.integration.test.ts +2 -2
  311. package/src/__tests__/nineslice-handle-surface.unit.test.ts +4 -4
  312. package/src/__tests__/pbr-view-layout.unit.test.ts +9 -0
  313. package/src/__tests__/physical-clearcoat.integration.test.ts +58 -0
  314. package/src/__tests__/prepared-graphics-capability.unit.test.ts +66 -2
  315. package/src/__tests__/probe-bindings.unit.test.ts +582 -1
  316. package/src/__tests__/probe-scene-dirty.integration.test.ts +28 -1
  317. package/src/__tests__/receipt-observation-retirement.unit.test.ts +65 -0
  318. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +69 -0
  319. package/src/__tests__/render-error-code-owner.test-d.ts +7 -0
  320. package/src/__tests__/render-feature-diagnostics.unit.test.ts +15 -2
  321. package/src/__tests__/render-feature-error-reporting.unit.test.ts +21 -1
  322. package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +10 -1
  323. package/src/__tests__/render-feature-retry.unit.test.ts +12 -1
  324. package/src/__tests__/render-scene-mixed-updates.integration.test.ts +145 -19
  325. package/src/__tests__/render-scene-projection.unit.test.ts +36 -11
  326. package/src/__tests__/render-scene-temporal-retry.unit.test.ts +20 -20
  327. package/src/__tests__/render-target-probe-soak.perf.test.ts +2 -2
  328. package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +3 -3
  329. package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +5 -2
  330. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +41 -7
  331. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +16 -0
  332. package/src/__tests__/renderer-frame-transaction.integration.test.ts +26 -10
  333. package/src/__tests__/resource-allocation.unit.test.ts +123 -0
  334. package/src/__tests__/single-layer-medium-passes.unit.test.ts +275 -0
  335. package/src/__tests__/skin-motion-regression.unit.test.ts +70 -1
  336. package/src/__tests__/skin-palette-recycling.dawn.test.ts +97 -0
  337. package/src/__tests__/skinned-shadow-caster.test.ts +15 -0
  338. package/src/__tests__/ssr-carrier-public-contract.unit.test.ts +21 -0
  339. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +4 -4
  340. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +2 -2
  341. package/src/__tests__/ssr-gpu-dispatch.ts +1 -1
  342. package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +6 -2
  343. package/src/__tests__/standard-output-chain-contract.unit.test.ts +28 -0
  344. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +46 -1
  345. package/src/__tests__/standard-pipeline.integration.test.ts +182 -1
  346. package/src/__tests__/surface-dynamic-buffer-lifecycle.unit.test.ts +506 -0
  347. package/src/__tests__/surface-submission-observation.unit.test.ts +812 -0
  348. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +32 -0
  349. package/src/__tests__/temporal-performance.integration.test.ts +3 -3
  350. package/src/__tests__/visibility-instances.integration.test.ts +7 -19
  351. package/src/__tests__/visibility-producer-matrix.integration.test.ts +9 -20
  352. package/src/__tests__/volume-optics-oracle.unit.test.ts +4 -2
  353. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +30 -20
  354. package/src/__tests__/volumetric-fog-errors.unit.test.ts +4 -3
  355. package/src/__tests__/volumetric-fog-multiple.unit.test.ts +55 -0
  356. package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +1 -1
  357. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +113 -9
  358. package/src/__tests__/volumetric-fog-topology.unit.test.ts +5 -3
  359. package/src/assembly/backend-contract.ts +30 -0
  360. package/src/assembly/barrel-distortion-inspection.ts +22 -0
  361. package/src/assembly/canvas-draw-guard.ts +11 -0
  362. package/src/assembly/cloud-shadow-entry.ts +22 -0
  363. package/src/assembly/dynamic-geometry-host.ts +65 -37
  364. package/src/assembly/dynamic-geometry-runtime.ts +7 -1
  365. package/src/assembly/factory.ts +2 -30
  366. package/src/assembly/host-contract.ts +3 -3
  367. package/src/assembly/material/assembly.ts +25 -13
  368. package/src/assembly/material/pipeline-helpers.ts +53 -12
  369. package/src/assembly/material-shader-policy.ts +55 -0
  370. package/src/assembly/material-shader-prewarm.ts +21 -10
  371. package/src/assembly/recovery/device-loss-fanout.ts +13 -0
  372. package/src/assembly/recovery/renderer-recover.ts +41 -18
  373. package/src/assembly/render-feature-post-processes.ts +54 -0
  374. package/src/assembly/renderer-facade.ts +4 -1
  375. package/src/assembly/renderer-feature-inspection.ts +8 -2
  376. package/src/assembly/renderer-frame-transaction.ts +23 -0
  377. package/src/assembly/renderer-inspection.ts +28 -0
  378. package/src/assembly/skin-palette-owner.ts +1 -1
  379. package/src/assembly/temporal-post-process-prewarm.ts +3 -3
  380. package/src/assembly/webgpu-pbr-ready.ts +9 -1
  381. package/src/assembly/webgpu-ready-per-pass.ts +104 -0
  382. package/src/assembly/webgpu-ready.ts +115 -61
  383. package/src/assembly/webgpu-renderer-bootstrap.ts +0 -12
  384. package/src/assembly/webgpu-renderer-observation-owner.ts +222 -0
  385. package/src/assembly/webgpu-renderer.ts +214 -252
  386. package/src/assembly/webgpu-vertex-layouts.ts +1 -1
  387. package/src/barrel-distortion.ts +247 -0
  388. package/src/cloud/__tests__/cloud-density-parity.dawn.test.ts +173 -0
  389. package/src/cloud/__tests__/cloud-layer-framebuffer-y.dawn.test.ts +265 -0
  390. package/src/cloud/__tests__/cloud-layer.browser.test.ts +432 -0
  391. package/src/cloud/__tests__/cloud-layer.unit.test.ts +634 -0
  392. package/src/cloud/density.ts +600 -0
  393. package/src/cloud/extract.ts +110 -0
  394. package/src/cloud/feature.ts +1810 -0
  395. package/src/cloud/frame-context.ts +42 -0
  396. package/src/cloud/inspection.ts +130 -0
  397. package/src/cloud/optics.ts +285 -0
  398. package/src/cloud/parameters.ts +255 -0
  399. package/src/cloud/resources.ts +122 -0
  400. package/src/cloud/shadow.ts +167 -0
  401. package/src/cloud/temporal.ts +226 -0
  402. package/src/components/barrel-distortion.ts +46 -0
  403. package/src/components/cloud-layer.ts +47 -0
  404. package/src/components/index.ts +2 -0
  405. package/src/components/instances.ts +18 -45
  406. package/src/components/sprite-instances.ts +1 -1
  407. package/src/construct-renderer.ts +1 -1
  408. package/src/dynamic-geometry.ts +12 -1
  409. package/src/errors/cloud.ts +103 -0
  410. package/src/errors/render.ts +44 -7
  411. package/src/examples/single-layer-medium-surface.ts +59 -0
  412. package/src/extract/camera.ts +15 -1
  413. package/src/extract/contracts.ts +5 -0
  414. package/src/features/barrel-distortion.ts +172 -0
  415. package/src/features/error-reporting.ts +26 -6
  416. package/src/features/host.ts +115 -14
  417. package/src/features/plan.ts +8 -0
  418. package/src/features/render-graph-contribution.ts +9 -0
  419. package/src/features/render-graph-raster.ts +45 -3
  420. package/src/features/targets.ts +66 -0
  421. package/src/features/types.ts +47 -0
  422. package/src/fullscreen-post-process-pass.ts +41 -9
  423. package/src/gpu-dirty-ranges.ts +48 -0
  424. package/src/gpu-driven/batch-topology.ts +53 -0
  425. package/src/gpu-driven/material-artifacts.ts +33 -25
  426. package/src/gpu-driven/prepared-draw.ts +12 -4
  427. package/src/gpu-driven/production-raster-material.ts +1894 -0
  428. package/src/gpu-driven/production-raster.ts +825 -784
  429. package/src/gpu-driven/resource-allocation.ts +142 -0
  430. package/src/gpu-driven/shadow-ownership.ts +71 -0
  431. package/src/gpu-driven/view-gpu.ts +164 -10
  432. package/src/gpu-scene.ts +75 -84
  433. package/src/hdrp-buffers.ts +4 -3
  434. package/src/ibl/skylight-bind-group.ts +49 -7
  435. package/src/index.ts +196 -9
  436. package/src/inspection-types.ts +162 -0
  437. package/src/instance-buffer-cache.ts +4 -4
  438. package/src/instances.ts +149 -569
  439. package/src/pbr-pipeline.ts +219 -13
  440. package/src/pipeline/single-layer-medium-passes.ts +267 -0
  441. package/src/pipeline/standard-forward-lane.ts +313 -57
  442. package/src/pipeline/standard-output/graph.ts +8 -2
  443. package/src/pipeline/standard-output/types.ts +12 -1
  444. package/src/pipeline/standard-pipeline.ts +291 -18
  445. package/src/pipeline/standard-post.ts +90 -24
  446. package/src/pipeline-builder.ts +1 -1
  447. package/src/plugin.ts +4 -0
  448. package/src/points-lines/__tests__/points-lines.bench.ts +17 -8
  449. package/src/prepare/prepared-graphics-resolver.ts +2 -0
  450. package/src/record/__tests__/main-pass-geometry.unit.test.ts +30 -0
  451. package/src/record/__tests__/sprite-material-abi.unit.test.ts +1 -1
  452. package/src/record/__tests__/temporal-payload.unit.test.ts +1 -1
  453. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +11 -0
  454. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -0
  455. package/src/record/__tests__/view-ubo-layout.unit.test.ts +4 -3
  456. package/src/record/barrel-distortion-frame.ts +110 -0
  457. package/src/record/dynamic-geometry-consumption.ts +39 -9
  458. package/src/record/fold-buckets.ts +852 -0
  459. package/src/record/fold-instance-buffer.ts +51 -0
  460. package/src/record/frame-lighting.ts +36 -2
  461. package/src/record/frame-snapshot.ts +39 -3
  462. package/src/record/frame.ts +217 -1084
  463. package/src/record/gpu-draw-receipts.ts +38 -0
  464. package/src/record/gpu-timing.ts +48 -5
  465. package/src/record/main-pass-geometry.ts +251 -82
  466. package/src/record/main-pass-material.ts +66 -4
  467. package/src/record/main-pass-sprite-draws.ts +7 -65
  468. package/src/record/main-pass.ts +156 -20
  469. package/src/record/motion-blur-receipt.ts +91 -0
  470. package/src/record/probe-blend-buffer.ts +180 -0
  471. package/src/record/recovery-pipeline.ts +35 -3
  472. package/src/record/render-context.ts +39 -3
  473. package/src/record/shadow-pass.ts +30 -1
  474. package/src/record/skybox-post-pass.ts +28 -4
  475. package/src/record/typed-frame-graph.ts +770 -134
  476. package/src/record/view-ubo.ts +26 -3
  477. package/src/record/volume-params.ts +225 -0
  478. package/src/recovery/render-system-candidate.ts +235 -42
  479. package/src/reflection/record-owner.ts +7 -3
  480. package/src/render-contract.ts +48 -4
  481. package/src/render-graph-primitives.ts +7 -1
  482. package/src/render-pipeline.ts +130 -0
  483. package/src/render-system-extract-tail.ts +57 -61
  484. package/src/render-system-extract.ts +74 -76
  485. package/src/render-system-presentation.ts +56 -0
  486. package/src/render-system-producer-roots.ts +96 -0
  487. package/src/render-system-residency.ts +159 -0
  488. package/src/render-system.ts +347 -363
  489. package/src/scene/probe-blend.ts +96 -4
  490. package/src/scene/render-scene.ts +349 -280
  491. package/src/ssao-buffers.ts +95 -2
  492. package/src/surface/__tests__/dynamic-input.unit.test.ts +374 -0
  493. package/src/surface/__tests__/gpu-driven.unit.test.ts +87 -0
  494. package/src/surface/dynamic-input.ts +658 -0
  495. package/src/surface/gpu-driven.ts +147 -0
  496. package/src/surface/submission-observation.ts +517 -0
  497. package/src/systems/skin-palette-allocator.ts +4 -1
  498. package/src/temporal/gpu.ts +101 -17
  499. package/src/temporal/inspection.ts +7 -1
  500. package/src/temporal/standard-scene-data.ts +5 -0
  501. package/src/temporal/target-coverage-attachment.ts +4 -0
  502. package/src/transmission/__tests__/single-layer-medium.unit.test.ts +149 -0
  503. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +12 -11
  504. package/src/transmission/single-layer-medium.ts +322 -0
  505. package/src/typed-render-graph-primitives.ts +70 -2
  506. package/src/volume/component.ts +10 -9
  507. package/src/volume/extract.ts +11 -5
  508. package/src/volume/inspection.ts +3 -0
  509. package/src/volume/passes.ts +87 -4
  510. package/src/volume/resources.ts +2 -1
  511. package/dist/chunk-3RTCJTH4.mjs.map +0 -1
  512. package/dist/chunk-NB2S7ZRX.mjs.map +0 -1
  513. package/dist/chunk-NNQL7CB3.mjs +0 -1713
  514. package/dist/chunk-NNQL7CB3.mjs.map +0 -1
  515. package/dist/chunk-OO3DRYK7.mjs.map +0 -1
  516. package/dist/chunk-PA5DRJNI.mjs.map +0 -1
  517. package/dist/chunk-PLAXOOX5.mjs.map +0 -1
  518. package/dist/chunk-VOZWDM3L.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 },
@@ -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,9 +35,11 @@ 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', () => {
@@ -406,7 +406,9 @@ describe('volumetric fog budget and recovery contract', () => {
406
406
  );
407
407
  });
408
408
 
409
- it('uses the canonical World clock to bound real volume history reuse', async () => {
409
+ it.each([
410
+ 1, 2,
411
+ ])('uses the canonical World clock for %i owners and bounds real history reuse', async (ownerCount) => {
410
412
  const queue = new RecordingNullQueue();
411
413
  const adapter = new RecoverableNullAdapter(() => queue);
412
414
  const backend = {
@@ -441,22 +443,23 @@ describe('volumetric fog budget and recovery contract', () => {
441
443
  data: { direction: [-0.4, -0.8, -0.3], castShadow: true },
442
444
  })
443
445
  .unwrap();
444
- world
445
- .spawn({
446
- component: VolumetricFog,
447
- data: {
448
- light: sun,
449
- density,
450
- boundsMin: [-1, -1, -1],
451
- boundsMax: [1, 1, 1],
452
- extinction: [0.2, 0.2, 0.2],
453
- albedo: [0.8, 0.8, 0.8],
454
- emission: [0, 0, 0],
455
- anisotropy: 0,
456
- maxDistance: 50,
457
- },
458
- })
459
- .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();
460
463
 
461
464
  const lease = renderer.attach(world);
462
465
  expect(lease.ok).toBe(true);
@@ -477,10 +480,17 @@ describe('volumetric fog budget and recovery contract', () => {
477
480
  if (!frame.ok) return { history: -1, worldTime: -1, delta: -1 };
478
481
  expect((await frame.value.completed).ok).toBe(true);
479
482
  const paramsWrite = queue.writes
480
- .filter((write) => write.offset === 0 && write.bytes.byteLength === 144)
483
+ .filter(
484
+ (write) => write.offset === 0 && write.bytes.byteLength === VOLUMETRIC_FOG_PARAMS_BYTES,
485
+ )
481
486
  .at(-1);
482
487
  expect(paramsWrite).toBeDefined();
483
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
+ }
484
494
  const historyWrite = queue.writes
485
495
  .filter(
486
496
  (write) =>
@@ -559,7 +569,7 @@ describe('volumetric fog budget and recovery contract', () => {
559
569
  expect(resolved?.extent).toEqual({ width: 960, height: 540, depthOrArrayLayers: 1 });
560
570
  });
561
571
 
562
- 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 () => {
563
573
  const adapter = new RecoverableNullAdapter();
564
574
  const backend = {
565
575
  ...nullRhi,
@@ -766,7 +776,7 @@ describe('volumetric fog budget and recovery contract', () => {
766
776
  readonly totalBytes: number;
767
777
  readonly physicalResourceCount: number;
768
778
  }> = [];
769
- for (let frame = 0; frame < 300; frame += 1) {
779
+ for (let frame = 0; frame < 60; frame += 1) {
770
780
  const inspection = await draw();
771
781
  const facts = inspection?.volumetricFog?.resourceFacts;
772
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
 
@@ -0,0 +1,55 @@
1
+ import type { EntityHandle } from '@forgeax/engine-ecs';
2
+ import { describe, expect, it } from 'vitest';
3
+ import type { VolumetricFogAuthoring } from '../volume/component';
4
+ import { extractVolumetricFog } from '../volume/extract';
5
+
6
+ const fog: VolumetricFogAuthoring = {
7
+ light: 0 as EntityHandle,
8
+ density: {
9
+ guid: 'smoke-a',
10
+ generation: 1,
11
+ format: 'r8unorm',
12
+ colorSpace: 'linear',
13
+ shape: { viewDimension: '3d', extent: { width: 16, height: 16, depth: 16 } },
14
+ },
15
+ bounds: { min: [-1, -1, -1], max: [1, 1, 1] },
16
+ extinction: [1, 1, 1],
17
+ albedo: [0.8, 0.8, 0.8],
18
+ emission: [0, 0, 0],
19
+ anisotropy: 0,
20
+ maxDistance: 20,
21
+ };
22
+
23
+ describe('independent local volumetric fog owners', () => {
24
+ it('accepts overlapping owners and retains their distinct optical properties', () => {
25
+ const second = { ...fog, emission: [1, 0, 0] as const };
26
+ const result = extractVolumetricFog([fog, second]);
27
+ expect(result.ok).toBe(true);
28
+ if (result.ok && result.value.status === 'available') {
29
+ expect(result.value.fogs).toHaveLength(2);
30
+ expect(result.value.fogs[1]?.emission).toEqual([1, 0, 0]);
31
+ }
32
+ });
33
+ it('validates every owner, not only the first', () => {
34
+ const result = extractVolumetricFog([fog, { ...fog, anisotropy: 2 }]);
35
+ expect(result.ok).toBe(false);
36
+ if (!result.ok) expect(result.error.code).toBe('volume-invalid-parameters');
37
+ });
38
+ it('removes one owner independently and turns off only after the last', () => {
39
+ const result = extractVolumetricFog([fog]);
40
+ expect(result.ok && result.value.status === 'available' && result.value.fogs.length).toBe(1);
41
+ expect(extractVolumetricFog([])).toEqual({ ok: true, value: { status: 'off' } });
42
+ });
43
+ it('rejects overflow explicitly instead of dropping visible owners', () => {
44
+ const result = extractVolumetricFog(Array.from({ length: 9 }, () => fog));
45
+ expect(result.ok).toBe(false);
46
+ if (!result.ok) expect(result.error.code).toBe('volume-owner-conflict');
47
+ });
48
+ it('rejects unknown sampling modes in untyped input', () => {
49
+ const result = extractVolumetricFog([
50
+ { ...fog, sampling: 'unknown' } as unknown as VolumetricFogAuthoring,
51
+ ]);
52
+ expect(result.ok).toBe(false);
53
+ if (!result.ok) expect(result.error.code).toBe('volume-invalid-parameters');
54
+ });
55
+ });
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { describe, expect, it } from 'vitest';
3
3
 
4
- const frame = readFileSync(new URL('../record/frame.ts', import.meta.url), 'utf8');
4
+ const frame = readFileSync(new URL('../record/volume-params.ts', import.meta.url), 'utf8');
5
5
 
6
6
  describe('volumetric Spot frame optics', () => {
7
7
  it('passes extracted colorTimesIntensity through once', () => {