@forgeax/engine-render 0.1.19 → 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 (452) 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 +34 -20
  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-RJRUS55L.mjs → chunk-5YADUCA7.mjs} +1896 -1624
  21. package/dist/chunk-5YADUCA7.mjs.map +1 -0
  22. package/dist/{chunk-LOSO4CTV.mjs → chunk-6OPXDQ2E.mjs} +423 -46
  23. package/dist/chunk-6OPXDQ2E.mjs.map +1 -0
  24. package/dist/{chunk-IPXCABEP.mjs → chunk-BXBQQKFQ.mjs} +3 -9
  25. package/dist/chunk-BXBQQKFQ.mjs.map +1 -0
  26. package/dist/chunk-FS2KNUQH.mjs +1294 -0
  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/components/spot-light.d.ts +12 -0
  42. package/dist/components/spot-light.d.ts.map +1 -1
  43. package/dist/construct-renderer.mjs +8552 -1612
  44. package/dist/construct-renderer.mjs.map +1 -1
  45. package/dist/device/gpu-residency.d.ts +19 -2
  46. package/dist/device/gpu-residency.d.ts.map +1 -1
  47. package/dist/environment/frame.d.ts.map +1 -1
  48. package/dist/environment/lifecycle.d.ts +5 -0
  49. package/dist/environment/lifecycle.d.ts.map +1 -1
  50. package/dist/errors/render.d.ts +209 -93
  51. package/dist/errors/render.d.ts.map +1 -1
  52. package/dist/extract/directional-shadow-projection.d.ts +23 -0
  53. package/dist/extract/directional-shadow-projection.d.ts.map +1 -0
  54. package/dist/extract/environment.d.ts +14 -0
  55. package/dist/extract/environment.d.ts.map +1 -1
  56. package/dist/extract/visibility.d.ts.map +1 -1
  57. package/dist/features/fullscreen.d.ts.map +1 -1
  58. package/dist/features/targets.d.ts.map +1 -1
  59. package/dist/features/types.d.ts +10 -0
  60. package/dist/features/types.d.ts.map +1 -1
  61. package/dist/fullscreen-post-process-pass.d.ts +13 -2
  62. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  63. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  64. package/dist/gpu-driven/production-raster.d.ts +0 -1
  65. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  66. package/dist/gpu-scene.d.ts.map +1 -1
  67. package/dist/gpu-usage.d.ts +2 -0
  68. package/dist/gpu-usage.d.ts.map +1 -1
  69. package/dist/hdrp-buffers.d.ts +19 -17
  70. package/dist/hdrp-buffers.d.ts.map +1 -1
  71. package/dist/ibl/IblPipelineCache.d.ts +4 -0
  72. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  73. package/dist/ibl/kernel-cache.d.ts +36 -0
  74. package/dist/ibl/kernel-cache.d.ts.map +1 -0
  75. package/dist/index.d.ts +35 -4
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.mjs +4 -4
  78. package/dist/index.mjs.map +1 -1
  79. package/dist/inspection-types.d.ts +51 -0
  80. package/dist/inspection-types.d.ts.map +1 -1
  81. package/dist/instances-derived-bounds.d.ts +50 -0
  82. package/dist/instances-derived-bounds.d.ts.map +1 -0
  83. package/dist/internal.mjs +1 -1
  84. package/dist/light-buffer-layout.d.ts +30 -13
  85. package/dist/light-buffer-layout.d.ts.map +1 -1
  86. package/dist/mesh-material-bindings.d.ts +54 -0
  87. package/dist/mesh-material-bindings.d.ts.map +1 -1
  88. package/dist/pbr-pipeline.d.ts +20 -7
  89. package/dist/pbr-pipeline.d.ts.map +1 -1
  90. package/dist/pipeline/standard-forward-lane.d.ts +2 -1
  91. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  92. package/dist/pipeline/standard-lighting/errors.d.ts +4 -0
  93. package/dist/pipeline/standard-lighting/errors.d.ts.map +1 -0
  94. package/dist/pipeline/standard-lighting/graph.d.ts +24 -0
  95. package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -0
  96. package/dist/pipeline/standard-lighting/inspection.d.ts +38 -0
  97. package/dist/pipeline/standard-lighting/inspection.d.ts.map +1 -0
  98. package/dist/pipeline/standard-lighting/layout.d.ts +28 -0
  99. package/dist/pipeline/standard-lighting/layout.d.ts.map +1 -0
  100. package/dist/pipeline/standard-lighting/light-frame.d.ts +33 -0
  101. package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -0
  102. package/dist/pipeline/standard-lighting/membership.d.ts +14 -0
  103. package/dist/pipeline/standard-lighting/membership.d.ts.map +1 -0
  104. package/dist/pipeline/standard-lighting/prepare.d.ts +16 -0
  105. package/dist/pipeline/standard-lighting/prepare.d.ts.map +1 -0
  106. package/dist/pipeline/standard-lighting/topology.d.ts +45 -0
  107. package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -0
  108. package/dist/pipeline/standard-lighting/transport.d.ts +36 -0
  109. package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -0
  110. package/dist/pipeline/standard-pipeline.d.ts +4 -13
  111. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  112. package/dist/pipeline/standard-post.d.ts.map +1 -1
  113. package/dist/pipeline/standard-profile.d.ts +12 -20
  114. package/dist/pipeline/standard-profile.d.ts.map +1 -1
  115. package/dist/pipeline-spec.d.ts +40 -7
  116. package/dist/pipeline-spec.d.ts.map +1 -1
  117. package/dist/point-shadow-inspection.d.ts +21 -0
  118. package/dist/point-shadow-inspection.d.ts.map +1 -0
  119. package/dist/record/frame-lighting.d.ts +29 -32
  120. package/dist/record/frame-lighting.d.ts.map +1 -1
  121. package/dist/record/frame-snapshot.d.ts +65 -15
  122. package/dist/record/frame-snapshot.d.ts.map +1 -1
  123. package/dist/record/frame-targets.d.ts +5 -0
  124. package/dist/record/frame-targets.d.ts.map +1 -1
  125. package/dist/record/frame.d.ts +12 -5
  126. package/dist/record/frame.d.ts.map +1 -1
  127. package/dist/record/gpu-pass-timing/contract.d.ts +96 -0
  128. package/dist/record/gpu-pass-timing/contract.d.ts.map +1 -0
  129. package/dist/record/gpu-pass-timing/errors.d.ts +18 -0
  130. package/dist/record/gpu-pass-timing/errors.d.ts.map +1 -0
  131. package/dist/record/gpu-pass-timing/index.d.ts +8 -0
  132. package/dist/record/gpu-pass-timing/index.d.ts.map +1 -0
  133. package/dist/record/gpu-pass-timing/parser.d.ts +11 -0
  134. package/dist/record/gpu-pass-timing/parser.d.ts.map +1 -0
  135. package/dist/record/gpu-pass-timing/session.d.ts +43 -0
  136. package/dist/record/gpu-pass-timing/session.d.ts.map +1 -0
  137. package/dist/record/gpu-timing.d.ts +37 -0
  138. package/dist/record/gpu-timing.d.ts.map +1 -0
  139. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  140. package/dist/record/main-pass-material.d.ts +23 -2
  141. package/dist/record/main-pass-material.d.ts.map +1 -1
  142. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  143. package/dist/record/main-pass.d.ts +2 -0
  144. package/dist/record/main-pass.d.ts.map +1 -1
  145. package/dist/record/render-context.d.ts +48 -3
  146. package/dist/record/render-context.d.ts.map +1 -1
  147. package/dist/record/shadow-pass.d.ts.map +1 -1
  148. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  149. package/dist/record/typed-frame-graph.d.ts +81 -5
  150. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  151. package/dist/record/view-ubo.d.ts +7 -4
  152. package/dist/record/view-ubo.d.ts.map +1 -1
  153. package/dist/reflection/filter.d.ts +22 -0
  154. package/dist/reflection/filter.d.ts.map +1 -0
  155. package/dist/reflection/gpu-table.d.ts +25 -0
  156. package/dist/reflection/gpu-table.d.ts.map +1 -0
  157. package/dist/reflection/projection.d.ts +91 -0
  158. package/dist/reflection/projection.d.ts.map +1 -0
  159. package/dist/render-contract.d.ts +114 -7
  160. package/dist/render-contract.d.ts.map +1 -1
  161. package/dist/render-data.d.ts +10 -6
  162. package/dist/render-data.d.ts.map +1 -1
  163. package/dist/render-graph-primitives.d.ts +1 -0
  164. package/dist/render-graph-primitives.d.ts.map +1 -1
  165. package/dist/render-pipeline.d.ts +78 -2
  166. package/dist/render-pipeline.d.ts.map +1 -1
  167. package/dist/render-system-extract.d.ts +113 -20
  168. package/dist/render-system-extract.d.ts.map +1 -1
  169. package/dist/render-system.d.ts +24 -3
  170. package/dist/render-system.d.ts.map +1 -1
  171. package/dist/renderer-inspect.d.ts +29 -0
  172. package/dist/renderer-inspect.d.ts.map +1 -0
  173. package/dist/renderer.d.ts +43 -0
  174. package/dist/renderer.d.ts.map +1 -0
  175. package/dist/scene/render-scene.d.ts +14 -0
  176. package/dist/scene/render-scene.d.ts.map +1 -1
  177. package/dist/shadow-atlas.d.ts +3 -3
  178. package/dist/shadow-atlas.d.ts.map +1 -1
  179. package/dist/ssao-buffers.d.ts +3 -0
  180. package/dist/ssao-buffers.d.ts.map +1 -1
  181. package/dist/targets/contracts.d.ts +66 -0
  182. package/dist/targets/contracts.d.ts.map +1 -0
  183. package/dist/targets/graph-projection.d.ts +29 -0
  184. package/dist/targets/graph-projection.d.ts.map +1 -0
  185. package/dist/targets/material-source.d.ts +34 -0
  186. package/dist/targets/material-source.d.ts.map +1 -0
  187. package/dist/targets/owner.d.ts +32 -0
  188. package/dist/targets/owner.d.ts.map +1 -0
  189. package/dist/targets/physical.d.ts +18 -0
  190. package/dist/targets/physical.d.ts.map +1 -0
  191. package/dist/targets/readback.d.ts +39 -0
  192. package/dist/targets/readback.d.ts.map +1 -0
  193. package/dist/temporal/frame.d.ts +44 -0
  194. package/dist/temporal/frame.d.ts.map +1 -0
  195. package/dist/temporal/index.mjs +3 -3
  196. package/dist/typed-render-graph-primitives.d.ts +6 -1
  197. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  198. package/dist/typed-shadow-atlas.d.ts +10 -0
  199. package/dist/typed-shadow-atlas.d.ts.map +1 -0
  200. package/dist/typed-shadow-passes.d.ts +1 -1
  201. package/dist/typed-shadow-passes.d.ts.map +1 -1
  202. package/dist/volume/capability.d.ts +57 -0
  203. package/dist/volume/capability.d.ts.map +1 -0
  204. package/dist/volume/component.d.ts +61 -0
  205. package/dist/volume/component.d.ts.map +1 -0
  206. package/dist/volume/extract.d.ts +11 -0
  207. package/dist/volume/extract.d.ts.map +1 -0
  208. package/dist/volume/inspection.d.ts +38 -0
  209. package/dist/volume/inspection.d.ts.map +1 -0
  210. package/dist/volume/passes.d.ts +38 -0
  211. package/dist/volume/passes.d.ts.map +1 -0
  212. package/dist/volume/recovery.d.ts +38 -0
  213. package/dist/volume/recovery.d.ts.map +1 -0
  214. package/dist/volume/resources.d.ts +57 -0
  215. package/dist/volume/resources.d.ts.map +1 -0
  216. package/dist/volume/temporal.d.ts +35 -0
  217. package/dist/volume/temporal.d.ts.map +1 -0
  218. package/package.json +20 -20
  219. package/src/__tests__/bloom-exact-zero.unit.test.ts +48 -19
  220. package/src/__tests__/bloom-lifecycle.integration.test.ts +41 -13
  221. package/src/__tests__/camera-target-consumption.unit.test.ts +53 -0
  222. package/src/__tests__/cluster-binner.unit.test.ts +35 -0
  223. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +7 -2
  224. package/src/__tests__/cube-camera-scheduler.unit.test.ts +70 -0
  225. package/src/__tests__/cube-camera-single-submit.perf.test.ts +75 -0
  226. package/src/__tests__/cube-camera-view-matrices.unit.test.ts +41 -0
  227. package/src/__tests__/cube-camera-vocabulary.unit.test.ts +47 -0
  228. package/src/__tests__/cube-camera.integration.test.ts +42 -0
  229. package/src/__tests__/device-feature-admission.unit.test.ts +73 -0
  230. package/src/__tests__/direct-light-snapshot.unit.test.ts +3 -3
  231. package/src/__tests__/docs-gate-green.unit.test.ts +38 -0
  232. package/src/__tests__/factory-contract.integration.test.ts +255 -29
  233. package/src/__tests__/fog-component.unit.test.ts +67 -0
  234. package/src/__tests__/fog.integration.test.ts +86 -0
  235. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +37 -0
  236. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +10 -9
  237. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +17 -1
  238. package/src/__tests__/gpu-driven-production.integration.test.ts +16 -17
  239. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +12 -4
  240. package/src/__tests__/gpu-pass-timing-browser-runner.ts +281 -0
  241. package/src/__tests__/gpu-pass-timing-coexistence.integration.test.ts +80 -0
  242. package/src/__tests__/gpu-pass-timing-contract.unit.test.ts +243 -0
  243. package/src/__tests__/gpu-pass-timing-exact-zero.unit.test.ts +131 -0
  244. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +230 -0
  245. package/src/__tests__/gpu-pass-timing-observation.unit.test.ts +233 -0
  246. package/src/__tests__/gpu-pass-timing-session.unit.test.ts +438 -0
  247. package/src/__tests__/gpu-pass-timing.browser.test.ts +26 -0
  248. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +125 -0
  249. package/src/__tests__/hdrp-group2-visibility.unit.test.ts +24 -0
  250. package/src/__tests__/instances-culling.integration.test.ts +94 -0
  251. package/src/__tests__/instances-derived-bounds.unit.test.ts +118 -0
  252. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +14 -14
  253. package/src/__tests__/material-observation.integration.test.ts +101 -0
  254. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
  255. package/src/__tests__/pbr-view-layout.unit.test.ts +16 -0
  256. package/src/__tests__/point-shadow-inspection.unit.test.ts +60 -0
  257. package/src/__tests__/public-surface.test-d.ts +10 -0
  258. package/src/__tests__/reflection-probe-authoring.unit.test.ts +71 -0
  259. package/src/__tests__/reflection-probe-filter.unit.test.ts +54 -0
  260. package/src/__tests__/reflection-probe-ibl-topology.unit.test.ts +45 -0
  261. package/src/__tests__/reflection-probe-pass-encode.integration.test.ts +101 -0
  262. package/src/__tests__/reflection-probe-performance.perf.test.ts +60 -0
  263. package/src/__tests__/reflection-probe-recovery.integration.test.ts +55 -0
  264. package/src/__tests__/reflection-probe-selection.unit.test.ts +46 -0
  265. package/src/__tests__/render-error-code-owner.test-d.ts +23 -3
  266. package/src/__tests__/render-feature-docs.unit.test.ts +1 -1
  267. package/src/__tests__/render-target-admission.unit.test.ts +80 -0
  268. package/src/__tests__/render-target-graph-projection.unit.test.ts +102 -0
  269. package/src/__tests__/render-target-inspection.unit.test.ts +52 -0
  270. package/src/__tests__/render-target-material-source.test-d.ts +30 -0
  271. package/src/__tests__/render-target-material-source.unit.test.ts +97 -0
  272. package/src/__tests__/render-target-owner-placement.unit.test.ts +203 -0
  273. package/src/__tests__/render-target-owner.unit.test.ts +122 -0
  274. package/src/__tests__/render-target-probe-soak.perf.test.ts +94 -0
  275. package/src/__tests__/render-target-public-schema.json +107 -0
  276. package/src/__tests__/render-target-public-schema.unit.test.ts +91 -0
  277. package/src/__tests__/render-target-public-surface.test-d.ts +83 -0
  278. package/src/__tests__/render-target-readback.integration.test.ts +90 -0
  279. package/src/__tests__/render-target-recovery.integration.test.ts +85 -0
  280. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +76 -0
  281. package/src/__tests__/renderer-frame-transaction.integration.test.ts +1 -1
  282. package/src/__tests__/renderer-lifecycle.integration.test.ts +14 -0
  283. package/src/__tests__/shader-stage-surface.unit.test.ts +19 -4
  284. package/src/__tests__/skin-motion-regression.unit.test.ts +132 -0
  285. package/src/__tests__/skinned-shadow-caster.test.ts +32 -2
  286. package/src/__tests__/standard-cluster-capability.integration.test.ts +63 -0
  287. package/src/__tests__/standard-cluster-transport.unit.test.ts +121 -0
  288. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +232 -0
  289. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +8 -5
  290. package/src/__tests__/standard-lighting-inspection.integration.test.ts +138 -0
  291. package/src/__tests__/standard-lighting-ssot.unit.test.ts +18 -0
  292. package/src/__tests__/standard-lighting-write.unit.test.ts +377 -0
  293. package/src/__tests__/standard-lighting.integration.test.ts +67 -0
  294. package/src/__tests__/standard-pipeline.dawn.test.ts +206 -5
  295. package/src/__tests__/standard-pipeline.integration.test.ts +181 -150
  296. package/src/__tests__/standard-probe-binding.unit.test.ts +25 -0
  297. package/src/__tests__/standard-profile-oracle.unit.test.ts +23 -36
  298. package/src/__tests__/typed-pipeline-topology.unit.test.ts +395 -14
  299. package/src/__tests__/urp-pipeline-targets.unit.test.ts +2 -2
  300. package/src/__tests__/visibility-resolver.unit.test.ts +15 -0
  301. package/src/__tests__/volume-optics-oracle.unit.test.ts +85 -0
  302. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +655 -0
  303. package/src/__tests__/volumetric-fog-component.unit.test.ts +71 -0
  304. package/src/__tests__/volumetric-fog-composite-stage-readback.dawn.test.ts +376 -0
  305. package/src/__tests__/volumetric-fog-errors.unit.test.ts +51 -0
  306. package/src/__tests__/volumetric-fog-inspection.unit.test.ts +77 -0
  307. package/src/__tests__/volumetric-fog-light-capability.unit.test.ts +19 -0
  308. package/src/__tests__/volumetric-fog-light-consumers.integration.test.ts +18 -0
  309. package/src/__tests__/volumetric-fog-light-errors.unit.test.ts +17 -0
  310. package/src/__tests__/volumetric-fog-light-topology.unit.test.ts +21 -0
  311. package/src/__tests__/volumetric-fog-light-transaction.integration.test.ts +53 -0
  312. package/src/__tests__/volumetric-fog-m9-watcher.integration.test.ts +27 -0
  313. package/src/__tests__/volumetric-fog-point-light-owner.integration.test.ts +44 -0
  314. package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +49 -0
  315. package/src/__tests__/volumetric-fog-point-spot-contract.unit.test.ts +28 -0
  316. package/src/__tests__/volumetric-fog-point-spot-lkg.integration.test.ts +65 -0
  317. package/src/__tests__/volumetric-fog-point-spot-selection.integration.test.ts +49 -0
  318. package/src/__tests__/volumetric-fog-point-spot-temporal.unit.test.ts +55 -0
  319. package/src/__tests__/volumetric-fog-projector-contract.unit.test.ts +22 -0
  320. package/src/__tests__/volumetric-fog-projector-inspection.unit.test.ts +12 -0
  321. package/src/__tests__/volumetric-fog-projector-surface-volume.integration.test.ts +11 -0
  322. package/src/__tests__/volumetric-fog-projector-transaction.integration.test.ts +26 -0
  323. package/src/__tests__/volumetric-fog-recovery.integration.test.ts +47 -0
  324. package/src/__tests__/volumetric-fog-selected-light-resolution.integration.test.ts +63 -0
  325. package/src/__tests__/volumetric-fog-spot-closure.integration.test.ts +26 -0
  326. package/src/__tests__/volumetric-fog-spot-contract.test-d.ts +21 -0
  327. package/src/__tests__/volumetric-fog-spot-contract.unit.test.ts +27 -0
  328. package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +14 -0
  329. package/src/__tests__/volumetric-fog-spot-optics.integration.test.ts +29 -0
  330. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +550 -0
  331. package/src/__tests__/volumetric-fog-temporal-selected-light.unit.test.ts +33 -0
  332. package/src/__tests__/volumetric-fog-temporal.unit.test.ts +45 -0
  333. package/src/__tests__/volumetric-fog-topology.unit.test.ts +237 -0
  334. package/src/__tests__/volumetric-fog-transparent.integration.test.ts +27 -0
  335. package/src/assembly/device-feature-admission.ts +73 -0
  336. package/src/assembly/directional-shadow-inspection.ts +122 -0
  337. package/src/assembly/factory.ts +1093 -246
  338. package/src/assembly/host-contract.ts +25 -0
  339. package/src/assembly/render-target-host.ts +847 -0
  340. package/src/assets/asset-decoders.ts +1 -1
  341. package/src/assets/render-asset-port.ts +7 -1
  342. package/src/capture/cube-views.ts +69 -0
  343. package/src/capture/scheduler.ts +265 -0
  344. package/src/components/__tests__/schema-contract.unit.test.ts +12 -0
  345. package/src/components/__tests__/schema-parity.unit.test.ts +9 -0
  346. package/src/components/camera.ts +4 -0
  347. package/src/components/cube-camera.ts +52 -0
  348. package/src/components/directional-light.ts +22 -2
  349. package/src/components/directional-shadow-filter.ts +57 -0
  350. package/src/components/index.ts +2 -0
  351. package/src/components/light-helpers.ts +85 -7
  352. package/src/components/reflection-probe.ts +49 -0
  353. package/src/components/spot-light.ts +22 -3
  354. package/src/device/gpu-residency.ts +168 -58
  355. package/src/environment/frame.ts +6 -24
  356. package/src/environment/lifecycle.ts +12 -0
  357. package/src/errors/render.ts +405 -143
  358. package/src/extract/directional-shadow-projection.ts +61 -0
  359. package/src/extract/environment.ts +57 -0
  360. package/src/extract/visibility.ts +25 -1
  361. package/src/features/fullscreen.ts +3 -0
  362. package/src/features/targets.ts +4 -1
  363. package/src/features/types.ts +10 -0
  364. package/src/fullscreen-post-process-pass.ts +43 -2
  365. package/src/gpu-driven/batch-topology.ts +1 -0
  366. package/src/gpu-driven/production-raster.ts +1 -3
  367. package/src/gpu-scene.ts +8 -1
  368. package/src/gpu-usage.ts +2 -0
  369. package/src/hdrp-buffers.ts +165 -59
  370. package/src/ibl/IblPipelineCache.ts +11 -0
  371. package/src/ibl/kernel-cache.ts +88 -0
  372. package/src/index.ts +186 -1
  373. package/src/inspection-types.ts +56 -0
  374. package/src/instances-derived-bounds.ts +171 -0
  375. package/src/light-buffer-layout.ts +74 -26
  376. package/src/mesh-material-bindings.ts +132 -0
  377. package/src/pbr-pipeline.ts +139 -27
  378. package/src/pipeline/standard-forward-lane.ts +65 -15
  379. package/src/pipeline/standard-lighting/errors.ts +6 -0
  380. package/src/pipeline/standard-lighting/graph.ts +157 -0
  381. package/src/pipeline/standard-lighting/inspection.ts +88 -0
  382. package/src/pipeline/standard-lighting/layout.ts +31 -0
  383. package/src/pipeline/standard-lighting/light-frame.ts +34 -0
  384. package/src/pipeline/standard-lighting/membership.ts +43 -0
  385. package/src/pipeline/standard-lighting/prepare.ts +37 -0
  386. package/src/pipeline/standard-lighting/topology.ts +120 -0
  387. package/src/pipeline/standard-lighting/transport.ts +77 -0
  388. package/src/pipeline/standard-pipeline.ts +141 -192
  389. package/src/pipeline/standard-post.ts +26 -7
  390. package/src/pipeline/standard-profile.ts +23 -30
  391. package/src/pipeline-spec.ts +84 -21
  392. package/src/point-shadow-inspection.ts +55 -0
  393. package/src/record/__tests__/temporal-payload.unit.test.ts +42 -1
  394. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +73 -0
  395. package/src/record/__tests__/view-ubo-layout.unit.test.ts +40 -0
  396. package/src/record/frame-lighting.ts +470 -516
  397. package/src/record/frame-snapshot.ts +76 -28
  398. package/src/record/frame-targets.ts +23 -2
  399. package/src/record/frame.ts +1169 -79
  400. package/src/record/gpu-pass-timing/contract.ts +180 -0
  401. package/src/record/gpu-pass-timing/errors.ts +34 -0
  402. package/src/record/gpu-pass-timing/index.ts +37 -0
  403. package/src/record/gpu-pass-timing/parser.ts +97 -0
  404. package/src/record/gpu-pass-timing/session.ts +938 -0
  405. package/src/record/gpu-timing.ts +233 -0
  406. package/src/record/main-pass-geometry.ts +120 -80
  407. package/src/record/main-pass-material.ts +101 -11
  408. package/src/record/main-pass-sprite-draws.ts +54 -25
  409. package/src/record/main-pass.ts +71 -15
  410. package/src/record/render-context.ts +77 -0
  411. package/src/record/shadow-pass.ts +104 -66
  412. package/src/record/skybox-post-pass.ts +20 -4
  413. package/src/record/typed-frame-graph.ts +1016 -32
  414. package/src/record/view-ubo.ts +63 -28
  415. package/src/reflection/filter.ts +81 -0
  416. package/src/reflection/gpu-table.ts +44 -0
  417. package/src/reflection/projection.ts +281 -0
  418. package/src/render-contract.ts +147 -4
  419. package/src/render-data.ts +50 -22
  420. package/src/render-graph-primitives.ts +9 -4
  421. package/src/render-pipeline.ts +161 -2
  422. package/src/render-system-extract.ts +886 -103
  423. package/src/render-system.ts +1174 -66
  424. package/src/renderer-inspect.ts +66 -0
  425. package/src/renderer.ts +222 -0
  426. package/src/scene/render-scene.ts +117 -9
  427. package/src/shadow-atlas.ts +5 -5
  428. package/src/ssao-buffers.ts +9 -2
  429. package/src/targets/contracts.ts +182 -0
  430. package/src/targets/graph-projection.ts +169 -0
  431. package/src/targets/material-source.ts +143 -0
  432. package/src/targets/owner.ts +214 -0
  433. package/src/targets/physical.ts +18 -0
  434. package/src/targets/readback.ts +153 -0
  435. package/src/temporal/frame.ts +128 -0
  436. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +61 -23
  437. package/src/typed-render-graph-primitives.ts +34 -2
  438. package/src/typed-shadow-atlas.ts +17 -0
  439. package/src/typed-shadow-passes.ts +47 -40
  440. package/src/volume/capability.ts +173 -0
  441. package/src/volume/component.ts +110 -0
  442. package/src/volume/extract.ts +21 -0
  443. package/src/volume/inspection.ts +78 -0
  444. package/src/volume/passes.ts +686 -0
  445. package/src/volume/recovery.ts +86 -0
  446. package/src/volume/resources.ts +236 -0
  447. package/src/volume/temporal.ts +89 -0
  448. package/dist/chunk-IPXCABEP.mjs.map +0 -1
  449. package/dist/chunk-LOSO4CTV.mjs.map +0 -1
  450. package/dist/chunk-RJRUS55L.mjs.map +0 -1
  451. package/dist/chunk-YTQ5C7YQ.mjs +0 -584
  452. package/dist/chunk-YTQ5C7YQ.mjs.map +0 -1
