@forgeax/engine-render 0.1.28 → 0.1.29

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 (642) hide show
  1. package/README.md +627 -124
  2. package/dist/assembly/atmosphere-shader-prewarm.d.ts +6 -0
  3. package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -0
  4. package/dist/assembly/dynamic-geometry-host.d.ts +21 -0
  5. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
  6. package/dist/assembly/dynamic-geometry-runtime.d.ts +20 -0
  7. package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -0
  8. package/dist/assembly/factory.d.ts +3 -1
  9. package/dist/assembly/factory.d.ts.map +1 -1
  10. package/dist/assembly/gpu-driven-owner.d.ts +10 -0
  11. package/dist/assembly/gpu-driven-owner.d.ts.map +1 -0
  12. package/dist/assembly/host-contract.d.ts +8 -0
  13. package/dist/assembly/host-contract.d.ts.map +1 -1
  14. package/dist/assembly/material/assembly.d.ts +7 -0
  15. package/dist/assembly/material/assembly.d.ts.map +1 -1
  16. package/dist/assembly/material-shader-policy.d.ts +3 -1
  17. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  18. package/dist/assembly/recovery/failure-location.d.ts +12 -0
  19. package/dist/assembly/recovery/failure-location.d.ts.map +1 -0
  20. package/dist/assembly/recovery/renderer-recover.d.ts +6 -4
  21. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  22. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  23. package/dist/assembly/renderer-feature-inspection.d.ts +5 -0
  24. package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -0
  25. package/dist/assembly/renderer-frame-transaction.d.ts +19 -0
  26. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  27. package/dist/assembly/renderer-helpers.d.ts +3 -0
  28. package/dist/assembly/renderer-helpers.d.ts.map +1 -1
  29. package/dist/assembly/renderer-inspection.d.ts +4 -0
  30. package/dist/assembly/renderer-inspection.d.ts.map +1 -1
  31. package/dist/assembly/shader-prewarm-policy.d.ts +6 -0
  32. package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -1
  33. package/dist/assembly/skin-palette-owner.d.ts +8 -0
  34. package/dist/assembly/skin-palette-owner.d.ts.map +1 -0
  35. package/dist/assembly/ssr-shader-prewarm.d.ts +6 -0
  36. package/dist/assembly/ssr-shader-prewarm.d.ts.map +1 -0
  37. package/dist/assembly/webgpu-device-loss.d.ts +15 -0
  38. package/dist/assembly/webgpu-device-loss.d.ts.map +1 -0
  39. package/dist/assembly/webgpu-pbr-ready.d.ts +38 -0
  40. package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -0
  41. package/dist/assembly/webgpu-ready-mipmap.d.ts +11 -0
  42. package/dist/assembly/webgpu-ready-mipmap.d.ts.map +1 -0
  43. package/dist/assembly/webgpu-ready.d.ts +7 -3
  44. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  45. package/dist/assembly/webgpu-renderer-bootstrap.d.ts +10 -0
  46. package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -0
  47. package/dist/assembly/webgpu-renderer-contract.d.ts +62 -0
  48. package/dist/assembly/webgpu-renderer-contract.d.ts.map +1 -0
  49. package/dist/assembly/webgpu-renderer-guards.d.ts +9 -0
  50. package/dist/assembly/webgpu-renderer-guards.d.ts.map +1 -0
  51. package/dist/assembly/webgpu-renderer-observation.d.ts +6 -0
  52. package/dist/assembly/webgpu-renderer-observation.d.ts.map +1 -0
  53. package/dist/assembly/webgpu-renderer-recovery-failure.d.ts +12 -0
  54. package/dist/assembly/webgpu-renderer-recovery-failure.d.ts.map +1 -0
  55. package/dist/assembly/webgpu-renderer.d.ts +7 -141
  56. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  57. package/dist/assembly/webgpu-vertex-layouts.d.ts +28 -84
  58. package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -1
  59. package/dist/authoring.mjs +2 -2
  60. package/dist/{chunk-JAASCMXA.mjs → chunk-33XZFTQU.mjs} +245 -39
  61. package/dist/chunk-33XZFTQU.mjs.map +1 -0
  62. package/dist/{chunk-GF523LHF.mjs → chunk-BLXGONR2.mjs} +30 -12
  63. package/dist/chunk-BLXGONR2.mjs.map +1 -0
  64. package/dist/{chunk-J52NMQKR.mjs → chunk-BQVQOQJH.mjs} +1086 -838
  65. package/dist/chunk-BQVQOQJH.mjs.map +1 -0
  66. package/dist/{chunk-IQMKJLNU.mjs → chunk-BTWH7BP4.mjs} +386 -17
  67. package/dist/chunk-BTWH7BP4.mjs.map +1 -0
  68. package/dist/{chunk-MBU4ZVE7.mjs → chunk-FUODTTIA.mjs} +3921 -671
  69. package/dist/chunk-FUODTTIA.mjs.map +1 -0
  70. package/dist/{chunk-MYJ5RPJO.mjs → chunk-JQTSEI3Z.mjs} +3 -3
  71. package/dist/{chunk-MYJ5RPJO.mjs.map → chunk-JQTSEI3Z.mjs.map} +1 -1
  72. package/dist/{chunk-GE3SDD72.mjs → chunk-NICXSHAT.mjs} +16 -38
  73. package/dist/chunk-NICXSHAT.mjs.map +1 -0
  74. package/dist/{chunk-J6KACNOH.mjs → chunk-NRNXBLIJ.mjs} +577 -7
  75. package/dist/chunk-NRNXBLIJ.mjs.map +1 -0
  76. package/dist/{chunk-RBDBZ5R7.mjs → chunk-PA5DRJNI.mjs} +2 -2
  77. package/dist/chunk-PA5DRJNI.mjs.map +1 -0
  78. package/dist/{chunk-VSL23LZE.mjs → chunk-S4DNKQYQ.mjs} +1117 -445
  79. package/dist/chunk-S4DNKQYQ.mjs.map +1 -0
  80. package/dist/components/camera.d.ts +62 -2
  81. package/dist/components/camera.d.ts.map +1 -1
  82. package/dist/components/index.d.ts +1 -0
  83. package/dist/components/index.d.ts.map +1 -1
  84. package/dist/components/instances.d.ts +6 -20
  85. package/dist/components/instances.d.ts.map +1 -1
  86. package/dist/components/scene-instance.d.ts +2 -1
  87. package/dist/components/scene-instance.d.ts.map +1 -1
  88. package/dist/components/screen-space-reflection.d.ts +14 -0
  89. package/dist/components/screen-space-reflection.d.ts.map +1 -0
  90. package/dist/construct-renderer.mjs +12888 -7025
  91. package/dist/construct-renderer.mjs.map +1 -1
  92. package/dist/device/gpu-residency.d.ts +1 -12
  93. package/dist/device/gpu-residency.d.ts.map +1 -1
  94. package/dist/environment/background.d.ts +6 -0
  95. package/dist/environment/background.d.ts.map +1 -0
  96. package/dist/errors/gpu-driven.d.ts +6 -3
  97. package/dist/errors/gpu-driven.d.ts.map +1 -1
  98. package/dist/errors/render.d.ts +7 -4
  99. package/dist/errors/render.d.ts.map +1 -1
  100. package/dist/extract/camera.d.ts.map +1 -1
  101. package/dist/extract/gpu-driven.d.ts +33 -3
  102. package/dist/extract/gpu-driven.d.ts.map +1 -1
  103. package/dist/features/host.d.ts +23 -1
  104. package/dist/features/host.d.ts.map +1 -1
  105. package/dist/features/noise-texture.d.ts +17 -0
  106. package/dist/features/noise-texture.d.ts.map +1 -0
  107. package/dist/features/plan.d.ts +83 -2
  108. package/dist/features/plan.d.ts.map +1 -1
  109. package/dist/features/prepared-gpu-work.d.ts +37 -4
  110. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  111. package/dist/features/prepared-graphics-store.d.ts.map +1 -1
  112. package/dist/features/prepared-graphics.d.ts +5 -0
  113. package/dist/features/prepared-graphics.d.ts.map +1 -1
  114. package/dist/features/render-graph-compute.d.ts +5 -3
  115. package/dist/features/render-graph-compute.d.ts.map +1 -1
  116. package/dist/features/render-graph-contribution.d.ts +11 -3
  117. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  118. package/dist/features/render-graph-raster.d.ts +19 -3
  119. package/dist/features/render-graph-raster.d.ts.map +1 -1
  120. package/dist/features/types.d.ts +11 -0
  121. package/dist/features/types.d.ts.map +1 -1
  122. package/dist/fullscreen-post-process-pass.d.ts +2 -0
  123. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  124. package/dist/gpu-driven/batch-topology.d.ts +34 -1
  125. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  126. package/dist/gpu-driven/pbr-program.d.ts +10 -0
  127. package/dist/gpu-driven/pbr-program.d.ts.map +1 -0
  128. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  129. package/dist/gpu-driven/production-raster.d.ts +144 -23
  130. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  131. package/dist/gpu-driven/shadow-views.d.ts +126 -0
  132. package/dist/gpu-driven/shadow-views.d.ts.map +1 -0
  133. package/dist/gpu-driven/view-gpu.d.ts +18 -2
  134. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  135. package/dist/gpu-scene-schema.d.ts +2 -8
  136. package/dist/gpu-scene-schema.d.ts.map +1 -1
  137. package/dist/gpu-scene.d.ts +14 -0
  138. package/dist/gpu-scene.d.ts.map +1 -1
  139. package/dist/gpu-texture-usage.d.ts +1 -0
  140. package/dist/gpu-texture-usage.d.ts.map +1 -1
  141. package/dist/hdrp-buffers.d.ts +6 -0
  142. package/dist/hdrp-buffers.d.ts.map +1 -1
  143. package/dist/ibl/frame-resources.d.ts +8 -0
  144. package/dist/ibl/frame-resources.d.ts.map +1 -0
  145. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  146. package/dist/index.d.ts +18 -8
  147. package/dist/index.d.ts.map +1 -1
  148. package/dist/index.mjs +8 -8
  149. package/dist/inspection-types.d.ts +164 -0
  150. package/dist/inspection-types.d.ts.map +1 -1
  151. package/dist/instances-derived-bounds.d.ts +11 -0
  152. package/dist/instances-derived-bounds.d.ts.map +1 -1
  153. package/dist/instances.d.ts +89 -44
  154. package/dist/instances.d.ts.map +1 -1
  155. package/dist/internal.d.ts +3 -0
  156. package/dist/internal.d.ts.map +1 -1
  157. package/dist/internal.mjs +117 -9
  158. package/dist/internal.mjs.map +1 -1
  159. package/dist/lifecycle.d.ts +2 -1
  160. package/dist/lifecycle.d.ts.map +1 -1
  161. package/dist/material-row.d.ts +5 -0
  162. package/dist/material-row.d.ts.map +1 -0
  163. package/dist/materials.d.ts +15 -0
  164. package/dist/materials.d.ts.map +1 -1
  165. package/dist/pbr-pipeline.d.ts +21 -5
  166. package/dist/pbr-pipeline.d.ts.map +1 -1
  167. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  168. package/dist/pipeline/standard-output/auto-exposure/capability.d.ts +12 -0
  169. package/dist/pipeline/standard-output/auto-exposure/capability.d.ts.map +1 -0
  170. package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts +70 -0
  171. package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts.map +1 -0
  172. package/dist/pipeline/standard-output/auto-exposure/graph.d.ts +75 -0
  173. package/dist/pipeline/standard-output/auto-exposure/graph.d.ts.map +1 -0
  174. package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts +111 -0
  175. package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts.map +1 -0
  176. package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts +16 -0
  177. package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts.map +1 -0
  178. package/dist/pipeline/standard-output/auto-exposure/preset.d.ts +14 -0
  179. package/dist/pipeline/standard-output/auto-exposure/preset.d.ts.map +1 -0
  180. package/dist/pipeline/standard-output/auto-exposure/state.d.ts +46 -0
  181. package/dist/pipeline/standard-output/auto-exposure/state.d.ts.map +1 -0
  182. package/dist/pipeline/standard-output/color-transform.d.ts +27 -0
  183. package/dist/pipeline/standard-output/color-transform.d.ts.map +1 -0
  184. package/dist/pipeline/standard-output/graph.d.ts +15 -0
  185. package/dist/pipeline/standard-output/graph.d.ts.map +1 -0
  186. package/dist/pipeline/standard-output/lut-admission.d.ts +29 -0
  187. package/dist/pipeline/standard-output/lut-admission.d.ts.map +1 -0
  188. package/dist/pipeline/standard-output/lut-gpu.d.ts +48 -0
  189. package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -0
  190. package/dist/pipeline/standard-output/lut-state.d.ts +68 -0
  191. package/dist/pipeline/standard-output/lut-state.d.ts.map +1 -0
  192. package/dist/pipeline/standard-output/resources.d.ts +10 -0
  193. package/dist/pipeline/standard-output/resources.d.ts.map +1 -0
  194. package/dist/pipeline/standard-output/types.d.ts +39 -0
  195. package/dist/pipeline/standard-output/types.d.ts.map +1 -0
  196. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  197. package/dist/pipeline/standard-post.d.ts +2 -2
  198. package/dist/pipeline/standard-post.d.ts.map +1 -1
  199. package/dist/pipeline-builder.d.ts +2 -1
  200. package/dist/pipeline-builder.d.ts.map +1 -1
  201. package/dist/pipeline-spec-types.d.ts +4 -0
  202. package/dist/pipeline-spec-types.d.ts.map +1 -1
  203. package/dist/pipeline-spec.d.ts +1 -9
  204. package/dist/pipeline-spec.d.ts.map +1 -1
  205. package/dist/points-lines/standard-owner.d.ts +1 -1
  206. package/dist/points-lines/standard-owner.d.ts.map +1 -1
  207. package/dist/prepare/prepared-graphics-resolver.d.ts +6 -1
  208. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  209. package/dist/record/dynamic-geometry-consumption.d.ts +15 -0
  210. package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -0
  211. package/dist/record/frame-lighting.d.ts +3 -3
  212. package/dist/record/frame-lighting.d.ts.map +1 -1
  213. package/dist/record/frame-snapshot.d.ts +106 -5
  214. package/dist/record/frame-snapshot.d.ts.map +1 -1
  215. package/dist/record/frame-targets.d.ts.map +1 -1
  216. package/dist/record/frame.d.ts +39 -4
  217. package/dist/record/frame.d.ts.map +1 -1
  218. package/dist/record/main-pass-geometry.d.ts +7 -8
  219. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  220. package/dist/record/main-pass-material.d.ts +38 -1
  221. package/dist/record/main-pass-material.d.ts.map +1 -1
  222. package/dist/record/main-pass-sprite-draws.d.ts +2 -10
  223. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  224. package/dist/record/main-pass.d.ts +12 -2
  225. package/dist/record/main-pass.d.ts.map +1 -1
  226. package/dist/record/mesh-ssbo.d.ts +15 -6
  227. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  228. package/dist/record/prepared-material-bindings.d.ts +19 -0
  229. package/dist/record/prepared-material-bindings.d.ts.map +1 -0
  230. package/dist/record/recovery-pipeline.d.ts +2 -9
  231. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  232. package/dist/record/render-context.d.ts +74 -26
  233. package/dist/record/render-context.d.ts.map +1 -1
  234. package/dist/record/shadow-pass.d.ts +62 -7
  235. package/dist/record/shadow-pass.d.ts.map +1 -1
  236. package/dist/record/skybox-post-pass.d.ts +1 -1
  237. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  238. package/dist/record/typed-frame-graph.d.ts +35 -7
  239. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  240. package/dist/record/view-ubo.d.ts +1 -0
  241. package/dist/record/view-ubo.d.ts.map +1 -1
  242. package/dist/recovery/render-system-candidate.d.ts +10 -2
  243. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  244. package/dist/recovery/render-system-roots.d.ts +11 -0
  245. package/dist/recovery/render-system-roots.d.ts.map +1 -0
  246. package/dist/recovery/types.d.ts +76 -0
  247. package/dist/recovery/types.d.ts.map +1 -0
  248. package/dist/reflection/record-owner.d.ts +0 -2
  249. package/dist/reflection/record-owner.d.ts.map +1 -1
  250. package/dist/render-contract.d.ts +173 -30
  251. package/dist/render-contract.d.ts.map +1 -1
  252. package/dist/render-graph-primitives.d.ts +1 -0
  253. package/dist/render-graph-primitives.d.ts.map +1 -1
  254. package/dist/render-pipeline.d.ts +64 -5
  255. package/dist/render-pipeline.d.ts.map +1 -1
  256. package/dist/render-system-extract-tail.d.ts +7 -5
  257. package/dist/render-system-extract-tail.d.ts.map +1 -1
  258. package/dist/render-system-extract.d.ts +101 -41
  259. package/dist/render-system-extract.d.ts.map +1 -1
  260. package/dist/render-system-projections.d.ts +22 -0
  261. package/dist/render-system-projections.d.ts.map +1 -0
  262. package/dist/render-system.d.ts +27 -77
  263. package/dist/render-system.d.ts.map +1 -1
  264. package/dist/scene/render-scene-types.d.ts +14 -7
  265. package/dist/scene/render-scene-types.d.ts.map +1 -1
  266. package/dist/scene/render-scene.d.ts +29 -11
  267. package/dist/scene/render-scene.d.ts.map +1 -1
  268. package/dist/scene/shadow-visibility.d.ts +5 -0
  269. package/dist/scene/shadow-visibility.d.ts.map +1 -0
  270. package/dist/ssr/admission.d.ts +70 -1
  271. package/dist/ssr/admission.d.ts.map +1 -1
  272. package/dist/ssr/compose.d.ts +13 -0
  273. package/dist/ssr/compose.d.ts.map +1 -0
  274. package/dist/ssr/errors.d.ts +35 -0
  275. package/dist/ssr/errors.d.ts.map +1 -1
  276. package/dist/ssr/graph.d.ts +60 -0
  277. package/dist/ssr/graph.d.ts.map +1 -0
  278. package/dist/ssr/history.d.ts +97 -0
  279. package/dist/ssr/history.d.ts.map +1 -0
  280. package/dist/ssr/hiz.d.ts +37 -0
  281. package/dist/ssr/hiz.d.ts.map +1 -0
  282. package/dist/ssr/inspection.d.ts +42 -0
  283. package/dist/ssr/inspection.d.ts.map +1 -0
  284. package/dist/ssr/resources.d.ts +58 -0
  285. package/dist/ssr/resources.d.ts.map +1 -0
  286. package/dist/ssr/temporal.d.ts +38 -0
  287. package/dist/ssr/temporal.d.ts.map +1 -0
  288. package/dist/systems/skin-palette-allocator.d.ts +32 -10
  289. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  290. package/dist/systems/skin-palette-types.d.ts +15 -0
  291. package/dist/systems/skin-palette-types.d.ts.map +1 -1
  292. package/dist/systems/transparent-dispatch.d.ts +9 -0
  293. package/dist/systems/transparent-dispatch.d.ts.map +1 -0
  294. package/dist/targets/material-source.d.ts +7 -0
  295. package/dist/targets/material-source.d.ts.map +1 -1
  296. package/dist/temporal/frame-coordinator.d.ts +7 -0
  297. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  298. package/dist/temporal/gpu.d.ts +8 -0
  299. package/dist/temporal/gpu.d.ts.map +1 -1
  300. package/dist/temporal/history.d.ts.map +1 -1
  301. package/dist/temporal/index.mjs +6 -6
  302. package/dist/temporal/inspection.d.ts.map +1 -1
  303. package/dist/temporal/standard-scene-data.d.ts +2 -1
  304. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  305. package/dist/temporal/temporal-view.d.ts +2 -0
  306. package/dist/temporal/temporal-view.d.ts.map +1 -1
  307. package/dist/typed-render-graph-primitives.d.ts +35 -1
  308. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  309. package/dist/typed-shadow-passes.d.ts +3 -1
  310. package/dist/typed-shadow-passes.d.ts.map +1 -1
  311. package/dist/volume/temporal.d.ts +6 -0
  312. package/dist/volume/temporal.d.ts.map +1 -1
  313. package/package.json +21 -20
  314. package/src/__tests__/auto-exposure-gpu-evidence.ts +291 -0
  315. package/src/__tests__/auto-exposure-inspection.unit.test.ts +80 -0
  316. package/src/__tests__/auto-exposure-transaction.integration.test.ts +99 -0
  317. package/src/__tests__/auto-exposure.browser.test.ts +33 -0
  318. package/src/__tests__/auto-exposure.dawn.test.ts +33 -0
  319. package/src/__tests__/detached-inspection.integration.test.ts +5 -0
  320. package/src/__tests__/device-scope-lifecycle.unit.test.ts +18 -0
  321. package/src/__tests__/device-scope-stale-matrix.unit.test.ts +18 -0
  322. package/src/__tests__/docs-gate-green.unit.test.ts +3 -3
  323. package/src/__tests__/factory-contract.integration.test.ts +67 -8
  324. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +19 -0
  325. package/src/__tests__/feature-depth-input.dawn.test.ts +121 -0
  326. package/src/__tests__/feature-noise.unit.test.ts +38 -0
  327. package/src/__tests__/fixtures/taa-resolve-pinned-baseline.wgsl +301 -0
  328. package/src/__tests__/frame-camera-selection.unit.test.ts +131 -0
  329. package/src/__tests__/frame-plan-contract.unit.test.ts +24 -0
  330. package/src/__tests__/frame-targets.unit.test.ts +24 -0
  331. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +3 -0
  332. package/src/__tests__/gpu-driven-baseline.characterization.test.ts +79 -0
  333. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +287 -6
  334. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +122 -142
  335. package/src/__tests__/gpu-driven-indirect-raster.browser.test.ts +10 -1
  336. package/src/__tests__/gpu-driven-lod-cache.unit.test.ts +264 -0
  337. package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +349 -0
  338. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +1417 -0
  339. package/src/__tests__/gpu-driven-pbr.integration.test.ts +21 -0
  340. package/src/__tests__/gpu-driven-production-fixture.ts +96 -0
  341. package/src/__tests__/gpu-driven-production.integration.test.ts +1075 -93
  342. package/src/__tests__/gpu-driven-scaling.unit.test.ts +1 -3
  343. package/src/__tests__/gpu-driven-shadow-views.browser.test.ts +63 -0
  344. package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +294 -0
  345. package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +97 -0
  346. package/src/__tests__/gpu-driven-shadow-views.unit.test.ts +168 -0
  347. package/src/__tests__/gpu-driven-skin-contract.test-d.ts +11 -0
  348. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +72 -20
  349. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +30 -9
  350. package/src/__tests__/gpu-driven-view.unit.test.ts +159 -52
  351. package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -1
  352. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +42 -0
  353. package/src/__tests__/gpu-scene-render-graph-gpu.ts +19 -5
  354. package/src/__tests__/gpu-scene.dawn.test.ts +40 -13
  355. package/src/__tests__/gpu-scene.unit.test.ts +559 -33
  356. package/src/__tests__/instances-culling.integration.test.ts +36 -10
  357. package/src/__tests__/instances-derived-bounds.unit.test.ts +54 -0
  358. package/src/__tests__/instances-store.unit.test.ts +242 -39
  359. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +6 -0
  360. package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +13 -16
  361. package/src/__tests__/lut-lkg-transaction.integration.test.ts +122 -0
  362. package/src/__tests__/material-contract-inventory.unit.test.ts +5 -6
  363. package/src/__tests__/material-dispatch-shader-id.unit.test.ts +51 -0
  364. package/src/__tests__/material-lane-selection.unit.test.ts +60 -0
  365. package/src/__tests__/material-numeric-upload.unit.test.ts +53 -0
  366. package/src/__tests__/material-texture-handles.unit.test.ts +18 -0
  367. package/src/__tests__/material-texture-source.unit.test.ts +87 -0
  368. package/src/__tests__/materials-authoring-validation.unit.test.ts +41 -0
  369. package/src/__tests__/materials-standard-contract.unit.test.ts +52 -2
  370. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +4 -0
  371. package/src/__tests__/normal-fallback.dawn.test.ts +122 -0
  372. package/src/__tests__/particle-material-input-layout.unit.test.ts +104 -0
  373. package/src/__tests__/physical-clearcoat.integration.test.ts +37 -3
  374. package/src/__tests__/pipeline-texture-specialization.unit.test.ts +138 -0
  375. package/src/__tests__/point-shadow-inspection.unit.test.ts +2 -0
  376. package/src/__tests__/prepared-gpu-driven-pbr-contract.test-d.ts +31 -0
  377. package/src/__tests__/prepared-gpu-driven-pbr-contract.unit.test.ts +39 -0
  378. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +35 -4
  379. package/src/__tests__/prepared-graphics-capability.unit.test.ts +21 -0
  380. package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +6 -2
  381. package/src/__tests__/profiler-phase-catalog.test.ts +5 -0
  382. package/src/__tests__/public-surface.test-d.ts +3 -0
  383. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +30 -12
  384. package/src/__tests__/reflection-fallback-async-publication.unit.test.ts +104 -0
  385. package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +2 -1
  386. package/src/__tests__/render-error-code-owner.test-d.ts +4 -0
  387. package/src/__tests__/render-error-detail-ssot.test-d.ts +22 -0
  388. package/src/__tests__/render-error-exhaustive.test-d.ts +15 -0
  389. package/src/__tests__/render-feature-gpu-work.integration.test.ts +285 -2
  390. package/src/__tests__/render-feature-plan.unit.test.ts +245 -0
  391. package/src/__tests__/render-feature-shadow-order.unit.test.ts +228 -0
  392. package/src/__tests__/render-feature-zero-work.unit.test.ts +35 -1
  393. package/src/__tests__/render-graph-fallback.integration.test.ts +2 -2
  394. package/src/__tests__/render-owner-cohesion.unit.test.ts +55 -0
  395. package/src/__tests__/render-scene-mixed-updates.integration.test.ts +426 -0
  396. package/src/__tests__/render-scene-projection.unit.test.ts +914 -23
  397. package/src/__tests__/render-scene-resource-updates.integration.test.ts +214 -0
  398. package/src/__tests__/render-scene-skin-lifecycle.integration.test.ts +173 -0
  399. package/src/__tests__/render-scene-temporal-retry.unit.test.ts +133 -0
  400. package/src/__tests__/render-target-material-source.test-d.ts +27 -3
  401. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +60 -0
  402. package/src/__tests__/renderer-frame-transaction.integration.test.ts +923 -5
  403. package/src/__tests__/shader-manifest-fixture.ts +39 -0
  404. package/src/__tests__/skin-motion-regression.unit.test.ts +0 -33
  405. package/src/__tests__/skin-palette-persistent.integration.test.ts +28 -0
  406. package/src/__tests__/skin-palette-persistent.unit.test.ts +247 -0
  407. package/src/__tests__/skinned-shadow-caster.test.ts +118 -14
  408. package/src/__tests__/specular-environment-scale.dawn.test.ts +156 -0
  409. package/src/__tests__/ssr-admission-matrix.integration.test.ts +7 -1
  410. package/src/__tests__/ssr-confidence-compose.unit.test.ts +84 -0
  411. package/src/__tests__/ssr-coordinate-oracle.unit.test.ts +28 -0
  412. package/src/__tests__/ssr-format-probe-gating.integration.test.ts +82 -0
  413. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +91 -0
  414. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +112 -0
  415. package/src/__tests__/ssr-gpu-dispatch.ts +946 -0
  416. package/src/__tests__/ssr-graph-topology.unit.test.ts +104 -0
  417. package/src/__tests__/ssr-history-filter.dawn.test.ts +198 -0
  418. package/src/__tests__/ssr-hit-color.dawn.test.ts +273 -0
  419. package/src/__tests__/ssr-hiz-odd-reduction.dawn.test.ts +569 -0
  420. package/src/__tests__/ssr-hiz-rescue.dawn.test.ts +279 -0
  421. package/src/__tests__/ssr-hiz.browser.test.ts +150 -0
  422. package/src/__tests__/ssr-hiz.dawn.test.ts +150 -0
  423. package/src/__tests__/ssr-hiz.unit.test.ts +56 -0
  424. package/src/__tests__/ssr-inspection.unit.test.ts +105 -0
  425. package/src/__tests__/ssr-memory-budget.unit.test.ts +45 -0
  426. package/src/__tests__/ssr-performance-sequence-aggregation.unit.test.ts +15 -0
  427. package/src/__tests__/ssr-public-surface.test-d.ts +37 -0
  428. package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +387 -0
  429. package/src/__tests__/ssr-reflection-mip-reducer.dawn.test.ts +309 -0
  430. package/src/__tests__/ssr-reprojection-probe.ts +90 -0
  431. package/src/__tests__/ssr-roughness-mip.dawn.test.ts +163 -0
  432. package/src/__tests__/ssr-shader-prewarm.unit.test.ts +59 -0
  433. package/src/__tests__/ssr-spatial.integration.test.ts +141 -0
  434. package/src/__tests__/ssr-stable-lattice.dawn.test.ts +475 -0
  435. package/src/__tests__/ssr-temporal-history.unit.test.ts +202 -0
  436. package/src/__tests__/ssr-texel-crossing.dawn.test.ts +448 -0
  437. package/src/__tests__/ssr-zero-work.integration.test.ts +87 -0
  438. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +44 -12
  439. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +29 -0
  440. package/src/__tests__/standard-output-chain-contract.unit.test.ts +99 -0
  441. package/src/__tests__/standard-output-chain.integration.test.ts +73 -0
  442. package/src/__tests__/standard-output-gpu-evidence.ts +205 -0
  443. package/src/__tests__/standard-output.browser.test.ts +15 -0
  444. package/src/__tests__/standard-output.dawn.test.ts +15 -0
  445. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +38 -0
  446. package/src/__tests__/standard-pipeline.integration.test.ts +320 -3
  447. package/src/__tests__/standard-profile-oracle.unit.test.ts +22 -0
  448. package/src/__tests__/standard-surface-lighting.unit.test.ts +41 -0
  449. package/src/__tests__/standard-texture-specialization.dawn.test.ts +141 -0
  450. package/src/__tests__/surface-adjacent-regressions.integration.test.ts +6 -3
  451. package/src/__tests__/taa-coverage-edge.dawn.test.ts +193 -0
  452. package/src/__tests__/taa-history-clipping.dawn.test.ts +328 -0
  453. package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +778 -0
  454. package/src/__tests__/taa-stability-feedback.browser.test.ts +23 -0
  455. package/src/__tests__/taa-stability-feedback.dawn.test.ts +68 -0
  456. package/src/__tests__/taa-stability-feedback.ts +341 -0
  457. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +49 -7
  458. package/src/__tests__/temporal-reset-matrix.unit.test.ts +9 -0
  459. package/src/__tests__/temporal-transaction.integration.test.ts +28 -0
  460. package/src/__tests__/texture-usage-surface.unit.test.ts +4 -2
  461. package/src/__tests__/typed-pipeline-topology.unit.test.ts +109 -4
  462. package/src/__tests__/typed-scene-pass-material-cache.integration.test.ts +126 -0
  463. package/src/__tests__/vertex-layouts-ssot.unit.test.ts +47 -0
  464. package/src/__tests__/visibility-extract.perf.test.ts +20 -8
  465. package/src/__tests__/visibility-instances.integration.test.ts +19 -7
  466. package/src/__tests__/visibility-producer-matrix.integration.test.ts +20 -9
  467. package/src/assembly/atmosphere-shader-prewarm.ts +38 -0
  468. package/src/assembly/dynamic-geometry-host.ts +48 -0
  469. package/src/assembly/dynamic-geometry-runtime.ts +71 -0
  470. package/src/assembly/factory.ts +4 -0
  471. package/src/assembly/gpu-driven-owner.ts +29 -0
  472. package/src/assembly/host-contract.ts +10 -0
  473. package/src/assembly/material/assembly.ts +38 -0
  474. package/src/assembly/material-shader-policy.ts +71 -28
  475. package/src/assembly/recovery/failure-location.ts +62 -0
  476. package/src/assembly/recovery/renderer-recover.ts +19 -17
  477. package/src/assembly/renderer-facade.ts +12 -0
  478. package/src/assembly/renderer-feature-inspection.ts +13 -0
  479. package/src/assembly/renderer-frame-transaction.ts +63 -0
  480. package/src/assembly/renderer-helpers.ts +26 -0
  481. package/src/assembly/renderer-inspection.ts +6 -0
  482. package/src/assembly/shader-prewarm-policy.ts +32 -0
  483. package/src/assembly/skin-palette-owner.ts +21 -0
  484. package/src/assembly/ssr-shader-prewarm.ts +41 -0
  485. package/src/assembly/webgpu-device-loss.ts +75 -0
  486. package/src/assembly/webgpu-pbr-ready.ts +232 -0
  487. package/src/assembly/webgpu-ready-mipmap.ts +26 -0
  488. package/src/assembly/webgpu-ready.ts +142 -121
  489. package/src/assembly/webgpu-renderer-bootstrap.ts +85 -0
  490. package/src/assembly/webgpu-renderer-contract.ts +69 -0
  491. package/src/assembly/webgpu-renderer-guards.ts +32 -0
  492. package/src/assembly/webgpu-renderer-observation.ts +43 -0
  493. package/src/assembly/webgpu-renderer-recovery-failure.ts +62 -0
  494. package/src/assembly/webgpu-renderer.ts +666 -652
  495. package/src/assembly/webgpu-vertex-layouts.ts +233 -87
  496. package/src/components/__tests__/camera-exposure.contract.unit.test.ts +153 -0
  497. package/src/components/camera.ts +287 -7
  498. package/src/components/index.ts +1 -0
  499. package/src/components/instances.ts +45 -18
  500. package/src/components/scene-instance.ts +2 -1
  501. package/src/components/screen-space-reflection.ts +21 -0
  502. package/src/components/sprite-animation.ts +2 -2
  503. package/src/components/sprite-instances.ts +1 -1
  504. package/src/device/gpu-residency.ts +34 -31
  505. package/src/environment/background.ts +263 -0
  506. package/src/errors/gpu-driven.ts +39 -5
  507. package/src/errors/render.ts +27 -3
  508. package/src/extract/camera.ts +26 -1
  509. package/src/extract/gpu-driven.ts +185 -10
  510. package/src/features/host.ts +237 -19
  511. package/src/features/noise-texture.ts +91 -0
  512. package/src/features/plan.ts +553 -24
  513. package/src/features/prepared-gpu-work.ts +631 -49
  514. package/src/features/prepared-graphics-store.ts +31 -0
  515. package/src/features/prepared-graphics.ts +5 -0
  516. package/src/features/render-graph-compute.ts +41 -8
  517. package/src/features/render-graph-contribution.ts +141 -3
  518. package/src/features/render-graph-raster.ts +143 -30
  519. package/src/features/types.ts +11 -0
  520. package/src/fullscreen-post-process-pass.ts +3 -0
  521. package/src/gpu-driven/batch-topology.ts +424 -102
  522. package/src/gpu-driven/pbr-program.ts +31 -0
  523. package/src/gpu-driven/prepared-draw.ts +29 -4
  524. package/src/gpu-driven/production-raster.ts +2267 -694
  525. package/src/gpu-driven/shadow-views.ts +539 -0
  526. package/src/gpu-driven/view-gpu.ts +188 -41
  527. package/src/gpu-scene-schema.ts +52 -11
  528. package/src/gpu-scene.ts +186 -99
  529. package/src/gpu-texture-usage.ts +1 -0
  530. package/src/hdrp-buffers.ts +72 -36
  531. package/src/ibl/frame-resources.ts +38 -0
  532. package/src/ibl/skylight-bind-group.ts +5 -4
  533. package/src/index.ts +109 -1
  534. package/src/inspection-types.ts +191 -0
  535. package/src/instances-derived-bounds.ts +258 -34
  536. package/src/instances.ts +459 -150
  537. package/src/internal.ts +22 -0
  538. package/src/lifecycle.ts +2 -0
  539. package/src/material-row.ts +145 -0
  540. package/src/materials.ts +44 -8
  541. package/src/pbr-pipeline.ts +159 -17
  542. package/src/pipeline/standard-forward-lane.ts +52 -24
  543. package/src/pipeline/standard-output/__tests__/lut-admission.unit.test.ts +103 -0
  544. package/src/pipeline/standard-output/auto-exposure/__tests__/capability.unit.test.ts +40 -0
  545. package/src/pipeline/standard-output/auto-exposure/__tests__/gpu.contract.unit.test.ts +125 -0
  546. package/src/pipeline/standard-output/auto-exposure/__tests__/graph.contract.unit.test.ts +94 -0
  547. package/src/pipeline/standard-output/auto-exposure/__tests__/oracle.test.ts +108 -0
  548. package/src/pipeline/standard-output/auto-exposure/__tests__/preset.test.ts +26 -0
  549. package/src/pipeline/standard-output/auto-exposure/capability.ts +38 -0
  550. package/src/pipeline/standard-output/auto-exposure/gpu.ts +567 -0
  551. package/src/pipeline/standard-output/auto-exposure/graph.ts +221 -0
  552. package/src/pipeline/standard-output/auto-exposure/inspection.ts +231 -0
  553. package/src/pipeline/standard-output/auto-exposure/oracle.ts +291 -0
  554. package/src/pipeline/standard-output/auto-exposure/preset.ts +28 -0
  555. package/src/pipeline/standard-output/auto-exposure/state.ts +156 -0
  556. package/src/pipeline/standard-output/color-transform.ts +124 -0
  557. package/src/pipeline/standard-output/graph.ts +139 -0
  558. package/src/pipeline/standard-output/lut-admission.ts +121 -0
  559. package/src/pipeline/standard-output/lut-gpu.ts +399 -0
  560. package/src/pipeline/standard-output/lut-state.ts +176 -0
  561. package/src/pipeline/standard-output/resources.ts +121 -0
  562. package/src/pipeline/standard-output/types.ts +58 -0
  563. package/src/pipeline/standard-pipeline.ts +192 -59
  564. package/src/pipeline/standard-post.ts +186 -27
  565. package/src/pipeline-builder.ts +16 -6
  566. package/src/pipeline-spec-types.ts +4 -0
  567. package/src/pipeline-spec.ts +37 -29
  568. package/src/points-lines/standard-owner.ts +6 -14
  569. package/src/prepare/prepared-graphics-resolver.ts +9 -2
  570. package/src/record/__tests__/material-slot-plan.unit.test.ts +48 -0
  571. package/src/record/__tests__/mesh-ssbo.unit.test.ts +40 -1
  572. package/src/record/__tests__/recovery-pipeline.unit.test.ts +2 -29
  573. package/src/record/__tests__/sprite-material-abi.unit.test.ts +36 -5
  574. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +45 -0
  575. package/src/record/__tests__/view-ubo-layout.unit.test.ts +5 -0
  576. package/src/record/dynamic-geometry-consumption.ts +59 -0
  577. package/src/record/frame-lighting.ts +12 -11
  578. package/src/record/frame-snapshot.ts +133 -23
  579. package/src/record/frame-targets.ts +9 -0
  580. package/src/record/frame.ts +725 -133
  581. package/src/record/main-pass-geometry.ts +149 -94
  582. package/src/record/main-pass-material.ts +115 -26
  583. package/src/record/main-pass-sprite-draws.ts +16 -107
  584. package/src/record/main-pass.ts +78 -8
  585. package/src/record/material-uniforms.ts +1 -1
  586. package/src/record/mesh-ssbo.ts +59 -15
  587. package/src/record/prepared-material-bindings.ts +234 -0
  588. package/src/record/recovery-pipeline.ts +59 -136
  589. package/src/record/render-context.ts +90 -22
  590. package/src/record/shadow-pass.ts +1231 -209
  591. package/src/record/skybox-post-pass.ts +14 -3
  592. package/src/record/typed-frame-graph.ts +1004 -44
  593. package/src/record/view-ubo.ts +11 -0
  594. package/src/recovery/render-system-candidate.ts +90 -66
  595. package/src/recovery/render-system-roots.ts +113 -0
  596. package/src/recovery/types.ts +77 -0
  597. package/src/reflection/record-owner.ts +26 -54
  598. package/src/render-contract.ts +195 -25
  599. package/src/render-graph-primitives.ts +6 -2
  600. package/src/render-pipeline.ts +70 -1
  601. package/src/render-system-extract-tail.ts +409 -160
  602. package/src/render-system-extract.ts +376 -102
  603. package/src/render-system-projections.ts +241 -0
  604. package/src/render-system.ts +681 -822
  605. package/src/scene/render-scene-types.ts +13 -4
  606. package/src/scene/render-scene.ts +569 -355
  607. package/src/scene/shadow-visibility.ts +21 -0
  608. package/src/ssr/admission.ts +380 -14
  609. package/src/ssr/compose.ts +144 -0
  610. package/src/ssr/errors.ts +48 -0
  611. package/src/ssr/graph.ts +777 -0
  612. package/src/ssr/history.ts +436 -0
  613. package/src/ssr/hiz.ts +124 -0
  614. package/src/ssr/inspection.ts +124 -0
  615. package/src/ssr/resources.ts +202 -0
  616. package/src/ssr/temporal.ts +164 -0
  617. package/src/systems/skin-palette-allocator.ts +495 -148
  618. package/src/systems/skin-palette-types.ts +17 -0
  619. package/src/systems/transparent-dispatch.ts +90 -0
  620. package/src/targets/material-source.ts +34 -0
  621. package/src/temporal/coverage.ts +1 -1
  622. package/src/temporal/frame-coordinator.ts +23 -2
  623. package/src/temporal/gpu.ts +74 -12
  624. package/src/temporal/history.ts +3 -1
  625. package/src/temporal/inspection.ts +2 -1
  626. package/src/temporal/standard-scene-data.ts +3 -1
  627. package/src/temporal/temporal-view.ts +3 -0
  628. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +204 -139
  629. package/src/typed-render-graph-primitives.ts +266 -74
  630. package/src/typed-shadow-passes.ts +122 -12
  631. package/src/volume/temporal.ts +10 -0
  632. package/dist/chunk-GE3SDD72.mjs.map +0 -1
  633. package/dist/chunk-GF523LHF.mjs.map +0 -1
  634. package/dist/chunk-IQMKJLNU.mjs.map +0 -1
  635. package/dist/chunk-J52NMQKR.mjs.map +0 -1
  636. package/dist/chunk-J6KACNOH.mjs.map +0 -1
  637. package/dist/chunk-JAASCMXA.mjs.map +0 -1
  638. package/dist/chunk-MBU4ZVE7.mjs.map +0 -1
  639. package/dist/chunk-RBDBZ5R7.mjs.map +0 -1
  640. package/dist/chunk-VSL23LZE.mjs.map +0 -1
  641. package/src/__tests__/instances-world-ownership.integration.test.ts +0 -62
  642. package/src/scene/__tests__/changed-block-summary.candidate.test.ts +0 -36
