@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
@@ -0,0 +1,20 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { projectStandardSurfacePasses } from '../assembly/material/surface-projection';
3
+ import { layerPlanFor, passFor, SURFACE_CASES } from './surface-standard-pipeline.fixture';
4
+
5
+ describe('Standard Surface render projection contract', () => {
6
+ it('keeps physical roots Forward-only while base roots remain Deferred', () => {
7
+ for (const surfaceCase of SURFACE_CASES) {
8
+ const passes = projectStandardSurfacePasses({
9
+ surfaceModule: surfaceCase.surfaceModule,
10
+ values: surfaceCase.material.values ?? {},
11
+ geometryVariant: 'skinned',
12
+ lightingLane: 'clustered',
13
+ alphaClip: true,
14
+ layerPlan: layerPlanFor(surfaceCase),
15
+ });
16
+ expect(passes.some((entry) => entry.name === passFor(surfaceCase))).toBe(true);
17
+ expect(passes[0]?.program.moduleSlots?.surface).toBe(surfaceCase.surfaceModule);
18
+ }
19
+ });
20
+ });
@@ -0,0 +1,132 @@
1
+ import { AssetGuid, type PackageId } from '@forgeax/engine-pack/guid';
2
+ import type { MaterialAsset } from '@forgeax/engine-types';
3
+ import { deriveStandardLayerPlan } from '@forgeax/engine-types';
4
+
5
+ type SurfacePack = {
6
+ packageId: PackageId;
7
+ build: () =>
8
+ | { readonly ok: true; readonly value: Readonly<Record<string, MaterialAsset>> }
9
+ | Promise<{ readonly ok: true; readonly value: Readonly<Record<string, MaterialAsset>> }>;
10
+ };
11
+
12
+ type ViteImportMeta = ImportMeta & {
13
+ glob<T>(
14
+ patterns: readonly string[],
15
+ options: { readonly eager: true; readonly import: 'default' },
16
+ ): Readonly<Record<string, T>>;
17
+ };
18
+
19
+ const packModules = Object.values(
20
+ (import.meta as ViteImportMeta).glob<SurfacePack>(
21
+ [
22
+ '../../../../templates/game-3d/assets/materials.pack.ts',
23
+ '../../../../apps/preview/assets/surface-standard-evidence.pack.ts',
24
+ ],
25
+ {
26
+ eager: true,
27
+ import: 'default',
28
+ },
29
+ ),
30
+ );
31
+ if (packModules.length !== 2)
32
+ throw new Error('surface-evidence-fixture: required pack module missing');
33
+
34
+ const builtPacks = packModules.map((pack) => {
35
+ const built = pack.build();
36
+ if (built instanceof Promise || !built.ok) {
37
+ throw new Error('surface-evidence-fixture: materials pack did not produce outputs');
38
+ }
39
+ return { packageId: pack.packageId, values: built.value };
40
+ });
41
+ const materials = Object.assign({}, ...builtPacks.map((pack) => pack.values)) as Record<
42
+ string,
43
+ MaterialAsset | undefined
44
+ >;
45
+
46
+ function guidForSourceKey(sourceKey: string): AssetGuid {
47
+ const owner = builtPacks.find((pack) => Object.hasOwn(pack.values, sourceKey));
48
+ if (owner === undefined) {
49
+ throw new Error(`surface-evidence-fixture: source key '${sourceKey}' is not published`);
50
+ }
51
+ return AssetGuid.derive(owner.packageId, sourceKey);
52
+ }
53
+
54
+ const rustedIron = materials['material/rusted-iron'];
55
+
56
+ const defaultBase = materials['material/ground'];
57
+ const defaultPhysical = materials['material/painted'];
58
+ const customPhysical = materials['material/rusted-iron-custom-physical'];
59
+ if (
60
+ rustedIron === undefined ||
61
+ defaultBase === undefined ||
62
+ defaultPhysical === undefined ||
63
+ customPhysical === undefined
64
+ )
65
+ throw new Error('surface-evidence-fixture: required material row missing');
66
+
67
+ export const SURFACE_CLOSURE = 'forgeax_material::surface_v1';
68
+ export const SURFACE_SOURCE_CLOSURE = [
69
+ 'templates/game-3d/assets/materials.pack.ts',
70
+ 'templates/game-3d/assets/shaders/rusted-iron.wgsl',
71
+ ] as const;
72
+ export const SURFACE_SOURCE_PATH = 'templates/game-3d/assets/shaders/rusted-iron.wgsl';
73
+ export const SURFACE_MATERIAL_SOURCE_KEY = 'material/rusted-iron';
74
+ export const EVIDENCE_SOURCE_SHA =
75
+ (import.meta as ImportMeta & { env?: Record<string, string | undefined> }).env
76
+ ?.VITE_FORGEAX_EVIDENCE_SOURCE_SHA ?? 'unavailable';
77
+
78
+ export const SURFACE_CASES = [
79
+ {
80
+ id: 'default-base',
81
+ material: defaultBase,
82
+ materialGuid: AssetGuid.format(guidForSourceKey('material/ground')),
83
+ surfaceModule: defaultBase.passes?.[0]?.program.moduleSlots?.surface ?? '',
84
+ physical: false,
85
+ },
86
+ {
87
+ id: 'custom-base',
88
+ material: rustedIron,
89
+ materialGuid: AssetGuid.format(guidForSourceKey('material/rusted-iron')),
90
+ surfaceModule: rustedIron.passes?.[0]?.program.moduleSlots?.surface ?? '',
91
+ physical: false,
92
+ },
93
+ {
94
+ id: 'default-physical',
95
+ material: defaultPhysical,
96
+ materialGuid: '019fb7ce-3200-7000-8000-000000000002',
97
+ surfaceModule: defaultPhysical.passes?.[0]?.program.moduleSlots?.surface ?? '',
98
+ physical: true,
99
+ },
100
+ {
101
+ id: 'custom-physical',
102
+ material: customPhysical,
103
+ materialGuid: '019fb7ce-3f00-7000-8000-000000000001',
104
+ surfaceModule: customPhysical.passes?.[0]?.program.moduleSlots?.surface ?? '',
105
+ physical: true,
106
+ },
107
+ ] as const;
108
+
109
+ export function layerPlanFor(surfaceCase: (typeof SURFACE_CASES)[number]) {
110
+ return deriveStandardLayerPlan(
111
+ surfaceCase.physical ? (surfaceCase.material.parameters ?? []) : [],
112
+ );
113
+ }
114
+
115
+ export function baseColorFor(
116
+ surfaceCase: (typeof SURFACE_CASES)[number],
117
+ ): readonly [number, number, number] {
118
+ const values = surfaceCase.material.values ?? {};
119
+ const value = values.baseColor ?? values.ironColor;
120
+ if (!Array.isArray(value) || value.length < 3) {
121
+ throw new Error(`surface-evidence-fixture: missing base color for ${surfaceCase.id}`);
122
+ }
123
+ return [Number(value[0]), Number(value[1]), Number(value[2])];
124
+ }
125
+
126
+ export function passFor(surfaceCase: (typeof SURFACE_CASES)[number]): 'forward' | 'deferred' {
127
+ return layerPlanFor(surfaceCase).mode === 'physical' ? 'forward' : 'deferred';
128
+ }
129
+
130
+ export function publicationGenerationFor(_surfaceCase: (typeof SURFACE_CASES)[number]): number {
131
+ return 1;
132
+ }
@@ -0,0 +1,33 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ projectStandardSurfacePasses,
4
+ type StandardSurfaceProjectionOptions,
5
+ } from '../assembly/material/surface-projection';
6
+
7
+ type FullCustomFixture = StandardSurfaceProjectionOptions & {
8
+ readonly surfaceKind: 'full-custom';
9
+ readonly declaredPasses: readonly ('forward' | 'deferred' | 'shadow-caster')[];
10
+ };
11
+
12
+ describe('full-custom Surface provenance', () => {
13
+ it('does not synthesize an undeclared Deferred pass or fallback identity', () => {
14
+ const options: FullCustomFixture = {
15
+ surfaceModule: 'game_3d::full_custom_surface',
16
+ values: { tint: [1, 0.1, 0.05, 1] },
17
+ surfaceKind: 'full-custom',
18
+ declaredPasses: ['forward', 'shadow-caster'],
19
+ };
20
+
21
+ const passes = projectStandardSurfacePasses(options);
22
+
23
+ expect(passes.map((pass) => pass.name)).toEqual(['forward', 'shadow-caster']);
24
+ expect(passes.map((pass) => pass.program.module)).toEqual([
25
+ 'forgeax_material::standard',
26
+ 'forgeax_material::standard',
27
+ ]);
28
+ for (const pass of passes) {
29
+ expect(pass.program.moduleSlots?.surface).toBe(options.surfaceModule);
30
+ expect((pass.renderState?.tags as Record<string, string>).SurfaceKind).toBe('full-custom');
31
+ }
32
+ });
33
+ });
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
2
2
  import {
3
3
  type MaterialShaderManifestEntry,
4
4
  selectHdrpPbrPrewarmVariants,
5
+ selectProbePrewarmVariants,
5
6
  selectStandardPbrTransmissionPrewarmVariants,
6
7
  } from '../assembly/factory.js';
