@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
@@ -4,12 +4,21 @@ export interface MotionBlurParams {
4
4
  readonly shutterAngle: number;
5
5
  readonly maxRadiusPixels: number;
6
6
  readonly sampleCount: number;
7
+ /** Target presentation rate used to convert frame displacement to exposure. 0 follows frame time. */
8
+ readonly targetFps: number;
7
9
  }
8
10
 
11
+ /** WGSL uniform size for the renderer-owned Motion Blur parameter block. */
12
+ export const MOTION_BLUR_PARAMS_BYTE_SIZE = 32;
13
+
14
+ /** The only quality tiers the shader is allowed to execute. */
15
+ export type MotionBlurSampleTier = 0 | 4 | 8 | 16;
16
+
9
17
  export const DEFAULT_MOTION_BLUR_PARAMS: MotionBlurParams = Object.freeze({
10
18
  shutterAngle: 180,
11
19
  maxRadiusPixels: 32,
12
20
  sampleCount: 8,
21
+ targetFps: 60,
13
22
  });
14
23
 
15
24
  export interface MotionBlurInvalidParamsDetail {
@@ -68,17 +77,76 @@ export function validateMotionBlurParams(
68
77
  const shutterAngle = value.shutterAngle ?? DEFAULT_MOTION_BLUR_PARAMS.shutterAngle;
69
78
  const maxRadiusPixels = value.maxRadiusPixels ?? DEFAULT_MOTION_BLUR_PARAMS.maxRadiusPixels;
70
79
  const sampleCount = value.sampleCount ?? DEFAULT_MOTION_BLUR_PARAMS.sampleCount;
80
+ const targetFps = value.targetFps ?? DEFAULT_MOTION_BLUR_PARAMS.targetFps;
71
81
  const shutterError = validateField('shutterAngle', shutterAngle, 0, 360);
72
82
  if (shutterError !== undefined) return err(shutterError);
73
83
  const radiusError = validateField('maxRadiusPixels', maxRadiusPixels, 0, 64);
74
84
  if (radiusError !== undefined) return err(radiusError);
75
85
  const sampleError = validateField('sampleCount', sampleCount, 4, 16, true);
76
86
  if (sampleError !== undefined) return err(sampleError);
77
- return ok(Object.freeze({ shutterAngle, maxRadiusPixels, sampleCount }));
87
+ const targetFpsError = validateField('targetFps', targetFps, 0, 240, true);
88
+ if (targetFpsError !== undefined) return err(targetFpsError);
89
+ return ok(Object.freeze({ shutterAngle, maxRadiusPixels, sampleCount, targetFps }));
78
90
  }
79
91
 
80
92
  export function motionBlurTemporalDemand(params: MotionBlurParams | undefined): boolean {
81
- return params !== undefined && params.shutterAngle > 0;
93
+ return params !== undefined && params.shutterAngle > 0 && params.maxRadiusPixels > 0;
94
+ }
95
+
96
+ /** Round authored work down to the bounded shader tiers without changing exposure length. */
97
+ export function effectiveMotionBlurSampleCount(sampleCount: number): MotionBlurSampleTier {
98
+ if (!Number.isFinite(sampleCount) || sampleCount < 4) return 0;
99
+ if (sampleCount < 8) return 4;
100
+ if (sampleCount < 16) return 8;
101
+ return 16;
102
+ }
103
+
104
+ /** Reject a frame interval that cannot represent a stable temporal pair. */
105
+ export function isMotionBlurIntervalValid(frameDeltaSeconds: number): boolean {
106
+ return Number.isFinite(frameDeltaSeconds) && frameDeltaSeconds > 0 && frameDeltaSeconds <= 0.1;
107
+ }
108
+
109
+ /**
110
+ * Convert current-to-previous frame displacement into the authored target-rate exposure.
111
+ * Invalid intervals return zero so callers can commit a fresh baseline without blurring it.
112
+ */
113
+ export function motionBlurExposureScale(frameDeltaSeconds: number, targetFps: number): number {
114
+ if (!isMotionBlurIntervalValid(frameDeltaSeconds)) return 0;
115
+ if (
116
+ !Number.isFinite(targetFps) ||
117
+ targetFps < 0 ||
118
+ targetFps > 240 ||
119
+ !Number.isInteger(targetFps)
120
+ ) {
121
+ return 0;
122
+ }
123
+ if (targetFps === 0) return 1;
124
+ return 1 / (targetFps * frameDeltaSeconds);
125
+ }
126
+
127
+ /**
128
+ * Derive a render interval from the host sample clock before ECS time policy
129
+ * clamps it. A finite raw pair is never clamped here, so a hitch remains
130
+ * visible to temporal admission.
131
+ */
132
+ export function motionBlurSampleDelta(
133
+ sampleTimeSeconds: number | undefined,
134
+ previousSampleTimeSeconds: number | undefined,
135
+ fallbackDeltaSeconds: number,
136
+ ): number {
137
+ // An explicitly supplied non-finite sample is a temporal discontinuity. Do
138
+ // not silently replace it with ECS delta: that would turn a corrupt host or
139
+ // replay clock into an apparently valid blur interval.
140
+ if (
141
+ (sampleTimeSeconds !== undefined && !Number.isFinite(sampleTimeSeconds)) ||
142
+ (previousSampleTimeSeconds !== undefined && !Number.isFinite(previousSampleTimeSeconds))
143
+ ) {
144
+ return Number.NaN;
145
+ }
146
+ if (Number.isFinite(sampleTimeSeconds) && Number.isFinite(previousSampleTimeSeconds)) {
147
+ return (sampleTimeSeconds as number) - (previousSampleTimeSeconds as number);
148
+ }
149
+ return fallbackDeltaSeconds;
82
150
  }
83
151
 
84
152
  export function resolveMotionBlurParams(
@@ -0,0 +1,176 @@
1
+ import type { MotionBlurExecutionReceipt, MotionBlurInspection } from '../../inspection-types';
2
+ import type { CameraSnapshot } from '../../render-contract';
3
+ import type { TemporalFrameTransactionInspection } from '../../temporal/frame';
4
+ import type { TemporalView } from '../../temporal/view';
5
+ import type { MotionBlurFeatureInput } from './motion-blur-feature';
6
+ import {
7
+ DEFAULT_MOTION_BLUR_PARAMS,
8
+ effectiveMotionBlurSampleCount,
9
+ isMotionBlurIntervalValid,
10
+ type MotionBlurValidationError,
11
+ motionBlurSampleDelta,
12
+ motionBlurTemporalDemand,
13
+ } from './motion-blur-params';
14
+
15
+ export interface MotionBlurRuntimeCapabilities {
16
+ readonly compute: boolean;
17
+ readonly storageBuffer: boolean;
18
+ readonly storageTexture: boolean;
19
+ readonly rgba16floatRenderable: boolean;
20
+ }
21
+
22
+ export interface MotionBlurFramePlan {
23
+ readonly rawSampleDelta: number;
24
+ readonly demanded: boolean;
25
+ readonly featureInput: MotionBlurFeatureInput | undefined;
26
+ }
27
+
28
+ export interface MotionBlurDrawOptions {
29
+ readonly sampleTimeSeconds?: number;
30
+ readonly temporalReset?: true;
31
+ }
32
+
33
+ export function motionBlurDrawOptions(input: {
34
+ readonly sampleTimeSeconds?: number;
35
+ readonly temporalReset?: boolean;
36
+ }): MotionBlurDrawOptions {
37
+ return {
38
+ ...(input.sampleTimeSeconds === undefined
39
+ ? {}
40
+ : { sampleTimeSeconds: input.sampleTimeSeconds }),
41
+ ...(input.temporalReset === true ? { temporalReset: true as const } : {}),
42
+ };
43
+ }
44
+
45
+ /** Derive one motion-blur temporal plan before the record transaction starts. */
46
+ export function planMotionBlurFrame(input: {
47
+ readonly params: CameraSnapshot['motionBlur'];
48
+ readonly sampleTimeSeconds: number | undefined;
49
+ readonly acceptedSampleTimeSeconds: number | undefined;
50
+ readonly fallbackDeltaSeconds: number;
51
+ readonly acceptedTemporal: TemporalView | undefined;
52
+ readonly temporalReset: boolean;
53
+ readonly width: number;
54
+ readonly height: number;
55
+ readonly cameraViewIdentity: string;
56
+ readonly historyVersion: number;
57
+ readonly environmentSignature: string;
58
+ readonly fogSignature: string;
59
+ readonly deviceGeneration: number;
60
+ }): MotionBlurFramePlan {
61
+ const rawSampleDelta = motionBlurSampleDelta(
62
+ input.sampleTimeSeconds,
63
+ input.acceptedSampleTimeSeconds,
64
+ input.fallbackDeltaSeconds,
65
+ );
66
+ const accepted = input.acceptedTemporal;
67
+ const reset =
68
+ input.temporalReset ||
69
+ accepted === undefined ||
70
+ (accepted.historyValid === false && accepted.resetReason === undefined) ||
71
+ !isMotionBlurIntervalValid(rawSampleDelta) ||
72
+ accepted.input.width !== input.width ||
73
+ accepted.input.height !== input.height ||
74
+ accepted.input.viewIdentity !== input.cameraViewIdentity ||
75
+ accepted.input.historyVersion !== input.historyVersion ||
76
+ accepted.input.environmentSignature !== input.environmentSignature ||
77
+ accepted.input.fogSignature !== input.fogSignature ||
78
+ accepted.input.deviceGeneration !== input.deviceGeneration;
79
+ const params =
80
+ input.params === undefined
81
+ ? undefined
82
+ : { ...input.params, targetFps: input.params.targetFps ?? 60 };
83
+ const demanded = motionBlurTemporalDemand(params);
84
+ return {
85
+ rawSampleDelta,
86
+ demanded,
87
+ featureInput:
88
+ params === undefined
89
+ ? undefined
90
+ : {
91
+ params,
92
+ frameDeltaSeconds: rawSampleDelta,
93
+ reset: demanded && reset,
94
+ },
95
+ };
96
+ }
97
+
98
+ /** Project the bounded inspection record without exposing renderer internals. */
99
+ export function projectMotionBlurInspection(input: {
100
+ readonly camera: CameraSnapshot | undefined;
101
+ readonly invalidParams: MotionBlurValidationError | undefined;
102
+ readonly capabilities: MotionBlurRuntimeCapabilities;
103
+ readonly acceptedTemporal: TemporalView | undefined;
104
+ readonly transaction: TemporalFrameTransactionInspection;
105
+ readonly execution: MotionBlurExecutionReceipt | undefined;
106
+ readonly temporalFrameId: number | undefined;
107
+ readonly deviceGeneration: number;
108
+ readonly graphGeneration: number;
109
+ }): MotionBlurInspection | undefined {
110
+ if (input.camera?.motionBlur === undefined && input.invalidParams === undefined) {
111
+ return undefined;
112
+ }
113
+ const motionBlur =
114
+ input.camera?.motionBlur === undefined
115
+ ? DEFAULT_MOTION_BLUR_PARAMS
116
+ : { ...input.camera.motionBlur, targetFps: input.camera.motionBlur.targetFps ?? 60 };
117
+ const demanded = motionBlurTemporalDemand(motionBlur);
118
+ const unavailable = demanded && !input.capabilities.rgba16floatRenderable;
119
+ const lane =
120
+ input.capabilities.compute &&
121
+ input.capabilities.storageBuffer &&
122
+ input.capabilities.storageTexture &&
123
+ input.capabilities.rgba16floatRenderable
124
+ ? ('compute' as const)
125
+ : ('raster-limited' as const);
126
+ const accepted = input.acceptedTemporal;
127
+ const submitFailure =
128
+ input.transaction.lastFailure === 'queue-submit-failed' ||
129
+ accepted?.resetReason === 'submit-failure';
130
+ const execution = input.execution;
131
+ const executionAccepted =
132
+ execution !== undefined &&
133
+ input.temporalFrameId === execution.frameId &&
134
+ execution.deviceGeneration === input.deviceGeneration &&
135
+ execution.graphGeneration === input.graphGeneration;
136
+ const active =
137
+ demanded &&
138
+ !unavailable &&
139
+ lane === 'compute' &&
140
+ executionAccepted &&
141
+ accepted?.historyValid === true &&
142
+ accepted.resetReason === undefined;
143
+ return {
144
+ enabled: demanded,
145
+ status:
146
+ input.invalidParams !== undefined
147
+ ? 'invalid'
148
+ : !demanded
149
+ ? 'off'
150
+ : active
151
+ ? 'active'
152
+ : !unavailable && lane === 'raster-limited'
153
+ ? 'limited'
154
+ : 'reset',
155
+ lane,
156
+ shutterAngle: motionBlur.shutterAngle,
157
+ maxRadiusPixels: motionBlur.maxRadiusPixels,
158
+ sampleCount: motionBlur.sampleCount,
159
+ targetFps: motionBlur.targetFps ?? 60,
160
+ effectiveSampleCount: !demanded ? 0 : effectiveMotionBlurSampleCount(motionBlur.sampleCount),
161
+ temporalDemand: demanded ? 'scene-data-temporal-v1' : 'none',
162
+ passName: 'motion-blur',
163
+ historyWrites: 0,
164
+ passCount: !demanded ? 0 : executionAccepted ? 2 : lane === 'raster-limited' ? 1 : 0,
165
+ tapBudget: !demanded ? 0 : effectiveMotionBlurSampleCount(motionBlur.sampleCount),
166
+ ...(executionAccepted ? { execution } : {}),
167
+ ...(accepted?.resetReason === undefined ? {} : { resetReason: accepted.resetReason }),
168
+ ...(input.invalidParams !== undefined
169
+ ? { lastFailure: 'invalid-params' as const }
170
+ : unavailable
171
+ ? { lastFailure: 'scene-data-unavailable' as const }
172
+ : submitFailure
173
+ ? { lastFailure: 'submit-failure' as const }
174
+ : {}),
175
+ };
176
+ }
@@ -2,7 +2,7 @@ import type { GraphBufferAccess, GraphTextureAccess } from '@forgeax/engine-rend
2
2
  import type { RhiCaps, TextureFormat } from '@forgeax/engine-rhi';
3
3
  import { err, ok, type Result } from '@forgeax/engine-types';
4
4
  import type { RenderError } from '../errors/render';
5
- import type { SceneDataTarget } from '../temporal/scene-data';
5
+ import { isSceneDataTarget, type SceneDataTarget } from '../temporal/scene-data';
6
6
  import type {
7
7
  RenderFeatureGpuBufferUsage,
8
8
  RenderFeatureGpuProgramDescriptor,
@@ -51,7 +51,13 @@ export interface RenderFeatureFullscreenProgramDeclaration {
51
51
  readonly kind: 'fullscreen-program';
52
52
  readonly name: string;
53
53
  readonly source: string;
54
+ /** Optional fragment entry point when one WGSL module owns multiple passes. */
55
+ readonly fragmentEntryPoint?: string;
54
56
  readonly reads?: readonly RenderFeatureFullscreenRead[];
57
+ /** Bind the renderer-owned View group at group(0) for camera reconstruction. */
58
+ readonly usesView?: boolean;
59
+ /** Read-only storage buffers appended after the regular color/depth reads. */
60
+ readonly storageBindings?: readonly number[];
55
61
  readonly params?: {
56
62
  readonly byteSize: number;
57
63
  readonly defaultValue: Uint8Array;
@@ -100,8 +106,8 @@ export type RenderFeatureResourceDeclaration =
100
106
  }
101
107
  | { readonly kind: 'texture-view'; readonly value: unknown }
102
108
  | { readonly kind: 'sampler'; readonly value: unknown };
103
- /** Optional graph target that backs a resident sampled resource. */
104
- readonly logicalTarget?: string;
109
+ /** Optional graph target or semantic scene-data target backing the resource. */
110
+ readonly logicalTarget?: RenderFeatureSampledTarget;
105
111
  }
106
112
  | {
107
113
  readonly kind: 'compute-bindings';
@@ -244,7 +250,12 @@ export interface RenderFeatureLogicalTarget {
244
250
 
245
251
  export interface RenderFeaturePlanContext {
246
252
  readonly caps: Readonly<RhiCaps>;
247
- readonly frame: { readonly frameNumber: number };
253
+ readonly frame: {
254
+ readonly frameNumber: number;
255
+ /** Physical render extent used for bounded compute dispatch sizing. */
256
+ readonly width?: number;
257
+ readonly height?: number;
258
+ };
248
259
  readonly generation: number;
249
260
  readonly targets: readonly RenderFeatureLogicalTarget[];
250
261
  /** Read-only semantic scene-data resolver bound to this plan generation. */
@@ -270,10 +281,12 @@ export interface RenderFeaturePlannedFrame {
270
281
  readonly generation: number;
271
282
  readonly signature: string;
272
283
  readonly plan: RenderFeaturePlan;
284
+ /** Placement selected by the installed feature; omitted means the legacy post stage. */
285
+ readonly placement?: import('./types').RenderFeaturePlacement;
273
286
  }
274
287
 
275
288
  export interface RenderFeatureDerivedAccess {
276
- readonly resource: string;
289
+ readonly resource: string | SceneDataTarget;
277
290
  readonly usage: RenderFeatureResourceUsage;
278
291
  }
279
292
 
@@ -821,7 +834,16 @@ export function deriveRenderFeaturePassAccess(
821
834
  declaration?.kind === 'prepared-gpu-resource' &&
822
835
  declaration.logicalTarget !== undefined
823
836
  ) {
824
- accesses.push({ resource: declaration.logicalTarget, usage: 'sampled-read' });
837
+ const layout = layoutEntries.find((candidate) => candidate.binding === entry.binding);
838
+ const usage =
839
+ layout?.storageTexture?.access === 'write-only'
840
+ ? 'storage-write'
841
+ : layout?.storageTexture?.access === 'read-only'
842
+ ? 'storage-read'
843
+ : layout?.storageTexture?.access === 'read-write'
844
+ ? 'sampled-storage-read-write'
845
+ : 'sampled-read';
846
+ accesses.push({ resource: declaration.logicalTarget, usage });
825
847
  }
826
848
  }
827
849
  }
@@ -895,7 +917,9 @@ export function freezeRenderFeaturePlan(
895
917
  if (
896
918
  resource.kind === 'prepared-gpu-resource' &&
897
919
  resource.logicalTarget !== undefined &&
898
- !targets.has(resource.logicalTarget)
920
+ !(typeof resource.logicalTarget === 'string'
921
+ ? targets.has(resource.logicalTarget)
922
+ : isSceneDataTarget(resource.logicalTarget))
899
923
  ) {
900
924
  return err(stageFailure(identity));
901
925
  }
@@ -926,7 +950,8 @@ export function freezeRenderFeaturePlan(
926
950
  const validPrepared =
927
951
  resource?.kind === 'prepared-gpu-resource' &&
928
952
  ((resource.resource.kind === 'buffer' && layout?.buffer !== undefined) ||
929
- (resource.resource.kind === 'texture-view' && layout?.texture !== undefined) ||
953
+ (resource.resource.kind === 'texture-view' &&
954
+ (layout?.texture !== undefined || layout?.storageTexture !== undefined)) ||
930
955
  (resource.resource.kind === 'sampler' && layout?.sampler !== undefined));
931
956
  if (!validBuffer && !validPrepared) {
932
957
  return err(stageFailure(identity));
@@ -17,9 +17,13 @@ import { type BindGroupLayoutDescriptor, err, ok, type Result } from '@forgeax/e
17
17
  import type { DeviceScope, LifecycleResourceSpec } from '../device/device-scope';
18
18
  import { type RenderError, RenderFeaturePreparationFailedError } from '../errors/render';
19
19
  import type { PipelineBuilderShaderModuleFactory } from '../pipeline-builder';
20
+ import type { SceneDataTarget } from '../temporal/scene-data';
20
21
  import type { RenderFeatureTargetHandle } from './targets';
21
22
  import type { RenderFeatureShaderModuleMode } from './types';
22
23
 
24
+ /** A graph target that can be late-bound at encode time. */
25
+ export type RenderFeatureComputeTarget = string | RenderFeatureTargetHandle | SceneDataTarget;
26
+
23
27
  declare const RenderFeatureGpuProgramBrand: unique symbol;
24
28
  export interface RenderFeatureGpuProgramRef {
25
29
  readonly [RenderFeatureGpuProgramBrand]: void;
@@ -104,8 +108,8 @@ export interface RenderFeatureGpuPrepare {
104
108
  ): Result<RenderFeatureGpuBufferRef, RenderError>;
105
109
  prepareTextureView(
106
110
  name: string,
107
- resource: TextureView,
108
- logicalTarget?: RenderFeatureTargetHandle,
111
+ resource: TextureView | undefined,
112
+ logicalTarget?: RenderFeatureComputeTarget,
109
113
  ): Result<RenderFeatureGpuTextureViewRef, RenderError>;
110
114
  prepareSampler(name: string, resource: Sampler): Result<RenderFeatureGpuSamplerRef, RenderError>;
111
115
  prepareBindings(
@@ -141,7 +145,8 @@ export interface RenderFeatureGpuComputePassDescriptor {
141
145
  }
142
146
 
143
147
  export interface RenderFeatureResolvedGpuComputePass {
144
- readonly sampledTargets?: readonly RenderFeatureTargetHandle[];
148
+ readonly sampledTargets?: readonly RenderFeatureComputeTarget[];
149
+ readonly storageTargets?: readonly RenderFeatureComputeTarget[];
145
150
  readonly buffers: readonly {
146
151
  readonly name: string;
147
152
  readonly buffer: Buffer;
@@ -154,7 +159,7 @@ export interface RenderFeatureResolvedGpuComputePass {
154
159
  readonly pipeline: ComputePipeline;
155
160
  readonly bindGroup: BindGroup;
156
161
  readonly resolveBindGroup?: (
157
- resolve: (target: RenderFeatureTargetHandle) => TextureView,
162
+ resolve: (target: RenderFeatureComputeTarget) => TextureView,
158
163
  ) => BindGroup;
159
164
  readonly workgroups: readonly [number, number, number];
160
165
  }
@@ -162,7 +167,7 @@ export interface RenderFeatureResolvedGpuComputePass {
162
167
  readonly pipeline: ComputePipeline;
163
168
  readonly bindGroup: BindGroup;
164
169
  readonly resolveBindGroup?: (
165
- resolve: (target: RenderFeatureTargetHandle) => TextureView,
170
+ resolve: (target: RenderFeatureComputeTarget) => TextureView,
166
171
  ) => BindGroup;
167
172
  readonly indirectBuffer: Buffer;
168
173
  readonly indirectOffset: number;
@@ -179,7 +184,7 @@ export interface RenderFeatureResolvedGpuBuffer {
179
184
  export function encodeRenderFeatureGpuComputePass(
180
185
  pass: RhiComputePassEncoder,
181
186
  work: RenderFeatureResolvedGpuComputePass,
182
- resolveTarget?: (target: RenderFeatureTargetHandle) => TextureView,
187
+ resolveTarget?: (target: RenderFeatureComputeTarget) => TextureView,
183
188
  ): void {
184
189
  for (const dispatch of work.dispatches) {
185
190
  pass.setPipeline(dispatch.pipeline);
@@ -221,6 +226,10 @@ interface ProgramItem {
221
226
  readonly pipelines: ReadonlyMap<string, ComputePipeline>;
222
227
  readonly bindGroupLayout?: BindGroupLayout;
223
228
  readonly bindingLayouts: ReadonlyMap<number, GPUBufferBindingType>;
229
+ readonly textureBindings: ReadonlyMap<
230
+ number,
231
+ 'sampled' | 'storage-write' | 'storage-read' | 'storage-read-write'
232
+ >;
224
233
  }
225
234
 
226
235
  interface BufferItem {
@@ -234,9 +243,9 @@ interface BufferItem {
234
243
  }
235
244
 
236
245
  interface TextureViewItem {
237
- readonly logicalTarget?: RenderFeatureTargetHandle;
246
+ readonly logicalTarget?: RenderFeatureComputeTarget;
238
247
  readonly name: string;
239
- readonly resource: TextureView;
248
+ readonly resource: TextureView | undefined;
240
249
  readonly reference: RenderFeatureGpuTextureViewRef;
241
250
  }
242
251
 
@@ -248,7 +257,7 @@ interface SamplerItem {
248
257
 
249
258
  interface BindingResourceItem {
250
259
  readonly binding: number;
251
- readonly resource: RhiBindingResource;
260
+ readonly resource?: RhiBindingResource;
252
261
  readonly buffer?: BufferItem;
253
262
  readonly textureView?: TextureViewItem;
254
263
  readonly sampler?: SamplerItem;
@@ -623,6 +632,24 @@ function createRenderFeatureGpuWorkSession(input: {
623
632
  pipelineMap.set(entryPoint, pipeline.value);
624
633
  }
625
634
  const reference = Object.freeze({ generation: input.generation }) as RenderFeatureGpuProgramRef;
635
+ const textureBindings = new Map<
636
+ number,
637
+ 'sampled' | 'storage-write' | 'storage-read' | 'storage-read-write'
638
+ >();
639
+ for (const entry of descriptor.bindings?.[0]?.entries ?? []) {
640
+ if (entry.texture !== undefined) {
641
+ textureBindings.set(entry.binding, 'sampled');
642
+ } else if (entry.storageTexture !== undefined) {
643
+ textureBindings.set(
644
+ entry.binding,
645
+ entry.storageTexture.access === 'write-only'
646
+ ? 'storage-write'
647
+ : entry.storageTexture.access === 'read-only'
648
+ ? 'storage-read'
649
+ : 'storage-read-write',
650
+ );
651
+ }
652
+ }
626
653
  const item = {
627
654
  name,
628
655
  descriptor: cloneProgramDescriptor(descriptor),
@@ -636,6 +663,7 @@ function createRenderFeatureGpuWorkSession(input: {
636
663
  : [[entry.binding, entry.buffer.type ?? 'uniform'] as const],
637
664
  ),
638
665
  ),
666
+ textureBindings,
639
667
  ...(bindGroupLayout === undefined ? {} : { bindGroupLayout }),
640
668
  };
641
669
  programs.set(name, item);
@@ -964,7 +992,9 @@ function createRenderFeatureGpuWorkSession(input: {
964
992
  }
965
993
  entryItems.push({
966
994
  binding: entry.binding,
967
- resource: { kind: 'textureView', value: item.resource },
995
+ ...(item.resource === undefined
996
+ ? {}
997
+ : { resource: { kind: 'textureView' as const, value: item.resource } }),
968
998
  textureView: item,
969
999
  });
970
1000
  continue;
@@ -992,7 +1022,7 @@ function createRenderFeatureGpuWorkSession(input: {
992
1022
  entries: entryItems.map((entry) => [
993
1023
  entry.binding,
994
1024
  entry.buffer?.name ?? entry.textureView?.name ?? entry.sampler?.name,
995
- entry.resource.kind,
1025
+ entry.resource?.kind ?? 'late-texture',
996
1026
  ]),
997
1027
  });
998
1028
  const prior = bindings.get(name) ?? parkedBindings.get(name)?.item;
@@ -1026,15 +1056,32 @@ function createRenderFeatureGpuWorkSession(input: {
1026
1056
  ),
1027
1057
  );
1028
1058
  }
1029
- const created = input.device.createBindGroup({
1030
- label: name,
1031
- layout,
1032
- entries: entryItems.map((entry) => ({
1033
- binding: entry.binding,
1034
- resource: entry.resource,
1035
- })),
1036
- });
1037
- if (!created.ok) {
1059
+ const hasLateTextures = entryItems.some(
1060
+ (entry) => entry.textureView?.logicalTarget !== undefined,
1061
+ );
1062
+ const hasMissingResource = entryItems.some((entry) => entry.resource === undefined);
1063
+ if (hasMissingResource && !hasLateTextures) {
1064
+ return err(
1065
+ failure(
1066
+ input.featureIdentity,
1067
+ 'prepare-gpu-bindings',
1068
+ 'bindings',
1069
+ name,
1070
+ 'texture-view-unavailable',
1071
+ ),
1072
+ );
1073
+ }
1074
+ const created = hasLateTextures
1075
+ ? undefined
1076
+ : input.device.createBindGroup({
1077
+ label: name,
1078
+ layout,
1079
+ entries: entryItems.map((entry) => ({
1080
+ binding: entry.binding,
1081
+ resource: entry.resource as RhiBindingResource,
1082
+ })),
1083
+ });
1084
+ if (created !== undefined && !created.ok) {
1038
1085
  return err(
1039
1086
  failure(
1040
1087
  input.featureIdentity,
@@ -1057,7 +1104,10 @@ function createRenderFeatureGpuWorkSession(input: {
1057
1104
  buffers: entryItems.flatMap((entry) =>
1058
1105
  entry.buffer === undefined ? [] : [{ binding: entry.binding, item: entry.buffer }],
1059
1106
  ),
1060
- bindGroup: created.value,
1107
+ // A bind group with semantic targets is created once the graph resolves
1108
+ // the current generation's physical views. The placeholder is never
1109
+ // encoded because resolveComputePass installs resolveBindGroup.
1110
+ bindGroup: created?.value as BindGroup,
1061
1111
  };
1062
1112
  bindings.set(name, item);
1063
1113
  bindingRefs.set(reference, item);
@@ -1188,7 +1238,7 @@ function createRenderFeatureGpuWorkSession(input: {
1188
1238
  (entry) => entry.textureView?.logicalTarget !== undefined,
1189
1239
  )
1190
1240
  ? {
1191
- resolveBindGroup: (resolve: (target: RenderFeatureTargetHandle) => TextureView) => {
1241
+ resolveBindGroup: (resolve: (target: RenderFeatureComputeTarget) => TextureView) => {
1192
1242
  const layout =
1193
1243
  program.bindGroupLayout ??
1194
1244
  (pipeline as ComputePipeline & RhiComputePipelineOps).getBindGroupLayout(0);
@@ -1199,7 +1249,7 @@ function createRenderFeatureGpuWorkSession(input: {
1199
1249
  binding: entry.binding,
1200
1250
  resource:
1201
1251
  entry.textureView?.logicalTarget === undefined
1202
- ? entry.resource
1252
+ ? (entry.resource as RhiBindingResource)
1203
1253
  : {
1204
1254
  kind: 'textureView' as const,
1205
1255
  value: resolve(entry.textureView.logicalTarget),
@@ -1289,19 +1339,20 @@ function createRenderFeatureGpuWorkSession(input: {
1289
1339
  accesses.add('indirect-read');
1290
1340
  resolvedBuffers.set(item, accesses);
1291
1341
  }
1342
+ const sampledTargets = new Set<RenderFeatureComputeTarget>();
1343
+ const storageTargets = new Set<RenderFeatureComputeTarget>();
1344
+ for (const item of dispatchBindings) {
1345
+ for (const entry of item?.entries ?? []) {
1346
+ const target = entry.textureView?.logicalTarget;
1347
+ if (target === undefined) continue;
1348
+ const role = program.textureBindings.get(entry.binding) ?? 'sampled';
1349
+ if (role === 'sampled') sampledTargets.add(target);
1350
+ else storageTargets.add(target);
1351
+ }
1352
+ }
1292
1353
  return ok({
1293
- sampledTargets: [
1294
- ...new Set(
1295
- dispatchBindings.flatMap(
1296
- (item) =>
1297
- item?.entries.flatMap((entry) =>
1298
- entry.textureView?.logicalTarget === undefined
1299
- ? []
1300
- : [entry.textureView.logicalTarget],
1301
- ) ?? [],
1302
- ),
1303
- ),
1304
- ],
1354
+ sampledTargets: [...sampledTargets],
1355
+ storageTargets: [...storageTargets],
1305
1356
  buffers: [...resolvedBuffers].flatMap(([item, accesses]) =>
1306
1357
  [...accesses].map((access) => ({
1307
1358
  name: item.name,
@@ -9,6 +9,7 @@ import {
9
9
  RenderFeatureDrawRecordingFailedError,
10
10
  RenderFeatureStageFailedError,
11
11
  } from '../errors/render';
12
+ import type { RenderFeatureComputeTarget } from './prepared-gpu-work';
12
13
  import {
13
14
  encodeRenderFeatureGpuComputePass,
14
15
  type RenderFeatureResolvedGpuComputePass,
@@ -19,7 +20,6 @@ import {
19
20
  importRenderFeatureGraphBuffer,
20
21
  type RenderFeatureGraphBufferState,
21
22
  } from './render-graph-resources';
22
- import type { RenderFeatureTargetHandle } from './targets';
23
23
 
24
24
  export class RenderFeatureComputeGraphProjection<FrameCtx extends RenderGraphFrame> {
25
25
  private readonly resources: RenderFeatureGraphBufferState;
@@ -41,22 +41,30 @@ export class RenderFeatureComputeGraphProjection<FrameCtx extends RenderGraphFra
41
41
  ): Result<void, RenderGraphError | RenderError> {
42
42
  const accesses = [];
43
43
  const targets = new Map<
44
- RenderFeatureTargetHandle,
44
+ RenderFeatureComputeTarget,
45
45
  NonNullable<ReturnType<RenderFeatureGraphTargetResolver>>
46
46
  >();
47
- for (const handle of work.sampledTargets ?? []) {
47
+ const sampledTargets = new Set(work.sampledTargets ?? []);
48
+ const storageTargets = new Set(work.storageTargets ?? []);
49
+ for (const handle of [...sampledTargets, ...storageTargets]) {
48
50
  let target = this.resolveTarget?.(handle);
49
51
  if (target === undefined)
50
52
  return err(
51
53
  new RenderFeatureStageFailedError(featureIdentity, order, 'record', 'renderer-recover'),
52
54
  );
53
- if (handle.kind === 'scene-depth') {
55
+ if (typeof handle !== 'string' && handle.kind === 'scene-depth') {
54
56
  const view = this.builder.view(target.texture, { aspect: 'depth-only' });
55
57
  if (!view.ok) return view;
56
58
  target = { ...target, view: view.value };
57
59
  }
58
60
  targets.set(handle, target);
59
- accesses.push({ resource: target.view, usage: 'sampled-read' as const });
61
+ if (sampledTargets.has(handle) && storageTargets.has(handle)) {
62
+ accesses.push({ resource: target.view, usage: 'sampled-storage-read-write' as const });
63
+ } else if (storageTargets.has(handle)) {
64
+ accesses.push({ resource: target.view, usage: 'storage-write' as const });
65
+ } else {
66
+ accesses.push({ resource: target.view, usage: 'sampled-read' as const });
67
+ }
60
68
  }
61
69
  for (const resource of work.buffers) {
62
70
  const imported = importRenderFeatureGraphBuffer(