@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
@@ -19,6 +19,7 @@ import { describe, expect, it, vi } from 'vitest';
19
19
  import type { RhiBackendPack } from '../assembly/backend-contract';
20
20
  import { createRenderer as constructRenderer } from '../assembly/factory';
21
21
  import {
22
+ BarrelDistortion,
22
23
  DirectionalLight,
23
24
  DynamicResolution,
24
25
  MeshFilter,
@@ -164,7 +165,75 @@ async function createMotionBlurScenario() {
164
165
  };
165
166
  }
166
167
 
168
+ function createGenericPostProbeFeature(): RenderFeature<undefined> {
169
+ return {
170
+ identity: 'synthetic.generic-post-probe',
171
+ placement: 'post',
172
+ shaderModuleMode: 'immediate',
173
+ extract: () => ok(undefined),
174
+ plan: () =>
175
+ ok({
176
+ resources: [
177
+ {
178
+ kind: 'compute-program' as const,
179
+ name: 'generic-post-probe-program',
180
+ program: {
181
+ wgsl: '@compute @workgroup_size(1) fn main() {}',
182
+ entryPoints: ['main'],
183
+ },
184
+ },
185
+ {
186
+ kind: 'compute-bindings' as const,
187
+ name: 'generic-post-probe-bindings',
188
+ program: 'generic-post-probe-program',
189
+ entries: [],
190
+ },
191
+ ],
192
+ passes: [
193
+ {
194
+ kind: 'compute' as const,
195
+ name: 'generic-post-probe',
196
+ program: 'generic-post-probe-program',
197
+ bindings: 'generic-post-probe-bindings',
198
+ dispatches: [{ kind: 'direct' as const, entryPoint: 'main', workgroups: [1] }],
199
+ },
200
+ ],
201
+ }),
202
+ };
203
+ }
204
+
167
205
  describe('factory contract', () => {
206
+ it('does not publish renderer initialization before boot queue work settles', async () => {
207
+ const fence = deferred<undefined>();
208
+ const onSubmittedWorkDone = vi
209
+ .spyOn(RhiNullQueue.prototype, 'onSubmittedWorkDone')
210
+ .mockReturnValue(fence.promise);
211
+ const canvas = { width: 64, height: 64, getContext: () => null };
212
+ let renderer: Awaited<ReturnType<typeof constructRenderer>> | undefined;
213
+ try {
214
+ renderer = await constructRenderer(
215
+ canvas,
216
+ { rhi },
217
+ { shaderManifestUrl: renderLifecycleManifestUrl() },
218
+ );
219
+ let settled = false;
220
+ const initialization = renderer.initialization.then(() => {
221
+ settled = true;
222
+ });
223
+ await vi.waitFor(() => expect(onSubmittedWorkDone).toHaveBeenCalled(), {
224
+ timeout: 2_000,
225
+ });
226
+ expect(settled).toBe(false);
227
+ fence.resolve(undefined);
228
+ await initialization;
229
+ expect(settled).toBe(true);
230
+ } finally {
231
+ fence.resolve(undefined);
232
+ onSubmittedWorkDone.mockRestore();
233
+ await renderer?.dispose();
234
+ }
235
+ });
236
+
168
237
  it('requires one Renderer-owned target lifecycle seam', () => {
169
238
  expect(existsSync(renderTargetHostSourcePath)).toBe(true);
170
239
  const factorySource = readFileSync(factorySourcePath, 'utf8');
@@ -199,7 +268,7 @@ describe('factory contract', () => {
199
268
  it('releases its profiler catalog contribution on dispose', async () => {
200
269
  const profiler = createProfiler();
201
270
  const renderer = await constructRenderer(
202
- { getContext: () => null },
271
+ { width: 64, height: 64, getContext: () => null },
203
272
  { profiler, rhi },
204
273
  { shaderManifestUrl: manifestUrl() },
205
274
  );
@@ -227,7 +296,7 @@ describe('factory contract', () => {
227
296
 
228
297
  try {
229
298
  const renderer = await constructRenderer(
230
- { getContext: () => null },
299
+ { width: 64, height: 64, getContext: () => null },
231
300
  { rhi },
232
301
  { shaderManifestUrl: renderLifecycleManifestUrl() },
233
302
  );
@@ -273,7 +342,7 @@ describe('factory contract', () => {
273
342
  }),
274
343
  } as never;
275
344
  const renderer = await constructRenderer(
276
- { getContext: () => null },
345
+ { width: 64, height: 64, getContext: () => null },
277
346
  {
278
347
  rhi: timingRhi,
279
348
  gpuPassTiming: { maxPassesPerFrame: 4, maxFramesInFlight: 1, retentionFrames: 2 },
@@ -286,7 +355,7 @@ describe('factory contract', () => {
286
355
 
287
356
  it('completes the receipt-bound create, attach, draw, observe, recover chain', async () => {
288
357
  const renderer = await constructRenderer(
289
- { getContext: () => null },
358
+ { width: 64, height: 64, getContext: () => null },
290
359
  { rhi },
291
360
  { shaderManifestUrl: manifestUrl() },
292
361
  );
@@ -303,7 +372,7 @@ describe('factory contract', () => {
303
372
  camera: { lease },
304
373
  environment: { lease },
305
374
  });
306
- expect(frame.ok).toBe(true);
375
+ expect(frame.ok, frame.ok ? '' : JSON.stringify(frame.error)).toBe(true);
307
376
  if (!frame.ok || frame.value === undefined) return;
308
377
 
309
378
  const observed = await renderer.observe(frame.value, { include: ['timings'] });
@@ -316,7 +385,7 @@ describe('factory contract', () => {
316
385
 
317
386
  it('rejects the removed Standard reflection-probe profile field at the Renderer boundary', async () => {
318
387
  const renderer = await constructRenderer(
319
- { getContext: () => null },
388
+ { width: 64, height: 64, getContext: () => null },
320
389
  { rhi },
321
390
  { shaderManifestUrl: manifestUrl() },
322
391
  );
@@ -332,7 +401,7 @@ describe('factory contract', () => {
332
401
 
333
402
  it('projects stable output and observation identity without pixel payloads', async () => {
334
403
  const renderer = await constructRenderer(
335
- { getContext: () => null },
404
+ { width: 64, height: 64, getContext: () => null },
336
405
  { rhi },
337
406
  { shaderManifestUrl: manifestUrl() },
338
407
  );
@@ -361,7 +430,7 @@ describe('factory contract', () => {
361
430
 
362
431
  it('delivers a target readback only through its matching receipt', async () => {
363
432
  const renderer = await constructRenderer(
364
- { getContext: () => null },
433
+ { width: 64, height: 64, getContext: () => null },
365
434
  { rhi },
366
435
  { shaderManifestUrl: manifestUrl() },
367
436
  );
@@ -390,7 +459,7 @@ describe('factory contract', () => {
390
459
  camera: { lease: attached.value },
391
460
  environment: { lease: attached.value },
392
461
  });
393
- expect(frame.ok).toBe(true);
462
+ expect(frame.ok, frame.ok ? '' : JSON.stringify(frame.error)).toBe(true);
394
463
  if (!frame.ok || frame.value === undefined) return;
395
464
  const observed = await renderer.observe(frame.value, {
396
465
  include: ['target-readbacks'],
@@ -439,11 +508,65 @@ describe('factory contract', () => {
439
508
  camera: { lease: attached.value },
440
509
  environment: { lease: attached.value },
441
510
  });
442
- expect(frame.ok).toBe(true);
511
+ expect(frame.ok, frame.ok ? '' : JSON.stringify(frame.error)).toBe(true);
443
512
  expect(planCalls).toBe(1);
444
513
  renderer.dispose();
445
514
  });
446
515
 
516
+ it('projects the active barrel stage through the ordinary feature host once', async () => {
517
+ const renderer = await constructRenderer(
518
+ { width: 64, height: 64, getContext: () => null },
519
+ { rhi },
520
+ { shaderManifestUrl: renderLifecycleManifestUrl() },
521
+ );
522
+ expect((await renderer.initialization).ok).toBe(true);
523
+ const world = new World();
524
+ const attached = renderer.attach(world);
525
+ expect(attached.ok).toBe(true);
526
+ if (!attached.ok) return;
527
+ registerPropagateTransforms(world);
528
+ world
529
+ .spawn(
530
+ { component: Transform, data: { pos: [0, 0, 4] } },
531
+ {
532
+ component: LocalCamera,
533
+ data: {
534
+ fov: 1,
535
+ aspect: 1,
536
+ near: 0.1,
537
+ far: 100,
538
+ antialias: ANTIALIAS_NONE,
539
+ tonemap: TONEMAP_ACES_FILMIC,
540
+ bloom: BLOOM_DISABLED,
541
+ },
542
+ },
543
+ { component: BarrelDistortion, data: { strength: 0.2, centerX: 0.5, centerY: 0.5 } },
544
+ )
545
+ .unwrap();
546
+ expect(world.update().ok).toBe(true);
547
+ const frame = renderer.draw({
548
+ leases: [attached.value],
549
+ camera: { lease: attached.value },
550
+ environment: { lease: attached.value },
551
+ });
552
+ expect(frame.ok, frame.ok ? '' : JSON.stringify(frame.error)).toBe(true);
553
+ if (frame.ok && frame.value !== undefined) {
554
+ expect(frame.value.barrelDistortion).toMatchObject({
555
+ width: 64,
556
+ height: 64,
557
+ camera: { projection: 'perspective' },
558
+ });
559
+ expect(frame.value.barrelDistortion?.strength).toBeCloseTo(0.2, 6);
560
+ expect(
561
+ renderer.perFramePassNames.filter((name) => name === 'barrel-distortion'),
562
+ ).toHaveLength(1);
563
+ expect(
564
+ renderer.perFramePassNames.filter((name) => name.startsWith('post-effect-')),
565
+ ).toHaveLength(0);
566
+ }
567
+ await renderer.dispose();
568
+ });
569
+
447
570
  it('keeps the active graph as LKG while a feature candidate is pending', async () => {
448
571
  let invalid = false;
449
572
  let featurePlanCalls = 0;
@@ -679,7 +802,7 @@ describe('factory contract', () => {
679
802
 
680
803
  it('attaches derived-state systems once without putting writes in draw', async () => {
681
804
  const renderer = await constructRenderer(
682
- { getContext: () => null },
805
+ { width: 64, height: 64, getContext: () => null },
683
806
  { rhi },
684
807
  { shaderManifestUrl: manifestUrl() },
685
808
  );
@@ -695,12 +818,12 @@ describe('factory contract', () => {
695
818
 
696
819
  it('releases one World without disposing the shared Renderer', async () => {
697
820
  const first = await constructRenderer(
698
- { getContext: () => null },
821
+ { width: 64, height: 64, getContext: () => null },
699
822
  { rhi },
700
823
  { shaderManifestUrl: manifestUrl() },
701
824
  );
702
825
  const second = await constructRenderer(
703
- { getContext: () => null },
826
+ { width: 64, height: 64, getContext: () => null },
704
827
  { rhi },
705
828
  { shaderManifestUrl: manifestUrl() },
706
829
  );
@@ -717,7 +840,11 @@ describe('factory contract', () => {
717
840
  it('resolves a renderer for a host canvas and rejects missing input', async () => {
718
841
  const manifest = `data:application/json,${encodeURIComponent(JSON.stringify({ schemaVersion: '1.0.0', entries: [] }))}`;
719
842
  await expect(
720
- constructRenderer({ getContext: () => null }, { rhi }, { shaderManifestUrl: manifest }),
843
+ constructRenderer(
844
+ { width: 64, height: 64, getContext: () => null },
845
+ { rhi },
846
+ { shaderManifestUrl: manifest },
847
+ ),
721
848
  ).resolves.toMatchObject({
722
849
  attach: expect.any(Function),
723
850
  draw: expect.any(Function),
@@ -728,7 +855,7 @@ describe('factory contract', () => {
728
855
  it('temporarily releases presentation while preserving the Renderer identity', async () => {
729
856
  const manifest = `data:application/json,${encodeURIComponent(JSON.stringify({ schemaVersion: '1.0.0', entries: [] }))}`;
730
857
  const renderer = await constructRenderer(
731
- { getContext: () => null },
858
+ { width: 64, height: 64, getContext: () => null },
732
859
  { rhi },
733
860
  { shaderManifestUrl: manifest },
734
861
  );
@@ -744,7 +871,7 @@ describe('factory contract', () => {
744
871
  it('does not unconfigure a surface after its ownership was released', async () => {
745
872
  let unconfigureCalls = 0;
746
873
  const renderer = await constructRenderer(
747
- { getContext: () => null },
874
+ { width: 64, height: 64, getContext: () => null },
748
875
  {
749
876
  rhi: {
750
877
  ...rhi,
@@ -773,19 +900,70 @@ describe('factory contract', () => {
773
900
  expect(unconfigureCalls).toBe(1);
774
901
  });
775
902
 
903
+ it('keeps direct RHI instrumentation explicit-only at the factory boundary', async () => {
904
+ const drawOneFrame = (renderer: Awaited<ReturnType<typeof constructRenderer>>) => {
905
+ const world = new World();
906
+ const attached = renderer.attach(world);
907
+ expect(attached.ok).toBe(true);
908
+ if (!attached.ok) return;
909
+ expect(world.update().ok).toBe(true);
910
+ const frame = renderer.draw({
911
+ leases: [attached.value],
912
+ camera: { lease: attached.value },
913
+ environment: { lease: attached.value },
914
+ });
915
+ expect(frame.ok, frame.ok ? '' : JSON.stringify(frame.error)).toBe(true);
916
+ };
917
+ const moduleBoundary = vi.fn();
918
+ const implicit = await constructRenderer(
919
+ { width: 64, height: 64, getContext: () => null },
920
+ {
921
+ rhi: {
922
+ ...rhi,
923
+ instrumentation: { onFrameBoundary: moduleBoundary },
924
+ } as never,
925
+ },
926
+ { shaderManifestUrl: manifestUrl() },
927
+ );
928
+ expect((await implicit.initialization).ok).toBe(true);
929
+ drawOneFrame(implicit);
930
+ expect(moduleBoundary).not.toHaveBeenCalled();
931
+ expect((await implicit.dispose()).ok).toBe(true);
932
+
933
+ const explicitBoundary = vi.fn();
934
+ const explicit = await constructRenderer(
935
+ { width: 64, height: 64, getContext: () => null },
936
+ {
937
+ rhi: {
938
+ ...rhi,
939
+ instrumentation: { onFrameBoundary: moduleBoundary },
940
+ } as never,
941
+ rhiInstrumentation: { onFrameBoundary: explicitBoundary },
942
+ },
943
+ { shaderManifestUrl: manifestUrl() },
944
+ );
945
+ expect((await explicit.initialization).ok).toBe(true);
946
+ drawOneFrame(explicit);
947
+ expect(explicitBoundary).toHaveBeenCalled();
948
+ expect(moduleBoundary).not.toHaveBeenCalled();
949
+ expect((await explicit.dispose()).ok).toBe(true);
950
+ });
951
+
776
952
  it('retains the device-owned prewarmed TAA shader across off and on', async () => {
777
953
  const pending: Array<{
778
954
  readonly label: string;
779
955
  readonly promise: Promise<ReturnType<typeof ok<ShaderModule>>>;
780
956
  readonly resolve: (value: ReturnType<typeof ok<ShaderModule>>) => void;
781
957
  }> = [];
782
- const moduleCalls: string[] = [];
958
+ const moduleSources: string[] = [];
959
+ const frameBoundary = vi.fn();
783
960
  const backend: RhiBackendPack = {
784
961
  rhi,
962
+ instrumentation: { onFrameBoundary: frameBoundary },
785
963
  createShaderModule: (_device, desc) => {
786
964
  const label = desc.label ?? '<unlabeled>';
787
- moduleCalls.push(label);
788
- if (!label.startsWith('post-process-forgeax.taa-resolve-pso-')) {
965
+ moduleSources.push(desc.code);
966
+ if (!desc.code.includes('fs_taa_resolve')) {
789
967
  return Promise.resolve(ok({} as ShaderModule));
790
968
  }
791
969
  let resolve!: (value: ReturnType<typeof ok<ShaderModule>>) => void;
@@ -836,7 +1014,8 @@ describe('factory contract', () => {
836
1014
  });
837
1015
 
838
1016
  const first = renderer.draw(frameInput());
839
- expect(first.ok).toBe(true);
1017
+ expect(first.ok, first.ok ? '' : JSON.stringify(first.error)).toBe(true);
1018
+ expect(frameBoundary).toHaveBeenCalled();
840
1019
  expect(pending).toHaveLength(1);
841
1020
 
842
1021
  world.set(camera, LocalCamera, { antialias: ANTIALIAS_NONE }).unwrap();
@@ -844,14 +1023,12 @@ describe('factory contract', () => {
844
1023
  const off = renderer.draw(frameInput());
845
1024
  expect(off.ok).toBe(true);
846
1025
 
847
- expect(
848
- moduleCalls.filter((label) => label.startsWith('post-process-forgeax.taa-resolve-pso-')),
849
- ).toHaveLength(1);
1026
+ expect(moduleSources.filter((source) => source.includes('fs_taa_resolve'))).toHaveLength(1);
850
1027
 
851
1028
  world.set(camera, LocalCamera, { antialias: ANTIALIAS_TAA }).unwrap();
852
1029
  expect(world.update().ok).toBe(true);
853
1030
  const second = renderer.draw(frameInput());
854
- expect(second.ok).toBe(true);
1031
+ expect(second.ok, second.ok ? '' : JSON.stringify(second.error)).toBe(true);
855
1032
  expect(pending).toHaveLength(1);
856
1033
  expect(renderer.draw(frameInput()).ok).toBe(true);
857
1034
  world.set(camera, LocalCamera, { antialias: ANTIALIAS_NONE }).unwrap();
@@ -865,13 +1042,11 @@ describe('factory contract', () => {
865
1042
 
866
1043
  expect((await renderer.dispose()).ok).toBe(true);
867
1044
  expect((await renderer.dispose()).ok).toBe(true);
868
- expect(
869
- moduleCalls.filter((label) => label.startsWith('post-process-forgeax.taa-resolve-pso-')),
870
- ).toHaveLength(1);
1045
+ expect(moduleSources.filter((source) => source.includes('fs_taa_resolve'))).toHaveLength(1);
871
1046
  });
872
1047
 
873
1048
  it('rebuilds a TAA graph for a same-mode target resize', async () => {
874
- const moduleCalls: string[] = [];
1049
+ const moduleSources: string[] = [];
875
1050
  const created = new Map<object, string>();
876
1051
  const destroyed = new Set<object>();
877
1052
  let wrappedDevice: object | undefined;
@@ -914,7 +1089,7 @@ describe('factory contract', () => {
914
1089
  const backend: RhiBackendPack = {
915
1090
  rhi: instrumentedRhi,
916
1091
  createShaderModule: async (_device, desc) => {
917
- moduleCalls.push(desc.label ?? '<unlabeled>');
1092
+ moduleSources.push(desc.code);
918
1093
  return ok({} as ShaderModule);
919
1094
  },
920
1095
  };
@@ -953,14 +1128,12 @@ describe('factory contract', () => {
953
1128
  await Promise.resolve();
954
1129
  first = renderer.draw(frameInput());
955
1130
  }
956
- expect(first.ok).toBe(true);
1131
+ expect(first.ok, first.ok ? '' : JSON.stringify(first.error)).toBe(true);
957
1132
  const firstGeneration = [...created.entries()]
958
1133
  .filter(([_, label]) => label.startsWith('taa-history-'))
959
1134
  .slice(-4);
960
1135
  expect(firstGeneration).toHaveLength(4);
961
- expect(
962
- moduleCalls.filter((label) => label.startsWith('post-process-forgeax.taa-resolve-pso-')),
963
- ).toHaveLength(1);
1136
+ expect(moduleSources.filter((source) => source.includes('fs_taa_resolve'))).toHaveLength(1);
964
1137
  const firstFrame = renderer.inspect().frame.frameId;
965
1138
 
966
1139
  canvas.width = 96;
@@ -982,9 +1155,7 @@ describe('factory contract', () => {
982
1155
  expect(replacementGeneration.every(([handle]) => !destroyed.has(handle))).toBe(true);
983
1156
  expect(renderer.inspect().frame.frameId).toBe(firstFrame + 1);
984
1157
  expect(renderer.inspect().state).toBe('alive');
985
- expect(
986
- moduleCalls.filter((label) => label.startsWith('post-process-forgeax.taa-resolve-pso-')),
987
- ).toHaveLength(1);
1158
+ expect(moduleSources.filter((source) => source.includes('fs_taa_resolve'))).toHaveLength(1);
988
1159
  world.set(camera, LocalCamera, { antialias: ANTIALIAS_NONE }).unwrap();
989
1160
  expect(world.update().ok).toBe(true);
990
1161
  expect(renderer.draw(frameInput()).ok).toBe(true);
@@ -1165,6 +1336,86 @@ describe('factory contract', () => {
1165
1336
  await renderer.dispose();
1166
1337
  });
1167
1338
 
1339
+ it('projects generic post work once and keeps Motion Blur independent across Standard lanes', async () => {
1340
+ for (const renderPath of ['forward', 'deferred'] as const) {
1341
+ for (const taa of [false, true]) {
1342
+ const renderer = await constructRenderer(
1343
+ { width: 64, height: 64, getContext: () => null },
1344
+ { rhi, features: [createGenericPostProbeFeature()] },
1345
+ { shaderManifestUrl: renderLifecycleManifestUrl() },
1346
+ );
1347
+ try {
1348
+ expect((await renderer.initialization).ok).toBe(true);
1349
+ expect(renderer.setProfile({ ...DEFAULT_STANDARD_PROFILE, renderPath }).ok).toBe(true);
1350
+ const world = new World();
1351
+ const attached = renderer.attach(world);
1352
+ expect(attached.ok).toBe(true);
1353
+ if (!attached.ok) continue;
1354
+ registerPropagateTransforms(world);
1355
+ const camera = world
1356
+ .spawn(
1357
+ { component: Transform, data: { pos: [0, 0, 4] } },
1358
+ {
1359
+ component: LocalCamera,
1360
+ data: {
1361
+ fov: 1,
1362
+ aspect: 1,
1363
+ near: 0.1,
1364
+ far: 100,
1365
+ antialias: taa ? ANTIALIAS_TAA : ANTIALIAS_NONE,
1366
+ tonemap: TONEMAP_ACES_FILMIC,
1367
+ },
1368
+ },
1369
+ )
1370
+ .unwrap();
1371
+ world
1372
+ .addComponent(camera, {
1373
+ component: MotionBlur,
1374
+ data: { shutterAngle: 180, maxRadiusPixels: 8, sampleCount: 4, targetFps: 60 },
1375
+ })
1376
+ .unwrap();
1377
+ expect(world.update().ok).toBe(true);
1378
+ const draw = renderer.draw({
1379
+ leases: [attached.value],
1380
+ camera: { lease: attached.value },
1381
+ environment: { lease: attached.value },
1382
+ });
1383
+ expect(draw.ok).toBe(true);
1384
+ if (!draw.ok || draw.value === undefined) continue;
1385
+ const completed = await draw.value.completed;
1386
+ expect(completed.ok).toBe(true);
1387
+ if (!completed.ok) continue;
1388
+ const inspection = renderer.inspect();
1389
+ const names = inspection.perFramePassNames;
1390
+ const genericIndex = names.indexOf('generic-post-probe');
1391
+ const motionSummaryIndex = names.indexOf('motion-blur-tile-summary');
1392
+ const motionIndex = names.indexOf('motion-blur');
1393
+ const outputIndex = names.indexOf('output-transform');
1394
+ expect(names.filter((name) => name === 'generic-post-probe')).toHaveLength(1);
1395
+ expect(names.filter((name) => name === 'motion-blur-tile-summary')).toHaveLength(1);
1396
+ expect(names.filter((name) => name === 'motion-blur')).toHaveLength(1);
1397
+ expect(genericIndex).toBeGreaterThanOrEqual(0);
1398
+ expect(motionSummaryIndex).toBeGreaterThanOrEqual(0);
1399
+ expect(motionIndex).toBeGreaterThan(motionSummaryIndex);
1400
+ expect(outputIndex).toBeGreaterThan(motionIndex);
1401
+ if (taa) expect(names.indexOf('taa-resolve')).toBeLessThan(motionSummaryIndex);
1402
+ else expect(names).not.toContain('taa-resolve');
1403
+ expect(inspection.motionBlur).toMatchObject({
1404
+ enabled: true,
1405
+ passCount: 2,
1406
+ execution: { passes: [{ name: 'motion-blur-tile-summary' }, { name: 'motion-blur' }] },
1407
+ });
1408
+ // A completed two-pass execution is the public Renderer proof that
1409
+ // the named motion-blurred-color target had a writer and was then
1410
+ // consumed by the shared output chain.
1411
+ expect(inspection.featureGraph?.compileSuccesses ?? 0).toBeGreaterThan(0);
1412
+ } finally {
1413
+ await renderer.dispose();
1414
+ }
1415
+ }
1416
+ }
1417
+ });
1418
+
1168
1419
  it('publishes invalid MotionBlur parameters through renderer inspection', async () => {
1169
1420
  const renderer = await constructRenderer(
1170
1421
  { width: 64, height: 64, getContext: () => null },
@@ -1320,7 +1571,7 @@ describe('factory contract', () => {
1320
1571
  await Promise.resolve();
1321
1572
  first = renderer.draw(frameInput());
1322
1573
  }
1323
- expect(first.ok).toBe(true);
1574
+ expect(first.ok, first.ok ? '' : JSON.stringify(first.error)).toBe(true);
1324
1575
  const historyHandles = [...created.entries()].filter(([_, label]) =>
1325
1576
  label.startsWith('taa-history-'),
1326
1577
  );
@@ -1701,11 +1952,11 @@ describe('factory contract', () => {
1701
1952
  });
1702
1953
 
1703
1954
  it('aggregates active and fence-retiring Bloom bundles through Renderer.inspect', async () => {
1955
+ const scenario = await createBloomScenario();
1704
1956
  const fence = deferred<undefined>();
1705
1957
  const onSubmittedWorkDone = vi
1706
1958
  .spyOn(RhiNullQueue.prototype, 'onSubmittedWorkDone')
1707
1959
  .mockReturnValue(fence.promise);
1708
- const scenario = await createBloomScenario();
1709
1960
  try {
1710
1961
  expect(scenario.draw().ok).toBe(true);
1711
1962
  scenario.setBloom('off');
@@ -1751,11 +2002,11 @@ describe('factory contract', () => {
1751
2002
  });
1752
2003
 
1753
2004
  it('settles Bloom off to exact zero without re-enabling it', async () => {
2005
+ const scenario = await createBloomScenario();
1754
2006
  const fence = deferred<undefined>();
1755
2007
  const onSubmittedWorkDone = vi
1756
2008
  .spyOn(RhiNullQueue.prototype, 'onSubmittedWorkDone')
1757
2009
  .mockReturnValue(fence.promise);
1758
- const scenario = await createBloomScenario();
1759
2010
  try {
1760
2011
  expect(scenario.draw().ok).toBe(true);
1761
2012
  scenario.setBloom('off');
@@ -1788,11 +2039,11 @@ describe('factory contract', () => {
1788
2039
  });
1789
2040
 
1790
2041
  it('reports one structured Bloom fence rejection and drains late-retiring resources', async () => {
2042
+ const scenario = await createBloomScenario();
1791
2043
  const fence = deferred<undefined>();
1792
2044
  const onSubmittedWorkDone = vi
1793
2045
  .spyOn(RhiNullQueue.prototype, 'onSubmittedWorkDone')
1794
2046
  .mockReturnValue(fence.promise);
1795
- const scenario = await createBloomScenario();
1796
2047
  const errors: unknown[] = [];
1797
2048
  let errorSnapshot: ReturnType<typeof scenario.renderer.inspect> | undefined;
1798
2049
  const unsubscribe = scenario.renderer.onError((error) => {
@@ -1838,6 +2089,7 @@ describe('factory contract', () => {
1838
2089
  });
1839
2090
 
1840
2091
  it('keeps drained Bloom fence rejection callbacks inert', async () => {
2092
+ const scenario = await createBloomScenario();
1841
2093
  const callbacks: Array<{
1842
2094
  readonly resolve: (value: undefined) => unknown;
1843
2095
  readonly reject: ((reason: unknown) => unknown) | undefined;
@@ -1852,7 +2104,6 @@ describe('factory contract', () => {
1852
2104
  const onSubmittedWorkDone = vi
1853
2105
  .spyOn(RhiNullQueue.prototype, 'onSubmittedWorkDone')
1854
2106
  .mockReturnValue(lateFence);
1855
- const scenario = await createBloomScenario();
1856
2107
  const errors: unknown[] = [];
1857
2108
  const unsubscribe = scenario.renderer.onError((error) => errors.push(error));
1858
2109
  try {
@@ -2129,10 +2380,9 @@ describe('factory contract', () => {
2129
2380
  staleLossEvents: 0,
2130
2381
  });
2131
2382
  expect(renderer.inspect().recovery.rehydratedRoots).toBeGreaterThan(0);
2132
- // Recovery publishes only after the retained CPU/LKG frame has prepared
2133
- // the replacement graph and visible residency. The first post-recovery
2134
- // draw therefore starts with graph accessors and Standard inspection.
2135
- expect(renderer.inspect().perFramePassNames.length).toBeGreaterThan(0);
2383
+ // Publication retires the graph bound to the previous canvas context;
2384
+ // the first recovered draw acquires the replacement presentation target.
2385
+ expect(renderer.inspect().perFramePassNames).toHaveLength(0);
2136
2386
  expect(renderer.inspect().standardLighting).toBeDefined();
2137
2387
  expect(world.update().ok).toBe(true);
2138
2388
  expect(renderer.draw(input()).ok).toBe(true);