@forgeax/engine-render 0.1.29 → 0.1.31

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 (350) hide show
  1. package/README.md +248 -21
  2. package/dist/assembly/atmosphere-shader-prewarm.d.ts +1 -1
  3. package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -1
  4. package/dist/assembly/device-feature-admission.d.ts +4 -3
  5. package/dist/assembly/device-feature-admission.d.ts.map +1 -1
  6. package/dist/assembly/dynamic-geometry-host.d.ts +6 -0
  7. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
  8. package/dist/assembly/host-contract.d.ts +6 -0
  9. package/dist/assembly/host-contract.d.ts.map +1 -1
  10. package/dist/assembly/material/assembly.d.ts +26 -3
  11. package/dist/assembly/material/assembly.d.ts.map +1 -1
  12. package/dist/assembly/material/pipeline-helpers.d.ts +24 -0
  13. package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -0
  14. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  15. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
  16. package/dist/assembly/recovery/recovery-attempt.d.ts +1 -6
  17. package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -1
  18. package/dist/assembly/recovery/renderer-recover.d.ts +1 -0
  19. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  20. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  21. package/dist/assembly/shader-prewarm-policy.d.ts +1 -1
  22. package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -1
  23. package/dist/assembly/webgpu-ready.d.ts +1 -1
  24. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  25. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  26. package/dist/capture/scene-projection.d.ts +41 -0
  27. package/dist/capture/scene-projection.d.ts.map +1 -0
  28. package/dist/{chunk-BQVQOQJH.mjs → chunk-7MQ3XBSP.mjs} +20 -14
  29. package/dist/chunk-7MQ3XBSP.mjs.map +1 -0
  30. package/dist/{chunk-S4DNKQYQ.mjs → chunk-C3DDSXYK.mjs} +86 -33
  31. package/dist/chunk-C3DDSXYK.mjs.map +1 -0
  32. package/dist/{chunk-BTWH7BP4.mjs → chunk-FUUO4UND.mjs} +73 -29
  33. package/dist/chunk-FUUO4UND.mjs.map +1 -0
  34. package/dist/{chunk-NRNXBLIJ.mjs → chunk-JRBDO2KB.mjs} +46 -6
  35. package/dist/chunk-JRBDO2KB.mjs.map +1 -0
  36. package/dist/{chunk-NICXSHAT.mjs → chunk-MDQJPY3T.mjs} +8 -6
  37. package/dist/chunk-MDQJPY3T.mjs.map +1 -0
  38. package/dist/{chunk-JQTSEI3Z.mjs → chunk-NO4WZCAH.mjs} +3 -3
  39. package/dist/chunk-NO4WZCAH.mjs.map +1 -0
  40. package/dist/{chunk-FUODTTIA.mjs → chunk-PSEQ6QPH.mjs} +621 -108
  41. package/dist/chunk-PSEQ6QPH.mjs.map +1 -0
  42. package/dist/{chunk-33XZFTQU.mjs → chunk-S5UE2T5P.mjs} +48 -6
  43. package/dist/chunk-S5UE2T5P.mjs.map +1 -0
  44. package/dist/components/directional-light.d.ts +2 -2
  45. package/dist/components/dynamic-resolution.d.ts +15 -0
  46. package/dist/components/dynamic-resolution.d.ts.map +1 -0
  47. package/dist/components/index.d.ts +1 -0
  48. package/dist/components/index.d.ts.map +1 -1
  49. package/dist/components/light-helpers.d.ts.map +1 -1
  50. package/dist/components/reflection-probe.d.ts +1 -0
  51. package/dist/components/reflection-probe.d.ts.map +1 -1
  52. package/dist/components/skylight.d.ts +4 -1
  53. package/dist/components/skylight.d.ts.map +1 -1
  54. package/dist/construct-renderer.mjs +2172 -796
  55. package/dist/construct-renderer.mjs.map +1 -1
  56. package/dist/device/device-scope.d.ts +2 -0
  57. package/dist/device/device-scope.d.ts.map +1 -1
  58. package/dist/device/gpu-residency.d.ts +2 -2
  59. package/dist/device/gpu-residency.d.ts.map +1 -1
  60. package/dist/dynamic-geometry.d.ts.map +1 -1
  61. package/dist/environment/background.d.ts +3 -2
  62. package/dist/environment/background.d.ts.map +1 -1
  63. package/dist/environment/ibl.d.ts +11 -0
  64. package/dist/environment/ibl.d.ts.map +1 -0
  65. package/dist/environment/storage.d.ts +14 -0
  66. package/dist/environment/storage.d.ts.map +1 -0
  67. package/dist/errors/gpu-driven.d.ts +2 -2
  68. package/dist/errors/gpu-driven.d.ts.map +1 -1
  69. package/dist/errors/render.d.ts +35 -1
  70. package/dist/errors/render.d.ts.map +1 -1
  71. package/dist/extract/camera.d.ts.map +1 -1
  72. package/dist/extract/contracts.d.ts +7 -0
  73. package/dist/extract/contracts.d.ts.map +1 -1
  74. package/dist/extract/gpu-driven.d.ts +18 -1
  75. package/dist/extract/gpu-driven.d.ts.map +1 -1
  76. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  77. package/dist/gpu-driven/material-artifacts.d.ts +24 -0
  78. package/dist/gpu-driven/material-artifacts.d.ts.map +1 -0
  79. package/dist/gpu-driven/material-bindings.d.ts +7 -0
  80. package/dist/gpu-driven/material-bindings.d.ts.map +1 -0
  81. package/dist/gpu-driven/prepared-draw.d.ts +2 -0
  82. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  83. package/dist/gpu-driven/production-raster.d.ts +27 -6
  84. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  85. package/dist/gpu-resource.d.ts +5 -2
  86. package/dist/gpu-resource.d.ts.map +1 -1
  87. package/dist/gpu-scene-schema.d.ts.map +1 -1
  88. package/dist/gpu-scene.d.ts.map +1 -1
  89. package/dist/hdrp-buffers.d.ts +3 -0
  90. package/dist/hdrp-buffers.d.ts.map +1 -1
  91. package/dist/ibl/IblPipelineCache.d.ts +2 -0
  92. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  93. package/dist/ibl/skylight-bind-group.d.ts +3 -1
  94. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  95. package/dist/index.d.ts +83 -81
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.mjs +7 -5
  98. package/dist/index.mjs.map +1 -1
  99. package/dist/inspection-types.d.ts +16 -1
  100. package/dist/inspection-types.d.ts.map +1 -1
  101. package/dist/internal.mjs +6 -6
  102. package/dist/material-row.d.ts +10 -0
  103. package/dist/material-row.d.ts.map +1 -1
  104. package/dist/pbr-pipeline.d.ts +3 -1
  105. package/dist/pbr-pipeline.d.ts.map +1 -1
  106. package/dist/pipeline/render-extent.d.ts +51 -0
  107. package/dist/pipeline/render-extent.d.ts.map +1 -0
  108. package/dist/pipeline/standard-build-context.d.ts +7 -0
  109. package/dist/pipeline/standard-build-context.d.ts.map +1 -0
  110. package/dist/pipeline/standard-forward-lane.d.ts +3 -2
  111. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  112. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  113. package/dist/pipeline/standard-post.d.ts +1 -1
  114. package/dist/pipeline/standard-post.d.ts.map +1 -1
  115. package/dist/pipeline/standard-profile.d.ts +2 -1
  116. package/dist/pipeline/standard-profile.d.ts.map +1 -1
  117. package/dist/pipeline-builder.d.ts +3 -1
  118. package/dist/pipeline-builder.d.ts.map +1 -1
  119. package/dist/plugin.d.ts.map +1 -1
  120. package/dist/post-process-errors.d.ts +9 -2
  121. package/dist/post-process-errors.d.ts.map +1 -1
  122. package/dist/prepare/extended-lighting/resources.d.ts +3 -3
  123. package/dist/record/frame-lighting.d.ts.map +1 -1
  124. package/dist/record/frame-snapshot.d.ts +3 -8
  125. package/dist/record/frame-snapshot.d.ts.map +1 -1
  126. package/dist/record/frame.d.ts +3 -2
  127. package/dist/record/frame.d.ts.map +1 -1
  128. package/dist/record/main-pass-geometry.d.ts +7 -2
  129. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  130. package/dist/record/main-pass-material.d.ts +6 -3
  131. package/dist/record/main-pass-material.d.ts.map +1 -1
  132. package/dist/record/main-pass.d.ts +2 -0
  133. package/dist/record/main-pass.d.ts.map +1 -1
  134. package/dist/record/prepared-material-bindings.d.ts.map +1 -1
  135. package/dist/record/recovery-pipeline.d.ts +9 -1
  136. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  137. package/dist/record/render-context.d.ts +34 -3
  138. package/dist/record/render-context.d.ts.map +1 -1
  139. package/dist/record/shadow-pass.d.ts +2 -2
  140. package/dist/record/shadow-pass.d.ts.map +1 -1
  141. package/dist/record/typed-frame-graph.d.ts +5 -2
  142. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  143. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  144. package/dist/reflection/background.d.ts +14 -0
  145. package/dist/reflection/background.d.ts.map +1 -0
  146. package/dist/reflection/filter.d.ts.map +1 -1
  147. package/dist/reflection/projection.d.ts +1 -0
  148. package/dist/reflection/projection.d.ts.map +1 -1
  149. package/dist/reflection/record-owner.d.ts +4 -2
  150. package/dist/reflection/record-owner.d.ts.map +1 -1
  151. package/dist/render-contract.d.ts +47 -32
  152. package/dist/render-contract.d.ts.map +1 -1
  153. package/dist/render-graph-primitives.d.ts.map +1 -1
  154. package/dist/render-pipeline.d.ts +17 -1
  155. package/dist/render-pipeline.d.ts.map +1 -1
  156. package/dist/render-system-extract-tail.d.ts +9 -2
  157. package/dist/render-system-extract-tail.d.ts.map +1 -1
  158. package/dist/render-system-extract.d.ts +29 -2
  159. package/dist/render-system-extract.d.ts.map +1 -1
  160. package/dist/render-system.d.ts.map +1 -1
  161. package/dist/scene/render-scene.d.ts +2 -0
  162. package/dist/scene/render-scene.d.ts.map +1 -1
  163. package/dist/ssao-config.d.ts +8 -6
  164. package/dist/ssao-config.d.ts.map +1 -1
  165. package/dist/ssr/graph.d.ts.map +1 -1
  166. package/dist/temporal/gpu.d.ts.map +1 -1
  167. package/dist/temporal/index.mjs +5 -5
  168. package/dist/temporal/standard-scene-data.d.ts +2 -1
  169. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  170. package/dist/temporal/target-coverage-attachment.d.ts +15 -0
  171. package/dist/temporal/target-coverage-attachment.d.ts.map +1 -0
  172. package/dist/temporal/temporal-view.d.ts +6 -2
  173. package/dist/temporal/temporal-view.d.ts.map +1 -1
  174. package/dist/temporal/view.d.ts +6 -0
  175. package/dist/temporal/view.d.ts.map +1 -1
  176. package/dist/typed-render-graph-primitives.d.ts +6 -0
  177. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  178. package/dist/volume/component.d.ts +1 -0
  179. package/dist/volume/component.d.ts.map +1 -1
  180. package/package.json +21 -21
  181. package/src/__tests__/capture-scene-projection.unit.test.ts +24 -0
  182. package/src/__tests__/device-feature-admission.unit.test.ts +15 -24
  183. package/src/__tests__/device-loss-fanout.unit.test.ts +43 -0
  184. package/src/__tests__/dynamic-geometry-batch.unit.test.ts +157 -0
  185. package/src/__tests__/dynamic-geometry.unit.test.ts +36 -0
  186. package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +81 -0
  187. package/src/__tests__/factory-contract.integration.test.ts +164 -1
  188. package/src/__tests__/frame-observation.contract.test.ts +21 -0
  189. package/src/__tests__/gpu-driven-baseline.characterization.test.ts +1 -1
  190. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +20 -1
  191. package/src/__tests__/gpu-driven-lod-cache.unit.test.ts +0 -3
  192. package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +0 -3
  193. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +11 -6
  194. package/src/__tests__/gpu-driven-production.integration.test.ts +13 -25
  195. package/src/__tests__/gpu-driven-view.unit.test.ts +1 -0
  196. package/src/__tests__/gpu-pass-timing-browser-runner.ts +15 -0
  197. package/src/__tests__/gpu-pass-timing.browser.test.ts +14 -1
  198. package/src/__tests__/gpu-resource-scope.unit.test.ts +71 -0
  199. package/src/__tests__/gpu-scene.unit.test.ts +55 -1
  200. package/src/__tests__/materials-standard-contract.unit.test.ts +5 -1
  201. package/src/__tests__/mesh-submission-lifetime.unit.test.ts +8 -0
  202. package/src/__tests__/morph-neutral-admission.unit.test.ts +19 -0
  203. package/src/__tests__/physical-clearcoat.integration.test.ts +6 -1
  204. package/src/__tests__/post-process-error-policy-owner.unit.test.ts +6 -5
  205. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +216 -5
  206. package/src/__tests__/reflection-fallback-async-publication.unit.test.ts +68 -1
  207. package/src/__tests__/reflection-probe-authoring.unit.test.ts +12 -10
  208. package/src/__tests__/reflection-probe-box.dawn.test.ts +71 -0
  209. package/src/__tests__/reflection-probe-filter.unit.test.ts +9 -0
  210. package/src/__tests__/reflection-probe-orientation.dawn.test.ts +294 -0
  211. package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +5 -15
  212. package/src/__tests__/reflection-probe-updates.unit.test.ts +129 -0
  213. package/src/__tests__/render-error-code-owner.test-d.ts +3 -0
  214. package/src/__tests__/render-error-detail-ssot.test-d.ts +5 -1
  215. package/src/__tests__/render-extent.unit.test.ts +63 -0
  216. package/src/__tests__/render-scene-projection.unit.test.ts +3 -2
  217. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +173 -9
  218. package/src/__tests__/shader-manifest-fixture.ts +6 -0
  219. package/src/__tests__/skinned-shadow-caster.test.ts +8 -1
  220. package/src/__tests__/specular-environment-scale.dawn.test.ts +4 -2
  221. package/src/__tests__/ssao-config.unit.test.ts +7 -0
  222. package/src/__tests__/ssao-surface.dawn.test.ts +252 -0
  223. package/src/__tests__/ssr-cube-silhouette.dawn.test.ts +270 -0
  224. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +51 -43
  225. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +4 -0
  226. package/src/__tests__/ssr-gpu-dispatch.ts +10 -5
  227. package/src/__tests__/ssr-graph-topology.unit.test.ts +2 -2
  228. package/src/__tests__/ssr-hiz.browser.test.ts +1 -1
  229. package/src/__tests__/ssr-hiz.dawn.test.ts +1 -1
  230. package/src/__tests__/ssr-spatial.integration.test.ts +1 -1
  231. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +40 -0
  232. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +238 -1
  233. package/src/__tests__/standard-profile-oracle.unit.test.ts +27 -0
  234. package/src/__tests__/taa-coverage-edge.dawn.test.ts +6 -0
  235. package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +5 -1
  236. package/src/__tests__/taa-stability-feedback.ts +1 -0
  237. package/src/__tests__/temporal-reset-matrix.unit.test.ts +2 -2
  238. package/src/__tests__/temporal-view.unit.test.ts +17 -0
  239. package/src/__tests__/transmission-prewarm.unit.test.ts +57 -0
  240. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +62 -13
  241. package/src/__tests__/volumetric-fog-topology.unit.test.ts +7 -7
  242. package/src/assembly/atmosphere-shader-prewarm.ts +3 -2
  243. package/src/assembly/device-feature-admission.ts +9 -4
  244. package/src/assembly/dynamic-geometry-host.ts +236 -163
  245. package/src/assembly/host-contract.ts +9 -0
  246. package/src/assembly/material/assembly.ts +350 -6
  247. package/src/assembly/material/pipeline-helpers.ts +100 -0
  248. package/src/assembly/material-shader-policy.ts +39 -18
  249. package/src/assembly/recovery/device-loss-fanout.ts +5 -0
  250. package/src/assembly/recovery/recovery-attempt.ts +2 -29
  251. package/src/assembly/recovery/renderer-recover.ts +6 -9
  252. package/src/assembly/renderer-facade.ts +22 -1
  253. package/src/assembly/shader-prewarm-policy.ts +14 -1
  254. package/src/assembly/webgpu-ready.ts +28 -19
  255. package/src/assembly/webgpu-renderer.ts +95 -95
  256. package/src/capture/scene-projection.ts +37 -0
  257. package/src/components/__tests__/dynamic-resolution-schema.unit.test.ts +65 -0
  258. package/src/components/__tests__/light-radius.integration.test.ts +59 -0
  259. package/src/components/directional-light.ts +3 -3
  260. package/src/components/dynamic-resolution.ts +67 -0
  261. package/src/components/index.ts +1 -0
  262. package/src/components/light-helpers.ts +6 -1
  263. package/src/components/reflection-probe.ts +1 -0
  264. package/src/components/skylight.ts +4 -1
  265. package/src/device/device-scope.ts +13 -7
  266. package/src/device/gpu-residency.ts +11 -11
  267. package/src/dynamic-geometry.ts +13 -8
  268. package/src/environment/background.ts +30 -12
  269. package/src/environment/ibl.ts +211 -0
  270. package/src/environment/storage.ts +63 -0
  271. package/src/errors/gpu-driven.ts +10 -3
  272. package/src/errors/render.ts +64 -0
  273. package/src/extract/camera.ts +12 -1
  274. package/src/extract/contracts.ts +10 -0
  275. package/src/extract/gpu-driven.ts +72 -17
  276. package/src/gpu-driven/batch-topology.ts +33 -11
  277. package/src/gpu-driven/material-artifacts.ts +179 -0
  278. package/src/gpu-driven/material-bindings.ts +41 -0
  279. package/src/gpu-driven/prepared-draw.ts +13 -2
  280. package/src/gpu-driven/production-raster.ts +681 -164
  281. package/src/gpu-resource.ts +21 -2
  282. package/src/gpu-scene-schema.ts +11 -2
  283. package/src/gpu-scene.ts +14 -3
  284. package/src/hdrp-buffers.ts +69 -0
  285. package/src/ibl/IblPipelineCache.ts +5 -0
  286. package/src/ibl/skylight-bind-group.ts +27 -17
  287. package/src/index.ts +92 -81
  288. package/src/inspection-types.ts +16 -1
  289. package/src/material-row.ts +60 -11
  290. package/src/pbr-pipeline.ts +13 -2
  291. package/src/pipeline/render-extent.ts +111 -0
  292. package/src/pipeline/standard-build-context.ts +7 -0
  293. package/src/pipeline/standard-forward-lane.ts +41 -24
  294. package/src/pipeline/standard-pipeline.ts +47 -18
  295. package/src/pipeline/standard-post.ts +4 -0
  296. package/src/pipeline/standard-profile.ts +2 -1
  297. package/src/pipeline-builder.ts +12 -3
  298. package/src/plugin.ts +4 -0
  299. package/src/post-process-errors.ts +26 -18
  300. package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +2 -2
  301. package/src/prepare/extended-lighting/resources.ts +3 -3
  302. package/src/record/__tests__/main-pass-geometry.unit.test.ts +68 -0
  303. package/src/record/__tests__/material-bg-cache.unit.test.ts +45 -0
  304. package/src/record/__tests__/material-slot-plan.unit.test.ts +9 -0
  305. package/src/record/__tests__/recovery-pipeline.unit.test.ts +47 -1
  306. package/src/record/__tests__/sprite-material-abi.unit.test.ts +7 -3
  307. package/src/record/__tests__/temporal-payload.unit.test.ts +2 -0
  308. package/src/record/frame-lighting.ts +3 -1
  309. package/src/record/frame-snapshot.ts +13 -11
  310. package/src/record/frame.ts +150 -74
  311. package/src/record/main-pass-geometry.ts +60 -9
  312. package/src/record/main-pass-material.ts +47 -19
  313. package/src/record/main-pass.ts +63 -4
  314. package/src/record/prepared-material-bindings.ts +4 -0
  315. package/src/record/recovery-pipeline.ts +56 -0
  316. package/src/record/render-context.ts +34 -3
  317. package/src/record/shadow-pass.ts +187 -50
  318. package/src/record/typed-frame-graph.ts +60 -3
  319. package/src/record/view-ubo.ts +1 -1
  320. package/src/recovery/render-system-candidate.ts +54 -11
  321. package/src/reflection/background.ts +151 -0
  322. package/src/reflection/filter.ts +3 -0
  323. package/src/reflection/projection.ts +16 -6
  324. package/src/reflection/record-owner.ts +282 -86
  325. package/src/render-contract.ts +50 -32
  326. package/src/render-graph-primitives.ts +28 -3
  327. package/src/render-pipeline.ts +20 -1
  328. package/src/render-system-extract-tail.ts +23 -3
  329. package/src/render-system-extract.ts +127 -42
  330. package/src/render-system.ts +35 -7
  331. package/src/scene/render-scene.ts +8 -0
  332. package/src/ssao-config.ts +25 -8
  333. package/src/ssr/graph.ts +78 -58
  334. package/src/temporal/gpu.ts +5 -0
  335. package/src/temporal/standard-scene-data.ts +7 -1
  336. package/src/temporal/target-coverage-attachment.ts +72 -0
  337. package/src/temporal/temporal-view.ts +10 -4
  338. package/src/temporal/view.ts +12 -2
  339. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +24 -9
  340. package/src/typed-render-graph-primitives.ts +38 -0
  341. package/src/volume/component.ts +1 -0
  342. package/src/volume/passes.ts +3 -3
  343. package/dist/chunk-33XZFTQU.mjs.map +0 -1
  344. package/dist/chunk-BQVQOQJH.mjs.map +0 -1
  345. package/dist/chunk-BTWH7BP4.mjs.map +0 -1
  346. package/dist/chunk-FUODTTIA.mjs.map +0 -1
  347. package/dist/chunk-JQTSEI3Z.mjs.map +0 -1
  348. package/dist/chunk-NICXSHAT.mjs.map +0 -1
  349. package/dist/chunk-NRNXBLIJ.mjs.map +0 -1
  350. package/dist/chunk-S4DNKQYQ.mjs.map +0 -1
