@forgeax/engine-render 0.1.20 → 0.1.21

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 (372) hide show
  1. package/README.md +215 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/assembly/device-feature-admission.d.ts +18 -0
  4. package/dist/assembly/device-feature-admission.d.ts.map +1 -0
  5. package/dist/assembly/directional-shadow-inspection.d.ts +38 -0
  6. package/dist/assembly/directional-shadow-inspection.d.ts.map +1 -0
  7. package/dist/assembly/factory.d.ts +21 -18
  8. package/dist/assembly/factory.d.ts.map +1 -1
  9. package/dist/assembly/host-contract.d.ts +9 -0
  10. package/dist/assembly/host-contract.d.ts.map +1 -1
  11. package/dist/assembly/render-target-host.d.ts +46 -0
  12. package/dist/assembly/render-target-host.d.ts.map +1 -0
  13. package/dist/assets/render-asset-port.d.ts.map +1 -1
  14. package/dist/authoring.mjs +3 -0
  15. package/dist/authoring.mjs.map +1 -1
  16. package/dist/capture/cube-views.d.ts +21 -0
  17. package/dist/capture/cube-views.d.ts.map +1 -0
  18. package/dist/capture/scheduler.d.ts +54 -0
  19. package/dist/capture/scheduler.d.ts.map +1 -0
  20. package/dist/{chunk-4XINDLAY.mjs → chunk-5YADUCA7.mjs} +1552 -1361
  21. package/dist/chunk-5YADUCA7.mjs.map +1 -0
  22. package/dist/{chunk-TBQJOK2L.mjs → chunk-6OPXDQ2E.mjs} +364 -51
  23. package/dist/chunk-6OPXDQ2E.mjs.map +1 -0
  24. package/dist/{chunk-M2MP4ZOU.mjs → chunk-BXBQQKFQ.mjs} +3 -9
  25. package/dist/chunk-BXBQQKFQ.mjs.map +1 -0
  26. package/dist/{chunk-CDTWTHZR.mjs → chunk-FS2KNUQH.mjs} +548 -6
  27. package/dist/chunk-FS2KNUQH.mjs.map +1 -0
  28. package/dist/components/camera.d.ts +1 -0
  29. package/dist/components/camera.d.ts.map +1 -1
  30. package/dist/components/cube-camera.d.ts +20 -0
  31. package/dist/components/cube-camera.d.ts.map +1 -0
  32. package/dist/components/directional-light.d.ts +11 -2
  33. package/dist/components/directional-light.d.ts.map +1 -1
  34. package/dist/components/directional-shadow-filter.d.ts +25 -0
  35. package/dist/components/directional-shadow-filter.d.ts.map +1 -0
  36. package/dist/components/index.d.ts +2 -0
  37. package/dist/components/index.d.ts.map +1 -1
  38. package/dist/components/light-helpers.d.ts.map +1 -1
  39. package/dist/components/reflection-probe.d.ts +17 -0
  40. package/dist/components/reflection-probe.d.ts.map +1 -0
  41. package/dist/construct-renderer.mjs +5826 -1003
  42. package/dist/construct-renderer.mjs.map +1 -1
  43. package/dist/environment/frame.d.ts.map +1 -1
  44. package/dist/environment/lifecycle.d.ts +5 -0
  45. package/dist/environment/lifecycle.d.ts.map +1 -1
  46. package/dist/errors/render.d.ts +141 -93
  47. package/dist/errors/render.d.ts.map +1 -1
  48. package/dist/extract/directional-shadow-projection.d.ts +23 -0
  49. package/dist/extract/directional-shadow-projection.d.ts.map +1 -0
  50. package/dist/extract/environment.d.ts +14 -0
  51. package/dist/extract/environment.d.ts.map +1 -1
  52. package/dist/extract/visibility.d.ts.map +1 -1
  53. package/dist/features/fullscreen.d.ts.map +1 -1
  54. package/dist/features/targets.d.ts.map +1 -1
  55. package/dist/features/types.d.ts +10 -0
  56. package/dist/features/types.d.ts.map +1 -1
  57. package/dist/fullscreen-post-process-pass.d.ts +13 -2
  58. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  59. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  60. package/dist/gpu-driven/production-raster.d.ts +0 -1
  61. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  62. package/dist/gpu-scene.d.ts.map +1 -1
  63. package/dist/hdrp-buffers.d.ts +19 -17
  64. package/dist/hdrp-buffers.d.ts.map +1 -1
  65. package/dist/ibl/IblPipelineCache.d.ts +4 -0
  66. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  67. package/dist/ibl/kernel-cache.d.ts +36 -0
  68. package/dist/ibl/kernel-cache.d.ts.map +1 -0
  69. package/dist/index.d.ts +28 -3
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.mjs +4 -4
  72. package/dist/index.mjs.map +1 -1
  73. package/dist/inspection-types.d.ts +51 -0
  74. package/dist/inspection-types.d.ts.map +1 -1
  75. package/dist/instances-derived-bounds.d.ts +50 -0
  76. package/dist/instances-derived-bounds.d.ts.map +1 -0
  77. package/dist/internal.mjs +1 -1
  78. package/dist/light-buffer-layout.d.ts +10 -1
  79. package/dist/light-buffer-layout.d.ts.map +1 -1
  80. package/dist/mesh-material-bindings.d.ts +54 -0
  81. package/dist/mesh-material-bindings.d.ts.map +1 -1
  82. package/dist/pbr-pipeline.d.ts +14 -4
  83. package/dist/pbr-pipeline.d.ts.map +1 -1
  84. package/dist/pipeline/standard-forward-lane.d.ts +2 -1
  85. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  86. package/dist/pipeline/standard-lighting/errors.d.ts +4 -0
  87. package/dist/pipeline/standard-lighting/errors.d.ts.map +1 -0
  88. package/dist/pipeline/standard-lighting/graph.d.ts +24 -0
  89. package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -0
  90. package/dist/pipeline/standard-lighting/inspection.d.ts +38 -0
  91. package/dist/pipeline/standard-lighting/inspection.d.ts.map +1 -0
  92. package/dist/pipeline/standard-lighting/layout.d.ts +28 -0
  93. package/dist/pipeline/standard-lighting/layout.d.ts.map +1 -0
  94. package/dist/pipeline/standard-lighting/light-frame.d.ts +33 -0
  95. package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -0
  96. package/dist/pipeline/standard-lighting/membership.d.ts +14 -0
  97. package/dist/pipeline/standard-lighting/membership.d.ts.map +1 -0
  98. package/dist/pipeline/standard-lighting/prepare.d.ts +16 -0
  99. package/dist/pipeline/standard-lighting/prepare.d.ts.map +1 -0
  100. package/dist/pipeline/standard-lighting/topology.d.ts +45 -0
  101. package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -0
  102. package/dist/pipeline/standard-lighting/transport.d.ts +36 -0
  103. package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -0
  104. package/dist/pipeline/standard-pipeline.d.ts +4 -13
  105. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  106. package/dist/pipeline/standard-post.d.ts.map +1 -1
  107. package/dist/pipeline/standard-profile.d.ts +5 -26
  108. package/dist/pipeline/standard-profile.d.ts.map +1 -1
  109. package/dist/pipeline-spec.d.ts +40 -7
  110. package/dist/pipeline-spec.d.ts.map +1 -1
  111. package/dist/point-shadow-inspection.d.ts +21 -0
  112. package/dist/point-shadow-inspection.d.ts.map +1 -0
  113. package/dist/record/frame-lighting.d.ts +27 -32
  114. package/dist/record/frame-lighting.d.ts.map +1 -1
  115. package/dist/record/frame-snapshot.d.ts +28 -14
  116. package/dist/record/frame-snapshot.d.ts.map +1 -1
  117. package/dist/record/frame-targets.d.ts +5 -0
  118. package/dist/record/frame-targets.d.ts.map +1 -1
  119. package/dist/record/frame.d.ts +10 -4
  120. package/dist/record/frame.d.ts.map +1 -1
  121. package/dist/record/gpu-pass-timing/contract.d.ts +96 -0
  122. package/dist/record/gpu-pass-timing/contract.d.ts.map +1 -0
  123. package/dist/record/gpu-pass-timing/errors.d.ts +18 -0
  124. package/dist/record/gpu-pass-timing/errors.d.ts.map +1 -0
  125. package/dist/record/gpu-pass-timing/index.d.ts +8 -0
  126. package/dist/record/gpu-pass-timing/index.d.ts.map +1 -0
  127. package/dist/record/gpu-pass-timing/parser.d.ts +11 -0
  128. package/dist/record/gpu-pass-timing/parser.d.ts.map +1 -0
  129. package/dist/record/gpu-pass-timing/session.d.ts +43 -0
  130. package/dist/record/gpu-pass-timing/session.d.ts.map +1 -0
  131. package/dist/record/gpu-timing.d.ts +8 -3
  132. package/dist/record/gpu-timing.d.ts.map +1 -1
  133. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  134. package/dist/record/main-pass-material.d.ts +22 -1
  135. package/dist/record/main-pass-material.d.ts.map +1 -1
  136. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  137. package/dist/record/main-pass.d.ts +2 -0
  138. package/dist/record/main-pass.d.ts.map +1 -1
  139. package/dist/record/render-context.d.ts +43 -2
  140. package/dist/record/render-context.d.ts.map +1 -1
  141. package/dist/record/shadow-pass.d.ts.map +1 -1
  142. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  143. package/dist/record/typed-frame-graph.d.ts +72 -4
  144. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  145. package/dist/record/view-ubo.d.ts +7 -4
  146. package/dist/record/view-ubo.d.ts.map +1 -1
  147. package/dist/reflection/filter.d.ts +22 -0
  148. package/dist/reflection/filter.d.ts.map +1 -0
  149. package/dist/reflection/gpu-table.d.ts +25 -0
  150. package/dist/reflection/gpu-table.d.ts.map +1 -0
  151. package/dist/reflection/projection.d.ts +91 -0
  152. package/dist/reflection/projection.d.ts.map +1 -0
  153. package/dist/render-contract.d.ts +49 -7
  154. package/dist/render-contract.d.ts.map +1 -1
  155. package/dist/render-graph-primitives.d.ts +1 -0
  156. package/dist/render-graph-primitives.d.ts.map +1 -1
  157. package/dist/render-pipeline.d.ts +42 -2
  158. package/dist/render-pipeline.d.ts.map +1 -1
  159. package/dist/render-system-extract.d.ts +58 -17
  160. package/dist/render-system-extract.d.ts.map +1 -1
  161. package/dist/render-system.d.ts +17 -2
  162. package/dist/render-system.d.ts.map +1 -1
  163. package/dist/renderer.d.ts +43 -0
  164. package/dist/renderer.d.ts.map +1 -0
  165. package/dist/scene/render-scene.d.ts +14 -0
  166. package/dist/scene/render-scene.d.ts.map +1 -1
  167. package/dist/shadow-atlas.d.ts +3 -3
  168. package/dist/shadow-atlas.d.ts.map +1 -1
  169. package/dist/ssao-buffers.d.ts +3 -0
  170. package/dist/ssao-buffers.d.ts.map +1 -1
  171. package/dist/targets/contracts.d.ts +66 -0
  172. package/dist/targets/contracts.d.ts.map +1 -0
  173. package/dist/targets/graph-projection.d.ts +29 -0
  174. package/dist/targets/graph-projection.d.ts.map +1 -0
  175. package/dist/targets/material-source.d.ts +34 -0
  176. package/dist/targets/material-source.d.ts.map +1 -0
  177. package/dist/targets/owner.d.ts +32 -0
  178. package/dist/targets/owner.d.ts.map +1 -0
  179. package/dist/targets/physical.d.ts +18 -0
  180. package/dist/targets/physical.d.ts.map +1 -0
  181. package/dist/targets/readback.d.ts +39 -0
  182. package/dist/targets/readback.d.ts.map +1 -0
  183. package/dist/temporal/index.mjs +3 -3
  184. package/dist/typed-render-graph-primitives.d.ts +1 -1
  185. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  186. package/dist/typed-shadow-passes.d.ts +1 -1
  187. package/dist/typed-shadow-passes.d.ts.map +1 -1
  188. package/dist/volume/passes.d.ts +2 -2
  189. package/dist/volume/passes.d.ts.map +1 -1
  190. package/package.json +20 -20
  191. package/src/__tests__/bloom-exact-zero.unit.test.ts +48 -19
  192. package/src/__tests__/bloom-lifecycle.integration.test.ts +41 -13
  193. package/src/__tests__/camera-target-consumption.unit.test.ts +53 -0
  194. package/src/__tests__/cluster-binner.unit.test.ts +35 -0
  195. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +7 -2
  196. package/src/__tests__/cube-camera-scheduler.unit.test.ts +70 -0
  197. package/src/__tests__/cube-camera-single-submit.perf.test.ts +75 -0
  198. package/src/__tests__/cube-camera-view-matrices.unit.test.ts +41 -0
  199. package/src/__tests__/cube-camera-vocabulary.unit.test.ts +47 -0
  200. package/src/__tests__/cube-camera.integration.test.ts +42 -0
  201. package/src/__tests__/device-feature-admission.unit.test.ts +73 -0
  202. package/src/__tests__/direct-light-snapshot.unit.test.ts +3 -3
  203. package/src/__tests__/docs-gate-green.unit.test.ts +38 -0
  204. package/src/__tests__/factory-contract.integration.test.ts +255 -29
  205. package/src/__tests__/fog-component.unit.test.ts +67 -0
  206. package/src/__tests__/fog.integration.test.ts +86 -0
  207. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +37 -0
  208. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +10 -9
  209. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +17 -1
  210. package/src/__tests__/gpu-driven-production.integration.test.ts +16 -17
  211. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +12 -4
  212. package/src/__tests__/gpu-pass-timing-browser-runner.ts +281 -0
  213. package/src/__tests__/gpu-pass-timing-coexistence.integration.test.ts +80 -0
  214. package/src/__tests__/gpu-pass-timing-contract.unit.test.ts +243 -0
  215. package/src/__tests__/gpu-pass-timing-exact-zero.unit.test.ts +131 -0
  216. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +230 -0
  217. package/src/__tests__/gpu-pass-timing-observation.unit.test.ts +233 -0
  218. package/src/__tests__/gpu-pass-timing-session.unit.test.ts +438 -0
  219. package/src/__tests__/gpu-pass-timing.browser.test.ts +26 -0
  220. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +125 -0
  221. package/src/__tests__/hdrp-group2-visibility.unit.test.ts +24 -0
  222. package/src/__tests__/instances-culling.integration.test.ts +94 -0
  223. package/src/__tests__/instances-derived-bounds.unit.test.ts +118 -0
  224. package/src/__tests__/material-observation.integration.test.ts +101 -0
  225. package/src/__tests__/pbr-view-layout.unit.test.ts +16 -0
  226. package/src/__tests__/point-shadow-inspection.unit.test.ts +60 -0
  227. package/src/__tests__/public-surface.test-d.ts +10 -0
  228. package/src/__tests__/reflection-probe-authoring.unit.test.ts +71 -0
  229. package/src/__tests__/reflection-probe-filter.unit.test.ts +54 -0
  230. package/src/__tests__/reflection-probe-ibl-topology.unit.test.ts +45 -0
  231. package/src/__tests__/reflection-probe-pass-encode.integration.test.ts +101 -0
  232. package/src/__tests__/reflection-probe-performance.perf.test.ts +60 -0
  233. package/src/__tests__/reflection-probe-recovery.integration.test.ts +55 -0
  234. package/src/__tests__/reflection-probe-selection.unit.test.ts +46 -0
  235. package/src/__tests__/render-error-code-owner.test-d.ts +18 -3
  236. package/src/__tests__/render-feature-docs.unit.test.ts +1 -1
  237. package/src/__tests__/render-target-admission.unit.test.ts +80 -0
  238. package/src/__tests__/render-target-graph-projection.unit.test.ts +102 -0
  239. package/src/__tests__/render-target-inspection.unit.test.ts +52 -0
  240. package/src/__tests__/render-target-material-source.test-d.ts +30 -0
  241. package/src/__tests__/render-target-material-source.unit.test.ts +97 -0
  242. package/src/__tests__/render-target-owner-placement.unit.test.ts +203 -0
  243. package/src/__tests__/render-target-owner.unit.test.ts +122 -0
  244. package/src/__tests__/render-target-probe-soak.perf.test.ts +94 -0
  245. package/src/__tests__/render-target-public-schema.json +107 -0
  246. package/src/__tests__/render-target-public-schema.unit.test.ts +91 -0
  247. package/src/__tests__/render-target-public-surface.test-d.ts +83 -0
  248. package/src/__tests__/render-target-readback.integration.test.ts +90 -0
  249. package/src/__tests__/render-target-recovery.integration.test.ts +85 -0
  250. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +48 -0
  251. package/src/__tests__/renderer-lifecycle.integration.test.ts +14 -0
  252. package/src/__tests__/shader-stage-surface.unit.test.ts +19 -4
  253. package/src/__tests__/skin-motion-regression.unit.test.ts +132 -0
  254. package/src/__tests__/skinned-shadow-caster.test.ts +11 -2
  255. package/src/__tests__/standard-cluster-capability.integration.test.ts +63 -0
  256. package/src/__tests__/standard-cluster-transport.unit.test.ts +121 -0
  257. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +232 -0
  258. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +8 -5
  259. package/src/__tests__/standard-lighting-inspection.integration.test.ts +138 -0
  260. package/src/__tests__/standard-lighting-ssot.unit.test.ts +18 -0
  261. package/src/__tests__/standard-lighting-write.unit.test.ts +377 -0
  262. package/src/__tests__/standard-lighting.integration.test.ts +67 -0
  263. package/src/__tests__/standard-pipeline.dawn.test.ts +206 -5
  264. package/src/__tests__/standard-pipeline.integration.test.ts +181 -150
  265. package/src/__tests__/standard-probe-binding.unit.test.ts +25 -0
  266. package/src/__tests__/standard-profile-oracle.unit.test.ts +23 -38
  267. package/src/__tests__/typed-pipeline-topology.unit.test.ts +395 -14
  268. package/src/__tests__/urp-pipeline-targets.unit.test.ts +2 -2
  269. package/src/__tests__/visibility-resolver.unit.test.ts +15 -0
  270. package/src/assembly/device-feature-admission.ts +73 -0
  271. package/src/assembly/directional-shadow-inspection.ts +122 -0
  272. package/src/assembly/factory.ts +877 -286
  273. package/src/assembly/host-contract.ts +25 -0
  274. package/src/assembly/render-target-host.ts +847 -0
  275. package/src/assets/asset-decoders.ts +1 -1
  276. package/src/assets/render-asset-port.ts +7 -1
  277. package/src/capture/cube-views.ts +69 -0
  278. package/src/capture/scheduler.ts +265 -0
  279. package/src/components/__tests__/schema-contract.unit.test.ts +12 -0
  280. package/src/components/__tests__/schema-parity.unit.test.ts +9 -0
  281. package/src/components/camera.ts +4 -0
  282. package/src/components/cube-camera.ts +52 -0
  283. package/src/components/directional-light.ts +22 -2
  284. package/src/components/directional-shadow-filter.ts +57 -0
  285. package/src/components/index.ts +2 -0
  286. package/src/components/light-helpers.ts +81 -7
  287. package/src/components/reflection-probe.ts +49 -0
  288. package/src/environment/frame.ts +6 -24
  289. package/src/environment/lifecycle.ts +12 -0
  290. package/src/errors/render.ts +286 -142
  291. package/src/extract/directional-shadow-projection.ts +61 -0
  292. package/src/extract/environment.ts +57 -0
  293. package/src/extract/visibility.ts +25 -1
  294. package/src/features/fullscreen.ts +3 -0
  295. package/src/features/targets.ts +4 -1
  296. package/src/features/types.ts +10 -0
  297. package/src/fullscreen-post-process-pass.ts +43 -2
  298. package/src/gpu-driven/batch-topology.ts +1 -0
  299. package/src/gpu-driven/production-raster.ts +1 -3
  300. package/src/gpu-scene.ts +8 -1
  301. package/src/hdrp-buffers.ts +165 -59
  302. package/src/ibl/IblPipelineCache.ts +11 -0
  303. package/src/ibl/kernel-cache.ts +88 -0
  304. package/src/index.ts +148 -0
  305. package/src/inspection-types.ts +56 -0
  306. package/src/instances-derived-bounds.ts +171 -0
  307. package/src/light-buffer-layout.ts +20 -5
  308. package/src/mesh-material-bindings.ts +132 -0
  309. package/src/pbr-pipeline.ts +106 -17
  310. package/src/pipeline/standard-forward-lane.ts +46 -16
  311. package/src/pipeline/standard-lighting/errors.ts +6 -0
  312. package/src/pipeline/standard-lighting/graph.ts +157 -0
  313. package/src/pipeline/standard-lighting/inspection.ts +88 -0
  314. package/src/pipeline/standard-lighting/layout.ts +31 -0
  315. package/src/pipeline/standard-lighting/light-frame.ts +34 -0
  316. package/src/pipeline/standard-lighting/membership.ts +43 -0
  317. package/src/pipeline/standard-lighting/prepare.ts +37 -0
  318. package/src/pipeline/standard-lighting/topology.ts +120 -0
  319. package/src/pipeline/standard-lighting/transport.ts +77 -0
  320. package/src/pipeline/standard-pipeline.ts +122 -193
  321. package/src/pipeline/standard-post.ts +26 -7
  322. package/src/pipeline/standard-profile.ts +17 -47
  323. package/src/pipeline-spec.ts +84 -21
  324. package/src/point-shadow-inspection.ts +55 -0
  325. package/src/record/__tests__/temporal-payload.unit.test.ts +42 -1
  326. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +73 -0
  327. package/src/record/__tests__/view-ubo-layout.unit.test.ts +40 -0
  328. package/src/record/frame-lighting.ts +367 -427
  329. package/src/record/frame-snapshot.ts +35 -27
  330. package/src/record/frame-targets.ts +17 -0
  331. package/src/record/frame.ts +261 -74
  332. package/src/record/gpu-pass-timing/contract.ts +180 -0
  333. package/src/record/gpu-pass-timing/errors.ts +34 -0
  334. package/src/record/gpu-pass-timing/index.ts +37 -0
  335. package/src/record/gpu-pass-timing/parser.ts +97 -0
  336. package/src/record/gpu-pass-timing/session.ts +938 -0
  337. package/src/record/gpu-timing.ts +21 -12
  338. package/src/record/main-pass-geometry.ts +120 -80
  339. package/src/record/main-pass-material.ts +95 -9
  340. package/src/record/main-pass-sprite-draws.ts +54 -25
  341. package/src/record/main-pass.ts +71 -15
  342. package/src/record/render-context.ts +72 -0
  343. package/src/record/shadow-pass.ts +17 -28
  344. package/src/record/skybox-post-pass.ts +20 -4
  345. package/src/record/typed-frame-graph.ts +893 -44
  346. package/src/record/view-ubo.ts +63 -28
  347. package/src/reflection/filter.ts +81 -0
  348. package/src/reflection/gpu-table.ts +44 -0
  349. package/src/reflection/projection.ts +281 -0
  350. package/src/render-contract.ts +76 -5
  351. package/src/render-graph-primitives.ts +9 -4
  352. package/src/render-pipeline.ts +117 -2
  353. package/src/render-system-extract.ts +557 -100
  354. package/src/render-system.ts +1095 -66
  355. package/src/renderer.ts +222 -0
  356. package/src/scene/render-scene.ts +115 -9
  357. package/src/shadow-atlas.ts +5 -5
  358. package/src/ssao-buffers.ts +9 -2
  359. package/src/targets/contracts.ts +182 -0
  360. package/src/targets/graph-projection.ts +169 -0
  361. package/src/targets/material-source.ts +143 -0
  362. package/src/targets/owner.ts +214 -0
  363. package/src/targets/physical.ts +18 -0
  364. package/src/targets/readback.ts +153 -0
  365. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +59 -20
  366. package/src/typed-render-graph-primitives.ts +5 -3
  367. package/src/typed-shadow-passes.ts +46 -40
  368. package/src/volume/passes.ts +15 -9
  369. package/dist/chunk-4XINDLAY.mjs.map +0 -1
  370. package/dist/chunk-CDTWTHZR.mjs.map +0 -1
  371. package/dist/chunk-M2MP4ZOU.mjs.map +0 -1
  372. package/dist/chunk-TBQJOK2L.mjs.map +0 -1
