@forgeax/engine-render 0.1.23 → 0.1.25

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 (322) hide show
  1. package/README.md +231 -10
  2. package/dist/assembly/bundler-contract.d.ts +9 -0
  3. package/dist/assembly/bundler-contract.d.ts.map +1 -0
  4. package/dist/assembly/device-feature-admission.d.ts +8 -0
  5. package/dist/assembly/device-feature-admission.d.ts.map +1 -1
  6. package/dist/assembly/extended-lighting-fallback.d.ts +13 -0
  7. package/dist/assembly/extended-lighting-fallback.d.ts.map +1 -0
  8. package/dist/assembly/factory.d.ts +7 -243
  9. package/dist/assembly/factory.d.ts.map +1 -1
  10. package/dist/assembly/material/assembly.d.ts +3 -2
  11. package/dist/assembly/material/assembly.d.ts.map +1 -1
  12. package/dist/assembly/material/standard-layer-projection.d.ts +10 -0
  13. package/dist/assembly/material/standard-layer-projection.d.ts.map +1 -0
  14. package/dist/assembly/material/surface-projection.d.ts +24 -0
  15. package/dist/assembly/material/surface-projection.d.ts.map +1 -0
  16. package/dist/assembly/material-shader-policy.d.ts +188 -0
  17. package/dist/assembly/material-shader-policy.d.ts.map +1 -0
  18. package/dist/assembly/material-shader-prewarm.d.ts +21 -0
  19. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -0
  20. package/dist/assembly/mesh-ssbo-grow.d.ts +88 -0
  21. package/dist/assembly/mesh-ssbo-grow.d.ts.map +1 -0
  22. package/dist/assembly/recovery/device-loss-fanout.d.ts +14 -0
  23. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -0
  24. package/dist/assembly/recovery/generation.d.ts +46 -0
  25. package/dist/assembly/recovery/generation.d.ts.map +1 -0
  26. package/dist/assembly/recovery/recovery-attempt.d.ts +63 -0
  27. package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -0
  28. package/dist/assembly/recovery/renderer-recover.d.ts +115 -0
  29. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -0
  30. package/dist/assembly/renderer-facade.d.ts +10 -0
  31. package/dist/assembly/renderer-facade.d.ts.map +1 -0
  32. package/dist/assembly/renderer-frame-transaction.d.ts +17 -0
  33. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  34. package/dist/assembly/renderer-helpers.d.ts +19 -0
  35. package/dist/assembly/renderer-helpers.d.ts.map +1 -0
  36. package/dist/assembly/renderer-lifecycle.d.ts +34 -0
  37. package/dist/assembly/renderer-lifecycle.d.ts.map +1 -1
  38. package/dist/assembly/shader-prewarm-policy.d.ts +15 -0
  39. package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -0
  40. package/dist/assembly/webgpu-ready-contract.d.ts +9 -0
  41. package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -0
  42. package/dist/assembly/webgpu-ready.d.ts +55 -0
  43. package/dist/assembly/webgpu-ready.d.ts.map +1 -0
  44. package/dist/assembly/webgpu-renderer.d.ts +152 -0
  45. package/dist/assembly/webgpu-renderer.d.ts.map +1 -0
  46. package/dist/assembly/webgpu-vertex-layouts.d.ts +89 -0
  47. package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -0
  48. package/dist/assets/asset-decoders.d.ts.map +1 -1
  49. package/dist/authoring.mjs +1 -1
  50. package/dist/{chunk-FF7K3FSO.mjs → chunk-ADHHVYLW.mjs} +89 -89
  51. package/dist/chunk-ADHHVYLW.mjs.map +1 -0
  52. package/dist/{chunk-YALPS44A.mjs → chunk-E34VL5VI.mjs} +47 -47
  53. package/dist/chunk-E34VL5VI.mjs.map +1 -0
  54. package/dist/{chunk-4L3P6DT5.mjs → chunk-GE3SDD72.mjs} +37 -5
  55. package/dist/chunk-GE3SDD72.mjs.map +1 -0
  56. package/dist/{chunk-HZUPSYW7.mjs → chunk-HKXTW355.mjs} +105 -15
  57. package/dist/chunk-HKXTW355.mjs.map +1 -0
  58. package/dist/chunk-JSEDTZYS.mjs +413 -0
  59. package/dist/chunk-JSEDTZYS.mjs.map +1 -0
  60. package/dist/{chunk-FBC4R44J.mjs → chunk-KM2NOX2I.mjs} +57 -57
  61. package/dist/chunk-KM2NOX2I.mjs.map +1 -0
  62. package/dist/{chunk-DIYW3M5D.mjs → chunk-N3RT2EUR.mjs} +2288 -2238
  63. package/dist/chunk-N3RT2EUR.mjs.map +1 -0
  64. package/dist/{chunk-ZUWKAZ2I.mjs → chunk-TCLDX7SZ.mjs} +1167 -368
  65. package/dist/chunk-TCLDX7SZ.mjs.map +1 -0
  66. package/dist/{chunk-QODA5UKU.mjs → chunk-ZZ4YQ474.mjs} +2 -2
  67. package/dist/chunk-ZZ4YQ474.mjs.map +1 -0
  68. package/dist/components/instances.d.ts +13 -31
  69. package/dist/components/instances.d.ts.map +1 -1
  70. package/dist/construct-renderer.mjs +20688 -16314
  71. package/dist/construct-renderer.mjs.map +1 -1
  72. package/dist/device/device-scope.d.ts +2 -0
  73. package/dist/device/device-scope.d.ts.map +1 -1
  74. package/dist/device/gpu-residency.d.ts +24 -4
  75. package/dist/device/gpu-residency.d.ts.map +1 -1
  76. package/dist/environment/frame.d.ts +5 -0
  77. package/dist/environment/frame.d.ts.map +1 -1
  78. package/dist/environment/inspection.d.ts +7 -0
  79. package/dist/environment/inspection.d.ts.map +1 -1
  80. package/dist/environment/lifecycle.d.ts +20 -2
  81. package/dist/environment/lifecycle.d.ts.map +1 -1
  82. package/dist/errors/recover.d.ts +58 -2
  83. package/dist/errors/recover.d.ts.map +1 -1
  84. package/dist/errors/render.d.ts +13 -0
  85. package/dist/errors/render.d.ts.map +1 -1
  86. package/dist/extract/contracts.d.ts +899 -0
  87. package/dist/extract/contracts.d.ts.map +1 -0
  88. package/dist/features/host.d.ts +2 -0
  89. package/dist/features/host.d.ts.map +1 -1
  90. package/dist/features/prepared-gpu-work.d.ts +3 -0
  91. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  92. package/dist/features/prepared-graphics-store.d.ts +2 -0
  93. package/dist/features/prepared-graphics-store.d.ts.map +1 -1
  94. package/dist/gpu-driven/production-raster.d.ts +34 -1
  95. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  96. package/dist/gpu-scene.d.ts +5 -0
  97. package/dist/gpu-scene.d.ts.map +1 -1
  98. package/dist/ibl/IblPipelineCache.d.ts +7 -6
  99. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  100. package/dist/ibl/skylight-bind-group.d.ts +7 -1
  101. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  102. package/dist/index.d.ts +2 -1
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.mjs +27 -9
  105. package/dist/index.mjs.map +1 -1
  106. package/dist/instance-buffer-cache.d.ts +7 -1
  107. package/dist/instance-buffer-cache.d.ts.map +1 -1
  108. package/dist/instances.d.ts +101 -0
  109. package/dist/instances.d.ts.map +1 -0
  110. package/dist/internal.mjs +6 -6
  111. package/dist/light-buffer-layout.d.ts +3 -0
  112. package/dist/light-buffer-layout.d.ts.map +1 -1
  113. package/dist/materials.d.ts +96 -11
  114. package/dist/materials.d.ts.map +1 -1
  115. package/dist/mesh-material-bindings.d.ts +56 -0
  116. package/dist/mesh-material-bindings.d.ts.map +1 -1
  117. package/dist/occlusion-proxy.d.ts +4 -0
  118. package/dist/occlusion-proxy.d.ts.map +1 -0
  119. package/dist/pbr-pipeline.d.ts +32 -9
  120. package/dist/pbr-pipeline.d.ts.map +1 -1
  121. package/dist/pipeline-builder.d.ts.map +1 -1
  122. package/dist/pipeline-spec.d.ts.map +1 -1
  123. package/dist/points-lines/prepare.d.ts +3 -0
  124. package/dist/points-lines/prepare.d.ts.map +1 -1
  125. package/dist/points-lines/standard-owner.d.ts +33 -0
  126. package/dist/points-lines/standard-owner.d.ts.map +1 -0
  127. package/dist/record/frame-snapshot.d.ts +18 -2
  128. package/dist/record/frame-snapshot.d.ts.map +1 -1
  129. package/dist/record/frame-targets.d.ts.map +1 -1
  130. package/dist/record/frame.d.ts +14 -2
  131. package/dist/record/frame.d.ts.map +1 -1
  132. package/dist/record/main-pass-geometry.d.ts +36 -4
  133. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  134. package/dist/record/main-pass-material.d.ts +7 -17
  135. package/dist/record/main-pass-material.d.ts.map +1 -1
  136. package/dist/record/main-pass.d.ts.map +1 -1
  137. package/dist/record/mesh-ssbo.d.ts +11 -3
  138. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  139. package/dist/record/recovery-pipeline.d.ts +30 -0
  140. package/dist/record/recovery-pipeline.d.ts.map +1 -0
  141. package/dist/record/render-context.d.ts +28 -5
  142. package/dist/record/render-context.d.ts.map +1 -1
  143. package/dist/record/shadow-pass.d.ts +11 -1
  144. package/dist/record/shadow-pass.d.ts.map +1 -1
  145. package/dist/record/typed-frame-graph.d.ts +1 -1
  146. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  147. package/dist/recovery/render-system-candidate.d.ts +65 -0
  148. package/dist/recovery/render-system-candidate.d.ts.map +1 -0
  149. package/dist/render-contract.d.ts +68 -7
  150. package/dist/render-contract.d.ts.map +1 -1
  151. package/dist/render-system-extract-tail.d.ts +16 -0
  152. package/dist/render-system-extract-tail.d.ts.map +1 -0
  153. package/dist/render-system-extract.d.ts +180 -22
  154. package/dist/render-system-extract.d.ts.map +1 -1
  155. package/dist/render-system.d.ts +131 -4
  156. package/dist/render-system.d.ts.map +1 -1
  157. package/dist/scene/render-scene-types.d.ts +2 -0
  158. package/dist/scene/render-scene-types.d.ts.map +1 -1
  159. package/dist/scene/render-scene.d.ts +31 -1
  160. package/dist/scene/render-scene.d.ts.map +1 -1
  161. package/dist/systems/skin-palette-allocator.d.ts +6 -0
  162. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  163. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  164. package/dist/temporal/index.mjs +6 -6
  165. package/dist/temporal/taa-history-store.d.ts +15 -1
  166. package/dist/temporal/taa-history-store.d.ts.map +1 -1
  167. package/dist/temporal/transaction.d.ts +5 -0
  168. package/dist/temporal/transaction.d.ts.map +1 -1
  169. package/package.json +20 -20
  170. package/src/__tests__/device-feature-admission.unit.test.ts +6 -0
  171. package/src/__tests__/device-loss-fanout.unit.test.ts +107 -0
  172. package/src/__tests__/device-scope-lifecycle.unit.test.ts +21 -0
  173. package/src/__tests__/environment-generation.integration.test.ts +23 -0
  174. package/src/__tests__/expose-renderer-errors.unit.test.ts +34 -0
  175. package/src/__tests__/factory-contract.integration.test.ts +122 -7
  176. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +4 -1
  177. package/src/__tests__/gpu-scene.unit.test.ts +38 -2
  178. package/src/__tests__/ibl/face-uniforms.unit.test.ts +2 -0
  179. package/src/__tests__/ibl-residency-publication.unit.test.ts +113 -0
  180. package/src/__tests__/instances-store.unit.test.ts +46 -0
  181. package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
  182. package/src/__tests__/material-contract-inventory.unit.test.ts +29 -22
  183. package/src/__tests__/material-cooked-projection.unit.test.ts +19 -1
  184. package/src/__tests__/material-pass-policy.unit.test.ts +69 -0
  185. package/src/__tests__/material-snapshot-mutation.integration.test.ts +69 -0
  186. package/src/__tests__/material-static-dynamic-route.unit.test.ts +29 -0
  187. package/src/__tests__/material-stride-surface.unit.test.ts +7 -3
  188. package/src/__tests__/material-surface-authoring.test-d.ts +62 -0
  189. package/src/__tests__/materials-standard-contract.unit.test.ts +104 -0
  190. package/src/__tests__/materials-transmission-validation.unit.test.ts +24 -0
  191. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -5
  192. package/src/__tests__/physical-clearcoat.integration.test.ts +89 -0
  193. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +1 -1
  194. package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +1 -1
  195. package/src/__tests__/recover-error-policy-owner.unit.test.ts +6 -3
  196. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +140 -0
  197. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +127 -18
  198. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +47 -0
  199. package/src/__tests__/renderer-generation-assembly.unit.test.ts +77 -0
  200. package/src/__tests__/renderer-generation-failure.unit.test.ts +108 -0
  201. package/src/__tests__/renderer-producer-root-matrix.integration.test.ts +33 -0
  202. package/src/__tests__/renderer-recovery-capability-matrix.unit.test.ts +59 -0
  203. package/src/__tests__/renderer-recovery-contract.unit.test.ts +73 -0
  204. package/src/__tests__/renderer-recovery-lifecycle.unit.test.ts +216 -0
  205. package/src/__tests__/renderer-target-history-recovery.integration.test.ts +80 -0
  206. package/src/__tests__/shader-manifest-fixture.ts +13 -16
  207. package/src/__tests__/shader-stage-surface.unit.test.ts +1 -1
  208. package/src/__tests__/skin-motion-regression.unit.test.ts +33 -0
  209. package/src/__tests__/skinned-shadow-caster.test.ts +30 -2
  210. package/src/__tests__/standard-layer-projection.unit.test.ts +23 -0
  211. package/src/__tests__/surface-adjacent-regressions.integration.test.ts +60 -0
  212. package/src/__tests__/surface-standard-pipeline.browser.test.ts +20 -0
  213. package/src/__tests__/surface-standard-pipeline.dawn.test.ts +20 -0
  214. package/src/__tests__/surface-standard-pipeline.fixture.ts +132 -0
  215. package/src/__tests__/surface-variant-provenance.integration.test.ts +33 -0
  216. package/src/__tests__/transmission-prewarm.unit.test.ts +95 -85
  217. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +1 -1
  218. package/src/__tests__/volumetric-fog-topology.unit.test.ts +3 -1
  219. package/src/assembly/bundler-contract.ts +9 -0
  220. package/src/assembly/device-feature-admission.ts +15 -5
  221. package/src/assembly/extended-lighting-fallback.ts +216 -0
  222. package/src/assembly/factory.ts +83 -10088
  223. package/src/assembly/material/__tests__/standard-layer-plan-projection.integration.test.ts +18 -0
  224. package/src/assembly/material/__tests__/surface-pass-projection.integration.test.ts +62 -0
  225. package/src/assembly/material/assembly.ts +18 -2
  226. package/src/assembly/material/standard-layer-projection.ts +16 -0
  227. package/src/assembly/material/surface-projection.ts +90 -0
  228. package/src/assembly/material-shader-policy.ts +932 -0
  229. package/src/assembly/material-shader-prewarm.ts +88 -0
  230. package/src/assembly/mesh-ssbo-grow.ts +270 -0
  231. package/src/assembly/recovery/device-loss-fanout.ts +89 -0
  232. package/src/assembly/recovery/generation.ts +141 -0
  233. package/src/assembly/recovery/recovery-attempt.ts +800 -0
  234. package/src/assembly/recovery/renderer-recover.ts +906 -0
  235. package/src/assembly/renderer-facade.ts +342 -0
  236. package/src/assembly/renderer-frame-transaction.ts +45 -0
  237. package/src/assembly/renderer-helpers.ts +99 -0
  238. package/src/assembly/renderer-lifecycle.ts +159 -0
  239. package/src/assembly/shader-prewarm-policy.ts +157 -0
  240. package/src/assembly/webgpu-ready-contract.ts +14 -0
  241. package/src/assembly/webgpu-ready.ts +4066 -0
  242. package/src/assembly/webgpu-renderer.ts +4027 -0
  243. package/src/assembly/webgpu-vertex-layouts.ts +132 -0
  244. package/src/assets/asset-decoders.ts +27 -4
  245. package/src/components/camera.ts +1 -1
  246. package/src/components/instances.ts +14 -197
  247. package/src/debug-draw-glue.ts +1 -1
  248. package/src/device/device-scope.ts +35 -6
  249. package/src/device/gpu-residency.ts +194 -26
  250. package/src/environment/frame.ts +6 -0
  251. package/src/environment/inspection.ts +10 -0
  252. package/src/environment/lifecycle.ts +114 -8
  253. package/src/errors/recover.ts +76 -5
  254. package/src/errors/render.ts +17 -0
  255. package/src/extract/contracts.ts +974 -0
  256. package/src/features/host.ts +16 -0
  257. package/src/features/prepared-gpu-work.ts +20 -2
  258. package/src/features/prepared-graphics-store.ts +17 -0
  259. package/src/gpu-driven/production-raster.ts +131 -3
  260. package/src/gpu-scene.ts +87 -31
  261. package/src/ibl/IblPipelineCache.ts +134 -50
  262. package/src/ibl/skylight-bind-group.ts +26 -0
  263. package/src/index.ts +10 -1
  264. package/src/instance-buffer-cache.ts +14 -1
  265. package/src/instances.ts +288 -0
  266. package/src/light-buffer-layout.ts +3 -0
  267. package/src/materials.ts +365 -53
  268. package/src/mesh-material-bindings.ts +59 -0
  269. package/src/occlusion-proxy.ts +64 -0
  270. package/src/pbr-pipeline.ts +139 -15
  271. package/src/pipeline-builder.ts +14 -10
  272. package/src/pipeline-spec.ts +6 -14
  273. package/src/points-lines/__tests__/cache-recovery.unit.test.ts +22 -0
  274. package/src/points-lines/prepare.ts +9 -0
  275. package/src/points-lines/standard-owner.ts +403 -0
  276. package/src/record/__tests__/frame-targets-recovery.unit.test.ts +60 -0
  277. package/src/record/__tests__/main-pass-geometry.unit.test.ts +26 -1
  278. package/src/record/__tests__/mesh-ssbo.unit.test.ts +38 -0
  279. package/src/record/__tests__/recovery-pipeline.unit.test.ts +124 -0
  280. package/src/record/__tests__/sprite-material-abi.unit.test.ts +28 -13
  281. package/src/record/__tests__/standard-pbr-ubo-layout.unit.test.ts +47 -4
  282. package/src/record/__tests__/temporal-payload.unit.test.ts +22 -43
  283. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -1
  284. package/src/record/frame-snapshot.ts +34 -2
  285. package/src/record/frame-targets.ts +11 -1
  286. package/src/record/frame.ts +37 -4
  287. package/src/record/helpers.ts +1 -1
  288. package/src/record/main-pass-geometry.ts +727 -63
  289. package/src/record/main-pass-material.ts +316 -209
  290. package/src/record/main-pass-sprite-draws.ts +7 -8
  291. package/src/record/main-pass.ts +94 -6
  292. package/src/record/mesh-ssbo.ts +32 -10
  293. package/src/record/recovery-pipeline.ts +695 -0
  294. package/src/record/render-context.ts +39 -4
  295. package/src/record/shadow-pass.ts +46 -99
  296. package/src/record/typed-frame-graph.ts +45 -1
  297. package/src/record/view-ubo.ts +3 -3
  298. package/src/recovery/render-system-candidate.ts +876 -0
  299. package/src/render-contract.ts +69 -7
  300. package/src/render-system-extract-tail.ts +3039 -0
  301. package/src/render-system-extract.ts +177 -3003
  302. package/src/render-system.ts +906 -662
  303. package/src/scene/render-scene-types.ts +2 -0
  304. package/src/scene/render-scene.ts +703 -37
  305. package/src/systems/skin-palette-allocator.ts +129 -7
  306. package/src/temporal/frame-coordinator.ts +4 -1
  307. package/src/temporal/taa-history-store.ts +46 -2
  308. package/src/temporal/temporal-view.ts +1 -1
  309. package/src/temporal/transaction.ts +9 -0
  310. package/src/tilemap-chunk-extract-system.ts +1 -1
  311. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +19 -12
  312. package/src/transmission/__tests__/surface-compatibility.integration.test.ts +32 -0
  313. package/dist/chunk-4L3P6DT5.mjs.map +0 -1
  314. package/dist/chunk-4NPUASZR.mjs +0 -203
  315. package/dist/chunk-4NPUASZR.mjs.map +0 -1
  316. package/dist/chunk-DIYW3M5D.mjs.map +0 -1
  317. package/dist/chunk-FBC4R44J.mjs.map +0 -1
  318. package/dist/chunk-FF7K3FSO.mjs.map +0 -1
  319. package/dist/chunk-HZUPSYW7.mjs.map +0 -1
  320. package/dist/chunk-QODA5UKU.mjs.map +0 -1
  321. package/dist/chunk-YALPS44A.mjs.map +0 -1
  322. package/dist/chunk-ZUWKAZ2I.mjs.map +0 -1