7
8
 
@@ -9,10 +10,6 @@ const falseKey =
9
10
  'CLUSTER_FORWARD_AVAILABLE=false+STORAGE_BUFFER_AVAILABLE=true+TRANSMISSION_AVAILABLE=false+VERTEX_COLOR_AVAILABLE=false';
10
11
  const trueKey =
11
12
  'CLUSTER_FORWARD_AVAILABLE=false+STORAGE_BUFFER_AVAILABLE=true+TRANSMISSION_AVAILABLE=true+VERTEX_COLOR_AVAILABLE=false';
12
- const hdrpFalseKey =
13
- 'CLUSTER_FORWARD_AVAILABLE=true+STORAGE_BUFFER_AVAILABLE=true+TRANSMISSION_AVAILABLE=false+VERTEX_COLOR_AVAILABLE=false';
14
- const hdrpTrueKey =
15
- 'CLUSTER_FORWARD_AVAILABLE=true+STORAGE_BUFFER_AVAILABLE=true+TRANSMISSION_AVAILABLE=true+VERTEX_COLOR_AVAILABLE=false';
16
13
  const probeFalseKey =
17
14
  'CLUSTER_FORWARD_AVAILABLE=false+PROBE_BLEND_AVAILABLE=true+STORAGE_BUFFER_AVAILABLE=true+TRANSMISSION_AVAILABLE=false+VERTEX_COLOR_AVAILABLE=false';
