@forgeax/engine-render 0.1.20 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) hide show
  1. package/README.md +215 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/assembly/device-feature-admission.d.ts +18 -0
  4. package/dist/assembly/device-feature-admission.d.ts.map +1 -0
  5. package/dist/assembly/directional-shadow-inspection.d.ts +38 -0
  6. package/dist/assembly/directional-shadow-inspection.d.ts.map +1 -0
  7. package/dist/assembly/factory.d.ts +21 -18
  8. package/dist/assembly/factory.d.ts.map +1 -1
  9. package/dist/assembly/host-contract.d.ts +9 -0
  10. package/dist/assembly/host-contract.d.ts.map +1 -1
  11. package/dist/assembly/render-target-host.d.ts +46 -0
  12. package/dist/assembly/render-target-host.d.ts.map +1 -0
  13. package/dist/assets/render-asset-port.d.ts.map +1 -1
  14. package/dist/authoring.mjs +3 -0
  15. package/dist/authoring.mjs.map +1 -1
  16. package/dist/capture/cube-views.d.ts +21 -0
  17. package/dist/capture/cube-views.d.ts.map +1 -0
  18. package/dist/capture/scheduler.d.ts +54 -0
  19. package/dist/capture/scheduler.d.ts.map +1 -0
  20. package/dist/{chunk-4XINDLAY.mjs → chunk-5YADUCA7.mjs} +1552 -1361
  21. package/dist/chunk-5YADUCA7.mjs.map +1 -0
  22. package/dist/{chunk-TBQJOK2L.mjs → chunk-6OPXDQ2E.mjs} +364 -51
  23. package/dist/chunk-6OPXDQ2E.mjs.map +1 -0
  24. package/dist/{chunk-M2MP4ZOU.mjs → chunk-BXBQQKFQ.mjs} +3 -9
  25. package/dist/chunk-BXBQQKFQ.mjs.map +1 -0
  26. package/dist/{chunk-CDTWTHZR.mjs → chunk-FS2KNUQH.mjs} +548 -6
  27. package/dist/chunk-FS2KNUQH.mjs.map +1 -0
  28. package/dist/components/camera.d.ts +1 -0
  29. package/dist/components/camera.d.ts.map +1 -1
  30. package/dist/components/cube-camera.d.ts +20 -0
  31. package/dist/components/cube-camera.d.ts.map +1 -0
  32. package/dist/components/directional-light.d.ts +11 -2
  33. package/dist/components/directional-light.d.ts.map +1 -1
  34. package/dist/components/directional-shadow-filter.d.ts +25 -0
  35. package/dist/components/directional-shadow-filter.d.ts.map +1 -0
  36. package/dist/components/index.d.ts +2 -0
  37. package/dist/components/index.d.ts.map +1 -1
  38. package/dist/components/light-helpers.d.ts.map +1 -1
  39. package/dist/components/reflection-probe.d.ts +17 -0
  40. package/dist/components/reflection-probe.d.ts.map +1 -0
  41. package/dist/construct-renderer.mjs +5826 -1003
  42. package/dist/construct-renderer.mjs.map +1 -1
  43. package/dist/environment/frame.d.ts.map +1 -1
  44. package/dist/environment/lifecycle.d.ts +5 -0
  45. package/dist/environment/lifecycle.d.ts.map +1 -1
  46. package/dist/errors/render.d.ts +141 -93
  47. package/dist/errors/render.d.ts.map +1 -1
  48. package/dist/extract/directional-shadow-projection.d.ts +23 -0
  49. package/dist/extract/directional-shadow-projection.d.ts.map +1 -0
  50. package/dist/extract/environment.d.ts +14 -0
  51. package/dist/extract/environment.d.ts.map +1 -1
  52. package/dist/extract/visibility.d.ts.map +1 -1
  53. package/dist/features/fullscreen.d.ts.map +1 -1
  54. package/dist/features/targets.d.ts.map +1 -1
  55. package/dist/features/types.d.ts +10 -0
  56. package/dist/features/types.d.ts.map +1 -1
  57. package/dist/fullscreen-post-process-pass.d.ts +13 -2
  58. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  59. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  60. package/dist/gpu-driven/production-raster.d.ts +0 -1
  61. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  62. package/dist/gpu-scene.d.ts.map +1 -1
  63. package/dist/hdrp-buffers.d.ts +19 -17
  64. package/dist/hdrp-buffers.d.ts.map +1 -1
  65. package/dist/ibl/IblPipelineCache.d.ts +4 -0
  66. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  67. package/dist/ibl/kernel-cache.d.ts +36 -0
  68. package/dist/ibl/kernel-cache.d.ts.map +1 -0
  69. package/dist/index.d.ts +28 -3
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.mjs +4 -4
  72. package/dist/index.mjs.map +1 -1
  73. package/dist/inspection-types.d.ts +51 -0
  74. package/dist/inspection-types.d.ts.map +1 -1
  75. package/dist/instances-derived-bounds.d.ts +50 -0
  76. package/dist/instances-derived-bounds.d.ts.map +1 -0
  77. package/dist/internal.mjs +1 -1
  78. package/dist/light-buffer-layout.d.ts +10 -1
  79. package/dist/light-buffer-layout.d.ts.map +1 -1
  80. package/dist/mesh-material-bindings.d.ts +54 -0
  81. package/dist/mesh-material-bindings.d.ts.map +1 -1
  82. package/dist/pbr-pipeline.d.ts +14 -4
  83. package/dist/pbr-pipeline.d.ts.map +1 -1
  84. package/dist/pipeline/standard-forward-lane.d.ts +2 -1
  85. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  86. package/dist/pipeline/standard-lighting/errors.d.ts +4 -0
  87. package/dist/pipeline/standard-lighting/errors.d.ts.map +1 -0
  88. package/dist/pipeline/standard-lighting/graph.d.ts +24 -0
  89. package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -0
  90. package/dist/pipeline/standard-lighting/inspection.d.ts +38 -0
  91. package/dist/pipeline/standard-lighting/inspection.d.ts.map +1 -0
  92. package/dist/pipeline/standard-lighting/layout.d.ts +28 -0
  93. package/dist/pipeline/standard-lighting/layout.d.ts.map +1 -0
  94. package/dist/pipeline/standard-lighting/light-frame.d.ts +33 -0
  95. package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -0
  96. package/dist/pipeline/standard-lighting/membership.d.ts +14 -0
  97. package/dist/pipeline/standard-lighting/membership.d.ts.map +1 -0
  98. package/dist/pipeline/standard-lighting/prepare.d.ts +16 -0
  99. package/dist/pipeline/standard-lighting/prepare.d.ts.map +1 -0
  100. package/dist/pipeline/standard-lighting/topology.d.ts +45 -0
  101. package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -0
  102. package/dist/pipeline/standard-lighting/transport.d.ts +36 -0
  103. package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -0
  104. package/dist/pipeline/standard-pipeline.d.ts +4 -13
  105. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  106. package/dist/pipeline/standard-post.d.ts.map +1 -1
  107. package/dist/pipeline/standard-profile.d.ts +5 -26
  108. package/dist/pipeline/standard-profile.d.ts.map +1 -1
  109. package/dist/pipeline-spec.d.ts +40 -7
  110. package/dist/pipeline-spec.d.ts.map +1 -1
  111. package/dist/point-shadow-inspection.d.ts +21 -0
  112. package/dist/point-shadow-inspection.d.ts.map +1 -0
  113. package/dist/record/frame-lighting.d.ts +27 -32
  114. package/dist/record/frame-lighting.d.ts.map +1 -1
  115. package/dist/record/frame-snapshot.d.ts +28 -14
  116. package/dist/record/frame-snapshot.d.ts.map +1 -1
  117. package/dist/record/frame-targets.d.ts +5 -0
  118. package/dist/record/frame-targets.d.ts.map +1 -1
  119. package/dist/record/frame.d.ts +10 -4
  120. package/dist/record/frame.d.ts.map +1 -1
  121. package/dist/record/gpu-pass-timing/contract.d.ts +96 -0
  122. package/dist/record/gpu-pass-timing/contract.d.ts.map +1 -0
  123. package/dist/record/gpu-pass-timing/errors.d.ts +18 -0
  124. package/dist/record/gpu-pass-timing/errors.d.ts.map +1 -0
  125. package/dist/record/gpu-pass-timing/index.d.ts +8 -0
  126. package/dist/record/gpu-pass-timing/index.d.ts.map +1 -0
  127. package/dist/record/gpu-pass-timing/parser.d.ts +11 -0
  128. package/dist/record/gpu-pass-timing/parser.d.ts.map +1 -0
  129. package/dist/record/gpu-pass-timing/session.d.ts +43 -0
  130. package/dist/record/gpu-pass-timing/session.d.ts.map +1 -0
  131. package/dist/record/gpu-timing.d.ts +8 -3
  132. package/dist/record/gpu-timing.d.ts.map +1 -1
  133. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  134. package/dist/record/main-pass-material.d.ts +22 -1
  135. package/dist/record/main-pass-material.d.ts.map +1 -1
  136. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  137. package/dist/record/main-pass.d.ts +2 -0
  138. package/dist/record/main-pass.d.ts.map +1 -1
  139. package/dist/record/render-context.d.ts +43 -2
  140. package/dist/record/render-context.d.ts.map +1 -1
  141. package/dist/record/shadow-pass.d.ts.map +1 -1
  142. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  143. package/dist/record/typed-frame-graph.d.ts +72 -4
  144. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  145. package/dist/record/view-ubo.d.ts +7 -4
  146. package/dist/record/view-ubo.d.ts.map +1 -1
  147. package/dist/reflection/filter.d.ts +22 -0
  148. package/dist/reflection/filter.d.ts.map +1 -0
  149. package/dist/reflection/gpu-table.d.ts +25 -0
  150. package/dist/reflection/gpu-table.d.ts.map +1 -0
  151. package/dist/reflection/projection.d.ts +91 -0
  152. package/dist/reflection/projection.d.ts.map +1 -0
  153. package/dist/render-contract.d.ts +49 -7
  154. package/dist/render-contract.d.ts.map +1 -1
  155. package/dist/render-graph-primitives.d.ts +1 -0
  156. package/dist/render-graph-primitives.d.ts.map +1 -1
  157. package/dist/render-pipeline.d.ts +42 -2
  158. package/dist/render-pipeline.d.ts.map +1 -1
  159. package/dist/render-system-extract.d.ts +58 -17
  160. package/dist/render-system-extract.d.ts.map +1 -1
  161. package/dist/render-system.d.ts +17 -2
  162. package/dist/render-system.d.ts.map +1 -1
  163. package/dist/renderer.d.ts +43 -0
  164. package/dist/renderer.d.ts.map +1 -0
  165. package/dist/scene/render-scene.d.ts +14 -0
  166. package/dist/scene/render-scene.d.ts.map +1 -1
  167. package/dist/shadow-atlas.d.ts +3 -3
  168. package/dist/shadow-atlas.d.ts.map +1 -1
  169. package/dist/ssao-buffers.d.ts +3 -0
  170. package/dist/ssao-buffers.d.ts.map +1 -1
  171. package/dist/targets/contracts.d.ts +66 -0
  172. package/dist/targets/contracts.d.ts.map +1 -0
  173. package/dist/targets/graph-projection.d.ts +29 -0
  174. package/dist/targets/graph-projection.d.ts.map +1 -0
  175. package/dist/targets/material-source.d.ts +34 -0
  176. package/dist/targets/material-source.d.ts.map +1 -0
  177. package/dist/targets/owner.d.ts +32 -0
  178. package/dist/targets/owner.d.ts.map +1 -0
  179. package/dist/targets/physical.d.ts +18 -0
  180. package/dist/targets/physical.d.ts.map +1 -0
  181. package/dist/targets/readback.d.ts +39 -0
  182. package/dist/targets/readback.d.ts.map +1 -0
  183. package/dist/temporal/index.mjs +3 -3
  184. package/dist/typed-render-graph-primitives.d.ts +1 -1
  185. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  186. package/dist/typed-shadow-passes.d.ts +1 -1
  187. package/dist/typed-shadow-passes.d.ts.map +1 -1
  188. package/dist/volume/passes.d.ts +2 -2
  189. package/dist/volume/passes.d.ts.map +1 -1
  190. package/package.json +20 -20
  191. package/src/__tests__/bloom-exact-zero.unit.test.ts +48 -19
  192. package/src/__tests__/bloom-lifecycle.integration.test.ts +41 -13
  193. package/src/__tests__/camera-target-consumption.unit.test.ts +53 -0
  194. package/src/__tests__/cluster-binner.unit.test.ts +35 -0
  195. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +7 -2
  196. package/src/__tests__/cube-camera-scheduler.unit.test.ts +70 -0
  197. package/src/__tests__/cube-camera-single-submit.perf.test.ts +75 -0
  198. package/src/__tests__/cube-camera-view-matrices.unit.test.ts +41 -0
  199. package/src/__tests__/cube-camera-vocabulary.unit.test.ts +47 -0
  200. package/src/__tests__/cube-camera.integration.test.ts +42 -0
  201. package/src/__tests__/device-feature-admission.unit.test.ts +73 -0
  202. package/src/__tests__/direct-light-snapshot.unit.test.ts +3 -3
  203. package/src/__tests__/docs-gate-green.unit.test.ts +38 -0
  204. package/src/__tests__/factory-contract.integration.test.ts +255 -29
  205. package/src/__tests__/fog-component.unit.test.ts +67 -0
  206. package/src/__tests__/fog.integration.test.ts +86 -0
  207. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +37 -0
  208. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +10 -9
  209. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +17 -1
  210. package/src/__tests__/gpu-driven-production.integration.test.ts +16 -17
  211. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +12 -4
  212. package/src/__tests__/gpu-pass-timing-browser-runner.ts +281 -0
  213. package/src/__tests__/gpu-pass-timing-coexistence.integration.test.ts +80 -0
  214. package/src/__tests__/gpu-pass-timing-contract.unit.test.ts +243 -0
  215. package/src/__tests__/gpu-pass-timing-exact-zero.unit.test.ts +131 -0
  216. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +230 -0
  217. package/src/__tests__/gpu-pass-timing-observation.unit.test.ts +233 -0
  218. package/src/__tests__/gpu-pass-timing-session.unit.test.ts +438 -0
  219. package/src/__tests__/gpu-pass-timing.browser.test.ts +26 -0
  220. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +125 -0
  221. package/src/__tests__/hdrp-group2-visibility.unit.test.ts +24 -0
  222. package/src/__tests__/instances-culling.integration.test.ts +94 -0
  223. package/src/__tests__/instances-derived-bounds.unit.test.ts +118 -0
  224. package/src/__tests__/material-observation.integration.test.ts +101 -0
  225. package/src/__tests__/pbr-view-layout.unit.test.ts +16 -0
  226. package/src/__tests__/point-shadow-inspection.unit.test.ts +60 -0
  227. package/src/__tests__/public-surface.test-d.ts +10 -0
  228. package/src/__tests__/reflection-probe-authoring.unit.test.ts +71 -0
  229. package/src/__tests__/reflection-probe-filter.unit.test.ts +54 -0
  230. package/src/__tests__/reflection-probe-ibl-topology.unit.test.ts +45 -0
  231. package/src/__tests__/reflection-probe-pass-encode.integration.test.ts +101 -0
  232. package/src/__tests__/reflection-probe-performance.perf.test.ts +60 -0
  233. package/src/__tests__/reflection-probe-recovery.integration.test.ts +55 -0
  234. package/src/__tests__/reflection-probe-selection.unit.test.ts +46 -0
  235. package/src/__tests__/render-error-code-owner.test-d.ts +18 -3
  236. package/src/__tests__/render-feature-docs.unit.test.ts +1 -1
  237. package/src/__tests__/render-target-admission.unit.test.ts +80 -0
  238. package/src/__tests__/render-target-graph-projection.unit.test.ts +102 -0
  239. package/src/__tests__/render-target-inspection.unit.test.ts +52 -0
  240. package/src/__tests__/render-target-material-source.test-d.ts +30 -0
  241. package/src/__tests__/render-target-material-source.unit.test.ts +97 -0
  242. package/src/__tests__/render-target-owner-placement.unit.test.ts +203 -0
  243. package/src/__tests__/render-target-owner.unit.test.ts +122 -0
  244. package/src/__tests__/render-target-probe-soak.perf.test.ts +94 -0
  245. package/src/__tests__/render-target-public-schema.json +107 -0
  246. package/src/__tests__/render-target-public-schema.unit.test.ts +91 -0
  247. package/src/__tests__/render-target-public-surface.test-d.ts +83 -0
  248. package/src/__tests__/render-target-readback.integration.test.ts +90 -0
  249. package/src/__tests__/render-target-recovery.integration.test.ts +85 -0
  250. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +48 -0
  251. package/src/__tests__/renderer-lifecycle.integration.test.ts +14 -0
  252. package/src/__tests__/shader-stage-surface.unit.test.ts +19 -4
  253. package/src/__tests__/skin-motion-regression.unit.test.ts +132 -0
  254. package/src/__tests__/skinned-shadow-caster.test.ts +11 -2
  255. package/src/__tests__/standard-cluster-capability.integration.test.ts +63 -0
  256. package/src/__tests__/standard-cluster-transport.unit.test.ts +121 -0
  257. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +232 -0
  258. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +8 -5
  259. package/src/__tests__/standard-lighting-inspection.integration.test.ts +138 -0
  260. package/src/__tests__/standard-lighting-ssot.unit.test.ts +18 -0
  261. package/src/__tests__/standard-lighting-write.unit.test.ts +377 -0
  262. package/src/__tests__/standard-lighting.integration.test.ts +67 -0
  263. package/src/__tests__/standard-pipeline.dawn.test.ts +206 -5
  264. package/src/__tests__/standard-pipeline.integration.test.ts +181 -150
  265. package/src/__tests__/standard-probe-binding.unit.test.ts +25 -0
  266. package/src/__tests__/standard-profile-oracle.unit.test.ts +23 -38
  267. package/src/__tests__/typed-pipeline-topology.unit.test.ts +395 -14
  268. package/src/__tests__/urp-pipeline-targets.unit.test.ts +2 -2
  269. package/src/__tests__/visibility-resolver.unit.test.ts +15 -0
  270. package/src/assembly/device-feature-admission.ts +73 -0
  271. package/src/assembly/directional-shadow-inspection.ts +122 -0
  272. package/src/assembly/factory.ts +877 -286
  273. package/src/assembly/host-contract.ts +25 -0
  274. package/src/assembly/render-target-host.ts +847 -0
  275. package/src/assets/asset-decoders.ts +1 -1
  276. package/src/assets/render-asset-port.ts +7 -1
  277. package/src/capture/cube-views.ts +69 -0
  278. package/src/capture/scheduler.ts +265 -0
  279. package/src/components/__tests__/schema-contract.unit.test.ts +12 -0
  280. package/src/components/__tests__/schema-parity.unit.test.ts +9 -0
  281. package/src/components/camera.ts +4 -0
  282. package/src/components/cube-camera.ts +52 -0
  283. package/src/components/directional-light.ts +22 -2
  284. package/src/components/directional-shadow-filter.ts +57 -0
  285. package/src/components/index.ts +2 -0
  286. package/src/components/light-helpers.ts +81 -7
  287. package/src/components/reflection-probe.ts +49 -0
  288. package/src/environment/frame.ts +6 -24
  289. package/src/environment/lifecycle.ts +12 -0
  290. package/src/errors/render.ts +286 -142
  291. package/src/extract/directional-shadow-projection.ts +61 -0
  292. package/src/extract/environment.ts +57 -0
  293. package/src/extract/visibility.ts +25 -1
  294. package/src/features/fullscreen.ts +3 -0
  295. package/src/features/targets.ts +4 -1
  296. package/src/features/types.ts +10 -0
  297. package/src/fullscreen-post-process-pass.ts +43 -2
  298. package/src/gpu-driven/batch-topology.ts +1 -0
  299. package/src/gpu-driven/production-raster.ts +1 -3
  300. package/src/gpu-scene.ts +8 -1
  301. package/src/hdrp-buffers.ts +165 -59
  302. package/src/ibl/IblPipelineCache.ts +11 -0
  303. package/src/ibl/kernel-cache.ts +88 -0
  304. package/src/index.ts +148 -0
  305. package/src/inspection-types.ts +56 -0
  306. package/src/instances-derived-bounds.ts +171 -0
  307. package/src/light-buffer-layout.ts +20 -5
  308. package/src/mesh-material-bindings.ts +132 -0
  309. package/src/pbr-pipeline.ts +106 -17
  310. package/src/pipeline/standard-forward-lane.ts +46 -16
  311. package/src/pipeline/standard-lighting/errors.ts +6 -0
  312. package/src/pipeline/standard-lighting/graph.ts +157 -0
  313. package/src/pipeline/standard-lighting/inspection.ts +88 -0
  314. package/src/pipeline/standard-lighting/layout.ts +31 -0
  315. package/src/pipeline/standard-lighting/light-frame.ts +34 -0
  316. package/src/pipeline/standard-lighting/membership.ts +43 -0
  317. package/src/pipeline/standard-lighting/prepare.ts +37 -0
  318. package/src/pipeline/standard-lighting/topology.ts +120 -0
  319. package/src/pipeline/standard-lighting/transport.ts +77 -0
  320. package/src/pipeline/standard-pipeline.ts +122 -193
  321. package/src/pipeline/standard-post.ts +26 -7
  322. package/src/pipeline/standard-profile.ts +17 -47
  323. package/src/pipeline-spec.ts +84 -21
  324. package/src/point-shadow-inspection.ts +55 -0
  325. package/src/record/__tests__/temporal-payload.unit.test.ts +42 -1
  326. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +73 -0
  327. package/src/record/__tests__/view-ubo-layout.unit.test.ts +40 -0
  328. package/src/record/frame-lighting.ts +367 -427
  329. package/src/record/frame-snapshot.ts +35 -27
  330. package/src/record/frame-targets.ts +17 -0
  331. package/src/record/frame.ts +261 -74
  332. package/src/record/gpu-pass-timing/contract.ts +180 -0
  333. package/src/record/gpu-pass-timing/errors.ts +34 -0
  334. package/src/record/gpu-pass-timing/index.ts +37 -0
  335. package/src/record/gpu-pass-timing/parser.ts +97 -0
  336. package/src/record/gpu-pass-timing/session.ts +938 -0
  337. package/src/record/gpu-timing.ts +21 -12
  338. package/src/record/main-pass-geometry.ts +120 -80
  339. package/src/record/main-pass-material.ts +95 -9
  340. package/src/record/main-pass-sprite-draws.ts +54 -25
  341. package/src/record/main-pass.ts +71 -15
  342. package/src/record/render-context.ts +72 -0
  343. package/src/record/shadow-pass.ts +17 -28
  344. package/src/record/skybox-post-pass.ts +20 -4
  345. package/src/record/typed-frame-graph.ts +893 -44
  346. package/src/record/view-ubo.ts +63 -28
  347. package/src/reflection/filter.ts +81 -0
  348. package/src/reflection/gpu-table.ts +44 -0
  349. package/src/reflection/projection.ts +281 -0
  350. package/src/render-contract.ts +76 -5
  351. package/src/render-graph-primitives.ts +9 -4
  352. package/src/render-pipeline.ts +117 -2
  353. package/src/render-system-extract.ts +557 -100
  354. package/src/render-system.ts +1095 -66
  355. package/src/renderer.ts +222 -0
  356. package/src/scene/render-scene.ts +115 -9
  357. package/src/shadow-atlas.ts +5 -5
  358. package/src/ssao-buffers.ts +9 -2
  359. package/src/targets/contracts.ts +182 -0
  360. package/src/targets/graph-projection.ts +169 -0
  361. package/src/targets/material-source.ts +143 -0
  362. package/src/targets/owner.ts +214 -0
  363. package/src/targets/physical.ts +18 -0
  364. package/src/targets/readback.ts +153 -0
  365. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +59 -20
  366. package/src/typed-render-graph-primitives.ts +5 -3
  367. package/src/typed-shadow-passes.ts +46 -40
  368. package/src/volume/passes.ts +15 -9
  369. package/dist/chunk-4XINDLAY.mjs.map +0 -1
  370. package/dist/chunk-CDTWTHZR.mjs.map +0 -1
  371. package/dist/chunk-M2MP4ZOU.mjs.map +0 -1
  372. package/dist/chunk-TBQJOK2L.mjs.map +0 -1