@@ -20,14 +20,20 @@ import type {
20
20
  TextureView,
21
21
  } from '@forgeax/engine-rhi';
22
22
  import { err, ok, RhiError } from '@forgeax/engine-rhi';
23
- import type { MaterialRuntimeArtifact, MaterialShaderArtifact } from '@forgeax/engine-shader';
23
+ import {
24
+ DEFAULT_STANDARD_PBR_PARAM_SCHEMA,
25
+ type MaterialRuntimeArtifact,
26
+ type MaterialShaderArtifact,
27
+ } from '@forgeax/engine-shader';
24
28
  import type {
29
+ MaterialAsset,
25
30
  MaterialRenderState,
26
31
  ParamSchemaEntry,
27
32
  PassKind,
28
33
  PrimitiveTopology,
29
34
  VertexAttributeMap,
30
35
  } from '@forgeax/engine-types';
36
+ import { derive } from '@forgeax/engine-types';
31
37
  import type { MaterialRenderProjection } from '../assembly/material/assembly';
32
38
  import type { DeviceScope } from '../device/device-scope';
33
39
  import type { MeshGpuHandles } from '../device/gpu-residency';
@@ -103,6 +109,18 @@ export interface MaterialShaderPipelineEntry {
103
109
  readonly group2Contract: PipelineGroup2Contract;
104
110
  }
