@forgeax/engine-render 0.1.21 → 0.1.23

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 (426) hide show
  1. package/README.md +150 -29
  2. package/dist/assembly/backend-contract.d.ts +13 -0
  3. package/dist/assembly/backend-contract.d.ts.map +1 -1
  4. package/dist/assembly/device-feature-admission.d.ts.map +1 -1
  5. package/dist/assembly/factory.d.ts +14 -6
  6. package/dist/assembly/factory.d.ts.map +1 -1
  7. package/dist/assembly/host-contract.d.ts +10 -0
  8. package/dist/assembly/host-contract.d.ts.map +1 -1
  9. package/dist/authoring.mjs +1 -1
  10. package/dist/{chunk-PCHBPYML.mjs → chunk-4L3P6DT5.mjs} +2 -2
  11. package/dist/chunk-4L3P6DT5.mjs.map +1 -0
  12. package/dist/{chunk-VFPLTKCU.mjs → chunk-4NPUASZR.mjs} +15 -4
  13. package/dist/chunk-4NPUASZR.mjs.map +1 -0
  14. package/dist/chunk-DIYW3M5D.mjs +5085 -0
  15. package/dist/chunk-DIYW3M5D.mjs.map +1 -0
  16. package/dist/chunk-FBC4R44J.mjs +1003 -0
  17. package/dist/chunk-FBC4R44J.mjs.map +1 -0
  18. package/dist/{chunk-FS2KNUQH.mjs → chunk-FF7K3FSO.mjs} +253 -489
  19. package/dist/chunk-FF7K3FSO.mjs.map +1 -0
  20. package/dist/chunk-HZUPSYW7.mjs +1104 -0
  21. package/dist/chunk-HZUPSYW7.mjs.map +1 -0
  22. package/dist/{chunk-6OPXDQ2E.mjs → chunk-QODA5UKU.mjs} +33 -294
  23. package/dist/chunk-QODA5UKU.mjs.map +1 -0
  24. package/dist/chunk-YALPS44A.mjs +296 -0
  25. package/dist/chunk-YALPS44A.mjs.map +1 -0
  26. package/dist/{chunk-5YADUCA7.mjs → chunk-ZUWKAZ2I.mjs} +585 -991
  27. package/dist/chunk-ZUWKAZ2I.mjs.map +1 -0
  28. package/dist/cluster-binner.d.ts +5 -0
  29. package/dist/cluster-binner.d.ts.map +1 -1
  30. package/dist/components/index.d.ts +3 -0
  31. package/dist/components/index.d.ts.map +1 -1
  32. package/dist/components/light-helpers.d.ts +4 -0
  33. package/dist/components/light-helpers.d.ts.map +1 -1
  34. package/dist/components/light-probe.d.ts +9 -0
  35. package/dist/components/light-probe.d.ts.map +1 -0
  36. package/dist/components/rect-area-light.d.ts +12 -0
  37. package/dist/components/rect-area-light.d.ts.map +1 -0
  38. package/dist/components/scene-instance.d.ts +4 -0
  39. package/dist/components/scene-instance.d.ts.map +1 -1
  40. package/dist/components/spot-light.d.ts +3 -0
  41. package/dist/components/spot-light.d.ts.map +1 -1
  42. package/dist/construct-renderer.mjs +10437 -10044
  43. package/dist/construct-renderer.mjs.map +1 -1
  44. package/dist/device/gpu-residency.d.ts +8 -1
  45. package/dist/device/gpu-residency.d.ts.map +1 -1
  46. package/dist/device/lifecycle-transaction.d.ts +2 -0
  47. package/dist/device/lifecycle-transaction.d.ts.map +1 -0
  48. package/dist/environment/lifecycle.d.ts.map +1 -1
  49. package/dist/errors/gpu-driven.d.ts +26 -0
  50. package/dist/errors/gpu-driven.d.ts.map +1 -0
  51. package/dist/errors/index.d.ts +1 -0
  52. package/dist/errors/index.d.ts.map +1 -1
  53. package/dist/errors/recover.d.ts +9 -1
  54. package/dist/errors/recover.d.ts.map +1 -1
  55. package/dist/errors/render.d.ts +26 -1
  56. package/dist/errors/render.d.ts.map +1 -1
  57. package/dist/extract/gpu-driven.d.ts +50 -0
  58. package/dist/extract/gpu-driven.d.ts.map +1 -0
  59. package/dist/extract/visibility.d.ts.map +1 -1
  60. package/dist/gpu-driven/batch-topology.d.ts +34 -0
  61. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  62. package/dist/gpu-driven/prepared-draw.d.ts +63 -0
  63. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -0
  64. package/dist/gpu-driven/production-raster.d.ts +98 -2
  65. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  66. package/dist/gpu-driven/view-gpu.d.ts +64 -1
  67. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  68. package/dist/gpu-scene-schema.d.ts +14 -0
  69. package/dist/gpu-scene-schema.d.ts.map +1 -1
  70. package/dist/gpu-scene.d.ts +8 -1
  71. package/dist/gpu-scene.d.ts.map +1 -1
  72. package/dist/gpu-usage.d.ts +0 -1
  73. package/dist/gpu-usage.d.ts.map +1 -1
  74. package/dist/hdrp-buffers.d.ts +3 -3
  75. package/dist/hdrp-buffers.d.ts.map +1 -1
  76. package/dist/index.d.ts +12 -1
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.mjs +90 -5
  79. package/dist/index.mjs.map +1 -1
  80. package/dist/inspection-types.d.ts +163 -1
  81. package/dist/inspection-types.d.ts.map +1 -1
  82. package/dist/internal.d.ts +9 -0
  83. package/dist/internal.d.ts.map +1 -1
  84. package/dist/internal.mjs +23 -2
  85. package/dist/internal.mjs.map +1 -1
  86. package/dist/light-buffer-layout.d.ts +58 -149
  87. package/dist/light-buffer-layout.d.ts.map +1 -1
  88. package/dist/materials.d.ts.map +1 -1
  89. package/dist/pbr-pipeline.d.ts +26 -12
  90. package/dist/pbr-pipeline.d.ts.map +1 -1
  91. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  92. package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -1
  93. package/dist/pipeline/standard-lighting/light-frame.d.ts +3 -3
  94. package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -1
  95. package/dist/pipeline/standard-lighting/topology.d.ts +1 -1
  96. package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -1
  97. package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -1
  98. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  99. package/dist/pipeline-builder.d.ts +2 -0
  100. package/dist/pipeline-builder.d.ts.map +1 -1
  101. package/dist/pipeline-spec.d.ts +1 -1
  102. package/dist/pipeline-spec.d.ts.map +1 -1
  103. package/dist/plugin.d.ts.map +1 -1
  104. package/dist/prepare/extended-lighting/ltc-resources.d.ts +31 -0
  105. package/dist/prepare/extended-lighting/ltc-resources.d.ts.map +1 -0
  106. package/dist/prepare/extended-lighting/resources.d.ts +72 -0
  107. package/dist/prepare/extended-lighting/resources.d.ts.map +1 -0
  108. package/dist/prepare/extended-lighting/spot-modifiers.d.ts +42 -0
  109. package/dist/prepare/extended-lighting/spot-modifiers.d.ts.map +1 -0
  110. package/dist/prepare/extended-lighting/state.d.ts +24 -0
  111. package/dist/prepare/extended-lighting/state.d.ts.map +1 -0
  112. package/dist/record/frame-lighting.d.ts +8 -6
  113. package/dist/record/frame-lighting.d.ts.map +1 -1
  114. package/dist/record/frame-snapshot.d.ts +50 -10
  115. package/dist/record/frame-snapshot.d.ts.map +1 -1
  116. package/dist/record/frame.d.ts +7 -3
  117. package/dist/record/frame.d.ts.map +1 -1
  118. package/dist/record/gpu-timing.d.ts +19 -7
  119. package/dist/record/gpu-timing.d.ts.map +1 -1
  120. package/dist/record/helpers.d.ts +0 -20
  121. package/dist/record/helpers.d.ts.map +1 -1
  122. package/dist/record/main-pass-geometry.d.ts +6 -1
  123. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  124. package/dist/record/main-pass-material.d.ts +3 -1
  125. package/dist/record/main-pass-material.d.ts.map +1 -1
  126. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  127. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  128. package/dist/record/render-context.d.ts +57 -6
  129. package/dist/record/render-context.d.ts.map +1 -1
  130. package/dist/record/shadow-pass.d.ts.map +1 -1
  131. package/dist/record/typed-frame-graph.d.ts +49 -2
  132. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  133. package/dist/record/view-ubo.d.ts +1 -2
  134. package/dist/record/view-ubo.d.ts.map +1 -1
  135. package/dist/reflection/inspection.d.ts +10 -0
  136. package/dist/reflection/inspection.d.ts.map +1 -0
  137. package/dist/reflection/projection.d.ts +49 -0
  138. package/dist/reflection/projection.d.ts.map +1 -1
  139. package/dist/reflection/record-owner.d.ts +60 -0
  140. package/dist/reflection/record-owner.d.ts.map +1 -0
  141. package/dist/render-contract.d.ts +14 -8
  142. package/dist/render-contract.d.ts.map +1 -1
  143. package/dist/render-pipeline.d.ts +7 -0
  144. package/dist/render-pipeline.d.ts.map +1 -1
  145. package/dist/render-system-extract.d.ts +104 -16
  146. package/dist/render-system-extract.d.ts.map +1 -1
  147. package/dist/render-system.d.ts +12 -1
  148. package/dist/render-system.d.ts.map +1 -1
  149. package/dist/scene/probe-blend-record.d.ts +22 -0
  150. package/dist/scene/probe-blend-record.d.ts.map +1 -0
  151. package/dist/scene/probe-blend.d.ts +142 -0
  152. package/dist/scene/probe-blend.d.ts.map +1 -0
  153. package/dist/scene/render-scene-types.d.ts +0 -6
  154. package/dist/scene/render-scene-types.d.ts.map +1 -1
  155. package/dist/scene/render-scene.d.ts +103 -9
  156. package/dist/scene/render-scene.d.ts.map +1 -1
  157. package/dist/scene/visibility/bounds.d.ts +20 -0
  158. package/dist/scene/visibility/bounds.d.ts.map +1 -0
  159. package/dist/scene/visibility/budget.d.ts +16 -0
  160. package/dist/scene/visibility/budget.d.ts.map +1 -0
  161. package/dist/scene/visibility/facet.d.ts +73 -0
  162. package/dist/scene/visibility/facet.d.ts.map +1 -0
  163. package/dist/scene/visibility/gpu-frame-timing.d.ts +41 -0
  164. package/dist/scene/visibility/gpu-frame-timing.d.ts.map +1 -0
  165. package/dist/scene/visibility/gpu-lod.d.ts +46 -0
  166. package/dist/scene/visibility/gpu-lod.d.ts.map +1 -0
  167. package/dist/scene/visibility/inspection.d.ts +132 -0
  168. package/dist/scene/visibility/inspection.d.ts.map +1 -0
  169. package/dist/scene/visibility/lod-selector.d.ts +25 -0
  170. package/dist/scene/visibility/lod-selector.d.ts.map +1 -0
  171. package/dist/scene/visibility/occlusion-confidence.d.ts +81 -0
  172. package/dist/scene/visibility/occlusion-confidence.d.ts.map +1 -0
  173. package/dist/scene/visibility/occlusion-pass.d.ts +57 -0
  174. package/dist/scene/visibility/occlusion-pass.d.ts.map +1 -0
  175. package/dist/scene/visibility/occlusion-query-pool.d.ts +57 -0
  176. package/dist/scene/visibility/occlusion-query-pool.d.ts.map +1 -0
  177. package/dist/scene/visibility/occlusion-runtime.d.ts +96 -0
  178. package/dist/scene/visibility/occlusion-runtime.d.ts.map +1 -0
  179. package/dist/scene/visibility/types.d.ts +34 -0
  180. package/dist/scene/visibility/types.d.ts.map +1 -0
  181. package/dist/ssr/admission.d.ts +122 -0
  182. package/dist/ssr/admission.d.ts.map +1 -0
  183. package/dist/ssr/composition.d.ts +32 -0
  184. package/dist/ssr/composition.d.ts.map +1 -0
  185. package/dist/ssr/errors.d.ts +39 -0
  186. package/dist/ssr/errors.d.ts.map +1 -0
  187. package/dist/ssr/identity.d.ts +13 -0
  188. package/dist/ssr/identity.d.ts.map +1 -0
  189. package/dist/systems/skin-palette-allocator.d.ts +1 -1
  190. package/dist/temporal/frame-coordinator.d.ts +13 -0
  191. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  192. package/dist/temporal/frame.d.ts +2 -0
  193. package/dist/temporal/frame.d.ts.map +1 -1
  194. package/dist/temporal/index.mjs +6 -5
  195. package/dist/temporal/view.d.ts +7 -0
  196. package/dist/temporal/view.d.ts.map +1 -1
  197. package/dist/tilemap-chunk-extract-system.d.ts +1 -1
  198. package/dist/typed-render-graph-primitives.d.ts +4 -0
  199. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  200. package/dist/volume/passes.d.ts +8 -4
  201. package/dist/volume/passes.d.ts.map +1 -1
  202. package/package.json +20 -20
  203. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +5 -0
  204. package/src/__tests__/detached-inspection.integration.test.ts +6 -0
  205. package/src/__tests__/device-feature-admission.unit.test.ts +13 -1
  206. package/src/__tests__/direct-light-snapshot.unit.test.ts +59 -3
  207. package/src/__tests__/environment-generation.integration.test.ts +16 -0
  208. package/src/__tests__/evidence-identity.ts +13 -0
  209. package/src/__tests__/evidence-identity.unit.test.ts +24 -0
  210. package/src/__tests__/extended-lighting-recovery.browser.test.ts +14 -0
  211. package/src/__tests__/extended-lighting-recovery.dawn.test.ts +14 -0
  212. package/src/__tests__/extended-lighting-recovery.integration.test.ts +81 -0
  213. package/src/__tests__/extended-lighting-resources.unit.test.ts +51 -0
  214. package/src/__tests__/extended-lighting-zero-feature.integration.test.ts +52 -0
  215. package/src/__tests__/factory-contract.integration.test.ts +33 -0
  216. package/src/__tests__/frame-observation.contract.test.ts +15 -0
  217. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +146 -0
  218. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +1 -0
  219. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +1 -0
  220. package/src/__tests__/gpu-driven-production.integration.test.ts +572 -9
  221. package/src/__tests__/gpu-driven-scaling.unit.test.ts +66 -2
  222. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +26 -1
  223. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +89 -2
  224. package/src/__tests__/gpu-driven-view.browser.test.ts +3 -3
  225. package/src/__tests__/gpu-driven-view.dawn.test.ts +3 -3
  226. package/src/__tests__/gpu-driven-view.unit.test.ts +50 -0
  227. package/src/__tests__/gpu-lod-evidence.ts +188 -0
  228. package/src/__tests__/gpu-lod-fallback.integration.test.ts +10 -0
  229. package/src/__tests__/gpu-lod-indirect.integration.test.ts +40 -0
  230. package/src/__tests__/gpu-lod-parity.dawn.test.ts +29 -0
  231. package/src/__tests__/gpu-lod-readiness.integration.test.ts +52 -0
  232. package/src/__tests__/gpu-lod-schema.unit.test.ts +66 -0
  233. package/src/__tests__/gpu-lod-selector.unit.test.ts +28 -0
  234. package/src/__tests__/gpu-lod-silhouette.browser.test.ts +13 -0
  235. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +66 -1
  236. package/src/__tests__/gpu-scene.dawn.test.ts +8 -1
  237. package/src/__tests__/gpu-scene.unit.test.ts +49 -3
  238. package/src/__tests__/light-buffer-layout.unit.test.ts +113 -3
  239. package/src/__tests__/light-inspection.unit.test.ts +55 -0
  240. package/src/__tests__/light-resource-error.unit.test.ts +43 -0
  241. package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +109 -0
  242. package/src/__tests__/lod-bounds.unit.test.ts +42 -0
  243. package/src/__tests__/lod-cpu-lane.integration.test.ts +55 -0
  244. package/src/__tests__/lod-cpu-no-change.perf.test.ts +19 -0
  245. package/src/__tests__/lod-gpu-frame-timing.unit.test.ts +49 -0
  246. package/src/__tests__/lod-multi-view.integration.test.ts +63 -0
  247. package/src/__tests__/lod-occlusion-device-loss.integration.test.ts +27 -0
  248. package/src/__tests__/lod-occlusion-final-decision.integration.test.ts +47 -0
  249. package/src/__tests__/lod-occlusion-inspection.integration.test.ts +217 -0
  250. package/src/__tests__/lod-occlusion-inspection.schema.test-d.ts +14 -0
  251. package/src/__tests__/lod-occlusion-inspection.unit.test.ts +72 -0
  252. package/src/__tests__/lod-occlusion-visual-evidence.ts +91 -0
  253. package/src/__tests__/lod-occlusion.browser.test.ts +15 -0
  254. package/src/__tests__/lod-occlusion.dawn.test.ts +20 -0
  255. package/src/__tests__/lod-selector.profile.test.ts +35 -0
  256. package/src/__tests__/lod-selector.unit.test.ts +68 -0
  257. package/src/__tests__/lod-visibility-identity.unit.test.ts +198 -0
  258. package/src/__tests__/lod-visibility-lifecycle.integration.test.ts +132 -0
  259. package/src/__tests__/ltc-resources.unit.test.ts +32 -0
  260. package/src/__tests__/material-contract-inventory.unit.test.ts +17 -0
  261. package/src/__tests__/occlusion-confidence.unit.test.ts +168 -0
  262. package/src/__tests__/occlusion-invalidation.integration.test.ts +37 -0
  263. package/src/__tests__/occlusion-pass-routing.unit.test.ts +16 -0
  264. package/src/__tests__/occlusion-pass.dawn.test.ts +20 -0
  265. package/src/__tests__/occlusion-pass.integration.test.ts +43 -0
  266. package/src/__tests__/occlusion-pass.rhi-null.test.ts +22 -0
  267. package/src/__tests__/occlusion-query-pool.rhi-null.test.ts +21 -0
  268. package/src/__tests__/occlusion-query-pool.unit.test.ts +95 -0
  269. package/src/__tests__/occlusion-query-ticket.integration.test.ts +47 -0
  270. package/src/__tests__/occlusion-query.dawn.test.ts +16 -0
  271. package/src/__tests__/occlusion-query.evidence.ts +178 -0
  272. package/src/__tests__/occlusion-query.rhi-null.test.ts +31 -0
  273. package/src/__tests__/occlusion-runtime.unit.test.ts +120 -0
  274. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +196 -0
  275. package/src/__tests__/probe-bindings.unit.test.ts +200 -0
  276. package/src/__tests__/probe-blend-numeric.unit.test.ts +67 -0
  277. package/src/__tests__/probe-blend-oracle.unit.test.ts +136 -0
  278. package/src/__tests__/probe-capacity.integration.test.ts +97 -0
  279. package/src/__tests__/probe-recovery.integration.test.ts +104 -0
  280. package/src/__tests__/probe-scene-dirty.integration.test.ts +211 -0
  281. package/src/__tests__/profiler-phase-catalog.test.ts +4 -1
  282. package/src/__tests__/profiler-phase-token.test.ts +1 -0
  283. package/src/__tests__/rect-area-boundary.unit.test.ts +24 -0
  284. package/src/__tests__/reflection-fallback-graph.unit.test.ts +67 -0
  285. package/src/__tests__/reflection-fallback-inspection.unit.test.ts +77 -0
  286. package/src/__tests__/reflection-fallback-lifecycle.integration.test.ts +21 -0
  287. package/src/__tests__/reflection-fallback-projection.unit.test.ts +90 -0
  288. package/src/__tests__/reflection-fallback-source.unit.test.ts +32 -0
  289. package/src/__tests__/reflection-probe-owner-placement.unit.test.ts +39 -0
  290. package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +120 -0
  291. package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
  292. package/src/__tests__/render-feature-docs.unit.test.ts +12 -7
  293. package/src/__tests__/render-graph-fallback.integration.test.ts +24 -0
  294. package/src/__tests__/render-perf/extended-lighting.bench.test.ts +66 -0
  295. package/src/__tests__/render-scene-projection.unit.test.ts +447 -18
  296. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +76 -0
  297. package/src/__tests__/skin-motion-regression.unit.test.ts +22 -0
  298. package/src/__tests__/skinned-shadow-caster.test.ts +2 -2
  299. package/src/__tests__/spot-modifier-projection.unit.test.ts +70 -0
  300. package/src/__tests__/ssr-admission-matrix.integration.test.ts +206 -0
  301. package/src/__tests__/ssr-composition.unit.test.ts +46 -0
  302. package/src/__tests__/ssr-fallback-zero-work.bench.test.ts +94 -0
  303. package/src/__tests__/ssr-recovery-errors.unit.test.ts +58 -0
  304. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +67 -0
  305. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +7 -0
  306. package/src/__tests__/standard-lighting-inspection.integration.test.ts +1 -1
  307. package/src/__tests__/standard-lighting-write.unit.test.ts +66 -2
  308. package/src/__tests__/temporal-fallback-generation.integration.test.ts +16 -0
  309. package/src/__tests__/temporal-view.unit.test.ts +3 -3
  310. package/src/__tests__/transmission-prewarm.unit.test.ts +69 -0
  311. package/src/__tests__/visibility-budget.integration.test.ts +140 -0
  312. package/src/__tests__/visibility-budget.unit.test.ts +70 -0
  313. package/src/__tests__/visibility-extract.integration.test.ts +11 -6
  314. package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +6 -3
  315. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +31 -28
  316. package/src/assembly/backend-contract.ts +13 -0
  317. package/src/assembly/device-feature-admission.ts +16 -9
  318. package/src/assembly/factory.ts +547 -93
  319. package/src/assembly/host-contract.ts +10 -0
  320. package/src/cluster-binner.ts +31 -0
  321. package/src/components/__tests__/lighting-authoring.unit.test.ts +39 -0
  322. package/src/components/__tests__/rect-area-light.unit.test.ts +33 -0
  323. package/src/components/index.ts +3 -0
  324. package/src/components/light-helpers.ts +52 -1
  325. package/src/components/light-probe.ts +13 -0
  326. package/src/components/rect-area-light.ts +15 -0
  327. package/src/components/scene-instance.ts +6 -0
  328. package/src/components/spot-light.ts +3 -0
  329. package/src/device/__tests__/extended-lighting-recovery.unit.test.ts +24 -0
  330. package/src/device/gpu-residency.ts +140 -9
  331. package/src/device/lifecycle-transaction.ts +12 -0
  332. package/src/environment/lifecycle.ts +8 -1
  333. package/src/errors/gpu-driven.ts +92 -0
  334. package/src/errors/index.ts +1 -0
  335. package/src/errors/recover.ts +32 -0
  336. package/src/errors/render.ts +55 -1
  337. package/src/extract/camera.ts +3 -3
  338. package/src/extract/gpu-driven.ts +108 -0
  339. package/src/extract/visibility.ts +0 -23
  340. package/src/gpu-driven/batch-topology.ts +206 -28
  341. package/src/gpu-driven/prepared-draw.ts +208 -0
  342. package/src/gpu-driven/production-raster.ts +953 -41
  343. package/src/gpu-driven/view-gpu.ts +411 -29
  344. package/src/gpu-scene-schema.ts +13 -0
  345. package/src/gpu-scene.ts +134 -65
  346. package/src/gpu-usage.ts +0 -1
  347. package/src/hdrp-buffers.ts +6 -6
  348. package/src/index.ts +74 -0
  349. package/src/inspection-types.ts +220 -1
  350. package/src/internal.ts +38 -0
  351. package/src/light-buffer-layout.ts +154 -324
  352. package/src/materials.ts +16 -2
  353. package/src/pbr-pipeline.ts +151 -45
  354. package/src/pipeline/standard-forward-lane.ts +47 -0
  355. package/src/pipeline/standard-lighting/graph.ts +2 -1
  356. package/src/pipeline/standard-lighting/light-frame.ts +3 -2
  357. package/src/pipeline/standard-lighting/topology.ts +1 -1
  358. package/src/pipeline/standard-lighting/transport.ts +2 -1
  359. package/src/pipeline/standard-pipeline.ts +2 -0
  360. package/src/pipeline-builder.ts +5 -2
  361. package/src/pipeline-spec.ts +12 -1
  362. package/src/plugin.ts +4 -0
  363. package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +56 -0
  364. package/src/prepare/extended-lighting/ltc-resources.ts +136 -0
  365. package/src/prepare/extended-lighting/resources.ts +389 -0
  366. package/src/prepare/extended-lighting/spot-modifiers.ts +194 -0
  367. package/src/prepare/extended-lighting/state.ts +92 -0
  368. package/src/record/__tests__/gpu-timing.unit.test.ts +177 -0
  369. package/src/record/__tests__/temporal-payload.unit.test.ts +23 -1
  370. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +20 -1
  371. package/src/record/frame-lighting.ts +252 -53
  372. package/src/record/frame-snapshot.ts +57 -12
  373. package/src/record/frame.ts +410 -93
  374. package/src/record/gpu-timing.ts +108 -44
  375. package/src/record/helpers.ts +0 -58
  376. package/src/record/main-pass-geometry.ts +204 -39
  377. package/src/record/main-pass-material.ts +8 -1
  378. package/src/record/main-pass-sprite-draws.ts +52 -85
  379. package/src/record/mesh-ssbo.ts +47 -36
  380. package/src/record/render-context.ts +65 -4
  381. package/src/record/shadow-pass.ts +66 -40
  382. package/src/record/typed-frame-graph.ts +225 -83
  383. package/src/record/view-ubo.ts +14 -24
  384. package/src/reflection/inspection.ts +67 -0
  385. package/src/reflection/projection.ts +196 -1
  386. package/src/reflection/record-owner.ts +1283 -0
  387. package/src/render-contract.ts +17 -5
  388. package/src/render-pipeline.ts +5 -0
  389. package/src/render-system-extract.ts +493 -134
  390. package/src/render-system.ts +811 -615
  391. package/src/scene/__tests__/changed-block-summary.candidate.test.ts +36 -0
  392. package/src/scene/probe-blend-record.ts +54 -0
  393. package/src/scene/probe-blend.ts +813 -0
  394. package/src/scene/render-scene-types.ts +0 -7
  395. package/src/scene/render-scene.ts +848 -183
  396. package/src/scene/visibility/bounds.ts +57 -0
  397. package/src/scene/visibility/budget.ts +40 -0
  398. package/src/scene/visibility/facet.ts +400 -0
  399. package/src/scene/visibility/gpu-frame-timing.ts +101 -0
  400. package/src/scene/visibility/gpu-lod.ts +119 -0
  401. package/src/scene/visibility/inspection.ts +313 -0
  402. package/src/scene/visibility/lod-selector.ts +84 -0
  403. package/src/scene/visibility/occlusion-confidence.ts +478 -0
  404. package/src/scene/visibility/occlusion-pass.ts +165 -0
  405. package/src/scene/visibility/occlusion-query-pool.ts +178 -0
  406. package/src/scene/visibility/occlusion-runtime.ts +672 -0
  407. package/src/scene/visibility/types.ts +50 -0
  408. package/src/ssr/admission.ts +451 -0
  409. package/src/ssr/composition.ts +73 -0
  410. package/src/ssr/errors.ts +76 -0
  411. package/src/ssr/identity.ts +12 -0
  412. package/src/systems/skin-palette-allocator.ts +1 -1
  413. package/src/temporal/frame-coordinator.ts +26 -0
  414. package/src/temporal/frame.ts +14 -1
  415. package/src/temporal/view.ts +11 -0
  416. package/src/tilemap-chunk-extract-system.ts +4 -4
  417. package/src/typed-render-graph-primitives.ts +48 -0
  418. package/src/volume/passes.ts +46 -49
  419. package/dist/.tsbuildinfo +0 -1
  420. package/dist/chunk-5YADUCA7.mjs.map +0 -1
  421. package/dist/chunk-6OPXDQ2E.mjs.map +0 -1
  422. package/dist/chunk-BXBQQKFQ.mjs +0 -67
  423. package/dist/chunk-BXBQQKFQ.mjs.map +0 -1
  424. package/dist/chunk-FS2KNUQH.mjs.map +0 -1
  425. package/dist/chunk-PCHBPYML.mjs.map +0 -1
  426. package/dist/chunk-VFPLTKCU.mjs.map +0 -1
