@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
@@ -21,8 +21,14 @@ import {
21
21
  } from '@forgeax/engine-rhi';
22
22
  import type { MaterialRenderState, MeshAsset } from '@forgeax/engine-types';
23
23
  import { handleSlot, toShared } from '@forgeax/engine-types';
24
+ import { CUBE_CAMERA_FACE_ORDER } from '../components/cube-camera';
25
+ import type { DirectionalShadowQuality } from '../components/directional-shadow-filter';
24
26
  import type { MeshGpuHandles } from '../device/gpu-residency';
25
- import { ObservationUnavailableError, type ObservationUnavailableReason } from '../errors/render';
27
+ import {
28
+ ObservationUnavailableError,
29
+ type ObservationUnavailableReason,
30
+ RendererOperationError,
31
+ } from '../errors/render';
26
32
  import type { FogFrame } from '../extract/environment';
27
33
  import type { GpuDrivenProduction, PreparedGpuDrivenFrame } from '../gpu-driven/production-raster';
28
34
  import { GpuBuffer } from '../gpu-resource';
@@ -32,8 +38,12 @@ import {
32
38
  GPU_BUFFER_USAGE_VERTEX,
33
39
  } from '../gpu-usage';
34
40
  import { disposeTransientInstanceBuffers } from '../instance-buffer-cache';