@@ -27,6 +27,7 @@ import type {
27
27
  RenderPipelineTopology,
28
28
  } from '../render-pipeline';
29
29
  import { resolveOutputDither } from '../render-pipeline';
30
+ import { admitSsrSpatial } from '../ssr/admission';
30
31
  import { standardTemporalPostOrder } from '../temporal/standard-scene-data';
31
32
 
32
33
  let device: RhiDevice;
@@ -178,10 +179,10 @@ function disabledDirectionalVolumeTopology(lightKind: 'point' | 'spot'): RenderP
178
179
  }
179
180
 
180
181
  function createImportedHistory(graph: RenderGraphBuilder<RenderPipelineFrame>) {
181
- const target = (label: string) => {
182
+ const target = (label: string, format: 'rgba16float' | 'r8unorm' = 'rgba16float') => {
182
183
  const texture = graph.importTexture(
183
184
  label,
184
- { format: 'rgba16float', size: 'surface', usage: 0x17 },
185
+ { format, size: 'surface', usage: 0x17 },
185
186
  (frame) => frame.currentTexture,
186
187
  );
187
188
  if (!texture.ok) throw texture.error;
@@ -190,7 +191,7 @@ function createImportedHistory(graph: RenderGraphBuilder<RenderPipelineFrame>) {
190
191
  return {
191
192
  texture: texture.value,
192
193
  view: view.value,
193
- format: 'rgba16float' as const,
194
+ format,
194
195
  sampleCount: 1 as const,
195
196
  };
196
197
  };
@@ -199,12 +200,15 @@ function createImportedHistory(graph: RenderGraphBuilder<RenderPipelineFrame>) {
199
200
  previousColor: target('taa-history-previous-color'),
200
201
  currentTemporal: target('taa-history-current-temporal'),
201
202
  previousTemporal: target('taa-history-previous-temporal'),
203
+ currentStability: target('taa-history-current-stability', 'r8unorm'),
204
+ previousStability: target('taa-history-previous-stability', 'r8unorm'),
202
205
  };
203
206
  }
204
207
 
205
208
  describe('typed built-in pipeline topology', () => {
206
209
  it('defaults final output dither on and accepts an asset-level opt-out', () => {
207
210
  expect(resolveOutputDither(undefined)).toBe(true);
211
+ expect(resolveOutputDither({})).toBe(true);
208
212
  expect(resolveOutputDither({ outputDither: true })).toBe(true);
209
213
  expect(resolveOutputDither({ outputDither: false })).toBe(false);
210
214
  });
@@ -466,6 +470,14 @@ describe('typed built-in pipeline topology', () => {
466
470
  new URL('../render-graph-primitives.ts', import.meta.url),
467
471
  'utf8',
468
472
  );
473
+ const fxaaOwnerSource = readFileSync(
474
+ new URL('../record/skybox-post-pass.ts', import.meta.url),
475
+ 'utf8',
476
+ );
477
+ const standardPostSource = readFileSync(
478
+ new URL('../pipeline/standard-post.ts', import.meta.url),
479
+ 'utf8',
480
+ );
469
481
  expect(typedSource).toContain(
470
482
  'paramsOverride: options.paramsTransform(frame.postProcessParams.get(options.shader))',
471
483
  );
@@ -474,6 +486,28 @@ describe('typed built-in pipeline topology', () => {
474
486
  'const data = paramsOverride ?? ctx.postProcessParams.get(shader);',
475
487
  );
476
488
  expect(dispatcherSource).toContain('input.paramsOverride,');
489
+ expect(fxaaOwnerSource).toContain('paramsOverride?.byteLength === 16');
490
+ expect(fxaaOwnerSource).toContain(
491
+ 'resolveOutputDither(c.frameState.installedPipelineConfig) ? 1 : 0',
492
+ );
493
+ expect(standardPostSource).toContain('paramsTransform: (): Uint8Array => new Uint8Array(16)');
494
+ expect(standardPostSource).toContain('dither: outputDither');
495
+ });
496
+
497
+ it('keeps the admitted LUT topology linear until the single encoding writer', () => {
498
+ const source = readFileSync(new URL('../pipeline/standard-post.ts', import.meta.url), 'utf8');
499
+ const tone = source.indexOf("name: 'standard-tone'");
500
+ const lut = source.indexOf("'standard-color-lut'");
501
+ const fxaa = source.indexOf("name: 'fxaa'");
502
+ const encoding = source.indexOf("name: 'standard-output-encoding'");
503
+ expect(tone).toBeGreaterThanOrEqual(0);
504
+ expect(lut).toBeGreaterThan(tone);
505
+ expect(fxaa).toBeGreaterThan(lut);
506
+ expect(encoding).toBeGreaterThan(fxaa);
507
+ expect(source).toContain("fragmentEntryPoint: 'fs_tone_only'");
508
+ expect(source).toContain("fragmentEntryPoint: 'fs_encode_only'");
509
+ expect(source).toContain("domain: 'linear-ldr'");
510
+ expect(source).toContain('{ dither: false }');
477
511
  });
478
512
 
479
513
  it('declares stable JSON-safe output and observation inspection facts', () => {
@@ -491,6 +525,7 @@ describe('typed built-in pipeline topology', () => {
491
525
  ]) {
492
526
  expect(source).toContain(field);
493
527
  }
528
+ expect(source).toContain('readonly output: RenderOutputInspection;');
494
529
  expect(source).not.toContain('pixelPayload');
495
530
  });
496
531
 
@@ -537,7 +572,7 @@ describe('typed built-in pipeline topology', () => {
537
572
  expect.arrayContaining([
538
573
  { resource: 'gbuffer-normal-roughness', usage: 'sampled-read' },
539
574
  { resource: 'gbuffer-albedo-metallic', usage: 'sampled-read' },
540
- { resource: 'gbuffer-emissive-ao', usage: 'sampled-read' },
575
+ { resource: 'gbuffer-specular-response-ao', usage: 'sampled-read' },
541
576
  { resource: 'ssao-blurred', usage: 'sampled-read' },
542
577
  { resource: 'hdrp-scene-color', usage: 'color-attachment' },
543
578
  ]),
@@ -676,6 +711,76 @@ describe('typed built-in pipeline topology', () => {
676
711
  );
677
712
  });
678
713
 
714
+ it('feeds the SSR source-reactivity producer to final TAA without a second mask resource', () => {
715
+ const ssr = admitSsrSpatial({
716
+ camera: {
717
+ projection: 'perspective',
718
+ near: 0.1,
719
+ far: 100,
720
+ screenSpaceReflection: { maxDistance: 12, thickness: 0.2, maxRoughness: 0.65 },
721
+ },
722
+ environment: {
723
+ lane: 'deferred',
724
+ m0: { status: 'admitted' },
725
+ sceneInputs: true,
726
+ temporal: true,
727
+ reflectionFallback: true,
728
+ capabilities: {
729
+ compute: true,
730
+ storageTexture: true,
731
+ rgba16floatRenderable: true,
732
+ r32floatSampledStorage: true,
733
+ },
734
+ },
735
+ });
736
+ const compiled = compile(
737
+ standardPipeline,
738
+ topology('forgeax::standard', {
739
+ standardProfile: { ...DEFAULT_STANDARD_PROFILE, renderPath: 'deferred' },
740
+ camera: { tonemap: 'aces-filmic', antialias: 'taa', bloom: 'off' },
741
+ reflectionFallback: { enabled: true },
742
+ ssr,
743
+ }),
744
+ ).unwrap();
745
+ const info = compiled.inspect();
746
+ expect(info.resources.filter((r) => r.label === 'ssr-hit-reactivity')).toHaveLength(1);
747
+ expect(info.passes.find((p) => p.name === 'ssr-trace')?.accesses).toContainEqual({
748
+ resource: 'ssr-hit-reactivity',
749
+ usage: 'storage-write',
750
+ });
751
+ expect(info.passes.find((p) => p.name === 'taa-resolve')?.accesses).toContainEqual({
752
+ resource: 'ssr-hit-reactivity',
753
+ usage: 'sampled-read',
754
+ });
755
+ const names = info.passes.map((p) => p.name);
756
+ expect(names.indexOf('ssr-trace')).toBeLessThan(names.indexOf('taa-resolve'));
757
+ });
758
+
759
+ it('pairs post-TAA Motion Blur color with resolved temporal coverage', () => {
760
+ const compiled = compile(
761
+ standardPipeline,
762
+ topology('forgeax::standard', {
763
+ camera: { tonemap: 'aces-filmic', antialias: 'taa', bloom: 'off' },
764
+ temporal: { taa: true, motionBlur: true },
765
+ }),
766
+ );
767
+ expect(compiled.ok).toBe(true);
768
+ if (!compiled.ok) return;
769
+ const accesses = compiled.value
770
+ .inspect()
771
+ .passes.find((pass) => pass.name === 'motion-blur')?.accesses;
772
+ expect(accesses).toEqual(
773
+ expect.arrayContaining([
774
+ { resource: 'taa-history-current-color', usage: 'sampled-read' },
775
+ { resource: 'taa-history-current-temporal', usage: 'sampled-read' },
776
+ ]),
777
+ );
778
+ expect(accesses).not.toContainEqual({
779
+ resource: 'standard-scene-temporal',
780
+ usage: 'sampled-read',
781
+ });
782
+ });
783
+
679
784
  it('fails closed when TAA history imports are missing', () => {
680
785
  const graph = new RenderGraphBuilder<RenderPipelineFrame>();
681
786
  const standardLighting = zeroStandardLighting({ compute: true, storageBuffer: true });
@@ -0,0 +1,126 @@
1
+ import { RenderGraphBuilder } from '@forgeax/engine-render-graph';
2
+ import type { RhiDevice } from '@forgeax/engine-rhi';
3
+ import { rhi } from '@forgeax/engine-rhi-null';
4
+ import type { PassSelector } from '@forgeax/engine-types';
5
+ import { describe, expect, it, vi } from 'vitest';
6
+ import type { RenderPipelineFrame } from '../render-pipeline';
7
+ import { createRenderPipelineTarget } from '../render-pipeline';
8
+
9
+ const { encodeMainPass, buildPerFrameBindGroups } = vi.hoisted(() => ({
10
+ encodeMainPass: vi.fn(),
11
+ buildPerFrameBindGroups: vi.fn(),
12
+ }));
13
+
14
+ vi.mock('../record/main-pass', () => ({ encodeMainPass }));
15
+ vi.mock('../record/frame-lighting', () => ({ buildPerFrameBindGroups }));
16
+
17
+ import { addTypedScenePass } from '../typed-render-graph-primitives';
18
+
19
+ function targetDescriptor(format: 'rgba8unorm' | 'depth24plus-stencil8') {
20
+ return { format, size: { width: 1, height: 1 } };
21
+ }
22
+
23
+ function frameFor(device: RhiDevice, materialSlots: readonly object[]): RenderPipelineFrame {
24
+ return {
25
+ encoder: device.createCommandEncoder({ label: 'material-cache-test' }).unwrap(),
26
+ runtime: {
27
+ device,
28
+ errorRegistry: { fire: vi.fn() },
29
+ },
30
+ frameState: {},
31
+ pipelineState: { colorAttachmentFormat: 'rgba8unorm' },
32
+ validated: [],
33
+ validatedOrdered: [],
34
+ materialSlots,
35
+ materialSlotCount: materialSlots.length,
36
+ bindGroupCounts: {},
37
+ } as unknown as RenderPipelineFrame;
38
+ }
39
+
40
+ describe('typed scene pass material payload cache', () => {
41
+ it('reuses one frame owner cache across scene passes and refreshes on a new frame', async () => {
42
+ const adapter = (await rhi.requestAdapter()).unwrap();
43
+ const device = (await adapter.requestDevice()).unwrap();
44
+ buildPerFrameBindGroups.mockReturnValue({
45
+ viewBindGroup: null,
46
+ meshBindGroup: null,
47
+ hdrpClusterBindGroup: null,
48
+ hdrpClusterMembershipBindGroup: null,
49
+ });
50
+
51
+ const materialA = [{ id: 'A' }];
52
+ const materialB = [{ id: 'B' }];
53
+ const payloadA = new Uint8Array([1]);
54
+ const payloadB = new Uint8Array([2]);
55
+ const seenCacheInputs: Array<unknown> = [];
56
+ encodeMainPass.mockImplementation(
57
+ (
58
+ context: {
59
+ materialSlots: readonly object[];
60
+ materialSlotCount: number;
61
+ materialUboPayloadCache?: {
62
+ materialSlots: readonly object[];
63
+ materialSlotCount: number;
64
+ payload: Uint8Array;
65
+ };
66
+ },
67
+ _pass: { end(): void },
68
+ ) => {
69
+ seenCacheInputs.push(context.materialUboPayloadCache);
70
+ const payload = context.materialSlots === materialA ? payloadA : payloadB;
71
+ if (
72
+ context.materialUboPayloadCache?.materialSlots !== context.materialSlots ||
73
+ context.materialUboPayloadCache?.materialSlotCount !== context.materialSlotCount
74
+ ) {
75
+ context.materialUboPayloadCache = {
76
+ materialSlots: context.materialSlots,
77
+ materialSlotCount: context.materialSlotCount,
78
+ payload,
79
+ };
80
+ }
81
+ },
82
+ );
83
+
84
+ const graph = new RenderGraphBuilder<RenderPipelineFrame>();
85
+ const color = createRenderPipelineTarget(
86
+ graph,
87
+ 'material-cache-color',
88
+ targetDescriptor('rgba8unorm'),
89
+ ).unwrap();
90
+ const depth = createRenderPipelineTarget(
91
+ graph,
92
+ 'material-cache-depth',
93
+ targetDescriptor('depth24plus-stencil8'),
94
+ ).unwrap();
95
+ const options = {
96
+ name: 'material-cache-scene',
97
+ color,
98
+ depth,
99
+ selector: {} as PassSelector,
100
+ };
101
+ expect(addTypedScenePass(graph, options).ok).toBe(true);
102
+ expect(addTypedScenePass(graph, { ...options, name: 'material-cache-scene-2' }).ok).toBe(true);
103
+ const compiled = graph.compile({ device, surfaceSize: { width: 1, height: 1 } }).unwrap();
104
+
105
+ const firstFrame = frameFor(device, materialA);
106
+ const firstExecution = compiled.execute(firstFrame);
107
+ expect(firstExecution.ok).toBe(true);
108
+ expect(encodeMainPass).toHaveBeenCalledTimes(2);
109
+ expect(seenCacheInputs).toEqual([undefined, expect.objectContaining({ payload: payloadA })]);
110
+ const firstCache = (firstFrame as unknown as { materialUboPayloadCache?: unknown })
111
+ .materialUboPayloadCache;
112
+ expect(seenCacheInputs[1]).toBe(firstCache);
113
+ expect((firstCache as { payload: Uint8Array }).payload).toBe(payloadA);
114
+
115
+ encodeMainPass.mockClear();
116
+ seenCacheInputs.length = 0;
117
+ const nextFrame = frameFor(device, materialB);
118
+ const nextExecution = compiled.execute(nextFrame);
119
+ expect(nextExecution.ok).toBe(true);
120
+ expect(seenCacheInputs).toEqual([undefined, expect.objectContaining({ payload: payloadB })]);
121
+ const nextCache = (nextFrame as unknown as { materialUboPayloadCache?: unknown })
122
+ .materialUboPayloadCache;
123
+ expect(seenCacheInputs[1]).toBe(nextCache);
124
+ expect((nextCache as { payload: Uint8Array }).payload).toBe(payloadB);
125
+ });
126
+ });
@@ -0,0 +1,47 @@
1
+ import { DEFAULT_VERTEX_ATTRIBUTE_MAP, deriveVertexBufferLayout } from '@forgeax/engine-geometry';
2
+ import { describe, expect, it } from 'vitest';
3
+ import {
4
+ BILLBOARD_MATERIAL_INSTANCE_VERTEX_BUFFERS,
5
+ MESH_GEOMETRY_MATERIAL_INSTANCE_VERTEX_BUFFERS,
6
+ POSITION_SIZE_COLOR_INSTANCE_VERTEX_BUFFERS,
7
+ TOPOLOGY_SEGMENT_INSTANCE_VERTEX_BUFFERS,
8
+ toGpuVertexBufferLayouts,
9
+ } from '../assembly/webgpu-vertex-layouts';
10
+
11
+ const FLOAT32_COMPONENT_COUNT = {
12
+ float32x2: 2,
13
+ float32x3: 3,
14
+ float32x4: 4,
15
+ } as const;
16
+
17
+ describe('prepared feature vertex layout SSOT', () => {
18
+ it('derives the mesh vertex stream from geometry layout ownership', () => {
19
+ const [expected] = toGpuVertexBufferLayouts(
20
+ deriveVertexBufferLayout(DEFAULT_VERTEX_ATTRIBUTE_MAP),
21
+ );
22
+ const [actual] = MESH_GEOMETRY_MATERIAL_INSTANCE_VERTEX_BUFFERS;
23
+
24
+ expect(actual).toEqual({ ...expected, stepMode: 'vertex' });
25
+ });
26
+
27
+ it.each([
28
+ ['position-size-color', POSITION_SIZE_COLOR_INSTANCE_VERTEX_BUFFERS],
29
+ ['billboard-material', BILLBOARD_MATERIAL_INSTANCE_VERTEX_BUFFERS],
30
+ ['topology-segment', TOPOLOGY_SEGMENT_INSTANCE_VERTEX_BUFFERS],
31
+ ['mesh-material-instance', [MESH_GEOMETRY_MATERIAL_INSTANCE_VERTEX_BUFFERS[1]]],
32
+ ] as const)('%s derives stride from its declared attributes', (_name, buffers) => {
33
+ for (const buffer of buffers) {
34
+ if (buffer === undefined) {
35
+ throw new Error('vertex layout fixture is missing its buffer declaration');
36
+ }
37
+ const end = Math.max(
38
+ ...buffer.attributes.map(
39
+ (attribute) =>
40
+ attribute.offset +
41
+ FLOAT32_COMPONENT_COUNT[attribute.format as keyof typeof FLOAT32_COMPONENT_COUNT] * 4,
42
+ ),
43
+ );
44
+ expect(buffer.arrayStride).toBe(end);
45
+ }
46
+ });
47
+ });
@@ -11,6 +11,12 @@ import { RenderScene } from '../scene/render-scene';
11
11
 
12
12
  const ENTITY_COUNT = 10_000;
13
13
  const SAMPLE_COUNT = 101;
14
+ const LIGHTWEIGHT_RENDER_SCENE_PERF = process.env.FORGEAX_RENDER_PERF_LIGHTWEIGHT === '1';
15
+ // The full 100k scene remains the local/nightly scaling check. CI keeps the
16
+ // same delta/no-change assertions at a bounded 10k population so a standard
17
+ // runner does not spend its memory budget constructing a second large graph.
18
+ const RENDER_SCENE_ENTITY_COUNT = LIGHTWEIGHT_RENDER_SCENE_PERF ? 10_000 : 100_000;
19
+ const RENDER_SCENE_DELTA_COUNT = LIGHTWEIGHT_RENDER_SCENE_PERF ? 1_000 : 10_000;
14
20
 
15
21
  const sceneMaterial = {} as MaterialSnapshot;
16
22
 
@@ -88,7 +94,7 @@ function measureWorlds(worlds: readonly World[]): number[] {
88
94
  return measureWorldSamples(worlds).map((values) => median(values));
89
95
  }
90
96
 
91
- describe('visibility extraction performance (10k)', () => {
97
+ describe(`visibility extraction performance (10k + RenderScene ${RENDER_SCENE_ENTITY_COUNT / 1_000}k)`, () => {
92
98
  it('keeps inherited hierarchy extraction within the 10 percent budget', () => {
93
99
  const flat = buildWorld(false, false);
94
100
  const hierarchy = buildWorld(false, true);
@@ -104,27 +110,33 @@ describe('visibility extraction performance (10k)', () => {
104
110
  expect(overhead).toBeLessThanOrEqual(0.1);
105
111
  }, 60_000);
106
112
 
107
- it('keeps RenderScene delta and no-change evidence on the 100k workload', () => {
113
+ it(`keeps RenderScene delta and no-change evidence on the ${RENDER_SCENE_ENTITY_COUNT / 1_000}k workload`, () => {
108
114
  const scene = new RenderScene();
109
- scene.reset(Array.from({ length: 100_000 }, (_, entityKey) => sceneSnapshot(entityKey)));
115
+ scene.reset(
116
+ Array.from({ length: RENDER_SCENE_ENTITY_COUNT }, (_, entityKey) => sceneSnapshot(entityKey)),
117
+ );
110
118
  const stable = scene.materialize();
111
119
  const noChange = scene.apply([]);
112
120
  const delta = scene.apply(
113
- Array.from({ length: 10_000 }, (_, entityKey) => {
121
+ Array.from({ length: RENDER_SCENE_DELTA_COUNT }, (_, entityKey) => {
114
122
  const previous = stable[entityKey];
115
123
  const world =
116
124
  previous === undefined
117
125
  ? new Float32Array(16)
118
126
  : new Float32Array(previous.transform.world);
119
127
  world[12] = (world[12] ?? 0) + 1;
120
- return { kind: 'update-transform' as const, worldId: 0, entityKey, world };
128
+ return { kind: 'update' as const, worldId: 0, entityKey, world };
121
129
  }),
122
130
  );
123
131
 
124
132
  expect(noChange).toMatchObject({ updated: 0, resynced: 0 });
125
- expect(delta).toMatchObject({ updated: 10_000, resynced: 0 });
126
- expect(scene.inspect()).toMatchObject({ renderableScans: 10_000 });
127
- expect(scene.materialize()).not.toBe(stable);
133
+ expect(delta).toMatchObject({ updated: RENDER_SCENE_DELTA_COUNT, resynced: 0 });
134
+ expect(scene.inspect()).toMatchObject({ renderableScans: RENDER_SCENE_DELTA_COUNT });
135
+ // Transform-only updates patch the retained snapshot in place so the
136
+ // unified scene flow does not allocate a second large publication.
137
+ expect(scene.materialize()).toBe(stable);
138
+ expect(stable[0]?.transform.world[12]).toBe(1);
139
+ expect(stable[RENDER_SCENE_DELTA_COUNT - 1]?.transform.world[12]).toBe(1_000);
128
140
  expect(scene.querySpatial({ min: [-1, -1, -1], max: [1, 1, 1] }).length).toBeGreaterThan(0);
129
141
  }, 60_000);
130
142
  });
@@ -6,17 +6,23 @@ 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';
9
10
  import { extractFrames } from '../render-system-extract';
10
11
 
11
12
  const IDENTITY = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
12
13
 
13
- function spawnInstance(world: World, state: keyof typeof VisibilityStateValue) {
14
+ function spawnInstance(
15
+ world: World,
16
+ instanceCollections: InstanceCollectionStore,
17
+ state: keyof typeof VisibilityStateValue,
18
+ ) {
19
+ const collectionId = instanceCollections.create({ transforms: IDENTITY }).unwrap().collectionId;
14
20
  return world
15
21
  .spawn(
16
22
  { component: Transform, data: {} },
17
23
  { component: MeshFilter, data: { assetHandle: HANDLE_CUBE } },
18
24
  { component: MeshRenderer, data: {} },
19
- { component: Instances, data: { transforms: new Float32Array(IDENTITY) } },
25
+ { component: Instances, data: { collectionId } },
20
26
  { component: Visibility, data: { state: VisibilityStateValue[state] } },
21
27
  )
22
28
  .unwrap();
@@ -25,9 +31,12 @@ function spawnInstance(world: World, state: keyof typeof VisibilityStateValue) {
25
31
  describe('visibility instance producer gate', () => {
26
32
  it('gates an explicit Instances owner before producing a renderable', () => {
27
33
  const world = new World();
28
- const entity = spawnInstance(world, 'hidden');
34
+ const instanceCollections = new InstanceCollectionStore();
35
+ const entity = spawnInstance(world, instanceCollections, 'hidden');
29
36
 
30
- const frame = extractFrames([world], 0);
37
+ const frame = extractFrames([world], 0, undefined, undefined, undefined, {
38
+ instanceCollections,
39
+ });
31
40
 
32
41
  expect(frame.renderables.some((item) => item.entityKey === entity)).toBe(false);
33
42
  expect(frame.dispatch.some((item) => item.entityIndex === 0)).toBe(false);
@@ -35,10 +44,13 @@ describe('visibility instance producer gate', () => {
35
44
 
36
45
  it('keeps visible members of a mixed batch independent from hidden members', () => {
37
46
  const world = new World();
38
- spawnInstance(world, 'hidden');
39
- const visible = spawnInstance(world, 'visible');
47
+ const instanceCollections = new InstanceCollectionStore();
48
+ spawnInstance(world, instanceCollections, 'hidden');
49
+ const visible = spawnInstance(world, instanceCollections, 'visible');
40
50
 
41
- const frame = extractFrames([world], 0);
51
+ const frame = extractFrames([world], 0, undefined, undefined, undefined, {
52
+ instanceCollections,
53
+ });
42
54
 
43
55
  expect(frame.renderables.map((item) => item.entityKey)).toEqual([visible]);
44
56
  expect(frame.dispatch.length).toBe(2);
@@ -11,6 +11,7 @@ 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';
14
15
  import { extractFrames } from '../render-system-extract';
15
16
  import { classifySceneDataCoverage } from '../temporal/coverage';
16
17
  import { tilemapChunkExtractSystem } from '../tilemap-chunk-extract-system';
@@ -19,7 +20,7 @@ const IDENTITY = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
19
20
 
20
21
  type ProducerCase = {
21
22
  readonly name: string;
22
- readonly setup: (world: World) => EntityHandle;
23
+ readonly setup: (world: World, instanceCollections: InstanceCollectionStore) => EntityHandle;
23
24
  };
24
25
 
25
26
  function staticMesh(world: World): EntityHandle {
@@ -60,25 +61,33 @@ function spriteMesh(world: World): EntityHandle {
60
61
  .unwrap();
61
62
  }
62
63
 
63
- function autoFoldInstances(world: World): EntityHandle {
64
+ function autoFoldInstances(
65
+ world: World,
66
+ instanceCollections: InstanceCollectionStore,
67
+ ): EntityHandle {
68
+ const collectionId = instanceCollections.create({ transforms: IDENTITY }).unwrap().collectionId;
64
69
  return world
65
70
  .spawn(
66
71
  { component: Transform, data: {} },
67
72
  { component: MeshFilter, data: { assetHandle: HANDLE_CUBE } },
68
73
  { component: MeshRenderer, data: {} },
69
- { component: Instances, data: { transforms: IDENTITY } },
74
+ { component: Instances, data: { collectionId } },
70
75
  { component: Visibility, data: { state: VisibilityStateValue.hidden } },
71
76
  )
72
77
  .unwrap();
73
78
  }
74
79
 
75
- function explicitInstances(world: World): EntityHandle {
80
+ function explicitInstances(
81
+ world: World,
82
+ instanceCollections: InstanceCollectionStore,
83
+ ): EntityHandle {
84
+ const collectionId = instanceCollections.create({ transforms: IDENTITY }).unwrap().collectionId;
76
85
  return world
77
86
  .spawn(
78
87
  { component: Transform, data: {} },
79
88
  { component: MeshFilter, data: { assetHandle: HANDLE_CUBE } },
80
89
  { component: MeshRenderer, data: {} },
81
- { component: Instances, data: { transforms: IDENTITY } },
90
+ { component: Instances, data: { collectionId } },
82
91
  { component: Visibility, data: { state: VisibilityStateValue.hidden } },
83
92
  )
84
93
  .unwrap();
@@ -132,18 +141,20 @@ describe('visibility built-in producer matrix', () => {
132
141
 
133
142
  it.each(PRODUCERS)('$name is hidden, visible, then restorable', ({ setup }) => {
134
143
  const world = new World();
135
- const entity = setup(world);
144
+ const instanceCollections = new InstanceCollectionStore();
145
+ const entity = setup(world, instanceCollections);
136
146
 
137
- const hidden = extractFrames([world], 0);
147
+ const options = { instanceCollections };
148
+ const hidden = extractFrames([world], 0, undefined, undefined, undefined, options);
138
149
  expect(hidden.renderables).toHaveLength(0);
139
150
  expect(hidden.dispatch).toHaveLength(0);
140
151
 
141
152
  world.set(entity, Visibility, { state: VisibilityStateValue.visible }).unwrap();
142
- const visible = extractFrames([world], 0);
153
+ const visible = extractFrames([world], 0, undefined, undefined, undefined, options);
143
154
  expect(visible.visibilitySnapshots[0]?.get(entity)?.effective).toBe('visible');
144
155
 
145
156
  world.set(entity, Visibility, { state: VisibilityStateValue.hidden }).unwrap();
146
- const restored = extractFrames([world], 0);
157
+ const restored = extractFrames([world], 0, undefined, undefined, undefined, options);
147
158
  expect(restored.renderables).toHaveLength(0);
148
159
  expect(restored.dispatch).toHaveLength(0);
149
160
  });
@@ -0,0 +1,38 @@
1
+ import { RhiError, type ShaderModule } from '@forgeax/engine-rhi';
2
+ import type { ManifestEntry } from '@forgeax/engine-types';
3
+ import type { AtmosphereShaderSources } from '../render-contract';
4
+
5
+ /** Admit and compile the complete analytic-atmosphere shader pair atomically. */
6
+ export async function prewarmAtmosphereShaders(
7
+ entries: readonly ManifestEntry[],
8
+ compile: (label: string, source: string) => Promise<ShaderModule>,
9
+ seed: (label: string, module: ShaderModule) => void,
10
+ ): Promise<AtmosphereShaderSources | undefined> {
11
+ const stages = [
12
+ ['cube', 'atmosphere_cube', 'atmosphere_cubemap_vs'],
13
+ ['background', 'atmosphere_background', 'atmosphere_background_vs'],
14
+ ] as const;
15
+ const resolved = stages.map(([stage, label, entryPoint]) => ({
16
+ stage,
17
+ label,
18
+ entry: entries.find((entry) => entry.wgsl.includes(`fn ${entryPoint}(`)),
19
+ }));
20
+ if (resolved.every(({ entry }) => entry === undefined)) return undefined;
21
+ const missing = resolved.find(({ entry }) => entry === undefined);
22
+ if (missing !== undefined) {
23
+ throw new RhiError({
24
+ code: 'shader-compile-failed',
25
+ expected: 'shader manifest contains both analytic-atmosphere utility entries',
26
+ hint: `add the package-owned WGSL entry for '${missing.label}' to the engine shader manifest`,
27
+ });
28
+ }
29
+ const compiled: Array<{ label: string; module: ShaderModule }> = [];
30
+ const sources = {} as Record<keyof AtmosphereShaderSources, string>;
31
+ for (const { stage, label, entry } of resolved) {
32
+ if (entry === undefined) throw new Error(`missing atmosphere shader ${label}`);
33
+ compiled.push({ label, module: await compile(label, entry.wgsl) });
34
+ sources[stage] = entry.wgsl;
35
+ }
36
+ for (const { label, module } of compiled) seed(label, module);
37
+ return sources;
38
+ }
@@ -8,6 +8,7 @@ import { MeshRenderer } from '../components/mesh-renderer';
8
8
  import type { GpuResidencyCache } from '../device/gpu-residency';
9
9
  import type { MeshResidencyLease } from '../device/mesh-residency-lifetime';
10
10
  import {
11
+ createDynamicGeometryLifecycle,
11
12
  type DynamicGeometryCandidate,
12
13
  DynamicGeometryError,
13
14
  type DynamicGeometryLifecycle,
@@ -837,3 +838,50 @@ export function createDynamicGeometryHost(options: {
837
838
  },
838
839
  };
839
840
  }
841
+
842
+ /**
843
+ * Renderer assembly adapter for the host's temporal geometry owner.
844
+ *
845
+ * Keeping the render-system callback at this boundary prevents the concrete
846
+ * WebGPU assembly from growing another owner-specific block while preserving
847
+ * the same lifecycle and residency contracts.
848
+ */
849
+ export function createRendererDynamicGeometryHost(options: {
850
+ readonly lifecycle: DynamicGeometryLifecycle;
851
+ readonly attachedWorlds: ReadonlySet<World>;
852
+ readonly getGpuStore: () => GpuResidencyCache;
853
+ readonly currentGeneration: () => number;
854
+ readonly renderSystem: {
855
+ readonly invalidateGeometryHistory: () => void;
856
+ readonly isDynamicGeometryConsumed: (
857
+ world: World,
858
+ entity: number,
859
+ meshHandle: DynamicGeometryCandidate['meshHandle'],
860
+ ) => boolean;
861
+ };
862
+ }): DynamicGeometryHostImplementation {
863
+ const { renderSystem, ...hostOptions } = options;
864
+ return createDynamicGeometryHost({
865
+ ...hostOptions,
866
+ onTopologyChanged: () => renderSystem.invalidateGeometryHistory(),
867
+ isConsumedByRenderFrame: (candidate) =>
868
+ renderSystem.isDynamicGeometryConsumed(
869
+ candidate.world as World,
870
+ candidate.entity ?? -1,
871
+ candidate.meshHandle,
872
+ ),
873
+ });
874
+ }
875
+
876
+ export function createRendererDynamicGeometryController(
877
+ options: Omit<Parameters<typeof createRendererDynamicGeometryHost>[0], 'lifecycle'>,
878
+ ): {
879
+ readonly lifecycle: DynamicGeometryLifecycle;
880
+ readonly host: DynamicGeometryHostImplementation;
881
+ } {
882
+ const lifecycle = createDynamicGeometryLifecycle();
883
+ return {
884
+ lifecycle,
885
+ host: createRendererDynamicGeometryHost({ ...options, lifecycle }),
886
+ };
887
+ }