@forgeax/engine-render 0.1.19 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (452) hide show
  1. package/README.md +215 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/assembly/device-feature-admission.d.ts +18 -0
  4. package/dist/assembly/device-feature-admission.d.ts.map +1 -0
  5. package/dist/assembly/directional-shadow-inspection.d.ts +38 -0
  6. package/dist/assembly/directional-shadow-inspection.d.ts.map +1 -0
  7. package/dist/assembly/factory.d.ts +34 -20
  8. package/dist/assembly/factory.d.ts.map +1 -1
  9. package/dist/assembly/host-contract.d.ts +9 -0
  10. package/dist/assembly/host-contract.d.ts.map +1 -1
  11. package/dist/assembly/render-target-host.d.ts +46 -0
  12. package/dist/assembly/render-target-host.d.ts.map +1 -0
  13. package/dist/assets/render-asset-port.d.ts.map +1 -1
  14. package/dist/authoring.mjs +3 -0
  15. package/dist/authoring.mjs.map +1 -1
  16. package/dist/capture/cube-views.d.ts +21 -0
  17. package/dist/capture/cube-views.d.ts.map +1 -0
  18. package/dist/capture/scheduler.d.ts +54 -0
  19. package/dist/capture/scheduler.d.ts.map +1 -0
  20. package/dist/{chunk-RJRUS55L.mjs → chunk-5YADUCA7.mjs} +1896 -1624
  21. package/dist/chunk-5YADUCA7.mjs.map +1 -0
  22. package/dist/{chunk-LOSO4CTV.mjs → chunk-6OPXDQ2E.mjs} +423 -46
  23. package/dist/chunk-6OPXDQ2E.mjs.map +1 -0
  24. package/dist/{chunk-IPXCABEP.mjs → chunk-BXBQQKFQ.mjs} +3 -9
  25. package/dist/chunk-BXBQQKFQ.mjs.map +1 -0
  26. package/dist/chunk-FS2KNUQH.mjs +1294 -0
  27. package/dist/chunk-FS2KNUQH.mjs.map +1 -0
  28. package/dist/components/camera.d.ts +1 -0
  29. package/dist/components/camera.d.ts.map +1 -1
  30. package/dist/components/cube-camera.d.ts +20 -0
  31. package/dist/components/cube-camera.d.ts.map +1 -0
  32. package/dist/components/directional-light.d.ts +11 -2
  33. package/dist/components/directional-light.d.ts.map +1 -1
  34. package/dist/components/directional-shadow-filter.d.ts +25 -0
  35. package/dist/components/directional-shadow-filter.d.ts.map +1 -0
  36. package/dist/components/index.d.ts +2 -0
  37. package/dist/components/index.d.ts.map +1 -1
  38. package/dist/components/light-helpers.d.ts.map +1 -1
  39. package/dist/components/reflection-probe.d.ts +17 -0
  40. package/dist/components/reflection-probe.d.ts.map +1 -0
  41. package/dist/components/spot-light.d.ts +12 -0
  42. package/dist/components/spot-light.d.ts.map +1 -1
  43. package/dist/construct-renderer.mjs +8552 -1612
  44. package/dist/construct-renderer.mjs.map +1 -1
  45. package/dist/device/gpu-residency.d.ts +19 -2
  46. package/dist/device/gpu-residency.d.ts.map +1 -1
  47. package/dist/environment/frame.d.ts.map +1 -1
  48. package/dist/environment/lifecycle.d.ts +5 -0
  49. package/dist/environment/lifecycle.d.ts.map +1 -1
  50. package/dist/errors/render.d.ts +209 -93
  51. package/dist/errors/render.d.ts.map +1 -1
  52. package/dist/extract/directional-shadow-projection.d.ts +23 -0
  53. package/dist/extract/directional-shadow-projection.d.ts.map +1 -0
  54. package/dist/extract/environment.d.ts +14 -0
  55. package/dist/extract/environment.d.ts.map +1 -1
  56. package/dist/extract/visibility.d.ts.map +1 -1
  57. package/dist/features/fullscreen.d.ts.map +1 -1
  58. package/dist/features/targets.d.ts.map +1 -1
  59. package/dist/features/types.d.ts +10 -0
  60. package/dist/features/types.d.ts.map +1 -1
  61. package/dist/fullscreen-post-process-pass.d.ts +13 -2
  62. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  63. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  64. package/dist/gpu-driven/production-raster.d.ts +0 -1
  65. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  66. package/dist/gpu-scene.d.ts.map +1 -1
  67. package/dist/gpu-usage.d.ts +2 -0
  68. package/dist/gpu-usage.d.ts.map +1 -1
  69. package/dist/hdrp-buffers.d.ts +19 -17
  70. package/dist/hdrp-buffers.d.ts.map +1 -1
  71. package/dist/ibl/IblPipelineCache.d.ts +4 -0
  72. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  73. package/dist/ibl/kernel-cache.d.ts +36 -0
  74. package/dist/ibl/kernel-cache.d.ts.map +1 -0
  75. package/dist/index.d.ts +35 -4
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.mjs +4 -4
  78. package/dist/index.mjs.map +1 -1
  79. package/dist/inspection-types.d.ts +51 -0
  80. package/dist/inspection-types.d.ts.map +1 -1
  81. package/dist/instances-derived-bounds.d.ts +50 -0
  82. package/dist/instances-derived-bounds.d.ts.map +1 -0
  83. package/dist/internal.mjs +1 -1
  84. package/dist/light-buffer-layout.d.ts +30 -13
  85. package/dist/light-buffer-layout.d.ts.map +1 -1
  86. package/dist/mesh-material-bindings.d.ts +54 -0
  87. package/dist/mesh-material-bindings.d.ts.map +1 -1
  88. package/dist/pbr-pipeline.d.ts +20 -7
  89. package/dist/pbr-pipeline.d.ts.map +1 -1
  90. package/dist/pipeline/standard-forward-lane.d.ts +2 -1
  91. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  92. package/dist/pipeline/standard-lighting/errors.d.ts +4 -0
  93. package/dist/pipeline/standard-lighting/errors.d.ts.map +1 -0
  94. package/dist/pipeline/standard-lighting/graph.d.ts +24 -0
  95. package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -0
  96. package/dist/pipeline/standard-lighting/inspection.d.ts +38 -0
  97. package/dist/pipeline/standard-lighting/inspection.d.ts.map +1 -0
  98. package/dist/pipeline/standard-lighting/layout.d.ts +28 -0
  99. package/dist/pipeline/standard-lighting/layout.d.ts.map +1 -0
  100. package/dist/pipeline/standard-lighting/light-frame.d.ts +33 -0
  101. package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -0
  102. package/dist/pipeline/standard-lighting/membership.d.ts +14 -0
  103. package/dist/pipeline/standard-lighting/membership.d.ts.map +1 -0
  104. package/dist/pipeline/standard-lighting/prepare.d.ts +16 -0
  105. package/dist/pipeline/standard-lighting/prepare.d.ts.map +1 -0
  106. package/dist/pipeline/standard-lighting/topology.d.ts +45 -0
  107. package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -0
  108. package/dist/pipeline/standard-lighting/transport.d.ts +36 -0
  109. package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -0
  110. package/dist/pipeline/standard-pipeline.d.ts +4 -13
  111. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  112. package/dist/pipeline/standard-post.d.ts.map +1 -1
  113. package/dist/pipeline/standard-profile.d.ts +12 -20
  114. package/dist/pipeline/standard-profile.d.ts.map +1 -1
  115. package/dist/pipeline-spec.d.ts +40 -7
  116. package/dist/pipeline-spec.d.ts.map +1 -1
  117. package/dist/point-shadow-inspection.d.ts +21 -0
  118. package/dist/point-shadow-inspection.d.ts.map +1 -0
  119. package/dist/record/frame-lighting.d.ts +29 -32
  120. package/dist/record/frame-lighting.d.ts.map +1 -1
  121. package/dist/record/frame-snapshot.d.ts +65 -15
  122. package/dist/record/frame-snapshot.d.ts.map +1 -1
  123. package/dist/record/frame-targets.d.ts +5 -0
  124. package/dist/record/frame-targets.d.ts.map +1 -1
  125. package/dist/record/frame.d.ts +12 -5
  126. package/dist/record/frame.d.ts.map +1 -1
  127. package/dist/record/gpu-pass-timing/contract.d.ts +96 -0
  128. package/dist/record/gpu-pass-timing/contract.d.ts.map +1 -0
  129. package/dist/record/gpu-pass-timing/errors.d.ts +18 -0
  130. package/dist/record/gpu-pass-timing/errors.d.ts.map +1 -0
  131. package/dist/record/gpu-pass-timing/index.d.ts +8 -0
  132. package/dist/record/gpu-pass-timing/index.d.ts.map +1 -0
  133. package/dist/record/gpu-pass-timing/parser.d.ts +11 -0
  134. package/dist/record/gpu-pass-timing/parser.d.ts.map +1 -0
  135. package/dist/record/gpu-pass-timing/session.d.ts +43 -0
  136. package/dist/record/gpu-pass-timing/session.d.ts.map +1 -0
  137. package/dist/record/gpu-timing.d.ts +37 -0
  138. package/dist/record/gpu-timing.d.ts.map +1 -0
  139. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  140. package/dist/record/main-pass-material.d.ts +23 -2
  141. package/dist/record/main-pass-material.d.ts.map +1 -1
  142. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  143. package/dist/record/main-pass.d.ts +2 -0
  144. package/dist/record/main-pass.d.ts.map +1 -1
  145. package/dist/record/render-context.d.ts +48 -3
  146. package/dist/record/render-context.d.ts.map +1 -1
  147. package/dist/record/shadow-pass.d.ts.map +1 -1
  148. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  149. package/dist/record/typed-frame-graph.d.ts +81 -5
  150. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  151. package/dist/record/view-ubo.d.ts +7 -4
  152. package/dist/record/view-ubo.d.ts.map +1 -1
  153. package/dist/reflection/filter.d.ts +22 -0
  154. package/dist/reflection/filter.d.ts.map +1 -0
  155. package/dist/reflection/gpu-table.d.ts +25 -0
  156. package/dist/reflection/gpu-table.d.ts.map +1 -0
  157. package/dist/reflection/projection.d.ts +91 -0
  158. package/dist/reflection/projection.d.ts.map +1 -0
  159. package/dist/render-contract.d.ts +114 -7
  160. package/dist/render-contract.d.ts.map +1 -1
  161. package/dist/render-data.d.ts +10 -6
  162. package/dist/render-data.d.ts.map +1 -1
  163. package/dist/render-graph-primitives.d.ts +1 -0
  164. package/dist/render-graph-primitives.d.ts.map +1 -1
  165. package/dist/render-pipeline.d.ts +78 -2
  166. package/dist/render-pipeline.d.ts.map +1 -1
  167. package/dist/render-system-extract.d.ts +113 -20
  168. package/dist/render-system-extract.d.ts.map +1 -1
  169. package/dist/render-system.d.ts +24 -3
  170. package/dist/render-system.d.ts.map +1 -1
  171. package/dist/renderer-inspect.d.ts +29 -0
  172. package/dist/renderer-inspect.d.ts.map +1 -0
  173. package/dist/renderer.d.ts +43 -0
  174. package/dist/renderer.d.ts.map +1 -0
  175. package/dist/scene/render-scene.d.ts +14 -0
  176. package/dist/scene/render-scene.d.ts.map +1 -1
  177. package/dist/shadow-atlas.d.ts +3 -3
  178. package/dist/shadow-atlas.d.ts.map +1 -1
  179. package/dist/ssao-buffers.d.ts +3 -0
  180. package/dist/ssao-buffers.d.ts.map +1 -1
  181. package/dist/targets/contracts.d.ts +66 -0
  182. package/dist/targets/contracts.d.ts.map +1 -0
  183. package/dist/targets/graph-projection.d.ts +29 -0
  184. package/dist/targets/graph-projection.d.ts.map +1 -0
  185. package/dist/targets/material-source.d.ts +34 -0
  186. package/dist/targets/material-source.d.ts.map +1 -0
  187. package/dist/targets/owner.d.ts +32 -0
  188. package/dist/targets/owner.d.ts.map +1 -0
  189. package/dist/targets/physical.d.ts +18 -0
  190. package/dist/targets/physical.d.ts.map +1 -0
  191. package/dist/targets/readback.d.ts +39 -0
  192. package/dist/targets/readback.d.ts.map +1 -0
  193. package/dist/temporal/frame.d.ts +44 -0
  194. package/dist/temporal/frame.d.ts.map +1 -0
  195. package/dist/temporal/index.mjs +3 -3
  196. package/dist/typed-render-graph-primitives.d.ts +6 -1
  197. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  198. package/dist/typed-shadow-atlas.d.ts +10 -0
  199. package/dist/typed-shadow-atlas.d.ts.map +1 -0
  200. package/dist/typed-shadow-passes.d.ts +1 -1
  201. package/dist/typed-shadow-passes.d.ts.map +1 -1
  202. package/dist/volume/capability.d.ts +57 -0
  203. package/dist/volume/capability.d.ts.map +1 -0
  204. package/dist/volume/component.d.ts +61 -0
  205. package/dist/volume/component.d.ts.map +1 -0
  206. package/dist/volume/extract.d.ts +11 -0
  207. package/dist/volume/extract.d.ts.map +1 -0
  208. package/dist/volume/inspection.d.ts +38 -0
  209. package/dist/volume/inspection.d.ts.map +1 -0
  210. package/dist/volume/passes.d.ts +38 -0
  211. package/dist/volume/passes.d.ts.map +1 -0
  212. package/dist/volume/recovery.d.ts +38 -0
  213. package/dist/volume/recovery.d.ts.map +1 -0
  214. package/dist/volume/resources.d.ts +57 -0
  215. package/dist/volume/resources.d.ts.map +1 -0
  216. package/dist/volume/temporal.d.ts +35 -0
  217. package/dist/volume/temporal.d.ts.map +1 -0
  218. package/package.json +20 -20
  219. package/src/__tests__/bloom-exact-zero.unit.test.ts +48 -19
  220. package/src/__tests__/bloom-lifecycle.integration.test.ts +41 -13
  221. package/src/__tests__/camera-target-consumption.unit.test.ts +53 -0
  222. package/src/__tests__/cluster-binner.unit.test.ts +35 -0
  223. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +7 -2
  224. package/src/__tests__/cube-camera-scheduler.unit.test.ts +70 -0
  225. package/src/__tests__/cube-camera-single-submit.perf.test.ts +75 -0
  226. package/src/__tests__/cube-camera-view-matrices.unit.test.ts +41 -0
  227. package/src/__tests__/cube-camera-vocabulary.unit.test.ts +47 -0
  228. package/src/__tests__/cube-camera.integration.test.ts +42 -0
  229. package/src/__tests__/device-feature-admission.unit.test.ts +73 -0
  230. package/src/__tests__/direct-light-snapshot.unit.test.ts +3 -3
  231. package/src/__tests__/docs-gate-green.unit.test.ts +38 -0
  232. package/src/__tests__/factory-contract.integration.test.ts +255 -29
  233. package/src/__tests__/fog-component.unit.test.ts +67 -0
  234. package/src/__tests__/fog.integration.test.ts +86 -0
  235. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +37 -0
  236. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +10 -9
  237. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +17 -1
  238. package/src/__tests__/gpu-driven-production.integration.test.ts +16 -17
  239. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +12 -4
  240. package/src/__tests__/gpu-pass-timing-browser-runner.ts +281 -0
  241. package/src/__tests__/gpu-pass-timing-coexistence.integration.test.ts +80 -0
  242. package/src/__tests__/gpu-pass-timing-contract.unit.test.ts +243 -0
  243. package/src/__tests__/gpu-pass-timing-exact-zero.unit.test.ts +131 -0
  244. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +230 -0
  245. package/src/__tests__/gpu-pass-timing-observation.unit.test.ts +233 -0
  246. package/src/__tests__/gpu-pass-timing-session.unit.test.ts +438 -0
  247. package/src/__tests__/gpu-pass-timing.browser.test.ts +26 -0
  248. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +125 -0
  249. package/src/__tests__/hdrp-group2-visibility.unit.test.ts +24 -0
  250. package/src/__tests__/instances-culling.integration.test.ts +94 -0
  251. package/src/__tests__/instances-derived-bounds.unit.test.ts +118 -0
  252. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +14 -14
  253. package/src/__tests__/material-observation.integration.test.ts +101 -0
  254. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
  255. package/src/__tests__/pbr-view-layout.unit.test.ts +16 -0
  256. package/src/__tests__/point-shadow-inspection.unit.test.ts +60 -0
  257. package/src/__tests__/public-surface.test-d.ts +10 -0
  258. package/src/__tests__/reflection-probe-authoring.unit.test.ts +71 -0
  259. package/src/__tests__/reflection-probe-filter.unit.test.ts +54 -0
  260. package/src/__tests__/reflection-probe-ibl-topology.unit.test.ts +45 -0
  261. package/src/__tests__/reflection-probe-pass-encode.integration.test.ts +101 -0
  262. package/src/__tests__/reflection-probe-performance.perf.test.ts +60 -0
  263. package/src/__tests__/reflection-probe-recovery.integration.test.ts +55 -0
  264. package/src/__tests__/reflection-probe-selection.unit.test.ts +46 -0
  265. package/src/__tests__/render-error-code-owner.test-d.ts +23 -3
  266. package/src/__tests__/render-feature-docs.unit.test.ts +1 -1
  267. package/src/__tests__/render-target-admission.unit.test.ts +80 -0
  268. package/src/__tests__/render-target-graph-projection.unit.test.ts +102 -0
  269. package/src/__tests__/render-target-inspection.unit.test.ts +52 -0
  270. package/src/__tests__/render-target-material-source.test-d.ts +30 -0
  271. package/src/__tests__/render-target-material-source.unit.test.ts +97 -0
  272. package/src/__tests__/render-target-owner-placement.unit.test.ts +203 -0
  273. package/src/__tests__/render-target-owner.unit.test.ts +122 -0
  274. package/src/__tests__/render-target-probe-soak.perf.test.ts +94 -0
  275. package/src/__tests__/render-target-public-schema.json +107 -0
  276. package/src/__tests__/render-target-public-schema.unit.test.ts +91 -0
  277. package/src/__tests__/render-target-public-surface.test-d.ts +83 -0
  278. package/src/__tests__/render-target-readback.integration.test.ts +90 -0
  279. package/src/__tests__/render-target-recovery.integration.test.ts +85 -0
  280. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +76 -0
  281. package/src/__tests__/renderer-frame-transaction.integration.test.ts +1 -1
  282. package/src/__tests__/renderer-lifecycle.integration.test.ts +14 -0
  283. package/src/__tests__/shader-stage-surface.unit.test.ts +19 -4
  284. package/src/__tests__/skin-motion-regression.unit.test.ts +132 -0
  285. package/src/__tests__/skinned-shadow-caster.test.ts +32 -2
  286. package/src/__tests__/standard-cluster-capability.integration.test.ts +63 -0
  287. package/src/__tests__/standard-cluster-transport.unit.test.ts +121 -0
  288. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +232 -0
  289. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +8 -5
  290. package/src/__tests__/standard-lighting-inspection.integration.test.ts +138 -0
  291. package/src/__tests__/standard-lighting-ssot.unit.test.ts +18 -0
  292. package/src/__tests__/standard-lighting-write.unit.test.ts +377 -0
  293. package/src/__tests__/standard-lighting.integration.test.ts +67 -0
  294. package/src/__tests__/standard-pipeline.dawn.test.ts +206 -5
  295. package/src/__tests__/standard-pipeline.integration.test.ts +181 -150
  296. package/src/__tests__/standard-probe-binding.unit.test.ts +25 -0
  297. package/src/__tests__/standard-profile-oracle.unit.test.ts +23 -36
  298. package/src/__tests__/typed-pipeline-topology.unit.test.ts +395 -14
  299. package/src/__tests__/urp-pipeline-targets.unit.test.ts +2 -2
  300. package/src/__tests__/visibility-resolver.unit.test.ts +15 -0
  301. package/src/__tests__/volume-optics-oracle.unit.test.ts +85 -0
  302. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +655 -0
  303. package/src/__tests__/volumetric-fog-component.unit.test.ts +71 -0
  304. package/src/__tests__/volumetric-fog-composite-stage-readback.dawn.test.ts +376 -0
  305. package/src/__tests__/volumetric-fog-errors.unit.test.ts +51 -0
  306. package/src/__tests__/volumetric-fog-inspection.unit.test.ts +77 -0
  307. package/src/__tests__/volumetric-fog-light-capability.unit.test.ts +19 -0
  308. package/src/__tests__/volumetric-fog-light-consumers.integration.test.ts +18 -0
  309. package/src/__tests__/volumetric-fog-light-errors.unit.test.ts +17 -0
  310. package/src/__tests__/volumetric-fog-light-topology.unit.test.ts +21 -0
  311. package/src/__tests__/volumetric-fog-light-transaction.integration.test.ts +53 -0
  312. package/src/__tests__/volumetric-fog-m9-watcher.integration.test.ts +27 -0
  313. package/src/__tests__/volumetric-fog-point-light-owner.integration.test.ts +44 -0
  314. package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +49 -0
  315. package/src/__tests__/volumetric-fog-point-spot-contract.unit.test.ts +28 -0
  316. package/src/__tests__/volumetric-fog-point-spot-lkg.integration.test.ts +65 -0
  317. package/src/__tests__/volumetric-fog-point-spot-selection.integration.test.ts +49 -0
  318. package/src/__tests__/volumetric-fog-point-spot-temporal.unit.test.ts +55 -0
  319. package/src/__tests__/volumetric-fog-projector-contract.unit.test.ts +22 -0
  320. package/src/__tests__/volumetric-fog-projector-inspection.unit.test.ts +12 -0
  321. package/src/__tests__/volumetric-fog-projector-surface-volume.integration.test.ts +11 -0
  322. package/src/__tests__/volumetric-fog-projector-transaction.integration.test.ts +26 -0
  323. package/src/__tests__/volumetric-fog-recovery.integration.test.ts +47 -0
  324. package/src/__tests__/volumetric-fog-selected-light-resolution.integration.test.ts +63 -0
  325. package/src/__tests__/volumetric-fog-spot-closure.integration.test.ts +26 -0
  326. package/src/__tests__/volumetric-fog-spot-contract.test-d.ts +21 -0
  327. package/src/__tests__/volumetric-fog-spot-contract.unit.test.ts +27 -0
  328. package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +14 -0
  329. package/src/__tests__/volumetric-fog-spot-optics.integration.test.ts +29 -0
  330. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +550 -0
  331. package/src/__tests__/volumetric-fog-temporal-selected-light.unit.test.ts +33 -0
  332. package/src/__tests__/volumetric-fog-temporal.unit.test.ts +45 -0
  333. package/src/__tests__/volumetric-fog-topology.unit.test.ts +237 -0
  334. package/src/__tests__/volumetric-fog-transparent.integration.test.ts +27 -0
  335. package/src/assembly/device-feature-admission.ts +73 -0
  336. package/src/assembly/directional-shadow-inspection.ts +122 -0
  337. package/src/assembly/factory.ts +1093 -246
  338. package/src/assembly/host-contract.ts +25 -0
  339. package/src/assembly/render-target-host.ts +847 -0
  340. package/src/assets/asset-decoders.ts +1 -1
  341. package/src/assets/render-asset-port.ts +7 -1
  342. package/src/capture/cube-views.ts +69 -0
  343. package/src/capture/scheduler.ts +265 -0
  344. package/src/components/__tests__/schema-contract.unit.test.ts +12 -0
  345. package/src/components/__tests__/schema-parity.unit.test.ts +9 -0
  346. package/src/components/camera.ts +4 -0
  347. package/src/components/cube-camera.ts +52 -0
  348. package/src/components/directional-light.ts +22 -2
  349. package/src/components/directional-shadow-filter.ts +57 -0
  350. package/src/components/index.ts +2 -0
  351. package/src/components/light-helpers.ts +85 -7
  352. package/src/components/reflection-probe.ts +49 -0
  353. package/src/components/spot-light.ts +22 -3
  354. package/src/device/gpu-residency.ts +168 -58
  355. package/src/environment/frame.ts +6 -24
  356. package/src/environment/lifecycle.ts +12 -0
  357. package/src/errors/render.ts +405 -143
  358. package/src/extract/directional-shadow-projection.ts +61 -0
  359. package/src/extract/environment.ts +57 -0
  360. package/src/extract/visibility.ts +25 -1
  361. package/src/features/fullscreen.ts +3 -0
  362. package/src/features/targets.ts +4 -1
  363. package/src/features/types.ts +10 -0
  364. package/src/fullscreen-post-process-pass.ts +43 -2
  365. package/src/gpu-driven/batch-topology.ts +1 -0
  366. package/src/gpu-driven/production-raster.ts +1 -3
  367. package/src/gpu-scene.ts +8 -1
  368. package/src/gpu-usage.ts +2 -0
  369. package/src/hdrp-buffers.ts +165 -59
  370. package/src/ibl/IblPipelineCache.ts +11 -0
  371. package/src/ibl/kernel-cache.ts +88 -0
  372. package/src/index.ts +186 -1
  373. package/src/inspection-types.ts +56 -0
  374. package/src/instances-derived-bounds.ts +171 -0
  375. package/src/light-buffer-layout.ts +74 -26
  376. package/src/mesh-material-bindings.ts +132 -0
  377. package/src/pbr-pipeline.ts +139 -27
  378. package/src/pipeline/standard-forward-lane.ts +65 -15
  379. package/src/pipeline/standard-lighting/errors.ts +6 -0
  380. package/src/pipeline/standard-lighting/graph.ts +157 -0
  381. package/src/pipeline/standard-lighting/inspection.ts +88 -0
  382. package/src/pipeline/standard-lighting/layout.ts +31 -0
  383. package/src/pipeline/standard-lighting/light-frame.ts +34 -0
  384. package/src/pipeline/standard-lighting/membership.ts +43 -0
  385. package/src/pipeline/standard-lighting/prepare.ts +37 -0
  386. package/src/pipeline/standard-lighting/topology.ts +120 -0
  387. package/src/pipeline/standard-lighting/transport.ts +77 -0
  388. package/src/pipeline/standard-pipeline.ts +141 -192
  389. package/src/pipeline/standard-post.ts +26 -7
  390. package/src/pipeline/standard-profile.ts +23 -30
  391. package/src/pipeline-spec.ts +84 -21
  392. package/src/point-shadow-inspection.ts +55 -0
  393. package/src/record/__tests__/temporal-payload.unit.test.ts +42 -1
  394. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +73 -0
  395. package/src/record/__tests__/view-ubo-layout.unit.test.ts +40 -0
  396. package/src/record/frame-lighting.ts +470 -516
  397. package/src/record/frame-snapshot.ts +76 -28
  398. package/src/record/frame-targets.ts +23 -2
  399. package/src/record/frame.ts +1169 -79
  400. package/src/record/gpu-pass-timing/contract.ts +180 -0
  401. package/src/record/gpu-pass-timing/errors.ts +34 -0
  402. package/src/record/gpu-pass-timing/index.ts +37 -0
  403. package/src/record/gpu-pass-timing/parser.ts +97 -0
  404. package/src/record/gpu-pass-timing/session.ts +938 -0
  405. package/src/record/gpu-timing.ts +233 -0
  406. package/src/record/main-pass-geometry.ts +120 -80
  407. package/src/record/main-pass-material.ts +101 -11
  408. package/src/record/main-pass-sprite-draws.ts +54 -25
  409. package/src/record/main-pass.ts +71 -15
  410. package/src/record/render-context.ts +77 -0
  411. package/src/record/shadow-pass.ts +104 -66
  412. package/src/record/skybox-post-pass.ts +20 -4
  413. package/src/record/typed-frame-graph.ts +1016 -32
  414. package/src/record/view-ubo.ts +63 -28
  415. package/src/reflection/filter.ts +81 -0
  416. package/src/reflection/gpu-table.ts +44 -0
  417. package/src/reflection/projection.ts +281 -0
  418. package/src/render-contract.ts +147 -4
  419. package/src/render-data.ts +50 -22
  420. package/src/render-graph-primitives.ts +9 -4
  421. package/src/render-pipeline.ts +161 -2
  422. package/src/render-system-extract.ts +886 -103
  423. package/src/render-system.ts +1174 -66
  424. package/src/renderer-inspect.ts +66 -0
  425. package/src/renderer.ts +222 -0
  426. package/src/scene/render-scene.ts +117 -9
  427. package/src/shadow-atlas.ts +5 -5
  428. package/src/ssao-buffers.ts +9 -2
  429. package/src/targets/contracts.ts +182 -0
  430. package/src/targets/graph-projection.ts +169 -0
  431. package/src/targets/material-source.ts +143 -0
  432. package/src/targets/owner.ts +214 -0
  433. package/src/targets/physical.ts +18 -0
  434. package/src/targets/readback.ts +153 -0
  435. package/src/temporal/frame.ts +128 -0
  436. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +61 -23
  437. package/src/typed-render-graph-primitives.ts +34 -2
  438. package/src/typed-shadow-atlas.ts +17 -0
  439. package/src/typed-shadow-passes.ts +47 -40
  440. package/src/volume/capability.ts +173 -0
  441. package/src/volume/component.ts +110 -0
  442. package/src/volume/extract.ts +21 -0
  443. package/src/volume/inspection.ts +78 -0
  444. package/src/volume/passes.ts +686 -0
  445. package/src/volume/recovery.ts +86 -0
  446. package/src/volume/resources.ts +236 -0
  447. package/src/volume/temporal.ts +89 -0
  448. package/dist/chunk-IPXCABEP.mjs.map +0 -1
  449. package/dist/chunk-LOSO4CTV.mjs.map +0 -1
  450. package/dist/chunk-RJRUS55L.mjs.map +0 -1
  451. package/dist/chunk-YTQ5C7YQ.mjs +0 -584
  452. package/dist/chunk-YTQ5C7YQ.mjs.map +0 -1