@@ -1,21 +1,13 @@
1
- import type {
2
- GraphAccess,
3
- GraphBuffer,
4
- GraphTextureDescriptor,
5
- RenderGraphBuilder,
1
+ import {
2
+ type GraphAccess,
3
+ type GraphTextureDescriptor,
4
+ type RenderGraphBuilder,
6
5
  RenderGraphError,
7
6
  } from '@forgeax/engine-render-graph';
8
7
  import { err, ok, type Result } from '@forgeax/engine-types';
9
- import { HdrpDeferredCapsInsufficientError, SceneDataUnavailableError } from '../errors/render';
8
+ import { SceneDataUnavailableError, StandardProfileInvalidError } from '../errors/render';
10
9
  import type { FramePlan } from '../extract/environment';
11
- import {
12
- GPU_BUFFER_USAGE_COPY_DST,
13
- GPU_BUFFER_USAGE_STORAGE,
14
- GPU_BUFFER_USAGE_UNIFORM,
15
- } from '../gpu-usage';
16
- import { getOrCreateHdrpBuffers, type HdrpBuffers } from '../hdrp-buffers';
17
10
  import { DEFERRED_COLOR_FORMATS } from '../pipeline-spec';
18
- import type { _InternalRenderPipelineContext } from '../record/render-context';
19
11
  import type {
20
12
  RenderPipeline,
21
13
  RenderPipelineBuildContext,
@@ -48,16 +40,20 @@ import {
48
40
  } from '../typed-render-graph-primitives';
49
41
  import { addTypedShadowPasses } from '../typed-shadow-passes';
50
42
  import { addAuthoredVolumetricFogPasses } from '../volume/passes';
51
-
52
43
  import { buildStandardForwardLane } from './standard-forward-lane';
44
+ import {
45
+ addStandardClusterMembershipPass,
46
+ importStandardClusterBuffers,
47
+ standardClusterReadAccesses,
48
+ } from './standard-lighting/graph';
49
+ import {
50
+ deriveStandardTopologyInput,
51
+ type StandardTopologyInputValue,
52
+ } from './standard-lighting/topology';
53
53
  import { addStandardPost } from './standard-post';
54
54
  import {
55
- CLUSTER_GRID_STRIDE_U32,
56
- DEFAULT_CLUSTER_GRID,
57
55
  DEFAULT_STANDARD_PROFILE,
58
- LIGHT_INDEX_LIST_CAPACITY,
59
- MAX_LIGHTS,
60
- resolveStandardLane,
56
+ STANDARD_LIGHT_COUNTS,
61
57
  STANDARD_PIPELINE_ID,
62
58
  type StandardProfile,
63
59
  } from './standard-profile';
@@ -125,26 +121,11 @@ fn cs_cluster_membership(@builtin(global_invocation_id) global_id : vec3<u32>) {
125
121
  }
126
122
  }
127
123
  `;
128
- export class HdrpInstallError extends Error {
129
- readonly code = 'hdrp-grid-invalid' as const;
130
- readonly expected = 'clusterGrid.{x,y,z} each integer in [1, 64]';
131
- readonly hint: string;
132
- readonly detail: { readonly x: number; readonly y: number; readonly z: number };
133
-
134
- constructor(x: number, y: number, z: number) {
135
- const hint = `clusterGrid {x:${x}, y:${y}, z:${z}} is invalid; set {x,y,z} to positive integers in [1, 64]`;
136
- super(`hdrp-grid-invalid: ${hint}`);
137
- this.name = 'HdrpInstallError';
138
- this.hint = hint;
139
- this.detail = { x, y, z };
140
- }
141
- }
142
-
143
124
  export function validateClusterGrid(grid: {
144
125
  x: number;
145
126
  y: number;
146
127
  z: number;
147
- }): Result<{ x: number; y: number; z: number }, HdrpInstallError> {
128
+ }): Result<{ x: number; y: number; z: number }, StandardProfileInvalidError> {
148
129
  const { x, y, z } = grid;
149
130
  return Number.isInteger(x) &&
150
131
  Number.isInteger(y) &&
@@ -156,7 +137,12 @@ export function validateClusterGrid(grid: {
156
137
  z >= 1 &&
157
138
  z <= 64
158
139
  ? ok({ x, y, z })
159
- : err(new HdrpInstallError(x, y, z));
140
+ : err(
141
+ new StandardProfileInvalidError(
142
+ `clusterGrid {x:${x}, y:${y}, z:${z}} is invalid; set x, y, and z to positive integers in [1, 64]`,
143
+ { field: 'clusterGrid', actual: { x, y, z } },
144
+ ),
145
+ );
160
146
  }
161
147
 
162
148
  function target(
@@ -167,114 +153,20 @@ function target(
167
153
  return createRenderPipelineTarget(graph, label, descriptor);
168
154
  }
169
155
 
170
- interface HdrpGraphBuffers {
171
- readonly lightData: GraphBuffer;
172
- readonly clusterGrid: GraphBuffer;
173
- readonly lightIndexList: GraphBuffer;
174
- readonly clusterUniform: GraphBuffer;
175
- readonly lightBounds: GraphBuffer;
176
- }
177
-
178
- function importBuffers(
179
- graph: RenderGraphBuilder<RenderPipelineFrame>,
180
- topology: RenderPipelineTopology,
181
- ): Result<HdrpGraphBuffers, RenderGraphError> {
182
- const grid = topology.config?.clusterGrid ?? DEFAULT_CLUSTER_GRID;
183
- const cells = grid.x * grid.y * grid.z;
184
- const storage = topology.lane.storageBuffer;
185
- const usage =
186
- (storage ? GPU_BUFFER_USAGE_STORAGE : GPU_BUFFER_USAGE_UNIFORM) | GPU_BUFFER_USAGE_COPY_DST;
187
- const resolve = (frame: RenderPipelineFrame): HdrpBuffers => {
188
- const internal = frame as _InternalRenderPipelineContext;
189
- const buffers = getOrCreateHdrpBuffers(internal.runtime, grid);
190
- if (buffers === null) throw new Error('HDRP persistent buffer allocation failed');
191
- return buffers;
192
- };
193
- const lightData = graph.importBuffer(
194
- 'hdrp-light-data',
195
- { size: (storage ? MAX_LIGHTS : 128) * 64, usage },
196
- (frame) => resolve(frame).lightDataBuffer,
197
- );
198
- if (!lightData.ok) return lightData;
199
- const clusterGrid = graph.importBuffer(
200
- 'hdrp-cluster-grid',
201
- { size: storage ? cells * CLUSTER_GRID_STRIDE_U32 * 4 : 32, usage },
202
- (frame) => resolve(frame).clusterGridBuffer,
203
- );
204
- if (!clusterGrid.ok) return clusterGrid;
205
- const lightIndexList = graph.importBuffer(
206
- 'hdrp-light-index-list',
207
- { size: storage ? LIGHT_INDEX_LIST_CAPACITY * 4 : 32, usage },
208
- (frame) => resolve(frame).lightIndexListBuffer,
209
- );
210
- if (!lightIndexList.ok) return lightIndexList;
211
- const clusterUniform = graph.importBuffer(
212
- 'hdrp-cluster-uniform',
213
- { size: 32, usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST },
214
- (frame) => resolve(frame).clusterUniformBuffer,
215
- );
216
- if (!clusterUniform.ok) return clusterUniform;
217
- const lightBounds = graph.importBuffer(
218
- 'hdrp-light-bounds',
219
- { size: MAX_LIGHTS * 6 * 4, usage },
220
- (frame) => resolve(frame).lightBoundsBuffer,
221
- );
222
- if (!lightBounds.ok) return lightBounds;
223
- return ok({
224
- lightData: lightData.value,
225
- clusterGrid: clusterGrid.value,
226
- lightIndexList: lightIndexList.value,
227
- clusterUniform: clusterUniform.value,
228
- lightBounds: lightBounds.value,
229
- });
230
- }
231
-
232
- function addMembershipPass(
233
- graph: RenderGraphBuilder<RenderPipelineFrame>,
234
- topology: RenderPipelineTopology,
235
- buffers: HdrpGraphBuffers,
236
- ): Result<void, RenderGraphError> {
237
- if (!topology.lane.compute || !topology.lane.storageBuffer) return ok(undefined);
238
- return graph.addComputePass('cluster-membership-producer', {
239
- accesses: [
240
- { resource: buffers.clusterGrid, usage: 'storage-read' },
241
- { resource: buffers.clusterUniform, usage: 'uniform-read' },
242
- { resource: buffers.lightBounds, usage: 'storage-read' },
243
- { resource: buffers.lightIndexList, usage: 'storage-write' },
244
- ],
245
- executeIf: (frame) => {
246
- const internal = frame as _InternalRenderPipelineContext;
247
- return (
248
- internal.pipelineState.hdrpClusterMembershipPipeline !== null &&
249
- internal.hdrpClusterMembershipBindGroup !== null
250
- );
251
- },
252
- encode: ({ pass, frame }) => {
253
- const internal = frame as _InternalRenderPipelineContext;
254
- const pipeline = internal.pipelineState.hdrpClusterMembershipPipeline;
255
- const bindings = internal.hdrpClusterMembershipBindGroup;
256
- if (pipeline === null || bindings === null) return;
257
- const grid = topology.config?.clusterGrid ?? DEFAULT_CLUSTER_GRID;
258
- pass.setPipeline(pipeline);
259
- pass.setBindGroup(0, bindings);
260
- pass.dispatchWorkgroups(Math.ceil((grid.x * grid.y * grid.z) / 64));
261
- },
262
- });
263
- }
264
-
265
- function buildHdrp(
156
+ function buildStandardDeferredLane(
266
157
  context: RenderPipelineBuildContext<RenderPipelineFrame>,
267
158
  topology: RenderPipelineTopology,
159
+ lighting: StandardTopologyInputValue,
268
160
  ): ReturnType<RenderPipeline['build']> {
269
- if (topology.lane.maxColorAttachments < 4) {
270
- return err(new HdrpDeferredCapsInsufficientError(topology.lane.maxColorAttachments));
271
- }
272
161
  const graph = context.graph;
273
162
  const surface = importRenderPipelineSurface(graph, topology);
274
163
  if (!surface.ok) return surface;
275
- const buffers = importBuffers(graph, topology);
164
+ const buffers = importStandardClusterBuffers(graph, lighting);
276
165
  if (!buffers.ok) return buffers;
277
- const membership = addMembershipPass(graph, topology, buffers.value);
166
+ const membership =
167
+ buffers.value === null
168
+ ? ok(undefined)
169
+ : addStandardClusterMembershipPass(graph, lighting, buffers.value);
278
170
  if (!membership.ok) return membership;
279
171
  const shadows = addTypedShadowPasses(graph, topology);
280
172
  if (!shadows.ok) return shadows;
@@ -305,9 +197,7 @@ function buildHdrp(
305
197
  taa: topology.camera.antialias === 'taa',
306
198
  motionBlur: topology.temporal?.motionBlur === true,
307
199
  });
308
- const temporalLane = topology.lane.compute
309
- ? (topology.standardProfile?.lighting ?? 'direct')
310
- : 'cpu-webgl2';
200
+ const temporalLane = topology.lane.compute ? 'clustered' : 'cpu-webgl2';
311
201
  const temporalAdmission = standardTemporalLaneAdmission({
312
202
  lane: temporalLane,
313
203
  demand: temporalDemand,
@@ -330,7 +220,11 @@ function buildHdrp(
330
220
  }),
331
221
  );
332
222
  }
333
-
223
+ const gpuDriven = context.projectGpuDriven({
224
+ format: scene.value.format,
225
+ sampleCount: scene.value.sampleCount,
226
+ });
227
+ if (!gpuDriven.ok) return gpuDriven;
334
228
  const gbuffer = addTypedScenePass(graph, {
335
229
  name: 'g-buffer',
336
230
  color: gbuffer0.value,
@@ -363,25 +257,13 @@ function buildHdrp(
363
257
  { resource: gbuffer1.value.view, usage: 'sampled-read' },
364
258
  { resource: gbuffer2.value.view, usage: 'sampled-read' },
365
259
  { resource: depth.value.view, usage: 'depth-stencil-read' },
366
- {
367
- resource: buffers.value.lightData,
368
- usage: topology.lane.storageBuffer ? 'storage-read' : 'uniform-read',
369
- },
370
- {
371
- resource: buffers.value.clusterGrid,
372
- usage: topology.lane.storageBuffer ? 'storage-read' : 'uniform-read',
373
- },
374
- {
375
- resource: buffers.value.lightIndexList,
376
- usage: topology.lane.storageBuffer ? 'storage-read' : 'uniform-read',
377
- },
378
- { resource: buffers.value.clusterUniform, usage: 'uniform-read' },
260
+ ...(buffers.value === null ? [] : standardClusterReadAccesses(buffers.value)),
379
261
  ...(ssao === undefined
380
262
  ? []
381
263
  : ([{ resource: ssao.view, usage: 'sampled-read' }] satisfies GraphAccess[])),
382
264
  { resource: scene.value.view, usage: 'color-attachment' },
383
265
  ];
384
- const lighting = graph.addRasterPass('lighting', {
266
+ const lightingPass = graph.addRasterPass('lighting', {
385
267
  accesses: lightingAccesses,
386
268
  colorAttachments: [
387
269
  {
@@ -393,7 +275,7 @@ function buildHdrp(
393
275
  ],
394
276
  encode: () => undefined,
395
277
  });
396
- if (!lighting.ok) return lighting;
278
+ if (!lightingPass.ok) return lightingPass;
397
279
 
398
280
  const skybox = addTypedSkyboxPass(graph, scene.value);
399
281
  if (!skybox.ok) return skybox;
@@ -403,21 +285,8 @@ function buildHdrp(
403
285
  demand: transmissionDemand,
404
286
  sourceSampleCount: scene.value.sampleCount,
405
287
  }).active;
406
- const forwardExtraAccesses: GraphAccess[] = [
407
- {
408
- resource: buffers.value.lightData,
409
- usage: topology.lane.storageBuffer ? 'storage-read' : 'uniform-read',
410
- },
411
- {
412
- resource: buffers.value.clusterGrid,
413
- usage: topology.lane.storageBuffer ? 'storage-read' : 'uniform-read',
414
- },
415
- {
416
- resource: buffers.value.lightIndexList,
417
- usage: topology.lane.storageBuffer ? 'storage-read' : 'uniform-read',
418
- },
419
- { resource: buffers.value.clusterUniform, usage: 'uniform-read' },
420
- ];
288
+ const forwardExtraAccesses: GraphAccess[] =
289
+ buffers.value === null ? [] : [...standardClusterReadAccesses(buffers.value)];
421
290
 
422
291
  const forward = addTypedScenePass(graph, {
423
292
  name: 'forward',
@@ -430,14 +299,17 @@ function buildHdrp(
430
299
  gbuffer0.value,
431
300
  gbuffer1.value,
432
301
  gbuffer2.value,
433
- shadows.value.directional,
302
+ ...(shadows.value.directional === undefined ? [] : [shadows.value.directional]),
434
303
  shadows.value.spot,
435
304
  ...(shadows.value.point === undefined ? [] : [shadows.value.point]),
436
305
  ...(ssao === undefined ? [] : [ssao]),
437
306
  ],
438
- directionalShadow: shadows.value.directional,
307
+ ...(shadows.value.directional === undefined
308
+ ? {}
309
+ : { directionalShadow: shadows.value.directional }),
439
310
  spotShadow: shadows.value.spot,
440
311
  ...(ssao === undefined ? {} : { ssao }),
312
+ ...(gpuDriven.value === undefined ? {} : { gpuDriven: gpuDriven.value }),
441
313
  passKind: 'forward',
442
314
  ...(transmissionActive ? { recordMode: 'opaque' as const } : {}),
443
315
  extraAccesses: forwardExtraAccesses,
@@ -472,7 +344,7 @@ function buildHdrp(
472
344
  gbuffer0.value,
473
345
  gbuffer1.value,
474
346
  gbuffer2.value,
475
- shadows.value.directional,
347
+ ...(shadows.value.directional === undefined ? [] : [shadows.value.directional]),
476
348
  shadows.value.spot,
477
349
  ...(shadows.value.point === undefined ? [] : [shadows.value.point]),
478
350
  ...(ssao === undefined ? [] : [ssao]),
@@ -501,7 +373,7 @@ function buildHdrp(
501
373
  gbuffer0.value,
502
374
  gbuffer1.value,
503
375
  gbuffer2.value,
504
- shadows.value.directional,
376
+ ...(shadows.value.directional === undefined ? [] : [shadows.value.directional]),
505
377
  shadows.value.spot,
506
378
  ...(shadows.value.point === undefined ? [] : [shadows.value.point]),
507
379
  ...(ssao === undefined ? [] : [ssao]),
@@ -529,7 +401,7 @@ function buildHdrp(
529
401
  topology,
530
402
  scene.value,
531
403
  depthSample.value,
532
- shadows.value.directional.view,
404
+ shadows.value.directional?.view,
533
405
  shadows.value.spot.view,
534
406
  );
535
407
  if (!volume.ok) return volume;
@@ -556,8 +428,7 @@ function buildHdrp(
556
428
  if (!features.ok) return features;
557
429
  const observation = addTypedFrameObservationPass(graph, scene.value, 'forgeax::standard');
558
430
  if (!observation.ok) return observation;
559
-
560
- return addStandardPost(
431
+ const post = addStandardPost(
561
432
  context,
562
433
  topology,
563
434
  scene.value,
@@ -565,13 +436,19 @@ function buildHdrp(
565
436
  surface.value,
566
437
  temporal.value?.temporal,
567
438
  );
439
+ if (!post.ok) return post;
440
+ const cubeCaptures = context.contributeCubeCaptures?.();
441
+ if (cubeCaptures !== undefined && !cubeCaptures.ok) return cubeCaptures;
442
+ return ok(undefined);
568
443
  }
569
- const buildStandardClusteredLane = buildHdrp;
570
-
571
444
  export interface StandardPipeline extends RenderPipeline {
572
445
  readonly identity: typeof STANDARD_PIPELINE_ID;
573
446
  }
574
447
 
448
+ export function standardProfileSupportsReflectionProbes(_profile: StandardProfile): boolean {
449
+ return true;
450
+ }
451
+
575
452
  /** Standard graph inputs are derived once from the immutable frame plan. */
576
453
  export type StandardFramePlan = FramePlan;
577
454
 
@@ -579,6 +456,43 @@ function profileFor(topology: RenderPipelineTopology): StandardProfile {
579
456
  return topology.standardProfile ?? DEFAULT_STANDARD_PROFILE;
580
457
  }
581
458
 
459
+ function resolveStandardTopologyInput(
460
+ context: RenderPipelineBuildContext<RenderPipelineFrame>,
461
+ ): Result<StandardTopologyInputValue, RenderGraphError> {
462
+ const supplied = context.standardLighting;
463
+ if (supplied !== undefined) {
464
+ const derived = deriveStandardTopologyInput(supplied);
465
+ if (derived.ok) return derived;
466
+ return err(
467
+ new RenderGraphError({
468
+ code: 'resource-descriptor-invalid',
469
+ expected: 'Forward/Deferred consume one prepared Standard topology input',
470
+ hint: derived.error.hint,
471
+ detail: {
472
+ resourceLabel: 'forgeax::standard',
473
+ field: `standardLighting.${derived.error.detail.field}`,
474
+ expected: derived.error.expected,
475
+ actual: 'invalid',
476
+ },
477
+ }),
478
+ );
479
+ }
480
+
481
+ return err(
482
+ new RenderGraphError({
483
+ code: 'resource-descriptor-invalid',
484
+ expected: 'RenderPipelineBuildContext.standardLighting from frame preparation',
485
+ hint: 'prepare Standard lighting once, select a proven Cluster transport, and pass it to both lanes',
486
+ detail: {
487
+ resourceLabel: 'forgeax::standard',
488
+ field: 'standardLighting',
489
+ expected: 'prepared Standard lighting topology input',
490
+ actual: 'invalid',
491
+ },
492
+ }),
493
+ );
494
+ }
495
+
582
496
  function standardTopology(
583
497
  topology: RenderPipelineTopology,
584
498
  profile: StandardProfile,
@@ -596,26 +510,41 @@ function standardTopology(
596
510
  }
597
511
 
598
512
  /**
599
- * The sole built-in pipeline entry point. Direct and clustered lighting are
600
- * execution lanes selected from the same profile and graph owner; callers do
601
- * not install a second topology or submit authority.
513
+ * The sole built-in pipeline entry point. All local lights use the same
514
+ * prepared Cluster topology; the profile chooses only Forward or Deferred
515
+ * graph shape and never a second lighting authority.
602
516
  */
603
517
  function buildStandard(
604
518
  context: RenderPipelineBuildContext<RenderPipelineFrame>,
605
519
  topology: RenderPipelineTopology,
606
520
  ): ReturnType<RenderPipeline['build']> {
607
521
  const profile = profileFor(topology);
608
- const lane = resolveStandardLane(profile, {
609
- compute: topology.lane.compute,
610
- storageBuffer: topology.lane.storageBuffer,
611
- });
522
+ if (!STANDARD_LIGHT_COUNTS.some((count) => count === profile.lightCount)) {
523
+ return err(
524
+ new RenderGraphError({
525
+ code: 'resource-descriptor-invalid',
526
+ expected: 'StandardProfile.lightCount is one of 1, 32, or 256',
527
+ hint: 'set StandardProfile.lightCount to 1, 32, or 256',
528
+ detail: {
529
+ resourceLabel: 'forgeax::standard',
530
+ field: 'lightCount',
531
+ expected: '1 | 32 | 256',
532
+ actual: profile.lightCount,
533
+ },
534
+ }),
535
+ );
536
+ }
612
537
  const effective = standardTopology(topology, profile);
613
- // A missing Camera is an intentional clear-only frame. Keep it on the
614
- // direct raster lane so clustered/HDRP resources cannot introduce work or
615
- // a post-process lookup into this minimal submit contract.
616
- if (effective.clearOnly === true) return buildStandardForwardLane(context, effective);
617
- if (lane === 'clustered') return buildStandardClusteredLane(context, effective);
618
- return buildStandardForwardLane(context, effective);
538
+ const lighting = resolveStandardTopologyInput(context);
539
+ if (!lighting.ok) return lighting;
540
+ // A missing Camera is an intentional clear-only frame. Keep it on the
541
+ // forward raster lane so clustered resources cannot introduce work.
542
+ if (effective.clearOnly === true) {
543
+ return buildStandardForwardLane(context, effective, lighting.value);
544
+ }
545
+ return profile.renderPath === 'forward'
546
+ ? buildStandardForwardLane(context, effective, lighting.value)
547
+ : buildStandardDeferredLane(context, effective, lighting.value);
619
548
  }
620
549
 
621
550
  export const standardPipeline: StandardPipeline = Object.freeze({
@@ -11,7 +11,11 @@ import type {
11
11
  RenderPipelineFrame,
12
12
  RenderPipelineTopology,
13
13
  } from '../render-pipeline';
14
- import { createRenderPipelineTarget, type RenderPipelineTarget } from '../render-pipeline';
14
+ import {
15
+ createRenderPipelineTarget,
16
+ type RenderPipelineTarget,
17
+ resolveOutputDither,
18
+ } from '../render-pipeline';
15
19
  import {
16
20
  addTypedBloomPasses,
17
21
  addTypedCompositePostEffects,
@@ -134,7 +138,7 @@ function missingTemporalContributor(
134
138
  return new SceneDataUnavailableError({
135
139
  featureIdentity: 'forgeax::standard',
136
140
  schema: 'forgeax::scene-data::temporal-v1',
137
- lane: topology.lane.compute ? (topology.standardProfile?.lighting ?? 'direct') : 'cpu-webgl2',
141
+ lane: topology.lane.compute ? 'clustered' : 'cpu-webgl2',
138
142
  reason: 'producer-missing',
139
143
  missingContributorIds: [contributorId],
140
144
  omittedMissingContributorCount: 0,
@@ -165,9 +169,13 @@ export function addStandardPost(
165
169
  // left clear and the following Output Transform presents a black frame.
166
170
  const bloomActive = hdr && topology.camera.bloom === 'on' && topology.camera.tonemap !== 'none';
167
171
  const fxaa = !clearOnly && topology.camera.antialias === 'fxaa';
168
- const msaa = !clearOnly && topology.camera.antialias === 'msaa' && topology.lane.multisample;
169
- const linearLdr = !clearOnly && ((!hdr && topology.lane.storageBuffer) || fxaa || msaa);
172
+ // All non-clear Standard frames use the graph-owned float scene target and
173
+ // the shared Output Transform, even when AA is disabled or the backend has
174
+ // no storage buffers. This keeps the final dither at one owner-level
175
+ // surface boundary instead of making no-AA/WebGL2 a silent bypass.
176
+ const linearLdr = !clearOnly && !hdr;
170
177
  const rawOnly = !clearOnly && topology.surface.profile?.kind === 'raw-only';
178
+ const outputDither = resolveOutputDither(topology.config);
171
179
  const motionBlur = !clearOnly && topology.temporal?.motionBlur === true;
172
180
  const postEffects = clearOnly ? [] : (topology.config?.postEffects ?? []);
173
181
  const rawOnlyPostInput =
@@ -312,7 +320,11 @@ export function addStandardPost(
312
320
  domain: 'display-encoded',
313
321
  });
314
322
  if (!output.ok) return output;
315
- const outputTransform = addTypedOutputTransformPass(context.graph, postInput, output.value);
323
+ // FXAA samples this display-encoded intermediate; leave dither to the
324
+ // final FXAA writer so the same pixel is not dithered twice.
325
+ const outputTransform = addTypedOutputTransformPass(context.graph, postInput, output.value, {
326
+ dither: false,
327
+ });
316
328
  if (!outputTransform.ok) return outputTransform;
317
329
  const aa = addTypedFullscreenPass(context.graph, {
318
330
  name: 'fxaa',
@@ -326,13 +338,20 @@ export function addStandardPost(
326
338
  context.graph,
327
339
  postInput,
328
340
  rawOnlyPostInput.value,
341
+ {
342
+ dither: outputDither && postEffects.length === 0,
343
+ },
329
344
  );
330
345
  if (!transformed.ok) return transformed;
331
346
  } else if (rawOnly || linearLdr) {
332
- const transformed = addTypedOutputTransformPass(context.graph, postInput, finalPresent);
347
+ const transformed = addTypedOutputTransformPass(context.graph, postInput, finalPresent, {
348
+ dither: outputDither,
349
+ });
333
350
  if (!transformed.ok) return transformed;
334
351
  } else {
335
- const outputTransform = addTypedOutputTransformPass(context.graph, postInput, finalPresent);
352
+ const outputTransform = addTypedOutputTransformPass(context.graph, postInput, finalPresent, {
353
+ dither: outputDither,
354
+ });
336
355
  if (!outputTransform.ok) return outputTransform;
337
356
  }
338
357
  }
@@ -1,25 +1,22 @@
1
1
  export const STANDARD_PIPELINE_ID = 'forgeax::standard' as const;
2
2
  export const STANDARD_LIGHT_COUNTS = [1, 32, 256] as const;
3
- /** Shared clustered-lighting facts consumed by pipeline, record, and buffers. */
4
- export const DEFAULT_CLUSTER_GRID = { x: 16, y: 9, z: 24 } as const;
5
- export const CLUSTER_GRID_STRIDE_U32 = 2;
6
- export const LIGHT_INDEX_LIST_CAPACITY = 1048576;
7
- export const MAX_LIGHTS = 256;
3
+ export {
4
+ CLUSTER_GRID_STRIDE_U32,
5
+ DEFAULT_CLUSTER_GRID,
6
+ LIGHT_INDEX_LIST_CAPACITY,
7
+ MAX_LIGHTS,
8
+ } from './standard-lighting/layout';
8
9
 
9
10
  export type StandardLightCount = (typeof STANDARD_LIGHT_COUNTS)[number];
10
- export type StandardLightingLane = 'direct' | 'clustered';
11
- export type StandardFallbackLane = 'native' | 'cpu-webgl2';
12
11
  export type StandardShadowMode = 'off' | 'hard' | 'filtered';
13
- export type StandardTone =
14
- | 'none'
15
- | 'aces-filmic'
16
- | 'agx'
17
- | 'cineon'
18
- | 'linear'
19
- | 'neutral'
20
- | 'reinhard'
21
- | 'reinhard-extended';
22
- export type StandardAntialias = 'none' | 'fxaa' | 'msaa';
12
+ export type StandardVolumetricFogQuality = 'low' | 'high';
13
+
14
+ export interface StandardVolumetricFogProfile {
15
+ readonly quality: StandardVolumetricFogQuality;
16
+ readonly depth: 48 | 64;
17
+ readonly tileSize: 4 | 16;
18
+ }
19
+
23
20
  export const STANDARD_POST_STAGE_NAMES = [
24
21
  'transparent-blend',
25
22
  'bloom',
@@ -29,45 +26,28 @@ export const STANDARD_POST_STAGE_NAMES = [
29
26
  'present',
30
27
  ] as const;
31
28
  export type StandardPostStage = (typeof STANDARD_POST_STAGE_NAMES)[number];
32
- export type StandardVolumetricFogQuality = 'low' | 'high';
33
-
34
- export interface StandardVolumetricFogProfile {
35
- readonly quality: StandardVolumetricFogQuality;
36
- readonly depth: 48 | 64;
37
- readonly tileSize: 4 | 16;
38
- }
39
-
40
29
  export interface StandardProfile {
41
30
  readonly pipelineId: typeof STANDARD_PIPELINE_ID;
42
31
  readonly lightCount: StandardLightCount;
43
- readonly lighting: StandardLightingLane;
32
+ /** Graph topology selector; all local lights use the shared Cluster path. */
33
+ readonly renderPath: 'forward' | 'deferred';
44
34
  readonly shadows: StandardShadowMode;
45
35
  readonly pbr: boolean;
46
36
  readonly ibl: boolean;
47
37
  readonly ssao: boolean;
48
- readonly bloom: boolean;
49
- readonly tone: StandardTone;
50
- readonly antialias: StandardAntialias;
51
- readonly sky: boolean;
52
38
  /** Renderer-owned 1080p volume profile; tile width/height remain surface-derived. */
53
39
  readonly volumetricFog?: StandardVolumetricFogProfile | undefined;
54
- readonly fallback: StandardFallbackLane;
55
40
  readonly postStages: typeof STANDARD_POST_STAGE_NAMES;
56
41
  }
57
42
 
58
43
  export const DEFAULT_STANDARD_PROFILE: StandardProfile = Object.freeze({
59
44
  pipelineId: STANDARD_PIPELINE_ID,
60
45
  lightCount: 32,
61
- lighting: 'direct',
46
+ renderPath: 'forward',
62
47
  shadows: 'filtered',
63
48
  pbr: true,
64
49
  ibl: true,
65
50
  ssao: false,
66
- bloom: true,
67
- tone: 'aces-filmic',
68
- antialias: 'fxaa',
69
- sky: true,
70
- fallback: 'native',
71
51
  postStages: STANDARD_POST_STAGE_NAMES,
72
52
  });
73
53
 
@@ -76,13 +56,3 @@ export function resolveVolumetricFogProfile(
76
56
  ): StandardVolumetricFogProfile {
77
57
  return profile.volumetricFog ?? { quality: 'high', depth: 64, tileSize: 4 };
78
58
  }
79
-
80
- export type StandardLane = StandardLightingLane | 'cpu-webgl2';
81
-
82
- export function resolveStandardLane(
83
- profile: Pick<StandardProfile, 'lighting'>,
84
- capabilities: { readonly compute: boolean; readonly storageBuffer: boolean },
85
- ): StandardLane {
86
- if (!capabilities.compute || !capabilities.storageBuffer) return 'cpu-webgl2';
87
- return profile.lighting;
88
- }