@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,59 @@
1
+ import type { MaterialRootAsset } from '@forgeax/engine-types';
2
+
3
+ /**
4
+ * A public root asset showing the model declaration and its bounded input.
5
+ * The page is shared by every material instance; Render supplies the page
6
+ * bytes and each instance publishes its own range.
7
+ */
8
+ export const SINGLE_LAYER_MEDIUM_SURFACE_EXAMPLE = {
9
+ kind: 'material',
10
+ surface: {
11
+ model: 'single-layer-medium',
12
+ module: 'game::water_surface_a',
13
+ dynamicInput: {
14
+ name: 'waterEvents',
15
+ fields: [
16
+ { name: 'position', type: 'vec3<f32>' },
17
+ { name: 'time', type: 'f32' },
18
+ { name: 'eventId', type: 'u32' },
19
+ ],
20
+ maxRecords: 64,
21
+ maxDomains: 8,
22
+ maxPageBytes: 2048,
23
+ maxBindings: 1,
24
+ maxEventsPerSample: 8,
25
+ },
26
+ },
27
+ passes: [
28
+ {
29
+ name: 'color',
30
+ program: { module: 'forgeax::single-layer-medium' },
31
+ },
32
+ ],
33
+ parameters: [],
34
+ values: {},
35
+ } as const satisfies MaterialRootAsset;
36
+
37
+ /** Two different imported Surface implementations consuming one model ABI. */
38
+ export const SINGLE_LAYER_MEDIUM_SURFACE_EXAMPLES = [
39
+ {
40
+ module: 'game::water_surface_a',
41
+ source: `#define_import_path game::water_surface_a
42
+ #import forgeax_material::single_layer_medium_surface_v1::{SingleLayerMediumSurfaceInput, SingleLayerMediumSurfaceData}
43
+ fn evaluate_surface(input : SingleLayerMediumSurfaceInput) -> SingleLayerMediumSurfaceData {
44
+ let event = read_waterEvents(input.eventRangeStart);
45
+ let normal = normalize(input.geometricNormalWS + vec3<f32>(0.0, 0.0, event.time * 0.02));
46
+ return SingleLayerMediumSurfaceData(normal, 0.18, 1.0, 0.0, vec3<f32>(0.18, 0.06, 0.02), vec3<f32>(0.02, 0.04, 0.08), 1.333, 0.2, 1000.0);
47
+ }`,
48
+ },
49
+ {
50
+ module: 'game::water_surface_b',
51
+ source: `#define_import_path game::water_surface_b
52
+ #import forgeax_material::single_layer_medium_surface_v1::{SingleLayerMediumSurfaceInput, SingleLayerMediumSurfaceData}
53
+ fn evaluate_surface(input : SingleLayerMediumSurfaceInput) -> SingleLayerMediumSurfaceData {
54
+ let event = read_waterEvents(input.eventRangeStart);
55
+ let normal = normalize(input.geometricNormalWS + vec3<f32>(event.position.xy * 0.01, 0.0));
56
+ return SingleLayerMediumSurfaceData(normal, 0.32, 1.0, clamp(event.time * 0.1, 0.0, 1.0), vec3<f32>(0.04, 0.02, 0.01), vec3<f32>(0.08, 0.05, 0.02), 1.333, -0.1, 800.0);
57
+ }`,
58
+ },
59
+ ] as const;
@@ -2,6 +2,10 @@ import type { EntityHandle, World } from '@forgeax/engine-ecs';
2
2
  import { readRenderArrayView } from '@forgeax/engine-ecs/projection';
3
3
  import { mat4, vec3 } from '@forgeax/engine-math';
4
4
  import { GlobalTransform } from '@forgeax/engine-scene';
