@forgeax/engine-render 0.1.21 → 0.1.24

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 (584) hide show
  1. package/README.md +281 -38
  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/bundler-contract.d.ts +9 -0
  5. package/dist/assembly/bundler-contract.d.ts.map +1 -0
  6. package/dist/assembly/device-feature-admission.d.ts +8 -0
  7. package/dist/assembly/device-feature-admission.d.ts.map +1 -1
  8. package/dist/assembly/extended-lighting-fallback.d.ts +13 -0
  9. package/dist/assembly/extended-lighting-fallback.d.ts.map +1 -0
  10. package/dist/assembly/factory.d.ts +7 -235
  11. package/dist/assembly/factory.d.ts.map +1 -1
  12. package/dist/assembly/host-contract.d.ts +10 -0
  13. package/dist/assembly/host-contract.d.ts.map +1 -1
  14. package/dist/assembly/material/assembly.d.ts +3 -2
  15. package/dist/assembly/material/assembly.d.ts.map +1 -1
  16. package/dist/assembly/material/standard-layer-projection.d.ts +10 -0
  17. package/dist/assembly/material/standard-layer-projection.d.ts.map +1 -0
  18. package/dist/assembly/material-shader-policy.d.ts +168 -0
  19. package/dist/assembly/material-shader-policy.d.ts.map +1 -0
  20. package/dist/assembly/material-shader-prewarm.d.ts +21 -0
  21. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -0
  22. package/dist/assembly/mesh-ssbo-grow.d.ts +88 -0
  23. package/dist/assembly/mesh-ssbo-grow.d.ts.map +1 -0
  24. package/dist/assembly/recovery/device-loss-fanout.d.ts +14 -0
  25. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -0
  26. package/dist/assembly/recovery/generation.d.ts +46 -0
  27. package/dist/assembly/recovery/generation.d.ts.map +1 -0
  28. package/dist/assembly/recovery/recovery-attempt.d.ts +63 -0
  29. package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -0
  30. package/dist/assembly/recovery/renderer-recover.d.ts +115 -0
  31. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -0
  32. package/dist/assembly/renderer-facade.d.ts +10 -0
  33. package/dist/assembly/renderer-facade.d.ts.map +1 -0
  34. package/dist/assembly/renderer-frame-transaction.d.ts +17 -0
  35. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  36. package/dist/assembly/renderer-helpers.d.ts +19 -0
  37. package/dist/assembly/renderer-helpers.d.ts.map +1 -0
  38. package/dist/assembly/renderer-lifecycle.d.ts +34 -0
  39. package/dist/assembly/renderer-lifecycle.d.ts.map +1 -1
  40. package/dist/assembly/shader-prewarm-policy.d.ts +15 -0
  41. package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -0
  42. package/dist/assembly/webgpu-ready-contract.d.ts +9 -0
  43. package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -0
  44. package/dist/assembly/webgpu-ready.d.ts +55 -0
  45. package/dist/assembly/webgpu-ready.d.ts.map +1 -0
  46. package/dist/assembly/webgpu-renderer.d.ts +152 -0
  47. package/dist/assembly/webgpu-renderer.d.ts.map +1 -0
  48. package/dist/authoring.mjs +1 -1
  49. package/dist/{chunk-PCHBPYML.mjs → chunk-GE3SDD72.mjs} +37 -5
  50. package/dist/chunk-GE3SDD72.mjs.map +1 -0
  51. package/dist/chunk-KZFTK6DO.mjs +1191 -0
  52. package/dist/chunk-KZFTK6DO.mjs.map +1 -0
  53. package/dist/{chunk-FS2KNUQH.mjs → chunk-Q752SHI5.mjs} +332 -568
  54. package/dist/chunk-Q752SHI5.mjs.map +1 -0
  55. package/dist/{chunk-6OPXDQ2E.mjs → chunk-RR5L4SRS.mjs} +33 -294
  56. package/dist/chunk-RR5L4SRS.mjs.map +1 -0
  57. package/dist/chunk-TFH5I7JJ.mjs +296 -0
  58. package/dist/chunk-TFH5I7JJ.mjs.map +1 -0
  59. package/dist/chunk-UQYS7FRM.mjs +1003 -0
  60. package/dist/chunk-UQYS7FRM.mjs.map +1 -0
  61. package/dist/chunk-YKB5DQV7.mjs +345 -0
  62. package/dist/chunk-YKB5DQV7.mjs.map +1 -0
  63. package/dist/{chunk-5YADUCA7.mjs → chunk-YRUYGA4A.mjs} +1189 -1217
  64. package/dist/chunk-YRUYGA4A.mjs.map +1 -0
  65. package/dist/chunk-Z3YYH2MB.mjs +5082 -0
  66. package/dist/chunk-Z3YYH2MB.mjs.map +1 -0
  67. package/dist/cluster-binner.d.ts +5 -0
  68. package/dist/cluster-binner.d.ts.map +1 -1
  69. package/dist/components/index.d.ts +3 -0
  70. package/dist/components/index.d.ts.map +1 -1
  71. package/dist/components/light-helpers.d.ts +4 -0
  72. package/dist/components/light-helpers.d.ts.map +1 -1
  73. package/dist/components/light-probe.d.ts +9 -0
  74. package/dist/components/light-probe.d.ts.map +1 -0
  75. package/dist/components/rect-area-light.d.ts +12 -0
  76. package/dist/components/rect-area-light.d.ts.map +1 -0
  77. package/dist/components/scene-instance.d.ts +4 -0
  78. package/dist/components/scene-instance.d.ts.map +1 -1
  79. package/dist/components/spot-light.d.ts +3 -0
  80. package/dist/components/spot-light.d.ts.map +1 -1
  81. package/dist/construct-renderer.mjs +27276 -23232
  82. package/dist/construct-renderer.mjs.map +1 -1
  83. package/dist/device/device-scope.d.ts +2 -0
  84. package/dist/device/device-scope.d.ts.map +1 -1
  85. package/dist/device/gpu-residency.d.ts +32 -5
  86. package/dist/device/gpu-residency.d.ts.map +1 -1
  87. package/dist/device/lifecycle-transaction.d.ts +2 -0
  88. package/dist/device/lifecycle-transaction.d.ts.map +1 -0
  89. package/dist/environment/frame.d.ts +5 -0
  90. package/dist/environment/frame.d.ts.map +1 -1
  91. package/dist/environment/inspection.d.ts +7 -0
  92. package/dist/environment/inspection.d.ts.map +1 -1
  93. package/dist/environment/lifecycle.d.ts +20 -2
  94. package/dist/environment/lifecycle.d.ts.map +1 -1
  95. package/dist/errors/gpu-driven.d.ts +26 -0
  96. package/dist/errors/gpu-driven.d.ts.map +1 -0
  97. package/dist/errors/index.d.ts +1 -0
  98. package/dist/errors/index.d.ts.map +1 -1
  99. package/dist/errors/recover.d.ts +67 -3
  100. package/dist/errors/recover.d.ts.map +1 -1
  101. package/dist/errors/render.d.ts +39 -1
  102. package/dist/errors/render.d.ts.map +1 -1
  103. package/dist/extract/contracts.d.ts +899 -0
  104. package/dist/extract/contracts.d.ts.map +1 -0
  105. package/dist/extract/gpu-driven.d.ts +50 -0
  106. package/dist/extract/gpu-driven.d.ts.map +1 -0
  107. package/dist/extract/visibility.d.ts.map +1 -1
  108. package/dist/features/host.d.ts +2 -0
  109. package/dist/features/host.d.ts.map +1 -1
  110. package/dist/features/prepared-gpu-work.d.ts +3 -0
  111. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  112. package/dist/features/prepared-graphics-store.d.ts +2 -0
  113. package/dist/features/prepared-graphics-store.d.ts.map +1 -1
  114. package/dist/gpu-driven/batch-topology.d.ts +34 -0
  115. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  116. package/dist/gpu-driven/prepared-draw.d.ts +63 -0
  117. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -0
  118. package/dist/gpu-driven/production-raster.d.ts +126 -2
  119. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  120. package/dist/gpu-driven/view-gpu.d.ts +64 -1
  121. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  122. package/dist/gpu-scene-schema.d.ts +14 -0
  123. package/dist/gpu-scene-schema.d.ts.map +1 -1
  124. package/dist/gpu-scene.d.ts +9 -1
  125. package/dist/gpu-scene.d.ts.map +1 -1
  126. package/dist/gpu-usage.d.ts +0 -1
  127. package/dist/gpu-usage.d.ts.map +1 -1
  128. package/dist/hdrp-buffers.d.ts +3 -3
  129. package/dist/hdrp-buffers.d.ts.map +1 -1
  130. package/dist/ibl/IblPipelineCache.d.ts +7 -6
  131. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  132. package/dist/ibl/skylight-bind-group.d.ts +7 -1
  133. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  134. package/dist/index.d.ts +13 -2
  135. package/dist/index.d.ts.map +1 -1
  136. package/dist/index.mjs +90 -5
  137. package/dist/index.mjs.map +1 -1
  138. package/dist/inspection-types.d.ts +163 -1
  139. package/dist/inspection-types.d.ts.map +1 -1
  140. package/dist/internal.d.ts +9 -0
  141. package/dist/internal.d.ts.map +1 -1
  142. package/dist/internal.mjs +23 -2
  143. package/dist/internal.mjs.map +1 -1
  144. package/dist/light-buffer-layout.d.ts +60 -148
  145. package/dist/light-buffer-layout.d.ts.map +1 -1
  146. package/dist/materials.d.ts +34 -10
  147. package/dist/materials.d.ts.map +1 -1
  148. package/dist/mesh-material-bindings.d.ts +56 -0
  149. package/dist/mesh-material-bindings.d.ts.map +1 -1
  150. package/dist/occlusion-proxy.d.ts +4 -0
  151. package/dist/occlusion-proxy.d.ts.map +1 -0
  152. package/dist/pbr-pipeline.d.ts +58 -21
  153. package/dist/pbr-pipeline.d.ts.map +1 -1
  154. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  155. package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -1
  156. package/dist/pipeline/standard-lighting/light-frame.d.ts +3 -3
  157. package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -1
  158. package/dist/pipeline/standard-lighting/topology.d.ts +1 -1
  159. package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -1
  160. package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -1
  161. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  162. package/dist/pipeline-builder.d.ts +2 -0
  163. package/dist/pipeline-builder.d.ts.map +1 -1
  164. package/dist/pipeline-spec.d.ts +1 -1
  165. package/dist/pipeline-spec.d.ts.map +1 -1
  166. package/dist/plugin.d.ts.map +1 -1
  167. package/dist/points-lines/prepare.d.ts +3 -0
  168. package/dist/points-lines/prepare.d.ts.map +1 -1
  169. package/dist/points-lines/standard-owner.d.ts +33 -0
  170. package/dist/points-lines/standard-owner.d.ts.map +1 -0
  171. package/dist/prepare/extended-lighting/ltc-resources.d.ts +31 -0
  172. package/dist/prepare/extended-lighting/ltc-resources.d.ts.map +1 -0
  173. package/dist/prepare/extended-lighting/resources.d.ts +72 -0
  174. package/dist/prepare/extended-lighting/resources.d.ts.map +1 -0
  175. package/dist/prepare/extended-lighting/spot-modifiers.d.ts +42 -0
  176. package/dist/prepare/extended-lighting/spot-modifiers.d.ts.map +1 -0
  177. package/dist/prepare/extended-lighting/state.d.ts +24 -0
  178. package/dist/prepare/extended-lighting/state.d.ts.map +1 -0
  179. package/dist/record/frame-lighting.d.ts +8 -6
  180. package/dist/record/frame-lighting.d.ts.map +1 -1
  181. package/dist/record/frame-snapshot.d.ts +55 -10
  182. package/dist/record/frame-snapshot.d.ts.map +1 -1
  183. package/dist/record/frame-targets.d.ts.map +1 -1
  184. package/dist/record/frame.d.ts +21 -5
  185. package/dist/record/frame.d.ts.map +1 -1
  186. package/dist/record/gpu-timing.d.ts +19 -7
  187. package/dist/record/gpu-timing.d.ts.map +1 -1
  188. package/dist/record/helpers.d.ts +0 -20
  189. package/dist/record/helpers.d.ts.map +1 -1
  190. package/dist/record/main-pass-geometry.d.ts +20 -2
  191. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  192. package/dist/record/main-pass-material.d.ts +10 -18
  193. package/dist/record/main-pass-material.d.ts.map +1 -1
  194. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  195. package/dist/record/main-pass.d.ts.map +1 -1
  196. package/dist/record/mesh-ssbo.d.ts +11 -3
  197. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  198. package/dist/record/recovery-pipeline.d.ts +30 -0
  199. package/dist/record/recovery-pipeline.d.ts.map +1 -0
  200. package/dist/record/render-context.d.ts +82 -9
  201. package/dist/record/render-context.d.ts.map +1 -1
  202. package/dist/record/shadow-pass.d.ts.map +1 -1
  203. package/dist/record/typed-frame-graph.d.ts +49 -2
  204. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  205. package/dist/record/view-ubo.d.ts +1 -2
  206. package/dist/record/view-ubo.d.ts.map +1 -1
  207. package/dist/recovery/render-system-candidate.d.ts +65 -0
  208. package/dist/recovery/render-system-candidate.d.ts.map +1 -0
  209. package/dist/reflection/inspection.d.ts +10 -0
  210. package/dist/reflection/inspection.d.ts.map +1 -0
  211. package/dist/reflection/projection.d.ts +49 -0
  212. package/dist/reflection/projection.d.ts.map +1 -1
  213. package/dist/reflection/record-owner.d.ts +60 -0
  214. package/dist/reflection/record-owner.d.ts.map +1 -0
  215. package/dist/render-contract.d.ts +78 -15
  216. package/dist/render-contract.d.ts.map +1 -1
  217. package/dist/render-pipeline.d.ts +7 -0
  218. package/dist/render-pipeline.d.ts.map +1 -1
  219. package/dist/render-system-extract-tail.d.ts +12 -0
  220. package/dist/render-system-extract-tail.d.ts.map +1 -0
  221. package/dist/render-system-extract.d.ts +252 -30
  222. package/dist/render-system-extract.d.ts.map +1 -1
  223. package/dist/render-system.d.ts +140 -5
  224. package/dist/render-system.d.ts.map +1 -1
  225. package/dist/scene/probe-blend-record.d.ts +22 -0
  226. package/dist/scene/probe-blend-record.d.ts.map +1 -0
  227. package/dist/scene/probe-blend.d.ts +142 -0
  228. package/dist/scene/probe-blend.d.ts.map +1 -0
  229. package/dist/scene/render-scene-types.d.ts +0 -6
  230. package/dist/scene/render-scene-types.d.ts.map +1 -1
  231. package/dist/scene/render-scene.d.ts +122 -9
  232. package/dist/scene/render-scene.d.ts.map +1 -1
  233. package/dist/scene/visibility/bounds.d.ts +20 -0
  234. package/dist/scene/visibility/bounds.d.ts.map +1 -0
  235. package/dist/scene/visibility/budget.d.ts +16 -0
  236. package/dist/scene/visibility/budget.d.ts.map +1 -0
  237. package/dist/scene/visibility/facet.d.ts +73 -0
  238. package/dist/scene/visibility/facet.d.ts.map +1 -0
  239. package/dist/scene/visibility/gpu-frame-timing.d.ts +41 -0
  240. package/dist/scene/visibility/gpu-frame-timing.d.ts.map +1 -0
  241. package/dist/scene/visibility/gpu-lod.d.ts +46 -0
  242. package/dist/scene/visibility/gpu-lod.d.ts.map +1 -0
  243. package/dist/scene/visibility/inspection.d.ts +132 -0
  244. package/dist/scene/visibility/inspection.d.ts.map +1 -0
  245. package/dist/scene/visibility/lod-selector.d.ts +25 -0
  246. package/dist/scene/visibility/lod-selector.d.ts.map +1 -0
  247. package/dist/scene/visibility/occlusion-confidence.d.ts +81 -0
  248. package/dist/scene/visibility/occlusion-confidence.d.ts.map +1 -0
  249. package/dist/scene/visibility/occlusion-pass.d.ts +57 -0
  250. package/dist/scene/visibility/occlusion-pass.d.ts.map +1 -0
  251. package/dist/scene/visibility/occlusion-query-pool.d.ts +57 -0
  252. package/dist/scene/visibility/occlusion-query-pool.d.ts.map +1 -0
  253. package/dist/scene/visibility/occlusion-runtime.d.ts +96 -0
  254. package/dist/scene/visibility/occlusion-runtime.d.ts.map +1 -0
  255. package/dist/scene/visibility/types.d.ts +34 -0
  256. package/dist/scene/visibility/types.d.ts.map +1 -0
  257. package/dist/ssr/admission.d.ts +122 -0
  258. package/dist/ssr/admission.d.ts.map +1 -0
  259. package/dist/ssr/composition.d.ts +32 -0
  260. package/dist/ssr/composition.d.ts.map +1 -0
  261. package/dist/ssr/errors.d.ts +39 -0
  262. package/dist/ssr/errors.d.ts.map +1 -0
  263. package/dist/ssr/identity.d.ts +13 -0
  264. package/dist/ssr/identity.d.ts.map +1 -0
  265. package/dist/systems/skin-palette-allocator.d.ts +1 -1
  266. package/dist/temporal/frame-coordinator.d.ts +13 -0
  267. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  268. package/dist/temporal/frame.d.ts +2 -0
  269. package/dist/temporal/frame.d.ts.map +1 -1
  270. package/dist/temporal/index.mjs +6 -5
  271. package/dist/temporal/taa-history-store.d.ts +15 -1
  272. package/dist/temporal/taa-history-store.d.ts.map +1 -1
  273. package/dist/temporal/transaction.d.ts +5 -0
  274. package/dist/temporal/transaction.d.ts.map +1 -1
  275. package/dist/temporal/view.d.ts +7 -0
  276. package/dist/temporal/view.d.ts.map +1 -1
  277. package/dist/tilemap-chunk-extract-system.d.ts +1 -1
  278. package/dist/typed-render-graph-primitives.d.ts +4 -0
  279. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  280. package/dist/volume/passes.d.ts +8 -4
  281. package/dist/volume/passes.d.ts.map +1 -1
  282. package/package.json +20 -20
  283. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +5 -0
  284. package/src/__tests__/detached-inspection.integration.test.ts +6 -0
  285. package/src/__tests__/device-feature-admission.unit.test.ts +19 -1
  286. package/src/__tests__/device-loss-fanout.unit.test.ts +107 -0
  287. package/src/__tests__/device-scope-lifecycle.unit.test.ts +21 -0
  288. package/src/__tests__/direct-light-snapshot.unit.test.ts +59 -3
  289. package/src/__tests__/environment-generation.integration.test.ts +39 -0
  290. package/src/__tests__/evidence-identity.ts +13 -0
  291. package/src/__tests__/evidence-identity.unit.test.ts +24 -0
  292. package/src/__tests__/expose-renderer-errors.unit.test.ts +34 -0
  293. package/src/__tests__/extended-lighting-recovery.browser.test.ts +14 -0
  294. package/src/__tests__/extended-lighting-recovery.dawn.test.ts +14 -0
  295. package/src/__tests__/extended-lighting-recovery.integration.test.ts +81 -0
  296. package/src/__tests__/extended-lighting-resources.unit.test.ts +51 -0
  297. package/src/__tests__/extended-lighting-zero-feature.integration.test.ts +52 -0
  298. package/src/__tests__/factory-contract.integration.test.ts +154 -6
  299. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +4 -1
  300. package/src/__tests__/frame-observation.contract.test.ts +15 -0
  301. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +146 -0
  302. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +1 -0
  303. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +1 -0
  304. package/src/__tests__/gpu-driven-production.integration.test.ts +572 -9
  305. package/src/__tests__/gpu-driven-scaling.unit.test.ts +66 -2
  306. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +26 -1
  307. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +89 -2
  308. package/src/__tests__/gpu-driven-view.browser.test.ts +3 -3
  309. package/src/__tests__/gpu-driven-view.dawn.test.ts +3 -3
  310. package/src/__tests__/gpu-driven-view.unit.test.ts +50 -0
  311. package/src/__tests__/gpu-lod-evidence.ts +188 -0
  312. package/src/__tests__/gpu-lod-fallback.integration.test.ts +10 -0
  313. package/src/__tests__/gpu-lod-indirect.integration.test.ts +40 -0
  314. package/src/__tests__/gpu-lod-parity.dawn.test.ts +29 -0
  315. package/src/__tests__/gpu-lod-readiness.integration.test.ts +52 -0
  316. package/src/__tests__/gpu-lod-schema.unit.test.ts +66 -0
  317. package/src/__tests__/gpu-lod-selector.unit.test.ts +28 -0
  318. package/src/__tests__/gpu-lod-silhouette.browser.test.ts +13 -0
  319. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +66 -1
  320. package/src/__tests__/gpu-scene.dawn.test.ts +8 -1
  321. package/src/__tests__/gpu-scene.unit.test.ts +49 -3
  322. package/src/__tests__/ibl/face-uniforms.unit.test.ts +2 -0
  323. package/src/__tests__/ibl-residency-publication.unit.test.ts +113 -0
  324. package/src/__tests__/light-buffer-layout.unit.test.ts +113 -3
  325. package/src/__tests__/light-inspection.unit.test.ts +55 -0
  326. package/src/__tests__/light-resource-error.unit.test.ts +43 -0
  327. package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +109 -0
  328. package/src/__tests__/lod-bounds.unit.test.ts +42 -0
  329. package/src/__tests__/lod-cpu-lane.integration.test.ts +55 -0
  330. package/src/__tests__/lod-cpu-no-change.perf.test.ts +19 -0
  331. package/src/__tests__/lod-gpu-frame-timing.unit.test.ts +49 -0
  332. package/src/__tests__/lod-multi-view.integration.test.ts +63 -0
  333. package/src/__tests__/lod-occlusion-device-loss.integration.test.ts +27 -0
  334. package/src/__tests__/lod-occlusion-final-decision.integration.test.ts +47 -0
  335. package/src/__tests__/lod-occlusion-inspection.integration.test.ts +217 -0
  336. package/src/__tests__/lod-occlusion-inspection.schema.test-d.ts +14 -0
  337. package/src/__tests__/lod-occlusion-inspection.unit.test.ts +72 -0
  338. package/src/__tests__/lod-occlusion-visual-evidence.ts +91 -0
  339. package/src/__tests__/lod-occlusion.browser.test.ts +15 -0
  340. package/src/__tests__/lod-occlusion.dawn.test.ts +20 -0
  341. package/src/__tests__/lod-selector.profile.test.ts +35 -0
  342. package/src/__tests__/lod-selector.unit.test.ts +68 -0
  343. package/src/__tests__/lod-visibility-identity.unit.test.ts +198 -0
  344. package/src/__tests__/lod-visibility-lifecycle.integration.test.ts +132 -0
  345. package/src/__tests__/ltc-resources.unit.test.ts +32 -0
  346. package/src/__tests__/material-contract-inventory.unit.test.ts +41 -9
  347. package/src/__tests__/material-cooked-projection.unit.test.ts +19 -1
  348. package/src/__tests__/material-static-dynamic-route.unit.test.ts +29 -0
  349. package/src/__tests__/material-stride-surface.unit.test.ts +7 -3
  350. package/src/__tests__/materials-standard-contract.unit.test.ts +104 -0
  351. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
  352. package/src/__tests__/occlusion-confidence.unit.test.ts +168 -0
  353. package/src/__tests__/occlusion-invalidation.integration.test.ts +37 -0
  354. package/src/__tests__/occlusion-pass-routing.unit.test.ts +16 -0
  355. package/src/__tests__/occlusion-pass.dawn.test.ts +20 -0
  356. package/src/__tests__/occlusion-pass.integration.test.ts +43 -0
  357. package/src/__tests__/occlusion-pass.rhi-null.test.ts +22 -0
  358. package/src/__tests__/occlusion-query-pool.rhi-null.test.ts +21 -0
  359. package/src/__tests__/occlusion-query-pool.unit.test.ts +95 -0
  360. package/src/__tests__/occlusion-query-ticket.integration.test.ts +47 -0
  361. package/src/__tests__/occlusion-query.dawn.test.ts +16 -0
  362. package/src/__tests__/occlusion-query.evidence.ts +178 -0
  363. package/src/__tests__/occlusion-query.rhi-null.test.ts +31 -0
  364. package/src/__tests__/occlusion-runtime.unit.test.ts +120 -0
  365. package/src/__tests__/physical-clearcoat.integration.test.ts +89 -0
  366. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +196 -0
  367. package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +1 -1
  368. package/src/__tests__/probe-bindings.unit.test.ts +200 -0
  369. package/src/__tests__/probe-blend-numeric.unit.test.ts +67 -0
  370. package/src/__tests__/probe-blend-oracle.unit.test.ts +136 -0
  371. package/src/__tests__/probe-capacity.integration.test.ts +97 -0
  372. package/src/__tests__/probe-recovery.integration.test.ts +104 -0
  373. package/src/__tests__/probe-scene-dirty.integration.test.ts +211 -0
  374. package/src/__tests__/profiler-phase-catalog.test.ts +4 -1
  375. package/src/__tests__/profiler-phase-token.test.ts +1 -0
  376. package/src/__tests__/recover-error-policy-owner.unit.test.ts +6 -3
  377. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +140 -0
  378. package/src/__tests__/rect-area-boundary.unit.test.ts +24 -0
  379. package/src/__tests__/reflection-fallback-graph.unit.test.ts +67 -0
  380. package/src/__tests__/reflection-fallback-inspection.unit.test.ts +77 -0
  381. package/src/__tests__/reflection-fallback-lifecycle.integration.test.ts +21 -0
  382. package/src/__tests__/reflection-fallback-projection.unit.test.ts +90 -0
  383. package/src/__tests__/reflection-fallback-source.unit.test.ts +32 -0
  384. package/src/__tests__/reflection-probe-owner-placement.unit.test.ts +39 -0
  385. package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +120 -0
  386. package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
  387. package/src/__tests__/render-feature-docs.unit.test.ts +12 -7
  388. package/src/__tests__/render-graph-fallback.integration.test.ts +24 -0
  389. package/src/__tests__/render-perf/extended-lighting.bench.test.ts +66 -0
  390. package/src/__tests__/render-scene-projection.unit.test.ts +447 -18
  391. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +160 -17
  392. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +47 -0
  393. package/src/__tests__/renderer-generation-assembly.unit.test.ts +77 -0
  394. package/src/__tests__/renderer-generation-failure.unit.test.ts +108 -0
  395. package/src/__tests__/renderer-producer-root-matrix.integration.test.ts +33 -0
  396. package/src/__tests__/renderer-recovery-capability-matrix.unit.test.ts +59 -0
  397. package/src/__tests__/renderer-recovery-contract.unit.test.ts +73 -0
  398. package/src/__tests__/renderer-recovery-lifecycle.unit.test.ts +216 -0
  399. package/src/__tests__/renderer-target-history-recovery.integration.test.ts +80 -0
  400. package/src/__tests__/shader-manifest-fixture.ts +13 -16
  401. package/src/__tests__/shader-stage-surface.unit.test.ts +1 -1
  402. package/src/__tests__/skin-motion-regression.unit.test.ts +22 -0
  403. package/src/__tests__/skinned-shadow-caster.test.ts +2 -2
  404. package/src/__tests__/spot-modifier-projection.unit.test.ts +70 -0
  405. package/src/__tests__/ssr-admission-matrix.integration.test.ts +206 -0
  406. package/src/__tests__/ssr-composition.unit.test.ts +46 -0
  407. package/src/__tests__/ssr-fallback-zero-work.bench.test.ts +94 -0
  408. package/src/__tests__/ssr-recovery-errors.unit.test.ts +58 -0
  409. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +67 -0
  410. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +7 -0
  411. package/src/__tests__/standard-layer-projection.unit.test.ts +23 -0
  412. package/src/__tests__/standard-lighting-inspection.integration.test.ts +1 -1
  413. package/src/__tests__/standard-lighting-write.unit.test.ts +66 -2
  414. package/src/__tests__/temporal-fallback-generation.integration.test.ts +16 -0
  415. package/src/__tests__/temporal-view.unit.test.ts +3 -3
  416. package/src/__tests__/transmission-prewarm.unit.test.ts +111 -32
  417. package/src/__tests__/visibility-budget.integration.test.ts +140 -0
  418. package/src/__tests__/visibility-budget.unit.test.ts +70 -0
  419. package/src/__tests__/visibility-extract.integration.test.ts +11 -6
  420. package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +6 -3
  421. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +31 -28
  422. package/src/__tests__/volumetric-fog-topology.unit.test.ts +3 -1
  423. package/src/assembly/backend-contract.ts +13 -0
  424. package/src/assembly/bundler-contract.ts +9 -0
  425. package/src/assembly/device-feature-admission.ts +26 -9
  426. package/src/assembly/extended-lighting-fallback.ts +216 -0
  427. package/src/assembly/factory.ts +81 -9634
  428. package/src/assembly/host-contract.ts +10 -0
  429. package/src/assembly/material/assembly.ts +18 -2
  430. package/src/assembly/material/standard-layer-projection.ts +16 -0
  431. package/src/assembly/material-shader-policy.ts +854 -0
  432. package/src/assembly/material-shader-prewarm.ts +88 -0
  433. package/src/assembly/mesh-ssbo-grow.ts +270 -0
  434. package/src/assembly/recovery/device-loss-fanout.ts +89 -0
  435. package/src/assembly/recovery/generation.ts +141 -0
  436. package/src/assembly/recovery/recovery-attempt.ts +800 -0
  437. package/src/assembly/recovery/renderer-recover.ts +906 -0
  438. package/src/assembly/renderer-facade.ts +342 -0
  439. package/src/assembly/renderer-frame-transaction.ts +45 -0
  440. package/src/assembly/renderer-helpers.ts +99 -0
  441. package/src/assembly/renderer-lifecycle.ts +159 -0
  442. package/src/assembly/shader-prewarm-policy.ts +157 -0
  443. package/src/assembly/webgpu-ready-contract.ts +14 -0
  444. package/src/assembly/webgpu-ready.ts +4079 -0
  445. package/src/assembly/webgpu-renderer.ts +4095 -0
  446. package/src/cluster-binner.ts +31 -0
  447. package/src/components/__tests__/lighting-authoring.unit.test.ts +39 -0
  448. package/src/components/__tests__/rect-area-light.unit.test.ts +33 -0
  449. package/src/components/camera.ts +1 -1
  450. package/src/components/index.ts +3 -0
  451. package/src/components/light-helpers.ts +52 -1
  452. package/src/components/light-probe.ts +13 -0
  453. package/src/components/rect-area-light.ts +15 -0
  454. package/src/components/scene-instance.ts +6 -0
  455. package/src/components/spot-light.ts +3 -0
  456. package/src/debug-draw-glue.ts +1 -1
  457. package/src/device/__tests__/extended-lighting-recovery.unit.test.ts +24 -0
  458. package/src/device/device-scope.ts +35 -6
  459. package/src/device/gpu-residency.ts +334 -35
  460. package/src/device/lifecycle-transaction.ts +12 -0
  461. package/src/environment/frame.ts +6 -0
  462. package/src/environment/inspection.ts +10 -0
  463. package/src/environment/lifecycle.ts +122 -9
  464. package/src/errors/gpu-driven.ts +92 -0
  465. package/src/errors/index.ts +1 -0
  466. package/src/errors/recover.ts +108 -5
  467. package/src/errors/render.ts +72 -1
  468. package/src/extract/camera.ts +3 -3
  469. package/src/extract/contracts.ts +974 -0
  470. package/src/extract/gpu-driven.ts +108 -0
  471. package/src/extract/visibility.ts +0 -23
  472. package/src/features/host.ts +16 -0
  473. package/src/features/prepared-gpu-work.ts +20 -2
  474. package/src/features/prepared-graphics-store.ts +17 -0
  475. package/src/gpu-driven/batch-topology.ts +206 -28
  476. package/src/gpu-driven/prepared-draw.ts +208 -0
  477. package/src/gpu-driven/production-raster.ts +1046 -39
  478. package/src/gpu-driven/view-gpu.ts +411 -29
  479. package/src/gpu-scene-schema.ts +13 -0
  480. package/src/gpu-scene.ts +137 -67
  481. package/src/gpu-usage.ts +0 -1
  482. package/src/hdrp-buffers.ts +6 -6
  483. package/src/ibl/IblPipelineCache.ts +134 -50
  484. package/src/ibl/skylight-bind-group.ts +26 -0
  485. package/src/index.ts +83 -1
  486. package/src/inspection-types.ts +220 -1
  487. package/src/internal.ts +38 -0
  488. package/src/light-buffer-layout.ts +157 -324
  489. package/src/materials.ts +278 -47
  490. package/src/mesh-material-bindings.ts +59 -0
  491. package/src/occlusion-proxy.ts +64 -0
  492. package/src/pbr-pipeline.ts +290 -60
  493. package/src/pipeline/standard-forward-lane.ts +47 -0
  494. package/src/pipeline/standard-lighting/graph.ts +2 -1
  495. package/src/pipeline/standard-lighting/light-frame.ts +3 -2
  496. package/src/pipeline/standard-lighting/topology.ts +1 -1
  497. package/src/pipeline/standard-lighting/transport.ts +2 -1
  498. package/src/pipeline/standard-pipeline.ts +2 -0
  499. package/src/pipeline-builder.ts +5 -2
  500. package/src/pipeline-spec.ts +12 -1
  501. package/src/plugin.ts +4 -0
  502. package/src/points-lines/__tests__/cache-recovery.unit.test.ts +22 -0
  503. package/src/points-lines/prepare.ts +9 -0
  504. package/src/points-lines/standard-owner.ts +403 -0
  505. package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +56 -0
  506. package/src/prepare/extended-lighting/ltc-resources.ts +136 -0
  507. package/src/prepare/extended-lighting/resources.ts +389 -0
  508. package/src/prepare/extended-lighting/spot-modifiers.ts +194 -0
  509. package/src/prepare/extended-lighting/state.ts +92 -0
  510. package/src/record/__tests__/frame-targets-recovery.unit.test.ts +60 -0
  511. package/src/record/__tests__/gpu-timing.unit.test.ts +177 -0
  512. package/src/record/__tests__/main-pass-geometry.unit.test.ts +26 -1
  513. package/src/record/__tests__/mesh-ssbo.unit.test.ts +38 -0
  514. package/src/record/__tests__/recovery-pipeline.unit.test.ts +124 -0
  515. package/src/record/__tests__/sprite-material-abi.unit.test.ts +28 -13
  516. package/src/record/__tests__/standard-pbr-ubo-layout.unit.test.ts +47 -4
  517. package/src/record/__tests__/temporal-payload.unit.test.ts +29 -28
  518. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +21 -2
  519. package/src/record/frame-lighting.ts +252 -53
  520. package/src/record/frame-snapshot.ts +72 -12
  521. package/src/record/frame-targets.ts +11 -1
  522. package/src/record/frame.ts +416 -95
  523. package/src/record/gpu-timing.ts +108 -44
  524. package/src/record/helpers.ts +1 -59
  525. package/src/record/main-pass-geometry.ts +434 -44
  526. package/src/record/main-pass-material.ts +324 -210
  527. package/src/record/main-pass-sprite-draws.ts +58 -92
  528. package/src/record/main-pass.ts +94 -6
  529. package/src/record/mesh-ssbo.ts +79 -46
  530. package/src/record/recovery-pipeline.ts +695 -0
  531. package/src/record/render-context.ts +99 -7
  532. package/src/record/shadow-pass.ts +66 -40
  533. package/src/record/typed-frame-graph.ts +270 -84
  534. package/src/record/view-ubo.ts +17 -27
  535. package/src/recovery/render-system-candidate.ts +839 -0
  536. package/src/reflection/inspection.ts +67 -0
  537. package/src/reflection/projection.ts +196 -1
  538. package/src/reflection/record-owner.ts +1283 -0
  539. package/src/render-contract.ts +82 -12
  540. package/src/render-pipeline.ts +5 -0
  541. package/src/render-system-extract-tail.ts +2983 -0
  542. package/src/render-system-extract.ts +290 -2784
  543. package/src/render-system.ts +1597 -1192
  544. package/src/scene/__tests__/changed-block-summary.candidate.test.ts +36 -0
  545. package/src/scene/probe-blend-record.ts +54 -0
  546. package/src/scene/probe-blend.ts +813 -0
  547. package/src/scene/render-scene-types.ts +0 -7
  548. package/src/scene/render-scene.ts +972 -184
  549. package/src/scene/visibility/bounds.ts +57 -0
  550. package/src/scene/visibility/budget.ts +40 -0
  551. package/src/scene/visibility/facet.ts +400 -0
  552. package/src/scene/visibility/gpu-frame-timing.ts +101 -0
  553. package/src/scene/visibility/gpu-lod.ts +119 -0
  554. package/src/scene/visibility/inspection.ts +313 -0
  555. package/src/scene/visibility/lod-selector.ts +84 -0
  556. package/src/scene/visibility/occlusion-confidence.ts +478 -0
  557. package/src/scene/visibility/occlusion-pass.ts +165 -0
  558. package/src/scene/visibility/occlusion-query-pool.ts +178 -0
  559. package/src/scene/visibility/occlusion-runtime.ts +672 -0
  560. package/src/scene/visibility/types.ts +50 -0
  561. package/src/ssr/admission.ts +451 -0
  562. package/src/ssr/composition.ts +73 -0
  563. package/src/ssr/errors.ts +76 -0
  564. package/src/ssr/identity.ts +12 -0
  565. package/src/systems/skin-palette-allocator.ts +1 -1
  566. package/src/temporal/frame-coordinator.ts +30 -1
  567. package/src/temporal/frame.ts +14 -1
  568. package/src/temporal/taa-history-store.ts +46 -2
  569. package/src/temporal/temporal-view.ts +1 -1
  570. package/src/temporal/transaction.ts +9 -0
  571. package/src/temporal/view.ts +11 -0
  572. package/src/tilemap-chunk-extract-system.ts +5 -5
  573. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +19 -12
  574. package/src/typed-render-graph-primitives.ts +48 -0
  575. package/src/volume/passes.ts +46 -49
  576. package/dist/.tsbuildinfo +0 -1
  577. package/dist/chunk-5YADUCA7.mjs.map +0 -1
  578. package/dist/chunk-6OPXDQ2E.mjs.map +0 -1
  579. package/dist/chunk-BXBQQKFQ.mjs +0 -67
  580. package/dist/chunk-BXBQQKFQ.mjs.map +0 -1
  581. package/dist/chunk-FS2KNUQH.mjs.map +0 -1
  582. package/dist/chunk-PCHBPYML.mjs.map +0 -1
  583. package/dist/chunk-VFPLTKCU.mjs +0 -192
  584. 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 {
@@ -17,6 +18,7 @@ import type { DirectionalShadowQuality } from '../components/directional-shadow-
17
18
  import type { EnvironmentGeneration } from '../environment/generation';
18
19
  import type { EnvironmentLifecycle } from '../environment/lifecycle';
19
20
  import type { GpuBuffer } from '../gpu-resource';
21
+ import type { IblBindingInspection } from '../mesh-material-bindings';
20
22
  import type { StandardLightingInspection } from '../pipeline/standard-lighting/inspection';
21
23
  import type { PointShadowInspection } from '../point-shadow-inspection';
22
24
  import type { TemporalGpuState } from '../temporal/gpu';
@@ -81,6 +83,7 @@ import type {
81
83
  RenderableSnapshot,
82
84
  SpotLightSnapshot,
83
85
  } from '../render-system-extract';
86
+ import type { ProbeBlendRecord } from '../scene/probe-blend-record';
84
87
  import type { ShadowAtlas } from '../shadow-atlas';
85
88
  import type {
86
89
  TemporalFrame,
@@ -90,6 +93,18 @@ import type {
90
93
  import type { VolumetricFogInspection } from '../volume/inspection';
91
94
  import type { VolumeTemporalSignature } from '../volume/temporal';
92
95
 
96
+ /** Retained probe record bytes carried with one extracted render frame. */
97
+ export interface ProbeBlendFrameSnapshot {
98
+ readonly records: readonly ProbeBlendRecord[];
99
+ readonly recordByteLength: 160;
100
+ }
101
+
102
+ /** Renderer-owned GPU cache entry for one object's 160B probe record. */
103
+ export interface ProbeBlendBufferCacheEntry {
104
+ readonly generation: number;
105
+ readonly bytes: Uint8Array;
106
+ }
107
+
93
108
  /**
94
109
  * feat-20260608-create-app-param-surface-trim / M1 / D-8 (q8 user lock):
95
110
  * fallback clear color when the world carries no Camera entity. Opaque
@@ -124,6 +139,18 @@ export interface CurrentGraphTarget {
124
139
  const textureIdentities = new WeakMap<object, number>();
125
140
  let nextTextureIdentity = 1;
126
141
 
142
+ const opaqueResourceIdentities = new WeakMap<object, number>();
143
+ let nextOpaqueResourceIdentity = 1;
144
+
145
+ /** Stable detached identity for a renderer-owned opaque resource handle. */
146
+ export function getOpaqueResourceIdentity(resource: object): number {
147
+ const existing = opaqueResourceIdentities.get(resource);
148
+ if (existing !== undefined) return existing;
149
+ const identity = nextOpaqueResourceIdentity++;
150
+ opaqueResourceIdentities.set(resource, identity);
151
+ return identity;
152
+ }
153
+
127
154
  /**
128
155
  * Project an opaque RHI texture handle to a stable diagnostic identity.
129
156
  * RHI handles intentionally expose no serializable fields; object identity is
@@ -154,14 +181,17 @@ export function validateGraphTargetCaptureReadback(input: {
154
181
  readonly bytes: Uint8Array;
155
182
  readonly expectedByteLength: number;
156
183
  readonly sentinel?: Uint8Array;
184
+ readonly allowZero?: boolean;
157
185
  }): GraphTargetCaptureReadbackValidation {
158
- const { bytes, expectedByteLength, sentinel } = input;
186
+ const { bytes, expectedByteLength, sentinel, allowZero = false } = input;
159
187
  if (bytes.byteLength !== expectedByteLength)
160
188
  return { ok: false, code: 'capture-readback-length' };
161
189
  if (sentinel?.every((value, index) => bytes[index] === value)) {
162
190
  return { ok: false, code: 'capture-readback-untouched-sentinel' };
163
191
  }
164
- if (bytes.every((value) => value === 0)) return { ok: false, code: 'capture-readback-empty' };
192
+ if (!allowZero && bytes.every((value) => value === 0)) {
193
+ return { ok: false, code: 'capture-readback-empty' };
194
+ }
165
195
  for (let index = 0; index + 1 < bytes.length; index += 2) {
166
196
  const exponent = ((bytes[index + 1] ?? 0) >>> 2) & 0x1f;
167
197
  if (exponent === 0x1f) return { ok: false, code: 'capture-readback-non-finite' };
@@ -191,6 +221,25 @@ export interface GraphTargetCaptureRequest {
191
221
  };
192
222
  }
193
223
 
224
+ /** Renderer-private readback request for the Standard fallback MRT. */
225
+ export interface ReflectionFallbackReadbackRequest {
226
+ readonly name: string;
227
+ readonly buffer: Buffer;
228
+ readonly bytesPerRow: number;
229
+ readonly width: number;
230
+ readonly height: number;
231
+ readonly expected: {
232
+ readonly format: ResolvedColorTargetDescriptor['format'];
233
+ readonly width: number;
234
+ readonly height: number;
235
+ readonly usage: number;
236
+ readonly graphGeneration: number;
237
+ readonly frameId: number;
238
+ readonly textureIdentity: number;
239
+ };
240
+ encoded: boolean;
241
+ }
242
+
194
243
  /**
195
244
  * Build a synthetic CameraSnapshot the record stage uses when the world
196
245
  * carries no Camera entity. Identity-shaped projection / view inputs
@@ -253,12 +302,16 @@ export function makeZeroCameraFallbackSnapshot(): CameraSnapshot {
253
302
  */
254
303
  export interface RenderFrameState {
255
304
  frameNumber: number;
305
+ /** Final-submit Standard IBL binding-chain receipt for this frame. */
306
+ iblBindingInspection?: IblBindingInspection;
307
+ /** Demand for the frame-local Standard fallback MRT. */
308
+ reflectionFallbackDemand?: boolean;
256
309
  /** Monotonic compiled-graph identity used by diagnostic readbacks. */
257
310
  graphGeneration: number;
258
311
  /** Number of successfully submitted TAA frames; failed attempts do not advance it. */
259
312
  successfulTemporalFrameIndex?: number;
260
313
  /** The renderer-owned accepted-submit temporal fact consumed by consumers. */
261
- temporalFrame?: TemporalFrame;
314
+ temporalFrame?: TemporalFrame | undefined;
262
315
  /** The sole transaction that advances temporal history after queue.submit. */
263
316
  readonly temporalFrameTransaction: TemporalFrameTransaction;
264
317
  /** Staged input is consumed only by the graph submit owner. */
@@ -271,6 +324,8 @@ export interface RenderFrameState {
271
324
  readonly graphGeneration: number;
272
325
  } | null;
273
326
  graphTargetCapture?: GraphTargetCaptureRequest | undefined;
327
+ /** One-frame producer-owned copy target for fallback MRT readback. */
328
+ reflectionFallbackReadback?: ReflectionFallbackReadbackRequest | undefined;
274
329
  /** Last successfully rendered static directional shadow atlas token. */
275
330
  directionalShadowCache: DirectionalShadowCache | null;
276
331
  /** Last successfully submitted camera post-process mode. */
@@ -314,6 +369,10 @@ export interface RenderFrameState {
314
369
  readonly retiredCompiledFrameGraphs: Set<CompiledRenderGraph<RenderPipelineFrame>>;
315
370
  /** Physical outputs projected by typed graph passes for downstream observers. */
316
371
  currentFrameObservationSource: FrameObservationSource | undefined;
372
+ /** Completed-frame source for the producer-owned fallback MRT. */
373
+ reflectionFallbackObservationSource?: FrameObservationSource | undefined;
374
+ /** Completion fence for the producer-owned fallback row/readback promotion. */
375
+ reflectionFallbackCompletion?: Promise<void> | undefined;
317
376
  /** Camera antialias mode for the last successfully submitted graph. */
318
377
  lastSuccessfulCameraAntialias: CameraSnapshot['antialias'] | undefined;
319
378
  /** Staged TAA GPU state is promoted only after queue.submit succeeds. */
@@ -331,6 +390,11 @@ export interface RenderFrameState {
331
390
  currentDirectionalShadowView: TextureView | null;
332
391
  currentSpotShadowView: TextureView | null;
333
392
  readonly instanceBuffers: Map<number, InstanceBufferCacheEntry>;
393
+ /** Per-object ProbeBlendRecord buffers; upload is one atomic 160B write. */
394
+ readonly probeBlendBuffers: Map<number, ProbeBlendBufferCacheEntry>;
395
+ /** One renderer-owned record array; each object selects its 256B-aligned lane. */
396
+ probeBlendRecordBuffer?: GpuBuffer;
397
+ probeBlendRecordBufferCapacity: number;
334
398
  /** Per-entity vertex buffers for the Standard CPU morph lane. */
335
399
  readonly morphBuffers?: Map<number, MorphBufferCacheEntry>;
336
400
  readonly hdrpClusterBinScratch: ClusterBinScratch;
@@ -358,15 +422,11 @@ export interface RenderFrameState {
358
422
  transientInstanceBuffers: InstanceBufferCacheEntry[];
359
423
  warnedZeroLightStandard: boolean;
360
424
  /**
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).
425
+ * feat-20260608-multi-light-warn-once M3: once-warn latch for the
426
+ * single-directional authoring constraint. Standard local lights use the
427
+ * unified Cluster budget; there is no per-kind local-light cap.
366
428
  */
367
429
  warnedMultiLightDirectional: boolean;
368
- warnedMultiLightPoint: boolean;
369
- warnedMultiLightSpot: boolean;
370
430
  /**
371
431
  * feat-20260630-equirect-kind-internalized-ibl-declarative-skyligh M3 / w19:
372
432
  * once-warn latches for >1 Skylight / >1 SkyboxBackground entity. Fire at most
@@ -662,11 +722,11 @@ export interface DirectionalShadowCache {
662
722
  * ECS state that can change the contents of a directional shadow map.
663
723
  *
664
724
  * The ECS-owned RenderReadLease is the only render-facing mutation boundary.
665
- * Its cursor is intentionally conservative: any published World change
725
+ * Its version is intentionally conservative: any published World change
666
726
  * invalidates the cached atlas, while render never reaches through World for
667
727
  * component-clock storage.
668
728
  */
669
729
  export interface DirectionalShadowWorldState {
670
730
  readonly worldIdentity: string;
671
- readonly changeCursor: number;
731
+ readonly version: RenderReadVersion;
672
732
  }
@@ -75,9 +75,19 @@ export function acquireSwapChainTarget(
75
75
 
76
76
  let currentTextureResult = canvasContext.getCurrentTexture();
77
77
  if (!currentTextureResult.ok) {
78
+ const configuredDevice = internals.resolveSurfaceDevice?.(internals.device);
79
+ if (configuredDevice !== undefined && !configuredDevice.ok) {
80
+ internals.errorRegistry.fire(configuredDevice.error);
81
+ internals.healthRegistry.fire({
82
+ reason: 'internal-fault',
83
+ detail: { message: 'surface device resolution failed; current frame skipped' },
84
+ recoverable: true,
85
+ });
86
+ return null;
87
+ }
78
88
  const configured = configureSurface(
79
89
  canvasContext,
80
- internals.device,
90
+ configuredDevice?.value ?? internals.device,
81
91
  pipelineState.format,
82
92
  pipelineState.colorAttachmentFormat,
83
93
  );