@forgeax/engine-render 0.1.32 → 0.1.34

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 (624) hide show
  1. package/README.md +1374 -142
  2. package/dist/assembly/analytic-fog-registration.d.ts +3 -0
  3. package/dist/assembly/analytic-fog-registration.d.ts.map +1 -0
  4. package/dist/assembly/backend-contract.d.ts +2 -0
  5. package/dist/assembly/backend-contract.d.ts.map +1 -1
  6. package/dist/assembly/barrel-distortion-inspection.d.ts +3 -0
  7. package/dist/assembly/barrel-distortion-inspection.d.ts.map +1 -0
  8. package/dist/assembly/canvas-draw-guard.d.ts +3 -0
  9. package/dist/assembly/canvas-draw-guard.d.ts.map +1 -0
  10. package/dist/assembly/cloud-shadow-entry.d.ts +4 -0
  11. package/dist/assembly/cloud-shadow-entry.d.ts.map +1 -0
  12. package/dist/assembly/dynamic-geometry-host.d.ts +4 -1
  13. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
  14. package/dist/assembly/dynamic-geometry-runtime.d.ts +1 -1
  15. package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -1
  16. package/dist/assembly/factory.d.ts +1 -1
  17. package/dist/assembly/factory.d.ts.map +1 -1
  18. package/dist/assembly/fxaa-registration.d.ts +4 -0
  19. package/dist/assembly/fxaa-registration.d.ts.map +1 -0
  20. package/dist/assembly/host-contract.d.ts +3 -3
  21. package/dist/assembly/host-contract.d.ts.map +1 -1
  22. package/dist/assembly/lod-observation.d.ts +4 -0
  23. package/dist/assembly/lod-observation.d.ts.map +1 -0
  24. package/dist/assembly/material/assembly.d.ts.map +1 -1
  25. package/dist/assembly/material/pipeline-helpers.d.ts +5 -0
  26. package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -1
  27. package/dist/assembly/material-shader-policy.d.ts +16 -2
  28. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  29. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -1
  30. package/dist/assembly/motion-blur-registration.d.ts +3 -0
  31. package/dist/assembly/motion-blur-registration.d.ts.map +1 -0
  32. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
  33. package/dist/assembly/recovery/renderer-recover.d.ts +2 -0
  34. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  35. package/dist/assembly/render-feature-post-processes.d.ts +6 -0
  36. package/dist/assembly/render-feature-post-processes.d.ts.map +1 -0
  37. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  38. package/dist/assembly/renderer-feature-inspection.d.ts +1 -1
  39. package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -1
  40. package/dist/assembly/renderer-frame-transaction.d.ts +12 -0
  41. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  42. package/dist/assembly/renderer-inspection.d.ts +11 -0
  43. package/dist/assembly/renderer-inspection.d.ts.map +1 -1
  44. package/dist/assembly/temporal-post-process-prewarm.d.ts +13 -0
  45. package/dist/assembly/temporal-post-process-prewarm.d.ts.map +1 -0
  46. package/dist/assembly/webgpu-pbr-ready.d.ts +1 -0
  47. package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -1
  48. package/dist/assembly/webgpu-ready-contract.d.ts +4 -3
  49. package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -1
  50. package/dist/assembly/webgpu-ready-per-pass.d.ts +39 -0
  51. package/dist/assembly/webgpu-ready-per-pass.d.ts.map +1 -0
  52. package/dist/assembly/webgpu-ready.d.ts +2 -2
  53. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  54. package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -1
  55. package/dist/assembly/webgpu-renderer-observation-owner.d.ts +35 -0
  56. package/dist/assembly/webgpu-renderer-observation-owner.d.ts.map +1 -0
  57. package/dist/assembly/webgpu-renderer.d.ts +1 -1
  58. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  59. package/dist/authoring.mjs +1 -1
  60. package/dist/barrel-distortion.d.ts +47 -0
  61. package/dist/barrel-distortion.d.ts.map +1 -0
  62. package/dist/bloom-admission.d.ts +13 -0
  63. package/dist/bloom-admission.d.ts.map +1 -0
  64. package/dist/{chunk-PA5DRJNI.mjs → chunk-ELJFJ2BU.mjs} +2 -2
  65. package/dist/chunk-ELJFJ2BU.mjs.map +1 -0
  66. package/dist/{chunk-JCIRYQ4V.mjs → chunk-HEB564WF.mjs} +10 -4
  67. package/dist/chunk-HEB564WF.mjs.map +1 -0
  68. package/dist/{chunk-JRIPCMYC.mjs → chunk-MKDIT2EM.mjs} +1584 -375
  69. package/dist/chunk-MKDIT2EM.mjs.map +1 -0
  70. package/dist/{chunk-S665NJNF.mjs → chunk-OCDLEI6I.mjs} +3 -3
  71. package/dist/{chunk-S665NJNF.mjs.map → chunk-OCDLEI6I.mjs.map} +1 -1
  72. package/dist/chunk-ODZWR6JQ.mjs +4927 -0
  73. package/dist/chunk-ODZWR6JQ.mjs.map +1 -0
  74. package/dist/{chunk-DRR7D3KX.mjs → chunk-SHFUFJ5N.mjs} +192 -18
  75. package/dist/chunk-SHFUFJ5N.mjs.map +1 -0
  76. package/dist/{chunk-4NNZQCL3.mjs → chunk-UI5SFODX.mjs} +223 -277
  77. package/dist/chunk-UI5SFODX.mjs.map +1 -0
  78. package/dist/{chunk-GWCEMDVR.mjs → chunk-UINHLCZA.mjs} +787 -80
  79. package/dist/chunk-UINHLCZA.mjs.map +1 -0
  80. package/dist/{chunk-6TFBXLRJ.mjs → chunk-WEX45HKH.mjs} +222 -337
  81. package/dist/chunk-WEX45HKH.mjs.map +1 -0
  82. package/dist/cloud/density.d.ts +55 -0
  83. package/dist/cloud/density.d.ts.map +1 -0
  84. package/dist/cloud/extract.d.ts +25 -0
  85. package/dist/cloud/extract.d.ts.map +1 -0
  86. package/dist/cloud/feature.d.ts +57 -0
  87. package/dist/cloud/feature.d.ts.map +1 -0
  88. package/dist/cloud/frame-context.d.ts +8 -0
  89. package/dist/cloud/frame-context.d.ts.map +1 -0
  90. package/dist/cloud/inspection.d.ts +59 -0
  91. package/dist/cloud/inspection.d.ts.map +1 -0
  92. package/dist/cloud/optics.d.ts +56 -0
  93. package/dist/cloud/optics.d.ts.map +1 -0
  94. package/dist/cloud/parameters.d.ts +73 -0
  95. package/dist/cloud/parameters.d.ts.map +1 -0
  96. package/dist/cloud/resources.d.ts +60 -0
  97. package/dist/cloud/resources.d.ts.map +1 -0
  98. package/dist/cloud/shadow.d.ts +42 -0
  99. package/dist/cloud/shadow.d.ts.map +1 -0
  100. package/dist/cloud/temporal.d.ts +74 -0
  101. package/dist/cloud/temporal.d.ts.map +1 -0
  102. package/dist/components/atmosphere.d.ts +2 -0
  103. package/dist/components/atmosphere.d.ts.map +1 -1
  104. package/dist/components/barrel-distortion.d.ts +13 -0
  105. package/dist/components/barrel-distortion.d.ts.map +1 -0
  106. package/dist/components/camera.d.ts +20 -4
  107. package/dist/components/camera.d.ts.map +1 -1
  108. package/dist/components/cloud-layer.d.ts +29 -0
  109. package/dist/components/cloud-layer.d.ts.map +1 -0
  110. package/dist/components/index.d.ts +2 -0
  111. package/dist/components/index.d.ts.map +1 -1
  112. package/dist/components/instances.d.ts +20 -6
  113. package/dist/components/instances.d.ts.map +1 -1
  114. package/dist/components/mesh-filter.d.ts +6 -5
  115. package/dist/components/mesh-filter.d.ts.map +1 -1
  116. package/dist/components/motion-blur.d.ts +1 -0
  117. package/dist/components/motion-blur.d.ts.map +1 -1
  118. package/dist/construct-renderer.d.ts +1 -1
  119. package/dist/construct-renderer.d.ts.map +1 -1
  120. package/dist/construct-renderer.mjs +9751 -3528
  121. package/dist/construct-renderer.mjs.map +1 -1
  122. package/dist/device/gpu-residency.d.ts +6 -0
  123. package/dist/device/gpu-residency.d.ts.map +1 -1
  124. package/dist/dynamic-geometry.d.ts +5 -1
  125. package/dist/dynamic-geometry.d.ts.map +1 -1
  126. package/dist/environment/analytic-fog.d.ts +6 -0
  127. package/dist/environment/analytic-fog.d.ts.map +1 -0
  128. package/dist/errors/cloud.d.ts +59 -0
  129. package/dist/errors/cloud.d.ts.map +1 -0
  130. package/dist/errors/render.d.ts +23 -5
  131. package/dist/errors/render.d.ts.map +1 -1
  132. package/dist/examples/single-layer-medium-surface.d.ts +47 -0
  133. package/dist/examples/single-layer-medium-surface.d.ts.map +1 -0
  134. package/dist/extract/camera.d.ts.map +1 -1
  135. package/dist/extract/contracts.d.ts +12 -1
  136. package/dist/extract/contracts.d.ts.map +1 -1
  137. package/dist/extract/environment.d.ts +2 -0
  138. package/dist/extract/environment.d.ts.map +1 -1
  139. package/dist/features/barrel-distortion.d.ts +29 -0
  140. package/dist/features/barrel-distortion.d.ts.map +1 -0
  141. package/dist/features/error-reporting.d.ts +17 -0
  142. package/dist/features/error-reporting.d.ts.map +1 -0
  143. package/dist/features/host.d.ts +15 -0
  144. package/dist/features/host.d.ts.map +1 -1
  145. package/dist/features/motion-blur/motion-blur-feature.d.ts +20 -2
  146. package/dist/features/motion-blur/motion-blur-feature.d.ts.map +1 -1
  147. package/dist/features/motion-blur/motion-blur-params.d.ts +21 -0
  148. package/dist/features/motion-blur/motion-blur-params.d.ts.map +1 -1
  149. package/dist/features/motion-blur/motion-blur-runtime.d.ts +54 -0
  150. package/dist/features/motion-blur/motion-blur-runtime.d.ts.map +1 -0
  151. package/dist/features/plan.d.ts +15 -4
  152. package/dist/features/plan.d.ts.map +1 -1
  153. package/dist/features/prepared-gpu-work.d.ts +9 -5
  154. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  155. package/dist/features/render-graph-compute.d.ts.map +1 -1
  156. package/dist/features/render-graph-contribution.d.ts +5 -0
  157. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  158. package/dist/features/render-graph-raster.d.ts +1 -0
  159. package/dist/features/render-graph-raster.d.ts.map +1 -1
  160. package/dist/features/targets.d.ts +2 -0
  161. package/dist/features/targets.d.ts.map +1 -1
  162. package/dist/features/types.d.ts +44 -0
  163. package/dist/features/types.d.ts.map +1 -1
  164. package/dist/fullscreen-post-process-pass.d.ts +15 -7
  165. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  166. package/dist/gpu-dirty-ranges.d.ts +11 -0
  167. package/dist/gpu-dirty-ranges.d.ts.map +1 -0
  168. package/dist/gpu-driven/batch-topology.d.ts +17 -0
  169. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  170. package/dist/gpu-driven/material-artifacts.d.ts +6 -0
  171. package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
  172. package/dist/gpu-driven/prepared-draw.d.ts +7 -0
  173. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  174. package/dist/gpu-driven/production-raster-material.d.ts +178 -0
  175. package/dist/gpu-driven/production-raster-material.d.ts.map +1 -0
  176. package/dist/gpu-driven/production-raster.d.ts +50 -4
  177. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  178. package/dist/gpu-driven/resource-allocation.d.ts +31 -0
  179. package/dist/gpu-driven/resource-allocation.d.ts.map +1 -0
  180. package/dist/gpu-driven/shadow-ownership.d.ts +14 -0
  181. package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -0
  182. package/dist/gpu-driven/view-gpu.d.ts +35 -2
  183. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  184. package/dist/gpu-scene.d.ts +11 -1
  185. package/dist/gpu-scene.d.ts.map +1 -1
  186. package/dist/hdrp-buffers.d.ts +2 -2
  187. package/dist/hdrp-buffers.d.ts.map +1 -1
  188. package/dist/ibl/skylight-bind-group.d.ts +10 -1
  189. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  190. package/dist/index.d.ts +31 -8
  191. package/dist/index.d.ts.map +1 -1
  192. package/dist/index.mjs +258 -8
  193. package/dist/index.mjs.map +1 -1
  194. package/dist/inspection-types.d.ts +178 -3
  195. package/dist/inspection-types.d.ts.map +1 -1
  196. package/dist/instance-buffer-cache.d.ts +4 -4
  197. package/dist/instance-buffer-cache.d.ts.map +1 -1
  198. package/dist/instances.d.ts +49 -94
  199. package/dist/instances.d.ts.map +1 -1
  200. package/dist/internal.mjs +7 -7
  201. package/dist/pbr-pipeline.d.ts +19 -1
  202. package/dist/pbr-pipeline.d.ts.map +1 -1
  203. package/dist/pipeline/single-layer-medium-passes.d.ts +73 -0
  204. package/dist/pipeline/single-layer-medium-passes.d.ts.map +1 -0
  205. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  206. package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
  207. package/dist/pipeline/standard-output/types.d.ts +4 -2
  208. package/dist/pipeline/standard-output/types.d.ts.map +1 -1
  209. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  210. package/dist/pipeline/standard-post.d.ts +28 -19
  211. package/dist/pipeline/standard-post.d.ts.map +1 -1
  212. package/dist/pipeline-builder.d.ts +1 -1
  213. package/dist/pipeline-builder.d.ts.map +1 -1
  214. package/dist/pipeline-spec.d.ts +2 -2
  215. package/dist/plugin.d.ts.map +1 -1
  216. package/dist/prepare/prepared-graphics-resolver.d.ts +2 -0
  217. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  218. package/dist/record/barrel-distortion-frame.d.ts +15 -0
  219. package/dist/record/barrel-distortion-frame.d.ts.map +1 -0
  220. package/dist/record/dynamic-geometry-consumption.d.ts +10 -2
  221. package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -1
  222. package/dist/record/fold-buckets.d.ts +95 -0
  223. package/dist/record/fold-buckets.d.ts.map +1 -0
  224. package/dist/record/fold-instance-buffer.d.ts +6 -0
  225. package/dist/record/fold-instance-buffer.d.ts.map +1 -0
  226. package/dist/record/frame-lighting.d.ts +1 -0
  227. package/dist/record/frame-lighting.d.ts.map +1 -1
  228. package/dist/record/frame-snapshot.d.ts +40 -3
  229. package/dist/record/frame-snapshot.d.ts.map +1 -1
  230. package/dist/record/frame-targets.d.ts.map +1 -1
  231. package/dist/record/frame.d.ts +14 -62
  232. package/dist/record/frame.d.ts.map +1 -1
  233. package/dist/record/gpu-draw-receipts.d.ts +11 -0
  234. package/dist/record/gpu-draw-receipts.d.ts.map +1 -0
  235. package/dist/record/gpu-timing.d.ts +8 -0
  236. package/dist/record/gpu-timing.d.ts.map +1 -1
  237. package/dist/record/main-pass-geometry.d.ts +21 -5
  238. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  239. package/dist/record/main-pass-material.d.ts +7 -1
  240. package/dist/record/main-pass-material.d.ts.map +1 -1
  241. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  242. package/dist/record/main-pass.d.ts +13 -2
  243. package/dist/record/main-pass.d.ts.map +1 -1
  244. package/dist/record/mesh-ssbo.d.ts +6 -5
  245. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  246. package/dist/record/motion-blur-receipt.d.ts +11 -0
  247. package/dist/record/motion-blur-receipt.d.ts.map +1 -0
  248. package/dist/record/probe-blend-buffer.d.ts +16 -0
  249. package/dist/record/probe-blend-buffer.d.ts.map +1 -0
  250. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  251. package/dist/record/render-context.d.ts +34 -31
  252. package/dist/record/render-context.d.ts.map +1 -1
  253. package/dist/record/shadow-pass.d.ts +2 -0
  254. package/dist/record/shadow-pass.d.ts.map +1 -1
  255. package/dist/record/skybox-post-pass.d.ts +6 -4
  256. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  257. package/dist/record/typed-frame-graph.d.ts +22 -2
  258. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  259. package/dist/record/view-ubo.d.ts +11 -3
  260. package/dist/record/view-ubo.d.ts.map +1 -1
  261. package/dist/record/volume-params.d.ts +9 -0
  262. package/dist/record/volume-params.d.ts.map +1 -0
  263. package/dist/recovery/render-system-candidate.d.ts +10 -0
  264. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  265. package/dist/reflection/record-owner.d.ts.map +1 -1
  266. package/dist/render-contract.d.ts +66 -8
  267. package/dist/render-contract.d.ts.map +1 -1
  268. package/dist/render-graph-primitives.d.ts.map +1 -1
  269. package/dist/render-pipeline.d.ts +106 -3
  270. package/dist/render-pipeline.d.ts.map +1 -1
  271. package/dist/render-system-extract-tail.d.ts +3 -2
  272. package/dist/render-system-extract-tail.d.ts.map +1 -1
  273. package/dist/render-system-extract.d.ts +24 -10
  274. package/dist/render-system-extract.d.ts.map +1 -1
  275. package/dist/render-system-presentation.d.ts +23 -0
  276. package/dist/render-system-presentation.d.ts.map +1 -0
  277. package/dist/render-system-producer-roots.d.ts +10 -0
  278. package/dist/render-system-producer-roots.d.ts.map +1 -0
  279. package/dist/render-system-residency.d.ts +12 -0
  280. package/dist/render-system-residency.d.ts.map +1 -0
  281. package/dist/render-system.d.ts +26 -15
  282. package/dist/render-system.d.ts.map +1 -1
  283. package/dist/scene/probe-blend.d.ts +27 -0
  284. package/dist/scene/probe-blend.d.ts.map +1 -1
  285. package/dist/scene/render-scene.d.ts +14 -5
  286. package/dist/scene/render-scene.d.ts.map +1 -1
  287. package/dist/ssao-buffers.d.ts +9 -0
  288. package/dist/ssao-buffers.d.ts.map +1 -1
  289. package/dist/surface/dynamic-input.d.ts +133 -0
  290. package/dist/surface/dynamic-input.d.ts.map +1 -0
  291. package/dist/surface/gpu-driven.d.ts +41 -0
  292. package/dist/surface/gpu-driven.d.ts.map +1 -0
  293. package/dist/surface/submission-observation.d.ts +136 -0
  294. package/dist/surface/submission-observation.d.ts.map +1 -0
  295. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  296. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  297. package/dist/temporal/frame.d.ts +7 -0
  298. package/dist/temporal/frame.d.ts.map +1 -1
  299. package/dist/temporal/gpu.d.ts +13 -1
  300. package/dist/temporal/gpu.d.ts.map +1 -1
  301. package/dist/temporal/index.mjs +5 -5
  302. package/dist/temporal/inspection.d.ts.map +1 -1
  303. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  304. package/dist/temporal/target-coverage-attachment.d.ts.map +1 -1
  305. package/dist/temporal/view.d.ts +6 -1
  306. package/dist/temporal/view.d.ts.map +1 -1
  307. package/dist/transmission/single-layer-medium.d.ts +89 -0
  308. package/dist/transmission/single-layer-medium.d.ts.map +1 -0
  309. package/dist/typed-render-graph-primitives.d.ts +24 -9
  310. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  311. package/dist/volume/component.d.ts +9 -5
  312. package/dist/volume/component.d.ts.map +1 -1
  313. package/dist/volume/extract.d.ts +1 -1
  314. package/dist/volume/extract.d.ts.map +1 -1
  315. package/dist/volume/inspection.d.ts +2 -0
  316. package/dist/volume/inspection.d.ts.map +1 -1
  317. package/dist/volume/passes.d.ts +4 -1
  318. package/dist/volume/passes.d.ts.map +1 -1
  319. package/dist/volume/resources.d.ts.map +1 -1
  320. package/dist/volume/temporal.d.ts +3 -1
  321. package/dist/volume/temporal.d.ts.map +1 -1
  322. package/package.json +22 -21
  323. package/src/__tests__/analytic-fog-graph.unit.test.ts +199 -0
  324. package/src/__tests__/analytic-fog-optics.unit.test.ts +38 -0
  325. package/src/__tests__/atmosphere-daylight.dawn.test.ts +72 -0
  326. package/src/__tests__/barrel-distortion-lkg.integration.test.ts +353 -0
  327. package/src/__tests__/barrel-distortion.dawn.test.ts +41 -0
  328. package/src/__tests__/barrel-distortion.unit.test.ts +364 -0
  329. package/src/__tests__/bloom-exact-zero.unit.test.ts +112 -18
  330. package/src/__tests__/bloom-lifecycle.integration.test.ts +17 -4
  331. package/src/__tests__/construct-renderer-identity.unit.test.ts +49 -0
  332. package/src/__tests__/detached-recovery-graph.unit.test.ts +277 -0
  333. package/src/__tests__/device-loss-fanout.unit.test.ts +36 -1
  334. package/src/__tests__/docs-gate-green.unit.test.ts +112 -1
  335. package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +20 -0
  336. package/src/__tests__/environment-fog-lanes.integration.test.ts +14 -0
  337. package/src/__tests__/environment-selection.unit.test.ts +12 -0
  338. package/src/__tests__/factory-contract.integration.test.ts +626 -113
  339. package/src/__tests__/feature-depth-input.dawn.test.ts +129 -2
  340. package/src/__tests__/frame-camera-selection.unit.test.ts +39 -1
  341. package/src/__tests__/frame-plan-contract.unit.test.ts +2 -0
  342. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +14 -3
  343. package/src/__tests__/gpu-dirty-ranges.unit.test.ts +22 -0
  344. package/src/__tests__/gpu-driven-baseline.characterization.test.ts +16 -3
  345. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +12 -0
  346. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +2 -1
  347. package/src/__tests__/gpu-driven-material-artifacts.unit.test.ts +143 -0
  348. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +2 -2
  349. package/src/__tests__/gpu-driven-production.integration.test.ts +482 -3
  350. package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +7 -6
  351. package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +1 -1
  352. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +103 -4
  353. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +18 -2
  354. package/src/__tests__/gpu-driven-view.browser.test.ts +5 -8
  355. package/src/__tests__/gpu-driven-view.dawn.test.ts +6 -2
  356. package/src/__tests__/gpu-driven-view.unit.test.ts +1 -1
  357. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +1 -0
  358. package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -4
  359. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +1 -0
  360. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +12 -0
  361. package/src/__tests__/gpu-scene.unit.test.ts +210 -9
  362. package/src/__tests__/instance-allocation-failure.unit.test.ts +57 -0
  363. package/src/__tests__/instances-culling.integration.test.ts +10 -36
  364. package/src/__tests__/instances-store.unit.test.ts +105 -238
  365. package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
  366. package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +146 -0
  367. package/src/__tests__/material-shader-prewarm.unit.test.ts +59 -0
  368. package/src/__tests__/material-snapshot-mutation.integration.test.ts +39 -1
  369. package/src/__tests__/mesh-submission-lifetime.unit.test.ts +31 -0
  370. package/src/__tests__/morph-culling-reentry.dawn.test.ts +5 -5
  371. package/src/__tests__/morph-visual-evidence.integration.test.ts +8 -7
  372. package/src/__tests__/motion-blur-compute.dawn.test.ts +589 -0
  373. package/src/__tests__/motion-blur.pixel.integration.test.ts +2 -2
  374. package/src/__tests__/nineslice-handle-surface.unit.test.ts +4 -4
  375. package/src/__tests__/pbr-view-layout.unit.test.ts +9 -0
  376. package/src/__tests__/physical-clearcoat.integration.test.ts +58 -0
  377. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +21 -0
  378. package/src/__tests__/prepared-graphics-capability.unit.test.ts +66 -2
  379. package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +86 -0
  380. package/src/__tests__/probe-bindings.unit.test.ts +582 -1
  381. package/src/__tests__/probe-scene-dirty.integration.test.ts +28 -1
  382. package/src/__tests__/public-surface.test-d.ts +8 -0
  383. package/src/__tests__/receipt-observation-retirement.unit.test.ts +65 -0
  384. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +69 -0
  385. package/src/__tests__/render-error-code-owner.test-d.ts +9 -0
  386. package/src/__tests__/render-feature-diagnostics.unit.test.ts +15 -2
  387. package/src/__tests__/render-feature-error-reporting.unit.test.ts +44 -0
  388. package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +10 -1
  389. package/src/__tests__/render-feature-retry.unit.test.ts +12 -1
  390. package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +23 -0
  391. package/src/__tests__/render-scene-mixed-updates.integration.test.ts +145 -19
  392. package/src/__tests__/render-scene-projection.unit.test.ts +36 -11
  393. package/src/__tests__/render-scene-temporal-retry.unit.test.ts +37 -6
  394. package/src/__tests__/render-target-probe-soak.perf.test.ts +2 -2
  395. package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +3 -3
  396. package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +5 -2
  397. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +41 -7
  398. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +16 -0
  399. package/src/__tests__/renderer-frame-transaction.integration.test.ts +33 -11
  400. package/src/__tests__/resource-allocation.unit.test.ts +123 -0
  401. package/src/__tests__/shader-manifest-fixture.ts +4 -4
  402. package/src/__tests__/single-layer-medium-passes.unit.test.ts +275 -0
  403. package/src/__tests__/skin-motion-regression.unit.test.ts +70 -1
  404. package/src/__tests__/skin-palette-recycling.dawn.test.ts +97 -0
  405. package/src/__tests__/skinned-shadow-caster.test.ts +17 -0
  406. package/src/__tests__/ssr-carrier-public-contract.unit.test.ts +21 -0
  407. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +4 -4
  408. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +2 -2
  409. package/src/__tests__/ssr-gpu-dispatch.ts +1 -1
  410. package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +6 -2
  411. package/src/__tests__/standard-lighting-write.unit.test.ts +2 -1
  412. package/src/__tests__/standard-output-chain-contract.unit.test.ts +28 -0
  413. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +46 -1
  414. package/src/__tests__/standard-pipeline.dawn.test.ts +1 -1
  415. package/src/__tests__/standard-pipeline.integration.test.ts +200 -9
  416. package/src/__tests__/surface-dynamic-buffer-lifecycle.unit.test.ts +506 -0
  417. package/src/__tests__/surface-submission-observation.unit.test.ts +812 -0
  418. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +32 -0
  419. package/src/__tests__/temporal-performance.integration.test.ts +3 -3
  420. package/src/__tests__/temporal-reset-matrix.unit.test.ts +10 -0
  421. package/src/__tests__/temporal-view.unit.test.ts +2 -1
  422. package/src/__tests__/typed-pipeline-topology.unit.test.ts +20 -4
  423. package/src/__tests__/visibility-instances.integration.test.ts +7 -19
  424. package/src/__tests__/visibility-producer-matrix.integration.test.ts +9 -20
  425. package/src/__tests__/volume-optics-oracle.unit.test.ts +7 -3
  426. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +189 -15
  427. package/src/__tests__/volumetric-fog-errors.unit.test.ts +4 -3
  428. package/src/__tests__/volumetric-fog-multiple.unit.test.ts +55 -0
  429. package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +1 -1
  430. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +558 -68
  431. package/src/__tests__/volumetric-fog-temporal.unit.test.ts +27 -0
  432. package/src/__tests__/volumetric-fog-topology.unit.test.ts +23 -5
  433. package/src/assembly/analytic-fog-registration.ts +14 -0
  434. package/src/assembly/backend-contract.ts +30 -0
  435. package/src/assembly/barrel-distortion-inspection.ts +22 -0
  436. package/src/assembly/canvas-draw-guard.ts +11 -0
  437. package/src/assembly/cloud-shadow-entry.ts +22 -0
  438. package/src/assembly/dynamic-geometry-host.ts +65 -37
  439. package/src/assembly/dynamic-geometry-runtime.ts +7 -1
  440. package/src/assembly/factory.ts +2 -30
  441. package/src/assembly/fxaa-registration.ts +17 -0
  442. package/src/assembly/host-contract.ts +3 -3
  443. package/src/assembly/lod-observation.ts +12 -0
  444. package/src/assembly/material/assembly.ts +25 -13
  445. package/src/assembly/material/pipeline-helpers.ts +53 -12
  446. package/src/assembly/material-shader-policy.ts +55 -0
  447. package/src/assembly/material-shader-prewarm.ts +21 -10
  448. package/src/assembly/motion-blur-registration.ts +17 -0
  449. package/src/assembly/recovery/device-loss-fanout.ts +13 -0
  450. package/src/assembly/recovery/renderer-recover.ts +41 -18
  451. package/src/assembly/render-feature-post-processes.ts +54 -0
  452. package/src/assembly/renderer-facade.ts +6 -1
  453. package/src/assembly/renderer-feature-inspection.ts +8 -2
  454. package/src/assembly/renderer-frame-transaction.ts +23 -0
  455. package/src/assembly/renderer-inspection.ts +28 -0
  456. package/src/assembly/skin-palette-owner.ts +1 -1
  457. package/src/assembly/temporal-post-process-prewarm.ts +40 -0
  458. package/src/assembly/webgpu-pbr-ready.ts +9 -1
  459. package/src/assembly/webgpu-ready-contract.ts +8 -3
  460. package/src/assembly/webgpu-ready-per-pass.ts +104 -0
  461. package/src/assembly/webgpu-ready.ts +296 -306
  462. package/src/assembly/webgpu-renderer-bootstrap.ts +0 -12
  463. package/src/assembly/webgpu-renderer-observation-owner.ts +222 -0
  464. package/src/assembly/webgpu-renderer.ts +228 -281
  465. package/src/assembly/webgpu-vertex-layouts.ts +1 -1
  466. package/src/barrel-distortion.ts +247 -0
  467. package/src/bloom-admission.ts +21 -0
  468. package/src/cloud/__tests__/cloud-density-parity.dawn.test.ts +173 -0
  469. package/src/cloud/__tests__/cloud-layer-framebuffer-y.dawn.test.ts +265 -0
  470. package/src/cloud/__tests__/cloud-layer.browser.test.ts +432 -0
  471. package/src/cloud/__tests__/cloud-layer.unit.test.ts +634 -0
  472. package/src/cloud/density.ts +600 -0
  473. package/src/cloud/extract.ts +110 -0
  474. package/src/cloud/feature.ts +1810 -0
  475. package/src/cloud/frame-context.ts +42 -0
  476. package/src/cloud/inspection.ts +130 -0
  477. package/src/cloud/optics.ts +285 -0
  478. package/src/cloud/parameters.ts +255 -0
  479. package/src/cloud/resources.ts +122 -0
  480. package/src/cloud/shadow.ts +167 -0
  481. package/src/cloud/temporal.ts +226 -0
  482. package/src/components/__tests__/motion-blur.unit.test.ts +1 -0
  483. package/src/components/atmosphere.ts +4 -0
  484. package/src/components/barrel-distortion.ts +46 -0
  485. package/src/components/camera.ts +66 -10
  486. package/src/components/cloud-layer.ts +47 -0
  487. package/src/components/index.ts +2 -0
  488. package/src/components/instances.ts +18 -45
  489. package/src/components/mesh-filter.ts +11 -9
  490. package/src/components/motion-blur.ts +1 -0
  491. package/src/components/sprite-instances.ts +1 -1
  492. package/src/construct-renderer.ts +1 -1
  493. package/src/device/gpu-residency.ts +56 -12
  494. package/src/dynamic-geometry.ts +12 -1
  495. package/src/environment/analytic-fog.ts +44 -0
  496. package/src/environment/background.ts +2 -2
  497. package/src/errors/cloud.ts +103 -0
  498. package/src/errors/render.ts +58 -8
  499. package/src/examples/single-layer-medium-surface.ts +59 -0
  500. package/src/extract/camera.ts +35 -8
  501. package/src/extract/contracts.ts +12 -0
  502. package/src/extract/environment.ts +4 -0
  503. package/src/features/barrel-distortion.ts +172 -0
  504. package/src/features/error-reporting.ts +98 -0
  505. package/src/features/host.ts +138 -24
  506. package/src/features/motion-blur/__tests__/motion-blur-feature.unit.test.ts +111 -1
  507. package/src/features/motion-blur/__tests__/motion-blur-params.unit.test.ts +58 -3
  508. package/src/features/motion-blur/motion-blur-feature.ts +1519 -15
  509. package/src/features/motion-blur/motion-blur-params.ts +70 -2
  510. package/src/features/motion-blur/motion-blur-runtime.ts +176 -0
  511. package/src/features/plan.ts +33 -8
  512. package/src/features/prepared-gpu-work.ts +86 -35
  513. package/src/features/render-graph-compute.ts +13 -5
  514. package/src/features/render-graph-contribution.ts +11 -0
  515. package/src/features/render-graph-raster.ts +45 -3
  516. package/src/features/targets.ts +66 -0
  517. package/src/features/types.ts +47 -0
  518. package/src/fullscreen-post-process-pass.ts +41 -9
  519. package/src/gpu-dirty-ranges.ts +48 -0
  520. package/src/gpu-driven/batch-topology.ts +53 -0
  521. package/src/gpu-driven/material-artifacts.ts +35 -32
  522. package/src/gpu-driven/prepared-draw.ts +27 -14
  523. package/src/gpu-driven/production-raster-material.ts +1894 -0
  524. package/src/gpu-driven/production-raster.ts +904 -787
  525. package/src/gpu-driven/resource-allocation.ts +142 -0
  526. package/src/gpu-driven/shadow-ownership.ts +71 -0
  527. package/src/gpu-driven/view-gpu.ts +181 -10
  528. package/src/gpu-scene.ts +259 -101
  529. package/src/hdrp-buffers.ts +4 -3
  530. package/src/ibl/skylight-bind-group.ts +49 -7
  531. package/src/index.ts +228 -9
  532. package/src/inspection-types.ts +222 -2
  533. package/src/instance-buffer-cache.ts +4 -4
  534. package/src/instances.ts +151 -492
  535. package/src/pbr-pipeline.ts +219 -13
  536. package/src/pipeline/single-layer-medium-passes.ts +267 -0
  537. package/src/pipeline/standard-forward-lane.ts +313 -57
  538. package/src/pipeline/standard-output/graph.ts +8 -2
  539. package/src/pipeline/standard-output/types.ts +12 -1
  540. package/src/pipeline/standard-pipeline.ts +291 -18
  541. package/src/pipeline/standard-post.ts +397 -102
  542. package/src/pipeline-builder.ts +1 -1
  543. package/src/pipeline-spec.ts +5 -5
  544. package/src/plugin.ts +4 -0
  545. package/src/points-lines/__tests__/points-lines.bench.ts +17 -8
  546. package/src/points-lines/__tests__/view-ubo.unit.test.ts +2 -1
  547. package/src/prepare/prepared-graphics-resolver.ts +2 -0
  548. package/src/record/__tests__/main-pass-geometry.unit.test.ts +30 -0
  549. package/src/record/__tests__/sprite-material-abi.unit.test.ts +1 -1
  550. package/src/record/__tests__/temporal-payload.unit.test.ts +3 -2
  551. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +11 -0
  552. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -0
  553. package/src/record/__tests__/view-ubo-layout.unit.test.ts +4 -3
  554. package/src/record/barrel-distortion-frame.ts +110 -0
  555. package/src/record/dynamic-geometry-consumption.ts +39 -9
  556. package/src/record/fold-buckets.ts +852 -0
  557. package/src/record/fold-instance-buffer.ts +51 -0
  558. package/src/record/frame-lighting.ts +36 -2
  559. package/src/record/frame-snapshot.ts +44 -4
  560. package/src/record/frame-targets.ts +10 -6
  561. package/src/record/frame.ts +373 -1099
  562. package/src/record/gpu-draw-receipts.ts +38 -0
  563. package/src/record/gpu-timing.ts +48 -5
  564. package/src/record/main-pass-geometry.ts +281 -83
  565. package/src/record/main-pass-material.ts +66 -4
  566. package/src/record/main-pass-sprite-draws.ts +13 -66
  567. package/src/record/main-pass.ts +156 -20
  568. package/src/record/mesh-ssbo.ts +43 -6
  569. package/src/record/motion-blur-receipt.ts +91 -0
  570. package/src/record/probe-blend-buffer.ts +180 -0
  571. package/src/record/recovery-pipeline.ts +35 -3
  572. package/src/record/render-context.ts +51 -31
  573. package/src/record/shadow-pass.ts +36 -2
  574. package/src/record/skybox-post-pass.ts +150 -200
  575. package/src/record/typed-frame-graph.ts +824 -44
  576. package/src/record/view-ubo.ts +26 -3
  577. package/src/record/volume-params.ts +225 -0
  578. package/src/recovery/render-system-candidate.ts +257 -52
  579. package/src/reflection/record-owner.ts +7 -3
  580. package/src/render-contract.ts +71 -8
  581. package/src/render-graph-primitives.ts +7 -1
  582. package/src/render-pipeline.ts +136 -1
  583. package/src/render-system-extract-tail.ts +72 -60
  584. package/src/render-system-extract.ts +117 -87
  585. package/src/render-system-presentation.ts +56 -0
  586. package/src/render-system-producer-roots.ts +96 -0
  587. package/src/render-system-residency.ts +159 -0
  588. package/src/render-system.ts +601 -482
  589. package/src/scene/probe-blend.ts +96 -4
  590. package/src/scene/render-scene.ts +431 -281
  591. package/src/ssao-buffers.ts +95 -2
  592. package/src/surface/__tests__/dynamic-input.unit.test.ts +374 -0
  593. package/src/surface/__tests__/gpu-driven.unit.test.ts +87 -0
  594. package/src/surface/dynamic-input.ts +658 -0
  595. package/src/surface/gpu-driven.ts +147 -0
  596. package/src/surface/submission-observation.ts +517 -0
  597. package/src/systems/skin-palette-allocator.ts +4 -1
  598. package/src/temporal/__tests__/temporal-frame-transaction.unit.test.ts +36 -0
  599. package/src/temporal/frame-coordinator.ts +8 -1
  600. package/src/temporal/frame.ts +20 -0
  601. package/src/temporal/gpu.ts +101 -17
  602. package/src/temporal/inspection.ts +7 -1
  603. package/src/temporal/standard-scene-data.ts +5 -0
  604. package/src/temporal/target-coverage-attachment.ts +4 -0
  605. package/src/temporal/view.ts +13 -2
  606. package/src/transmission/__tests__/single-layer-medium.unit.test.ts +149 -0
  607. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +12 -11
  608. package/src/transmission/single-layer-medium.ts +322 -0
  609. package/src/typed-render-graph-primitives.ts +191 -50
  610. package/src/volume/component.ts +10 -10
  611. package/src/volume/extract.ts +11 -5
  612. package/src/volume/inspection.ts +3 -0
  613. package/src/volume/passes.ts +95 -8
  614. package/src/volume/resources.ts +2 -1
  615. package/src/volume/temporal.ts +11 -0
  616. package/dist/chunk-4NNZQCL3.mjs.map +0 -1
  617. package/dist/chunk-6TFBXLRJ.mjs.map +0 -1
  618. package/dist/chunk-DRR7D3KX.mjs.map +0 -1
  619. package/dist/chunk-GWCEMDVR.mjs.map +0 -1
  620. package/dist/chunk-JCIRYQ4V.mjs.map +0 -1
  621. package/dist/chunk-JRIPCMYC.mjs.map +0 -1
  622. package/dist/chunk-LULCA3OF.mjs +0 -1713
  623. package/dist/chunk-LULCA3OF.mjs.map +0 -1
  624. package/dist/chunk-PA5DRJNI.mjs.map +0 -1