@@ -1,3 +1,5 @@
1
+ import { collectGpuDrivenMaterialArtifacts } from '../gpu-driven/material-artifacts';
2
+ import { materialBindingKey, projectMaterialBindingClasses } from '../gpu-driven/material-bindings';
1
3
  import { uploadMaterialUniforms } from './material-uniforms';
2
4
  // @forgeax/engine-runtime - RenderSystem record stage: frame.
3
5
  // Extracted from render-system-record.ts (feat-20260704 M3/w17, pure move).
@@ -43,6 +45,7 @@ import {
43
45
  GPU_BUFFER_USAGE_VERTEX,
44
46
  } from '../gpu-usage';
45
47
  import { disposeTransientInstanceBuffers } from '../instance-buffer-cache';
48
+ import { deriveRenderExtent } from '../pipeline/render-extent';
46
49
  import { inspectStandardLighting } from '../pipeline/standard-lighting/inspection';
47
50
  import { standardLightingTopologySignature } from '../pipeline/standard-lighting/topology';
48
51
  import { inspectPointShadow } from '../point-shadow-inspection';
@@ -401,18 +404,25 @@ export interface MaterialSlotPlan<T extends object> {
401
404
  */
402
405
  export function buildMaterialSlotPlan<T extends object>(
403
406
  materialGroups: readonly (readonly T[])[],
407
+ bindingClass?: (material: T, owner: number) => string,
404
408
  ): MaterialSlotPlan<T> {
405
- const slotByMaterial = new Map<T, number>();
409
+ const slotByMaterial = new Map<T, Map<string, number>>();
406
410
  const slots: T[] = [];
407
411
  const slotOwners: number[] = [];
408
412
  const slotIndices = materialGroups.map((materials, ownerIndex) =>
409
413
  materials.map((material) => {
410
- const cached = slotByMaterial.get(material);
414
+ const resourceClass = bindingClass?.(material, ownerIndex) ?? '';
415
+ let materialClasses = slotByMaterial.get(material);
416
+ if (materialClasses === undefined) {
417
+ materialClasses = new Map();
418
+ slotByMaterial.set(material, materialClasses);
419
+ }
420
+ const cached = materialClasses.get(resourceClass);
411
421
  if (cached !== undefined) return cached;
412
422
  const slot = slots.length;
413
423
  slots.push(material);
414
424
  slotOwners.push(ownerIndex);
415
- slotByMaterial.set(material, slot);
425
+ materialClasses.set(resourceClass, slot);
416
426
  return slot;
417
427
  }),
418
428
  );
@@ -482,6 +492,7 @@ function limitRowsToMeshSsboCapacity<T>(rows: readonly T[], availableSlots: numb
482
492
  export type RecordProfileRunner = <T>(phase: RenderRecordPhase, action: () => T) => T;
483
493
 
484
494
  export interface CubeCaptureFrameInput {
495
+ readonly captureDispatch?: readonly DispatchEntry[];
485
496
  readonly snapshots: readonly CubeCameraSnapshot[];
486
497
  readonly scheduler: import('../capture/scheduler').CubeCaptureScheduler;
487
498
  readonly state: CubeCaptureGraphState;
@@ -659,15 +670,19 @@ async function mapReflectionFallbackReadback(
659
670
  for (let y = 0; y < request.height; y += 1) {
660
671
  for (let x = 0; x < request.width; x += 1) {
661
672
  const offset = y * request.bytesPerRow + x * 8;
662
- const pixel = [0, 1, 2, 3].map((channel) =>
663
- halfFloatToNumber(view.getUint16(offset + channel * 2, true)),
664
- ) as [number, number, number, number];
665
- // The clear attachment carries alpha=1, so alpha alone is not
666
- // evidence that the Standard BRDF wrote a fallback lobe. Decode the
667
- // RGB channels from the mapped MRT and admit the first finite
668
- // non-zero color sample only.
669
- if (pixel.slice(0, 3).some((value) => Number.isFinite(value) && value !== 0)) {
670
- linearHdr = pixel;
673
+ // Validation above already rejects non-finite half floats. Scan the
674
+ // raw RGB magnitudes (both signed zero encodings are empty) without
675
+ // allocating/decoding millions of background pixels each frame.
676
+ const red = view.getUint16(offset, true);
677
+ const green = view.getUint16(offset + 2, true);
678
+ const blue = view.getUint16(offset + 4, true);
679
+ if ((red & 0x7fff) !== 0 || (green & 0x7fff) !== 0 || (blue & 0x7fff) !== 0) {
680
+ linearHdr = [
681
+ halfFloatToNumber(red),
682
+ halfFloatToNumber(green),
683
+ halfFloatToNumber(blue),
684
+ halfFloatToNumber(view.getUint16(offset + 6, true)),
685
+ ];
671
686
  y = request.height;
672
687
  break;
673
688
  }
@@ -1854,15 +1869,21 @@ export function recordFrame(
1854
1869
  (volumetricFog?.status === 'degraded' && acceptedFallback !== undefined);
1855
1870
 
1856
1871
  let preparedGpuDriven: PreparedGpuDrivenFrame | undefined;
1872
+ let materialBindingClasses: ReadonlyMap<string, string> | undefined;
1857
1873
  const abortPreparedGpuDriven = (): void => {
1858
1874
  preparedGpuDriven?._abortResourceReplacement?.();
1859
1875
  };
1860
1876
  const cubeCaptureWork = prepareCubeCapture(internals, cubeCapture, camera);
1861
1877
  const reflectionFallbackCandidate = cubeCapture?.reflectionProbes?.fallbackDemand === true;
1862
- // The Standard PBR GPU raster owns one color target. Reflection fallback
1863
- // adds an MRT attachment, so keep this frame on the ordinary CPU producer
1864
- // instead of claiming rows that the one-target GPU pipeline cannot draw.
1865
- const frameGpuDriven = reflectionFallbackCandidate ? undefined : gpuDriven;
1878
+ // Capture views have independent draw ownership; display-camera GPU work
1879
+ // remains active while their faces and SSR fallback attachments are recorded.
1880
+ // ProbeBlendRecord is an object-level fragment ABI. The scene-index GPU
1881
+ // lane has no binding for that record yet, so claiming a probe-bearing
1882
+ // row would silently fall back to Skylight and make local diffuse probes
1883
+ // visually ineffective. Keep the whole frame on the CPU producer until
1884
+ // the scene-index lane carries the same record contract.
1885
+ const probeBlendFrame = renderables.some((entry) => entry.probeBlendRecord !== undefined);
1886
+ const frameGpuDriven = probeBlendFrame ? undefined : gpuDriven;
1866
1887
 
1867
1888
  // Validate first so user meshes are resident in the same frame. The GPU
1868
1889
  // owner consumes this producer-owned map together with the builtin mesh
@@ -1914,6 +1935,41 @@ export function recordFrame(
1914
1935
  ? projectValidatedRows(validatedProjection, renderables)
1915
1936
  : validatedProjection;
1916
1937
 
1938
+ // S_fallback is the second output of the built-in Standard PBR BRDF. A
1939
+ // graph may still carry the detached owner tickets for inspection, but
1940
+ // the MRT is admitted only when every CPU-rendered primary row in this
1941
+ // frame uses that exact output contract. Unsupported compositions remain
1942
+ // fail-closed for SSR instead of submitting a device-invalid command.
1943
+ const sceneInputs =
1944
+ validatedForResidency.length > 0 &&
1945
+ validatedForResidency.every(
1946
+ (entry) => entry.source.material.materialShaderId === 'forgeax::default-standard-pbr',
1947
+ );
1948
+ const ssrAdmission =
1949
+ ssrDependencies === undefined
1950
+ ? undefined
1951
+ : admitSsrSpatial({
1952
+ camera,
1953
+ environment: {
1954
+ lane: standard.prepared.renderPath === 'deferred' ? 'deferred' : 'forward',
1955
+ m0: ssrDependencies.admission,
1956
+ sceneInputs,
1957
+ // Standard geometry produces current motion/coverage in this
1958
+ // graph, including its first frame. Prior history is optional.
1959
+ temporal: sceneInputs,
1960
+ reflectionFallback: ssrDependencies.reflectionFallback !== undefined,
1961
+ capabilities: {
1962
+ compute: internals.device.caps.compute,
1963
+ storageTexture: internals.device.caps.storageTexture,
1964
+ rgba16floatRenderable: internals.device.caps.rgba16floatRenderable,
1965
+ r32floatSampledStorage: ssrDependencies.format?.verdict === 'admitted',
1966
+ },
1967
+ recovery: true,
1968
+ },
1969
+ });
1970
+ frameState.reflectionFallbackDemand =
1971
+ (reflectionFallbackCandidate || ssrAdmission?.status === 'admitted') && sceneInputs;
1972
+
1917
1973
  if (frameGpuDriven !== undefined) {
1918
1974
  const gpuDrivenMeshes = new Map(
1919
1975
  gpuDrivenMeshesForFrame(internals, pipelineState, worlds, frameGpuDriven.scene),
@@ -1930,10 +1986,28 @@ export function recordFrame(
1930
1986
  );
1931
1987
  if (slot !== undefined) gpuDrivenMeshes.set(slot, row.mesh);
1932
1988
  }
1933
- const standardPbrArtifact = internals.getMaterialShaderArtifact?.(
1934
- 'forgeax::default-standard-pbr',
1989
+ // Resolve one producer artifact for every prepared draw. COLOR_0 is a
1990
+ // per-geometry ABI axis, so a residency-wide `.some(color)` would pair
1991
+ // a no-color draw with the colored receipt (or vice versa) and collapse
1992
+ // two distinct pipeline/projection identities before indirect record.
1993
+ materialBindingClasses = projectMaterialBindingClasses(
1994
+ validatedForResidency,
1995
+ transparentDispatch,
1996
+ cubeCapture?.reflectionProbes,
1935
1997
  );
1936
- const standardPbrSkinArtifact = internals.getMaterialShaderArtifact?.('forgeax::pbr-skin');
1998
+ const { materialArtifacts, shadowMaterialArtifacts, firstArtifact, firstSkinArtifact } =
1999
+ collectGpuDrivenMaterialArtifacts({
2000
+ rows: validatedForResidency,
2001
+ shadowRows: validatedShadowForResidency ?? [],
2002
+ dispatch: transparentDispatch,
2003
+ shadowDispatch: shadowCasterProjection?.dispatch ?? [],
2004
+ resolve: internals.getMaterialShaderArtifact,
2005
+ clustered:
2006
+ standard.kind === 'clustered' &&
2007
+ (standard.prepared.local.length > 0 ||
2008
+ frameState.installedPipelineConfig?.ssao?.enabled === true),
2009
+ reflectionFallback: frameState.reflectionFallbackDemand === true,
2010
+ });
1937
2011
  const paletteUploadBytes = validatedForResidency.reduce(
1938
2012
  (total, row) => total + (row.source.skin?.uploadBytes ?? 0),
1939
2013
  0,
@@ -1944,14 +2018,7 @@ export function recordFrame(
1944
2018
  meshBySlot: gpuDrivenMeshes,
1945
2019
  viewBindGroupLayout: pipelineState.viewBindGroupLayout,
1946
2020
  meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
1947
- // GPU-driven Standard PBR currently owns the non-clustered path only.
1948
- // Cluster bindings also carry screen-space ambient occlusion. Admit
1949
- // the scene-index variant only when none of those surface inputs are
1950
- // required; a zero local-light count alone does not establish that.
1951
- hdrp:
1952
- standard.kind === 'clustered' &&
1953
- (standard.prepared.local.length > 0 ||
1954
- frameState.installedPipelineConfig?.ssao?.enabled === true),
2021
+ materialBindingClasses,
1955
2022
  ...(frameGpuDriven.activeEntityKeys === undefined
1956
2023
  ? {}
1957
2024
  : { activeEntityKeys: frameGpuDriven.activeEntityKeys }),
@@ -1970,9 +2037,14 @@ export function recordFrame(
1970
2037
  paletteUploadBytes,
1971
2038
  },
1972
2039
  }),
1973
- ...(standardPbrArtifact === undefined ? {} : { standardPbrArtifact }),
1974
- ...(standardPbrSkinArtifact === undefined ? {} : { standardPbrSkinArtifact }),
1975
- standardPbrPipelineState: pipelineState,
2040
+ ...(firstArtifact === undefined ? {} : { materialArtifact: firstArtifact }),
2041
+ ...(firstSkinArtifact === undefined ? {} : { materialSkinArtifact: firstSkinArtifact }),
2042
+ ...(materialArtifacts.size === 0 ? {} : { materialArtifacts }),
2043
+ ...(shadowMaterialArtifacts.size === 0 ? {} : { shadowMaterialArtifacts }),
2044
+ materialPipelineState: pipelineState,
2045
+ ...(internals.getMaterialShaderPipelineEntry === undefined
2046
+ ? {}
2047
+ : { materialPipelineFactory: internals.getMaterialShaderPipelineEntry }),
1976
2048
  ...(shadowCasterDrawKeys === undefined ? {} : { shadowCasterDrawKeys }),
1977
2049
  ...(shadowCasterMembership === undefined ? {} : { shadowCasterMembership }),
1978
2050
  });
@@ -2062,40 +2134,22 @@ export function recordFrame(
2062
2134
  // the current frame. This keeps the graph topology and its execution
2063
2135
  // closure on the same prepared GPU-owned draw set.
2064
2136
 
2065
- // S_fallback is the second output of the built-in Standard PBR BRDF. A
2066
- // graph may still carry the detached owner tickets for inspection, but
2067
- // the MRT is admitted only when every CPU-rendered primary row in this
2068
- // frame uses that exact output contract. Unsupported compositions remain
2069
- // fail-closed for SSR instead of submitting a device-invalid command.
2070
- const sceneInputs =
2071
- validated.length > 0 &&
2072
- validated.every(
2073
- (entry) => entry.source.material.materialShaderId === 'forgeax::default-standard-pbr',
2074
- );
2075
- const ssrAdmission =
2076
- ssrDependencies === undefined
2137
+ // RenderExtent is an authored DynamicResolution path. The native TAA path
2138
+ // intentionally has no extent object, so every Standard consumer keeps the
2139
+ // exact surface domain even when a surface is not 8-pixel aligned. An
2140
+ // adaptive range remains unavailable until a renderer-owned timing/
2141
+ // controller chain exists; do not turn maxScale into a fake performance
2142
+ // decision. Fixed minScale=maxScale is the only extent currently admitted.
2143
+ const renderExtent =
2144
+ camera.dynamicResolution === undefined ||
2145
+ camera.dynamicResolution.minScale !== camera.dynamicResolution.maxScale
2077
2146
  ? undefined
2078
- : admitSsrSpatial({
2079
- camera,
2080
- environment: {
2081
- lane: standard.prepared.renderPath === 'deferred' ? 'deferred' : 'forward',
2082
- m0: ssrDependencies.admission,
2083
- sceneInputs,
2084
- // Standard geometry produces current motion/coverage in this
2085
- // graph, including its first frame. Prior history is optional.
2086
- temporal: sceneInputs,
2087
- reflectionFallback: ssrDependencies.reflectionFallback !== undefined,
2088
- capabilities: {
2089
- compute: internals.device.caps.compute,
2090
- storageTexture: internals.device.caps.storageTexture,
2091
- rgba16floatRenderable: internals.device.caps.rgba16floatRenderable,
2092
- r32floatSampledStorage: ssrDependencies.format?.verdict === 'admitted',
2093
- },
2094
- recovery: true,
2095
- },
2147
+ : deriveRenderExtent({
2148
+ outputWidth: Math.max(1, internals.canvas.width),
2149
+ outputHeight: Math.max(1, internals.canvas.height),
2150
+ requestedScale: camera.dynamicResolution.minScale,
2151
+ generation: internals.deviceScope?.generation ?? 0,
2096
2152
  });
2097
- frameState.reflectionFallbackDemand =
2098
- (reflectionFallbackCandidate || ssrAdmission?.status === 'admitted') && sceneInputs;
2099
2153
  const preflightGraph = runRecordProfilePhase(profilePhase, 'record/render-graph', () =>
2100
2154
  ensureCompiledFrameGraph(
2101
2155
  internals,
@@ -2117,6 +2171,7 @@ export function recordFrame(
2117
2171
  standard,
2118
2172
  undefined,
2119
2173
  ssrAdmission,
2174
+ renderExtent,
2120
2175
  ),
2121
2176
  );
2122
2177
  if (preflightGraph === null) {
@@ -2214,6 +2269,12 @@ export function recordFrame(
2214
2269
  environmentSignature,
2215
2270
  fogSignature,
2216
2271
  deviceGeneration: internals.deviceScope?.generation ?? 0,
2272
+ ...(renderExtent === undefined
2273
+ ? {}
2274
+ : {
2275
+ internalWidth: renderExtent.internalWidth,
2276
+ internalHeight: renderExtent.internalHeight,
2277
+ }),
2217
2278
  ...(camera.historyVersion === undefined ? {} : { historyVersion: camera.historyVersion }),
2218
2279
  historyValid: false,
2219
2280
  currentUnjitteredViewProjection,
@@ -2371,6 +2432,7 @@ export function recordFrame(
2371
2432
  transparentDispatch,
2372
2433
  foldBuckets,
2373
2434
  validatedShadowForResidency ?? [],
2435
+ materialBindingClasses,
2374
2436
  ),
2375
2437
  );
2376
2438
  const validatedOrdered = dispatchPlan.validatedOrdered;
@@ -2594,6 +2656,7 @@ export function recordFrame(
2594
2656
  clear,
2595
2657
  targetW,
2596
2658
  targetH,
2659
+ ...(renderExtent === undefined ? {} : { extent: renderExtent }),
2597
2660
  currentTexture,
2598
2661
  camera: recordCamera,
2599
2662
  tonemapActive,
@@ -2656,6 +2719,9 @@ export function recordFrame(
2656
2719
  ...(cubeCapture?.reflectionProbes === undefined
2657
2720
  ? {}
2658
2721
  : { reflectionProbes: cubeCapture.reflectionProbes }),
2722
+ ...(cubeCapture?.captureDispatch === undefined
2723
+ ? {}
2724
+ : { captureDispatch: cubeCapture.captureDispatch }),
2659
2725
  pointsLines: pointsLinesOwner,
2660
2726
  materialBgAssemblyCache: frameState.materialBgAssemblyCache,
2661
2727
  ...(onRenderableDraw === undefined ? {} : { onRenderableDraw }),
@@ -2669,7 +2735,10 @@ export function recordFrame(
2669
2735
  ...(profilePhase !== undefined ? { profilePhase } : {}),
2670
2736
  };
2671
2737
  uploadMaterialUniforms(passCtx);
2672
- const fallbackReadbackRequest = createReflectionFallbackReadbackRequest(internals, frameState);
2738
+ const fallbackReadbackRequest =
2739
+ internals.captureReflectionFallbackReadback === true
2740
+ ? createReflectionFallbackReadbackRequest(internals, frameState)
2741
+ : undefined;
2673
2742
  if (fallbackReadbackRequest !== undefined) {
2674
2743
  frameState.reflectionFallbackReadback = fallbackReadbackRequest;
2675
2744
  }
@@ -2779,21 +2848,20 @@ export function recordFrame(
2779
2848
  if (!submitted && fallbackReadbackRequest !== undefined) {
2780
2849
  internals.device.destroyBuffer(fallbackReadbackRequest.buffer);
2781
2850
  }
2851
+ const fallbackSource =
2852
+ frameState.reflectionFallbackObservationSource as RenderFrameState['reflectionFallbackObservationSource'];
2782
2853
  const reflectionFallbackCompletion = cubeCapture?.reflectionProbes?.completeSubmission(
2783
2854
  submitted,
2784
2855
  completion,
2785
- fallbackReadbackRequest === undefined || fallbackReadback === undefined
2856
+ fallbackSource === undefined
2786
2857
  ? undefined
2787
2858
  : {
2788
- format: fallbackReadbackRequest.expected.format,
2789
- size: {
2790
- width: fallbackReadbackRequest.expected.width,
2791
- height: fallbackReadbackRequest.expected.height,
2792
- },
2793
- frameId: fallbackReadbackRequest.expected.frameId,
2794
- graphGeneration: fallbackReadbackRequest.expected.graphGeneration,
2795
- textureIdentity: fallbackReadbackRequest.expected.textureIdentity,
2796
- readback: fallbackReadback,
2859
+ format: fallbackSource.descriptor.format,
2860
+ size: fallbackSource.descriptor.size,
2861
+ frameId: fallbackSource.frameId,
2862
+ graphGeneration: frameState.graphGeneration,
2863
+ textureIdentity: getTextureIdentity(fallbackSource.texture),
2864
+ ...(fallbackReadback === undefined ? {} : { readback: fallbackReadback }),
2797
2865
  },
2798
2866
  frameState.reflectionFallbackDemand,
2799
2867
  );
@@ -3330,6 +3398,7 @@ export function buildDispatchPlan(
3330
3398
  transparentDispatch: readonly DispatchEntry[],
3331
3399
  foldBuckets: readonly FoldBucket[],
3332
3400
  shadowValidated: readonly ValidatedRenderable[] = [],
3401
+ materialBindingClasses?: ReadonlyMap<string, string>,
3333
3402
  ): {
3334
3403
  validatedOrdered: readonly ValidatedRenderable[];
3335
3404
  shadowValidatedOrdered: readonly ValidatedRenderable[];
@@ -3411,7 +3480,14 @@ export function buildDispatchPlan(
3411
3480
  }
3412
3481
  return entry.source.materials.length > 0 ? entry.source.materials : [entry.source.material];
3413
3482
  });
3414
- const materialSlotPlan = buildMaterialSlotPlan(materialGroups);
3483
+ const materialSlotPlan = buildMaterialSlotPlan(materialGroups, (material, owner) => {
3484
+ const row = materialRows[owner];
3485
+ return row === undefined
3486
+ ? ''
3487
+ : (materialBindingClasses?.get(
3488
+ materialBindingKey(row.source, material.materialHandle ?? -1),
3489
+ ) ?? '');
3490
+ });
3415
3491
  let materialSlotIndices = materialSlotPlan.slotIndices;
3416
3492
  let materialSlots = materialSlotPlan.slots;
3417
3493
  let materialSlotOwners = materialSlotPlan.slotOwners.map((index) => {
@@ -73,6 +73,23 @@ import type { _InternalRenderPipelineContext, MaterialShaderPipelineEntry } from
73
73
  import { MATERIAL_PER_ENTITY_STRIDE } from './render-context';
74
74
  import { POINTS_LINES_VIEW_SLOT_STRIDE, writePointsLinesViewUbo } from './view-ubo';
75
75
 
76
+ /** Temporal ignores authored color entry points; opaque duplicates are idempotent. */
77
+ export function sameOpaqueTemporalDraw(a: DispatchEntry, b: DispatchEntry): boolean {
78
+ return (
79
+ (isStandardPbrMaterialShader(a.materialShaderId) ||
80
+ (a.tags.SurfaceKind === 'standard' &&
81
+ b.tags.SurfaceKind === 'standard' &&
82
+ a.tags.SurfaceModule === b.tags.SurfaceModule)) &&
83
+ a.materialShaderId === b.materialShaderId &&
84
+ a.renderState?.blend === undefined &&
85
+ b.renderState?.blend === undefined &&
86
+ a.renderState?.stencil === undefined &&
87
+ b.renderState?.stencil === undefined &&
88
+ a.stencilReference === b.stencilReference &&
89
+ renderStateHash(a.renderState) === renderStateHash(b.renderState)
90
+ );
91
+ }
92
+
76
93
  type GeometryInstanceDraw = {
77
94
  readonly instanceBuffer: Buffer;
78
95
  readonly instanceBindGroup: BindGroup;
@@ -133,6 +150,25 @@ type GeometryRecordProfileSegment = 'material-bind-groups' | 'pipeline-selection
133
150
 
134
151
  type GeometryRenderState = ReturnType<typeof geometryRenderStateForTopology>;
135
152
 
153
+ /** Coverage is a dedicated temporal material variant, not a color/alpha tap. */
154
+ export function variantSetForCoveragePass(
155
+ variantSet: string | undefined,
156
+ coverageOnly: boolean,
157
+ ): string | undefined {
158
+ if (!coverageOnly) return variantSet;
159
+ if (variantSet?.includes('COVERAGE_ONLY=')) return variantSet;
160
+ return variantSet === undefined || variantSet === ''
161
+ ? 'COVERAGE_ONLY=true'
162
+ : `${variantSet}+COVERAGE_ONLY=true`;
163
+ }
164
+
165
+ export function isHdrGeometryTarget(
166
+ tonemapActive: boolean,
167
+ colorFormatOverride: GPUTextureFormat | undefined,
168
+ ): boolean {
169
+ return colorFormatOverride === undefined ? tonemapActive : colorFormatOverride === 'rgba16float';
170
+ }
171
+
136
172
  function gpuDrivenDrawIndicesBySubmesh(
137
173
  source: RenderableSnapshot,
138
174
  mesh: _InternalRenderPipelineContext['validatedOrdered'][number]['mesh'],
@@ -319,11 +355,12 @@ export function effectiveMaterialLayoutIdentity(
319
355
  export function geometryRenderStateForPass(
320
356
  base: GeometryRenderState,
321
357
  passKind: PassKind,
358
+ coverageOnly = false,
322
359
  ): GeometryRenderState {
323
360
  if (passKind !== 'temporal') return base;
324
361
  return {
325
362
  ...base,
326
- depthWriteEnabled: false,
363
+ depthWriteEnabled: coverageOnly,
327
364
  depthCompare: 'less-equal' as const,
328
365
  };
329
366
  }
@@ -434,6 +471,7 @@ export function recordGeometryDraws(
434
471
  passKind: PassKind = 'forward',
435
472
  selectedDispatch?: readonly DispatchEntry[],
436
473
  colorFormats: readonly GPUTextureFormat[] = [],
474
+ coverageOnly = false,
437
475
  ): void {
438
476
  const {
439
477
  runtime,
@@ -457,7 +495,11 @@ export function recordGeometryDraws(
457
495
  }
458
496
  const passes = byMaterial.get(dispatch.materialHandle);
459
497
  if (passes === undefined) byMaterial.set(dispatch.materialHandle, [dispatch]);
460
- else passes.push(dispatch);
498
+ else if (
499
+ passKind !== 'temporal' ||
500
+ !passes.some((previous) => sameOpaqueTemporalDraw(previous, dispatch))
501
+ )
502
+ passes.push(dispatch);
461
503
  }
462
504
  const diagnosticsEnabled = materialDiagnosticsEnabled();
463
505
  const colorFormatOverride = c.transparentColorFormat as GPUTextureFormat | undefined;
@@ -470,7 +512,9 @@ export function recordGeometryDraws(
470
512
  // numeric domain rather than the camera's tone-map switch, otherwise the
471
513
  // LDR/OETF fragment entry point is encoded once here and again by the
472
514
  // output transform.
473
- const isHdrTarget = tonemapActive || colorFormatOverride === 'rgba16float';
515
+ const isHdrTarget = isHdrGeometryTarget(tonemapActive, colorFormatOverride);
516
+ const withCoverageVariant = (variantSet: string | undefined): string | undefined =>
517
+ variantSetForCoveragePass(variantSet, coverageOnly);
474
518
  let lastVertexBuffer: GpuBuffer | null = null;
475
519
  let lastIndexBuffer: GpuBuffer | null = null;
476
520
  const bindingState: GeometryBindingState = {
@@ -520,6 +564,7 @@ export function recordGeometryDraws(
520
564
  fragmentEntry?: string,
521
565
  textureMask?: number,
522
566
  ): MaterialShaderPipelineEntry | null => {
567
+ const pipelineVariantSet = withCoverageVariant(variantSet);
523
568
  // The fallback MRT is an extension of the built-in Standard PBR output
524
569
  // contract only. The render pass may carry a second attachment whenever
525
570
  // another renderable needs S_fallback, but shaders such as unlit, sprite,
@@ -545,7 +590,7 @@ export function recordGeometryDraws(
545
590
  cached.renderStateKey === renderStateKey &&
546
591
  cached.topology === topology &&
547
592
  cached.indexFormat === indexFormat &&
548
- cached.variantSet === variantSet &&
593
+ cached.variantSet === pipelineVariantSet &&
549
594
  cached.vertexEntry === vertexEntry &&
550
595
  cached.fragmentEntry === fragmentEntry &&
551
596
  cached.textureMask === textureMask &&
@@ -562,10 +607,10 @@ export function recordGeometryDraws(
562
607
  const resolvedVariantSetResult =
563
608
  runtime.getMaterialArtifact?.(materialShaderId) !== undefined
564
609
  ? ({ ok: true, value: undefined } as const)
565
- : variantSet === undefined &&
610
+ : pipelineVariantSet === undefined &&
566
611
  (materialShaderId === 'forgeax::sprite' || materialShaderId === 'forgeax::sprite-lit')
567
612
  ? ({ ok: true, value: undefined } as const)
568
- : variantSetFromVertexLayoutProjection(layoutProjection, variantSet);
613
+ : variantSetFromVertexLayoutProjection(layoutProjection, pipelineVariantSet);
569
614
  if (!resolvedVariantSetResult.ok) {
570
615
  runtime.errorRegistry.fire(resolvedVariantSetResult.error);
571
616
  return null;
@@ -620,7 +665,7 @@ export function recordGeometryDraws(
620
665
  renderStateKey,
621
666
  topology,
622
667
  indexFormat,
623
- variantSet,
668
+ variantSet: pipelineVariantSet,
624
669
  vertexEntry,
625
670
  fragmentEntry,
626
671
  passKind,
@@ -878,7 +923,9 @@ export function recordGeometryDraws(
878
923
  ),
879
924
  );
880
925
  if (!skinVariantSetResult.ok) runtime.errorRegistry.fire(skinVariantSetResult.error);
881
- const skinVariantSet = skinVariantSetResult.ok ? skinVariantSetResult.value : undefined;
926
+ const skinVariantSet = skinVariantSetResult.ok
927
+ ? withCoverageVariant(skinVariantSetResult.value)
928
+ : undefined;
882
929
  const skinPsoProbe =
883
930
  skinResources !== null && skinVariantSetResult.ok
884
931
  ? (runtime.getMaterialShaderPipeline?.(
@@ -1196,7 +1243,11 @@ export function recordGeometryDraws(
1196
1243
  blend: selectedRenderState?.blend ?? SPRITE_PREMULTIPLIED_ALPHA_BLEND,
1197
1244
  }
1198
1245
  : geometryRenderStateForTopology(smTopology, selectedRenderState);
1199
- const pipelineRenderState = geometryRenderStateForPass(basePipelineRenderState, passKind);
1246
+ const pipelineRenderState = geometryRenderStateForPass(
1247
+ basePipelineRenderState,
1248
+ passKind,
1249
+ coverageOnly,
1250
+ );
1200
1251
  let smPipelineHandle: typeof pipelineState.unlitPipeline;
1201
1252
  let materialGroup2Contract: 'mesh' | 'skin' | 'cluster' | 'skin-cluster' | undefined;
1202
1253
  let materialPipelineEntry: MaterialShaderPipelineEntry | null = null;