@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
@@ -0,0 +1,275 @@
1
+ import { RenderGraphBuilder } from '@forgeax/engine-render-graph';
2
+ import type { RhiDevice, TextureFormat } from '@forgeax/engine-rhi';
3
+ import { rhi } from '@forgeax/engine-rhi-null';
4
+ import { SINGLE_LAYER_MEDIUM_RAW_DEPTH_WGSL } from '@forgeax/engine-shader';
5
+ import { beforeAll, describe, expect, it, vi } from 'vitest';
6
+ import {
7
+ addSingleLayerMediumMsaaPairProducer,
8
+ addSingleLayerMediumPasses,
9
+ addSingleLayerMediumRawDepthProducer,
10
+ SINGLE_LAYER_MEDIUM_RAW_DEPTH_POST_PROCESS_ID,
11
+ type SingleLayerMediumPassInput,
12
+ } from '../pipeline/single-layer-medium-passes';
13
+ import type {
14
+ RenderPipelineFrame,
15
+ RenderPipelineSurfaceMediumPair,
16
+ RenderPipelineTarget,
17
+ } from '../render-pipeline';
18
+
19
+ let device: RhiDevice;
20
+
21
+ beforeAll(async () => {
22
+ const adapter = await rhi.requestAdapter();
23
+ if (!adapter.ok) throw adapter.error;
24
+ const created = await adapter.value.requestDevice();
25
+ if (!created.ok) throw created.error;
26
+ device = created.value;
27
+ });
28
+
29
+ function target(
30
+ graph: RenderGraphBuilder<RenderPipelineFrame>,
31
+ label: string,
32
+ format: TextureFormat,
33
+ ): RenderPipelineTarget {
34
+ const texture = graph.createTexture(label, {
35
+ format,
36
+ size: { width: 1, height: 1 },
37
+ sampleCount: 1,
38
+ });
39
+ if (!texture.ok) throw texture.error;
40
+ const view = graph.view(texture.value, { label: `${label}.view` });
41
+ if (!view.ok) throw view.error;
42
+ return { texture: texture.value, view: view.value, format, sampleCount: 1 };
43
+ }
44
+
45
+ function mediumInput(
46
+ graph: RenderGraphBuilder<RenderPipelineFrame>,
47
+ surfacePair: RenderPipelineSurfaceMediumPair,
48
+ ): SingleLayerMediumPassInput {
49
+ return {
50
+ graph,
51
+ size: { width: 1, height: 1, depthOrArrayLayers: 1 },
52
+ nearestLayer: target(graph, 'medium-nearest-layer', 'rgba8unorm'),
53
+ nearestDepth: target(graph, 'medium-nearest-depth', 'depth24plus-stencil8'),
54
+ color: target(graph, 'medium-color', 'rgba8unorm'),
55
+ depth: target(graph, 'scene-depth', 'depth24plus-stencil8'),
56
+ surfacePair,
57
+ };
58
+ }
59
+
60
+ function addColorProducer(
61
+ graph: RenderGraphBuilder<RenderPipelineFrame>,
62
+ output: RenderPipelineTarget,
63
+ name: string,
64
+ ): void {
65
+ const result = graph.addRasterPass(name, {
66
+ accesses: [{ resource: output.view, usage: 'color-attachment' }],
67
+ colorAttachments: [
68
+ {
69
+ view: output.view,
70
+ loadOp: 'clear',
71
+ storeOp: 'store',
72
+ clearValue: { r: 0, g: 0, b: 0, a: 1 },
73
+ },
74
+ ],
75
+ encode: () => undefined,
76
+ });
77
+ if (!result.ok) throw result.error;
78
+ }
79
+
80
+ function addDepthProducer(
81
+ graph: RenderGraphBuilder<RenderPipelineFrame>,
82
+ output: RenderPipelineTarget,
83
+ name: string,
84
+ ): void {
85
+ const result = graph.addRasterPass(name, {
86
+ accesses: [{ resource: output.view, usage: 'depth-stencil-write' }],
87
+ colorAttachments: [],
88
+ depthStencilAttachment: {
89
+ view: output.view,
90
+ depthClearValue: 1,
91
+ depthLoadOp: 'clear',
92
+ depthStoreOp: 'store',
93
+ stencilClearValue: 0,
94
+ stencilLoadOp: 'clear',
95
+ stencilStoreOp: 'store',
96
+ },
97
+ encode: () => undefined,
98
+ });
99
+ if (!result.ok) throw result.error;
100
+ }
101
+
102
+ describe('single-layer medium graph producer pair', () => {
103
+ it('fails closed from the producer unavailable fact without adding passes', () => {
104
+ const graph = new RenderGraphBuilder<RenderPipelineFrame>();
105
+ const backdrop = target(graph, 'medium-backdrop', 'rgba8unorm');
106
+ addColorProducer(graph, backdrop, 'medium-backdrop-producer');
107
+ const result = addSingleLayerMediumPasses(
108
+ mediumInput(graph, {
109
+ opaqueColor: backdrop.view,
110
+ rawDepth: { status: 'unavailable', reason: 'depth-copy-unavailable' },
111
+ }),
112
+ );
113
+
114
+ expect(result).toMatchObject({
115
+ ok: false,
116
+ error: {
117
+ code: 'resource-descriptor-invalid',
118
+ detail: {
119
+ field: 'surfacePair.rawDepth',
120
+ actual: 'depth-copy-unavailable',
121
+ },
122
+ },
123
+ });
124
+ });
125
+
126
+ it('routes an available producer pair into both typed medium passes', async () => {
127
+ const graph = new RenderGraphBuilder<RenderPipelineFrame>();
128
+ const backdrop = target(graph, 'medium-backdrop', 'rgba8unorm');
129
+ addColorProducer(graph, backdrop, 'medium-backdrop-producer');
130
+ const input = mediumInput(graph, {
131
+ opaqueColor: backdrop.view,
132
+ rawDepth: { status: 'unavailable', reason: 'depth-copy-unavailable' },
133
+ });
134
+ addColorProducer(graph, input.color, 'medium-color-producer');
135
+ addDepthProducer(graph, input.depth, 'scene-depth-producer');
136
+
137
+ const rawDepth = addSingleLayerMediumRawDepthProducer({
138
+ graph,
139
+ sourceColor: backdrop,
140
+ sourceDepth: input.depth,
141
+ size: { width: 1, height: 1, depthOrArrayLayers: 1 },
142
+ });
143
+ expect(rawDepth.ok).toBe(true);
144
+ if (!rawDepth.ok) return;
145
+ const availableInput: SingleLayerMediumPassInput = {
146
+ ...input,
147
+ surfacePair: {
148
+ opaqueColor: backdrop.view,
149
+ rawDepth: { status: 'available', view: rawDepth.value.view },
150
+ },
151
+ };
152
+
153
+ const result = addSingleLayerMediumPasses(availableInput);
154
+ expect(result.ok).toBe(true);
155
+ if (!result.ok) return;
156
+
157
+ const createTextureView = vi.spyOn(device, 'createTextureView');
158
+ const compiled = graph.compile({ device, surfaceSize: { width: 1, height: 1 } });
159
+ const viewCalls = createTextureView.mock.calls;
160
+ createTextureView.mockRestore();
161
+ expect(compiled.ok).toBe(true);
162
+ if (!compiled.ok) return;
163
+ expect(viewCalls).toContainEqual([
164
+ expect.anything(),
165
+ expect.objectContaining({ label: 'single-layer-medium-nearest-depth-sampled.view' }),
166
+ ]);
167
+ const passes = compiled.value
168
+ .inspect()
169
+ .passes.filter(
170
+ (pass) =>
171
+ pass.name === 'single-layer-medium-nearest-layer' ||
172
+ pass.name === 'single-layer-medium-color',
173
+ );
174
+ expect(passes.map((pass) => pass.name)).toEqual([
175
+ 'single-layer-medium-nearest-layer',
176
+ 'single-layer-medium-color',
177
+ ]);
178
+ for (const pass of passes) {
179
+ expect(pass.accesses).toContainEqual({
180
+ resource: 'single-layer-medium-raw-depth',
181
+ usage: 'sampled-read',
182
+ });
183
+ }
184
+ expect(compiled.value.inspect().passes).toContainEqual(
185
+ expect.objectContaining({ name: 'single-layer-medium-nearest-depth-sampled-producer' }),
186
+ );
187
+ });
188
+
189
+ it('encodes the registered raw-depth producer through the Null RHI', async () => {
190
+ const graph = new RenderGraphBuilder<RenderPipelineFrame>();
191
+ const sourceColor = target(graph, 'raw-depth-source-color', 'rgba8unorm');
192
+ const sourceDepth = target(graph, 'raw-depth-source-depth', 'depth24plus-stencil8');
193
+ addColorProducer(graph, sourceColor, 'raw-depth-color-producer');
194
+ addDepthProducer(graph, sourceDepth, 'raw-depth-depth-producer');
195
+ const producer = addSingleLayerMediumRawDepthProducer({
196
+ graph,
197
+ sourceColor,
198
+ sourceDepth,
199
+ size: { width: 1, height: 1, depthOrArrayLayers: 1 },
200
+ });
201
+ expect(producer.ok).toBe(true);
202
+ if (!producer.ok) return;
203
+ expect(producer.value.format).toBe('r32float');
204
+ expect(producer.value.view).toBeDefined();
205
+
206
+ const createBindGroup = vi.spyOn(device, 'createBindGroup');
207
+ const errors: unknown[] = [];
208
+ const encoder = device.createCommandEncoder({ label: 'raw-depth-producer-encode' }).unwrap();
209
+ const frame = {
210
+ encoder,
211
+ runtime: {
212
+ device,
213
+ errorRegistry: { fire: (error: unknown) => errors.push(error) },
214
+ lookupPostProcess: (id: string) =>
215
+ id === SINGLE_LAYER_MEDIUM_RAW_DEPTH_POST_PROCESS_ID
216
+ ? {
217
+ source: SINGLE_LAYER_MEDIUM_RAW_DEPTH_WGSL,
218
+ reads: ['input', { key: 'scene-depth', sampleType: 'depth' as const }],
219
+ }
220
+ : undefined,
221
+ getPostProcessPipeline: () => ({}) as never,
222
+ },
223
+ pipelineState: { colorAttachmentFormat: 'r32float', format: 'rgba8unorm' },
224
+ frameState: { perFrameGraph: undefined },
225
+ bindGroupCounts: {},
226
+ geometryDepthKey: 'scene-depth',
227
+ postProcessParams: new Map(),
228
+ msaaActive: false,
229
+ targetW: 1,
230
+ targetH: 1,
231
+ view: undefined,
232
+ currentTexture: undefined,
233
+ clear: [0, 0, 0, 1],
234
+ geometryColorResolveView: null,
235
+ ldrSpriteColorView: null,
236
+ } as unknown as RenderPipelineFrame;
237
+ const compiled = graph.compile({ device, surfaceSize: { width: 1, height: 1 } });
238
+ expect(compiled.ok).toBe(true);
239
+ if (!compiled.ok) return;
240
+ const executed = compiled.value.execute(frame);
241
+ expect(executed.ok).toBe(true);
242
+ expect(errors).toEqual([]);
243
+ expect(createBindGroup).toHaveBeenCalled();
244
+ const entries = createBindGroup.mock.calls.at(-1)?.[0].entries as readonly {
245
+ binding: number;
246
+ }[];
247
+ expect(entries.map((entry) => entry.binding)).toEqual([0, 1, 3, 4]);
248
+ expect(encoder.finish().ok).toBe(true);
249
+ createBindGroup.mockRestore();
250
+ });
251
+
252
+ it('rejects a non-4x input at the paired resolver boundary', () => {
253
+ const graph = new RenderGraphBuilder<RenderPipelineFrame>();
254
+ const sourceColor = target(graph, 'paired-source-color', 'rgba16float');
255
+ const sourceDepth = target(graph, 'paired-source-depth', 'depth24plus-stencil8');
256
+ const result = addSingleLayerMediumMsaaPairProducer({
257
+ graph,
258
+ label: 'paired-invalid',
259
+ sourceColor,
260
+ sourceDepth,
261
+ size: { width: 1, height: 1, depthOrArrayLayers: 1 },
262
+ });
263
+ expect(result).toMatchObject({
264
+ ok: false,
265
+ error: {
266
+ code: 'resource-descriptor-invalid',
267
+ detail: {
268
+ field: 'source.sampleCount',
269
+ expected: 'color=4,depth=4',
270
+ actual: 'color=1,depth=1',
271
+ },
272
+ },
273
+ });
274
+ });
275
+ });
@@ -1,7 +1,8 @@
1
1
  import type { Mat4 } from '@forgeax/engine-math';
