@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
@@ -32,7 +32,11 @@ import type {
32
32
  TextureFormat,
33
33
  TextureView,
34
34
  } from '@forgeax/engine-rhi';
35
- import { DEFAULT_STANDARD_PBR_PARAM_SCHEMA, type ShaderCatalog } from '@forgeax/engine-shader';
35
+ import {
36
+ type ShaderCatalog,
37
+ STANDARD_PIPELINE_PARAM_SCHEMA,
38
+ standardPhysicalTextureFields,
39
+ } from '@forgeax/engine-shader';
36
40
  import { derive, type ParamSchemaEntry } from '@forgeax/engine-types';
37
41
  import { GPU_SHADER_STAGE_FRAGMENT, GPU_SHADER_STAGE_VERTEX } from './gpu-stage';
38
42
  import type { PipelineSpec } from './pipeline-spec-types';
@@ -101,11 +105,13 @@ export function standardPbrColorFormats(
101
105
  /**
102
106
  * The built-in standard-PBR material paramSchema's texture/user-region shape,
103
107
  * used as the fallback when the material BGL is built without a registry
104
- * (the caps-driven `buildPbrPipelineLayouts` seam). Declares the 8 Standard
105
- * user-region textures + a numeric UBO run; `derive()` collapses the numerics
106
- * into binding 0 and emits sampler/texture pairs at 1..6 — byte-equivalent to
107
- * the legacy fixed base-7. The shared PBR parameter contract comes from the
108
- * shader package; this module only derives the GPU layout.
108
+ * (the caps-driven `buildPbrPipelineLayouts` seam). Declares the canonical
109
+ * Standard user-region textures + a numeric UBO run; `derive()` collapses the
110
+ * numerics into binding 0 and emits one sampler/texture pair per declared
111
+ * texture. Physical map pairs stay outside this boot region and are appended
112
+ * only for an authored root that declares them. The shared PBR parameter
113
+ * contract comes from the shader package; this module only derives the GPU
114
+ * layout.
109
115
  */
110
116
  /**
111
117
  * Build the PBR material BGL **user-region** from a paramSchema via the
@@ -116,9 +122,10 @@ export function standardPbrColorFormats(
116
122
  * custom schema (e.g. parallax + heightTexture) shifts the injection region by
117
123
  * one sampler/texture pair automatically.
118
124
  *
119
- * The built-in `default-standard-pbr` paramSchema declares exactly 8 textures,
120
- * so this derives to binding 0 UBO + 8 pairs = 17 entries. Injection is appended
121
- * after that derived region.
125
+ * The canonical `STANDARD_PIPELINE_PARAM_SCHEMA` currently declares seven
126
+ * non-physical textures, so this derives to binding 0 UBO + seven pairs = 15
127
+ * entries. IBL and transmission injection are appended after that region;
128
+ * authored physical maps are appended after the engine-owned injections.
122
129
  *
123
130
  * One material-UBO convention is layered on top of the pure `derive()` output:
124
131
  * binding 0 is patched to `{ type: 'uniform', hasDynamicOffset: true }` with
@@ -132,9 +139,18 @@ export function standardPbrColorFormats(
132
139
  * built-in standard-PBR shape when omitted, for the caps-driven seam).
133
140
  */
134
141
  export function buildPbrMaterialUserRegionEntries(
135
- paramSchema: readonly ParamSchemaEntry[] = DEFAULT_STANDARD_PBR_PARAM_SCHEMA,
142
+ paramSchema: readonly ParamSchemaEntry[] = STANDARD_PIPELINE_PARAM_SCHEMA,
143
+ excludedTextureFields: readonly string[] = [],
136
144
  ): GPUBindGroupLayoutEntry[] {
137
- const derived = derive(paramSchema);
145
+ const excluded = new Set(excludedTextureFields);
146
+ for (const field of standardPhysicalTextureFields(paramSchema)) excluded.add(field);
147
+ const derived = derive(
148
+ excluded.size === 0
149
+ ? paramSchema
150
+ : paramSchema.filter(
151
+ (entry) => !excluded.has(entry.name) || !entry.type.startsWith('texture'),
152
+ ),
153
+ );
138
154
  // Project the engine-owned entry shape into the DOM WebGPU descriptor. Omit
139
155
  // absent optional members so exactOptionalPropertyTypes remains true at the
140
156
  // boundary instead of leaking an `undefined` property into the descriptor.
@@ -330,6 +346,68 @@ export const INJECTION_KIND_LENGTHS = {
330
346
  transmission: TRANSMISSION_INJECTION_LENGTH,
331
347
  };
332
348
 
349
+ export function physicalTextureFields(paramSchema: readonly ParamSchemaEntry[]): readonly string[] {
350
+ return standardPhysicalTextureFields(paramSchema);
351
+ }
352
+
353
+ export function appendTextureInjection(
354
+ bgl: readonly GPUBindGroupLayoutEntry[],
355
+ fields: readonly string[],
356
+ ): GPUBindGroupLayoutEntry[] {
357
+ const start = bgl.length;
358
+ const fieldSet = new Set(fields);
359
+ return standardPhysicalTextureFields(
360
+ fields.map((name) => ({ name, type: 'texture2d' as const })),
361
+ ).flatMap((field, index) => {
362
+ if (!fieldSet.has(field)) return [];
363
+ const binding = start + index * 2;
364
+ return [
365
+ {
366
+ binding,
367
+ visibility: GPU_SHADER_STAGE_FRAGMENT,
368
+ sampler: { type: 'filtering' as const },
369
+ },
370
+ {
371
+ binding: binding + 1,
372
+ visibility: GPU_SHADER_STAGE_FRAGMENT,
373
+ texture: { sampleType: 'float' as const, viewDimension: '2d' as const },
374
+ },
375
+ ];
376
+ });
377
+ }
378
+
379
+ /**
380
+ * Return the stable identity of the merged material bind-group layout.
381
+ *
382
+ * This is deliberately derived from the same descriptor builder consumed by
383
+ * the device path. Callers use it to keep pipeline, bind-group, and
384
+ * frame-local lookup caches on one layout identity even when equivalent
385
+ * paramSchema arrays were allocated independently.
386
+ */
387
+ export function materialBindGroupLayoutIdentity(
388
+ shaderId: string,
389
+ paramSchema: readonly ParamSchemaEntry[],
390
+ ): string {
391
+ const descriptor = buildBindGroupLayoutDescriptor(
392
+ {
393
+ shader: { id: shaderId, passKind: 'forward', variantSet: undefined },
394
+ attachments: { colorFormats: [], depthFormat: undefined, sampleCount: 1 },
395
+ geometry: { topology: 'triangle-list', vertexLayout: {} },
396
+ renderState: undefined,
397
+ },
398
+ { kind: 'pbr-material-merged', materialParamSchema: paramSchema },
399
+ );
400
+ // Descriptor shape alone cannot distinguish two Standard roots that expose
401
+ // the same number of physical texture pairs under different semantic names.
402
+ // The resource order is part of the admitted root contract, so include the
403
+ // canonical physical projection in the cache identity while keeping the
404
+ // actual WebGPU descriptor owned by the builder above.
405
+ return JSON.stringify({
406
+ entries: descriptor.entries,
407
+ physicalTextureFields: physicalTextureFields(paramSchema),
408
+ });
409
+ }
410
+
333
411
  /**
334
412
  * Caps shape consumed by the BGL factory functions for storage-buffer vs
335
413
  * uniform-buffer branching. Mirrors the two fields the engine reads from
@@ -568,7 +646,7 @@ export function buildPbrPipelineLayouts(
568
646
 
569
647
  // Material BGL: derived user region + Skylight + transmission injection.
570
648
  // The transmission start binding is computed from the post-Skylight BGL
571
- // length (= 24 for the built-in Standard schema) by appendInjection.
649
+ // length (= 22 for the canonical Standard schema) by appendInjection.
572
650
  const materialBglRes = device.createBindGroupLayout(
573
651
  buildBindGroupLayoutDescriptor(BGL_ONLY_SPEC_STUB, { kind: 'pbr-material-merged' }),
574
652
  );
@@ -658,6 +736,21 @@ export function buildPbrPipelineLayouts(
658
736
  export const SKIN_MATERIAL_SHADER_ID = 'forgeax::pbr-skin' as const;
659
737
  export const SHADOW_CASTER_SHADER_ID = 'forgeax::default-shadow-caster' as const;
660
738
 
739
+ // Authored Standard templates are published under a unique module id for each
740
+ // root contract. Keep the family marker in the id so every downstream render
741
+ // owner (extract, layout, and record) can make the same Standard-vs-custom and
742
+ // rigid-vs-skinned decision without a second registry or a runtime shader
743
+ // inspection pass.
744
+ const AUTHORED_STANDARD_ID_RE = /::(?:standard|pbr-skin)(?:-|$)/;
745
+
746
+ export function isStandardPbrSkinMaterialShader(shaderId: string | undefined): boolean {
747
+ return (
748
+ shaderId === SKIN_MATERIAL_SHADER_ID ||
749
+ shaderId === 'forgeax::default-standard-pbr-skin' ||
750
+ (shaderId !== undefined && /::pbr-skin(?:-|$)/.test(shaderId))
751
+ );
752
+ }
753
+
661
754
  export function shadowCasterVariantSet(storageBuffer: boolean, skinned: boolean): string {
662
755
  const skinningDisabled = !skinned;
663
756
  if (storageBuffer && skinningDisabled) return '';
@@ -692,6 +785,22 @@ export function resolvePipelineGroup2Contract(source: string): PipelineGroup2Con
692
785
 
693
786
  /** Returns true for the engine-shipped standard-PBR material shader family. */
694
787
  export function isStandardPbrMaterialShader(shaderId: string | undefined): boolean {
788
+ return (
789
+ shaderId === 'forgeax::default-standard-pbr' ||
790
+ shaderId === SKIN_MATERIAL_SHADER_ID ||
791
+ shaderId === 'forgeax::default-standard-pbr-skin' ||
792
+ (shaderId !== undefined && AUTHORED_STANDARD_ID_RE.test(shaderId))
793
+ );
794
+ }
795
+
796
+ /**
797
+ * Return true only for the two engine-owned Standard roots. Authored Standard
798
+ * aliases intentionally remain outside this set: their cooked root schema may
799
+ * omit transmission or other optional resources, so their compact user-region
800
+ * layout must be derived from that artifact instead of borrowing the boot
801
+ * layout's reserved slots.
802
+ */
803
+ export function isCanonicalStandardPbrMaterialShader(shaderId: string | undefined): boolean {
695
804
  return (
696
805
  shaderId === 'forgeax::default-standard-pbr' ||
697
806
  shaderId === SKIN_MATERIAL_SHADER_ID ||
@@ -1240,14 +1349,29 @@ export function buildBindGroupLayoutDescriptor(
1240
1349
  // + IBL injection (7) + transmission injection (2). The user-region
1241
1350
  // size is the only variable; injection start = userRegion.length so a
1242
1351
  // custom schema shifts IBL/transmission by its sampler/texture pairs.
1243
- // The built-in 8-texture standard-PBR schema is 17 + 7 + 2 = 26 entries.
1352
+ // The built-in standard-PBR schema is 17 + 7 + 2 = 26 entries;
1353
+ // clearcoat texture pairs are derived from the effective root schema.
1244
1354
  //
1245
1355
  // Schema source priority (D-1): explicit materialParamSchema option >
1246
1356
  // registry lookup of spec.shader.id > built-in standard-PBR fallback.
1247
1357
  const resolvedSchema = resolveMaterialParamSchema(spec, options);
1248
- const userRegion = buildPbrMaterialUserRegionEntries(resolvedSchema);
1358
+ const effectiveSchema = resolvedSchema ?? STANDARD_PIPELINE_PARAM_SCHEMA;
1359
+ const physicalFields = physicalTextureFields(effectiveSchema);
1360
+ // Standard's built-in shader keeps the transmission user-region slots
1361
+ // reserved even when a particular root omits those values. Physical
1362
+ // maps extend that canonical region; they must not compact the IBL
1363
+ // bindings to the smaller authored subset. A compact user region is
1364
+ // still correct for authored (non-engine) material shaders.
1365
+ const userRegionSchema = isCanonicalStandardPbrMaterialShader(spec.shader.id)
1366
+ ? STANDARD_PIPELINE_PARAM_SCHEMA
1367
+ : effectiveSchema;
1368
+ const userRegion = buildPbrMaterialUserRegionEntries(userRegionSchema);
1249
1369
  const afterIbl = [...userRegion, ...appendInjection(userRegion, 'ibl')];
1250
- const merged = [...afterIbl, ...appendInjection(afterIbl, 'transmission')];
1370
+ const afterTransmission = [...afterIbl, ...appendInjection(afterIbl, 'transmission')];
1371
+ const merged = [
1372
+ ...afterTransmission,
1373
+ ...appendTextureInjection(afterTransmission, physicalFields),
1374
+ ];
1251
1375
  return {
1252
1376
  label: 'pbr-material-skylight-bgl',
1253
1377
  entries: merged,
@@ -277,7 +277,13 @@ export function buildPipelineForMaterialShader(
277
277
  const vsEntry = vertexEntry ?? (passKind === 'temporal' ? 'vs_temporal' : 'vs_main');
278
278
  const fsEntry =
279
279
  fragmentEntry ??
280
- (passKind === 'temporal' ? 'fs_temporal' : passKind === 'deferred' ? 'fs_gbuffer' : 'fs_main');
280
+ (passKind === 'temporal'
281
+ ? 'fs_temporal'
282
+ : passKind === 'deferred'
283
+ ? 'fs_gbuffer'
284
+ : passKind === 'shadow-caster'
285
+ ? 'fs_shadow'
286
+ : 'fs_main');
281
287
 
282
288
  let source = entry.source;
283
289
  if (defines !== undefined && Object.keys(defines).length > 0) {
@@ -345,15 +351,13 @@ export function buildPipelineForMaterialShader(
345
351
  vertex: {
346
352
  module: shaderModule,
347
353
  entryPoint: vsEntry,
348
- buffers:
349
- ctx.layoutKind === 'pbr-skin'
350
- ? [...ctx.vertexBuffers]
351
- : [
352
- {
353
- arrayStride: 12 * 4,
354
- attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x3' as const }],
355
- },
356
- ],
354
+ // Surface-backed Standard shadow entries evaluate the same material
355
+ // surface as Forward/Deferred, so their vertex stage consumes normal,
356
+ // UV, and tangent inputs in addition to position. Keep the complete
357
+ // derived mesh layout here; extra attributes remain legal for legacy
358
+ // position-only custom casters, while trimming to position would make
359
+ // the built-in Surface shadow shader fail WebGPU validation.
360
+ buffers: [...ctx.vertexBuffers],
357
361
  },
358
362
  // Vertex-only depth pass writes `fragment: undefined` (GPU derives
359
363
  // depth from gl_Position.z). A custom ShadowCaster shader with a
@@ -19,6 +19,7 @@
19
19
 
20
20
  import {
21
21
  deriveVertexBufferLayout,
22
+ deriveVertexBufferLayoutFromProjection,
22
23
  deriveVertexLayoutProjection,
23
24
  type VertexLayoutProjection,
24
25
  } from '@forgeax/engine-geometry';
@@ -407,20 +408,11 @@ export function buildPipelineDescriptor(
407
408
  ? { shaderUvSetCount: geometry.shaderUvSetCount }
408
409
  : {}),
409
410
  })
410
- : geometry.vertexLayoutProjection.attributes.length === 0
411
- ? []
412
- : [
413
- {
414
- arrayStride: geometry.vertexLayoutProjection.arrayStride,
415
- attributes: geometry.vertexLayoutProjection.attributes.map(
416
- ({ shaderLocation, offset, format }) => ({
417
- shaderLocation,
418
- offset,
419
- format,
420
- }),
421
- ),
422
- },
423
- ];
411
+ : deriveVertexBufferLayoutFromProjection(geometry.vertexLayoutProjection, {
412
+ ...(geometry.shaderUvSetCount !== undefined
413
+ ? { shaderUvSetCount: geometry.shaderUvSetCount }
414
+ : {}),
415
+ });
424
416
 
425
417
  const descriptor: Record<string, unknown> = {
426
418
  vertex: {
@@ -154,4 +154,26 @@ describe('Points/Lines expansion cache and LKG recovery', () => {
154
154
  expect(preparation.prepare(snapshot(), mesh()).ok).toBe(true);
155
155
  expect(preparation.inspect()).toMatchObject({ status: 'resident', lastKnownGood: true });
156
156
  });
157
+
158
+ it('abandons lost-device handles without invoking the dead-device destroy path', () => {
159
+ let destroys = 0;
160
+ const preparation = new PointsLinesPreparation({
161
+ cache: new PointsLinesExpansionCache(),
162
+ adapter: {
163
+ create: () => ok('lost-device-resource'),
164
+ upload: () => ok(64),
165
+ validate: () => ok(undefined),
166
+ destroy: () => {
167
+ destroys += 1;
168
+ },
169
+ },
170
+ });
171
+
172
+ expect(preparation.prepare(snapshot(), mesh()).ok).toBe(true);
173
+ preparation.abandonForDeviceLoss();
174
+ preparation.abandonForDeviceLoss();
175
+
176
+ expect(destroys).toBe(0);
177
+ expect(preparation.inspect()).toMatchObject({ status: 'rebuild-pending', liveBytes: 0 });
178
+ });
157
179
  });
@@ -37,6 +37,8 @@ export interface PointsLinesLanePreparationAdapter<Resource> {
37
37
  mesh: MeshAsset,
38
38
  ): Result<PointsLinesPreparedResource<Resource>, PointsLinesPrepareFailedError>;
39
39
  resetForDeviceLoss(): void;
40
+ /** Drop lost-device handles without calling destroy on the dead device. */
41
+ abandonForDeviceLoss(): void;
40
42
  lastKnownGood(): PointsLinesPreparedResource<Resource> | undefined;
41
43
  inspect(): PointsLinesPreparationInspection<Resource>;
42
44
  }
@@ -121,6 +123,12 @@ export class PointsLinesPreparation<Resource> {
121
123
  this.status = 'rebuild-pending';
122
124
  }
123
125
 
126
+ abandonForDeviceLoss(): void {
127
+ this.live = undefined;
128
+ this.pendingCandidateBytes = 0;
129
+ this.status = 'rebuild-pending';
130
+ }
131
+
124
132
  lastKnownGood(): PointsLinesPreparedResource<Resource> | undefined {
125
133
  return this.live;
126
134
  }
@@ -171,6 +179,7 @@ export function createPointsLinesLanePreparationAdapter<Resource>(
171
179
  contract: createPointsLinesLaneContract(lane, backend),
172
180
  prepare: (snapshot, mesh) => preparation.prepare(snapshot, mesh),
173
181
  resetForDeviceLoss: () => preparation.resetForDeviceLoss(),
182
+ abandonForDeviceLoss: () => preparation.abandonForDeviceLoss(),
174
183
  lastKnownGood: () => preparation.lastKnownGood(),
175
184
  inspect: () => preparation.inspect(),
176
185
  };