@@ -40,9 +40,16 @@ import {
40
40
  type SkylightBindGroupResources,
41
41
  type TransmissionBindGroupResources,
42
42
  } from '../ibl/skylight-bind-group';
43
+ import type { PipelineGroup2Contract } from '../pbr-pipeline';
43
44
  import { isStandardPbrMaterialShader } from '../pbr-pipeline';
44
45
  import { deriveTextureExtent } from '../render-data';
45
46
  import type { MaterialSnapshot } from '../render-system-extract';
47
+ import type { RenderTargetTextureSource } from '../targets/contracts';
48
+ import {
49
+ type RenderTargetMaterialSourceBinding,
50
+ resolveRenderTargetMaterialSource,
51
+ } from '../targets/material-source';
52
+ import type { StandardReflectionProbeBinding } from './frame-lighting';
46
53
  import type { BindGroupCounts, MaterialBgAssemblyCacheEntry } from './frame-snapshot';
47
54
  import { extractEntryResourceHandle, getOrCreatePerEntity } from './mesh-ssbo';
48
55
  import {
@@ -51,6 +58,23 @@ import {
51
58
  STANDARD_PBR_UBO_SIZE,
52
59
  } from './render-context';
53
60
 
61
+ /**
62
+ * Resolve the group(2) resource layout from the material shader contract.
63
+ * Standard clustered PBR consumes the unified cluster bind group; every other
64
+ * material consumes the ordinary mesh bind group. Missing ordinary resources
65
+ * remain missing so the caller can skip the draw rather than violate layout
66
+ * compatibility by binding a cluster group.
67
+ */
68
+ export function selectMaterialGroup2(
69
+ clusterGroup: BindGroup | null,
70
+ meshGroup: BindGroup | null,
71
+ group2Contract: PipelineGroup2Contract,
72
+ ): BindGroup | null {
73
+ if (group2Contract === 'cluster') return clusterGroup;
74
+ if (group2Contract === 'mesh') return meshGroup;
75
+ return null;
76
+ }
77
+
54
78
  // Param schemas are immutable runtime contracts: the shader registry installs
55
79
  // them once and material snapshots only retain the same array reference. Keep
56
80
  // the expensive pure derivation on that identity so the per-submesh record
@@ -412,12 +436,31 @@ const DEFAULT_STANDARD_TEXTURE_COORDINATES = new Float32Array([
412
436
  0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
413
437
  ]);
414
438
 
439
+ export interface StandardReflectionProbeMaterialProjection {
440
+ readonly probeIndex: number | undefined;
441
+ readonly fallbackToSkylight: boolean;
442
+ }
443
+
444
+ /** Projects the selected scene row into the Standard material lane. */
445
+ export function projectReflectionProbeMaterialBinding(
446
+ binding: StandardReflectionProbeBinding,
447
+ ): StandardReflectionProbeMaterialProjection {
448
+ return {
449
+ probeIndex: binding.probeIndex,
450
+ fallbackToSkylight: binding.useSkylight,
451
+ };
452
+ }
453
+
415
454
  /** Derives the logical-content UV scale for an uploaded material texture. */
416
455
  export function materialTextureUvScale(
417
- texture: Pick<TextureAsset, 'width' | 'height' | 'format'> | undefined,
456
+ texture: Pick<TextureAsset, 'shape' | 'format'> | undefined,
418
457
  ): readonly [number, number] {
419
458
  if (texture === undefined) return [1, 1];
420
- return deriveTextureExtent(texture.format, texture.width, texture.height).uvScale;
459
+ return deriveTextureExtent(
460
+ texture.format,
461
+ texture.shape.extent.width,
462
+ texture.shape.extent.height,
463
+ ).uvScale;
421
464
  }
422
465
 
423
466
  function materialTextureForField(
@@ -429,6 +472,18 @@ function materialTextureForField(
429
472
  return material.textureHandles?.get(field);
430
473
  }
431
474
 
475
+ /** Resolve a renderer-local target source without entering AssetRegistry. */
476
+ export function materialRenderTargetSourceForField(
477
+ material: MaterialSnapshot,
478
+ field: string,
479
+ resolveSource?: RenderSystemRuntime['resolveRenderTargetTextureSource'],
480
+ ): RenderTargetMaterialSourceBinding | undefined {
481
+ const source = material.textureSources?.get(field) as RenderTargetTextureSource | undefined;
482
+ return source === undefined
483
+ ? undefined
484
+ : (resolveSource?.(source) ?? resolveRenderTargetMaterialSource(source));
485
+ }
486
+
432
487
  /**
433
488
  * Appends the engine-owned scale for every built-in material texture binding.
434
489
  * Asset dimensions remain logical; this is the sole record-stage projection
@@ -579,7 +634,15 @@ export function userRegionTextureFieldOrder(
579
634
  export function defaultViewForUserRegionField(
580
635
  field: string,
581
636
  pipelineState: PipelineState,
637
+ schema?: readonly ParamSchemaEntry[],
582
638
  ): TextureView {
639
+ const parameter = schema?.find((entry) => entry.name === field);
640
+ if (parameter?.type === 'texture_cube') {
641
+ // The renderer already owns a valid white cube for the Skylight fallback.
642
+ // Use it until a receipt-promoted RenderTarget source becomes available;
643
+ // binding a 2D view here would invalidate the custom cube material BGL.
644
+ return pipelineState.skylightFallback?.prefilterView ?? pipelineState.defaultWhiteTextureView;
645
+ }
583
646
  if (field === 'normalTexture') return pipelineState.defaultNormalTextureView;
584
647
  if (field === 'baseColorTexture') return pipelineState.fallbackTextureView;
585
648
  return pipelineState.defaultWhiteTextureView;
@@ -881,6 +944,11 @@ export interface PerSubmeshMaterialBgDeps {
881
944
  readonly materialSlice: number;
882
945
  readonly videoHighPerfAvailable: boolean;
883
946
  readonly skylightResources: SkylightBindGroupResources;
947
+ readonly resolveRenderTargetTextureSource?: RenderSystemRuntime['resolveRenderTargetTextureSource'];
948
+ readonly resolveReflectionProbeResources?: (
949
+ materialWorld: World,
950
+ entityKey: number,
951
+ ) => SkylightBindGroupResources | undefined;
884
952
  readonly materialBgShared: Map<string, WeakMap<object, unknown>>;
885
953
  /** Cross-frame final BG reuse keyed by the stable source material handle. */
886
954
  readonly materialBgAssemblyCache: Map<string, MaterialBgAssemblyCacheEntry>;
@@ -928,11 +996,15 @@ export function buildPerSubmeshMaterialBg(
928
996
  materialSlice,
929
997
  videoHighPerfAvailable,
930
998
  skylightResources,
999
+ resolveRenderTargetTextureSource,
1000
+ resolveReflectionProbeResources,
931
1001
  materialBgShared,
932
1002
  materialBgAssemblyCache,
933
1003
  transmissionBackdropView,
934
1004
  bindGroupCounts,
935
1005
  } = deps;
1006
+ const effectiveSkylightResources =
1007
+ resolveReflectionProbeResources?.(materialWorld, entityKey) ?? skylightResources;
936
1008
  const smMaterialHandle = submeshMaterial.materialHandle;
937
1009
  const materialCacheKey =
938
1010
  smMaterialHandle === undefined ? undefined : `${materialWorld.identity}:${smMaterialHandle}`;
@@ -951,7 +1023,7 @@ export function buildPerSubmeshMaterialBg(
951
1023
  submeshMaterial,
952
1024
  smMaterialBgl,
953
1025
  pipelineState.materialUniformBuffer.buffer,
954
- skylightResources,
1026
+ effectiveSkylightResources,
955
1027
  store.materialResourceEpoch,
956
1028
  transmissionBackdropView,
957
1029
  )
@@ -999,11 +1071,25 @@ export function buildPerSubmeshMaterialBg(
999
1071
  const textureBinding = samplerBinding + 1;
1000
1072
  let smView: TextureView =
1001
1073
  field !== undefined
1002
- ? defaultViewForUserRegionField(field, pipelineState)
1074
+ ? defaultViewForUserRegionField(field, pipelineState, smSchema)
1003
1075
  : pipelineState.defaultWhiteTextureView;
1004
1076
  const smVideoClip =
1005
1077
  field !== undefined ? submeshMaterial.videoTextureFields?.get(field) : undefined;
1006
- if (smVideoClip !== undefined) {
1078
+ const smTargetSource =
1079
+ field === undefined
1080
+ ? undefined
1081
+ : materialRenderTargetSourceForField(
1082
+ submeshMaterial,
1083
+ field,
1084
+ resolveRenderTargetTextureSource,
1085
+ );
1086
+ if (smTargetSource !== undefined) {
1087
+ // The Renderer host resolves the generation-checked physical view. A
1088
+ // candidate is never exposed here; until its matching FrameReceipt
1089
+ // completes, the material remains on the neutral fallback.
1090
+ if (smTargetSource.textureView !== undefined) smView = smTargetSource.textureView;
1091
+ else materialResourcesResident = false;
1092
+ } else if (smVideoClip !== undefined) {
1007
1093
  const view = videoTextureView(
1008
1094
  materialWorld,
1009
1095
  runtime.dynamicTextureStore,
@@ -1045,12 +1131,16 @@ export function buildPerSubmeshMaterialBg(
1045
1131
  // authored UV textures and is not a backdrop sampler.
1046
1132
  const transmissionSampler =
1047
1133
  transmissionBackdropView != null
1048
- ? skylightResources.prefilterSampler
1134
+ ? effectiveSkylightResources.prefilterSampler
1049
1135
  : pipelineState.defaultSampler;
1050
- const smMergedEntries = assembleMaterialWithSkylightEntries(smBaseEntries, skylightResources, {
1051
- sampler: transmissionSampler,
1052
- ...(transmissionBackdropView === undefined ? {} : { backdropView: transmissionBackdropView }),
1053
- } satisfies TransmissionBindGroupResources);
1136
+ const smMergedEntries = assembleMaterialWithSkylightEntries(
1137
+ smBaseEntries,
1138
+ effectiveSkylightResources,
1139
+ {
1140
+ sampler: transmissionSampler,
1141
+ ...(transmissionBackdropView === undefined ? {} : { backdropView: transmissionBackdropView }),
1142
+ } satisfies TransmissionBindGroupResources,
1143
+ );
1054
1144
  const createBindGroup = (): BindGroup => {
1055
1145
  const result = runtime.device.createBindGroup({
1056
1146
  label: 'pbr-material-skylight-bg',
@@ -1082,7 +1172,7 @@ export function buildPerSubmeshMaterialBg(
1082
1172
  materialResourceEpoch: store.materialResourceEpoch,
1083
1173
  materialBgl: smMaterialBgl,
1084
1174
  materialBuffer: pipelineState.materialUniformBuffer.buffer,
1085
- skylightResources,
1175
+ skylightResources: effectiveSkylightResources,
1086
1176
  bindGroup: smBindGroup,
1087
1177
  });
1088
1178
  }
@@ -28,6 +28,7 @@ import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from '../materials';
28
28
  import { SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET } from '../pbr-pipeline';
29
29
  import {
30
30
  buildBeginRenderPassDescriptor,
31
+ standardTopologyVariantSet,
31
32
  variantSetFromVertexLayoutProjection,
32
33
  } from '../pipeline-spec';
33
34
  import type { MaterialSnapshot, SpriteInstancesSnapshot } from '../render-system-extract';
@@ -37,6 +38,7 @@ import {
37
38
  defaultViewForUserRegionField,
38
39
  entityHasTransparentSubmesh,
39
40
  residentTextureView,
41
+ selectMaterialGroup2,
40
42
  } from './main-pass-material';
41
43
  import {
42
44
  extractEntryResourceHandle,
@@ -208,8 +210,13 @@ export function recordSpritePass(
208
210
  ldrSpriteColorView,
209
211
  geometryDepthView,
210
212
  viewBindGroup,
213
+ meshBindGroup,
214
+ hdrpClusterBindGroup,
215
+ viewBindGroupDynamicOffset = 0,
211
216
  splitLdrSprite,
212
217
  } = c;
218
+ const clusteredLighting = c.standardLighting?.kind === 'clustered';
219
+ const meshGroup2 = clusteredLighting ? hdrpClusterBindGroup : meshBindGroup;
213
220
  // The split pass is also used by the no-tone linear-LDR route. That route
214
221
  // keeps transparent draws in the graph-owned rgba16float attachment, so
215
222
  // sprite shaders must use their linear/HDR fragment entry point even though
@@ -260,7 +267,7 @@ export function recordSpritePass(
260
267
  ) as never,
261
268
  );
262
269
 
263
- spritePass.setBindGroup(0, viewBindGroup as BindGroup, [0]);
270
+ spritePass.setBindGroup(0, viewBindGroup as BindGroup, [viewBindGroupDynamicOffset, 0]);
264
271
 
265
272
  // feat-20260625-refactor-sprite-as-transparent-mesh M3 / w14 (D-7):
266
273
  // sprite PSO resolution migrated from the dedicated boot-time PSO
@@ -376,7 +383,12 @@ export function recordSpritePass(
376
383
  spritePremulBlend,
377
384
  'triangle-list',
378
385
  undefined,
379
- undefined,
386
+ // sprite-lit declares the same Standard capability axes as PBR. The
387
+ // boot artifact is intentionally the direct compatibility variant,
388
+ // so a clustered frame must request its explicit Cluster artifact;
389
+ // otherwise the host binds the unified group(2) while the shader
390
+ // still reads the now-cleared direct-light headers.
391
+ standardTopologyVariantSet(c.standardLighting, runtime.device.caps.storageBuffer, false),
380
392
  'forward',
381
393
  undefined,
382
394
  msaaActive ? 4 : 1,
@@ -416,6 +428,7 @@ export function recordSpritePass(
416
428
  materialSlotIndices,
417
429
  sampleCount,
418
430
  resolveMaterialBindGroup,
431
+ meshGroup2,
419
432
  );
420
433
 
421
434
  if (graphPass === undefined) spritePass.end();
@@ -448,9 +461,9 @@ function recordSpriteTransparentPbrDraws(
448
461
  entityKey: number,
449
462
  materialWorld: World,
450
463
  ) => BindGroup,
464
+ meshGroup2: BindGroup | null,
451
465
  ): void {
452
- const { runtime, pipelineState, frameState, bindGroupCounts, validatedOrdered, meshBindGroup } =
453
- c;
466
+ const { runtime, pipelineState, frameState, bindGroupCounts, validatedOrdered } = c;
454
467
  const isHdrTarget =
455
468
  c.tonemapActive || transparentPassColorFormat(c, pipelineState) === 'rgba16float';
456
469
  let lastPbrSubPipelineHandle: typeof pipelineState.unlitPipeline = null;
@@ -484,7 +497,6 @@ function recordSpriteTransparentPbrDraws(
484
497
  spritePass.setIndexBuffer(entry.mesh.indexBuffer.handle, entry.mesh.indexFormat);
485
498
  lastPbrSubIndexBuffer = entry.mesh.indexBuffer;
486
499
  }
487
- spritePass.setBindGroup(2, meshBindGroup as BindGroup, [i * MESH_PER_ENTITY_STRIDE]);
488
500
  // Identity instance BG (instanceCount=1); reuse the per-entity cache.
489
501
  const identityInstBg: BindGroup = getOrCreatePerEntity(
490
502
  frameState.instancesBgPerEntity,
@@ -512,9 +524,11 @@ function recordSpriteTransparentPbrDraws(
512
524
  );
513
525
  spritePass.setBindGroup(3, identityInstBg);
514
526
 
515
- const subVariantSet = frameState.isHdrpActive
516
- ? ''
517
- : 'CLUSTER_FORWARD_AVAILABLE=false+STORAGE_BUFFER_AVAILABLE=true';
527
+ const subVariantSet = standardTopologyVariantSet(
528
+ c.standardLighting,
529
+ runtime.device.caps.storageBuffer,
530
+ entry.mesh.layoutProjection.attributes.some((attribute) => attribute.key === 'color'),
531
+ );
518
532
  const projectionVariantSetResult = variantSetFromVertexLayoutProjection(
519
533
  entry.mesh.layoutProjection,
520
534
  subVariantSet,
@@ -535,21 +549,8 @@ function recordSpriteTransparentPbrDraws(
535
549
  if (submeshMaterial.transparent !== true) continue;
536
550
  const smShaderId = submeshMaterial.materialShaderId;
537
551
  if (smShaderId === undefined) continue;
538
-
539
- // Bind the per-submesh material BG first (mirrors the geometry pass
540
- // order), then resolve the PSO; a first-frame async-compile miss skips
541
- // only the draw, not the BG (one transient frame, PSO flows in next).
542
- const materialSlot = materialSlotIndices[i]?.[matSlotIdx] ?? materialSlotIndices[i]?.[0] ?? 0;
543
- const subBg = resolveMaterialBindGroup(
544
- materialSlot,
545
- submeshMaterial,
546
- entry.source.entityKey,
547
- entry.world ?? c.world,
548
- );
549
- spritePass.setBindGroup(1, subBg, [materialSlot * MATERIAL_PER_ENTITY_STRIDE]);
550
-
551
- const subPipeline =
552
- runtime.getMaterialShaderPipeline?.(
552
+ const subPipelineEntry =
553
+ c.runtime.getMaterialShaderPipelineEntry?.(
553
554
  smShaderId,
554
555
  isHdrTarget,
555
556
  submeshMaterial.renderState,
@@ -566,7 +567,25 @@ function recordSpriteTransparentPbrDraws(
566
567
  undefined,
567
568
  entry.mesh.layoutProjection,
568
569
  ) ?? null;
569
- if (subPipeline === null) continue;
570
+ if (subPipelineEntry === null) continue;
571
+ const submeshContract = subPipelineEntry.group2Contract;
572
+ const submeshGroup2 = selectMaterialGroup2(meshGroup2, c.meshBindGroup, submeshContract);
573
+ if (submeshGroup2 === null) continue;
574
+ spritePass.setBindGroup(2, submeshGroup2, [i * MESH_PER_ENTITY_STRIDE]);
575
+
576
+ // Bind the per-submesh material BG first (mirrors the geometry pass
577
+ // order), then resolve the PSO; a first-frame async-compile miss skips
578
+ // only the draw, not the BG (one transient frame, PSO flows in next).
579
+ const materialSlot = materialSlotIndices[i]?.[matSlotIdx] ?? materialSlotIndices[i]?.[0] ?? 0;
580
+ const subBg = resolveMaterialBindGroup(
581
+ materialSlot,
582
+ submeshMaterial,
583
+ entry.source.entityKey,
584
+ entry.world ?? c.world,
585
+ );
586
+ spritePass.setBindGroup(1, subBg, [materialSlot * MATERIAL_PER_ENTITY_STRIDE]);
587
+
588
+ const subPipeline = subPipelineEntry.pipeline;
570
589
 
571
590
  if (lastPbrSubPipelineHandle !== subPipeline) {
572
591
  spritePass.setPipeline(subPipeline);
@@ -930,6 +949,16 @@ function recordSpriteEntityDraws(
930
949
  spriteInstanceBuffer = _si.buffer;
931
950
  spriteInstanceCount = _si.count;
932
951
 
952
+ // sprite-lit's clustered variant declares the unified Standard group(2)
953
+ // contract, while the regular sprite variants keep the direct mesh
954
+ // contract. Select the resource from the same capability axis used to
955
+ // request the pipeline; binding the ordinary mesh group to the clustered
956
+ // sprite-lit pipeline is rejected by WebGPU before the draw is recorded.
957
+ const spriteGroup2 =
958
+ entityShaderId === 'forgeax::sprite-lit' && c.standardLighting?.kind === 'clustered'
959
+ ? c.hdrpClusterBindGroup
960
+ : meshBindGroup;
961
+
933
962
  // M3 / w12: LDR sprite split pass per-entity instances BG cache.
934
963
  // Same pattern as #7.
935
964
  const spriteInstancesBg: BindGroup = getOrCreatePerEntity(
@@ -957,7 +986,7 @@ function recordSpriteEntityDraws(
957
986
  bindGroupCounts,
958
987
  );
959
988
 
960
- spritePass.setBindGroup(2, meshBindGroup as BindGroup, [i * MESH_PER_ENTITY_STRIDE]);
989
+ spritePass.setBindGroup(2, spriteGroup2 as BindGroup, [i * MESH_PER_ENTITY_STRIDE]);
961
990
 
962
991
  // Per-entity sprite material bind group: same standard PBR user-region
963
992
  // layout as in the geometry pass sprite branch + Skylight merged entries
@@ -9,10 +9,13 @@ import {
9
9
  import type { Handle, PassKind, PassSelector } from '@forgeax/engine-types';
10
10
  import { createHdrpUnifiedBindGroup, getOrCreateHdrpBuffers } from '../hdrp-buffers';
11
11
  import { getOrCreateIblCache } from '../ibl/IblPipelineCache';
12
- import { buildBeginRenderPassDescriptor } from '../pipeline-spec';
12
+ import type { SkylightBindGroupResources } from '../ibl/skylight-bind-group';
13
+ import { buildBeginRenderPassDescriptor, standardTopologyBindGroupReady } from '../pipeline-spec';
13
14
  import { POINTS_LINES_MATERIAL_SHADER_ID } from '../points-lines/record';
15
+ import type { ReflectionProbeSelectionResult } from '../reflection/projection';
14
16
  import type { RenderRecordPhase } from '../render-contract';
15
17
  import type { MaterialSnapshot } from '../render-system-extract';
18
+ import { resolveReflectionProbeBinding } from './frame-lighting';
16
19
  import { recordGeometryDraws } from './main-pass-geometry';
17
20
  import {
18
21
  applyMaterialTextureUvScales,
@@ -31,6 +34,12 @@ import {
31
34
  buildMatchedRenderableIndices,
32
35
  } from './shadow-pass';
33
36
 
37
+ export function standardReflectionProbeIndex(
38
+ selection: ReflectionProbeSelectionResult,
39
+ ): number | undefined {
40
+ return resolveReflectionProbeBinding(selection).probeIndex;
41
+ }
42
+
34
43
  const ZERO_SKYLIGHT_PAYLOAD = new Float32Array([0, 0, 0, 0, 0, 0, 0, 1]);
35
44
 
36
45
  export type MainPassOptions = {
@@ -114,6 +123,7 @@ export function recordMainPass(
114
123
  geometryDepthView,
115
124
  validatedOrdered,
116
125
  viewBindGroup,
126
+ viewBindGroupDynamicOffset = 0,
117
127
  meshBindGroup,
118
128
  frameState,
119
129
  bindGroupCounts,
@@ -161,14 +171,26 @@ export function recordMainPass(
161
171
  // copyExternalImageToTexture path (w16) is the sole route. The branch exists
162
172
  // so the AC-09 two-path reserved hook is code-review-verifiable, not a TODO.
163
173
  const videoHighPerfAvailable = probeVideoHighPerfUpload(runtime.device);
164
- // feat-20260609-hdrp-cluster-fragment-ggx M4 / w16: HDRP active swaps the
174
+ // The prepared Standard topology is the frame authority. Do not infer the
175
+ // shader lane from a resource that may be absent after a failed rebuild: a
176
+ // clustered frame with no unified group must fail closed, never fall back to
177
+ // a direct-lighting shader with an incompatible group(2) contract.
178
+ const clusteredLighting = c.standardLighting?.kind === 'clustered';
179
+ // A clustered topology still needs the unified group for every actual
180
+ // material draw. A camera-only/clear frame has no group(2) consumer yet;
181
+ // treating that harmless absence as a frame error would make the full
182
+ // clustered ABI incompatible with valid zero-renderable frames.
183
+ const clusteredBindGroupMissing =
184
+ validatedOrdered.length > 0 &&
185
+ !standardTopologyBindGroupReady(c.standardLighting, hdrpClusterBindGroup);
186
+ // Standard clustered lighting swaps the
165
187
  // group(2) bindGroup for the unified 7-entry layout (mesh SSBO at binding 0
166
188
  // + cluster 4 buffer at bindings 3..6). The dynamic offset
167
189
  // (`i * MESH_PER_ENTITY_STRIDE`) stays valid because the unified BGL binds
168
190
  // the SAME mesh SSBO at binding 0; the cluster-forward shader reads the
169
191
  // cluster bindings off the rest of the layout. Plan D-1 (URP path zero
170
- // change) is preserved — when `!isHdrpActive` the URP `meshBindGroup`
171
- // path runs verbatim.
192
+ // change) is preserved — when clustered lighting is not selected the
193
+ // ordinary mesh bind group path runs verbatim.
172
194
  // feat-20260612-hdrp-ssao wiring fix: when the HDRP forward pass resolved a
173
195
  // real `ssaoBlurred` view (stashed on ctx by the pass execute closure), build
174
196
  // the unified group(2) bind group with that view at binding 7 so fs_main reads
@@ -178,11 +200,7 @@ export function recordMainPass(
178
200
  // Built per frame (the SSAO target is a graph transient, so no cross-frame
179
201
  // cache); HDRP-only and SSAO-only, so URP and SSAO-off paths are untouched.
180
202
  let hdrpSsaoBindGroup: BindGroup | null = null;
181
- if (
182
- frameState.isHdrpActive &&
183
- hdrpClusterBindGroup !== null &&
184
- c.hdrpSsaoBlurredView !== undefined
185
- ) {
203
+ if (clusteredLighting && hdrpClusterBindGroup !== null && c.hdrpSsaoBlurredView !== undefined) {
186
204
  const hdrpBuffers = getOrCreateHdrpBuffers(
187
205
  runtime,
188
206
  frameState.installedPipelineConfig?.clusterGrid,
@@ -196,10 +214,9 @@ export function recordMainPass(
196
214
  );
197
215
  }
198
216
  }
199
- const meshGroup2: BindGroup | null =
200
- frameState.isHdrpActive && hdrpClusterBindGroup !== null
201
- ? (hdrpSsaoBindGroup ?? hdrpClusterBindGroup)
202
- : meshBindGroup;
217
+ const meshGroup2: BindGroup | null = clusteredLighting
218
+ ? (hdrpSsaoBindGroup ?? hdrpClusterBindGroup)
219
+ : meshBindGroup;
203
220
  // ── Geometry (main colour) pass ──────────────────────────────────
204
221
  // D-2: tracks whether the geometry pass was explicitly ended inside
205
222
  // the `if (validatedOrdered.length > 0)` block (sprite split path),
@@ -273,7 +290,17 @@ export function recordMainPass(
273
290
  })),
274
291
  };
275
292
 
276
- if (validatedOrdered.length > 0) {
293
+ if (clusteredBindGroupMissing) {
294
+ runtime.errorRegistry.fire(
295
+ new RhiError({
296
+ code: 'webgpu-runtime-error',
297
+ expected: 'clustered Standard frame has a unified group(2) BindGroup',
298
+ hint: 'repair the clustered buffer/layout admission before retrying the frame; direct URP fallback is unsafe',
299
+ }),
300
+ );
301
+ }
302
+
303
+ if (validatedOrdered.length > 0 && !clusteredBindGroupMissing) {
277
304
  // feat-20260518-pbr-direct-lighting-mvp M5 / w22.10 (D-4 + D-9 +
278
305
  // AC-07 std140): per-entity material slice grew from 32 B (legacy
279
306
  // baseColor:vec4 + metallic + roughness + 8B padding) to 48 B
@@ -432,6 +459,35 @@ export function recordMainPass(
432
459
  materialSlice: MATERIAL_SLICE,
433
460
  videoHighPerfAvailable,
434
461
  skylightResources,
462
+ resolveRenderTargetTextureSource: runtime.resolveRenderTargetTextureSource,
463
+ resolveReflectionProbeResources: (materialWorld, entityKey) => {
464
+ const reflectionProbes = c.reflectionProbes;
465
+ if (reflectionProbes === undefined) return undefined;
466
+ const selection = reflectionProbes.selections.get(`${materialWorld.identity}:${entityKey}`);
467
+ if (selection === undefined) return undefined;
468
+ const binding = resolveReflectionProbeBinding(selection, reflectionProbes.table);
469
+ if (binding.useSkylight || binding.probeIndex === undefined) return undefined;
470
+ const row = reflectionProbes.table.rows.find(
471
+ (candidate) => candidate.index === binding.probeIndex,
472
+ );
473
+ if (
474
+ row?.filteredView === undefined ||
475
+ row.sampler === undefined ||
476
+ row.uniformBuffer === undefined
477
+ ) {
478
+ return undefined;
479
+ }
480
+ const probeResources: SkylightBindGroupResources = {
481
+ irradianceView: skylightResources.irradianceView,
482
+ irradianceSampler: skylightResources.irradianceSampler,
483
+ prefilterView: row.filteredView,
484
+ prefilterSampler: row.sampler,
485
+ brdfLutView: skylightResources.brdfLutView,
486
+ brdfLutSampler: skylightResources.brdfLutSampler,
487
+ intensityBuffer: row.uniformBuffer,
488
+ };
489
+ return probeResources;
490
+ },
435
491
  materialBgShared: frameState.materialBgShared,
436
492
  materialBgAssemblyCache: c.materialBgAssemblyCache,
437
493
  bindGroupCounts,
@@ -614,7 +670,7 @@ export function recordMainPass(
614
670
  buildPerSubmeshMaterialBg(material, entityKey, materialWorld))
615
671
  : buildPerSubmeshMaterialBg(material, entityKey, materialWorld);
616
672
 
617
- pass.setBindGroup(0, viewBindGroup as BindGroup, [0]);
673
+ pass.setBindGroup(0, viewBindGroup as BindGroup, [viewBindGroupDynamicOffset, 0]);
618
674
 
619
675
  // Track which (mesh-vertex-buffer, mesh-index-buffer, pipeline) combo
620
676
  // was last bound so consecutive entities sharing the same combo skip