105
111
 
112
+ /**
113
+ * Armed only for the first published recovery frame. Candidate preparation
114
+ * must make every pipeline and static residency lookup a cache hit before this
115
+ * guard is armed; a miss is a hard failure before queue submission.
116
+ */
117
+ export interface RecoveryColdWorkGuard {
118
+ arm(): void;
119
+ notePipelineColdWork(): void;
120
+ noteUploadColdWork(): void;
121
+ finish(): void;
122
+ }
123
+
106
124
  import type {
107
125
  RenderDebugOverlay,
108
126
  RenderPipelineContext,
@@ -133,9 +151,14 @@ export interface RenderSystemRuntime {
133
151
  /** Internal backend-fixture hook evaluated at the submit transaction edge. */
134
152
  readonly beforeSubmit?: ((device: RhiDevice) => RhiError | undefined) | undefined;
135
153
  readonly deviceScope: DeviceScope;
154
+ /** Resolve a recorder/backend wrapper before configuring the native surface. */
155
+ readonly resolveSurfaceDevice?: (device: RhiDevice) => Result<RhiDevice, RhiError>;
136
156
  readonly errorRegistry: RhiErrorListenerRegistry;
137
157
  readonly debugOverlay?: RenderDebugOverlay | undefined;
138
158
  readonly healthRegistry: HealthListenerRegistry;
159
+ readonly getMaterialProjectionForPayload?: (
160
+ material: MaterialAsset,
161
+ ) => MaterialRenderProjection | undefined;
139
162
  readonly getMaterialProjection?: (materialGuid: string) => MaterialRenderProjection | undefined;
140
163
  readonly getMaterialArtifact?: (specializationKey: string) => MaterialRuntimeArtifact | undefined;
141
164
  /** Producer-owned cooked Standard PBR artifact used by GPU-driven projection. */
@@ -180,11 +203,17 @@ export interface RenderSystemRuntime {
180
203
  layoutKindOverride?: 'pbr' | 'pbr-skin',
181
204
  additionalColorFormats?: readonly GPUTextureFormat[],
182
205
  ) => MaterialShaderPipelineEntry | null;
206
+ /** Reflection-derived UV count included in the material PipelineSpec cache key. */
207
+ readonly getMaterialShaderUvSetCount?: (materialShaderId: string) => number | undefined;
183
208
  readonly getMaterialShaderBindingContract?: (
184
209
  materialShaderId: string,
185
210
  ) => 'group-0' | 'group-0-resource' | 'view-only' | 'view-and-scene-depth' | 'render-material';
186
211
  readonly getParamSchema?: (materialShaderId: string) => readonly ParamSchemaEntry[] | undefined;
187
- readonly getMaterialBindGroupLayout?: (materialShaderId: string) => BindGroupLayout | undefined;
212
+ readonly getMaterialBindGroupLayout?: (
213
+ materialShaderId: string,
214
+ materialParamSchema?: readonly ParamSchemaEntry[],
215
+ ) => BindGroupLayout | undefined;
216
+ recoveryColdWorkGuard?: RecoveryColdWorkGuard | undefined;
188
217
  readonly metrics: EngineMetrics;
189
218
  readonly lookupPostProcess?: (
190
219
  id: string,
@@ -196,6 +225,8 @@ export interface RenderSystemRuntime {
196
225
  colorFormat: GPUTextureFormat,
197
226
  entry?: import('../fullscreen-post-process-pass').PostProcessShaderEntry,
198
227
  ) => RhiRenderPipeline | null;
228
+ /** Candidate-owned post-process pipelines transferred at publication. */
229
+ readonly getRecoveryPostProcessPipelines?: () => ReadonlyMap<string, RhiRenderPipeline>;
199
230
  readonly clearPostProcessPipelineCache?: (id: string) => void;
200
231
  readonly invalidateShaderModule?: (label: string) => void;
201
232
  readonly dynamicTextureStore?: DynamicTextureStore | undefined;
@@ -356,6 +387,8 @@ export interface PipelineState {
356
387
  readonly skinPaletteAllocator: SkinPaletteAllocator | null;
357
388
  readonly defaultWhiteTextureView: TextureView;
358
389
  readonly defaultNormalTextureView: TextureView;
390
+ /** Missing anisotropy maps encode a neutral direction and unit strength. */
391
+ readonly defaultAnisotropyTextureView?: TextureView;
359
392
  readonly unlitPipelineHdr: RhiRenderPipeline | null;
360
393
  readonly shadowFallbackTextureView: TextureView;
361
394
  readonly shadowAtlasFallbackTextureView: TextureView;
@@ -386,6 +419,8 @@ export interface RenderSystemInternals extends RenderSystemRuntime {
386
419
  readonly standardProfile?: StandardProfile | undefined;
387
420
  readonly standardPipeline: RenderFrameState['activePipeline'];
388
421
  readonly featureHost?: RenderFeatureHost | undefined;
422
+ /** Install a detached feature host only at the recovery publication boundary. */
423
+ readonly setFeatureHost?: ((host: RenderFeatureHost) => void) | undefined;
389
424
  readonly shaderModuleFactory?: PipelineBuilderShaderModuleFactory;
390
425
  readonly createShaderModule?: (
391
426
  device: import('@forgeax/engine-rhi').RhiDevice,
@@ -492,8 +527,8 @@ export type _InternalRenderPipelineContext = RenderPipelineContext & _StandardFo
492
527
 
493
528
  export type RecordProfileRunner = <T>(phase: RenderRecordPhase, action: () => T) => T;
494
529
 
495
- export const STANDARD_PBR_UBO_SIZE = 384;
496
- export const MATERIAL_PER_ENTITY_STRIDE = 512;
530
+ export const STANDARD_PBR_UBO_SIZE = derive(DEFAULT_STANDARD_PBR_PARAM_SCHEMA).uboLayout.totalBytes;
531
+ export const MATERIAL_PER_ENTITY_STRIDE = Math.ceil(STANDARD_PBR_UBO_SIZE / 256) * 256;
497
532
 
498
533
  export type SwapChainFormatPair = {
499
534
  readonly storage: GPUTextureFormat;
@@ -8,14 +8,8 @@ import {
8
8
  type RhiRenderPassEncoder,
9
9
  } from '@forgeax/engine-rhi';
10
10
  import type { MaterialRenderState, PassSelector } from '@forgeax/engine-types';
11
- import { GpuBuffer } from '../gpu-resource';
12
- import {
13
- GPU_BUFFER_USAGE_COPY_DST,
14
- GPU_BUFFER_USAGE_STORAGE,
15
- GPU_BUFFER_USAGE_UNIFORM,
16
- } from '../gpu-usage';
11
+ import type { GpuBuffer } from '../gpu-resource';
17
12
  import { assembleMaterialWithSkylightEntries } from '../ibl/skylight-bind-group';
18
- import type { InstanceBufferCacheEntry } from '../instance-buffer-cache';
19
13
  import {
20
14
  buildPbrMaterialUserRegionEntries,
21
15
  createPbrSkinMeshBindGroupEntries,
@@ -26,15 +20,15 @@ import {
26
20
  import { COOKIE_MATRIX_BYTES } from '../prepare/extended-lighting/resources';
27
21
  import type { DispatchEntry, ExtractedLights } from '../render-system-extract';
28
22
  import { matchPass } from '../systems/pass-selector';
29
- import { worldEntityKey } from './frame-snapshot';
30
- import { resolveGeometryInstancesBindGroup } from './main-pass-geometry';
23
+ import {
24
+ resolveGeometryInstanceBuffer,
25
+ resolveGeometryInstancesBindGroup,
26
+ } from './main-pass-geometry';
31
27
  import {
32
28
  getOrCreateFromChain,
33
- MAX_UNIFORM_INSTANCES,
34
29
  MESH_PER_ENTITY_STRIDE,
35
30
  MESH_SSBO_BYTES,
36
31
  MESH_UBO_FULL_ARRAY_BYTES,
37
- packInstanceStorageBuffer,
38
32
  } from './mesh-ssbo';
39
33
  import type { _InternalRenderPipelineContext } from './render-context';
40
34
  import { STANDARD_PBR_UBO_SIZE } from './render-context';
@@ -253,13 +247,16 @@ function ensureTypedShadowViewBg(
253
247
  }
254
248
 
255
249
  interface ShadowDispatch {
250
+ readonly materialHandle: number;
256
251
  readonly materialShaderId: string;
257
252
  readonly renderState: MaterialRenderState | undefined;
253
+ readonly materialSlot: number | undefined;
254
+ readonly paramSnapshot: DispatchEntry['paramSnapshot'];
258
255
  }
259
256
 
260
257
  type ShadowDispatchMap = ReadonlyMap<number, ReadonlyMap<number, ShadowDispatch>>;
261
258
 
262
- function shadowShaderMap(c: _InternalRenderPipelineContext): ShadowDispatchMap {
259
+ export function shadowShaderMap(c: _InternalRenderPipelineContext): ShadowDispatchMap {
263
260
  const shaders = new Map<number, Map<number, ShadowDispatch>>();
264
261
  for (const entry of c.dispatch) {
265
262
  if (entry.tags.LightMode === 'ShadowCaster' && entry.materialShaderId !== undefined) {
@@ -268,9 +265,20 @@ function shadowShaderMap(c: _InternalRenderPipelineContext): ShadowDispatchMap {
268
265
  byMaterial = new Map<number, ShadowDispatch>();
269
266
  shaders.set(entry.renderableIndex, byMaterial);
270
267
  }
268
+ const candidateSlots =
269
+ c.materialSlotIndices[entry.renderableIndex] ?? c.materialSlotIndices.flat();
270
+ const materialSlot = candidateSlots.find(
271
+ (slot) => c.materialSlots[slot]?.materialHandle === entry.materialHandle,
272
+ );
271
273
  byMaterial.set(entry.materialHandle, {
272
- materialShaderId: entry.materialShaderId,
274
+ materialHandle: entry.materialHandle,
275
+ materialShaderId:
276
+ entry.materialShaderId === 'forgeax::default-standard-pbr'
277
+ ? SHADOW_CASTER_SHADER_ID
278
+ : entry.materialShaderId,
273
279
  renderState: entry.renderState,
280
+ materialSlot,
281
+ paramSnapshot: entry.paramSnapshot,
274
282
  });
275
283
  }
276
284
  }
@@ -640,89 +648,25 @@ function recordShadowCasterDraws(
640
648
  shadowPass.setBindGroup(2, shadowMeshBindGroup, [i * MESH_PER_ENTITY_STRIDE]);
641
649
  }
642
650
 
643
- // C1 + C2 (w12): per-entity instance buffer + instanceCount
644
- let shadowInstanceBuffer: Buffer = pipelineState.identityInstanceBuffer;
645
- let shadowInstanceCount = 1;
646
- const shadowInst = entry.source.instances;
647
- if (shadowInst?.instanceCount === 0) continue;
648
- if (shadowInst !== undefined) {
649
- const uniformFallback = runtime.device.caps.storageBuffer === false;
650
- // Over-cap uniform fallback can't fit the per-instance window — bind
651
- // identity and let the shader collapse (same semantics as the main
652
- // pass). Otherwise build/upload the per-entity instance buffer: storage
653
- // by default, uniform when the device lacks storage buffers.
654
- if (uniformFallback && shadowInst.instanceCount > MAX_UNIFORM_INSTANCES) {
655
- shadowInstanceCount = shadowInst.instanceCount;
656
- shadowInstanceBuffer = pipelineState.identityInstanceBuffer;
657
- } else {
658
- const bufUsage = uniformFallback
659
- ? GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST
660
- : GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST;
661
- const instancePayload = uniformFallback
662
- ? shadowInst.transforms
663
- : packInstanceStorageBuffer(shadowInst.transforms);
664
- const requestedBytes = instancePayload.byteLength;
665
- const cached = c.frameState.instanceBuffers.get(
666
- worldEntityKey(entry.source.worldId, shadowInst.cacheKey),
667
- );
668
- let active: InstanceBufferCacheEntry | null = null;
669
- if (
670
- cached !== undefined &&
671
- cached.uploadedArchVersion === shadowInst.archVersion &&
672
- cached.uploadedByteLength === requestedBytes
673
- ) {
674
- active = cached;
675
- } else if (requestedBytes > 0) {
676
- const bufRes = runtime.device.createBuffer({
677
- size: requestedBytes,
678
- usage: bufUsage,
679
- mappedAtCreation: false,
680
- });
681
- if (!bufRes.ok) {
682
- runtime.errorRegistry.fire(bufRes.error);
683
- } else {
684
- // feat-20260619 M4 / F12: destroy the old cached buffer
685
- // before replacing it with the new one (D-6).
686
- if (cached !== undefined && !cached.buffer.isDestroyed) {
687
- const r = cached.buffer.destroy();
688
- if (!r.ok) runtime.errorRegistry.fire(r.error);
689
- }
690
- const newBuffer = new GpuBuffer(runtime.device, bufRes.value);
691
- active = {
692
- buffer: newBuffer,
693
- uploadedArchVersion: shadowInst.archVersion,
694
- uploadedByteLength: requestedBytes,
695
- };
696
- c.frameState.instanceBuffers.set(
697
- worldEntityKey(entry.source.worldId, shadowInst.cacheKey),
698
- active,
699
- );
700
- }
701
- }
702
- if (active !== null) {
703
- const writeRes = runtime.device.queue.writeBuffer(
704
- active.buffer.handle,
705
- 0,
706
- instancePayload,
707
- );
708
- if (!writeRes.ok) {
709
- runtime.errorRegistry.fire(writeRes.error);
710
- } else {
711
- shadowInstanceBuffer = active.buffer.handle;
712
- shadowInstanceCount = Math.max(1, shadowInst.instanceCount);
713
- }
714
- }
715
- }
716
- }
651
+ // Main and shadow passes share one instance residency owner and upload receipt.
652
+ const shadowInstanceDraws = resolveGeometryInstanceBuffer(
653
+ c,
654
+ entry,
655
+ [
656
+ {
657
+ instanceBuffer: pipelineState.identityInstanceBuffer,
658
+ instanceBindGroup: resolveGeometryInstancesBindGroup(
659
+ c,
660
+ pipelineState.identityInstanceBuffer,
661
+ ),
662
+ instanceCount: 1,
663
+ probeOffset: -1,
664
+ },
665
+ ],
666
+ false,
667
+ );
668
+ if (shadowInstanceDraws === null) continue;
717
669
 
718
- // Bind per-entity instances BG for @group(3) (or fallback identity).
719
- // D-4: write end of the HDRP shadow-instances producer/consumer pair.
720
- // outerKey = worldEntityKey(entry.source.worldId, entry.source.entityKey),
721
- // handle = shadowInstanceBuffer; the HDRP main pass read end (:3820 below)
722
- // must look up the same (compositeKey, instBuffer) leaf or the shadow
723
- // instances silently drop.
724
- const shadowInstancesBg = resolveGeometryInstancesBindGroup(c, shadowInstanceBuffer);
725
- shadowPass.setBindGroup(3, shadowInstancesBg);
726
670
  // feat-20260608 M4 / w16: per-submesh shadow draw loop.
727
671
  // Only draw submeshes whose topology is triangle-list or triangle-strip
728
672
  // (line-list / point-list submeshes cast no shadow and are skipped).
@@ -772,10 +716,13 @@ function recordShadowCasterDraws(
772
716
  shadowPass.setPipeline(entryShadowPipeline);
773
717
  shadowLastPipeline = entryShadowPipeline;
774
718
  }
775
- if (entry.mesh.indexed) {
776
- shadowPass.drawIndexed(sm.indexCount, shadowInstanceCount, sm.indexOffset, 0, 0);
777
- } else {
778
- shadowPass.draw(sm.vertexCount, shadowInstanceCount, 0, 0);
719
+ for (const instanceDraw of shadowInstanceDraws) {
720
+ shadowPass.setBindGroup(3, instanceDraw.instanceBindGroup);
721
+ if (entry.mesh.indexed) {
722
+ shadowPass.drawIndexed(sm.indexCount, instanceDraw.instanceCount, sm.indexOffset, 0, 0);
723
+ } else {
724
+ shadowPass.draw(sm.vertexCount, instanceDraw.instanceCount, 0, 0);
725
+ }
779
726
  }
780
727
  }
781
728
  }
@@ -811,6 +811,7 @@ export interface RenderFeatureGraphCandidate {
811
811
  }
812
812
 
813
813
  const featureGraphStates = new WeakMap<RenderSystemInternals, RenderFeatureGraphRuntimeState>();
814
+ const graphDeviceGenerations = new WeakMap<object, number>();
814
815
 
815
816
  interface TimingInstrumentationState {
816
817
  readonly instrumentation: RenderGraphPassInstrumentation<RenderPipelineFrame>;
@@ -1349,6 +1350,7 @@ export function ensureCompiledFrameGraph(
1349
1350
  volumetricFog?: ExtractedVolumetricFog,
1350
1351
  volumeTopologyCandidate = false,
1351
1352
  standardLighting?: StandardTopologyInputValue,
1353
+ onRecoveryError?: (error: unknown) => void,
1352
1354
  ): CompiledRenderGraph<RenderPipelineFrame> | null {
1353
1355
  const requestedStandardLightingSignature = standardLightingTopologySignature(standardLighting);
1354
1356
  const lastKnownGood = (): CompiledRenderGraph<RenderPipelineFrame> | null =>
@@ -1361,6 +1363,7 @@ export function ensureCompiledFrameGraph(
1361
1363
  const featurePlans = featureGraphCandidate?.plans ?? getRenderFeatureGraphState(internals).plans;
1362
1364
  const projectedFeaturePlans = validateRenderFeaturePlans(featurePlans);
1363
1365
  if (!projectedFeaturePlans.ok) {
1366
+ onRecoveryError?.(projectedFeaturePlans.error);
1364
1367
  reportRenderFeatureGraphError(internals, projectedFeaturePlans.error);
1365
1368
  featureGraphCandidate?.onRejected?.();
1366
1369
  return volumeTopologyCandidate ? null : lastKnownGood();
@@ -1383,6 +1386,7 @@ export function ensureCompiledFrameGraph(
1383
1386
  standardLighting,
1384
1387
  );
1385
1388
  if (!topologyResult.ok) {
1389
+ onRecoveryError?.(topologyResult.error);
1386
1390
  internals.errorRegistry.fire(topologyResult.error);
1387
1391
  featureGraphCandidate?.onRejected?.();
1388
1392
  return volumeTopologyCandidate ? null : lastKnownGood();
@@ -1521,6 +1525,7 @@ export function ensureCompiledFrameGraph(
1521
1525
  topology,
1522
1526
  );
1523
1527
  if (!built.ok) {
1528
+ onRecoveryError?.(built.error);
1524
1529
  internals.errorRegistry.fire(built.error);
1525
1530
  featureGraphCandidate?.onRejected?.();
1526
1531
  return volumeTopologyCandidate ? null : lastKnownGood();
@@ -1530,6 +1535,7 @@ export function ensureCompiledFrameGraph(
1530
1535
  surfaceSize: { width: surfaceWidth, height: surfaceHeight },
1531
1536
  });
1532
1537
  if (!compiled.ok) {
1538
+ onRecoveryError?.(compiled.error);
1533
1539
  internals.errorRegistry.fire(compiled.error);
1534
1540
  featureGraphCandidate?.onRejected?.();
1535
1541
  return lastKnownGood();
@@ -1552,6 +1558,10 @@ export function ensureCompiledFrameGraph(
1552
1558
  getColorTargetTexture: (name) => compiledTargets.getColorTargetTexture(name),
1553
1559
  graphGeneration: frameState.graphGeneration,
1554
1560
  };
1561
+ const deviceGeneration = internals.deviceScope?.generation;
1562
+ if (deviceGeneration !== undefined) {
1563
+ graphDeviceGenerations.set(compiled.value, deviceGeneration);
1564
+ }
1555
1565
  gpuDriven?._commitResourceReplacement();
1556
1566
  if (featureGraphCandidate !== undefined && !postProcessAdmission.pending) {
1557
1567
  commitFeatureCandidate(internals, featureGraphCandidate);
@@ -1635,6 +1645,29 @@ export function executeCompiledFrameGraph(
1635
1645
  : legacyTimingInstrumentation;
1636
1646
  type PreparedCommand = { readonly command: CommandBuffer };
1637
1647
  let preparedCommand: PreparedCommand = { command: undefined as never };
1648
+ const deviceScope = internals.deviceScope;
1649
+ const graphDeviceGeneration = graphDeviceGenerations.get(graph);
1650
+ if (
1651
+ graphDeviceGeneration !== undefined &&
1652
+ deviceScope !== undefined &&
1653
+ graphDeviceGeneration !== deviceScope.generation
1654
+ ) {
1655
+ internals.errorRegistry.fire(
1656
+ new RhiError({
1657
+ code: 'webgpu-runtime-error',
1658
+ expected: 'the compiled frame graph belongs to the active device generation',
1659
+ hint: 'discard the stale graph and rebuild it from the retained logical frame plan',
1660
+ detail: {
1661
+ error: {
1662
+ code: 'stale-frame-graph-generation',
1663
+ message: `graph=${graphDeviceGeneration}; current=${deviceScope.generation}`,
1664
+ },
1665
+ },
1666
+ }),
1667
+ );
1668
+ return false;
1669
+ }
1670
+ const capturedGeneration = deviceScope?.generation;
1638
1671
  const transaction = executeRendererFrameTransaction<PreparedCommand>({
1639
1672
  build: () => ({ ok: true, value: { command: undefined as never } }),
1640
1673
  execute: () => {
@@ -1662,7 +1695,7 @@ export function executeCompiledFrameGraph(
1662
1695
  const graphAccess = frameState.perFrameGraph;
1663
1696
  const graphTexture = graphAccess?.getColorTargetTexture(graphCapture.name);
1664
1697
  const descriptor = graphAccess?.getColorTargetDescriptor(graphCapture.name);
1665
- const frameId = frameState.frameNumber - 1;
1698
+ const frameId = frameState.frameNumber;
1666
1699
  const captureFailure = (message: string): void => {
1667
1700
  internals.errorRegistry.fire(
1668
1701
  new RhiError({
@@ -1865,6 +1898,17 @@ export function executeCompiledFrameGraph(
1865
1898
  timingCapture?.markSubmitted();
1866
1899
  return { ok: true, value: undefined };
1867
1900
  },
1901
+ ...(capturedGeneration === undefined || deviceScope === undefined
1902
+ ? {}
1903
+ : {
1904
+ generationFence: {
1905
+ capturedGeneration,
1906
+ // Read the owner at submit time. Recovery publishes a replacement
1907
+ // scope synchronously between finish and submit; retaining the
1908
+ // entry scope here would make that race invisible to the fence.
1909
+ currentGeneration: () => internals.deviceScope?.generation ?? -1,
1910
+ },
1911
+ }),
1868
1912
  commit: () => {
1869
1913
  frameHooks?.onSubmitted?.();
1870
1914
  if (frameState.temporalFrameInput !== undefined) {
@@ -164,9 +164,9 @@ export function writeViewUbo(
164
164
  const VIEW_PAYLOAD_FLOATS = 240;
165
165
  const viewPayload = new Float32Array(VIEW_PAYLOAD_FLOATS);
166
166
  for (let i = 0; i < 16; i++) viewPayload[i] = mainProjection[i] ?? 0;
167
- viewPayload[16] = (light.direction[0] ?? 0) * light.intensity;
168
- viewPayload[17] = (light.direction[1] ?? -1) * light.intensity;
169
- viewPayload[18] = (light.direction[2] ?? 0) * light.intensity;
167
+ viewPayload[16] = light.direction[0] ?? 0;
168
+ viewPayload[17] = light.direction[1] ?? -1;
169
+ viewPayload[18] = light.direction[2] ?? 0;
170
170
  viewPayload[20] = light.color[0] ?? 0;
171
171
  viewPayload[21] = light.color[1] ?? 0;
172
172
  viewPayload[22] = light.color[2] ?? 0;