2
2
  import type { Buffer, RhiDevice } from '@forgeax/engine-rhi';
3
3
  import { ok } from '@forgeax/engine-rhi';
4
- import { describe, expect, it } from 'vitest';
4
+ import { rhi } from '@forgeax/engine-rhi-null';
5
+ import { describe, expect, it, vi } from 'vitest';
5
6
  import {
6
7
  selectHdrpPbrPrewarmVariants,
7
8
  selectPipelineLayoutForVariant,
@@ -45,6 +46,74 @@ describe('custom skinned mesh motion consumer contract', () => {
45
46
  expect(writes[1]?.data[12]).toBe(0.5);
46
47
  });
47
48
 
49
+ it('keeps persistent uniform palettes isolated across frame-pool resets', async () => {
50
+ const adapter = (await rhi.requestAdapter()).unwrap();
51
+ const device = (await adapter.requestDevice()).unwrap();
52
+ const allocator = createSkinPaletteAllocator(device, 65536, false);
53
+ try {
54
+ const first = allocator.allocatePersistentSlice({
55
+ identity: 'world:1',
56
+ generation: 1,
57
+ jointCount: 1,
58
+ });
59
+ allocator.resetForFrame();
60
+ const second = allocator.allocatePersistentSlice({
61
+ identity: 'world:2',
62
+ generation: 1,
63
+ jointCount: 1,
64
+ });
65
+ expect(second.buffer).not.toBe(first.buffer);
66
+ expect(
67
+ allocator.allocatePersistentSlice({ identity: 'world:1', generation: 1, jointCount: 1 })
68
+ .buffer,
69
+ ).toBe(first.buffer);
70
+ } finally {
71
+ allocator.dispose();
72
+ }
73
+ });
74
+
75
+ it('keeps recycled storage slices inside their dynamic binding windows', async () => {
76
+ const adapter = (await rhi.requestAdapter()).unwrap();
77
+ const device = (await adapter.requestDevice()).unwrap();
78
+ const sizes = new Map<Buffer, number>();
79
+ const createBuffer = device.createBuffer.bind(device);
80
+ vi.spyOn(device, 'createBuffer').mockImplementation((descriptor) => {
81
+ const result = createBuffer(descriptor);
82
+ if (result.ok && descriptor.size !== undefined) sizes.set(result.value, descriptor.size);
83
+ return result;
84
+ });
85
+ const allocator = createSkinPaletteAllocator(device, 65536, true);
86
+ try {
87
+ const full = allocator.allocatePersistentSlice({
88
+ identity: 'world:full',
89
+ generation: 1,
90
+ jointCount: 255,
91
+ });
92
+ allocator.releasePersistentSlice('world:full');
93
+ await device.queue.onSubmittedWorkDone();
94
+ const first = allocator.allocatePersistentSlice({
95
+ identity: 'world:first',
96
+ generation: 1,
97
+ jointCount: 1,
98
+ });
99
+ const second = allocator.allocatePersistentSlice({
100
+ identity: 'world:second',
101
+ generation: 1,
102
+ jointCount: 1,
103
+ });
104
+ expect(first.byteOffset).toBe(full.byteOffset);
105
+ expect(second.byteOffset).not.toBe(first.byteOffset);
106
+ for (const slice of [first, second]) {
107
+ const bufferSize = sizes.get(slice.buffer);
108
+ if (bufferSize === undefined) throw new Error('palette buffer has no allocation size');
109
+ expect(slice.byteOffset % 256).toBe(0);
110
+ expect(slice.byteOffset + allocator.bindingWindowBytes).toBeLessThanOrEqual(bufferSize);
111
+ }
112
+ } finally {
113
+ allocator.dispose();
114
+ }
115
+ });
116
+
48
117
  it('prepares both URP and HDRP pbr-skin consumers with the skin-cluster contract', () => {
49
118
  const variants = selectSkinPrewarmVariants(
50
119
  {
@@ -0,0 +1,97 @@
1
+ import { mat4 } from '@forgeax/engine-math';
2
+ import { createShaderModuleImmediate, rhi } from '@forgeax/engine-rhi-webgpu';
3
+ import { expect, it } from 'vitest';
4
+ import { GPU_SHADER_STAGE_COMPUTE } from '../gpu-stage';
5
+ import {
6
+ GPU_BUFFER_USAGE_COPY_DST,
7
+ GPU_BUFFER_USAGE_COPY_SRC,
8
+ GPU_BUFFER_USAGE_MAP_READ,
9
+ GPU_BUFFER_USAGE_STORAGE,
10
+ } from '../gpu-usage';
11
+ import { createSkinPaletteAllocator } from '../systems/skin-palette-allocator';
12
+
13
+ it('submits recycled persistent palettes with complete dynamic binding windows', async () => {
14
+ const device = (await (await rhi.requestAdapter()).unwrap().requestDevice()).unwrap();
15
+ const allocator = createSkinPaletteAllocator(device, 65536, true);
16
+ const output = device
17
+ .createBuffer({ size: 16, usage: GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_SRC })
18
+ .unwrap();
19
+ const readback = device
20
+ .createBuffer({ size: 16, usage: GPU_BUFFER_USAGE_MAP_READ | GPU_BUFFER_USAGE_COPY_DST })
21
+ .unwrap();
22
+ try {
23
+ allocator.allocatePersistentSlice({ identity: 'full', generation: 1, jointCount: 255 });
24
+ allocator.releasePersistentSlice('full');
25
+ await device.queue.onSubmittedWorkDone();
26
+ allocator.allocatePersistentSlice({ identity: 'first', generation: 1, jointCount: 1 });
27
+ const second = allocator.allocatePersistentSlice({
28
+ identity: 'second',
29
+ generation: 1,
30
+ jointCount: 1,
31
+ });
32
+ expect(second.byteOffset).toBeGreaterThan(0);
33
+ const identity = mat4.create();
34
+ mat4.identity(identity);
35
+ const pose = mat4.create();
36
+ mat4.identity(pose);
37
+ pose[12] = 7;
38
+ allocator.writePersistentJointPalette(second, [identity], [pose]);
39
+ const module = createShaderModuleImmediate(device, {
40
+ code: `@group(0) @binding(0) var<storage, read> palette: array<mat4x4<f32>, 255>;
41
+ @group(0) @binding(1) var<storage, read_write> result: vec4<f32>;
42
+ @compute @workgroup_size(1) fn probe() { result = palette[0][3]; }`,
43
+ }).unwrap();
44
+ const groupLayout = device
45
+ .createBindGroupLayout({
46
+ entries: [
47
+ {
48
+ binding: 0,
49
+ visibility: GPU_SHADER_STAGE_COMPUTE,
50
+ buffer: {
51
+ type: 'read-only-storage',
52
+ hasDynamicOffset: true,
53
+ minBindingSize: allocator.bindingWindowBytes,
54
+ },
55
+ },
56
+ { binding: 1, visibility: GPU_SHADER_STAGE_COMPUTE, buffer: { type: 'storage' } },
57
+ ],
58
+ })
59
+ .unwrap();
60
+ const layout = device.createPipelineLayout({ bindGroupLayouts: [groupLayout] }).unwrap();
61
+ const pipeline = device
62
+ .createComputePipeline({ layout, compute: { module, entryPoint: 'probe' } })
63
+ .unwrap();
64
+ const group = device
65
+ .createBindGroup({
66
+ layout: groupLayout,
67
+ entries: [
68
+ {
69
+ binding: 0,
70
+ resource: {
71
+ kind: 'buffer',
72
+ value: { buffer: second.buffer, offset: 0, size: allocator.bindingWindowBytes },
73
+ },
74
+ },
75
+ { binding: 1, resource: { kind: 'buffer', value: { buffer: output } } },
76
+ ],
77
+ })
78
+ .unwrap();
79
+ const encoder = device.createCommandEncoder().unwrap();
80
+ const pass = encoder.beginComputePass();
81
+ pass.setPipeline(pipeline);
82
+ pass.setBindGroup(0, group, [second.byteOffset]);
83
+ pass.dispatchWorkgroups(1);
84
+ pass.end();
85
+ encoder.copyBufferToBuffer(output, 0, readback, 0, 16);
86
+ device.queue.submit([encoder.finish().unwrap()]).unwrap();
87
+ const mapped = (await readback.mapAsync(GPU_BUFFER_USAGE_MAP_READ)).unwrap();
88
+ const result = new Float32Array(mapped.getMappedRange().unwrap().slice(0));
89
+ mapped.unmap();
90
+ expect(Array.from(result)).toEqual([7, 0, 0, 1]);
91
+ } finally {
92
+ await device.queue.onSubmittedWorkDone();
93
+ allocator.dispose();
94
+ device.destroyBuffer(output).unwrap();
95
+ device.destroyBuffer(readback).unwrap();
96
+ }
97
+ });
@@ -159,6 +159,21 @@ describe('skinned shadow caster', () => {
159
159
  expect(recordSource).toContain('binding: 12');
160
160
  });
161
161
 
162
+ it('keeps shadow view bind groups complete when cloud transport extends the shared layout', () => {
163
+ const recordSource = readFileSync(
164
+ fileURLToPath(new URL('../record/shadow-pass.ts', import.meta.url)),
165
+ 'utf8',
166
+ );
167
+ const shadowViewHelper = recordSource.slice(
168
+ recordSource.indexOf('function ensureTypedShadowViewBg'),
169
+ recordSource.indexOf('interface ShadowDispatch'),
170
+ );
171
+ expect(shadowViewHelper).toContain('binding: 16');
172
+ expect(shadowViewHelper).toContain('binding: 17');
173
+ expect(shadowViewHelper).toContain('pipelineState.defaultWhiteTextureView');
174
+ expect(shadowViewHelper).toContain('pipelineState.defaultSampler');
175
+ });
176
+
162
177
  it('uses the visible scene index for multi-batch capable shadows', () => {
163
178
  const shader = readFileSync(
164
179
  fileURLToPath(new URL('../../../shader/src/shadow_caster.wgsl', import.meta.url)),
@@ -0,0 +1,21 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ const carrierScripts = [
5
+ new URL('../../../../apps/hello/ssr/scripts/smoke-dawn.mjs', import.meta.url),
6
+ new URL(
7
+ '../../../../apps/learn-render/6.pbr/4.render-target-reflection/scripts/smoke-dawn.mjs',
8
+ import.meta.url,
9
+ ),
10
+ ];
11
+
12
+ describe('SSR carrier public receipt contract', () => {
13
+ it('uses public receipt completion instead of private inspection completion', () => {
14
+ for (const scriptUrl of carrierScripts) {
15
+ const source = readFileSync(scriptUrl, 'utf8');
16
+ expect(source).not.toContain('reflectionFallbackCompletion');
17
+ expect(source).toContain('.completed');
18
+ expect(source).toContain('renderer.inspect()');
19
+ }
20
+ });
21
+ });
@@ -25,7 +25,7 @@ describe('SSR GPU dispatch Browser WebGPU probe', () => {
25
25
  device = (await adapter.requestDevice()).unwrap();
26
26
  });
27
27
 
28
- // Independent cases identify the failing invariant. The 300-frame case
28
+ // Independent cases identify the failing invariant. The 60-frame case
29
29
  // also includes cold software-driver compilation and completed readbacks;
30
30
  // its bounded 30-second execution allowance is not a frame-performance gate.
31
31
  it('reprojects history and reconstructs perspective and orthographic view distance', async () => {
@@ -40,8 +40,8 @@ describe('SSR GPU dispatch Browser WebGPU probe', () => {
40
40
  }
41
41
  });
42
42
 
43
- it('executes 300 stable Hi-Z, trace and temporal frames with finite visible hits', async () => {
44
- const hit = await runSsrGpuDispatch(device, sources, 300);
43
+ it('executes 60 stable Hi-Z, trace and temporal frames with finite visible hits', async () => {
44
+ const hit = await runSsrGpuDispatch(device, sources, 60);
45
45
  expect(hit.passNames.filter((name) => name.startsWith('ssr-hiz-reduce'))).toHaveLength(1);
46
46
  expect(hit.passNames.filter((name) => name.startsWith('ssr-reflection-mip'))).toHaveLength(1);
47
47
  expect(hit.passNames).toContain('ssr-hiz-seed');
@@ -49,7 +49,7 @@ describe('SSR GPU dispatch Browser WebGPU probe', () => {
49
49
  expect(hit.passNames).toContain('ssr-temporal');
50
50
  expect(hit.passNames.filter((name) => name === 'ssr-compose')).toHaveLength(1);
51
51
  expect(hit.resourceNames).toContain('ssr-resolved');
52
- expect(hit.pixels).toHaveLength(300);
52
+ expect(hit.pixels).toHaveLength(60);
53
53
  expect(new Set(hit.pixels.map((pixel) => pixel.join(','))).size).toBe(1);
54
54
  expect(hit.pixel.some((value) => value !== 0)).toBe(true);
55
55
  const hiz = hit.hizPixels[0] ?? [];
@@ -38,7 +38,7 @@ describe('SSR GPU dispatch Dawn probe', () => {
38
38
  expect(distances[0]).toBeCloseTo(6.58, 4);
39
39
  expect(distances[1]).toBeCloseTo(12, 4);
40
40
  }
41
- const hit = await runSsrGpuDispatch(device, sourcesFromManifest(manifest), 300);
41
+ const hit = await runSsrGpuDispatch(device, sourcesFromManifest(manifest), 60);
42
42
  // Mip count must not grow CPU pass recording: ordered dispatches share
43
43
  // one compute pass per pyramid, with every level still produced below.
44
44
  expect(hit.passNames.filter((name) => name.startsWith('ssr-hiz-reduce'))).toHaveLength(1);
@@ -49,7 +49,7 @@ describe('SSR GPU dispatch Dawn probe', () => {
49
49
  expect(hit.passNames.filter((name) => name === 'ssr-compose')).toHaveLength(1);
50
50
  expect(hit.passNames).toContain('ssr-trace');
51
51
  expect(hit.resourceNames).toContain('ssr-resolved');
52
- expect(hit.pixels).toHaveLength(300);
52
+ expect(hit.pixels).toHaveLength(60);
53
53
  expect(new Set(hit.pixels.map((pixel) => pixel.join(','))).size).toBe(1);
54
54
  expect(hit.pixel.some((value) => value !== 0)).toBe(true);
55
55
  // The history must store the actually shaded receiver/wall normal, not a
@@ -889,7 +889,7 @@ export async function runSsrGpuDispatch(
889
889
  await device.queue.onSubmittedWorkDone();
890
890
  // These independent copies share one completed submission. Request all
891
891
  // mappings together instead of paying three browser/driver round trips
892
- // for each of the 300 frames; every buffer is still read and validated.
892
+ // for each of the 60 frames; every buffer is still read and validated.
893
893
  const [mapped, traceMapped, hizMapped] = await Promise.all([
894
894
  readbackBuffer.mapAsync(GPU_BUFFER_USAGE_MAP_READ),
895
895
  traceReadbackBuffer.mapAsync(GPU_BUFFER_USAGE_MAP_READ),
@@ -16,6 +16,7 @@ import {
16
16
  GPU_BUFFER_USAGE_MAP_READ,
17
17
  GPU_BUFFER_USAGE_UNIFORM,
18
18
  } from '../gpu-usage';
19
+ import { VIEW_UNIFORM_BYTES } from '../record/view-ubo';
19
20
 
20
21
  async function composePixels(
21
22
  width: number,
@@ -85,9 +86,12 @@ async function composePixels(
85
86
  upload(width, height, normals),
86
87
  ];
87
88
  const view = device
88
- .createBuffer({ size: 960, usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST })
89
+ .createBuffer({
90
+ size: VIEW_UNIFORM_BYTES,
91
+ usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
92
+ })
89
93
  .unwrap();
90
- const viewData = new Float32Array(240);
94
+ const viewData = new Float32Array(VIEW_UNIFORM_BYTES / Float32Array.BYTES_PER_ELEMENT);
91
95
  viewData[238] = options.roughnessLimit ?? 1;
92
96
  viewData[239] = options.enabled ?? 1;
93
97
  device.queue.writeBuffer(view, 0, viewData).unwrap();
@@ -68,6 +68,34 @@ describe('Standard output chain contract', () => {
68
68
  expect(validateStandardOutputPlan(plan).ok).toBe(true);
69
69
  });
70
70
 
71
+ it('places barrel distortion in linear-LDR after LUT and before edge AA', () => {
72
+ const plan = createStandardOutputPlan({
73
+ ...fullRequest,
74
+ barrelDistortion: true,
75
+ });
76
+
77
+ expect(plan.logicalStages).toEqual([
78
+ 'temporal',
79
+ 'meter',
80
+ 'bloom',
81
+ 'exposure-white-balance',
82
+ 'tone',
83
+ 'lut',
84
+ 'barrel-distortion',
85
+ 'fxaa',
86
+ 'output-encoding',
87
+ ]);
88
+ expect(plan.physicalStages.at(6)).toEqual({
89
+ name: 'barrel-distortion',
90
+ input: 'linear-ldr',
91
+ output: 'linear-ldr',
92
+ });
93
+ expect(plan.incrementalResources).toContain('standard-barrel-distortion');
94
+ expect(plan.incrementalBindings).toBeGreaterThan(0);
95
+ expect(plan.physicalStages.at(8)?.output).toBe('display-encoded');
96
+ expect(validateStandardOutputPlan(plan).ok).toBe(true);
97
+ });
98
+
71
99
  it.each([
72
100
  [
73
101
  'swaps meter and bloom',
@@ -160,6 +160,48 @@ describe('Standard PBR artifact assembly', () => {
160
160
  expect(custom?.receipt).toEqual(colored);
161
161
  });
162
162
 
163
+ it('resolves a topology projection against the producer complete variant key', () => {
164
+ const receipt = createStandardPbrArtifactReceipt();
165
+ const shader = {
166
+ findMaterialArtifact: () => ({
167
+ ok: true as const,
168
+ value: { source: 'boot-direct', paramSchema: [], receipt },
169
+ }),
170
+ materialShaderManifestEntries: () => [
171
+ {
172
+ identifier: 'forgeax::default-standard-pbr',
173
+ sourcePath: 'default-standard-pbr.wgsl',
174
+ composedWgsl: 'boot-direct',
175
+ paramSchema: '[]',
176
+ variants: [
177
+ {
178
+ definesKey:
179
+ 'CLUSTER_FORWARD_AVAILABLE=false+GPU_DRIVEN_SCENE_INDEX_AVAILABLE=true+PROBE_BLEND_AVAILABLE=false+STORAGE_BUFFER_AVAILABLE=true+VERTEX_COLOR_AVAILABLE=false',
180
+ defines: {
181
+ CLUSTER_FORWARD_AVAILABLE: false,
182
+ GPU_DRIVEN_SCENE_INDEX_AVAILABLE: true,
183
+ PROBE_BLEND_AVAILABLE: false,
184
+ STORAGE_BUFFER_AVAILABLE: true,
185
+ VERTEX_COLOR_AVAILABLE: false,
186
+ },
187
+ composedWgsl: 'scene-index',
188
+ receipt,
189
+ },
190
+ ],
191
+ },
192
+ ],
193
+ } as unknown as Parameters<typeof resolveMaterialShaderArtifact>[1];
194
+ const artifact = resolveMaterialShaderArtifact('forgeax::default-standard-pbr', shader, {
195
+ address: 'scene-index',
196
+ pass: 'forward',
197
+ vertexColorAvailable: false,
198
+ variantSet:
199
+ 'CLUSTER_FORWARD_AVAILABLE=false+STORAGE_BUFFER_AVAILABLE=true+VERTEX_COLOR_AVAILABLE=false',
200
+ });
201
+ expect(artifact?.wgsl).toBe('scene-index');
202
+ expect(artifact?.receipt).toEqual(receipt);
203
+ });
204
+
163
205
  it('routes built-in Standard shadow requests through the shadow-caster artifact', () => {
164
206
  const rigidPlain = createStandardPbrArtifactReceipt();
165
207
  const rigidColor = createStandardPbrArtifactReceipt(false, true);
@@ -251,7 +293,10 @@ describe('Standard PBR artifact assembly', () => {
251
293
  };
252
294
  const registry = {
253
295
  findMaterialArtifact: () => ({ ok: true, value: entry }),
254
- materialShaderManifestEntries: () => [manifest].values(),
296
+ *materialShaderManifestEntries() {
297
+ yield manifest;
298
+ throw new Error('manifest search must stop at the first matching publication');
299
+ },
255
300
  } as unknown as Parameters<typeof resolveMaterialShaderArtifact>[1];
256
301
  const resolve = () =>
257
302
  resolveMaterialShaderArtifact('game::surface', registry, { address: 'scene-index' });