@@ -34,6 +34,13 @@ import {
34
34
  addTypedSkyboxPass,
35
35
  } from '../typed-render-graph-primitives';
36
36
  import { addTypedShadowPasses } from '../typed-shadow-passes';
37
+ import { addAuthoredVolumetricFogPasses } from '../volume/passes';
38
+ import {
39
+ addStandardClusterMembershipPass,
40
+ importStandardClusterBuffers,
41
+ standardClusterReadAccesses,
42
+ } from './standard-lighting/graph';
43
+ import type { StandardTopologyInputValue } from './standard-lighting/topology';
37
44
  import { addStandardPost } from './standard-post';
38
45
 
39
46
  function target(
@@ -47,6 +54,7 @@ function target(
47
54
  function buildUrp(
48
55
  context: RenderPipelineBuildContext<RenderPipelineFrame>,
49
56
  topology: RenderPipelineTopology,
57
+ lighting: StandardTopologyInputValue,
50
58
  ): ReturnType<RenderPipeline['build']> {
51
59
  const graph = context.graph;
52
60
  const surface = importRenderPipelineSurface(graph, topology);
@@ -59,16 +67,14 @@ function buildUrp(
59
67
  const fxaa = !clearOnly && topology.camera.antialias === 'fxaa';
60
68
  const taa = !clearOnly && topology.camera.antialias === 'taa';
61
69
  const rawOnly = !clearOnly && topology.surface.profile?.kind === 'raw-only';
62
- // A direct no-tone/no-FXAA lane writes the final display view directly;
63
- // keeping a linear-LDR intermediate would add a redundant fullscreen pass.
64
- // FXAA samples the scene before the Output Transform, so its input must
65
- // remain float even when tone mapping is disabled. This prevents an
66
- // eight-bit quantization before the OETF/display-encoded boundary.
70
+ // Every non-clear lane keeps a linear float scene target until the shared
71
+ // Output Transform. This includes no-AA and the no-storage fallback: the
72
+ // final dither and display encoding must have one graph-owned boundary.
67
73
  // MSAA resolves into a separate linear target before presentation. Treat
68
74
  // that resolved target like linear LDR so the shared Output Transform owns
69
75
  // its write into the surface storage view; without this, the resolve is
70
76
  // never presented and MSAA is indistinguishable from the no-AA path.
71
- const linearLdr = !clearOnly && ((!hdr && topology.lane.storageBuffer) || fxaa || msaa);
77
+ const linearLdr = !clearOnly && !hdr;
72
78
  const temporalDemand = aggregateTemporalDemand({
73
79
  taa,
74
80
  motionBlur: !clearOnly && topology.temporal?.motionBlur === true,
@@ -87,7 +93,14 @@ function buildUrp(
87
93
  if (!depth.ok) return depth;
88
94
 
89
95
  const sceneResolved = clearOnly
90
- ? ok(surface.value.display ?? surface.value.storage)
96
+ ? surface.value.display === undefined
97
+ ? target(graph, 'scene-color', {
98
+ format: topology.surface.storageFormat,
99
+ size: 'surface',
100
+ sampleCount: 1,
101
+ domain: 'display-encoded',
102
+ })
103
+ : ok(surface.value.display)
91
104
  : hdr || fxaa || linearLdr || msaa || rawOnly || needsTemporalIntermediate
92
105
  ? target(graph, 'scene-color', {
93
106
  format: sceneFormat,
@@ -125,6 +138,16 @@ function buildUrp(
125
138
  const shadows = addTypedShadowPasses(graph, topology);
126
139
  if (!shadows.ok) return shadows;
127
140
 
141
+ const clusterBuffers = clearOnly ? ok(null) : importStandardClusterBuffers(graph, lighting);
142
+ if (!clusterBuffers.ok) return clusterBuffers;
143
+ const clusterMembership =
144
+ clusterBuffers.value === null
145
+ ? ok(undefined)
146
+ : addStandardClusterMembershipPass(graph, lighting, clusterBuffers.value);
147
+ if (!clusterMembership.ok) return clusterMembership;
148
+ const clusterReads =
149
+ clusterBuffers.value === null ? [] : standardClusterReadAccesses(clusterBuffers.value);
150
+
128
151
  const skybox = addTypedSkyboxPass(graph, scene.value);
129
152
  if (!skybox.ok) return skybox;
130
153
  const gpuDriven = context.projectGpuDriven({
@@ -143,22 +166,23 @@ function buildUrp(
143
166
  depth: depth.value,
144
167
  ...(msaa ? { resolve: sceneResolved.value } : {}),
145
168
  sampled: [
146
- shadows.value.directional,
169
+ ...(shadows.value.directional === undefined ? [] : [shadows.value.directional]),
147
170
  shadows.value.spot,
148
171
  ...(shadows.value.point === undefined ? [] : [shadows.value.point]),
149
172
  ],
150
- directionalShadow: shadows.value.directional,
173
+ ...(shadows.value.directional === undefined
174
+ ? {}
175
+ : { directionalShadow: shadows.value.directional }),
151
176
  spotShadow: shadows.value.spot,
152
177
  selector: { LightMode: ['Forward'] },
153
178
  colorLoadOp: 'load',
179
+ extraAccesses: clusterReads,
154
180
  ...(transmissionActive ? { recordMode: 'opaque' as const } : {}),
155
181
  ...(gpuDriven.value === undefined ? {} : { gpuDriven: gpuDriven.value }),
156
182
  });
157
183
  if (!main.ok) return main;
158
184
 
159
- const temporalLane = topology.lane.compute
160
- ? (topology.standardProfile?.lighting ?? 'direct')
161
- : 'cpu-webgl2';
185
+ const temporalLane = topology.lane.compute ? 'clustered' : 'cpu-webgl2';
162
186
  const temporalAdmission = standardTemporalLaneAdmission({
163
187
  lane: temporalLane,
164
188
  demand: temporalDemand,
@@ -190,6 +214,7 @@ function buildUrp(
190
214
  const producer = addStandardSceneDataPass(graph, temporal.value.temporal, depth.value);
191
215
  if (!producer.ok) return producer;
192
216
  }
217
+
193
218
  const transmission = addTransmissionBackdropPasses({
194
219
  graph,
195
220
  source: sceneResolved.value,
@@ -206,7 +231,7 @@ function buildUrp(
206
231
  depth: depth.value,
207
232
  ...(msaa ? { resolve: sceneResolved.value } : {}),
208
233
  sampled: [
209
- shadows.value.directional,
234
+ ...(shadows.value.directional === undefined ? [] : [shadows.value.directional]),
210
235
  shadows.value.spot,
211
236
  ...(shadows.value.point === undefined ? [] : [shadows.value.point]),
212
237
  ],
@@ -216,6 +241,7 @@ function buildUrp(
216
241
  colorLoadOp: 'load',
217
242
  depthLoadOp: 'load',
218
243
  recordMode: 'transmission',
244
+ extraAccesses: clusterReads,
219
245
  // Inject the complete backdrop view so the Standard shader can select
220
246
  // the producer's optional roughness mip level. mipViews[0] is only the
221
247
  // copy destination view and intentionally exposes one level.
@@ -229,7 +255,7 @@ function buildUrp(
229
255
  depth: depth.value,
230
256
  ...(msaa ? { resolve: sceneResolved.value } : {}),
231
257
  sampled: [
232
- shadows.value.directional,
258
+ ...(shadows.value.directional === undefined ? [] : [shadows.value.directional]),
233
259
  shadows.value.spot,
234
260
  ...(shadows.value.point === undefined ? [] : [shadows.value.point]),
235
261
  ],
@@ -239,14 +265,34 @@ function buildUrp(
239
265
  colorLoadOp: 'load',
240
266
  depthLoadOp: 'load',
241
267
  recordMode: 'transparent',
268
+ extraAccesses: clusterReads,
242
269
  });
243
270
  if (!transparentPass.ok) return transparentPass;
244
271
  const temporal = addTransmissionBackdropTemporalPass(graph, sceneResolved.value);
245
272
  if (!temporal.ok) return temporal;
246
273
  }
247
274
 
275
+ if (topology.volumetricFog?.enabled === true) {
276
+ const depthSample = graph.view(depth.value.texture, {
277
+ label: 'scene-depth.sample',
278
+ dimension: '2d',
279
+ aspect: 'depth-only',
280
+ });
281
+ if (!depthSample.ok) return depthSample;
282
+ const volume = addAuthoredVolumetricFogPasses(
283
+ graph,
284
+ topology,
285
+ sceneResolved.value,
286
+ depthSample.value,
287
+ shadows.value.directional?.view,
288
+ shadows.value.spot.view,
289
+ );
290
+ if (!volume.ok) return volume;
291
+ }
292
+
248
293
  const features = context.contributeFeatures([
249
294
  {
295
+ name: 'linear-hdr',
250
296
  kind: 'scene-color',
251
297
  texture: scene.value.texture,
252
298
  view: scene.value.view,
@@ -273,7 +319,7 @@ function buildUrp(
273
319
  if (!observation.ok) return observation;
274
320
  }
275
321
 
276
- return addStandardPost(
322
+ const post = addStandardPost(
277
323
  context,
278
324
  topology,
279
325
  sceneResolved.value,
@@ -281,6 +327,10 @@ function buildUrp(
281
327
  surface.value,
282
328
  temporal.value?.temporal,
283
329
  );
330
+ if (!post.ok) return post;
331
+ const cubeCaptures = context.contributeCubeCaptures?.();
332
+ if (cubeCaptures !== undefined && !cubeCaptures.ok) return cubeCaptures;
333
+ return ok(undefined);
284
334
  }
285
335
 
286
336
  export const buildStandardForwardLane = buildUrp;
@@ -0,0 +1,6 @@
1
+ import type { ClusterBinError } from '../../cluster-binner';
2
+ import { StandardClusterIndexOverflowError } from '../../errors/render';
3
+
4
+ export function fromClusterBinError(error: ClusterBinError): StandardClusterIndexOverflowError {
5
+ return new StandardClusterIndexOverflowError(error.detail.actual, error.detail.capacity);
6
+ }
@@ -0,0 +1,157 @@
1
+ import type {
2
+ GraphAccess,
3
+ GraphBuffer,
4
+ RenderGraphBuilder,
5
+ RenderGraphError,
6
+ } from '@forgeax/engine-render-graph';
7
+ import { ok, type Result } from '@forgeax/engine-types';
8
+ import {
9
+ GPU_BUFFER_USAGE_COPY_DST,
10
+ GPU_BUFFER_USAGE_STORAGE,
11
+ GPU_BUFFER_USAGE_UNIFORM,
12
+ } from '../../gpu-usage';
13
+ import { getOrCreateHdrpBuffers, type HdrpBuffers } from '../../hdrp-buffers';
14
+ import type { _InternalRenderPipelineContext } from '../../record/render-context';
15
+ import type { RenderPipelineFrame } from '../../render-pipeline';
16
+ import type { StandardTopologyInputValue } from './topology';
17
+
18
+ /** Graph resources projected from one Standard lighting topology input. */
19
+ export interface StandardClusterGraphBuffers {
20
+ readonly lightData: GraphBuffer;
21
+ readonly clusterGrid: GraphBuffer;
22
+ readonly lightIndexList: GraphBuffer;
23
+ readonly clusterUniform: GraphBuffer;
24
+ readonly lightBounds: GraphBuffer;
25
+ }
26
+
27
+ /** Stable graph-time projection; never retain a frame's typed-array payload. */
28
+ interface StandardClusterGraphDescriptor {
29
+ readonly grid: { readonly x: number; readonly y: number; readonly z: number };
30
+ }
31
+
32
+ function projectDescriptor(input: StandardTopologyInputValue): StandardClusterGraphDescriptor {
33
+ const { x, y, z } = input.prepared.layout.grid;
34
+ return { grid: { x, y, z } };
35
+ }
36
+
37
+ function resolveBuffers(
38
+ frame: RenderPipelineFrame,
39
+ descriptor: StandardClusterGraphDescriptor,
40
+ ): HdrpBuffers {
41
+ const internal = frame as _InternalRenderPipelineContext;
42
+ const buffers = getOrCreateHdrpBuffers(internal.runtime, descriptor.grid);
43
+ if (buffers === null) throw new Error('Standard persistent buffer allocation failed');
44
+ return buffers;
45
+ }
46
+
47
+ /** Resolve membership execution from the frame's current prepared payload. */
48
+ export function shouldExecuteStandardClusterMembershipPass(frame: RenderPipelineFrame): boolean {
49
+ const standardLighting = (frame as _InternalRenderPipelineContext).standardLighting;
50
+ return (
51
+ standardLighting?.kind === 'clustered' &&
52
+ standardLighting.transport.kind === 'compute-storage' &&
53
+ standardLighting.prepared.local.length > 0
54
+ );
55
+ }
56
+
57
+ /** Import the shared Standard Cluster buffers with sizes derived from layout. */
58
+ export function importStandardClusterBuffers(
59
+ graph: RenderGraphBuilder<RenderPipelineFrame>,
60
+ input: StandardTopologyInputValue,
61
+ ): Result<StandardClusterGraphBuffers | null, RenderGraphError> {
62
+ if (input.kind === 'no-local-lights') return ok(null);
63
+ const layout = input.prepared.layout;
64
+ const descriptor = projectDescriptor(input);
65
+ const usage = GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST;
66
+ const lightData = graph.importBuffer(
67
+ 'hdrp-light-data',
68
+ { size: layout.lightDataSlotCount * 64, usage },
69
+ (frame) => resolveBuffers(frame, descriptor).lightDataBuffer,
70
+ );
71
+ if (!lightData.ok) return lightData;
72
+ const clusterGrid = graph.importBuffer(
73
+ 'hdrp-cluster-grid',
74
+ { size: layout.clusterGridU32Length * 4, usage },
75
+ (frame) => resolveBuffers(frame, descriptor).clusterGridBuffer,
76
+ );
77
+ if (!clusterGrid.ok) return clusterGrid;
78
+ const lightIndexList = graph.importBuffer(
79
+ 'hdrp-light-index-list',
80
+ { size: layout.lightIndexListCapacity * 4, usage },
81
+ (frame) => resolveBuffers(frame, descriptor).lightIndexListBuffer,
82
+ );
83
+ if (!lightIndexList.ok) return lightIndexList;
84
+ const clusterUniform = graph.importBuffer(
85
+ 'hdrp-cluster-uniform',
86
+ { size: 32, usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST },
87
+ (frame) => resolveBuffers(frame, descriptor).clusterUniformBuffer,
88
+ );
89
+ if (!clusterUniform.ok) return clusterUniform;
90
+ const lightBounds = graph.importBuffer(
91
+ 'hdrp-light-bounds',
92
+ { size: layout.lightBoundsInt32Length * 4, usage },
93
+ (frame) => resolveBuffers(frame, descriptor).lightBoundsBuffer,
94
+ );
95
+ if (!lightBounds.ok) return lightBounds;
96
+ return ok({
97
+ lightData: lightData.value,
98
+ clusterGrid: clusterGrid.value,
99
+ lightIndexList: lightIndexList.value,
100
+ clusterUniform: clusterUniform.value,
101
+ lightBounds: lightBounds.value,
102
+ });
103
+ }
104
+
105
+ /** Accesses used by raster consumers of the shared Cluster payload. */
106
+ export function standardClusterReadAccesses(
107
+ buffers: StandardClusterGraphBuffers,
108
+ ): readonly GraphAccess[] {
109
+ return [
110
+ { resource: buffers.lightData, usage: 'storage-read' },
111
+ { resource: buffers.clusterGrid, usage: 'storage-read' },
112
+ { resource: buffers.lightIndexList, usage: 'storage-read' },
113
+ { resource: buffers.clusterUniform, usage: 'uniform-read' },
114
+ ];
115
+ }
116
+
117
+ /**
118
+ * Add the sole GPU membership producer. CPU membership is already materialized
119
+ * in PreparedStandardLighting and therefore contributes no second pass.
120
+ */
121
+ export function addStandardClusterMembershipPass(
122
+ graph: RenderGraphBuilder<RenderPipelineFrame>,
123
+ input: StandardTopologyInputValue,
124
+ buffers: StandardClusterGraphBuffers,
125
+ ): Result<void, RenderGraphError> {
126
+ if (input.kind === 'no-local-lights') return ok(undefined);
127
+ if (input.transport.kind !== 'compute-storage') return ok(undefined);
128
+ const descriptor = projectDescriptor(input);
129
+ return graph.addComputePass('cluster-membership-producer', {
130
+ accesses: [
131
+ { resource: buffers.clusterGrid, usage: 'storage-read' },
132
+ { resource: buffers.clusterUniform, usage: 'uniform-read' },
133
+ { resource: buffers.lightBounds, usage: 'storage-read' },
134
+ { resource: buffers.lightIndexList, usage: 'storage-write' },
135
+ ],
136
+ // Keep the clustered graph/ABI stable for a directional-only frame, but
137
+ // do not require a producer bind group when there is no local corpus to
138
+ // bin. The zero-length prepared payload is already the complete result;
139
+ // executing this pass would only turn an otherwise valid frame into a
140
+ // null-backend encode failure.
141
+ executeIf: shouldExecuteStandardClusterMembershipPass,
142
+ encode: ({ pass, frame }) => {
143
+ const internal = frame as _InternalRenderPipelineContext;
144
+ const pipeline = internal.pipelineState.hdrpClusterMembershipPipeline;
145
+ const bindings = internal.hdrpClusterMembershipBindGroup;
146
+ if (pipeline === null || bindings === null) {
147
+ throw new Error(
148
+ 'standard-cluster-transport-unavailable: selected compute Cluster producer is not ready',
149
+ );
150
+ }
151
+ const { x, y, z } = descriptor.grid;
152
+ pass.setPipeline(pipeline);
153
+ pass.setBindGroup(0, bindings);
154
+ pass.dispatchWorkgroups(Math.ceil((x * y * z) / 64));
155
+ },
156
+ });
157
+ }
@@ -0,0 +1,88 @@
1
+ import type { StandardTopologyInputValue } from './topology';
2
+ import type { StandardClusterTransport } from './transport';
3
+
4
+ export interface StandardClusterTransportInspection {
5
+ readonly renderPath: 'forward' | 'deferred';
6
+ readonly transport: StandardClusterTransport['kind'];
7
+ readonly producer: StandardClusterTransport['producer'];
8
+ readonly requested: StandardClusterTransport['requestedLightCount'];
9
+ readonly admitted: StandardClusterTransport['admittedLightCount'];
10
+ readonly shadowed: number;
11
+ readonly maxLights: number;
12
+ readonly indexCount: StandardClusterTransport['membershipEntryCount'];
13
+ readonly occupied: StandardClusterTransport['occupiedClusterCount'];
14
+ readonly clusterGridBytes: number;
15
+ readonly lightIndexListBytes: number;
16
+ readonly lightDataBytes: number;
17
+ readonly lightBoundsBytes: number;
18
+ readonly grid: StandardClusterTransport['layout']['grid'];
19
+ }
20
+
21
+ export interface StandardNoLocalLightingInspection {
22
+ readonly renderPath: 'forward' | 'deferred';
23
+ readonly transport: 'not-required';
24
+ readonly producer: 'none';
25
+ readonly requested: 0;
26
+ readonly admitted: 0;
27
+ readonly shadowed: 0;
28
+ readonly maxLights: number;
29
+ readonly indexCount: 0;
30
+ readonly occupied: 0;
31
+ readonly clusterGridBytes: 0;
32
+ readonly lightIndexListBytes: 0;
33
+ readonly lightDataBytes: 0;
34
+ readonly lightBoundsBytes: 0;
35
+ readonly grid: StandardTopologyInputValue['prepared']['layout']['grid'];
36
+ }
37
+
38
+ export type StandardLightingInspection =
39
+ | StandardClusterTransportInspection
40
+ | StandardNoLocalLightingInspection;
41
+
42
+ export function inspectStandardClusterTransport(
43
+ transport: StandardClusterTransport,
44
+ prepared: StandardTopologyInputValue['prepared'],
45
+ ): StandardClusterTransportInspection {
46
+ return Object.freeze({
47
+ renderPath: prepared.renderPath,
48
+ transport: transport.kind,
49
+ producer: transport.producer,
50
+ requested: transport.requestedLightCount,
51
+ admitted: transport.admittedLightCount,
52
+ shadowed: prepared.local.filter((light) => light.shadowed === true).length,
53
+ maxLights: prepared.maxLights,
54
+ indexCount: transport.membershipEntryCount,
55
+ occupied: transport.occupiedClusterCount,
56
+ clusterGridBytes: transport.clusterGridBytes,
57
+ lightIndexListBytes: transport.lightIndexListBytes,
58
+ lightDataBytes: transport.lightDataBytes,
59
+ lightBoundsBytes: transport.lightBoundsBytes,
60
+ // The inspection boundary is JSON-safe and detached from the mutable
61
+ // layout object carried by a per-frame prepared result.
62
+ grid: Object.freeze({ ...transport.layout.grid }),
63
+ });
64
+ }
65
+
66
+ export function inspectStandardLighting(
67
+ input: StandardTopologyInputValue,
68
+ ): StandardLightingInspection {
69
+ if (input.kind === 'no-local-lights') {
70
+ return Object.freeze({
71
+ renderPath: input.prepared.renderPath,
72
+ transport: 'not-required' as const,
73
+ producer: 'none' as const,
74
+ requested: 0 as const,
75
+ admitted: 0 as const,
76
+ shadowed: 0 as const,
77
+ maxLights: input.prepared.maxLights,
78
+ indexCount: 0 as const,
79
+ occupied: 0 as const,
80
+ clusterGridBytes: 0 as const,
81
+ lightIndexListBytes: 0 as const,
82
+ lightDataBytes: 0 as const,
83
+ lightBoundsBytes: 0 as const,
84
+ grid: Object.freeze({ ...input.prepared.layout.grid }),
85
+ });
86
+ }
87
+ return inspectStandardClusterTransport(input.transport, input.prepared);
88
+ }
@@ -0,0 +1,31 @@
1
+ export const DEFAULT_CLUSTER_GRID = { x: 16, y: 9, z: 24 } as const;
2
+ export const CLUSTER_GRID_STRIDE_U32 = 2 as const;
3
+ export const LIGHT_INDEX_LIST_CAPACITY = 1048576 as const;
4
+ export const MAX_LIGHTS = 256 as const;
5
+
6
+ export interface StandardClusterLayout {
7
+ readonly grid: { readonly x: number; readonly y: number; readonly z: number };
8
+ readonly clusterCount: number;
9
+ readonly clusterGridStrideU32: typeof CLUSTER_GRID_STRIDE_U32;
10
+ readonly clusterGridU32Length: number;
11
+ readonly lightIndexListCapacity: typeof LIGHT_INDEX_LIST_CAPACITY;
12
+ readonly lightDataSlotCount: typeof MAX_LIGHTS;
13
+ readonly lightBoundsInt32Length: number;
14
+ }
15
+
16
+ export function createStandardClusterLayout(
17
+ grid: { readonly x: number; readonly y: number; readonly z: number } = DEFAULT_CLUSTER_GRID,
18
+ ): StandardClusterLayout {
19
+ const clusterCount = grid.x * grid.y * grid.z;
20
+ return Object.freeze({
21
+ grid,
22
+ clusterCount,
23
+ clusterGridStrideU32: CLUSTER_GRID_STRIDE_U32,
24
+ clusterGridU32Length: clusterCount * CLUSTER_GRID_STRIDE_U32,
25
+ lightIndexListCapacity: LIGHT_INDEX_LIST_CAPACITY,
26
+ lightDataSlotCount: MAX_LIGHTS,
27
+ lightBoundsInt32Length: MAX_LIGHTS * 6,
28
+ });
29
+ }
30
+
31
+ export const STANDARD_CLUSTER_LAYOUT = createStandardClusterLayout();
@@ -0,0 +1,34 @@
1
+ import type { Mat4, Vec3 } from '@forgeax/engine-math';
2
+ import type {
3
+ DirectionalLightSnapshot,
4
+ PointLightSnapshot,
5
+ SpotLightSnapshot,
6
+ } from '../../render-system-extract';
7
+ import type { StandardLightCount } from '../standard-profile';
8
+
9
+ export interface StandardLocalLightInput {
10
+ readonly kind: 'point' | 'spot';
11
+ readonly position: Vec3;
12
+ readonly range: number;
13
+ /** Whether the shadow producer admitted this local light to its atlas. */
14
+ readonly shadowed: boolean;
15
+ /**
16
+ * The immutable extract snapshot used by the record payload packer. Keeping
17
+ * it on the POD frame prevents Forward/Deferred from rebuilding a second
18
+ * light list while leaving hand-authored unit fixtures source-free.
19
+ */
20
+ readonly source?: PointLightSnapshot | SpotLightSnapshot;
21
+ }
22
+
23
+ export interface StandardLightFrame {
24
+ readonly directional: DirectionalLightSnapshot | undefined;
25
+ readonly local: readonly StandardLocalLightInput[];
26
+ readonly view: Mat4;
27
+ readonly projection: Mat4;
28
+ readonly near: number;
29
+ readonly far: number;
30
+ readonly grid: { readonly x: number; readonly y: number; readonly z: number };
31
+ readonly lightCount: StandardLightCount;
32
+ /** Graph path carried into inspection from the active Standard profile. */
33
+ readonly renderPath: 'forward' | 'deferred';
34
+ }
@@ -0,0 +1,43 @@
1
+ import { err, ok, type Result } from '@forgeax/engine-types';
2
+ import { bin, type ClusterBinScratch, createClusterBinScratch } from '../../cluster-binner';
3
+ import type { RenderError } from '../../errors/render';
4
+ import { fromClusterBinError } from './errors';
5
+ import type { StandardClusterLayout } from './layout';
6
+ import type { StandardLightFrame } from './light-frame';
7
+
8
+ export interface StandardClusterMembership {
9
+ readonly clusterGrid: Uint32Array;
10
+ readonly lightIndexList: Uint32Array;
11
+ readonly lightBounds: Int32Array;
12
+ readonly membershipEntryCount: number;
13
+ readonly scratch: ClusterBinScratch;
14
+ }
15
+
16
+ export function deriveStandardMembership(
17
+ frame: StandardLightFrame,
18
+ layout: StandardClusterLayout,
19
+ scratch = createClusterBinScratch(),
20
+ ): Result<StandardClusterMembership, RenderError> {
21
+ const clusterGrid = new Uint32Array(layout.clusterGridU32Length);
22
+ const lightIndexList = new Uint32Array(layout.lightIndexListCapacity);
23
+ const result = bin(
24
+ frame.local,
25
+ frame.view,
26
+ frame.projection,
27
+ layout.grid,
28
+ frame.near,
29
+ frame.far,
30
+ clusterGrid,
31
+ lightIndexList,
32
+ layout.lightIndexListCapacity,
33
+ scratch,
34
+ );
35
+ if (!result.ok) return err(fromClusterBinError(result.error));
36
+ return ok({
37
+ clusterGrid,
38
+ lightIndexList,
39
+ lightBounds: scratch.lightBounds.slice(0, frame.local.length * 6),
40
+ membershipEntryCount: result.value,
41
+ scratch,
42
+ });
43
+ }
@@ -0,0 +1,37 @@
1
+ import { err, ok, type Result } from '@forgeax/engine-types';
2
+ import { type RenderError, StandardLightBudgetExceededError } from '../../errors/render';
3
+ import { MAX_LIGHTS } from '../standard-profile';
4
+ import { createStandardClusterLayout, type StandardClusterLayout } from './layout';
5
+ import type { StandardLightFrame } from './light-frame';
6
+ import { deriveStandardMembership, type StandardClusterMembership } from './membership';
7
+
8
+ export type { StandardLightFrame } from './light-frame';
9
+
10
+ export interface PreparedStandardLighting extends StandardClusterMembership {
11
+ readonly directional: StandardLightFrame['directional'];
12
+ readonly local: StandardLightFrame['local'];
13
+ readonly layout: StandardClusterLayout;
14
+ readonly membershipLightCount: number;
15
+ readonly renderPath: NonNullable<StandardLightFrame['renderPath']>;
16
+ readonly maxLights: StandardLightFrame['lightCount'];
17
+ }
18
+
19
+ export function prepareStandardLighting(
20
+ frame: StandardLightFrame,
21
+ ): Result<PreparedStandardLighting, RenderError> {
22
+ if (frame.local.length > frame.lightCount || frame.local.length > MAX_LIGHTS) {
23
+ return err(new StandardLightBudgetExceededError(frame.local.length, frame.lightCount));
24
+ }
25
+ const layout = createStandardClusterLayout(frame.grid);
26
+ const membership = deriveStandardMembership(frame, layout);
27
+ if (!membership.ok) return membership;
28
+ return ok({
29
+ ...membership.value,
30
+ directional: frame.directional,
31
+ local: frame.local,
32
+ layout,
33
+ membershipLightCount: frame.local.length,
34
+ renderPath: frame.renderPath,
35
+ maxLights: frame.lightCount,
36
+ });
37
+ }