@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
@@ -30,6 +30,7 @@ import type { ManifestEntry } from '@forgeax/engine-types';
30
30
  import { handleSlot } from '@forgeax/engine-types';
31
31
  import { prewarmDepthOfField } from '../features/depth-of-field/depth-of-field-assembly';
32
32
  import { prewarmAtmosphereShaders } from './atmosphere-shader-prewarm';
33
+ import { selectCloudShadowCompatibleEntry } from './cloud-shadow-entry';
33
34
  import { prewarmSsrShaders } from './ssr-shader-prewarm';
34
35
 
35
36
  export type { MaterialShaderManifestEntry } from '@forgeax/engine-shader';
@@ -57,7 +58,7 @@ import {
57
58
  FALLBACK_BYTES_PER_ROW,
58
59
  type SkylightFallback,
59
60
  } from '../ibl/skylight-bind-group';
60
- import type { BloomInspection } from '../inspection-types';
61
+ import { type BloomInspection, emptyBloomInspection } from '../inspection-types';
61
62
  import type { DeviceScope, RhiErrorListenerRegistry } from '../lifecycle';
62
63
  import { assertStorageBufferCap } from '../light-buffer-layout';
63
64
  import {
@@ -85,11 +86,10 @@ import { deriveExtendedLightingCapability } from '../prepare/extended-lighting/r
85
86
  import type { BloomPersistentBundle } from '../record/render-context';
86
87
  import { SHADOW_CASTER_BUFFER_SIZE } from '../record/shadow-pass';
87
88
  import { POINTS_LINES_VIEW_BUFFER_SIZE } from '../record/view-ubo';
88
- import {
89
- type AtmosphereShaderSources,
90
- type SsrShaderSources,
91
- STANDARD_OUTPUT_TRANSFORM_FEATURE_ID,
92
- type VolumetricFogShaderSources,
89
+ import type {
90
+ AtmosphereShaderSources,
91
+ SsrShaderSources,
92
+ VolumetricFogShaderSources,
93
93
  } from '../render-contract';
94
94
  import {
95
95
  MATERIAL_PER_ENTITY_STRIDE,
@@ -119,15 +119,17 @@ import {
119
119
  selectProbePrewarmVariants,
120
120
  selectStandardPbrTransmissionPrewarmVariants,
121
121
  } from './shader-prewarm-policy';
122
+ import { createTemporalShaderPrewarmer } from './temporal-post-process-prewarm';
122
123
  import { buildGpuDrivenPbrReadyLayouts, buildGpuDrivenPbrReadyModules } from './webgpu-pbr-ready';
123
124
  import {
124
- BLUR_PARAMS_BYTES,
125
- BRIGHT_PARAMS_BYTES,
126
- COMPOSITE_PARAMS_BYTES,
125
+ BLOOM_COMPOSITE_PARAMS_BYTES,
126
+ BLOOM_DOWNSAMPLE_PARAMS_BYTES,
127
+ BLOOM_UPSAMPLE_PARAMS_BYTES,
127
128
  HDR_COLOR_ATTACHMENT_FORMAT,
128
129
  VIEW_UBO_BYTES,
129
130
  } from './webgpu-ready-contract';
130
131
  import { prewarmMipmapPipeline } from './webgpu-ready-mipmap';
132
+ import { createReadyPerPassResources } from './webgpu-ready-per-pass';
131
133
 
132
134
  /**
133
135
  * Build the `Renderer.initialization` Promise (D-S3 three-step strict-serial chain).
@@ -219,6 +221,7 @@ export async function buildReadyWebGPU(
219
221
  registerBuiltinTonemap: (source: string) => void,
220
222
  /** Register the special FXAA pass' final-output policy params UBO. */
221
223
  registerBuiltinFxaa: (source: string) => void,
224
+ registerBuiltinAnalyticFog: (source: string) => void,
222
225
  registerBuiltinTemporalPostProcesses: (entries: {
223
226
  readonly motionBlur?: string;
224
227
  readonly taaResolve?: string;
@@ -233,8 +236,7 @@ export async function buildReadyWebGPU(
233
236
  ): Promise<PipelineState> {
234
237
  const extendedLightingShaderAvailable = deriveExtendedLightingCapability(rhiDevice).admitted;
235
238
  // Shader and layout construction consume the same storage capability.
236
- const capValue = (rhiDevice.limits as Readonly<Record<string, number>>)
237
- .maxStorageBuffersPerShaderStage;
239
+ const capValue = rhiDevice.limits.maxStorageBuffersPerShaderStage;
238
240
  const webgl2Downlevel = rhiDevice.caps.backendKind === 'wgpu-webgl2';
239
241
  let storageBufferCapable = !webgl2Downlevel && rhiDevice.caps.storageBuffer;
240
242
  if (typeof capValue === 'number') {
@@ -320,8 +322,8 @@ export async function buildReadyWebGPU(
320
322
  let spriteLitModule: ShaderModule | null = null;
321
323
  let fxaaModule: ShaderModule | null = null;
322
324
  let skyboxModule: ShaderModule | null = null;
323
- let bloomBrightModule: ShaderModule | null = null;
324
- let bloomBlurModule: ShaderModule | null = null;
325
+ let bloomDownsampleModule: ShaderModule | null = null;
326
+ let bloomUpsampleModule: ShaderModule | null = null;
325
327
  let bloomCompositeModule: ShaderModule | null = null;
326
328
  let ssaoModule: ShaderModule | null = null;
327
329
  const findEngineManifestEntry = (identifier: string): ManifestEntry | undefined => {
@@ -385,8 +387,8 @@ export async function buildReadyWebGPU(
385
387
  let probeBackgroundEntry: ManifestEntry | undefined;
386
388
  let fxaaEntry: ManifestEntry | undefined;
387
389
  let skyboxEntry: ManifestEntry | undefined;
388
- let bloomBrightEntry: ManifestEntry | undefined;
389
- let bloomBlurEntry: ManifestEntry | undefined;
390
+ let bloomDownsampleEntry: ManifestEntry | undefined;
391
+ let bloomUpsampleEntry: ManifestEntry | undefined;
390
392
  let bloomCompositeEntry: ManifestEntry | undefined;
391
393
  let ssaoEntry: ManifestEntry | undefined;
392
394
  // feat-20260609 R3-fixup: shadow_caster module pre-bake. T-009 deleted
@@ -462,23 +464,16 @@ export async function buildReadyWebGPU(
462
464
  skyboxEntry ??= entry;
463
465
  continue;
464
466
  }
465
- // feat-20260531-bloom-first-declarative-render-graph-pass / w13:
466
- // bloom marker triage (D-7). Identify the 3 bloom WGSL modules by their
467
- // unique uniform-struct names ('BloomBrightParams' in bloom-bright.wgsl,
468
- // 'BloomBlurParams' in bloom-blur.wgsl shared by H/V pipelines,
469
- // 'BloomCompositeParams' in bloom-composite.wgsl). Struct names are naga
470
- // IR and survive naga_oil composition unchanged -- unlike the original
471
- // `// bloomBrightExtract` content-marker COMMENTS, which naga's WGSL
472
- // writeback drops (comments are not part of the IR), leaving the markers
473
- // absent from the composed `entry.wgsl` so the triage never matched and
474
- // bloom silently never initialised (bug-20260625). Same survives-naga
475
- // rationale as the SSAO `fs_ssao_calc` entry-point marker below.
476
- if (entry.wgsl.includes('BloomBrightParams')) {
477
- bloomBrightEntry ??= entry;
467
+ // Multiscale Bloom marker triage. The composed WGSL retains these
468
+ // uniform-struct names through naga_oil, so shader ownership is resolved
469
+ // from the cooked manifest rather than from a runtime compiler or a
470
+ // second heuristic path.
471
+ if (entry.wgsl.includes('BloomDownsampleParams')) {
472
+ bloomDownsampleEntry ??= entry;
478
473
  continue;
479
474
  }
480
- if (entry.wgsl.includes('BloomBlurParams')) {
481
- bloomBlurEntry ??= entry;
475
+ if (entry.wgsl.includes('BloomUpsampleParams')) {
476
+ bloomUpsampleEntry ??= entry;
482
477
  continue;
483
478
  }
484
479
  if (entry.wgsl.includes('BloomCompositeParams')) {
@@ -595,11 +590,25 @@ export async function buildReadyWebGPU(
595
590
  return wgsl !== undefined ? { ...entry, wgsl } : entry;
596
591
  };
597
592
  pbrEntry = patchRuntimeVariant(pbrEntry, 'forgeax::default-standard-pbr');
593
+ const pbrMaterialEntry = Array.from(registry.materialShaderManifestEntries()).find(
594
+ (entry) => entry.identifier === 'forgeax::default-standard-pbr',
595
+ );
596
+ pbrEntry = selectCloudShadowCompatibleEntry(pbrEntry, pbrMaterialEntry, projectorAvailable);
598
597
  if (pbrSkinEntry !== undefined) {
599
598
  pbrSkinEntry = patchRuntimeVariant(
600
599
  patchRuntimeVariant(pbrSkinEntry, SKIN_MATERIAL_SHADER_ID),
601
600
  'forgeax::default-standard-pbr-skin',
602
601
  );
602
+ const pbrSkinMaterialEntry = Array.from(registry.materialShaderManifestEntries()).find(
603
+ (entry) =>
604
+ entry.identifier === SKIN_MATERIAL_SHADER_ID ||
605
+ entry.identifier === 'forgeax::default-standard-pbr-skin',
606
+ );
607
+ pbrSkinEntry = selectCloudShadowCompatibleEntry(
608
+ pbrSkinEntry,
609
+ pbrSkinMaterialEntry,
610
+ projectorAvailable,
611
+ );
603
612
  }
604
613
  unlitEntry = patchRuntimeVariant(unlitEntry, 'forgeax::default-unlit');
605
614
  if (shadowCasterEntry !== undefined) {
@@ -698,7 +707,7 @@ export async function buildReadyWebGPU(
698
707
  seedShaderModule,
699
708
  });
700
709
  for (const postProcess of requiredFullscreenPostProcesses) {
701
- const label = postProcessShaderModuleLabel(postProcess.identity, postProcess.source);
710
+ const label = postProcessShaderModuleLabel(postProcess.source);
702
711
  const shaderResult = await runShimStep(
703
712
  () =>
704
713
  asyncCreateShaderModule
@@ -1116,7 +1125,7 @@ export async function buildReadyWebGPU(
1116
1125
  // frames (e.g. the hello-tonemap dawn smoke) would stall on a black frame
1117
1126
  // forever. Mirror the shadow_caster prewarm: await-compile the tonemap module
1118
1127
  // here and `seedShaderModule` it under the exact label
1119
- // `buildPostProcessPipeline` requests (`post-process-${id}-module`), so the
1128
+ // `buildPostProcessPipeline` requests (keyed by shader source), so the
1120
1129
  // first `getPostProcessPipeline(STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, …)` hits the module cache
1121
1130
  // synchronously and builds the pipeline on frame 1.
1122
1131
  const tonemapPrewarm = await runShimStep(
@@ -1124,51 +1133,30 @@ export async function buildReadyWebGPU(
1124
1133
  asyncCreateShaderModule
1125
1134
  ? asyncCreateShaderModule(rhiDevice, {
1126
1135
  code: tonemapEntry.wgsl,
1127
- label: postProcessShaderModuleLabel(
1128
- STANDARD_OUTPUT_TRANSFORM_FEATURE_ID,
1129
- tonemapEntry.wgsl,
1130
- ),
1136
+ label: postProcessShaderModuleLabel(tonemapEntry.wgsl),
1131
1137
  })
1132
1138
  : invokeDeviceCreateShaderModule(rhiDevice, {
1133
1139
  code: tonemapEntry.wgsl,
1134
- label: postProcessShaderModuleLabel(
1135
- STANDARD_OUTPUT_TRANSFORM_FEATURE_ID,
1136
- tonemapEntry.wgsl,
1137
- ),
1140
+ label: postProcessShaderModuleLabel(tonemapEntry.wgsl),
1138
1141
  }),
1139
1142
  'shader-compile-failed',
1140
1143
  'tonemap shader module compiled (unified post-process prewarm)',
1141
1144
  'inspect manifest tonemap entry composed wgsl; check device.features',
1142
1145
  );
1143
1146
  if (!tonemapPrewarm.ok) throw tonemapPrewarm.error;
1144
- seedShaderModule(
1145
- postProcessShaderModuleLabel(STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, tonemapEntry.wgsl),
1146
- tonemapPrewarm.value,
1147
- );
1147
+ seedShaderModule(postProcessShaderModuleLabel(tonemapEntry.wgsl), tonemapPrewarm.value);
1148
1148
  registerBuiltinTonemap(tonemapEntry.wgsl);
1149
1149
 
1150
1150
  // Prewarm temporal fullscreen modules before readiness so first-frame
1151
1151
  // recording cannot encounter an asynchronous compile miss.
1152
- const prewarmTemporal = async (entry: ManifestEntry | undefined, id: string) => {
1153
- if (entry === undefined) return;
1154
- const prewarm = await runShimStep(
1155
- () =>
1156
- asyncCreateShaderModule
1157
- ? asyncCreateShaderModule(rhiDevice, {
1158
- code: entry.wgsl,
1159
- label: postProcessShaderModuleLabel(id, entry.wgsl),
1160
- })
1161
- : invokeDeviceCreateShaderModule(rhiDevice, {
1162
- code: entry.wgsl,
1163
- label: postProcessShaderModuleLabel(id, entry.wgsl),
1164
- }),
1165
- 'shader-compile-failed',
1166
- `${id} shader module compiled (unified post-process prewarm)`,
1167
- `inspect manifest ${id} entry composed wgsl; check device.features`,
1168
- );
1169
- if (!prewarm.ok) throw prewarm.error;
1170
- seedShaderModule(postProcessShaderModuleLabel(id, entry.wgsl), prewarm.value);
1171
- };
1152
+ const prewarmTemporal = createTemporalShaderPrewarmer({
1153
+ device: rhiDevice,
1154
+ asyncCreateShaderModule,
1155
+ seedShaderModule,
1156
+ });
1157
+ const analyticFogEntry = findEngineManifestEntry('forgeax::analytic-fog');
1158
+ await prewarmTemporal(analyticFogEntry, 'forgeax.analytic-fog');
1159
+ if (analyticFogEntry !== undefined) registerBuiltinAnalyticFog(analyticFogEntry.wgsl);
1172
1160
  await prewarmTemporal(motionBlurEntry, 'forgeax.motion-blur');
1173
1161
  await prewarmTemporal(taaResolveEntry, 'forgeax.taa-resolve');
1174
1162
  await prewarmDepthOfField(manifestEntries, prewarmTemporal, registerBuiltinDepthOfField);
@@ -1326,6 +1314,12 @@ export async function buildReadyWebGPU(
1326
1314
  if (!fxaaShaderResult.ok) throw fxaaShaderResult.error;
1327
1315
  fxaaModule = fxaaShaderResult.value;
1328
1316
  registerBuiltinFxaa(fxaaEntry.wgsl);
1317
+ // The typed graph may need a second FXAA pipeline for a graph-owned
1318
+ // linear-LDR target (for example BarrelDistortion + FXAA). Seed the
1319
+ // shared post-process adapter with the already compiled module so that
1320
+ // this alternate color format is available on the first submitted
1321
+ // frame instead of waiting for an async recompile.
1322
+ seedShaderModule(postProcessShaderModuleLabel(fxaaEntry.wgsl), fxaaModule);
1329
1323
  }
1330
1324
 
1331
1325
  // feat-20260531-skybox-env-background M3 / w15: skybox shader module.
@@ -1354,40 +1348,43 @@ export async function buildReadyWebGPU(
1354
1348
  // Their construction is still part of renderer readiness because the
1355
1349
  // backend may expose only an asynchronous module factory; the expensive
1356
1350
  // pipeline, binding, and parameter resources stay deferred until admission.
1357
- if (bloomBrightEntry !== undefined) {
1351
+ if (bloomDownsampleEntry !== undefined) {
1358
1352
  const result = await runShimStep(
1359
1353
  () =>
1360
1354
  asyncCreateShaderModule
1361
1355
  ? asyncCreateShaderModule(rhiDevice, {
1362
- code: bloomBrightEntry.wgsl,
1363
- label: 'bloom-bright',
1356
+ code: bloomDownsampleEntry.wgsl,
1357
+ label: 'bloom-downsample',
1364
1358
  })
1365
1359
  : invokeDeviceCreateShaderModule(rhiDevice, {
1366
- code: bloomBrightEntry.wgsl,
1367
- label: 'bloom-bright',
1360
+ code: bloomDownsampleEntry.wgsl,
1361
+ label: 'bloom-downsample',
1368
1362
  }),
1369
1363
  'shader-compile-failed',
1370
- 'bloom-bright shader module compiled',
1371
- 'inspect manifest bloom-bright entry composed wgsl; check device.features',
1364
+ 'bloom-downsample shader module compiled',
1365
+ 'inspect manifest bloom-downsample entry composed wgsl; check device.features',
1372
1366
  );
1373
1367
  if (!result.ok) throw result.error;
1374
- bloomBrightModule = result.value;
1368
+ bloomDownsampleModule = result.value;
1375
1369
  }
1376
- if (bloomBlurEntry !== undefined) {
1370
+ if (bloomUpsampleEntry !== undefined) {
1377
1371
  const result = await runShimStep(
1378
1372
  () =>
1379
1373
  asyncCreateShaderModule
1380
- ? asyncCreateShaderModule(rhiDevice, { code: bloomBlurEntry.wgsl, label: 'bloom-blur' })
1374
+ ? asyncCreateShaderModule(rhiDevice, {
1375
+ code: bloomUpsampleEntry.wgsl,
1376
+ label: 'bloom-upsample',
1377
+ })
1381
1378
  : invokeDeviceCreateShaderModule(rhiDevice, {
1382
- code: bloomBlurEntry.wgsl,
1383
- label: 'bloom-blur',
1379
+ code: bloomUpsampleEntry.wgsl,
1380
+ label: 'bloom-upsample',
1384
1381
  }),
1385
1382
  'shader-compile-failed',
1386
- 'bloom-blur shader module compiled',
1387
- 'inspect manifest bloom-blur entry composed wgsl; check device.features',
1383
+ 'bloom-upsample shader module compiled',
1384
+ 'inspect manifest bloom-upsample entry composed wgsl; check device.features',
1388
1385
  );
1389
1386
  if (!result.ok) throw result.error;
1390
- bloomBlurModule = result.value;
1387
+ bloomUpsampleModule = result.value;
1391
1388
  }
1392
1389
  if (bloomCompositeEntry !== undefined) {
1393
1390
  const result = await runShimStep(
@@ -1493,6 +1490,7 @@ export async function buildReadyWebGPU(
1493
1490
  let hdrpPbrPipelineLayoutHandle: PipelineLayout | null = null;
1494
1491
  let hdrpMeshBindGroupLayoutHandle: BindGroupLayout | null = null;
1495
1492
  let hdrpProbePbrPipelineLayoutHandle: PipelineLayout | null = null;
1493
+ let hdrpUnifiedBglHandle: BindGroupLayout | null = null;
1496
1494
  // The cluster layout is storage-buffer backed. On the WebGL2/fallback
1497
1495
  // capability tier its uniform-buffer projection exceeds the backend's
1498
1496
  // fragment-stage limit, and HDRP is not a valid route there anyway.
@@ -1500,6 +1498,7 @@ export async function buildReadyWebGPU(
1500
1498
  const hdrpUnifiedBglDesc = createHdrpBindGroupLayoutDescriptor();
1501
1499
  const hdrpUnifiedBglRes = rhiDevice.createBindGroupLayout(hdrpUnifiedBglDesc);
1502
1500
  if (hdrpUnifiedBglRes.ok) {
1501
+ hdrpUnifiedBglHandle = hdrpUnifiedBglRes.value;
1503
1502
  hdrpMeshBindGroupLayoutHandle = hdrpUnifiedBglRes.value;
1504
1503
  const hdrpPlRes = rhiDevice.createPipelineLayout({
1505
1504
  label: 'hdrp-pbr-pl',
@@ -1585,6 +1584,7 @@ export async function buildReadyWebGPU(
1585
1584
  gpuDrivenInstancesBindGroupLayout: gpuDrivenInstancesBindGroupLayoutHandle,
1586
1585
  gpuDrivenPbrPipelineLayout: gpuDrivenPbrPipelineLayoutHandle,
1587
1586
  gpuDrivenPbrSkinPipelineLayout: gpuDrivenPbrSkinPipelineLayoutHandle,
1587
+ surfaceDirectInstancesBindGroupLayout: surfaceDirectInstancesBindGroupLayoutHandle,
1588
1588
  } = buildGpuDrivenPbrReadyLayouts(rhiDevice, storageBufferCapable, pbrLayouts.value);
1589
1589
  let hdrpSkinPipelineLayoutHandle: PipelineLayout | null = null;
1590
1590
  let hdrpSkinMeshBindGroupLayoutHandle: BindGroupLayout | null = null;
@@ -1609,6 +1609,46 @@ export async function buildReadyWebGPU(
1609
1609
  }
1610
1610
  }
1611
1611
 
1612
+ // GPU-driven Standard batches use the compact scene-index slot(3) while
1613
+ // retaining the same clustered group(2) bind group that the ordinary main
1614
+ // pass publishes. Build a matching pipeline layout from those exact
1615
+ // producer-owned layouts; a separate URP mesh-array layout would validate
1616
+ // the indirect draw but reject its clustered bind group at encode time.
1617
+ let gpuDrivenClusterPbrPipelineLayoutHandle: PipelineLayout | null = null;
1618
+ let gpuDrivenClusterPbrSkinPipelineLayoutHandle: PipelineLayout | null = null;
1619
+ if (
1620
+ storageBufferCapable &&
1621
+ hdrpUnifiedBglHandle !== null &&
1622
+ gpuDrivenInstancesBindGroupLayoutHandle !== null
1623
+ ) {
1624
+ const rigidResult = rhiDevice.createPipelineLayout({
1625
+ label: 'pbr-gpu-driven-cluster-pl',
1626
+ bindGroupLayouts: [
1627
+ viewBglResult.value,
1628
+ materialBglResult.value,
1629
+ hdrpUnifiedBglHandle,
1630
+ gpuDrivenInstancesBindGroupLayoutHandle,
1631
+ ],
1632
+ });
1633
+ if (rigidResult.ok) gpuDrivenClusterPbrPipelineLayoutHandle = rigidResult.value;
1634
+ }
1635
+ if (
1636
+ storageBufferCapable &&
1637
+ hdrpSkinMeshBindGroupLayoutHandle !== null &&
1638
+ gpuDrivenInstancesBindGroupLayoutHandle !== null
1639
+ ) {
1640
+ const skinResult = rhiDevice.createPipelineLayout({
1641
+ label: 'pbr-skin-gpu-driven-cluster-pl',
1642
+ bindGroupLayouts: [
1643
+ viewBglResult.value,
1644
+ materialBglResult.value,
1645
+ hdrpSkinMeshBindGroupLayoutHandle,
1646
+ gpuDrivenInstancesBindGroupLayoutHandle,
1647
+ ],
1648
+ });
1649
+ if (skinResult.ok) gpuDrivenClusterPbrSkinPipelineLayoutHandle = skinResult.value;
1650
+ }
1651
+
1612
1652
  // feat-20260612-skin-palette-per-frame-upload M1 / m1-2: skin palette
1613
1653
  // allocator. Replaces the prior 16320 B identity-seeded UBO stub (PR #353,
1614
1654
  // feat-20260611 R2 / M8 / w28 IS-14, retired identity-buffer field) with
@@ -1636,9 +1676,7 @@ export async function buildReadyWebGPU(
1636
1676
  const skinPaletteLimitKey = storageBufferCapable
1637
1677
  ? 'maxStorageBufferBindingSize'
1638
1678
  : 'maxUniformBufferBindingSize';
1639
- const skinPaletteDeviceLimit = (rhiDevice.limits as Readonly<Record<string, number>>)[
1640
- skinPaletteLimitKey
1641
- ];
1679
+ const skinPaletteDeviceLimit = rhiDevice.limits[skinPaletteLimitKey];
1642
1680
  const SKIN_PALETTE_MAX_BINDING_BYTES =
1643
1681
  typeof skinPaletteDeviceLimit === 'number' && skinPaletteDeviceLimit > 0
1644
1682
  ? skinPaletteDeviceLimit
@@ -2030,6 +2068,24 @@ export async function buildReadyWebGPU(
2030
2068
  );
2031
2069
  if (!defaultSamplerResult.ok) throw defaultSamplerResult.error;
2032
2070
 
2071
+ // r32float Surface raw-depth views are unfilterable. Keep their sampler
2072
+ // distinct from the filtering material sampler so WebGPU's sample-type
2073
+ // validation remains explicit at the material BGL boundary.
2074
+ const surfaceMediumDepthSamplerResult = runShimSyncStep(
2075
+ () =>
2076
+ rhiDevice.createSampler({
2077
+ label: 'surface-medium-depth-sampler',
2078
+ magFilter: 'nearest',
2079
+ minFilter: 'nearest',
2080
+ addressModeU: 'clamp-to-edge',
2081
+ addressModeV: 'clamp-to-edge',
2082
+ }),
2083
+ 'webgpu-runtime-error',
2084
+ 'createSampler (single-layer medium raw depth) succeeded',
2085
+ 'check device.limits.maxSamplersPerShaderStage',
2086
+ );
2087
+ if (!surfaceMediumDepthSamplerResult.ok) throw surfaceMediumDepthSamplerResult.error;
2088
+
2033
2089
  // feat-20260520-directional-light-shadow-mapping M1c / w8 + M2 / w14:
2034
2090
  // shadow comparison sampler — clamp-to-edge, linear filter, compare:'less'.
2035
2091
  // Used for shadow map sampling in M2/M3 and shadow depth pass. comparison
@@ -2514,10 +2570,10 @@ export async function buildReadyWebGPU(
2514
2570
  } else if (fragModule === skyboxModule) {
2515
2571
  const msaa = d.multisample as Record<string, unknown> | undefined;
2516
2572
  label = msaa !== undefined ? 'skybox-pipeline-msaa' : 'skybox-pipeline';
2517
- } else if (fragModule === bloomBrightModule) {
2518
- label = 'bloom-bright-pipeline';
2519
- } else if (fragModule === bloomBlurModule) {
2520
- label = (d.label as string) ?? 'bloom-blur-h-pipeline';
2573
+ } else if (fragModule === bloomDownsampleModule) {
2574
+ label = 'bloom-downsample-pipeline';
2575
+ } else if (fragModule === bloomUpsampleModule) {
2576
+ label = 'bloom-upsample-pipeline';
2521
2577
  } else if (fragModule === bloomCompositeModule) {
2522
2578
  label = 'bloom-composite-pipeline';
2523
2579
  } else if (fragModule === ssaoModule) {
@@ -2745,23 +2801,18 @@ export async function buildReadyWebGPU(
2745
2801
  let skyboxSamplerHandle: Sampler | null = null;
2746
2802
  let skyboxRotationBufferHandle: Buffer | null = null;
2747
2803
  let skyboxPipelineMsaaHandle: RenderPipeline | null = null;
2748
- // feat-20260531-bloom-first-declarative-render-graph-pass / w13:
2749
- // bloom pipeline handles (D-1, D-4, D-6). Bright + 2x blur (H/V per-axis)
2750
- // + 1x composite = 4 pipelines. Blur H/V share the same WGSL module but
2751
- // are separate pipelines with per-axis texelSize baked at creation (D-1).
2752
- // All 4 use rgba16float target format (D-6).
2753
- let bloomBrightPipelineHandle: RenderPipeline | null = null;
2754
- let bloomBlurHPipelineHandle: RenderPipeline | null = null;
2755
- let bloomBlurVPipelineHandle: RenderPipeline | null = null;
2804
+ // Multiscale Bloom pipeline handles. One level-parameterized downsample
2805
+ // pipeline and one tent upsample pipeline serve the whole five-level graph;
2806
+ // the composite is the only other Bloom pipeline. All use rgba16float.
2807
+ let bloomDownsamplePipelineHandle: RenderPipeline | null = null;
2808
+ let bloomUpsamplePipelineHandle: RenderPipeline | null = null;
2756
2809
  let bloomCompositePipelineHandle: RenderPipeline | null = null;
2757
- let bloomBrightBglHandle: BindGroupLayout | null = null;
2758
- let bloomBlurBglHandle: BindGroupLayout | null = null;
2810
+ let bloomDownsampleBglHandle: BindGroupLayout | null = null;
2811
+ let bloomUpsampleBglHandle: BindGroupLayout | null = null;
2759
2812
  let bloomCompositeBglHandle: BindGroupLayout | null = null;
2760
2813
  let bloomSamplerHandle: Sampler | null = null;
2761
- let bloomBrightParamsBufferHandle: Buffer | null = null;
2762
- // bug-20260625: separate H/V blur params UBOs (see PerPassResources comment).
2763
- let bloomBlurHParamsBufferHandle: Buffer | null = null;
2764
- let bloomBlurVParamsBufferHandle: Buffer | null = null;
2814
+ let bloomDownsampleParamsBufferHandle: Buffer | null = null;
2815
+ let bloomUpsampleParamsBufferHandle: Buffer | null = null;
2765
2816
  let bloomCompositeParamsBufferHandle: Buffer | null = null;
2766
2817
  let bloomResourcesReady = false;
2767
2818
  let ensureBloomResources: (() => void) | undefined;
@@ -2799,22 +2850,7 @@ export async function buildReadyWebGPU(
2799
2850
  const inspectBloomResourcesStable = (
2800
2851
  graph?: import('@forgeax/engine-render-graph').CompiledRenderGraphInfo,
2801
2852
  ): BloomInspection => {
2802
- return (
2803
- inspectBloomResources?.(graph) ?? {
2804
- graphStatus: 'empty',
2805
- enabled: false,
2806
- targetCount: 0,
2807
- targetBytes: 0,
2808
- resourceCount: 0,
2809
- passCount: 0,
2810
- encodeCount: 0,
2811
- bindGroupCount: 0,
2812
- uploadCount: 0,
2813
- residentChildBytes: 0,
2814
- generation: 0,
2815
- state: 'off',
2816
- }
2817
- );
2853
+ return inspectBloomResources?.(graph) ?? emptyBloomInspection();
2818
2854
  };
2819
2855
  // feat-20260612-hdrp-ssao M6 / w26 + w43: SSAO pipeline handles (D-A).
2820
2856
  // calc + blur RenderPipeline pair sharing a dedicated 6-entry BGL.
@@ -3108,18 +3144,10 @@ export async function buildReadyWebGPU(
3108
3144
  skyboxRotationBufferHandle = skyboxRotationBufferResult.value;
3109
3145
  }
3110
3146
 
3111
- // feat-20260531-bloom-first-declarative-render-graph-pass / w13:
3112
- // bloom pipeline assembly (D-1, D-4, D-6). Assembled inside the
3113
- // (unlit+pbr+tonemap) gate for rhiDevice access but bloom modules
3114
- // are optional each guard is independent. When bloom modules are
3115
- // absent (legacy manifest), handles stay null and execute closures
3116
- // skip the bloom passes entirely (zero-overhead opt-out).
3117
- //
3118
- // Pipeline roster:
3119
- // bloom-bright : 1-tex + sampler + UBO@2 BGL, rgba16float target
3120
- // bloom-blur-h : same BGL, same module as blur-v, H-axis texelSize
3121
- // bloom-blur-v : same BGL, same module as blur-h, V-axis texelSize
3122
- // bloom-composite: 2-tex + sampler + UBO@3 BGL, rgba16float target
3147
+ // Multiscale Bloom pipeline assembly. The runtime owns exactly one
3148
+ // parameterized downsample PSO, one tent upsample PSO, and one composite
3149
+ // PSO. The graph owns the five downsample and up to four upsample target
3150
+ // instances; no axis/radius compatibility route is retained.
3123
3151
 
3124
3152
  ensureBloomResources = (): void => {
3125
3153
  if (bloomResourcesReady) return;
@@ -3133,11 +3161,11 @@ export async function buildReadyWebGPU(
3133
3161
  return value;
3134
3162
  };
3135
3163
  try {
3136
- // Shared bloom sampler: linear filter + clamp-to-edge (all 4 passes
3164
+ // Shared Bloom sampler: linear filter + clamp-to-edge (all passes
3137
3165
  // sample from textures using fullscreen triangle UVs).
3138
3166
  if (
3139
- bloomBrightModule !== null ||
3140
- bloomBlurModule !== null ||
3167
+ bloomDownsampleModule !== null ||
3168
+ bloomUpsampleModule !== null ||
3141
3169
  bloomCompositeModule !== null
3142
3170
  ) {
3143
3171
  const bloomSamplerResult = runShimSyncStep(
@@ -3158,12 +3186,12 @@ export async function buildReadyWebGPU(
3158
3186
  bloomSamplerHandle = adopt('binding', bloomSamplerResult.value, () => undefined);
3159
3187
  }
3160
3188
 
3161
- // Bloom bright: 1-tex + sampler + UBO@2 BGL (D-4).
3162
- if (bloomBrightModule !== null && bloomSamplerHandle !== null) {
3163
- const brightBglResult = runShimSyncStep(
3189
+ // Bloom downsample: source texture + sampler + one level slice.
3190
+ if (bloomDownsampleModule !== null && bloomSamplerHandle !== null) {
3191
+ const downsampleBglResult = runShimSyncStep(
3164
3192
  () =>
3165
3193
  rhiDevice.createBindGroupLayout({
3166
- label: 'bloom-bright-bgl',
3194
+ label: 'bloom-downsample-bgl',
3167
3195
  entries: [
3168
3196
  {
3169
3197
  binding: 0,
@@ -3183,30 +3211,30 @@ export async function buildReadyWebGPU(
3183
3211
  ],
3184
3212
  }),
3185
3213
  'webgpu-runtime-error',
3186
- 'createBindGroupLayout(bloom-bright) succeeded',
3214
+ 'createBindGroupLayout(bloom-downsample) succeeded',
3187
3215
  'check device.limits.maxBindGroupsPerPipelineLayout',
3188
3216
  );
3189
- if (!brightBglResult.ok) throw brightBglResult.error;
3190
- bloomBrightBglHandle = adopt('binding', brightBglResult.value, () => undefined);
3217
+ if (!downsampleBglResult.ok) throw downsampleBglResult.error;
3218
+ bloomDownsampleBglHandle = adopt('binding', downsampleBglResult.value, () => undefined);
3191
3219
 
3192
- const brightPlResult = runShimSyncStep(
3220
+ const downsamplePlResult = runShimSyncStep(
3193
3221
  () =>
3194
3222
  rhiDevice.createPipelineLayout({
3195
- label: 'bloom-bright-pl',
3196
- bindGroupLayouts: [brightBglResult.value],
3223
+ label: 'bloom-downsample-pl',
3224
+ bindGroupLayouts: [downsampleBglResult.value],
3197
3225
  }),
3198
3226
  'webgpu-runtime-error',
3199
- 'createPipelineLayout(bloom-bright) succeeded',
3200
- 'verify bloom-bright BindGroupLayout matches shader @group(0) bindings',
3227
+ 'createPipelineLayout(bloom-downsample) succeeded',
3228
+ 'verify bloom-downsample BindGroupLayout matches shader @group(0) bindings',
3201
3229
  );
3202
- if (!brightPlResult.ok) throw brightPlResult.error;
3203
- adopt('binding', brightPlResult.value, () => undefined);
3230
+ if (!downsamplePlResult.ok) throw downsamplePlResult.error;
3231
+ adopt('binding', downsamplePlResult.value, () => undefined);
3204
3232
 
3205
- // M2-T4: bloom-bright pipeline via getOrBuildPipeline (lazy-build).
3233
+ // One pipeline serves D0 extraction and D1..D4 fixed reduction.
3206
3234
  {
3207
- const brightSpec: PipelineSpec = {
3235
+ const downsampleSpec: PipelineSpec = {
3208
3236
  shader: {
3209
- id: 'forgeax::post::bloom-bright',
3237
+ id: 'forgeax::post::bloom-downsample',
3210
3238
  passKind: 'post-process',
3211
3239
  variantSet: undefined,
3212
3240
  },
@@ -3223,20 +3251,21 @@ export async function buildReadyWebGPU(
3223
3251
  renderState: { cullMode: 'none' },
3224
3252
  };
3225
3253
  const modules = {
3226
- vertex: bloomBrightModule,
3227
- fragment: bloomBrightModule,
3228
- layout: brightPlResult.value,
3254
+ vertex: bloomDownsampleModule,
3255
+ fragment: bloomDownsampleModule,
3256
+ layout: downsamplePlResult.value,
3257
+ label: 'bloom-downsample-pipeline',
3229
3258
  };
3230
3259
  try {
3231
- bloomBrightPipelineHandle = getOrBuildPipeline(
3232
- brightSpec,
3260
+ bloomDownsamplePipelineHandle = getOrBuildPipeline(
3261
+ downsampleSpec,
3233
3262
  pipelineDeviceProvider,
3234
3263
  pipelineCache,
3235
3264
  modules,
3236
3265
  ) as RenderPipeline;
3237
- bloomBrightPipelineHandle = adopt(
3266
+ bloomDownsamplePipelineHandle = adopt(
3238
3267
  'pipeline',
3239
- bloomBrightPipelineHandle,
3268
+ bloomDownsamplePipelineHandle,
3240
3269
  () => undefined,
3241
3270
  );
3242
3271
  } catch (err) {
@@ -3244,37 +3273,41 @@ export async function buildReadyWebGPU(
3244
3273
  throw new PipelineSpecError({
3245
3274
  code: 'pipeline-build-failed',
3246
3275
  detail: { cause: err },
3247
- hint: 'createRenderPipeline (bloom-bright fullscreen) failed; inspect gpuMessage',
3276
+ hint: 'createRenderPipeline (bloom-downsample fullscreen) failed; inspect gpuMessage',
3248
3277
  });
3249
3278
  }
3250
3279
  }
3251
3280
 
3252
- // Bright params UBO: 16 B std140 (threshold f32 + 12 B pad).
3253
- const brightParamsResult = runShimSyncStep(
3281
+ // Five 32-byte std140 slices: threshold, knee, destination extent,
3282
+ // level and padding. The level is selected by the graph pass.
3283
+ const downsampleParamsResult = runShimSyncStep(
3254
3284
  () =>
3255
3285
  rhiDevice.createBuffer({
3256
- label: 'bloom-bright-params-ubo',
3257
- size: BRIGHT_PARAMS_BYTES,
3286
+ label: 'bloom-downsample-params-ubo',
3287
+ size: BLOOM_DOWNSAMPLE_PARAMS_BYTES,
3258
3288
  usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
3259
3289
  mappedAtCreation: false,
3260
3290
  }),
3261
3291
  'webgpu-runtime-error',
3262
- 'createBuffer (bloom-bright params UBO) succeeded',
3292
+ 'createBuffer (bloom-downsample params UBO) succeeded',
3263
3293
  'check device.limits.maxUniformBufferBindingSize',
3264
3294
  );
3265
- if (!brightParamsResult.ok) throw brightParamsResult.error;
3266
- bloomBrightParamsBufferHandle = adopt('buffer', brightParamsResult.value, (value) => {
3267
- rhiDevice.destroyBuffer(value);
3268
- });
3295
+ if (!downsampleParamsResult.ok) throw downsampleParamsResult.error;
3296
+ bloomDownsampleParamsBufferHandle = adopt(
3297
+ 'buffer',
3298
+ downsampleParamsResult.value,
3299
+ (value) => {
3300
+ rhiDevice.destroyBuffer(value);
3301
+ },
3302
+ );
3269
3303
  }
3270
3304
 
3271
- // Bloom blur H/V: same BGL (1-tex + sampler + UBO@2), same module,
3272
- // two separate pipelines with per-axis texelSize (D-1, D-4).
3273
- if (bloomBlurModule !== null && bloomSamplerHandle !== null) {
3274
- const blurBglResult = runShimSyncStep(
3305
+ // Bloom upsample: current/coarse textures + sampler + scatter slice.
3306
+ if (bloomUpsampleModule !== null && bloomSamplerHandle !== null) {
3307
+ const upsampleBglResult = runShimSyncStep(
3275
3308
  () =>
3276
3309
  rhiDevice.createBindGroupLayout({
3277
- label: 'bloom-blur-bgl',
3310
+ label: 'bloom-upsample-bgl',
3278
3311
  entries: [
3279
3312
  {
3280
3313
  binding: 0,
@@ -3284,43 +3317,45 @@ export async function buildReadyWebGPU(
3284
3317
  {
3285
3318
  binding: 1,
3286
3319
  visibility: GPU_SHADER_STAGE_FRAGMENT,
3287
- sampler: { type: 'filtering' },
3320
+ texture: { sampleType: 'float', viewDimension: '2d' },
3288
3321
  },
3289
3322
  {
3290
3323
  binding: 2,
3291
3324
  visibility: GPU_SHADER_STAGE_FRAGMENT,
3325
+ sampler: { type: 'filtering' },
3326
+ },
3327
+ {
3328
+ binding: 3,
3329
+ visibility: GPU_SHADER_STAGE_FRAGMENT,
3292
3330
  buffer: { type: 'uniform' },
3293
3331
  },
3294
3332
  ],
3295
3333
  }),
3296
3334
  'webgpu-runtime-error',
3297
- 'createBindGroupLayout(bloom-blur) succeeded',
3335
+ 'createBindGroupLayout(bloom-upsample) succeeded',
3298
3336
  'check device.limits.maxBindGroupsPerPipelineLayout',
3299
3337
  );
3300
- if (!blurBglResult.ok) throw blurBglResult.error;
3301
- bloomBlurBglHandle = adopt('binding', blurBglResult.value, () => undefined);
3338
+ if (!upsampleBglResult.ok) throw upsampleBglResult.error;
3339
+ bloomUpsampleBglHandle = adopt('binding', upsampleBglResult.value, () => undefined);
3302
3340
 
3303
- const blurPlResult = runShimSyncStep(
3341
+ const upsamplePlResult = runShimSyncStep(
3304
3342
  () =>
3305
3343
  rhiDevice.createPipelineLayout({
3306
- label: 'bloom-blur-pl',
3307
- bindGroupLayouts: [blurBglResult.value],
3344
+ label: 'bloom-upsample-pl',
3345
+ bindGroupLayouts: [upsampleBglResult.value],
3308
3346
  }),
3309
3347
  'webgpu-runtime-error',
3310
- 'createPipelineLayout(bloom-blur) succeeded',
3311
- 'verify bloom-blur BindGroupLayout matches shader @group(0) bindings',
3348
+ 'createPipelineLayout(bloom-upsample) succeeded',
3349
+ 'verify bloom-upsample BindGroupLayout matches shader @group(0) bindings',
3312
3350
  );
3313
- if (!blurPlResult.ok) throw blurPlResult.error;
3314
- adopt('binding', blurPlResult.value, () => undefined);
3351
+ if (!upsamplePlResult.ok) throw upsamplePlResult.error;
3352
+ adopt('binding', upsamplePlResult.value, () => undefined);
3315
3353
 
3316
- // M2-T4: bloom-blur H/V pipelines via getOrBuildPipeline (lazy-build).
3317
- // H and V share the same PSO descriptor — only per-axis texelSize UBO
3318
- // distinguishes them at record time. getOrBuildPipeline cache-hit on the
3319
- // second call returns the same handle (identical spec, identical PSO).
3354
+ // One pipeline serves every coarse-to-fine edge.
3320
3355
  {
3321
- const blurSpec: PipelineSpec = {
3356
+ const upsampleSpec: PipelineSpec = {
3322
3357
  shader: {
3323
- id: 'forgeax::post::bloom-blur',
3358
+ id: 'forgeax::post::bloom-upsample',
3324
3359
  passKind: 'post-process',
3325
3360
  variantSet: undefined,
3326
3361
  },
@@ -3337,21 +3372,21 @@ export async function buildReadyWebGPU(
3337
3372
  renderState: { cullMode: 'none' },
3338
3373
  };
3339
3374
  const modules = {
3340
- vertex: bloomBlurModule,
3341
- fragment: bloomBlurModule,
3342
- layout: blurPlResult.value,
3343
- label: 'bloom-blur-h-pipeline',
3375
+ vertex: bloomUpsampleModule,
3376
+ fragment: bloomUpsampleModule,
3377
+ layout: upsamplePlResult.value,
3378
+ label: 'bloom-upsample-pipeline',
3344
3379
  };
3345
3380
  try {
3346
- bloomBlurHPipelineHandle = getOrBuildPipeline(
3347
- blurSpec,
3381
+ bloomUpsamplePipelineHandle = getOrBuildPipeline(
3382
+ upsampleSpec,
3348
3383
  pipelineDeviceProvider,
3349
3384
  pipelineCache,
3350
3385
  modules,
3351
3386
  ) as RenderPipeline;
3352
- bloomBlurHPipelineHandle = adopt(
3387
+ bloomUpsamplePipelineHandle = adopt(
3353
3388
  'pipeline',
3354
- bloomBlurHPipelineHandle,
3389
+ bloomUpsamplePipelineHandle,
3355
3390
  () => undefined,
3356
3391
  );
3357
3392
  } catch (err) {
@@ -3359,47 +3394,26 @@ export async function buildReadyWebGPU(
3359
3394
  throw new PipelineSpecError({
3360
3395
  code: 'pipeline-build-failed',
3361
3396
  detail: { cause: err },
3362
- hint: 'createRenderPipeline (bloom-blur-h fullscreen) failed; inspect gpuMessage',
3397
+ hint: 'createRenderPipeline (bloom-upsample fullscreen) failed; inspect gpuMessage',
3363
3398
  });
3364
3399
  }
3365
- // V is cache-hit on the same spec (identical PSO; per-axis UBO differentiates at record time).
3366
- bloomBlurVPipelineHandle = bloomBlurHPipelineHandle;
3367
3400
  }
3368
3401
 
3369
- // Blur params UBOs: 16 B std140 (texelSize.xy + radius + pad) each.
3370
- // bug-20260625: one per axis -- H and V must not share a buffer (the
3371
- // shared-buffer writeBuffer race made both passes blur vertically).
3372
- const blurHParamsResult = runShimSyncStep(
3402
+ // Four 16-byte std140 slices carry scatter for U0..U3.
3403
+ const upsampleParamsResult = runShimSyncStep(
3373
3404
  () =>
3374
3405
  rhiDevice.createBuffer({
3375
- label: 'bloom-blur-h-params-ubo',
3376
- size: BLUR_PARAMS_BYTES,
3406
+ label: 'bloom-upsample-params-ubo',
3407
+ size: BLOOM_UPSAMPLE_PARAMS_BYTES,
3377
3408
  usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
3378
3409
  mappedAtCreation: false,
3379
3410
  }),
3380
3411
  'webgpu-runtime-error',
3381
- 'createBuffer (bloom-blur-h params UBO) succeeded',
3412
+ 'createBuffer (bloom-upsample params UBO) succeeded',
3382
3413
  'check device.limits.maxUniformBufferBindingSize',
3383
3414
  );
3384
- if (!blurHParamsResult.ok) throw blurHParamsResult.error;
3385
- bloomBlurHParamsBufferHandle = adopt('buffer', blurHParamsResult.value, (value) => {
3386
- rhiDevice.destroyBuffer(value);
3387
- });
3388
-
3389
- const blurVParamsResult = runShimSyncStep(
3390
- () =>
3391
- rhiDevice.createBuffer({
3392
- label: 'bloom-blur-v-params-ubo',
3393
- size: BLUR_PARAMS_BYTES,
3394
- usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
3395
- mappedAtCreation: false,
3396
- }),
3397
- 'webgpu-runtime-error',
3398
- 'createBuffer (bloom-blur-v params UBO) succeeded',
3399
- 'check device.limits.maxUniformBufferBindingSize',
3400
- );
3401
- if (!blurVParamsResult.ok) throw blurVParamsResult.error;
3402
- bloomBlurVParamsBufferHandle = adopt('buffer', blurVParamsResult.value, (value) => {
3415
+ if (!upsampleParamsResult.ok) throw upsampleParamsResult.error;
3416
+ bloomUpsampleParamsBufferHandle = adopt('buffer', upsampleParamsResult.value, (value) => {
3403
3417
  rhiDevice.destroyBuffer(value);
3404
3418
  });
3405
3419
  }
@@ -3505,7 +3519,7 @@ export async function buildReadyWebGPU(
3505
3519
  () =>
3506
3520
  rhiDevice.createBuffer({
3507
3521
  label: 'bloom-composite-params-ubo',
3508
- size: COMPOSITE_PARAMS_BYTES,
3522
+ size: BLOOM_COMPOSITE_PARAMS_BYTES,
3509
3523
  usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
3510
3524
  mappedAtCreation: false,
3511
3525
  }),
@@ -3526,17 +3540,15 @@ export async function buildReadyWebGPU(
3526
3540
  bloomCandidateBundle = {
3527
3541
  scope: candidateScope,
3528
3542
  generation: bloomGeneration + 1,
3529
- bloomBrightPipeline: bloomBrightPipelineHandle,
3530
- bloomBlurHPipeline: bloomBlurHPipelineHandle,
3531
- bloomBlurVPipeline: bloomBlurVPipelineHandle,
3543
+ bloomDownsamplePipeline: bloomDownsamplePipelineHandle,
3544
+ bloomUpsamplePipeline: bloomUpsamplePipelineHandle,
3532
3545
  bloomCompositePipeline: bloomCompositePipelineHandle,
3533
- bloomBrightBindGroupLayout: bloomBrightBglHandle,
3534
- bloomBlurBindGroupLayout: bloomBlurBglHandle,
3546
+ bloomDownsampleBindGroupLayout: bloomDownsampleBglHandle,
3547
+ bloomUpsampleBindGroupLayout: bloomUpsampleBglHandle,
3535
3548
  bloomCompositeBindGroupLayout: bloomCompositeBglHandle,
3536
3549
  bloomSampler: bloomSamplerHandle,
3537
- bloomBrightParamsBuffer: bloomBrightParamsBufferHandle,
3538
- bloomBlurHParamsBuffer: bloomBlurHParamsBufferHandle,
3539
- bloomBlurVParamsBuffer: bloomBlurVParamsBufferHandle,
3550
+ bloomDownsampleParamsBuffer: bloomDownsampleParamsBufferHandle,
3551
+ bloomUpsampleParamsBuffer: bloomUpsampleParamsBufferHandle,
3540
3552
  bloomCompositeParamsBuffer: bloomCompositeParamsBufferHandle,
3541
3553
  };
3542
3554
  inspectBloomResources = (graph): BloomInspection => {
@@ -3550,6 +3562,10 @@ export async function buildReadyWebGPU(
3550
3562
  return {
3551
3563
  graphStatus: 'empty',
3552
3564
  enabled: false,
3565
+ levelCount: 0,
3566
+ levelDimensions: [],
3567
+ downsamplePassCount: 0,
3568
+ upsamplePassCount: 0,
3553
3569
  targetCount: 0,
3554
3570
  targetBytes: 0,
3555
3571
  resourceCount: 0,
@@ -3573,6 +3589,10 @@ export async function buildReadyWebGPU(
3573
3589
  return {
3574
3590
  graphStatus: active === undefined ? 'empty' : graphInspection.status,
3575
3591
  enabled: active !== undefined,
3592
+ levelCount: active === undefined ? 0 : graphInspection.levelCount,
3593
+ levelDimensions: active === undefined ? [] : graphInspection.levelDimensions,
3594
+ downsamplePassCount: active === undefined ? 0 : graphInspection.downsamplePassCount,
3595
+ upsamplePassCount: active === undefined ? 0 : graphInspection.upsamplePassCount,
3576
3596
  targetCount: active === undefined ? 0 : graphInspection.targetCount,
3577
3597
  targetBytes: active === undefined ? 0 : graphInspection.targetBytes,
3578
3598
  resourceCount: ownedBundles.reduce(
@@ -3583,7 +3603,14 @@ export async function buildReadyWebGPU(
3583
3603
  encodeCount: active === undefined ? 0 : receipts.encodeCount,
3584
3604
  bindGroupCount: active === undefined ? 0 : receipts.bindGroupCount,
3585
3605
  uploadCount: active === undefined ? 0 : receipts.uploadCount,
3586
- residentChildBytes: 0,
3606
+ // The graph owns the target allocations. Use its validated
3607
+ // descriptor byte sum for the active child; zero is reserved for
3608
+ // an absent/invalid current graph, never a placeholder for a
3609
+ // live Bloom allocation.
3610
+ residentChildBytes:
3611
+ active === undefined || graphInspection.status !== 'valid'
3612
+ ? 0
3613
+ : graphInspection.targetBytes,
3587
3614
  generation: selected?.generation ?? 0,
3588
3615
  state,
3589
3616
  };
@@ -3660,17 +3687,15 @@ export async function buildReadyWebGPU(
3660
3687
  commitBloomResources = undefined;
3661
3688
  discardBloomResources = undefined;
3662
3689
  drainBloomResources = undefined;
3663
- bloomBrightPipelineHandle = null;
3664
- bloomBlurHPipelineHandle = null;
3665
- bloomBlurVPipelineHandle = null;
3690
+ bloomDownsamplePipelineHandle = null;
3691
+ bloomUpsamplePipelineHandle = null;
3666
3692
  bloomCompositePipelineHandle = null;
3667
- bloomBrightBglHandle = null;
3668
- bloomBlurBglHandle = null;
3693
+ bloomDownsampleBglHandle = null;
3694
+ bloomUpsampleBglHandle = null;
3669
3695
  bloomCompositeBglHandle = null;
3670
3696
  bloomSamplerHandle = null;
3671
- bloomBrightParamsBufferHandle = null;
3672
- bloomBlurHParamsBufferHandle = null;
3673
- bloomBlurVParamsBufferHandle = null;
3697
+ bloomDownsampleParamsBufferHandle = null;
3698
+ bloomUpsampleParamsBufferHandle = null;
3674
3699
  bloomCompositeParamsBufferHandle = null;
3675
3700
  throw cause;
3676
3701
  }
@@ -3924,6 +3949,18 @@ export async function buildReadyWebGPU(
3924
3949
  gpuDrivenPbrPrograms,
3925
3950
  gpuDrivenPbrSkinPipelineLayout:
3926
3951
  unlitModule !== null && pbrModule !== null ? gpuDrivenPbrSkinPipelineLayoutHandle : null,
3952
+ surfaceDirectInstancesBindGroupLayout:
3953
+ unlitModule !== null && pbrModule !== null
3954
+ ? surfaceDirectInstancesBindGroupLayoutHandle
3955
+ : null,
3956
+ gpuDrivenClusterPbrPipelineLayout:
3957
+ unlitModule !== null && pbrModule !== null ? gpuDrivenClusterPbrPipelineLayoutHandle : null,
3958
+ gpuDrivenClusterPbrSkinPipelineLayout:
3959
+ unlitModule !== null && pbrModule !== null
3960
+ ? gpuDrivenClusterPbrSkinPipelineLayoutHandle
3961
+ : null,
3962
+ gpuDrivenClusterMeshBindGroupLayout:
3963
+ unlitModule !== null && pbrModule !== null ? hdrpUnifiedBglHandle : null,
3927
3964
  // HDRP variant uses the unified group(2) layout built above.
3928
3965
  hdrpPbrPipelineLayout:
3929
3966
  unlitModule !== null && pbrModule !== null ? hdrpPbrPipelineLayoutHandle : null,
@@ -3969,6 +4006,7 @@ export async function buildReadyWebGPU(
3969
4006
  probeInstancesBindGroupLayout: probeInstancesBglResult.value,
3970
4007
  identityInstanceBuffer: identityInstanceResult.value,
3971
4008
  defaultSampler: defaultSamplerResult.value,
4009
+ surfaceMediumDepthSampler: surfaceMediumDepthSamplerResult.value,
3972
4010
  fallbackTextureView: fallbackTextureViewResult.value,
3973
4011
  defaultWhiteTextureView: fallbackTextureViewResult.value,
3974
4012
  // Exactly neutral half-float RG=(0.5,0.5) decodes to tangent (0,0,1).
@@ -3997,66 +4035,30 @@ export async function buildReadyWebGPU(
3997
4035
  // matrices fold into the View UBO tail (`view.spotLightViewProj`); no
3998
4036
  // dedicated buffer / view-BG binding 9 (WebGL2 uniform-buffer budget).
3999
4037
  skylightFallback,
4000
- // feat-20260529-rendergraph-pass-abstraction M3 / w11 (D-2 + Finding 3):
4001
- // per-pass mutable resource slots moved to PerPassResources.
4002
- perPassResources: {
4003
- depthTexture: null,
4004
- depthTextureView: null,
4005
- depthTextureWidth: 0,
4006
- depthTextureHeight: 0,
4007
- configured: false,
4008
- hdrColorTexture: null,
4009
- hdrColorView: null,
4010
- hdrDepthTexture: null,
4011
- hdrDepthView: null,
4012
- hdrTextureWidth: 0,
4013
- hdrTextureHeight: 0,
4014
- hdrDepthSampleCount: 1,
4038
+ perPassResources: createReadyPerPassResources({
4015
4039
  fxaaPipeline: fxaaPipelineHandle,
4016
4040
  fxaaBindGroupLayout: fxaaBglHandle,
4017
4041
  fxaaSampler: fxaaSamplerHandle,
4018
- // feat-20260604-learn-render-4.10-anti-aliasing-msaa M2 / w7: MSAA
4019
- // attachment slots. All null/0 until the first antialias='msaa' frame.
4020
- msaaColorTexture: null,
4021
- msaaColorView: null,
4022
- msaaSpriteColorTexture: null,
4023
- msaaSpriteColorView: null,
4024
- msaaDepthTexture: null,
4025
- msaaDepthView: null,
4026
- msaaTextureWidth: 0,
4027
- msaaTextureHeight: 0,
4028
- hdrColorMsaaTexture: null,
4029
- hdrColorMsaaView: null,
4030
4042
  skyboxPipeline: skyboxPipelineHandle,
4031
4043
  skyboxPipelineMsaa: skyboxPipelineMsaaHandle,
4032
4044
  skyboxBindGroupLayout: skyboxBglHandle,
4033
4045
  skyboxSampler: skyboxSamplerHandle,
4034
4046
  skyboxRotationBuffer: skyboxRotationBufferHandle,
4035
- shadowTexture: null,
4036
- shadowMapSize: 0,
4037
- shadowCascadeCount: 0,
4038
4047
  shadowSampler: shadowSamplerResult.value,
4039
4048
  shadowLightSpaceMatrix: null,
4040
4049
  shadowCsmLightViewProj: null,
4041
4050
  shadowCsmSelection: null,
4042
- // feat-20260531-bloom-first-declarative-render-graph-pass / w13 + w16:
4043
- // bloom per-pass resource slots. Pipeline handles assembled during
4044
- // buildReadyWebGPU (marker-triage + compile + createRenderPipeline).
4045
- // Intermediate textures are allocate in the execute closures at 1/2-res
4046
- // (ensureLazyTexture, slot width/height tracking for size-drift rebuild).
4047
- // BindGroup caches survive until the intermediate view is invalidated
4048
- // by a resize (width/height drift forces null).
4049
- bloomBrightPipeline: null,
4050
- bloomBlurHPipeline: null,
4051
- bloomBlurVPipeline: null,
4051
+ // Multiscale Bloom pipeline handles are generation-owned by the ready
4052
+ // builder; graph targets are frame-owned and are not stored here.
4053
+ bloomDownsamplePipeline: null,
4054
+ bloomUpsamplePipeline: null,
4052
4055
  bloomCompositePipeline: null,
4053
- bloomBrightBindGroupLayout: null,
4054
- bloomBlurBindGroupLayout: null,
4056
+ bloomDownsampleBindGroupLayout: null,
4057
+ bloomUpsampleBindGroupLayout: null,
4055
4058
  bloomCompositeBindGroupLayout: null,
4056
4059
  bloomSampler: null,
4057
- bloomBrightParamsBuffer: null,
4058
- bloomBlurHParamsBuffer: null,
4059
- bloomBlurVParamsBuffer: null,
4060
+ bloomDownsampleParamsBuffer: null,
4061
+ bloomUpsampleParamsBuffer: null,
4060
4062
  bloomCompositeParamsBuffer: null,
4061
4063
  ensureBloomResources: ensureBloomResourcesStable,
4062
4064
  getBloomResources: getBloomResourcesStable,
@@ -4068,29 +4070,17 @@ export async function buildReadyWebGPU(
4068
4070
  retireBloomResources: retireBloomResourcesStable,
4069
4071
  drainBloomResources: drainBloomResourcesStable,
4070
4072
  inspectBloomResources: inspectBloomResourcesStable,
4071
- bloomBrightTexture: null,
4072
- bloomBrightView: null,
4073
- bloomBrightWidth: 0,
4074
- bloomBrightHeight: 0,
4075
- bloomBlurHTexture: null,
4076
- bloomBlurHView: null,
4077
- bloomBlurHWidth: 0,
4078
- bloomBlurHHeight: 0,
4079
- bloomBlurVTexture: null,
4080
- bloomBlurVView: null,
4081
- bloomBlurVWidth: 0,
4082
- bloomBlurVHeight: 0,
4083
4073
  // feat-20260612-hdrp-ssao M6 / w26 + M8 / w38: SSAO pipeline slots.
4084
4074
  ssaoCalcPipeline: ssaoCalcPipelineHandle,
4085
4075
  ssaoBlurPipeline: ssaoBlurPipelineHandle,
4086
4076
  ssaoBgl: ssaoBglHandle,
4087
- // M8 / w38: lazy-allocated on first SSAO record frame. Sampler kinds
4088
- // and the 1x1 ssaoRaw fallback view are constant across frames and
4089
- // cached after first construction.
4090
- ssaoFilteringSampler: null,
4091
- ssaoDepthSampler: null,
4092
- ssaoFallbackRawView: null,
4093
- },
4077
+ }),
4094
4078
  };
4079
+ // Boot-time fallback resources submit real GPU work before the renderer is
4080
+ // published. Keep initialization as the readiness barrier for the first
4081
+ // frame: a Worker can otherwise receive `ready`, start its frame deadline,
4082
+ // and report `frame-submitted` while that earlier queue work still holds the
4083
+ // receipt's completion fence.
4084
+ await queue.onSubmittedWorkDone();
4095
4085
  return pipelineState;
4096
4086
  }