18
15
  const probeTrueKey =
@@ -30,29 +27,33 @@ function standardEntry(
30
27
  };
31
28
  }
32
29
 
33
- function transmissionVariant(definesKey: string, transmission: boolean) {
30
+ function variant(cluster: boolean, storage: boolean, projector?: boolean) {
31
+ const defines = {
32
+ CLUSTER_FORWARD_AVAILABLE: cluster,
33
+ STORAGE_BUFFER_AVAILABLE: storage,
34
+ VERTEX_COLOR_AVAILABLE: false,
35
+ ...(projector === undefined ? {} : { PROJECTOR_AVAILABLE: projector }),
36
+ };
34
37
  return {
35
- definesKey,
36
- defines: {
37
- CLUSTER_FORWARD_AVAILABLE: false,
38
- STORAGE_BUFFER_AVAILABLE: true,
39
- TRANSMISSION_AVAILABLE: transmission,
40
- VERTEX_COLOR_AVAILABLE: false,
41
- },
42
- composedWgsl: `standard-${transmission}`,
38
+ definesKey: Object.entries(defines)
39
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
40
+ .map(([name, value]) => `${name}=${value}`)
41
+ .join('+'),
42
+ defines,
43
+ composedWgsl: JSON.stringify(defines),
43
44
  };
44
45
  }
45
46
 
46
- function hdrpVariant(definesKey: string, transmission: boolean) {
47
+ function transmissionVariant(definesKey: string, transmission: boolean) {
47
48
  return {
48
49
  definesKey,
49
50
  defines: {
50
- CLUSTER_FORWARD_AVAILABLE: true,
51
+ CLUSTER_FORWARD_AVAILABLE: false,
51
52
  STORAGE_BUFFER_AVAILABLE: true,
52
53
  TRANSMISSION_AVAILABLE: transmission,
53
54
  VERTEX_COLOR_AVAILABLE: false,
54
55
  },
55
- composedWgsl: `hdrp-${transmission}`,
56
+ composedWgsl: `standard-${transmission}`,
56
57
  };
57
58
  }
58
59
 
@@ -60,103 +61,112 @@ function probeTransmissionVariant(definesKey: string, transmission: boolean) {
60
61
  const base = transmissionVariant(definesKey, transmission);
61
62
  return {
62
63
  ...base,
63
- definesKey,
64
64
  defines: { ...base.defines, PROBE_BLEND_AVAILABLE: true },
65
65
  };
66
66
  }
67
67
 
68
- function capabilityTransmissionVariant(
69
- definesKey: string,
70
- transmission: boolean,
71
- extendedLighting: boolean,
72
- ) {
73
- const base = transmissionVariant(definesKey, transmission);
74
- return {
75
- ...base,
76
- definesKey,
77
- defines: {
78
- ...base.defines,
79
- EXTENDED_LIGHTING_AVAILABLE: extendedLighting,
80
- PROJECTOR_AVAILABLE: true,
81
- },
82
- composedWgsl: `standard-${extendedLighting ? 'extended' : 'fallback'}-${transmission}`,
83
- };
84
- }
85
-
86
68
  describe('Standard transmission exact-key prewarm selection', () => {
87
- it('returns both declared variants through exact manifest lookup', () => {
69
+ it('returns both declared transmission variants', () => {
88
70
  const entry = standardEntry([
89
71
  transmissionVariant(falseKey, false),
90
72
  transmissionVariant(trueKey, true),
91
73
  ]);
92
- const selected = selectStandardPbrTransmissionPrewarmVariants(entry, true);
93
- expect(selected.map((variant) => variant.definesKey)).toEqual([falseKey, trueKey]);
74
+ expect(
75
+ selectStandardPbrTransmissionPrewarmVariants(entry, true).map((item) => item.definesKey),
76
+ ).toEqual([falseKey, trueKey]);
94
77
  });
95
78
 
96
- it('fails when the manifest does not declare an exact transmission key', () => {
79
+ it('rejects a missing exact transmission variant', () => {
97
80
  const entry = standardEntry([transmissionVariant(falseKey, false)]);
98
81
  expect(() => selectStandardPbrTransmissionPrewarmVariants(entry, true)).toThrow(
99
82
  'TRANSMISSION_AVAILABLE=true',
100
83
  );
101
84
  });
85
+ });
102
86
 
103
- it('fails when the Standard material shader manifest row is missing', () => {
104
- expect(() => selectStandardPbrTransmissionPrewarmVariants(undefined, true)).toThrow(
105
- 'manifest row is missing',
106
- );
107
- });
108
-
109
- it('keeps object-level probe variants out of the boot prewarm selection', () => {
87
+ describe('HDRP PBR capability prewarm selection', () => {
88
+ it('selects only the matching static capability/geometry state', () => {
110
89
  const entry = standardEntry([
111
- probeTransmissionVariant(probeFalseKey, false),
112
- transmissionVariant(falseKey, false),
113
- probeTransmissionVariant(probeTrueKey, true),
114
- transmissionVariant(trueKey, true),
90
+ variant(true, true),
91
+ variant(true, false),
92
+ variant(false, true),
93
+ variant(false, false),
115
94
  ]);
116
- expect(
117
- selectStandardPbrTransmissionPrewarmVariants(entry, true).map(
118
- (variant) => variant.definesKey,
119
- ),
120
- ).toEqual([falseKey, trueKey]);
95
+ expect(selectHdrpPbrPrewarmVariants(entry, true).map((item) => item.definesKey)).toEqual([
96
+ variant(true, true).definesKey,
97
+ ]);
98
+ // Cluster-forward requires the storage-backed group(2) ABI. A device
99
+ // without storage support must stay on the URP route and prewarm nothing.
100
+ expect(selectHdrpPbrPrewarmVariants(entry, false)).toEqual([]);
121
101
  });
122
102
 
123
- it('selects the prewarm pair for the admitted extended-lighting capability', () => {
124
- const fallbackFalseKey = `${falseKey}+EXTENDED_LIGHTING_AVAILABLE=false+PROJECTOR_AVAILABLE=true`;
125
- const fallbackTrueKey = `${trueKey}+EXTENDED_LIGHTING_AVAILABLE=false+PROJECTOR_AVAILABLE=true`;
126
- const extendedFalseKey = `${falseKey}+EXTENDED_LIGHTING_AVAILABLE=true+PROJECTOR_AVAILABLE=true`;
127
- const extendedTrueKey = `${trueKey}+EXTENDED_LIGHTING_AVAILABLE=true+PROJECTOR_AVAILABLE=true`;
103
+ it('respects an explicitly declared projector capability', () => {
128
104
  const entry = standardEntry([
129
- capabilityTransmissionVariant(fallbackFalseKey, false, false),
130
- capabilityTransmissionVariant(fallbackTrueKey, true, false),
131
- capabilityTransmissionVariant(extendedFalseKey, false, true),
132
- capabilityTransmissionVariant(extendedTrueKey, true, true),
105
+ variant(true, true, true),
106
+ variant(true, true, false),
107
+ variant(true, true),
133
108
  ]);
134
-
135
109
  expect(
136
- selectStandardPbrTransmissionPrewarmVariants(entry, true, true, true, false).map(
137
- (variant) => variant.definesKey,
138
- ),
139
- ).toEqual([fallbackFalseKey, fallbackTrueKey]);
140
- expect(
141
- selectStandardPbrTransmissionPrewarmVariants(entry, true, true, true, true).map(
142
- (variant) => variant.definesKey,
143
- ),
144
- ).toEqual([extendedFalseKey, extendedTrueKey]);
110
+ selectHdrpPbrPrewarmVariants(entry, true, undefined, true).map((item) => item.definesKey),
111
+ ).toEqual([variant(true, true, true).definesKey, variant(true, true).definesKey]);
145
112
  });
146
113
  });
147
114
 
148
- describe('HDRP PBR prewarm capability selection', () => {
149
- const entry = standardEntry([hdrpVariant(hdrpFalseKey, false), hdrpVariant(hdrpTrueKey, true)]);
150
-
151
- it('does not compile the transmission variant below the sampled-texture limit', () => {
152
- expect(
153
- selectHdrpPbrPrewarmVariants(entry, true, false).map((variant) => variant.definesKey),
154
- ).toEqual([hdrpFalseKey]);
115
+ describe('probe material module prewarm capability selection', () => {
116
+ it('keeps probe variants out of the fallback tier and matches device-owned axes', () => {
117
+ const entry = standardEntry([
118
+ probeTransmissionVariant(probeFalseKey, false),
119
+ probeTransmissionVariant(probeTrueKey, true),
120
+ ]);
121
+ expect(selectProbePrewarmVariants(entry, false)).toEqual([]);
122
+ expect(selectProbePrewarmVariants(entry, true).map((variant) => variant.definesKey)).toEqual([
123
+ probeFalseKey,
124
+ probeTrueKey,
125
+ ]);
155
126
  });
156
127
 
157
- it('retains both HDRP variants when transmission is supported', () => {
158
- expect(
159
- selectHdrpPbrPrewarmVariants(entry, true, true).map((variant) => variant.definesKey),
160
- ).toEqual([hdrpFalseKey, hdrpTrueKey]);
128
+ it('filters backend and sampled-texture axes but retains geometry/topology choices', () => {
129
+ const goodKey =
130
+ 'CLUSTER_FORWARD_AVAILABLE=true+EXTENDED_LIGHTING_AVAILABLE=false+PROBE_BLEND_AVAILABLE=true+PROJECTOR_AVAILABLE=false+STORAGE_BUFFER_AVAILABLE=true+TRANSMISSION_AVAILABLE=false+VERTEX_COLOR_AVAILABLE=true';
131
+ const wrongExtendedKey = goodKey.replace(
132
+ 'EXTENDED_LIGHTING_AVAILABLE=false',
133
+ 'EXTENDED_LIGHTING_AVAILABLE=true',
134
+ );
135
+ const wrongProjectorKey = goodKey.replace(
136
+ 'PROJECTOR_AVAILABLE=false',
137
+ 'PROJECTOR_AVAILABLE=true',
138
+ );
139
+ const wrongTransmissionKey = goodKey.replace(
140
+ 'TRANSMISSION_AVAILABLE=false',
141
+ 'TRANSMISSION_AVAILABLE=true',
142
+ );
143
+ const variant = (definesKey: string, defines: Record<string, boolean>) => ({
144
+ definesKey,
145
+ defines,
146
+ composedWgsl: definesKey,
147
+ });
148
+ const baseDefines = {
149
+ CLUSTER_FORWARD_AVAILABLE: true,
150
+ EXTENDED_LIGHTING_AVAILABLE: false,
151
+ PROBE_BLEND_AVAILABLE: true,
152
+ PROJECTOR_AVAILABLE: false,
153
+ STORAGE_BUFFER_AVAILABLE: true,
154
+ TRANSMISSION_AVAILABLE: false,
155
+ VERTEX_COLOR_AVAILABLE: true,
156
+ };
157
+ const selected = selectProbePrewarmVariants(
158
+ standardEntry([
159
+ variant(goodKey, baseDefines),
160
+ variant(wrongExtendedKey, { ...baseDefines, EXTENDED_LIGHTING_AVAILABLE: true }),
161
+ variant(wrongProjectorKey, { ...baseDefines, PROJECTOR_AVAILABLE: true }),
162
+ variant(wrongTransmissionKey, { ...baseDefines, TRANSMISSION_AVAILABLE: true }),
163
+ ]),
164
+ true,
165
+ false,
166
+ false,
167
+ true,
168
+ false,
169
+ );
170
+ expect(selected.map((candidate) => candidate.definesKey)).toEqual([goodKey]);
161
171
  });
162
172
  });
@@ -651,5 +651,5 @@ describe('volumetric fog budget and recovery contract', () => {
651
651
  activeLedger: steadyLedger,
652
652
  });
653
653
  await renderer.dispose();
654
- });
654
+ }, 30_000);
655
655
  });
@@ -219,7 +219,9 @@ describe('volumetric fog RenderGraph topology', () => {
219
219
  fileURLToPath(new URL('../render-system.ts', import.meta.url)),
220
220
  'utf8',
221
221
  );
222
- const recoverStart = renderSystemSource.indexOf('resetForRecover(): void {');
222
+ const recoverStart = renderSystemSource.indexOf(
223
+ 'resetForRecover(retiringPipelineState?: PipelineState, replacementDevice?: RhiDevice): void {',
224
+ );
223
225
  expect(recoverStart).toBeGreaterThanOrEqual(0);
224
226
  const recoverEnd = renderSystemSource.indexOf(
225
227
  'restorePostProcessResources(): void',
@@ -0,0 +1,9 @@
1
+ import type { ImportTransport } from '@forgeax/engine-types';
2
+
3
+ /** Build-tool inputs forwarded from the host into renderer assembly. */
4
+ export interface BundlerOptions {
5
+ readonly importTransport?: ImportTransport | undefined;
6
+ readonly shaderManifestUrl?: string | undefined;
7
+ /** Exact checkout revision emitted by the build-tool adapter. */
8
+ readonly build?: string | undefined;
9
+ }
@@ -14,6 +14,14 @@ const COMPRESSION_FEATURES: GPUFeatureName[] = [
14
14
  // request that larger limit when the adapter can provide it; adapter limits are
15
15
  // not automatically inherited by requestDevice().
16
16
  export const STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES = 17;
17
+ /**
18
+ * The complete Standard physical root can declare all optional texture slots.
19
+ * Its material group therefore needs one more sampled-texture entry than the
20
+ * extended-lighting topology alone. Request this ceiling when the adapter can
21
+ * provide it; lower-capability devices remain fail-closed at material
22
+ * pipeline admission instead of receiving an invalid WebGPU layout.
23
+ */
24
+ export const STANDARD_PHYSICAL_REQUIRED_SAMPLED_TEXTURES = 21;
17
25
 
18
26
  export interface DeviceFeatureAdmissionOptions {
19
27
  readonly gpuPassTiming?: object | undefined;
@@ -41,11 +49,13 @@ export function deriveDeviceFeatureAdmission(
41
49
  }
42
50
  const adapterSampledTextureLimit = adapter.limits?.maxSampledTexturesPerShaderStage ?? 0;
43
51
  const requiredSampledTextureLimit =
44
- adapterSampledTextureLimit >= EXTENDED_LIGHTING_REQUIRED_SAMPLED_TEXTURES
45
- ? EXTENDED_LIGHTING_REQUIRED_SAMPLED_TEXTURES
46
- : adapterSampledTextureLimit >= STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES
47
- ? STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES
48
- : undefined;
52
+ adapterSampledTextureLimit >= STANDARD_PHYSICAL_REQUIRED_SAMPLED_TEXTURES
53
+ ? STANDARD_PHYSICAL_REQUIRED_SAMPLED_TEXTURES
54
+ : adapterSampledTextureLimit >= EXTENDED_LIGHTING_REQUIRED_SAMPLED_TEXTURES
55
+ ? EXTENDED_LIGHTING_REQUIRED_SAMPLED_TEXTURES
56
+ : adapterSampledTextureLimit >= STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES
57
+ ? STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES
58
+ : undefined;
49
59
  return {
50
60
  requiredFeatures,
51
61
  ...(requiredSampledTextureLimit === undefined