5
+ import {
6
+ BarrelDistortion,
7
+ validateBarrelDistortionParameters,
8
+ } from '../components/barrel-distortion';
5
9
  import {
6
10
  antialiasFromF32,
7
11
  Camera,
@@ -38,13 +42,20 @@ export function extractCameraSnapshots(world: World): CameraSnapshot[] {
38
42
  const cameraQuery = world
39
43
  .query({
40
44
  read: [Camera],
41
- optional: [MotionBlur, DynamicResolution, ScreenSpaceReflection, DepthOfField],
45
+ optional: [
46
+ MotionBlur,
47
+ DynamicResolution,
48
+ ScreenSpaceReflection,
49
+ DepthOfField,
50
+ BarrelDistortion,
51
+ ],
42
52
  with: [GlobalTransform],
43
53
  })
44
54
  .unwrap();
45
55
  for (const row of cameraQuery) {
46
56
  const cam = row.get(Camera);
47
57
  const motionBlur = row.get(MotionBlur);
58
+ const barrelDistortion = row.get(BarrelDistortion);
48
59
  const motionBlurResult = resolveMotionBlurParams(motionBlur);
49
60
  if (!motionBlurResult.ok) throw motionBlurResult.error;
50
61
  const motionBlurParams = motionBlurResult.value;
@@ -75,6 +86,8 @@ export function extractCameraSnapshots(world: World): CameraSnapshot[] {
75
86
  const antialias = antialiasFromF32(cam.antialias);
76
87
  const dynamicResolutionResult = validateDynamicResolutionCamera(dynamicResolution, antialias);
77
88
  if (!dynamicResolutionResult.ok) throw dynamicResolutionResult.error;
89
+ const barrelDistortionResult = validateBarrelDistortionParameters(barrelDistortion);
90
+ if (!barrelDistortionResult.ok) throw barrelDistortionResult.error;
78
91
  cameras.push({
79
92
  entityKey: entity as number,
80
93
  historyVersion: cam.historyVersion,
@@ -128,6 +141,7 @@ export function extractCameraSnapshots(world: World): CameraSnapshot[] {
128
141
  targetFps: motionBlurParams.targetFps,
129
142
  },
130
143
  }),
144
+ ...(barrelDistortion === undefined ? {} : { barrelDistortion: barrelDistortionResult.value }),
131
145
  ...(screenSpaceReflection === undefined
132
146
  ? {}
133
147
  : {
@@ -13,6 +13,7 @@ import type {
13
13
  MaterialAsset,
14
14
  MaterialPass,
15
15
  MaterialRenderState,
16
+ MaterialSurfaceModel,
16
17
  MaterialTextureCoordinates,
17
18
  MeshLodLevel,
18
19
  ParamSchemaEntry,
@@ -652,6 +653,10 @@ export interface MaterialSnapshot {
652
653
  readonly baseColor: Vec3;
653
654
  readonly metallic: number;
654
655
  readonly roughness: number;
656
+ /** Root-owned Surface model used only for typed pass routing. */
657
+ readonly surfaceModel?: MaterialSurfaceModel | undefined;
658
+ /** Whether this material publishes a Deferred pass consumed before the forward-only lane. */
659
+ readonly deferredPass?: boolean | undefined;
655
660
  readonly clearcoat?: number | undefined;
656
661
  readonly clearcoatRoughness?: number | undefined;
657
662
  /** Authored Standard PBR specular tint in linear runtime color space. */
@@ -0,0 +1,172 @@
1
+ /** One graph-valid identity for the barrel stage, feature and shader prewarm. */
2
+ export const BARREL_DISTORTION_POST_PROCESS_ID = 'fullscreen.forgeax.barrel-distortion';
3
+ export const BARREL_DISTORTION_FEATURE_IDENTITY = BARREL_DISTORTION_POST_PROCESS_ID;
4
+
5
+ import { ok, type Result } from '@forgeax/engine-types';
6
+ import type { RenderError } from '../errors/render';
7
+ import type { RenderFeaturePlan } from './plan';
8
+ import type { RenderFeature, RenderFeatureExtractContext, RenderFeaturePlanContext } from './types';
9
+
10
+ /**
11
+ * The coordinate function is exported beside the production shader so a real
12
+ * f32 device probe can compile exactly the same implementation. Keeping the
13
+ * probe source here avoids a second test-only equation that could drift from
14
+ * the Standard pass.
15
+ */
16
+ export const BARREL_DISTORTION_WGSL_COORDINATE = /* wgsl */ `
17
+ struct BarrelDistortionParams {
18
+ strength : f32,
19
+ centerX : f32,
20
+ centerY : f32,
21
+ radiusSquared : f32,
22
+ };
23
+
24
+ fn barrel_distortion_display_to_scene_uv(
25
+ inputUv : vec2<f32>,
26
+ params : BarrelDistortionParams,
27
+ dimensions : vec2<f32>,
28
+ ) -> vec2<f32> {
29
+ let aspect = dimensions.x / max(dimensions.y, 1.0);
30
+ let p = vec2<f32>(
31
+ 2.0 * aspect * (inputUv.x - params.centerX),
32
+ 2.0 * (inputUv.y - params.centerY),
33
+ );
34
+ let t = dot(p, p) / max(params.radiusSquared, 1e-6);
35
+ let scale = (1.0 - params.strength) / (1.0 - params.strength * t);
36
+ return clamp(
37
+ vec2<f32>(
38
+ params.centerX + (inputUv.x - params.centerX) * scale,
39
+ params.centerY + (inputUv.y - params.centerY) * scale,
40
+ ),
41
+ vec2<f32>(0.0),
42
+ vec2<f32>(1.0),
43
+ );
44
+ }
45
+ `;
46
+
47
+ /**
48
+ * Manifest-equivalent WGSL for the bounded analytic model. The Standard
49
+ * owner registers this source once and keeps the graph, encoder, and submit
50
+ * lifecycle shared with every other fullscreen stage.
51
+ */
52
+ export const BARREL_DISTORTION_WGSL = /* wgsl */ `${BARREL_DISTORTION_WGSL_COORDINATE}
53
+ struct FullscreenOutput {
54
+ @builtin(position) position : vec4<f32>,
55
+ @location(0) uv : vec2<f32>,
56
+ };
57
+
58
+ fn fullscreen_triangle(vertex_index : u32) -> FullscreenOutput {
59
+ var x : f32 = -1.0;
60
+ var y : f32 = -1.0;
61
+ if (vertex_index == 1u) { x = 3.0; }
62
+ if (vertex_index == 2u) { y = 3.0; }
63
+ let u : f32 = (x + 1.0) * 0.5;
64
+ let v : f32 = 1.0 - (y + 1.0) * 0.5;
65
+ var out : FullscreenOutput;
66
+ out.position = vec4<f32>(x, y, 0.0, 1.0);
67
+ out.uv = vec2<f32>(u, v);
68
+ return out;
69
+ }
70
+
71
+ @group(1) @binding(0) var sceneColor : texture_2d<f32>;
72
+ @group(1) @binding(1) var sceneSampler : sampler;
73
+
74
+ @group(1) @binding(2) var<uniform> params : BarrelDistortionParams;
75
+
76
+ @vertex
77
+ fn vs_main(@builtin(vertex_index) vertex_index : u32) -> FullscreenOutput {
78
+ return fullscreen_triangle(vertex_index);
79
+ }
80
+
81
+ @fragment
82
+ fn fs_main(input : FullscreenOutput) -> @location(0) vec4<f32> {
83
+ let dimensions = vec2<f32>(textureDimensions(sceneColor));
84
+ let sampleUv = barrel_distortion_display_to_scene_uv(input.uv, params, dimensions);
85
+ return textureSampleLevel(sceneColor, sceneSampler, sampleUv, 0.0);
86
+ }
87
+ `;
88
+
89
+ /**
90
+ * Ordinary feature registration for the Standard linear-LDR stage.
91
+ * Standard still allocates the stage targets and chooses its position in the
92
+ * post chain; this feature owns the executable fullscreen declaration that is
93
+ * projected into that slot by the existing graph/encoder/submit path.
94
+ */
95
+ export interface BarrelDistortionFeatureFrame {
96
+ /** True only when the extracted camera requires a spatial warp this frame. */
97
+ readonly active: boolean;
98
+ }
99
+
100
+ export function createBarrelDistortionRenderFeature(): RenderFeature<BarrelDistortionFeatureFrame> {
101
+ return Object.freeze({
102
+ identity: BARREL_DISTORTION_FEATURE_IDENTITY,
103
+ requiredCapabilities: ['rgba16floatRenderable'] as const,
104
+ requiredFullscreenPostProcesses: [
105
+ { identity: BARREL_DISTORTION_POST_PROCESS_ID, source: BARREL_DISTORTION_WGSL },
106
+ ],
107
+ extract: (
108
+ context: RenderFeatureExtractContext,
109
+ ): Result<BarrelDistortionFeatureFrame, RenderError> =>
110
+ ok({ active: (context.selectedCamera?.barrelDistortion?.strength ?? 0) > 0 }),
111
+ plan: (
112
+ data: BarrelDistortionFeatureFrame,
113
+ _context: RenderFeaturePlanContext,
114
+ ): Result<RenderFeaturePlan, RenderError> =>
115
+ ok({
116
+ resources: data.active
117
+ ? [
118
+ {
119
+ kind: 'fullscreen-program' as const,
120
+ name: BARREL_DISTORTION_POST_PROCESS_ID,
121
+ source: BARREL_DISTORTION_WGSL,
122
+ reads: ['ldrColor'],
123
+ params: { byteSize: 16, defaultValue: new Uint8Array(16) },
124
+ },
125
+ {
126
+ kind: 'graphics-program' as const,
127
+ name: 'barrel-distortion-pipeline',
128
+ program: {
129
+ shader: BARREL_DISTORTION_POST_PROCESS_ID,
130
+ vertexLayout: 'none',
131
+ colorFormats: ['rgba16float'],
132
+ sampleCount: 1,
133
+ },
134
+ },
135
+ {
136
+ kind: 'graphics-bindings' as const,
137
+ name: 'barrel-distortion-bindings',
138
+ program: 'barrel-distortion-pipeline',
139
+ values: {
140
+ group: 1,
141
+ fullscreen: true,
142
+ shader: BARREL_DISTORTION_POST_PROCESS_ID,
143
+ input: 'barrel-input',
144
+ },
145
+ logicalTargets: { input: 'barrel-input' },
146
+ },
147
+ ]
148
+ : [],
149
+ passes: data.active
150
+ ? [
151
+ {
152
+ kind: 'raster' as const,
153
+ name: 'barrel-distortion',
154
+ colorAttachments: [
155
+ { target: 'barrel-output', loadOp: 'clear' as const, storeOp: 'store' as const },
156
+ ],
157
+ sampledTargets: ['barrel-input'],
158
+ draws: [
159
+ {
160
+ program: 'barrel-distortion-pipeline',
161
+ bindings: ['barrel-distortion-bindings'],
162
+ vertexData: [],
163
+ vertexLayout: 'none' as const,
164
+ draw: { kind: 'draw' as const, vertexCount: 3, instanceCount: 1 },
165
+ },
166
+ ],
167
+ },
168
+ ]
169
+ : [],
170
+ }),
171
+ });
172
+ }
@@ -8,22 +8,33 @@ import type { RenderError } from '../errors/render';
8
8
  */
9
9
  export const PENDING_RENDER_FEATURE_ERROR_REPORT_INTERVAL_FRAMES = 60;
10
10
 
11
+ export interface PendingRenderFeatureErrorReport {
12
+ readonly frame: number;
13
+ readonly fingerprint: string;
14
+ }
15
+
11
16
  export function shouldReportPendingRenderFeatureError(
12
- lastReportedFrame: Map<string, number>,
17
+ lastReportedFrame: Map<string, PendingRenderFeatureErrorReport>,
13
18
  key: string,
14
19
  frameNumber: number,
20
+ fingerprint = '',
15
21
  intervalFrames = PENDING_RENDER_FEATURE_ERROR_REPORT_INTERVAL_FRAMES,
16
22
  ): boolean {
17
23
  const lastReported = lastReportedFrame.get(key);
18
- if (lastReported !== undefined && frameNumber - lastReported < intervalFrames) return false;
19
- lastReportedFrame.set(key, frameNumber);
24
+ if (
25
+ lastReported !== undefined &&
26
+ lastReported.fingerprint === fingerprint &&
27
+ frameNumber - lastReported.frame < intervalFrames
28
+ )
29
+ return false;
30
+ lastReportedFrame.set(key, { frame: frameNumber, fingerprint });
20
31
  return true;
21
32
  }
22
33
 
23
34
  export function reportPendingRenderFeatureErrors(
24
35
  errors: readonly RenderError[],
25
36
  frameNumber: number,
26
- lastReportedFrame: Map<string, number>,
37
+ lastReportedFrame: Map<string, PendingRenderFeatureErrorReport>,
27
38
  errorRegistry: { fire(error: RenderError): void },
28
39
  ): void {
29
40
  for (const error of errors) {
@@ -36,7 +47,16 @@ export function reportPendingRenderFeatureErrors(
36
47
  // failures still receive first/periodic reporting.
37
48
  if (isSilentRenderFeatureWarmup(error)) continue;
38
49
  const key = pendingRenderFeatureErrorKey(error);
39
- if (shouldReportPendingRenderFeatureError(lastReportedFrame, key, frameNumber)) {
50
+ const cause = error.code === 'render-feature-stage-failed' ? error.detail.cause : undefined;
51
+ const causeText =
52
+ cause instanceof Error
53
+ ? `${cause.name}\0${cause.message}`
54
+ : typeof cause === 'string'
55
+ ? cause
56
+ : '';
57
+ const fingerprint = `${error.expected}\0${error.hint}\0${causeText}`;
58
+ // Retain one record per owner, not one record per changing cause string.
59
+ if (shouldReportPendingRenderFeatureError(lastReportedFrame, key, frameNumber, fingerprint)) {
40
60
  errorRegistry.fire(error);
41
61
  }
42
62
  }
@@ -74,5 +94,5 @@ function pendingRenderFeatureErrorKey(error: RenderError): string {
74
94
  detail !== undefined && 'order' in detail && typeof detail.order === 'number'
75
95
  ? detail.order
76
96
  : -1;
77
- return `${error.code}\0${featureIdentity}\0${order}\0${error.expected}\0${error.hint}`;
97
+ return `${error.code}\0${featureIdentity}\0${order}`;
78
98
  }
@@ -17,6 +17,7 @@ import type {
17
17
  PreparedGraphicsResolver,
18
18
  PreparedGraphicsResourceLease,
19
19
  } from '../prepare/prepared-graphics-resolver';
20
+ import type { CameraSnapshot } from '../render-contract';
20
21
  import { isSceneDataTarget, type SceneDataTarget } from '../temporal/scene-data';
21
22
  import { createSceneDataCatalog, type SceneDataCatalog } from '../temporal/scene-data-catalog';
22
23
  import {
@@ -114,6 +115,7 @@ export interface RenderFeaturePlanExecutionPass {
114
115
  export interface RenderFeaturePlanExecution {
115
116
  readonly featureIdentity: string;
116
117
  readonly order: number;
118
+ readonly placement?: import('./types').RenderFeaturePlacement;
117
119
  readonly passes: readonly RenderFeaturePlanExecutionPass[];
118
120
  }
119
121
 
@@ -134,6 +136,8 @@ export interface RenderFeatureFrameInput {
134
136
  readonly worlds: readonly import('@forgeax/engine-ecs').World[];
135
137
  readonly owner: number;
136
138
  readonly frameNumber: number;
139
+ /** Renderer-selected display camera forwarded to feature extraction. */
140
+ readonly selectedCamera?: CameraSnapshot;
137
141
  /** Physical render extent for bounded feature dispatch sizing. */
138
142
  readonly frameSize?: { readonly width: number; readonly height: number };
139
143
  readonly visibilitySnapshots?: readonly RenderFeatureWorldVisibilitySnapshot[];
@@ -144,6 +148,8 @@ export interface RenderFeatureFrameInput {
144
148
  readonly sceneData?: SceneDataCatalog;
145
149
  readonly generation?: number;
146
150
  readonly caps: Readonly<RhiCaps>;
151
+ /** Normal frame-owner facts shared with producer extraction. */
152
+ readonly frame?: import('./types').RenderFeatureFrameContext;
147
153
  /** Renderer-owned material binding contract projection for producer plans. */
148
154
  readonly materialShaderBindingContract?: (
149
155
  materialShaderId: string,
@@ -175,6 +181,8 @@ export interface RenderFeatureFrameResult {
175
181
  readonly errors: readonly RenderError[];
176
182
  readonly plans: readonly RenderFeaturePlannedFrame[];
177
183
  readonly fullscreenEffects: ReadonlyMap<string, PostProcessShaderEntry>;
184
+ /** Public Standard post-effect identities; graph-local fullscreen resources are not included. */
185
+ readonly postProcessIdentities: readonly string[];
178
186
  readonly preparedResourceBatches: readonly RenderFeaturePreparedResourceBatch[];
179
187
  /**
180
188
  * True only when the current graph resolves a transient graphics lease.
@@ -223,6 +231,8 @@ export interface RenderFeatureHost {
223
231
  recover(input: RenderFeatureRecoverInput): Result<void, RenderError>;
224
232
  createRecoveryRoot(scope: DeviceScope): LifecycleResourceSpec<unknown>;
225
233
  diagnostics(): readonly RenderFeatureDiagnostics[];
234
+ /** Retain an explicitly producer-published detached inspection value. */
235
+ setInspection(identity: string, inspection: unknown): void;
226
236
  /** Subscribe to lifecycle projection changes; unchanged active frames are silent. */
227
237
  subscribeDiagnostics(listener: () => void): () => void;
228
238
  /** Detached signature and prepared-resource accounting for diagnostics. */
@@ -269,6 +279,7 @@ interface FeatureSlot {
269
279
  readonly preparedStore: PreparedGraphicsStore;
270
280
  status: RenderFeatureStatus;
271
281
  latestError: RenderFeatureErrorDescriptor | undefined;
282
+ latestInspection?: unknown;
272
283
  }
273
284
 
274
285
  function freezeError(error: RenderFeatureErrorDescriptor): RenderFeatureErrorDescriptor {
@@ -291,6 +302,7 @@ function freezeDiagnostics(slot: FeatureSlot): RenderFeatureDiagnostics {
291
302
  order: slot.order,
292
303
  status: slot.status,
293
304
  latestError: slot.latestError === undefined ? undefined : freezeError(slot.latestError),
305
+ ...(slot.latestInspection === undefined ? {} : { inspection: slot.latestInspection }),
294
306
  });
295
307
  }
296
308
 
@@ -320,6 +332,16 @@ function missingCapability(
320
332
  return feature.requiredCapabilities?.find((capability) => caps[capability] !== true);
321
333
  }
322
334
 
335
+ /**
336
+ * Capability declarations gate work that the current plan actually admits.
337
+ * A producer is still allowed to extract and return an empty plan when its
338
+ * authored feature is disabled; that path must remain allocation-free on a
339
+ * backend which cannot support the optional feature.
340
+ */
341
+ function hasActivePlan(plan: RenderFeaturePlan): boolean {
342
+ return plan.resources.length > 0 || plan.passes.length > 0;
343
+ }
344
+
323
345
  function createPreparedGraphicsPrepare(
324
346
  transaction: PreparedGraphicsTransaction,
325
347
  ): RenderFeatureGraphicsPrepare {
@@ -346,6 +368,9 @@ function resolveGraphicsSnapshot(
346
368
  resolver: PreparedGraphicsResolver,
347
369
  descriptor: import('./prepared-graphics').RenderFeatureGraphicsPassDescriptor,
348
370
  generation: number,
371
+ resolveGpuResource?: (
372
+ name: string,
373
+ ) => import('./prepared-gpu-work').RenderFeatureResolvedGpuBuffer | undefined,
349
374
  ): Result<PreparedGraphicsResolvedSnapshot, RenderError> {
350
375
  const resources = new Map<
351
376
  object,
@@ -364,6 +389,7 @@ function resolveGraphicsSnapshot(
364
389
  ...(resolver.resolveGpuBuffer === undefined
365
390
  ? {}
366
391
  : { resolveGpuBuffer: resolver.resolveGpuBuffer }),
392
+ ...(resolveGpuResource === undefined ? {} : { resolveGpuResource }),
367
393
  });
368
394
  }
369
395
 
@@ -971,6 +997,7 @@ function projectPlanPasses(
971
997
  return ok({
972
998
  featureIdentity: slot.feature.identity,
973
999
  order: slot.order,
1000
+ ...(slot.feature.placement === undefined ? {} : { placement: slot.feature.placement }),
974
1001
  passes: Object.freeze(projected),
975
1002
  });
976
1003
  }
@@ -1330,6 +1357,13 @@ class FeatureHostImpl implements RenderFeatureHost {
1330
1357
  return Object.freeze(this.slots.map(freezeDiagnostics));
1331
1358
  }
1332
1359
 
1360
+ /** Retain only an explicitly producer-published detached inspection value. */
1361
+ setInspection(identity: string, inspection: unknown): void {
1362
+ const slot = findSlot(this.slots, identity);
1363
+ if (slot === undefined || slot.status === 'disposed') return;
1364
+ slot.latestInspection = inspection;
1365
+ }
1366
+
1333
1367
  subscribeDiagnostics(listener: () => void): () => void {
1334
1368
  this.diagnosticsListeners.add(listener);
1335
1369
  return () => this.diagnosticsListeners.delete(listener);
@@ -1360,6 +1394,7 @@ export function runRenderFeatureFrame(
1360
1394
  const errors: RenderError[] = [];
1361
1395
  const plans: RenderFeaturePlannedFrame[] = [];
1362
1396
  const fullscreenEffects = new Map<string, PostProcessShaderEntry>();
1397
+ const postProcessIdentities = new Set<string>();
1363
1398
  const preparedResourceBatches: RenderFeaturePreparedResourceBatch[] = [];
1364
1399
  let requiresPreparedResourceKey = false;
1365
1400
  const hiddenEntityReports: RenderFeatureHiddenEntityReport[] = [
@@ -1370,17 +1405,18 @@ export function runRenderFeatureFrame(
1370
1405
  const diagnostics = host.diagnostics();
1371
1406
  for (const [order, feature] of host.features.entries()) {
1372
1407
  const diagnostic = diagnostics[order];
1373
- if (diagnostic?.status === 'disabled' || diagnostic?.status === 'disposed') continue;
1374
- const missing = missingCapability(feature, input.caps);
1375
- if (missing !== undefined) {
1376
- const capabilityError = new RenderFeatureCapabilityMissingError(
1377
- feature.identity,
1378
- order,
1379
- missing,
1380
- );
1381
- errors.push(capabilityError);
1382
- host.setStatus(feature.identity, 'disabled', errorDescriptor(capabilityError));
1383
- continue;
1408
+ if (diagnostic?.status === 'disposed') continue;
1409
+ // Preserve the existing recovery contract for a capability-disabled
1410
+ // feature once the device becomes capable: an explicit host.recover()
1411
+ // still re-admits it. While the capability is absent, however, rerun the
1412
+ // producer so a camera/world can disable its authored plan and recover to
1413
+ // an empty, zero-resource frame without a second manager.
1414
+ if (diagnostic?.status === 'disabled') {
1415
+ const diagnosticError = diagnostic.latestError;
1416
+ const missing = missingCapability(feature, input.caps);
1417
+ if (diagnosticError?.code !== 'render-feature-capability-missing' || missing === undefined) {
1418
+ continue;
1419
+ }
1384
1420
  }
1385
1421
  const slot: FeatureSlot = {
1386
1422
  feature,
@@ -1391,6 +1427,9 @@ export function runRenderFeatureFrame(
1391
1427
  latestError: undefined,
1392
1428
  };
1393
1429
  const identity = slot.feature.identity;
1430
+ for (const postProcess of feature.requiredFullscreenPostProcesses ?? []) {
1431
+ postProcessIdentities.add(postProcess.identity);
1432
+ }
1394
1433
  const transaction = host.beginPreparedFrame(identity, input.generation ?? 0);
1395
1434
  if (transaction === undefined) continue;
1396
1435
  const graphics = createPreparedGraphicsPrepare(transaction);
@@ -1404,6 +1443,9 @@ export function runRenderFeatureFrame(
1404
1443
  worlds: input.worlds,
1405
1444
  owner: input.owner,
1406
1445
  frameNumber: input.frameNumber,
1446
+ ...(input.selectedCamera === undefined ? {} : { selectedCamera: input.selectedCamera }),
1447
+ caps: input.caps,
1448
+ ...(input.frame === undefined ? {} : { frame: input.frame }),
1407
1449
  reportHiddenEntity: (report) => featureHiddenEntityReports.push(report),
1408
1450
  ...(input.visibilitySnapshots === undefined
1409
1451
  ? {}
@@ -1421,6 +1463,16 @@ export function runRenderFeatureFrame(
1421
1463
  host.setStatus(identity, 'failed', slot.latestError);
1422
1464
  continue;
1423
1465
  }
1466
+ if (
1467
+ typeof extracted.value === 'object' &&
1468
+ extracted.value !== null &&
1469
+ 'inspection' in extracted.value
1470
+ ) {
1471
+ host.setInspection(
1472
+ identity,
1473
+ (extracted.value as { readonly inspection?: unknown }).inspection,
1474
+ );
1475
+ }
1424
1476
 
1425
1477
  const targets = logicalTargets(input.targets ?? []);
1426
1478
  const sceneData =
@@ -1466,21 +1518,43 @@ export function runRenderFeatureFrame(
1466
1518
  host.setStatus(identity, 'failed', slot.latestError);
1467
1519
  continue;
1468
1520
  }
1521
+ const missing = missingCapability(feature, input.caps);
1522
+ if (missing !== undefined && hasActivePlan(frozen.value)) {
1523
+ const capabilityError = new RenderFeatureCapabilityMissingError(identity, order, missing);
1524
+ errors.push(capabilityError);
1525
+ transaction.abort();
1526
+ host.setStatus(identity, 'disabled', errorDescriptor(capabilityError));
1527
+ continue;
1528
+ }
1469
1529
  const plannedFrame: RenderFeaturePlannedFrame = Object.freeze({
1470
1530
  featureIdentity: identity,
1471
1531
  generation: transaction.generation,
1472
1532
  signature:
1473
1533
  host.recordPlanSignature?.(identity, frozen.value) ??
1474
1534
  renderFeaturePlanSignature(frozen.value),
1535
+ ...(feature.placement === undefined ? {} : { placement: feature.placement }),
1475
1536
  plan: frozen.value,
1476
1537
  });
1477
1538
  for (const resource of frozen.value.resources) {
1478
1539
  if (resource.kind !== 'fullscreen-program') continue;
1479
- fullscreenEffects.set(identity, {
1540
+ const entry = {
1480
1541
  source: resource.source,
1542
+ ...(resource.fragmentEntryPoint === undefined
1543
+ ? {}
1544
+ : { fragmentEntryPoint: resource.fragmentEntryPoint }),
1481
1545
  ...(resource.reads === undefined ? {} : { reads: resource.reads }),
1546
+ ...(resource.usesView === undefined ? {} : { usesView: resource.usesView }),
1547
+ ...(resource.storageBindings === undefined
1548
+ ? {}
1549
+ : { storageBindings: resource.storageBindings }),
1482
1550
  ...(resource.params === undefined ? {} : { params: resource.params }),
1483
- });
1551
+ } satisfies PostProcessShaderEntry;
1552
+ // A feature may expose more than one graph-local fullscreen producer
1553
+ // (the cloud display composite and its shadow map). Keep each resource
1554
+ // addressable by its declared name and retain the feature identity alias
1555
+ // for the original public post-process route.
1556
+ fullscreenEffects.set(resource.name, entry);
1557
+ if (!fullscreenEffects.has(identity)) fullscreenEffects.set(identity, entry);
1484
1558
  }
1485
1559
  const prepared = preparePlanResources(slot, frozen.value, graphics, gpu, input.targets ?? []);
1486
1560
  if (!prepared.ok) {
@@ -1512,7 +1586,33 @@ export function runRenderFeatureFrame(
1512
1586
  : (descriptor: import('./prepared-graphics').RenderFeatureGraphicsPassDescriptor) => {
1513
1587
  // A pending pass can be skipped while earlier passes remain usable.
1514
1588
  // Keep their shared resolver leases alive until submission retires them.
1515
- return resolveGraphicsSnapshot(resolver, descriptor, transaction.generation);
1589
+ const resolveGpuResource = (name: string) => {
1590
+ const owned = prepared.value.buffers.get(name);
1591
+ const borrowed = prepared.value.preparedGpuResources.get(name);
1592
+ const reference =
1593
+ owned ?? (borrowed?.kind === 'buffer' ? borrowed.reference : undefined);
1594
+ return reference === undefined ? undefined : gpu?.resolveBuffer(reference);
1595
+ };
1596
+ const resolved = resolveGraphicsSnapshot(
1597
+ resolver,
1598
+ descriptor,
1599
+ transaction.generation,
1600
+ resolveGpuResource,
1601
+ );
1602
+ // A pending pipeline is a retryable partial frame. The resolver
1603
+ // leases cover resources used by earlier ready passes and must
1604
+ // stay retained until the submitted batch retires; only terminal
1605
+ // resolution failures release them here.
1606
+ if (
1607
+ !resolved.ok &&
1608
+ !(
1609
+ resolved.error instanceof RenderFeaturePreparationFailedError &&
1610
+ resolved.error.detail.reason === 'pipeline-pending'
1611
+ )
1612
+ ) {
1613
+ resolver.release();
1614
+ }
1615
+ return resolved;
1516
1616
  };
1517
1617
  const releaseResolver = (): void => {
1518
1618
  if (resolver !== undefined) resolver.release();
@@ -1577,6 +1677,7 @@ export function runRenderFeatureFrame(
1577
1677
  errors,
1578
1678
  plans,
1579
1679
  fullscreenEffects,
1680
+ postProcessIdentities: Object.freeze([...postProcessIdentities]),
1580
1681
  preparedResourceBatches,
1581
1682
  requiresPreparedResourceKey,
1582
1683
  hiddenEntityReports: mergeHiddenEntityReports(hiddenEntityReports),
@@ -51,7 +51,13 @@ export interface RenderFeatureFullscreenProgramDeclaration {
51
51
  readonly kind: 'fullscreen-program';
52
52
  readonly name: string;
53
53
  readonly source: string;
54
+ /** Optional fragment entry point when one WGSL module owns multiple passes. */
55
+ readonly fragmentEntryPoint?: string;
54
56
  readonly reads?: readonly RenderFeatureFullscreenRead[];
57
+ /** Bind the renderer-owned View group at group(0) for camera reconstruction. */
58
+ readonly usesView?: boolean;
59
+ /** Read-only storage buffers appended after the regular color/depth reads. */
60
+ readonly storageBindings?: readonly number[];
55
61
  readonly params?: {
56
62
  readonly byteSize: number;
57
63
  readonly defaultValue: Uint8Array;
@@ -275,6 +281,8 @@ export interface RenderFeaturePlannedFrame {
275
281
  readonly generation: number;
276
282
  readonly signature: string;
277
283
  readonly plan: RenderFeaturePlan;
284
+ /** Placement selected by the installed feature; omitted means the legacy post stage. */
285
+ readonly placement?: import('./types').RenderFeaturePlacement;
278
286
  }
279
287
 
280
288
  export interface RenderFeatureDerivedAccess {