@@ -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
@@ -11,6 +11,7 @@ import type {
11
11
  PipelineLayout,
12
12
  Result,
13
13
  RhiCanvasContext,
14
+ RhiCommandEncoder,
14
15
  RhiDevice,
15
16
  RenderPipeline as RhiRenderPipeline,
16
17
  Sampler,
@@ -41,10 +42,20 @@ import {
41
42
  import type { SkylightFallback } from '../ibl/skylight-bind-group';
42
43
  import type { BloomInspection } from '../inspection-types';
43
44
  import type { HealthListenerRegistry, RhiErrorListenerRegistry } from '../lifecycle';
45
+ import type { PipelineGroup2Contract } from '../pbr-pipeline';
46
+ import type { StandardTopologyInputValue } from '../pipeline/standard-lighting/topology';
44
47
  import type { StandardProfile } from '../pipeline/standard-profile';
45
48
  import type { PipelineBuilderShaderModuleFactory } from '../pipeline-builder';
46
49
  import type { PointsLinesInspection } from '../points-lines/inspection';
47
50
  import type { PointsLinesRecordPlan } from '../points-lines/record';
51
+ import type { ReflectionProbeTable } from '../reflection/gpu-table';
52
+ import type { ReflectionProbeSelectionResult } from '../reflection/projection';
53
+ import type { GpuPassTimingReason } from './gpu-pass-timing/errors';
54
+ import type {
55
+ GpuPassTimingCapture,
56
+ GpuPassTimingFrameIdentity,
57
+ GpuPassTimingSession,
58
+ } from './gpu-pass-timing/session';
48
59
 
49
60
  export interface PointsLinesRecordSubmission {
50
61
  readonly plan: PointsLinesRecordPlan;
@@ -53,6 +64,18 @@ export interface PointsLinesRecordSubmission {
53
64
  readonly layoutProjection: VertexLayoutProjection;
54
65
  }
55
66
 
67
+ export interface ReflectionProbeRecordState {
68
+ readonly table: ReflectionProbeTable;
69
+ readonly selections: ReadonlyMap<string, ReflectionProbeSelectionResult>;
70
+ readonly graph: import('./typed-frame-graph').ReflectionProbeGraphState;
71
+ readonly completeSubmission: (submitted: boolean, completed?: Promise<unknown>) => void;
72
+ }
73
+
74
+ export interface MaterialShaderPipelineEntry {
75
+ readonly pipeline: RhiRenderPipeline;
76
+ readonly group2Contract: PipelineGroup2Contract;
77
+ }
78
+
56
79
  import type {
57
80
  RenderDebugOverlay,
58
81
  RenderPipelineContext,
@@ -66,6 +89,8 @@ import type {
66
89
  SkylightSnapshot,
67
90
  } from '../render-system-extract';
68
91
  import type { SkinPaletteAllocator } from '../systems/skin-palette-allocator';
92
+ import type { RenderTarget } from '../targets/contracts';
93
+ import type { RenderTargetPhysical } from '../targets/physical';
69
94
  import type {
70
95
  BindGroupCounts,
71
96
  BloomFrameReceipts,
@@ -102,6 +127,23 @@ export interface RenderSystemRuntime {
102
127
  shaderModuleMode?: RenderFeatureShaderModuleMode,
103
128
  layoutKindOverride?: 'pbr' | 'pbr-skin',
104
129
  ) => RhiRenderPipeline | null;
130
+ readonly getMaterialShaderPipelineEntry?: (
131
+ materialShaderId: string,
132
+ isHdr: boolean,
133
+ renderState?: MaterialRenderState,
134
+ topology?: PrimitiveTopology,
135
+ indexFormat?: 'uint16' | 'uint32',
136
+ variantSet?: string,
137
+ passKind?: PassKind,
138
+ meshAttributes?: VertexAttributeMap,
139
+ sampleCount?: number,
140
+ colorFormatOverride?: GPUTextureFormat,
141
+ shaderUvSetCount?: number,
142
+ depthFormatOverride?: GPUTextureFormat | null,
143
+ vertexLayout?: string,
144
+ vertexLayoutProjection?: VertexLayoutProjection,
145
+ shaderModuleMode?: RenderFeatureShaderModuleMode,
146
+ ) => MaterialShaderPipelineEntry | null;
105
147
  readonly getMaterialShaderBindingContract?: (
106
148
  materialShaderId: string,
107
149
  ) => 'group-0' | 'group-0-resource' | 'view-only' | 'view-and-scene-depth' | 'render-material';
@@ -116,10 +158,15 @@ export interface RenderSystemRuntime {
116
158
  id: string,
117
159
  bgl: BindGroupLayout,
118
160
  colorFormat: GPUTextureFormat,
161
+ entry?: import('../fullscreen-post-process-pass').PostProcessShaderEntry,
119
162
  ) => RhiRenderPipeline | null;
120
163
  readonly clearPostProcessPipelineCache?: (id: string) => void;
121
164
  readonly invalidateShaderModule?: (label: string) => void;
122
165
  readonly dynamicTextureStore?: DynamicTextureStore | undefined;
166
+ /** Resolves a render-target source to the active generation-owned GPU view. */
167
+ readonly resolveRenderTargetTextureSource?: (
168
+ source: import('../targets/contracts').RenderTargetTextureSource,
169
+ ) => import('../targets/material-source').RenderTargetMaterialSourceBinding | undefined;
123
170
  }
124
171
 
125
172
  /** Renderer-owned bridge from retained Points/Lines facts into main geometry. */
@@ -231,6 +278,8 @@ export interface BloomPersistentBundle {
231
278
  }
232
279
 
233
280
  export interface PipelineState {
281
+ /** Device that owns every opaque handle in this pipeline state. */
282
+ readonly device: RhiDevice;
234
283
  readonly meshes: ReadonlyMap<number, MeshGpuHandles>;
235
284
  readonly format: TextureFormat;
236
285
  readonly colorAttachmentFormat: TextureFormat;
@@ -263,6 +312,8 @@ export interface PipelineState {
263
312
  readonly hdrpClusterMembershipBindGroupLayout: BindGroupLayout | null;
264
313
  readonly pbrSkinPipelineLayout: PipelineLayout | null;
265
314
  readonly pbrSkinMeshBindGroupLayout: BindGroupLayout | null;
315
+ readonly hdrpSkinPipelineLayout: PipelineLayout | null;
316
+ readonly hdrpSkinMeshBindGroupLayout: BindGroupLayout | null;
266
317
  readonly skinPaletteAllocator: SkinPaletteAllocator | null;
267
318
  readonly defaultWhiteTextureView: TextureView;
268
319
  readonly defaultNormalTextureView: TextureView;
@@ -280,6 +331,12 @@ export interface RenderSystemInternals extends RenderSystemRuntime {
280
331
  readonly standardPipeline: RenderFrameState['activePipeline'];
281
332
  readonly featureHost?: RenderFeatureHost | undefined;
282
333
  readonly shaderModuleFactory?: PipelineBuilderShaderModuleFactory;
334
+ readonly createShaderModule?: (
335
+ device: import('@forgeax/engine-rhi').RhiDevice,
336
+ descriptor: { readonly code: string; readonly label?: string },
337
+ ) => Promise<
338
+ Result<import('@forgeax/engine-rhi').ShaderModule, import('@forgeax/engine-rhi').RhiError>
339
+ >;
283
340
  /** Optional handle-first factory for feature-owned generated shader programs. */
284
341
  readonly immediateShaderModuleFactory?: PipelineBuilderShaderModuleFactory;
285
342
  readonly profiler?: Profiler | undefined;
@@ -305,6 +362,16 @@ export interface RenderSystemInternals extends RenderSystemRuntime {
305
362
  label: string,
306
363
  ) => RhiRenderPipeline | null)
307
364
  | undefined;
365
+ gpuPassTimingSession?: GpuPassTimingSession | undefined;
366
+ gpuPassTimingCapture?: GpuPassTimingCapture | undefined;
367
+ gpuPassTimingSubmittedWork?: Promise<void> | undefined;
368
+ gpuPassTimingFrameIdentity?: GpuPassTimingFrameIdentity | undefined;
369
+ gpuPassTimingBeginReason?: GpuPassTimingReason | undefined;
370
+ readonly getRenderTargetPhysical?: (target: RenderTarget) => RenderTargetPhysical | undefined;
371
+ readonly encodeRenderTargetReadbacks?: (
372
+ encoder: RhiCommandEncoder,
373
+ faces?: readonly number[],
374
+ ) => void;
308
375
  /** Composed renderer utility sources installed atomically at build-ready. */
309
376
  volumetricFogShaders?: VolumetricFogShaderSources | undefined;
310
377
  }
@@ -318,6 +385,8 @@ export interface _StandardForwardSceneView {
318
385
  readonly validated: readonly ValidatedRenderable[];
319
386
  readonly validatedOrdered: readonly ValidatedRenderable[];
320
387
  readonly viewBindGroup: BindGroup | null;
388
+ /** Dynamic View-UBO slot used by auxiliary cube/probe face cameras. */
389
+ readonly viewBindGroupDynamicOffset?: number;
321
390
  readonly meshBindGroup: BindGroup | null;
322
391
  readonly frameState: RenderFrameState;
323
392
  readonly dispatchCounts: DispatchCounts;
@@ -340,6 +409,8 @@ export interface _StandardForwardSceneView {
340
409
  readonly dispatch: readonly DispatchEntry[];
341
410
  readonly hdrpClusterBindGroup: BindGroup | null;
342
411
  readonly hdrpClusterMembershipBindGroup: BindGroup | null;
412
+ /** Prepared Standard lighting declaration accepted by this graph frame. */
413
+ readonly standardLighting?: StandardTopologyInputValue;
343
414
  readonly foldDispatchPlan: FoldDispatchPlan | null;
344
415
  readonly materialSlotIndices: readonly (readonly number[])[];
345
416
  readonly materialSlots: readonly MaterialSnapshot[];
@@ -347,6 +418,7 @@ export interface _StandardForwardSceneView {
347
418
  readonly materialSlotCount: number;
348
419
  readonly pointsLines: PointsLinesRecordOwner | undefined;
349
420
  readonly materialBgAssemblyCache: Map<string, MaterialBgAssemblyCacheEntry>;
421
+ readonly reflectionProbes?: ReflectionProbeRecordState;
350
422
  materialUboPayloadCache?: {
351
423
  readonly materialSlots: readonly MaterialSnapshot[];
352
424
  readonly materialSlotCount: number;
@@ -18,6 +18,8 @@ import { assembleMaterialWithSkylightEntries } from '../ibl/skylight-bind-group'
18
18
  import type { InstanceBufferCacheEntry } from '../instance-buffer-cache';
19
19
  import {
20
20
  buildPbrMaterialUserRegionEntries,
21
+ createPbrSkinMeshBindGroupEntries,
22
+ pbrSkinMeshDynamicOffsets,
21
23
  SHADOW_CASTER_SHADER_ID,
22
24
  shadowCasterVariantSet,
23
25
  } from '../pbr-pipeline';
@@ -290,27 +292,12 @@ function skinnedShadowMeshBindGroup(
290
292
  const created = c.runtime.device.createBindGroup({
291
293
  label: 'shadow-pbr-skin-mesh-bg',
292
294
  layout,
293
- entries: [
294
- {
295
- binding: 0,
296
- resource: {
297
- kind: 'buffer',
298
- value: {
299
- buffer: c.pipelineState.meshStorageBuffer.buffer,
300
- size: c.runtime.device.caps.storageBuffer
301
- ? MESH_SSBO_BYTES
302
- : MESH_UBO_FULL_ARRAY_BYTES,
303
- },
304
- },
305
- },
306
- {
307
- binding: 1,
308
- resource: {
309
- kind: 'buffer',
310
- value: { buffer: skin.buffer, size: allocator.bindingWindowBytes },
311
- },
312
- },
313
- ],
295
+ entries: createPbrSkinMeshBindGroupEntries(
296
+ c.pipelineState.meshStorageBuffer.buffer,
297
+ c.runtime.device.caps.storageBuffer ? MESH_SSBO_BYTES : MESH_UBO_FULL_ARRAY_BYTES,
298
+ skin.buffer,
299
+ allocator.bindingWindowBytes,
300
+ ),
314
301
  });
315
302
  if (!created.ok) throw created.error;
316
303
  return created.value;
@@ -338,7 +325,7 @@ export function encodeDirectionalShadowPass(
338
325
  c.frameState.directionalShadowCacheRecorded = true;
339
326
  pass.setViewport(viewport.x, viewport.y, viewport.w, viewport.h, 0, 1);
340
327
  pass.setPipeline(pipeline);
341
- pass.setBindGroup(0, viewBg, [0]);
328
+ pass.setBindGroup(0, viewBg, [0, 0]);
342
329
  pass.setBindGroup(1, materialBg, [0]);
343
330
  recordShadowCasterDraws(
344
331
  c,
@@ -368,7 +355,7 @@ export function encodePointShadowPass(
368
355
  const materialBg = ensureSpotShadowMaterialBg(c);
369
356
  if (pipeline === null || viewBg === null || materialBg === null) return;
370
357
  pass.setPipeline(pipeline);
371
- pass.setBindGroup(0, viewBg, [0]);
358
+ pass.setBindGroup(0, viewBg, [0, 0]);
372
359
  pass.setBindGroup(1, materialBg, [0]);
373
360
  recordShadowCasterDraws(
374
361
  c,
@@ -416,7 +403,7 @@ export function encodeSpotShadowPass(
416
403
  1,
417
404
  );
418
405
  pass.setPipeline(pipeline);
419
- pass.setBindGroup(0, viewBg, [0]);
406
+ pass.setBindGroup(0, viewBg, [0, 0]);
420
407
  pass.setBindGroup(1, materialBg, [0]);
421
408
  // Reuse the shared caster recorder. The compact spot-only loop used to
422
409
  // depend on instance bindings created by a directional pass, so a scene
@@ -594,10 +581,11 @@ function recordShadowCasterDraws(
594
581
  if (entry.source.skin !== undefined) {
595
582
  const skinMeshBindGroup = skinnedShadowMeshBindGroup(c, entry);
596
583
  if (skinMeshBindGroup === null) continue;
597
- shadowPass.setBindGroup(2, skinMeshBindGroup, [
598
- i * MESH_PER_ENTITY_STRIDE,
599
- entry.source.skin.byteOffset,
600
- ]);
584
+ shadowPass.setBindGroup(
585
+ 2,
586
+ skinMeshBindGroup,
587
+ pbrSkinMeshDynamicOffsets(i * MESH_PER_ENTITY_STRIDE, entry.source.skin.byteOffset),
588
+ );
601
589
  } else {
602
590
  shadowPass.setBindGroup(2, shadowMeshBindGroup, [i * MESH_PER_ENTITY_STRIDE]);
603
591
  }
@@ -606,6 +594,7 @@ function recordShadowCasterDraws(
606
594
  let shadowInstanceBuffer: Buffer = pipelineState.identityInstanceBuffer;
607
595
  let shadowInstanceCount = 1;
608
596
  const shadowInst = entry.source.instances;
597
+ if (shadowInst?.instanceCount === 0) continue;
609
598
  if (shadowInst !== undefined) {
610
599
  const uniformFallback = runtime.device.caps.storageBuffer === false;
611
600
  // Over-cap uniform fallback can't fit the per-instance window — bind
@@ -2,6 +2,8 @@ import type { ResolveContext } from '@forgeax/engine-render-graph';
2
2
  import { RhiError, type RhiRenderPassEncoder, type TextureView } from '@forgeax/engine-rhi';
3
3
  import { toShared } from '@forgeax/engine-types';
4
4
  import { buildBeginRenderPassDescriptor } from '../pipeline-spec';
5
+ import { FXAA_POST_PROCESS_ID } from '../render-contract';
6
+ import { resolveOutputDither } from '../render-pipeline';
5
7
  import type { BloomFrameReceipts } from './frame-snapshot';
6
8
  import { getOrCreateFromChain } from './mesh-ssbo';
7
9
  import type { _InternalRenderPipelineContext } from './render-context';
@@ -579,19 +581,29 @@ export function recordFxaaPass(
579
581
  fxaaActive &&
580
582
  pipelineState.perPassResources.fxaaPipeline !== null &&
581
583
  pipelineState.perPassResources.fxaaBindGroupLayout !== null &&
582
- pipelineState.perPassResources.fxaaSampler !== null
584
+ pipelineState.perPassResources.fxaaSampler !== null &&
585
+ runtime.getPostProcessParamsBuffer !== undefined
583
586
  ) {
584
587
  const inputView = resolve.resolve('ldrColor') as TextureView | undefined;
585
588
  if (inputView === undefined) return;
586
589
 
587
- // Compose the 2-entry FXAA BindGroup (input texture + sampler). The
590
+ const fxaaParams = runtime.getPostProcessParamsBuffer(FXAA_POST_PROCESS_ID);
591
+ if (fxaaParams === undefined) return;
592
+ const params = new Float32Array(4);
593
+ params[0] = resolveOutputDither(c.frameState.installedPipelineConfig) ? 1 : 0;
594
+ const paramsWrite = runtime.device.queue.writeBuffer(fxaaParams, 0, params);
595
+ if (!paramsWrite.ok) throw paramsWrite.error;
596
+
597
+ // Compose the 3-entry FXAA BindGroup (input texture + sampler + params).
588
598
  // graph view identity changes when the graph reallocates on resize, so
589
- // the identity-keyed cache rebuilds against the live target.
599
+ // the identity-keyed cache rebuilds against the live target. The params
600
+ // buffer is also a key so a device recovery cannot retain a bind group
601
+ // pointing at the retired resource.
590
602
  const fxaaBglLayout = pipelineState.perPassResources.fxaaBindGroupLayout;
591
603
  const fxaaSampler = pipelineState.perPassResources.fxaaSampler;
592
604
  const fxaaBg = getOrCreateFromChain(
593
605
  c.frameState.postProcessBgCache,
594
- [inputView],
606
+ [inputView, fxaaParams],
595
607
  'fxaa',
596
608
  () => {
597
609
  const fxaaBgRes = runtime.device.createBindGroup({
@@ -609,6 +621,10 @@ export function recordFxaaPass(
609
621
  binding: 1,
610
622
  resource: { kind: 'sampler', value: fxaaSampler },
611
623
  },
624
+ {
625
+ binding: 2,
626
+ resource: { kind: 'buffer', value: { buffer: fxaaParams } },
627
+ },
612
628
  ],
613
629
  });
614
630
  if (!fxaaBgRes.ok) throw fxaaBgRes.error;