@@ -19,6 +19,7 @@ import {
19
19
  resolveRenderFeatureDrawBuffers,
20
20
  } from './render-graph-raster';
21
21
  import { createRenderFeatureGraphBufferState } from './render-graph-resources';
22
+ import type { RenderFeaturePlacement } from './types';
22
23
 
23
24
  export function createRenderFeatureProjectionState() {
24
25
  return {
@@ -141,6 +142,10 @@ export function projectRenderFeaturePlans<FrameCtx extends RenderGraphFrame>(
141
142
  readonly reportError?: ((error: RenderError) => void) | undefined;
142
143
  readonly resolveStandardLighting?: RenderFeatureLightingResolver<FrameCtx> | undefined;
143
144
  readonly standardSurfaceAccesses?: readonly GraphAccess[] | undefined;
145
+ /** Restrict projection to one Standard graph placement. */
146
+ readonly placement?: RenderFeaturePlacement | undefined;
147
+ /** Restrict projection to the named passes for a multi-stage feature. */
148
+ readonly passNames?: ReadonlySet<string> | undefined;
144
149
  readonly state?: ReturnType<typeof createRenderFeatureProjectionState>;
145
150
  },
146
151
  ): Result<void, RenderGraphError | RenderError> {
@@ -162,7 +167,11 @@ export function projectRenderFeaturePlans<FrameCtx extends RenderGraphFrame>(
162
167
  options.standardSurfaceAccesses,
163
168
  );
164
169
  for (const execution of executions) {
170
+ if (options.placement !== undefined && (execution.placement ?? 'post') !== options.placement) {
171
+ continue;
172
+ }
165
173
  for (const pass of execution.passes) {
174
+ if (options.passNames !== undefined && !options.passNames.has(pass.name)) continue;
166
175
  if (state.projected.has(pass)) continue;
167
176
  if (pass.shadowCaster)
168
177
  return err(
@@ -181,6 +190,7 @@ export function projectRenderFeaturePlans<FrameCtx extends RenderGraphFrame>(
181
190
  pass.resolvedGpuCompute,
182
191
  );
183
192
  if (!added.ok) return added;
193
+ state.projected.add(pass);
184
194
  continue;
185
195
  }
186
196
  if (
@@ -197,6 +207,7 @@ export function projectRenderFeaturePlans<FrameCtx extends RenderGraphFrame>(
197
207
  pass.resolvedGraphics,
198
208
  );
199
209
  if (!added.ok) return added;
210
+ state.projected.add(pass);
200
211
  continue;
201
212
  }
202
213
  return err(
@@ -57,6 +57,7 @@ export type RenderFeatureGraphBindingsResolver<FrameCtx extends RenderGraphFrame
57
57
  readonly binding: Extract<PreparedGraphicsResolvedResource, { readonly kind: 'bindings' }>;
58
58
  readonly resources: GraphResourceResolver;
59
59
  readonly resolveTarget: RenderFeatureGraphTargetResolver;
60
+ readonly resolveGpuResource?: (name: string) => RenderFeatureResolvedGpuBuffer | undefined;
60
61
  }) => RenderFeatureGraphBindingsResolution | undefined;
61
62
 
62
63
  export type RenderFeatureLightingResolver<FrameCtx extends RenderGraphFrame> = (
@@ -275,6 +276,32 @@ export class RenderFeatureRasterGraphProjection<FrameCtx extends RenderGraphFram
275
276
  accesses.push({ resource: handle, usage: 'indirect-read' } as const);
276
277
  }
277
278
 
279
+ // Feature-owned fullscreen/material bind groups may carry a read-only
280
+ // storage buffer (for example the bounded procedural cloud cache). Import
281
+ // it into the same graph so the compute producer is ordered before the
282
+ // raster consumer and the buffer lease follows queue retirement.
283
+ for (const draw of descriptor.draws) {
284
+ for (const reference of draw.bindings) {
285
+ const binding = resolved(snapshot, reference);
286
+ const names =
287
+ binding?.kind === 'bindings' ? binding.descriptor?.values.storageBuffers : undefined;
288
+ if (!Array.isArray(names)) continue;
289
+ for (const name of names) {
290
+ if (typeof name !== 'string') continue;
291
+ const physical = snapshot.resolveGpuResource?.(name);
292
+ if (physical === undefined) continue;
293
+ const imported = importRenderFeatureGraphBuffer(
294
+ this.builder,
295
+ this.buffers,
296
+ `${name}.${featureIdentity}.${order}`,
297
+ physical,
298
+ );
299
+ if (!imported.ok) return imported;
300
+ accesses.push({ resource: imported.value, usage: 'storage-read' } as const);
301
+ }
302
+ }
303
+ }
304
+
278
305
  return this.builder.addRasterPass(name, {
279
306
  accesses,
280
307
  colorAttachments: colors,
@@ -377,9 +404,24 @@ export class RenderFeatureRasterGraphProjection<FrameCtx extends RenderGraphFram
377
404
  pass.setBindGroup(0, shadowView, [0, 0]);
378
405
  continue;
379
406
  }
380
- const resolvedBindings =
381
- binding.handle ??
382
- this.resolveBindings?.({ frame, binding, resources, resolveTarget: this.resolveTarget });
407
+ // Fullscreen feature bindings contain graph-owned texture views and a
408
+ // per-frame params UBO. Resolve them at execution time so a retained
409
+ // graph still follows the current post-process input and authored
410
+ // parameters; material/view bindings remain safely cached.
411
+ const resolvedBindings = (() => {
412
+ const resolve = () =>
413
+ this.resolveBindings?.({
414
+ frame,
415
+ binding,
416
+ resources,
417
+ resolveTarget: this.resolveTarget,
418
+ ...(snapshot.resolveGpuResource === undefined
419
+ ? {}
420
+ : { resolveGpuResource: snapshot.resolveGpuResource }),
421
+ });
422
+ if (binding.descriptor?.values.fullscreen === true) return resolve();
423
+ return binding.handle ?? resolve();
424
+ })();
383
425
  const handle =
384
426
  resolvedBindings !== undefined &&
385
427
  typeof resolvedBindings === 'object' &&
@@ -55,6 +55,8 @@ export function resolveStandardRenderFeatureTargets(input: {
55
55
  readonly storageBuffer: boolean;
56
56
  readonly multisample: boolean;
57
57
  readonly colorAttachmentFormat: TextureFormat;
58
+ /** Expose cloud history aliases only while a CloudLayer is authored. */
59
+ readonly cloudHistory?: boolean;
58
60
  }): readonly RenderFeatureTargetHandle[] {
59
61
  const sampleCount: 1 | 4 = input.antialias === 'msaa' && input.multisample ? 4 : 1;
60
62
  // Standard always exposes the scene to features as a linear float target;
@@ -91,5 +93,69 @@ export function resolveStandardRenderFeatureTargets(input: {
91
93
  format: colorFormat,
92
94
  sampleCount: 1,
93
95
  }),
96
+ // A graph-owned, single-sample map produced before direct-solar lighting.
97
+ // The logical handle lets the feature plan validate its producer before
98
+ // the Standard pipeline allocates the concrete texture.
99
+ createRenderFeatureTarget({
100
+ name: 'cloud-shadow',
101
+ kind: 'scene-color',
102
+ format: 'rgba16float',
103
+ sampleCount: 1,
104
+ }),
105
+ // These are declarative aliases only. Standard allocates the actual
106
+ // linear-LDR ping-pong target at the ordered post stage and resolves these
107
+ // names through `contributeFeatures` when that stage is reached.
108
+ createRenderFeatureTarget({
109
+ name: 'barrel-input',
110
+ kind: 'scene-color',
111
+ format: 'rgba16float',
112
+ sampleCount: 1,
113
+ }),
114
+ createRenderFeatureTarget({
115
+ name: 'barrel-output',
116
+ kind: 'scene-color',
117
+ format: 'rgba16float',
118
+ sampleCount: 1,
119
+ }),
120
+ ...(input.cloudHistory === true
121
+ ? [
122
+ createRenderFeatureTarget({
123
+ name: 'cloud-history-radiance-previous',
124
+ kind: 'scene-color',
125
+ format: 'rgba16float',
126
+ sampleCount: 1,
127
+ }),
128
+ createRenderFeatureTarget({
129
+ name: 'cloud-history-radiance-current',
130
+ kind: 'scene-color',
131
+ format: 'rgba16float',
132
+ sampleCount: 1,
133
+ }),
134
+ createRenderFeatureTarget({
135
+ name: 'cloud-history-transmittance-previous',
136
+ kind: 'scene-color',
137
+ format: 'rgba16float',
138
+ sampleCount: 1,
139
+ }),
140
+ createRenderFeatureTarget({
141
+ name: 'cloud-history-transmittance-current',
142
+ kind: 'scene-color',
143
+ format: 'rgba16float',
144
+ sampleCount: 1,
145
+ }),
146
+ createRenderFeatureTarget({
147
+ name: 'cloud-history-depth-previous',
148
+ kind: 'scene-color',
149
+ format: 'rgba16float',
150
+ sampleCount: 1,
151
+ }),
152
+ createRenderFeatureTarget({
153
+ name: 'cloud-history-depth-current',
154
+ kind: 'scene-color',
155
+ format: 'rgba16float',
156
+ sampleCount: 1,
157
+ }),
158
+ ]
159
+ : []),
94
160
  ];
95
161
  }
@@ -4,6 +4,7 @@ import type { RhiCaps } from '@forgeax/engine-rhi';
4
4
  import type { Result } from '@forgeax/engine-types';
5
5
  import type { RenderError, RenderFeatureErrorDescriptor } from '../errors/render';
6
6
  import type { VisibilitySnapshot } from '../extract/visibility';
7
+ import type { CameraSnapshot } from '../render-contract';
7
8
  import type { RenderFeaturePlan, RenderFeaturePlanContext } from './plan';
8
9
  import type { RenderFeatureCapabilityKey } from './vocabulary';
9
10
 
@@ -42,6 +43,17 @@ export type {
42
43
  /** Closed lifecycle states exposed by the feature host. */
43
44
  export type RenderFeatureStatus = 'active' | 'failed' | 'disabled' | 'disposed';
44
45
 
46
+ /**
47
+ * Graph placement for a producer-owned feature.
48
+ *
49
+ * `scene` is admitted after the opaque scene and before transmission,
50
+ * ordinary transparency, and volumetric composition. `post` is admitted at
51
+ * the existing post-TAA extension point. Keeping this as one closed value
52
+ * lets the Standard owner place atmospheric transport without adding a
53
+ * second renderer or a feature-specific side channel.
54
+ */
55
+ export type RenderFeaturePlacement = 'scene' | 'post';
56
+
45
57
  /**
46
58
  * Shader preparation policy for feature-owned GPU programs.
47
59
  *
@@ -63,6 +75,27 @@ export interface RenderFeatureWorldVisibilitySnapshot {
63
75
  readonly snapshot: VisibilitySnapshot;
64
76
  }
65
77
 
78
+ /** Renderer-owned view facts shared by feature extraction and temporal owners. */
79
+ export interface RenderFeatureViewContext {
80
+ readonly identity: string;
81
+ readonly width: number;
82
+ readonly height: number;
83
+ readonly cameraRevision: number;
84
+ readonly deviceGeneration: number;
85
+ readonly cameraPosition: readonly [number, number, number];
86
+ /** Stable world anchor for light-space cloud coverage (never screen-space). */
87
+ readonly shadowAnchor?: readonly [number, number, number];
88
+ readonly cameraCut?: boolean;
89
+ readonly recovery?: boolean;
90
+ readonly sceneDepthVersion?: number;
91
+ }
92
+
93
+ /** Normal frame projection consumed by feature owners at extract time. */
94
+ export interface RenderFeatureFrameContext {
95
+ readonly cloudLayer?: import('../cloud/extract').ExtractedCloudLayer;
96
+ readonly view?: RenderFeatureViewContext;
97
+ }
98
+
66
99
  /** A producer's structured report for one hidden render candidate. */
67
100
  export interface RenderFeatureHiddenEntityReport {
68
101
  readonly world: World;
@@ -73,6 +106,16 @@ export interface RenderFeatureExtractContext {
73
106
  readonly worlds: readonly World[];
74
107
  readonly owner: number;
75
108
  readonly frameNumber: number;
109
+ /**
110
+ * Renderer-selected display camera for this frame. Features that affect the
111
+ * display must derive their active plan from this snapshot instead of
112
+ * scanning every Camera in the composed worlds.
113
+ */
114
+ readonly selectedCamera?: CameraSnapshot;
115
+ /** Renderer capability facts for source extraction and inspect projections. */
116
+ readonly caps?: Readonly<RhiCaps>;
117
+ /** Normal frame-owner projection; feature code must consume this snapshot. */
118
+ readonly frame?: RenderFeatureFrameContext;
76
119
  /** Same-batch World snapshots; absent for synthetic direct feature probes. */
77
120
  readonly visibilitySnapshots?: readonly RenderFeatureWorldVisibilitySnapshot[];
78
121
  /** Host-owned report sink; feature code never owns the merged diagnostic. */
@@ -106,6 +149,8 @@ export interface RenderFeatureSubmission {
106
149
  */
107
150
  export interface RenderFeature<FrameData> {
108
151
  readonly identity: string;
152
+ /** Optional graph placement; omitted features retain the historical post stage. */
153
+ readonly placement?: RenderFeaturePlacement;
109
154
  readonly requiredCapabilities?: readonly RenderFeatureCapabilityKey[];
110
155
  /** Optional feature-owned shader preparation policy; defaults to `validated`. */
111
156
  readonly shaderModuleMode?: RenderFeatureShaderModuleMode;
@@ -151,4 +196,6 @@ export interface RenderFeatureDiagnostics {
151
196
  readonly order: number;
152
197
  readonly status: RenderFeatureStatus;
153
198
  readonly latestError: RenderFeatureErrorDescriptor | undefined;
199
+ /** Optional producer-owned detached inspection snapshot from the latest extract. */
200
+ readonly inspection?: unknown;
154
201
  }
@@ -48,14 +48,14 @@ const FULLSCREEN_DEFAULT_SPEC: PipelineSpec = Object.freeze({
48
48
  /**
49
49
  * Declared sample type for a post-process read entry.
50
50
  *
51
- * Closed union (plan-strategy D-7): only `'depth'` is an explicit sampleType
52
- * discriminant; color inputs are the default (bare `string` or un-suffixed
53
- * `{ key }`). Future texture types (`r32float`, etc.) are add-only.
51
+ * Closed union (plan-strategy D-7): `'depth'` identifies a depth input and
52
+ * `'multisampled-float'` identifies the explicit 4x paired-resolve color
53
+ * input. Color inputs otherwise use the default bare string / `{ key }` form.
54
54
  *
55
55
  * AI-user affordance: IDE autocomplete exposes `'depth'` at the register call
56
56
  * site; misspellings are caught by the type system (AC-08).
57
57
  */
58
- export type PostProcessReadSampleType = 'depth';
58
+ export type PostProcessReadSampleType = 'depth' | 'multisampled-float';
59
59
 
60
60
  // ─── Post-process shader entry (owned by the fullscreen feature host) ──────
61
61
 
@@ -105,6 +105,10 @@ export interface PostProcessShaderEntry {
105
105
  * represent a color input (plan-strategy D-1 / D-7).
106
106
  */
107
107
  readonly reads?: readonly (string | PostProcessReadEntry)[] | undefined;
108
+ /** Use the renderer-owned View bind group at group(0). */
109
+ readonly usesView?: boolean | undefined;
110
+ /** Read-only storage buffers appended to the fullscreen bind group. */
111
+ readonly storageBindings?: readonly number[] | undefined;
108
112
  }
109
113
 
110
114
  function postProcessSourceDigest(source: string): string {
@@ -124,8 +128,10 @@ export function postProcessShaderPipelineLabel(identity: string, source: string)
124
128
  }
125
129
 
126
130
  /** Content-addressed shader-module label shared by prewarm and lazy paths. */
127
- export function postProcessShaderModuleLabel(identity: string, source: string): string {
128
- return `${postProcessShaderPipelineLabel(identity, source)}-module`;
131
+ export function postProcessShaderModuleLabel(source: string): string {
132
+ // Graph-local names and public feature identities may alias the same source.
133
+ // Share the prewarmed module, while pipelines retain their own layouts/targets.
134
+ return `post-process-module-${postProcessSourceDigest(source)}`;
129
135
  }
130
136
 
131
137
  /**
@@ -145,6 +151,8 @@ export function postProcessShaderEntrySignature(entry: PostProcessShaderEntry):
145
151
  defaultValue: Array.from(entry.params.defaultValue),
146
152
  },
147
153
  reads: entry.reads,
154
+ usesView: entry.usesView,
155
+ storageBindings: entry.storageBindings,
148
156
  });
149
157
  }
150
158
 
@@ -161,8 +169,8 @@ export function isTemporalFullscreenBinding(values: Readonly<Record<string, unkn
161
169
  * Structured read entry for a post-process shader.
162
170
  *
163
171
  * `key` is the graph resource key (matching a graph color target).
164
- * `sampleType` is an optional type discriminant (D-1 SSOT); when omitted
165
- * the entry is treated as a color input (same as a bare string).
172
+ * `sampleType` is an optional type discriminant (D-1 SSOT); when omitted the
173
+ * entry is treated as a single-sample color input (same as a bare string).
166
174
  */
167
175
  export interface PostProcessReadEntry {
168
176
  readonly key: string;
@@ -307,6 +315,7 @@ export function buildFullscreenPostProcessPass(
307
315
  sampler: Sampler | null;
308
316
  depthSampler: Sampler | null;
309
317
  extraColorBindings: readonly number[];
318
+ extraStorageBindings: readonly number[];
310
319
  createHandle: (
311
320
  name: string,
312
321
  pipeline: unknown,
@@ -327,9 +336,14 @@ export function buildFullscreenPostProcessPass(
327
336
  // texture variant for a 4-sample scene target. params@2 is always present to
328
337
  // avoid 2x2 kind explosion.
329
338
  const hasDepth = entryHasDepthRead(entry);
339
+ const hasMultisampledColor = (entry.reads ?? []).some(
340
+ (read) => typeof read !== 'string' && read.sampleType === 'multisampled-float',
341
+ );
330
342
  const bglKind: BglKind = hasDepth
331
343
  ? depthMultisampled
332
- ? 'fullscreen-post-with-scene-depth-msaa'
344
+ ? hasMultisampledColor
345
+ ? 'fullscreen-post-with-paired-msaa'
346
+ : 'fullscreen-post-with-scene-depth-msaa'
333
347
  : 'fullscreen-post-with-scene-depth'
334
348
  : entry.params !== undefined
335
349
  ? 'fullscreen-post-with-params'
@@ -347,6 +361,14 @@ export function buildFullscreenPostProcessPass(
347
361
  texture: { sampleType: 'float', viewDimension: '2d' },
348
362
  });
349
363
  }
364
+ const firstStorageBinding = firstExtraBinding + extraColorCount;
365
+ for (const [index, binding] of (entry.storageBindings ?? []).entries()) {
366
+ descriptor.entries.push({
367
+ binding: binding ?? firstStorageBinding + index,
368
+ visibility: GPU_SHADER_STAGE_FRAGMENT,
369
+ buffer: { type: 'read-only-storage' },
370
+ });
371
+ }
350
372
  const bglRes = device.createBindGroupLayout(descriptor);
351
373
  if (!bglRes.ok) {
352
374
  ctx.errorRegistry.fire(bglRes.error);
@@ -364,6 +386,9 @@ export function buildFullscreenPostProcessPass(
364
386
  extraColorBindings: Object.freeze(
365
387
  Array.from({ length: extraColorCount }, (_, index) => firstExtraBinding + index),
366
388
  ),
389
+ extraStorageBindings: Object.freeze(
390
+ (entry.storageBindings ?? []).map((binding, index) => binding ?? firstStorageBinding + index),
391
+ ),
367
392
  createHandle: (name, pipeline, paramsBuffer) => ({
368
393
  name,
369
394
  paramsBuffer,
@@ -402,6 +427,7 @@ export function createFullscreenBindGroup(
402
427
  depthTexView?: TextureView | null,
403
428
  depthSampler?: Sampler | null,
404
429
  additionalViews: readonly { readonly binding: number; readonly view: TextureView }[] = [],
430
+ additionalBuffers: readonly { readonly binding: number; readonly buffer: Buffer }[] = [],
405
431
  ): BindGroup | null {
406
432
  const entries: { binding: number; resource: { kind: string; value: unknown } }[] = [
407
433
  { binding: 0, resource: { kind: 'textureView', value: inputView } },
@@ -427,6 +453,12 @@ export function createFullscreenBindGroup(
427
453
  resource: { kind: 'textureView', value: additional.view },
428
454
  });
429
455
  }
456
+ for (const additional of additionalBuffers) {
457
+ entries.push({
458
+ binding: additional.binding,
459
+ resource: { kind: 'buffer', value: { buffer: additional.buffer } },
460
+ });
461
+ }
430
462
  const res = device.createBindGroup({
431
463
  label: 'fullscreen-post-process-bg',
432
464
  layout: bgl,
@@ -0,0 +1,48 @@
1
+ /** Renderer-local linear ordering of fixed-width GPU row addresses. */
2
+ export class GpuDirtyRanges {
3
+ private first = new Uint32Array(0);
4
+ private second = new Uint32Array(0);
5
+ private readonly counts = new Uint32Array(256);
6
+
7
+ coalesce(slots: readonly number[]): readonly { readonly start: number; readonly end: number }[] {
8
+ if (slots.length === 0) return [];
9
+ if (slots.length > this.first.length) {
10
+ const capacity = 2 ** Math.ceil(Math.log2(slots.length));
11
+ this.first = new Uint32Array(capacity);
12
+ this.second = new Uint32Array(capacity);
13
+ }
14
+ this.first.set(slots);
15
+ let source = this.first;
16
+ let target = this.second;
17
+ for (let shift = 0; shift < 32; shift += 8) {
18
+ this.counts.fill(0);
19
+ for (let i = 0; i < slots.length; i++) {
20
+ const bucket = ((source[i] ?? 0) >>> shift) & 255;
21
+ this.counts[bucket] = (this.counts[bucket] ?? 0) + 1;
22
+ }
23
+ let offset = 0;
24
+ for (let i = 0; i < 256; i++) {
25
+ const count = this.counts[i] ?? 0;
26
+ this.counts[i] = offset;
27
+ offset += count;
28
+ }
29
+ for (let i = 0; i < slots.length; i++) {
30
+ const value = source[i] ?? 0;
31
+ const bucket = (value >>> shift) & 255;
32
+ const index = this.counts[bucket] ?? 0;
33
+ target[index] = value;
34
+ this.counts[bucket] = index + 1;
35
+ }
36
+ [source, target] = [target, source];
37
+ }
38
+ const ranges: { start: number; end: number }[] = [];
39
+ for (let i = 0; i < slots.length; i++) {
40
+ const slot = source[i] ?? 0;
41
+ const previous = ranges.at(-1);
42
+ if (previous !== undefined && slot <= previous.end)
43
+ previous.end = Math.max(previous.end, slot + 1);
44
+ else ranges.push({ start: slot, end: slot + 1 });
45
+ }
46
+ return ranges;
47
+ }
48
+ }
@@ -28,6 +28,8 @@ export interface GpuDrivenBatchKey {
28
28
  readonly resourceIdentity?: string;
29
29
  /** Closed admission lane for the prepared Standard PBR draw. */
30
30
  readonly admission?: 'opaque' | 'alpha-mask';
31
+ /** Graph lane that owns this material after the G-buffer projection. */
32
+ readonly materialPass?: 'deferred' | 'forward-only';
31
33
  }
32
34
 
33
35
  export interface GpuDrivenCandidate {
@@ -66,6 +68,52 @@ export interface SubmissionPlan {
66
68
  readonly visibleCapacity: number;
67
69
  }
68
70
 
71
+ export interface ResourceClassSplitInspection {
72
+ readonly resourceClassCount: number;
73
+ readonly resourceClassSplits: readonly {
74
+ readonly resourceIdentity: string;
75
+ readonly batchIds: readonly number[];
76
+ readonly candidateCount: number;
77
+ }[];
78
+ readonly resourceClassSplitReasons: readonly string[];
79
+ }
80
+
81
+ /**
82
+ * Attribute batch splits to the prepared resource identity. Draw ranges and
83
+ * dynamic page ranges are deliberately absent from this grouping, so they can
84
+ * never be reported as a resource-class split.
85
+ */
86
+ export function inspectResourceClassSplits(
87
+ plan: Pick<SubmissionPlan, 'batches'>,
88
+ ): ResourceClassSplitInspection {
89
+ const groups = new Map<string, { readonly batchIds: number[]; candidateCount: number }>();
90
+ for (const batch of plan.batches) {
91
+ const resourceIdentity = batch.key.resourceIdentity ?? batch.key.materialResourceClass;
92
+ const group = groups.get(resourceIdentity) ?? { batchIds: [], candidateCount: 0 };
93
+ group.batchIds.push(batch.batchId);
94
+ group.candidateCount += batch.candidates.length;
95
+ groups.set(resourceIdentity, group);
96
+ }
97
+ const resourceClassSplits = [...groups.entries()]
98
+ .sort(([left], [right]) => left.localeCompare(right))
99
+ .map(([resourceIdentity, group]) =>
100
+ Object.freeze({
101
+ resourceIdentity,
102
+ batchIds: Object.freeze([...group.batchIds].sort((left, right) => left - right)),
103
+ candidateCount: group.candidateCount,
104
+ }),
105
+ );
106
+ const resourceClassSplitReasons =
107
+ resourceClassSplits.length <= 1
108
+ ? []
109
+ : resourceClassSplits.map(({ resourceIdentity }) => `resource-class:${resourceIdentity}`);
110
+ return Object.freeze({
111
+ resourceClassCount: resourceClassSplits.length,
112
+ resourceClassSplits: Object.freeze(resourceClassSplits),
113
+ resourceClassSplitReasons: Object.freeze(resourceClassSplitReasons),
114
+ });
115
+ }
116
+
69
117
  export interface GpuLodCandidateInput {
70
118
  readonly candidate: GpuDrivenCandidate;
71
119
  readonly rows: readonly GpuLodRow[];
@@ -298,6 +346,7 @@ function eligibleDraws(slot: RenderSceneSlot): readonly EligibleGpuDrivenDraw[]
298
346
  preparedIdentity,
299
347
  resourceIdentity: draw.materialResourceClass,
300
348
  admission,
349
+ materialPass: material.deferredPass === true ? 'deferred' : 'forward-only',
301
350
  },
302
351
  prepared,
303
352
  ...candidatePayload(draw, snapshot),
@@ -657,6 +706,7 @@ export class BatchTopology {
657
706
  inspect(): BatchTopologyInspection {
658
707
  let candidateCount = 0;
659
708
  for (const batch of this.batches.values()) candidateCount += batch.candidates.size;
709
+ const resourceClasses = inspectResourceClassSplits(this.plan());
660
710
  return {
661
711
  revision: this.revision,
662
712
  batchCount: this.batches.size,
@@ -669,6 +719,9 @@ export class BatchTopology {
669
719
  membershipAllocations: this.membershipAllocations,
670
720
  candidateAdds: this.candidateAdds,
671
721
  candidateRemoves: this.candidateRemoves,
722
+ resourceClassCount: resourceClasses.resourceClassCount,
723
+ resourceClassSplits: resourceClasses.resourceClassSplits,
724
+ resourceClassSplitReasons: resourceClasses.resourceClassSplitReasons,
672
725
  };
673
726
  }
674
727
 
@@ -2,9 +2,22 @@ import type { MaterialShaderArtifact } from '@forgeax/engine-shader';
2
2
  import { gpuDrivenMaterialArtifactKey } from '../extract/gpu-driven';
3
3
  import type { RenderSystemInternals } from '../record/render-context';
4
4
  import type { DispatchEntry, RenderableSnapshot } from '../render-system-extract';
5
+ import { hasRequiredVertexInputs } from './prepared-draw';
5
6
 
6
7
  type ArtifactRow = { readonly source: RenderableSnapshot; readonly renderableIndex: number };
7
8
 
9
+ /**
10
+ * Resolve the producer fact that group(3) binding(1) needs a ProbeBlend page.
11
+ * Single-layer medium artifacts are compiled with the ProbeBlend ABI even
12
+ * when their runtime artifact variantSet is intentionally absent.
13
+ */
14
+ export function requiresProbeBlendRecord(artifact: MaterialShaderArtifact): boolean {
15
+ return (
16
+ artifact.variantSet?.includes('PROBE_BLEND_AVAILABLE=true') === true ||
17
+ artifact.receipt?.surface?.model === 'single-layer-medium'
18
+ );
19
+ }
20
+
8
21
  /** Resolve the same producer programs for normal frames and replacement devices. */
9
22
  export function collectGpuDrivenMaterialArtifacts({
10
23
  rows,
@@ -25,7 +38,7 @@ export function collectGpuDrivenMaterialArtifacts({
25
38
  }) {
26
39
  const materialArtifacts = new Map<string, MaterialShaderArtifact>();
27
40
  const shadowMaterialArtifacts = new Map<string, MaterialShaderArtifact>();
28
- const shadowProgramIds = new Map<string, string>();
41
+ const shadowProgramEntries = new Map<string, DispatchEntry>();
29
42
  const forwardProgramEntries = new Map<
30
43
  string,
31
44
  { readonly vertexEntry?: string; readonly fragmentEntry?: string }
@@ -41,10 +54,7 @@ export function collectGpuDrivenMaterialArtifacts({
41
54
  }
42
55
  for (const entry of shadowDispatch) {
43
56
  if (entry.tags.LightMode !== 'ShadowCaster' || entry.materialShaderId === undefined) continue;
44
- shadowProgramIds.set(
45
- `${entry.renderableIndex}:${entry.materialHandle}`,
46
- entry.materialShaderId,
47
- );
57
+ shadowProgramEntries.set(`${entry.renderableIndex}:${entry.materialHandle}`, entry);
48
58
  }
49
59
  const collectArtifacts = (
50
60
  rows: readonly ArtifactRow[],
@@ -57,20 +67,26 @@ export function collectGpuDrivenMaterialArtifacts({
57
67
  const prepared = draw.prepared;
58
68
  if (prepared === undefined) continue;
59
69
  const material = row.source.materials[draw.materialSlot] ?? row.source.material;
70
+ const programKey = `${row.renderableIndex}:${material.materialHandle ?? -1}`;
71
+ // Keep program identity and entry points on the same selected dispatch.
72
+ // Looking this up per draw must not scan the scene's dispatch roster.
73
+ const shadowProgramEntry = shadowProgramEntries.get(programKey);
60
74
  const logicalMaterialShaderId = material.materialShaderId ?? prepared.identity.material;
61
75
  const sceneIndexProgramKeys = material.materialSceneIndexProgramKeys;
62
76
  const directMaterialShaderId =
63
- pass === 'shadow'
64
- ? shadowProgramIds.get(`${row.renderableIndex}:${material.materialHandle ?? -1}`)
65
- : logicalMaterialShaderId;
66
- const directPassNames = new Set(
67
- Object.entries(material.materialProgramKeys ?? {})
68
- .filter(([, key]) => key === directMaterialShaderId)
69
- .map(([passName]) => passName),
70
- );
71
- const sceneIndexProgramKey = Object.entries(sceneIndexProgramKeys ?? {}).find(
72
- ([passName, program]) => program.pass === pass && directPassNames.has(passName),
73
- )?.[1]?.specializationKey;
77
+ pass === 'shadow' ? shadowProgramEntry?.materialShaderId : logicalMaterialShaderId;
78
+ let sceneIndexProgramKey: string | undefined;
79
+ for (const passName in sceneIndexProgramKeys) {
80
+ const program = sceneIndexProgramKeys[passName];
81
+ if (
82
+ program?.pass === pass &&
83
+ directMaterialShaderId !== undefined &&
84
+ material.materialProgramKeys?.[passName] === directMaterialShaderId
85
+ ) {
86
+ sceneIndexProgramKey = program.specializationKey;
87
+ break;
88
+ }
89
+ }
74
90
  // A modern publication may expose a scene-index pair for its
75
91
  // forward Pass but omit ShadowCaster. Keep that material on its
76
92
  // explicit CPU shadow lane; do not reinterpret its direct WGSL
@@ -81,7 +97,7 @@ export function collectGpuDrivenMaterialArtifacts({
81
97
  const materialShaderId =
82
98
  pass === 'shadow'
83
99
  ? (sceneIndexProgramKey ??
84
- shadowProgramIds.get(`${row.renderableIndex}:${material.materialHandle ?? -1}`) ??
100
+ shadowProgramEntry?.materialShaderId ??
85
101
  logicalMaterialShaderId)
86
102
  : (sceneIndexProgramKey ?? logicalMaterialShaderId);
87
103
  const artifact = resolve(materialShaderId, {
@@ -97,14 +113,7 @@ export function collectGpuDrivenMaterialArtifacts({
97
113
  : {}),
98
114
  });
99
115
  const programEntry =
100
- pass === 'shadow'
101
- ? shadowDispatch.find(
102
- (entry) =>
103
- entry.tags.LightMode === 'ShadowCaster' &&
104
- entry.renderableIndex === row.renderableIndex &&
105
- entry.materialHandle === (material.materialHandle ?? -1),
106
- )
107
- : forwardProgramEntries.get(`${row.renderableIndex}:${material.materialHandle ?? -1}`);
116
+ pass === 'shadow' ? shadowProgramEntry : forwardProgramEntries.get(programKey);
108
117
  const selectedArtifact =
109
118
  artifact === undefined
110
119
  ? undefined
@@ -136,13 +145,7 @@ export function collectGpuDrivenMaterialArtifacts({
136
145
  const shadowVertexInputsMatch =
137
146
  pass !== 'shadow' ||
138
147
  (receipt !== undefined &&
139
- receipt.vertexInputs.length === prepared.vertexInputs.length &&
140
- receipt.vertexInputs.every(
141
- (input, index) =>
142
- input.semantic === prepared.vertexInputs[index]?.semantic &&
143
- input.location === prepared.vertexInputs[index]?.location &&
144
- input.format === prepared.vertexInputs[index]?.format,
145
- ));
148
+ hasRequiredVertexInputs(receipt.vertexInputs, prepared.vertexInputs));
146
149
  if (
147
150
  selectedIdentityArtifact === undefined ||
148
151
  receipt === undefined ||