41
+ import { inspectStandardLighting } from '../pipeline/standard-lighting/inspection';
42
+ import { standardLightingTopologySignature } from '../pipeline/standard-lighting/topology';
43
+ import { inspectPointShadow } from '../point-shadow-inspection';
35
44
  import type {
36
45
  CameraSnapshot,
46
+ CubeCameraSnapshot,
37
47
  RenderRecordPhase,
38
48
  SpotLightProjectorFrameContext,
39
49
  VolumetricFogFrameContext,
@@ -48,6 +58,7 @@ import type {
48
58
  SkylightSnapshot,
49
59
  } from '../render-system-extract';
50
60
  import type { PersistentGpuDrivenState } from '../scene/render-scene';
61
+ import { SHADOW_ATLAS_DEFAULT_LAYERS } from '../shadow-atlas';
51
62
  import {
52
63
  getTransparentSortConfig,
53
64
  TRANSPARENT_SORT_MODE_LAYER_Y,
@@ -63,7 +74,6 @@ import {
63
74
  resolveSkyboxActive,
64
75
  warnZeroLightStandard,
65
76
  writeHdrpClusterAndSsaoBuffers,
66
- writePointSpotLightBuffers,
67
77
  writeShadowParamsBuffer,
68
78
  } from './frame-lighting';
69
79
  import {
@@ -223,12 +233,18 @@ import { computeSplitLdrSprite } from './main-pass-sprite-draws';
223
233
  import { cleanPerEntityCache, ensureMeshSsboCapacity, uploadMeshSsboBatch } from './mesh-ssbo';
224
234
  import { writeShadowCasterUniforms } from './shadow-pass';
225
235
  import {
236
+ type CubeCaptureGraphState,
226
237
  ensureCompiledFrameGraph,
227
238
  executeCompiledFrameGraph,
228
239
  type RenderFeatureGraphCandidate,
229
240
  settleVolumetricFogGraphCandidate,
230
241
  } from './typed-frame-graph';
231
- import { writePointsLinesViewUbo, writeViewUbo } from './view-ubo';
242
+ import {
243
+ CUBE_CAPTURE_VIEW_SLOT_BASE,
244
+ VIEW_UNIFORM_SLOT_STRIDE,
245
+ writePointsLinesViewUbo,
246
+ writeViewUbo,
247
+ } from './view-ubo';
232
248
 
233
249
  /**
234
250
  * A frame-local material table. Repeated snapshot objects share one UBO slot;
@@ -301,6 +317,67 @@ export function materialSlotCountForPrefix(
301
317
 
302
318
  export type RecordProfileRunner = <T>(phase: RenderRecordPhase, action: () => T) => T;
303
319
 
320
+ export interface CubeCaptureFrameInput {
321
+ readonly snapshots: readonly CubeCameraSnapshot[];
322
+ readonly scheduler: import('../capture/scheduler').CubeCaptureScheduler;
323
+ readonly state: CubeCaptureGraphState;
324
+ readonly reflectionProbes?: import('./render-context').ReflectionProbeRecordState;
325
+ }
326
+
327
+ function prepareCubeCapture(
328
+ internals: RenderSystemInternals,
329
+ input: CubeCaptureFrameInput | undefined,
330
+ displayCamera: CameraSnapshot,
331
+ ): readonly import('./typed-frame-graph').CubeCaptureGraphWork[] {
332
+ if (input === undefined) return [];
333
+ if (input.reflectionProbes === undefined) delete input.state.reflectionProbes;
334
+ else input.state.reflectionProbes = input.reflectionProbes.graph;
335
+ input.scheduler.beginFrame();
336
+ for (const snapshot of input.snapshots) {
337
+ const inspection = input.scheduler.inspect(snapshot.target);
338
+ if (inspection.candidateGeneration !== undefined) continue;
339
+ const requested = input.scheduler.request({
340
+ target: snapshot.target,
341
+ position: snapshot.position,
342
+ near: snapshot.near,
343
+ far: snapshot.far,
344
+ updateIntent: snapshot.updateIntent,
345
+ requestVersion: snapshot.requestVersion,
346
+ faceBudget: snapshot.faceBudget,
347
+ });
348
+ if (!requested.ok) internals.errorRegistry.fire(requested.error);
349
+ }
350
+ const work = input.scheduler.nextWork();
351
+ const physicalWork: import('./typed-frame-graph').CubeCaptureGraphWork[] = [];
352
+ for (const item of work) {
353
+ const physical = internals.getRenderTargetPhysical?.(item.target);
354
+ if (physical === undefined) continue;
355
+ const faceIndex = CUBE_CAMERA_FACE_ORDER.indexOf(item.face);
356
+ if (faceIndex < 0) continue;
357
+ const faceWorld = mat4.create();
358
+ mat4.invert(faceWorld, item.view);
359
+ physicalWork.push({
360
+ target: item.target,
361
+ faceIndex,
362
+ physical,
363
+ faceCamera: {
364
+ ...displayCamera,
365
+ position: vec3.create(item.position[0] ?? 0, item.position[1] ?? 0, item.position[2] ?? 0),
366
+ world: faceWorld,
367
+ fov: Math.PI / 2,
368
+ aspect: 1,
369
+ near: item.near,
370
+ far: item.far,
371
+ projection: 'perspective',
372
+ },
373
+ viewBindGroupDynamicOffset:
374
+ (CUBE_CAPTURE_VIEW_SLOT_BASE + physicalWork.length) * VIEW_UNIFORM_SLOT_STRIDE,
375
+ });
376
+ }
377
+ input.state.work = physicalWork;
378
+ return physicalWork;
379
+ }
380
+
304
381
  const EMPTY_ENTITY_KEYS: ReadonlySet<number> = new Set<number>();
305
382
 
306
383
  function runRecordProfilePhase<T>(
@@ -739,6 +816,8 @@ function prepareSpotLightProjectorFrame(
739
816
  texture: resident.value.texture.handle,
740
817
  view: resident.value.view,
741
818
  sampler: sampler.value,
819
+ spotIndex: lights.spot.indexOf(source),
820
+ lightSlotIndex: lights.point.length + lights.spot.indexOf(source),
742
821
  };
743
822
  }
744
823
 
@@ -1036,11 +1115,12 @@ export function recordFrame(
1036
1115
  gpuDriven?: {
1037
1116
  readonly owner: GpuDrivenProduction;
1038
1117
  readonly scene: PersistentGpuDrivenState | undefined;
1039
- readonly onCoverage: (ownsAll: boolean) => void;
1118
+ readonly onCoverage: (ownsAll: boolean, clusteredLighting: boolean) => void;
1040
1119
  },
1041
1120
  renderReadLeases?: readonly RenderReadLease[],
1042
1121
  featureGraphCandidate?: RenderFeatureGraphCandidate,
1043
1122
  pointsLinesOwner?: import('./render-context').PointsLinesRecordOwner,
1123
+ cubeCapture?: CubeCaptureFrameInput,
1044
1124
  environmentSignature = '',
1045
1125
  fogSignature = '',
1046
1126
  environmentReady = true,
@@ -1097,6 +1177,23 @@ export function recordFrame(
1097
1177
  if (!camera) return false;
1098
1178
  const pipelineState = internals.getPipelineState();
1099
1179
  if (pipelineState === null) return false;
1180
+ if (pipelineState.device !== internals.device) {
1181
+ internals.errorRegistry.fire(
1182
+ new RendererOperationError('device-operation-failed', {
1183
+ operation: 'draw',
1184
+ cause: {
1185
+ code: 'pipeline-device-mismatch',
1186
+ expected: 'pipelineState.device === runtime.device',
1187
+ hint: 'discard stale pipeline state before recording the next frame',
1188
+ detail: {
1189
+ expected: 'pipelineState.device === runtime.device',
1190
+ hint: 'discard stale pipeline state before recording the next frame',
1191
+ },
1192
+ },
1193
+ }),
1194
+ );
1195
+ return false;
1196
+ }
1100
1197
  frameState.currentFrameObservationSource = undefined;
1101
1198
  frameState.currentDirectionalShadowView = null;
1102
1199
  frameState.currentSpotShadowView = null;
@@ -1104,7 +1201,7 @@ export function recordFrame(
1104
1201
  // Record-stage fold operator linear scan (groups transparent-sort entries
1105
1202
  // into fold buckets + records the fold-eligible count metric). Extracted to
1106
1203
  // computeFoldBuckets (M3/w18).
1107
- const { foldBuckets, light } = runRecordProfilePhase(profilePhase, 'record/scene-state', () => {
1204
+ const lightingPreparation = runRecordProfilePhase(profilePhase, 'record/scene-state', () => {
1108
1205
  const foldBuckets = runRecordProfilePhase(
1109
1206
  profilePhase,
1110
1207
  'record/scene-state/fold-buckets',
@@ -1115,11 +1212,11 @@ export function recordFrame(
1115
1212
  // ensure + ExtractedLights three-arm destructure (directional fallback,
1116
1213
  // point/spot arrays, totalLightCount). Extracted to prepareFrameLighting
1117
1214
  // (M3/w18) so recordFrame stays a skeleton.
1118
- const { light, pointLights, spotLights, totalLightCount } = runRecordProfilePhase(
1119
- profilePhase,
1120
- 'record/scene-state/lighting-prep',
1121
- () => prepareFrameLighting(internals, frameState, lights),
1215
+ const lighting = runRecordProfilePhase(profilePhase, 'record/scene-state/lighting-prep', () =>
1216
+ prepareFrameLighting(internals, frameState, lights, camera, pipelineState),
1122
1217
  );
1218
+ if (!lighting.ok) return lighting;
1219
+ const { light, totalLightCount, standard } = lighting.value;
1123
1220
 
1124
1221
  runRecordProfilePhase(profilePhase, 'record/scene-state/ambient-resolution', () => {
1125
1222
  // feat-20260520-skylight-ibl-cubemap M4 / t27 (AC-10 + F-4 nit):
@@ -1166,31 +1263,17 @@ export function recordFrame(
1166
1263
  }
1167
1264
  });
1168
1265
 
1169
- // feat-20260608-cluster-lighting M5 / w21 + M6 / w23: HDRP per-frame CPU
1170
- // cluster binning + light-data / cluster-grid / SSAO buffer uploads. Runs
1171
- // only when the HDRP pipeline is active and there is at least one punctual
1172
- // light. Extracted to a sub-function (M3/w18) so recordFrame stays a
1173
- // skeleton; fail-soft error semantics are preserved verbatim.
1174
- runRecordProfilePhase(profilePhase, 'record/scene-state/hdrp-cluster', () => {
1175
- writeHdrpClusterAndSsaoBuffers(
1176
- internals,
1177
- frameState,
1178
- camera,
1179
- pointLights,
1180
- spotLights,
1181
- profilePhase,
1182
- pipelineState.hdrpClusterMembershipPipeline !== null &&
1183
- internals.device.caps?.compute === true,
1184
- pipelineState.hdrpClusterMembershipBindGroupLayout,
1185
- );
1186
- });
1187
-
1188
1266
  // Zero-light standard-material once-warn (no Skylight + 0 direct light +
1189
1267
  // >=1 lit material -> black). Extracted to warnZeroLightStandard (M3/w18).
1190
1268
  warnZeroLightStandard(frameState, renderables, skylight, totalLightCount);
1191
1269
 
1192
- return { foldBuckets, light };
1270
+ return { ok: true as const, value: { foldBuckets, light, standard } };
1193
1271
  });
1272
+ if (!lightingPreparation.ok) {
1273
+ internals.errorRegistry.fire(lightingPreparation.error);
1274
+ return false;
1275
+ }
1276
+ const { foldBuckets, light, standard } = lightingPreparation.value;
1194
1277
 
1195
1278
  // Case E (this commit): 0 renderables = legitimate scene (LO §1.1
1196
1279
  // hello-window minimum semantic). Mirrors the Case C softening for
@@ -1282,7 +1365,6 @@ export function recordFrame(
1282
1365
  meshes: pipelineState.meshes,
1283
1366
  viewBindGroupLayout: pipelineState.viewBindGroupLayout,
1284
1367
  meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
1285
- hdrp: frameState.isHdrpActive,
1286
1368
  });
1287
1369
  if (prepared.ok) preparedGpuDriven = prepared.value;
1288
1370
  else if (prepared.error.code !== 'rhi-not-available')
@@ -1296,7 +1378,36 @@ export function recordFrame(
1296
1378
  lights.pointShadow.length > 0 ||
1297
1379
  lights.spot.some((spot) => spot.castShadow);
1298
1380
  const gpuOwnsAllPrimaryRaster = preparedGpuDriven?.ownsAllRenderables === true;
1299
- gpuDriven?.onCoverage(gpuOwnsAllPrimaryRaster && !cpuShadowCastersActive);
1381
+ gpuDriven?.onCoverage(
1382
+ gpuOwnsAllPrimaryRaster && !cpuShadowCastersActive,
1383
+ standard.kind === 'clustered',
1384
+ );
1385
+
1386
+ const cubeCaptureWork = prepareCubeCapture(internals, cubeCapture, camera);
1387
+
1388
+ // Validate renderables before graph admission. Besides producing the draw
1389
+ // plan, this is the owner of per-frame cache eviction; running it before a
1390
+ // graph candidate can fail guarantees despawned instance buffers are
1391
+ // released even when the frame cannot be submitted.
1392
+ const gpuOwnedEntityKeys = preparedGpuDriven?.entityKeys ?? EMPTY_ENTITY_KEYS;
1393
+ const validated = runRecordProfilePhase(profilePhase, 'record/validation', () =>
1394
+ gpuOwnsAllPrimaryRaster && !cpuShadowCastersActive && cubeCaptureWork.length === 0
1395
+ ? []
1396
+ : validateRenderables(
1397
+ internals,
1398
+ world,
1399
+ worlds,
1400
+ pipelineState,
1401
+ frameState,
1402
+ renderables,
1403
+ transparentDispatch,
1404
+ cpuShadowCastersActive || cubeCaptureWork.length > 0
1405
+ ? EMPTY_ENTITY_KEYS
1406
+ : gpuOwnedEntityKeys,
1407
+ ),
1408
+ );
1409
+ gpuDriven?.owner.recordCpuValidation(validated, gpuOwnedEntityKeys);
1410
+ cleanPerFrameCaches(internals, frameState, validated, gpuOwnedEntityKeys);
1300
1411
 
1301
1412
  const preflightGraph = runRecordProfilePhase(profilePhase, 'record/render-graph', () =>
1302
1413
  ensureCompiledFrameGraph(
@@ -1310,14 +1421,60 @@ export function recordFrame(
1310
1421
  graphShadowMapSize,
1311
1422
  preparedGpuDriven,
1312
1423
  featureGraphCandidate,
1424
+ cubeCapture?.state,
1313
1425
  cameras.length === 0,
1314
1426
  transmissionDemand,
1315
1427
  graphVolumetricFog,
1316
1428
  volumeTopologyCandidate,
1429
+ standard,
1317
1430
  ),
1318
1431
  );
1319
1432
  if (preflightGraph === null) return false;
1320
1433
 
1434
+ // The compiled graph is the acceptance boundary for the prepared
1435
+ // Standard lighting declaration. Upload the same declaration only after
1436
+ // that graph is current; a failed candidate therefore cannot leave an old
1437
+ // graph consuming a new buffer generation.
1438
+ if (frameState.standardLightingGraphSignature !== standardLightingTopologySignature(standard)) {
1439
+ internals.errorRegistry.fire(
1440
+ new RendererOperationError('device-operation-failed', {
1441
+ operation: 'draw',
1442
+ cause: {
1443
+ code: 'standard-graph-generation-mismatch',
1444
+ expected: 'compiled graph and prepared Standard lighting share one topology signature',
1445
+ hint: 'retry the frame after the Standard graph candidate is accepted',
1446
+ detail: {
1447
+ expected: standardLightingTopologySignature(standard),
1448
+ actual: frameState.standardLightingGraphSignature,
1449
+ },
1450
+ },
1451
+ }),
1452
+ );
1453
+ return false;
1454
+ }
1455
+ if (standard.kind === 'clustered') {
1456
+ const clusterWrite = runRecordProfilePhase(
1457
+ profilePhase,
1458
+ 'record/scene-state/hdrp-cluster',
1459
+ () =>
1460
+ writeHdrpClusterAndSsaoBuffers(
1461
+ internals,
1462
+ frameState,
1463
+ camera,
1464
+ standard.prepared,
1465
+ standard.transport,
1466
+ profilePhase,
1467
+ pipelineState.hdrpClusterMembershipPipeline !== null,
1468
+ pipelineState.hdrpClusterMembershipBindGroupLayout,
1469
+ preparedSpotLightProjector?.lightSlotIndex,
1470
+ ),
1471
+ );
1472
+ if (!clusterWrite.ok) {
1473
+ internals.errorRegistry.fire(clusterWrite.error);
1474
+ return false;
1475
+ }
1476
+ }
1477
+
1321
1478
  // Acquire the swap-chain texture + colour view + target dimensions (with
1322
1479
  // one reconfigure-and-retry on surface-outdated). Extracted to
1323
1480
  // acquireSwapChainTarget (M3/w18); returns null on unrecoverable failure
@@ -1501,41 +1658,6 @@ export function recordFrame(
1501
1658
  const geometryColorResolveView: TextureView | null = null;
1502
1659
  const ldrSpriteColorView: TextureView | null = null;
1503
1660
 
1504
- // Validate renderable handles + collect render plan. Empty `renderables`
1505
- // input or all-unregistered handles both produce `validatedOrdered.length === 0`,
1506
- // which is the Case E (clear-pass-only) path.
1507
- //
1508
- // feat-20260520-2d-sprite-layer-mvp M-3 / w25 (@new-surface): per-entry
1509
- // `renderableIndex` tracks the position into the original renderables[]
1510
- // array so the bucket-aware reordering below can correlate
1511
- // transparent-sort output (TransparentEntry.renderableIndex) back to
1512
- // the validated row.
1513
- // bug-20260527-renderstate-pipeline-dispatch-gap D-4:
1514
- // build a renderableIndex -> renderState map from dispatch entries
1515
- // so each ValidatedRenderable carries its per-material renderState
1516
- // override without an O(n^2) back-scan in the draw loop.
1517
- const gpuOwnedEntityKeys = preparedGpuDriven?.entityKeys ?? EMPTY_ENTITY_KEYS;
1518
- const validated = runRecordProfilePhase(profilePhase, 'record/validation', () =>
1519
- gpuOwnsAllPrimaryRaster && !cpuShadowCastersActive
1520
- ? []
1521
- : validateRenderables(
1522
- internals,
1523
- world,
1524
- worlds,
1525
- pipelineState,
1526
- frameState,
1527
- renderables,
1528
- transparentDispatch,
1529
- cpuShadowCastersActive ? EMPTY_ENTITY_KEYS : gpuOwnedEntityKeys,
1530
- ),
1531
- );
1532
- gpuDriven?.owner.recordCpuValidation(validated, gpuOwnedEntityKeys);
1533
-
1534
- // Per-frame cache clean-up: drop per-entity BG cache entries + instance
1535
- // buffers whose entityKey is absent from the current validated set (despawn
1536
- // eviction). Extracted to cleanPerFrameCaches (M3/w18).
1537
- cleanPerFrameCaches(internals, frameState, validated, gpuOwnedEntityKeys);
1538
-
1539
1661
  // Dispatch-ordered render plan: reorder validated renderables to dispatch
1540
1662
  // order, run the mesh-SSBO capacity gate (graceful truncation), and build
1541
1663
  // the fold dispatch plan. Extracted to buildDispatchPlan (M3/w18).
@@ -1607,6 +1729,7 @@ export function recordFrame(
1607
1729
  frameState.spotShadowSnapshots,
1608
1730
  fog,
1609
1731
  temporalView,
1732
+ preparedSpotLightProjector?.spotIndex,
1610
1733
  );
1611
1734
  if (pipelineState.pointsLinesViewBuffer !== undefined) {
1612
1735
  writePointsLinesViewUbo(
@@ -1623,11 +1746,6 @@ export function recordFrame(
1623
1746
  lights,
1624
1747
  );
1625
1748
 
1626
- // Per-frame full rewrite of the PointLight + SpotLight std430 storage
1627
- // buffers (header + first-slice cap N=4 slots). Extracted to
1628
- // writePointSpotLightBuffers (M3/w18).
1629
- writePointSpotLightBuffers(internals, pipelineState, lights);
1630
-
1631
1749
  // Per-renderable entity_world upload (batched): all N mat4+normalMatrix
1632
1750
  // slots assembled into a single contiguous scratch buffer, then flushed
1633
1751
  // as one writeBuffer call. Extracted to mesh-ssbo.ts (M3/w21) so its
@@ -1676,10 +1794,40 @@ export function recordFrame(
1676
1794
  validated.length > 0 || preparedGpuDriven !== undefined,
1677
1795
  bindGroupCounts,
1678
1796
  undefined,
1679
- false,
1797
+ true,
1798
+ standard,
1680
1799
  ),
1681
1800
  );
1682
1801
 
1802
+ for (const capture of cubeCaptureWork) {
1803
+ writeViewUbo(
1804
+ internals.device.queue,
1805
+ pipelineState.viewUniformBuffer,
1806
+ capture.faceCamera,
1807
+ light,
1808
+ lights,
1809
+ frameState.spotShadowSnapshots,
1810
+ capture.viewBindGroupDynamicOffset,
1811
+ undefined,
1812
+ preparedSpotLightProjector?.spotIndex,
1813
+ );
1814
+ }
1815
+ for (const probe of cubeCapture?.reflectionProbes?.graph.work ?? []) {
1816
+ if (probe.faceCamera === undefined || probe.viewBindGroupDynamicOffset === undefined)
1817
+ continue;
1818
+ writeViewUbo(
1819
+ internals.device.queue,
1820
+ pipelineState.viewUniformBuffer,
1821
+ probe.faceCamera,
1822
+ light,
1823
+ lights,
1824
+ frameState.spotShadowSnapshots,
1825
+ probe.viewBindGroupDynamicOffset,
1826
+ undefined,
1827
+ preparedSpotLightProjector?.spotIndex,
1828
+ );
1829
+ }
1830
+
1683
1831
  // ── feat-20260529-rendergraph-pass-abstraction M4 / w13b ───────────
1684
1832
  // The compiled typed graph is the sole per-frame pass owner.
1685
1833
  //
@@ -1755,16 +1903,21 @@ export function recordFrame(
1755
1903
  dispatch: transparentDispatch,
1756
1904
  hdrpClusterBindGroup,
1757
1905
  hdrpClusterMembershipBindGroup,
1906
+ standardLighting: standard,
1758
1907
  foldDispatchPlan,
1759
1908
  materialSlotIndices,
1760
1909
  materialSlots,
1761
1910
  materialSlotOwners,
1762
1911
  materialSlotCount,
1912
+ ...(cubeCapture?.reflectionProbes === undefined
1913
+ ? {}
1914
+ : { reflectionProbes: cubeCapture.reflectionProbes }),
1763
1915
  pointsLines: pointsLinesOwner,
1764
1916
  materialBgAssemblyCache: frameState.materialBgAssemblyCache,
1765
1917
  directionalShadowCacheReuse: directionalShadowCache.reuse,
1766
1918
  ...(profilePhase !== undefined ? { profilePhase } : {}),
1767
1919
  };
1920
+ let completion: Promise<unknown> | undefined;
1768
1921
  const stagedTemporalFrame = {
1769
1922
  frameId: frameState.frameNumber,
1770
1923
  currentViewProjection: new Float32Array(currentViewProjection),
@@ -1788,9 +1941,18 @@ export function recordFrame(
1788
1941
  profilePhase === undefined
1789
1942
  ? undefined
1790
1943
  : (pass, encode) => profilePhase(graphExecutionPhase(pass.name), encode),
1944
+ cubeCaptureWork.length === 0 ? undefined : cubeCaptureWork.map((work) => work.faceIndex),
1945
+ (done) => {
1946
+ completion = done;
1947
+ },
1791
1948
  timingCapture,
1792
1949
  ),
1793
1950
  );
1951
+ if (cubeCapture !== undefined && cubeCaptureWork.length > 0) {
1952
+ const completed = cubeCapture.scheduler.completeSubmission(submitted, completion);
1953
+ if (!completed.ok) internals.errorRegistry.fire(completed.error);
1954
+ }
1955
+ cubeCapture?.reflectionProbes?.completeSubmission(submitted, completion);
1794
1956
  if (submitted && !directionalShadowCache.reuse) {
1795
1957
  frameState.directionalShadowCache = frameState.directionalShadowCacheRecorded
1796
1958
  ? directionalShadowCache.next
@@ -1837,6 +1999,11 @@ export function recordFrame(
1837
1999
  frameState.volumetricFogAccepted = volumetricFog;
1838
2000
  frameState.volumetricFogAcceptedContext = preparedVolumetricFog;
1839
2001
  }
2002
+ frameState.standardLightingInspection = inspectStandardLighting(standard);
2003
+ frameState.pointShadowInspection = inspectPointShadow(
2004
+ lights.pointShadow,
2005
+ SHADOW_ATLAS_DEFAULT_LAYERS,
2006
+ );
1840
2007
  }
1841
2008
  return submitted;
1842
2009
  } finally {
@@ -1857,9 +2024,9 @@ export function recordFrame(
1857
2024
  );
1858
2025
  }
1859
2026
  frameState.frameNumber += 1;
1860
- // feat-20260608-cluster-lighting M5 / w22: clear HDRP once-per-frame fired
2027
+ // feat-20260608-cluster-lighting M5 / w22: clear Standard once-per-frame fired
1861
2028
  // set so the next frame re-fires if the condition persists.
1862
- frameState.hdrpOncePerFrameFired.clear();
2029
+ frameState.standardOncePerFrameFired.clear();
1863
2030
  }
1864
2031
  }
1865
2032
 
@@ -1927,6 +2094,20 @@ function sameLightViewProj(
1927
2094
  return true;
1928
2095
  }
1929
2096
 
2097
+ function sameDirectionalShadowQuality(
2098
+ previous: DirectionalShadowQuality,
2099
+ next: DirectionalShadowQuality,
2100
+ ): boolean {
2101
+ if (previous.kind !== next.kind) return false;
2102
+ if (previous.kind === 'pcf' && next.kind === 'pcf') return previous.kernel === next.kernel;
2103
+ if (previous.kind !== 'pcss' || next.kind !== 'pcss') return false;
2104
+ return (
2105
+ previous.preset === next.preset &&
2106
+ previous.angularRadiusRadians === next.angularRadiusRadians &&
2107
+ previous.maxPenumbraTexels === next.maxPenumbraTexels
2108
+ );
2109
+ }
2110
+
1930
2111
  function prepareDirectionalShadowCache(
1931
2112
  internals: RenderSystemInternals,
1932
2113
  frameState: RenderFrameState,
@@ -1946,6 +2127,7 @@ function prepareDirectionalShadowCache(
1946
2127
  cascadeCount === undefined ||
1947
2128
  cascadeCount <= 0 ||
1948
2129
  lightViewProj === undefined ||
2130
+ lights.directionalShadowQuality === undefined ||
1949
2131
  validatedOrdered.length === 0
1950
2132
  ) {
1951
2133
  return { reuse: false, next: null };
@@ -1958,6 +2140,10 @@ function prepareDirectionalShadowCache(
1958
2140
  cached !== null &&
1959
2141
  cached.shadowMapSize === shadowMapSize &&
1960
2142
  cached.cascadeCount === cascadeCount &&
2143
+ sameDirectionalShadowQuality(
2144
+ cached.directionalShadowQuality,
2145
+ lights.directionalShadowQuality,
2146
+ ) &&
1961
2147
  cached.pipelineHandle === frameState.installedPipelineHandle &&
1962
2148
  cached.graphTopologyKey === frameState.compiledFrameGraphTopologyKey &&
1963
2149
  cached.assetCatalogEpoch === assetCatalogEpoch &&
@@ -1986,6 +2172,7 @@ function prepareDirectionalShadowCache(
1986
2172
  graphTopologyKey: frameState.compiledFrameGraphTopologyKey,
1987
2173
  shadowMapSize,
1988
2174
  cascadeCount,
2175
+ directionalShadowQuality: lights.directionalShadowQuality,
1989
2176
  lightViewProj: lightViewProj.map((matrix) => new Float32Array(matrix)),
1990
2177
  meshResidencyEpoch,
1991
2178
  },