@@ -25,7 +25,7 @@ import {
25
25
  getOrCreateHdrpBuffers,
26
26
  packClusterUniform,
27
27
  } from '../hdrp-buffers';
28
- import { packLightSlot } from '../light-buffer-layout';
28
+ import { DIRECT_LIGHT_SLOT_FLOAT_COUNT, packDirectLightSlot } from '../light-buffer-layout';
29
29
  import {
30
30
  type PreparedStandardLighting,
31
31
  prepareStandardLighting,
@@ -36,6 +36,12 @@ import {
36
36
  selectStandardClusterTransport,
37
37
  } from '../pipeline/standard-lighting/transport';
38
38
  import { DEFAULT_CLUSTER_GRID, DEFAULT_STANDARD_PROFILE } from '../pipeline/standard-profile';
39
+ import {
40
+ COOKIE_MATRIX_BYTES,
41
+ COOKIE_SLICE_SIZE,
42
+ IES_SLICE_HEIGHT,
43
+ IES_SLICE_WIDTH,
44
+ } from '../prepare/extended-lighting/resources';
39
45
  import type { ReflectionProbeTable } from '../reflection/gpu-table';
40
46
  import type { ReflectionProbeSelectionResult } from '../reflection/projection';
41
47
  import type { CameraSnapshot, RenderHdrpClusterPhase } from '../render-contract';
@@ -53,6 +59,8 @@ import {
53
59
  } from '../shadow-atlas';
54
60
  import { getOrCreateSsaoBuffers } from '../ssao-buffers';
55
61
  import { getSsaoParameters } from '../ssao-config';
62
+ // extendedLighting keeps Spot modifier factors on the existing light record
63
+ // path; no additional render pass or submit owner is introduced.
56
64
  import type { BindGroupCounts, RenderFrameState } from './frame-snapshot';
57
65
  import {
58
66
  computeProjectionMatrix,
@@ -155,10 +163,10 @@ export function buildPerFrameBindGroups(
155
163
  hdrpClusterBindGroup: BindGroup | null;
156
164
  hdrpClusterMembershipBindGroup: BindGroup | null;
157
165
  } {
158
- // View main (#1) chain = b0(viewUniformBuffer), b1(pointLightsBuffer),
159
- // b2(spotLightsBuffer), b3(graph shadowDepth view or
166
+ // View main (#1) chain = b0(viewUniformBuffer), b3(graph shadowDepth view or
160
167
  // shadowFallbackTextureView), b4(shadowSampler), b5(atlas view), b6
161
- // (shadowParams); variant 'view-main'.
168
+ // (shadowParams); variant 'view-main'. Local lights are never mirrored in
169
+ // the view group: Standard consumes the one Cluster group(2) payload.
162
170
  // Mesh (#2) chain = inner b0 buffer (meshStorageBuffer.buffer); variant 'mesh'.
163
171
  let viewBindGroup: BindGroup | null = null;
164
172
  let meshBindGroup: BindGroup | null = null;
@@ -229,6 +237,47 @@ export function buildPerFrameBindGroups(
229
237
  graphSpotShadowView !== undefined
230
238
  ? graphSpotShadowView
231
239
  : pipelineState.shadowFallbackTextureView;
240
+ const extendedLighting = pipelineState.extendedLightingAvailable ?? true;
241
+ const iesProfileTextureView = pipelineState.iesProfileTextureView;
242
+ const cookieTextureView = pipelineState.cookieTextureView;
243
+ const cookieMatrixBuffer = pipelineState.cookieMatrixBuffer;
244
+ const ltcLambertTextureView = pipelineState.ltcLambertTextureView;
245
+ const ltcGgxTextureView = pipelineState.ltcGgxTextureView;
246
+ const extendedLightingResources =
247
+ extendedLighting &&
248
+ iesProfileTextureView !== undefined &&
249
+ cookieTextureView !== undefined &&
250
+ cookieMatrixBuffer !== undefined &&
251
+ ltcLambertTextureView !== undefined &&
252
+ ltcGgxTextureView !== undefined
253
+ ? {
254
+ sampler: pipelineState.defaultSampler,
255
+ iesProfileTextureView,
256
+ cookieTextureView,
257
+ ltcLambertTextureView,
258
+ ltcGgxTextureView,
259
+ cookieMatrixBuffer,
260
+ }
261
+ : undefined;
262
+ if (extendedLighting && extendedLightingResources === undefined) {
263
+ return {
264
+ viewBindGroup: null,
265
+ meshBindGroup: null,
266
+ hdrpClusterBindGroup: null,
267
+ hdrpClusterMembershipBindGroup,
268
+ };
269
+ }
270
+ const extendedLightingViews =
271
+ extendedLightingResources !== undefined
272
+ ? [
273
+ extendedLightingResources.sampler,
274
+ extendedLightingResources.iesProfileTextureView,
275
+ extendedLightingResources.cookieTextureView,
276
+ extendedLightingResources.ltcLambertTextureView,
277
+ extendedLightingResources.ltcGgxTextureView,
278
+ extendedLightingResources.cookieMatrixBuffer,
279
+ ]
280
+ : [];
232
281
  const projectorAvailable = pipelineState.projectorAvailable !== false;
233
282
  const b11View = graphTargets?.projector ?? pipelineState.defaultWhiteTextureView;
234
283
  const b12Sampler = graphTargets?.projectorSampler ?? pipelineState.defaultSampler;
@@ -236,14 +285,15 @@ export function buildPerFrameBindGroups(
236
285
  frameState.viewBindGroupCache,
237
286
  [
238
287
  pipelineState.viewUniformBuffer,
239
- pipelineState.pointLightsBuffer,
240
- pipelineState.spotLightsBuffer,
241
288
  b3View,
242
289
  shadowSampler,
243
290
  b5View,
244
291
  pipelineState.shadowParamsBuffer,
245
292
  b8View,
246
- ...(projectorAvailable ? [b11View, b12Sampler] : []),
293
+ ...extendedLightingViews,
294
+ ...(extendedLightingResources === undefined && projectorAvailable
295
+ ? [b11View, b12Sampler]
296
+ : []),
247
297
  pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer,
248
298
  ],
249
299
  'view-main',
@@ -256,20 +306,6 @@ export function buildPerFrameBindGroups(
256
306
  value: { buffer: pipelineState.viewUniformBuffer, size: VIEW_UNIFORM_BYTES },
257
307
  },
258
308
  },
259
- {
260
- binding: 1,
261
- resource: {
262
- kind: 'buffer',
263
- value: { buffer: pipelineState.pointLightsBuffer },
264
- },
265
- },
266
- {
267
- binding: 2,
268
- resource: {
269
- kind: 'buffer',
270
- value: { buffer: pipelineState.spotLightsBuffer },
271
- },
272
- },
273
309
  {
274
310
  binding: 3,
275
311
  resource: {
@@ -339,12 +375,61 @@ export function buildPerFrameBindGroups(
339
375
  },
340
376
  },
341
377
  },
342
- ...(projectorAvailable
378
+ ...(extendedLightingResources === undefined && projectorAvailable
343
379
  ? [
344
380
  { binding: 11, resource: { kind: 'textureView' as const, value: b11View } },
345
381
  { binding: 12, resource: { kind: 'sampler' as const, value: b12Sampler } },
346
382
  ]
347
383
  : []),
384
+ ...(extendedLightingResources !== undefined
385
+ ? [
386
+ {
387
+ binding: 9,
388
+ resource: {
389
+ kind: 'sampler' as const,
390
+ value: extendedLightingResources.sampler,
391
+ },
392
+ },
393
+ {
394
+ binding: 11,
395
+ resource: {
396
+ kind: 'textureView' as const,
397
+ value: extendedLightingResources.iesProfileTextureView,
398
+ },
399
+ },
400
+ {
401
+ binding: 12,
402
+ resource: {
403
+ kind: 'textureView' as const,
404
+ value: extendedLightingResources.cookieTextureView,
405
+ },
406
+ },
407
+ {
408
+ binding: 13,
409
+ resource: {
410
+ kind: 'textureView' as const,
411
+ value: extendedLightingResources.ltcLambertTextureView,
412
+ },
413
+ },
414
+ {
415
+ binding: 14,
416
+ resource: {
417
+ kind: 'textureView' as const,
418
+ value: extendedLightingResources.ltcGgxTextureView,
419
+ },
420
+ },
421
+ {
422
+ binding: 15,
423
+ resource: {
424
+ kind: 'buffer' as const,
425
+ value: {
426
+ buffer: extendedLightingResources.cookieMatrixBuffer,
427
+ size: COOKIE_MATRIX_BYTES,
428
+ },
429
+ },
430
+ },
431
+ ]
432
+ : []),
348
433
  // feat-20260625-spot-light-shadow-mapping w25: the per-spot
349
434
  // fragment-read lightViewProj matrices fold into the View UBO
350
435
  // (binding 0, `view.spotLightViewProj`) — no standalone binding 9
@@ -451,12 +536,11 @@ export function buildPerFrameBindGroups(
451
536
 
452
537
  /**
453
538
  * feat-20260704 M3/w18: per-frame lighting preparation, extracted verbatim from
454
- * `recordFrame`. (1) pin the
455
- * point + spot shadow snapshot lists onto frameState for the URP shadow caster
539
+ * `recordFrame`. (1) pin the point + spot shadow snapshot lists onto frameState
540
+ * for the shadow caster
456
541
  * pass closures + lazy-allocate the point-shadow cube_array atlas on first
457
- * non-empty frame (AC-09 zero-shadow zero-alloc). (3) destructure ExtractedLights
458
- * into the directional-fallback `light` (zero-intensity Case C default), point /
459
- * spot arrays, and totalLightCount.
542
+ * non-empty frame (AC-09 zero-shadow zero-alloc). (3) derive the directional
543
+ * fallback and the unified local-light corpus used by Standard Cluster.
460
544
  *
461
545
  * @internal
462
546
  */
@@ -474,6 +558,7 @@ export function prepareFrameLighting(
474
558
  light: DirectionalLightSnapshot;
475
559
  pointLights: ExtractedLights['point'];
476
560
  spotLights: ExtractedLights['spot'];
561
+ rectLights: ExtractedLights['rect'];
477
562
  totalLightCount: number;
478
563
  standard: StandardTopologyInputValue;
479
564
  },
@@ -528,14 +613,16 @@ export function prepareFrameLighting(
528
613
  //
529
614
  // - lights.directional : feeds the View UBO at slot [16..23]
530
615
  // (lightDir + lightColor; existing path).
531
- // - lights.point[] : packed into pointLightsBuffer (std430).
532
- // - lights.spot[] : packed into spotLightsBuffer (std430).
616
+ // - lights.point/spot/rect[] : packed into the one unified Standard
617
+ // Cluster payload below. No per-kind light
618
+ // transport exists at the renderer boundary.
533
619
  //
534
620
  // Each variant carries the discriminant `kind` so the record-time packing
535
621
  // call sites can run exhaustive switch (charter P2 + AC-03).
536
622
  const directionalLight: DirectionalLightSnapshot | undefined = lights.directional;
537
623
  const pointLights = lights.point;
538
624
  const spotLights = lights.spot;
625
+ const rectLights = lights.rect;
539
626
 
540
627
  // Case C: 0 DirectionalLight = legitimate scene; the View UBO falls
541
628
  // back to a zero-intensity directional payload so the shader's
@@ -549,7 +636,10 @@ export function prepareFrameLighting(
549
636
  };
550
637
 
551
638
  const totalLightCount =
552
- (directionalLight !== undefined ? 1 : 0) + pointLights.length + spotLights.length;
639
+ (directionalLight !== undefined ? 1 : 0) +
640
+ pointLights.length +
641
+ spotLights.length +
642
+ rectLights.length;
553
643
 
554
644
  const clusterGrid = frameState.installedPipelineConfig?.clusterGrid ?? DEFAULT_CLUSTER_GRID;
555
645
  const local = [
@@ -574,6 +664,16 @@ export function prepareFrameLighting(
574
664
  : 1000,
575
665
  source,
576
666
  })),
667
+ ...rectLights.map((source) => ({
668
+ kind: 'rect-area' as const,
669
+ shadowed: false,
670
+ position: source.position,
671
+ range:
672
+ (Number.isFinite(source.invRangeSquared) && source.invRangeSquared > 0
673
+ ? Math.sqrt(1 / source.invRangeSquared)
674
+ : 1000) + Math.hypot(source.halfWidth, source.halfHeight),
675
+ source,
676
+ })),
577
677
  ];
578
678
  const prepared = prepareStandardLighting({
579
679
  directional: directionalLight,
@@ -590,14 +690,16 @@ export function prepareFrameLighting(
590
690
  // Storage-capable Standard frames keep the same Cluster ABI even when the
591
691
  // local-light corpus is empty. Directional-only scenes still need the
592
692
  // unified mesh group(2) contract so their PBR draws do not select a second
593
- // direct layout; the existing transport emits a zero-light payload and the
594
- // graph remains structurally identical. The non-storage backend is the
595
- // sole direct exception because no proven Cluster representation exists.
693
+ // local-light layout; the existing transport emits a zero-light payload and
694
+ // the graph remains structurally identical. The no-storage backend has no
695
+ // storage-backed local-light representation, so a directional-only frame
696
+ // remains the sole no-local transport case.
596
697
  if (prepared.value.local.length === 0 && !internals.device.caps.storageBuffer) {
597
698
  return ok({
598
699
  light,
599
700
  pointLights,
600
701
  spotLights,
702
+ rectLights,
601
703
  totalLightCount,
602
704
  standard: { kind: 'no-local-lights', prepared: prepared.value },
603
705
  });
@@ -619,11 +721,109 @@ export function prepareFrameLighting(
619
721
  light,
620
722
  pointLights,
621
723
  spotLights,
724
+ rectLights,
622
725
  totalLightCount,
623
726
  standard: { kind: 'clustered', prepared: prepared.value, transport: transport.value },
624
727
  });
625
728
  }
626
729
 
730
+ function modifierDigest(data: Uint8Array): number {
731
+ let hash = 2166136261;
732
+ for (let index = 0; index < data.length; index += 1) {
733
+ hash ^= data[index] ?? 0;
734
+ hash = Math.imul(hash, 16777619);
735
+ }
736
+ return hash >>> 0;
737
+ }
738
+
739
+ /** Upload authored Spot modifier payloads into the renderer-owned array lane. */
740
+ export function writeSpotModifierTextures(
741
+ internals: RenderSystemInternals,
742
+ pipelineState: PipelineState,
743
+ spots: readonly ExtractedLights['spot'][number][],
744
+ ): void {
745
+ const iesTexture = pipelineState.iesProfileTexture;
746
+ const cookieTexture = pipelineState.cookieTexture;
747
+ const cookieMatrixBuffer = pipelineState.cookieMatrixBuffer;
748
+ if (iesTexture === undefined && cookieTexture === undefined && cookieMatrixBuffer === undefined)
749
+ return;
750
+ let state = pipelineState.spotModifierUploadState;
751
+ if (state === undefined) {
752
+ state = { ies: new Map(), cookie: new Map(), cookieMatrix: new Map() };
753
+ pipelineState.spotModifierUploadState = state;
754
+ } else if (state.cookieMatrix === undefined) {
755
+ state = { ...state, cookieMatrix: new Map() };
756
+ pipelineState.spotModifierUploadState = state;
757
+ }
758
+ for (const spot of spots) {
759
+ if (spot.iesProfileSlice !== undefined && spot.iesProfileData !== undefined && iesTexture) {
760
+ if (spot.iesProfileData.byteLength === IES_SLICE_WIDTH * IES_SLICE_HEIGHT * 2) {
761
+ const digest = modifierDigest(spot.iesProfileData);
762
+ if (state.ies.get(spot.iesProfileSlice) !== digest) {
763
+ const result = internals.device.queue.writeTexture(
764
+ { texture: iesTexture, mipLevel: 0, origin: { x: 0, y: 0, z: spot.iesProfileSlice } },
765
+ spot.iesProfileData,
766
+ { offset: 0, bytesPerRow: IES_SLICE_WIDTH * 2, rowsPerImage: IES_SLICE_HEIGHT },
767
+ { width: IES_SLICE_WIDTH, height: IES_SLICE_HEIGHT, depthOrArrayLayers: 1 },
768
+ );
769
+ if (!result.ok) throw result.error;
770
+ state.ies.set(spot.iesProfileSlice, digest);
771
+ }
772
+ }
773
+ }
774
+ // Authored Cookie and the SpotLight projector share the same
775
+ // fixed 256x256 array transport on the extended topology. Their shader
776
+ // projection remains distinct: Cookie uses cookieMatrices, projector
777
+ // uses View.spotLightViewProj. Upload both identities when a frame carries
778
+ // both fields, while the direct-light metadata selects the one in use.
779
+ for (const modifier of [
780
+ {
781
+ slice: spot.cookieSlice,
782
+ data: spot.cookieData,
783
+ matrix: spot.cookieMatrix,
784
+ },
785
+ {
786
+ slice: spot.projectorSlice,
787
+ data: spot.projectorData,
788
+ matrix: spot.projectorMatrix,
789
+ },
790
+ ]) {
791
+ if (modifier.slice !== undefined && modifier.data !== undefined && cookieTexture) {
792
+ if (modifier.data.byteLength === COOKIE_SLICE_SIZE * COOKIE_SLICE_SIZE * 4) {
793
+ const digest = modifierDigest(modifier.data);
794
+ if (state.cookie.get(modifier.slice) !== digest) {
795
+ const result = internals.device.queue.writeTexture(
796
+ { texture: cookieTexture, mipLevel: 0, origin: { x: 0, y: 0, z: modifier.slice } },
797
+ modifier.data,
798
+ { offset: 0, bytesPerRow: COOKIE_SLICE_SIZE * 4, rowsPerImage: COOKIE_SLICE_SIZE },
799
+ { width: COOKIE_SLICE_SIZE, height: COOKIE_SLICE_SIZE, depthOrArrayLayers: 1 },
800
+ );
801
+ if (!result.ok) throw result.error;
802
+ state.cookie.set(modifier.slice, digest);
803
+ }
804
+ }
805
+ }
806
+ if (modifier.slice !== undefined && modifier.matrix !== undefined && cookieMatrixBuffer) {
807
+ const matrixBytes = new Uint8Array(
808
+ modifier.matrix.buffer,
809
+ modifier.matrix.byteOffset,
810
+ modifier.matrix.byteLength,
811
+ );
812
+ const digest = modifierDigest(matrixBytes);
813
+ if (state.cookieMatrix.get(modifier.slice) !== digest) {
814
+ const result = internals.device.queue.writeBuffer(
815
+ cookieMatrixBuffer,
816
+ modifier.slice * 16 * Float32Array.BYTES_PER_ELEMENT,
817
+ modifier.matrix,
818
+ );
819
+ if (!result.ok) throw result.error;
820
+ state.cookieMatrix.set(modifier.slice, digest);
821
+ }
822
+ }
823
+ }
824
+ }
825
+ }
826
+
627
827
  /**
628
828
  * feat-20260704 M3/w18: write the point-shadow params UBO (bound at viewBg
629
829
  * binding 6), extracted verbatim from `recordFrame`.
@@ -723,6 +923,7 @@ export function writeHdrpClusterAndSsaoBuffers(
723
923
  gpuMembershipPipelineReady = false,
724
924
  membershipBindGroupLayout: BindGroupLayout | null = null,
725
925
  projectorLightSlotIndex?: number,
926
+ pipelineState?: PipelineState,
726
927
  ): Result<void, RenderError> {
727
928
  const configuredClusterGrid = prepared.layout.grid;
728
929
  const gpuMembership = transport.producer === 'gpu';
@@ -778,6 +979,14 @@ export function writeHdrpClusterAndSsaoBuffers(
778
979
  };
779
980
  }
780
981
 
982
+ if (pipelineState !== undefined) {
983
+ const preparedSpots: ExtractedLights['spot'][number][] = [];
984
+ for (const local of prepared.local) {
985
+ if (local.source?.kind === 'spot') preparedSpots.push(local.source);
986
+ }
987
+ writeSpotModifierTextures(internals, pipelineState, preparedSpots);
988
+ }
989
+
781
990
  const gridX = prepared.layout.grid.x;
782
991
  const gridY = prepared.layout.grid.y;
783
992
  const gridZ = prepared.layout.grid.z;
@@ -795,30 +1004,17 @@ export function writeHdrpClusterAndSsaoBuffers(
795
1004
  profilePhase,
796
1005
  'record/scene-state/hdrp-cluster/payload-packing',
797
1006
  () => {
798
- const lightDataPayload = new Float32Array(prepared.layout.lightDataSlotCount * 16);
1007
+ const lightDataPayload = new Float32Array(
1008
+ prepared.layout.lightDataSlotCount * DIRECT_LIGHT_SLOT_FLOAT_COUNT,
1009
+ );
799
1010
  let slotIdx = 0;
800
1011
  for (const local of prepared.local) {
801
1012
  const source = local.source;
802
1013
  if (source === undefined) return null;
803
1014
  const projectorSelected =
804
- projectorLightSlotIndex !== undefined && projectorLightSlotIndex === slotIdx;
805
- const packed =
806
- source.kind === 'point' &&
807
- source.shadowAtlasLayer !== undefined &&
808
- source.shadowAtlasLayer >= 0 &&
809
- source.shadowNear !== undefined &&
810
- source.shadowFar !== undefined
811
- ? packLightSlot(
812
- source,
813
- {
814
- shadowAtlasLayer: source.shadowAtlasLayer,
815
- near: source.shadowNear,
816
- far: source.shadowFar,
817
- },
818
- projectorSelected,
819
- )
820
- : packLightSlot(source, undefined, projectorSelected);
821
- lightDataPayload.set(packed, slotIdx * 16);
1015
+ projectorLightSlotIndex === undefined ? undefined : projectorLightSlotIndex === slotIdx;
1016
+ const packed = packDirectLightSlot(source, projectorSelected);
1017
+ lightDataPayload.set(packed, slotIdx * DIRECT_LIGHT_SLOT_FLOAT_COUNT);
822
1018
  slotIdx += 1;
823
1019
  }
824
1020
 
@@ -843,7 +1039,10 @@ export function writeHdrpClusterAndSsaoBuffers(
843
1039
 
844
1040
  return {
845
1041
  hdrpBuffers,
846
- lightDataUploadPayload: lightDataPayload.subarray(0, slotIdx * 16),
1042
+ lightDataUploadPayload: lightDataPayload.subarray(
1043
+ 0,
1044
+ slotIdx * DIRECT_LIGHT_SLOT_FLOAT_COUNT,
1045
+ ),
847
1046
  clusterUniformPayload: new Uint8Array(clusterUniformPayload),
848
1047
  };
849
1048
  },
@@ -2,6 +2,7 @@
2
2
  // Extracted from render-system-record.ts (feat-20260704 M3/w17, pure move).
3
3
 
4
4
  import type { World } from '@forgeax/engine-ecs';
5
+ import type { RenderReadVersion } from '@forgeax/engine-ecs/projection';
5
6
  import { vec3 } from '@forgeax/engine-math';
6
7
  import type { ResolvedColorTargetDescriptor } from '@forgeax/engine-render-graph';
7
8
  import type {
@@ -81,6 +82,7 @@ import type {
81
82
  RenderableSnapshot,
82
83
  SpotLightSnapshot,
83
84
  } from '../render-system-extract';
85
+ import type { ProbeBlendRecord } from '../scene/probe-blend-record';
84
86
  import type { ShadowAtlas } from '../shadow-atlas';
85
87
  import type {
86
88
  TemporalFrame,
@@ -90,6 +92,18 @@ import type {
90
92
  import type { VolumetricFogInspection } from '../volume/inspection';
91
93
  import type { VolumeTemporalSignature } from '../volume/temporal';
92
94
 
95
+ /** Retained probe record bytes carried with one extracted render frame. */
96
+ export interface ProbeBlendFrameSnapshot {
97
+ readonly records: readonly ProbeBlendRecord[];
98
+ readonly recordByteLength: 160;
99
+ }
100
+
101
+ /** Renderer-owned GPU cache entry for one object's 160B probe record. */
102
+ export interface ProbeBlendBufferCacheEntry {
103
+ readonly generation: number;
104
+ readonly bytes: Uint8Array;
105
+ }
106
+
93
107
  /**
94
108
  * feat-20260608-create-app-param-surface-trim / M1 / D-8 (q8 user lock):
95
109
  * fallback clear color when the world carries no Camera entity. Opaque
@@ -154,14 +168,17 @@ export function validateGraphTargetCaptureReadback(input: {
154
168
  readonly bytes: Uint8Array;
155
169
  readonly expectedByteLength: number;
156
170
  readonly sentinel?: Uint8Array;
171
+ readonly allowZero?: boolean;
157
172
  }): GraphTargetCaptureReadbackValidation {
158
- const { bytes, expectedByteLength, sentinel } = input;
173
+ const { bytes, expectedByteLength, sentinel, allowZero = false } = input;
159
174
  if (bytes.byteLength !== expectedByteLength)
160
175
  return { ok: false, code: 'capture-readback-length' };
161
176
  if (sentinel?.every((value, index) => bytes[index] === value)) {
162
177
  return { ok: false, code: 'capture-readback-untouched-sentinel' };
163
178
  }
164
- if (bytes.every((value) => value === 0)) return { ok: false, code: 'capture-readback-empty' };
179
+ if (!allowZero && bytes.every((value) => value === 0)) {
180
+ return { ok: false, code: 'capture-readback-empty' };
181
+ }
165
182
  for (let index = 0; index + 1 < bytes.length; index += 2) {
166
183
  const exponent = ((bytes[index + 1] ?? 0) >>> 2) & 0x1f;
167
184
  if (exponent === 0x1f) return { ok: false, code: 'capture-readback-non-finite' };
@@ -191,6 +208,25 @@ export interface GraphTargetCaptureRequest {
191
208
  };
192
209
  }
193
210
 
211
+ /** Renderer-private readback request for the Standard fallback MRT. */
212
+ export interface ReflectionFallbackReadbackRequest {
213
+ readonly name: string;
214
+ readonly buffer: Buffer;
215
+ readonly bytesPerRow: number;
216
+ readonly width: number;
217
+ readonly height: number;
218
+ readonly expected: {
219
+ readonly format: ResolvedColorTargetDescriptor['format'];
220
+ readonly width: number;
221
+ readonly height: number;
222
+ readonly usage: number;
223
+ readonly graphGeneration: number;
224
+ readonly frameId: number;
225
+ readonly textureIdentity: number;
226
+ };
227
+ encoded: boolean;
228
+ }
229
+
194
230
  /**
195
231
  * Build a synthetic CameraSnapshot the record stage uses when the world
196
232
  * carries no Camera entity. Identity-shaped projection / view inputs
@@ -253,12 +289,14 @@ export function makeZeroCameraFallbackSnapshot(): CameraSnapshot {
253
289
  */
254
290
  export interface RenderFrameState {
255
291
  frameNumber: number;
292
+ /** Demand for the frame-local Standard fallback MRT. */
293
+ reflectionFallbackDemand?: boolean;
256
294
  /** Monotonic compiled-graph identity used by diagnostic readbacks. */
257
295
  graphGeneration: number;
258
296
  /** Number of successfully submitted TAA frames; failed attempts do not advance it. */
259
297
  successfulTemporalFrameIndex?: number;
260
298
  /** The renderer-owned accepted-submit temporal fact consumed by consumers. */
261
- temporalFrame?: TemporalFrame;
299
+ temporalFrame?: TemporalFrame | undefined;
262
300
  /** The sole transaction that advances temporal history after queue.submit. */
263
301
  readonly temporalFrameTransaction: TemporalFrameTransaction;
264
302
  /** Staged input is consumed only by the graph submit owner. */
@@ -271,6 +309,8 @@ export interface RenderFrameState {
271
309
  readonly graphGeneration: number;
272
310
  } | null;
273
311
  graphTargetCapture?: GraphTargetCaptureRequest | undefined;
312
+ /** One-frame producer-owned copy target for fallback MRT readback. */
313
+ reflectionFallbackReadback?: ReflectionFallbackReadbackRequest | undefined;
274
314
  /** Last successfully rendered static directional shadow atlas token. */
275
315
  directionalShadowCache: DirectionalShadowCache | null;
276
316
  /** Last successfully submitted camera post-process mode. */
@@ -314,6 +354,10 @@ export interface RenderFrameState {
314
354
  readonly retiredCompiledFrameGraphs: Set<CompiledRenderGraph<RenderPipelineFrame>>;
315
355
  /** Physical outputs projected by typed graph passes for downstream observers. */
316
356
  currentFrameObservationSource: FrameObservationSource | undefined;
357
+ /** Completed-frame source for the producer-owned fallback MRT. */
358
+ reflectionFallbackObservationSource?: FrameObservationSource | undefined;
359
+ /** Completion fence for the producer-owned fallback row/readback promotion. */
360
+ reflectionFallbackCompletion?: Promise<void> | undefined;
317
361
  /** Camera antialias mode for the last successfully submitted graph. */
318
362
  lastSuccessfulCameraAntialias: CameraSnapshot['antialias'] | undefined;
319
363
  /** Staged TAA GPU state is promoted only after queue.submit succeeds. */
@@ -331,6 +375,11 @@ export interface RenderFrameState {
331
375
  currentDirectionalShadowView: TextureView | null;
332
376
  currentSpotShadowView: TextureView | null;
333
377
  readonly instanceBuffers: Map<number, InstanceBufferCacheEntry>;
378
+ /** Per-object ProbeBlendRecord buffers; upload is one atomic 160B write. */
379
+ readonly probeBlendBuffers: Map<number, ProbeBlendBufferCacheEntry>;
380
+ /** One renderer-owned record array; each object selects its 256B-aligned lane. */
381
+ probeBlendRecordBuffer?: GpuBuffer;
382
+ probeBlendRecordBufferCapacity: number;
334
383
  /** Per-entity vertex buffers for the Standard CPU morph lane. */
335
384
  readonly morphBuffers?: Map<number, MorphBufferCacheEntry>;
336
385
  readonly hdrpClusterBinScratch: ClusterBinScratch;
@@ -358,15 +407,11 @@ export interface RenderFrameState {
358
407
  transientInstanceBuffers: InstanceBufferCacheEntry[];
359
408
  warnedZeroLightStandard: boolean;
360
409
  /**
361
- * feax-20260608-multi-light-warn-once M3: once-warn latch for multi-light
362
- * overrun per bucket (directional N>1 / point N>4 / spot N>4). Fires at
363
- * most once per RenderSystem lifetime per bucket so AI users see a single
364
- * actionable console.warn without per-frame flooding (charter P3 explicit
365
- * failure: warn-once preserves signal/noise floor).
410
+ * feat-20260608-multi-light-warn-once M3: once-warn latch for the
411
+ * single-directional authoring constraint. Standard local lights use the
412
+ * unified Cluster budget; there is no per-kind local-light cap.
366
413
  */
367
414
  warnedMultiLightDirectional: boolean;
368
- warnedMultiLightPoint: boolean;
369
- warnedMultiLightSpot: boolean;
370
415
  /**
371
416
  * feat-20260630-equirect-kind-internalized-ibl-declarative-skyligh M3 / w19:
372
417
  * once-warn latches for >1 Skylight / >1 SkyboxBackground entity. Fire at most
@@ -662,11 +707,11 @@ export interface DirectionalShadowCache {
662
707
  * ECS state that can change the contents of a directional shadow map.
663
708
  *
664
709
  * The ECS-owned RenderReadLease is the only render-facing mutation boundary.
665
- * Its cursor is intentionally conservative: any published World change
710
+ * Its version is intentionally conservative: any published World change
666
711
  * invalidates the cached atlas, while render never reaches through World for
667
712
  * component-clock storage.
668
713
  */
669
714
  export interface DirectionalShadowWorldState {
670
715
  readonly worldIdentity: string;
671
- readonly changeCursor: number;
716
+ readonly version: RenderReadVersion;
672
717
  }