@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
@@ -717,12 +717,10 @@ function recordSpriteEntityDraws(
717
717
  spritePass.setVertexBuffer(0, spriteEntry.mesh.vertexBuffer.handle);
718
718
  lastSpriteVertexBuffer = spriteEntry.mesh.vertexBuffer;
719
719
  }
720
- if (
721
- spriteEntry.mesh.indexBuffer !== null &&
722
- spriteEntry.mesh.indexBuffer !== lastSpriteIndexBuffer
723
- ) {
724
- spritePass.setIndexBuffer(spriteEntry.mesh.indexBuffer.handle, spriteEntry.mesh.indexFormat);
725
- lastSpriteIndexBuffer = spriteEntry.mesh.indexBuffer;
720
+ const indexBuffer = spriteEntry.mesh.indexBuffer;
721
+ if (indexBuffer !== null && indexBuffer !== lastSpriteIndexBuffer) {
722
+ spritePass.setIndexBuffer(indexBuffer.handle, spriteEntry.mesh.indexFormat);
723
+ lastSpriteIndexBuffer = indexBuffer;
726
724
  }
727
725
 
728
726
  // Instance buffer resolution: same cap-gate logic as the geometry
@@ -858,7 +856,7 @@ function recordSpriteEntityDraws(
858
856
  new RhiError({
859
857
  code: 'limit-exceeded',
860
858
  expected: `requestedBytes (${requestedBytes}) <= maxStorageBufferBindingSize (${cap})`,
861
- hint: 'reduce instance count to fit within device.limits.maxStorageBufferBindingSize, or split transforms across multiple Instances entries',
859
+ hint: 'reduce the sprite batch to fit within device.limits.maxStorageBufferBindingSize or use a storage-capable backend',
862
860
  detail: {
863
861
  maxStorageBufferBindingSize: cap,
864
862
  requestedBytes,
@@ -955,6 +953,8 @@ function recordSpriteEntityDraws(
955
953
 
956
954
  spritePass.setBindGroup(2, spriteGroup2 as BindGroup, [i * MESH_PER_ENTITY_STRIDE]);
957
955
 
956
+ const materialSlot = materialSlotIndices[i]?.[0] ?? 0;
957
+
958
958
  // Per-entity sprite material bind group: same standard PBR user-region
959
959
  // layout as in the geometry pass sprite branch + Skylight merged entries
960
960
  // the geometry pass sprite branch + Skylight merged entries (same
@@ -999,7 +999,6 @@ function recordSpriteEntityDraws(
999
999
  bindGroupCounts,
1000
1000
  );
1001
1001
 
1002
- const materialSlot = materialSlotIndices[i]?.[0] ?? 0;
1003
1002
  spritePass.setBindGroup(1, spritePassBg, [materialSlot * MATERIAL_PER_ENTITY_STRIDE]);
1004
1003
  spritePass.setBindGroup(3, spriteInstancesBg);
1005
1004
  spritePass.drawIndexed(spriteEntry.mesh.indexCount, spriteInstanceCount, 0, 0, 0);
@@ -16,6 +16,7 @@ import type { ReflectionProbeSelectionResult } from '../reflection/projection';
16
16
  import type { RenderRecordPhase } from '../render-contract';
17
17
  import type { MaterialSnapshot } from '../render-system-extract';
18
18
  import { resolveReflectionProbeBinding } from './frame-lighting';
19
+ import { getOpaqueResourceIdentity } from './frame-snapshot';
19
20
  import { recordGeometryDraws } from './main-pass-geometry';
20
21
  import {
21
22
  applyMaterialTextureUvScales,
@@ -42,6 +43,18 @@ export function standardReflectionProbeIndex(
42
43
 
43
44
  const ZERO_SKYLIGHT_PAYLOAD = new Float32Array([0, 0, 0, 0, 0, 0, 0, 1]);
44
45
 
46
+ function materialDiagnosticsEnabled(): boolean {
47
+ if (typeof globalThis !== 'object' || globalThis === null || !('process' in globalThis)) {
48
+ return false;
49
+ }
50
+ const processValue = (
51
+ globalThis as {
52
+ readonly process?: { readonly env?: Record<string, string | undefined> };
53
+ }
54
+ ).process;
55
+ return processValue?.env?.FORGEAX_MATERIAL_DIAGNOSTICS === '1';
56
+ }
57
+
45
58
  export type MainPassOptions = {
46
59
  readonly colorViews?: readonly (TextureView | null)[];
47
60
  readonly colorFormats?: readonly GPUTextureFormat[];
@@ -52,8 +65,6 @@ export type MainPassOptions = {
52
65
  readonly transmissionBackdropView?: TextureView | null;
53
66
  };
54
67
 
55
- const TRANSMISSION_VARIANT_SET = 'TRANSMISSION_AVAILABLE=true';
56
-
57
68
  function isTransmissionMaterial(material: MaterialSnapshot): boolean {
58
69
  return (
59
70
  material.materialShaderId === 'forgeax::default-standard-pbr' &&
@@ -284,10 +295,6 @@ export function recordMainPass(
284
295
  : {
285
296
  ...c,
286
297
  splitLdrSprite: recordMode === 'transparent' ? false : c.splitLdrSprite,
287
- validatedOrdered: c.validatedOrdered.map((entry) => ({
288
- ...entry,
289
- ...(recordMode === 'transmission' ? { variantSet: TRANSMISSION_VARIANT_SET } : {}),
290
- })),
291
298
  };
292
299
 
293
300
  if (clusteredBindGroupMissing) {
@@ -420,6 +427,64 @@ export function recordMainPass(
420
427
  brdfLutSampler: skylightFallback.sampler,
421
428
  intensityBuffer: skylightFallback.intensityBuffer,
422
429
  };
430
+ if (materialDiagnosticsEnabled()) {
431
+ const activeCache =
432
+ activeViews === undefined ? undefined : getOrCreateIblCache(runtime.deviceScope);
433
+ const prefilterViewMipCount =
434
+ activeCache?.prefilterFaceViewsByMip?.length ?? (activeViews === undefined ? 1 : 0);
435
+ frameState.iblBindingInspection = {
436
+ status: 'binding-chain-consistent',
437
+ frameId: frameState.frameNumber,
438
+ deviceGeneration: runtime.deviceScope.generation,
439
+ active: activeViews === undefined ? 'fallback' : 'active',
440
+ cache: {
441
+ identity: getOpaqueResourceIdentity((activeCache ?? skylightFallback) as object),
442
+ generation: runtime.deviceScope.generation,
443
+ prefilterMipCount: prefilterViewMipCount,
444
+ prefilterViewMipCount,
445
+ },
446
+ sampler: {
447
+ expected: {
448
+ magFilter: 'linear',
449
+ minFilter: 'linear',
450
+ mipmapFilter: 'linear',
451
+ addressModeU: 'clamp-to-edge',
452
+ addressModeV: 'clamp-to-edge',
453
+ addressModeW: 'clamp-to-edge',
454
+ },
455
+ identities: [
456
+ getOpaqueResourceIdentity(skylightResources.irradianceSampler as object),
457
+ getOpaqueResourceIdentity(skylightResources.prefilterSampler as object),
458
+ getOpaqueResourceIdentity(skylightResources.brdfLutSampler as object),
459
+ ],
460
+ },
461
+ resources: {
462
+ irradiance: {
463
+ viewIdentity: getOpaqueResourceIdentity(skylightResources.irradianceView as object),
464
+ samplerIdentity: getOpaqueResourceIdentity(
465
+ skylightResources.irradianceSampler as object,
466
+ ),
467
+ deviceGeneration: runtime.deviceScope.generation,
468
+ },
469
+ prefilter: {
470
+ viewIdentity: getOpaqueResourceIdentity(skylightResources.prefilterView as object),
471
+ samplerIdentity: getOpaqueResourceIdentity(
472
+ skylightResources.prefilterSampler as object,
473
+ ),
474
+ deviceGeneration: runtime.deviceScope.generation,
475
+ },
476
+ brdfLut: {
477
+ viewIdentity: getOpaqueResourceIdentity(skylightResources.brdfLutView as object),
478
+ samplerIdentity: getOpaqueResourceIdentity(skylightResources.brdfLutSampler as object),
479
+ deviceGeneration: runtime.deviceScope.generation,
480
+ },
481
+ intensityBufferIdentity: getOpaqueResourceIdentity(
482
+ skylightResources.intensityBuffer as object,
483
+ ),
484
+ },
485
+ errors: [],
486
+ };
487
+ }
423
488
  // Per-entity material uploads (D-P9 retained path).
424
489
  // feat-20260613 fix-issue-1 (D-8 channelMap split): the payload mirrors
425
490
  // the post-split sidecar paramSchema for default-standard-pbr (14 entries
@@ -490,6 +555,7 @@ export function recordMainPass(
490
555
  },
491
556
  materialBgShared: frameState.materialBgShared,
492
557
  materialBgAssemblyCache: c.materialBgAssemblyCache,
558
+ frameState,
493
559
  bindGroupCounts,
494
560
  ...(transmissionBackdropView === undefined ? {} : { transmissionBackdropView }),
495
561
  };
@@ -551,6 +617,28 @@ export function recordMainPass(
551
617
  mat.materialParamSchema ??
552
618
  (materialShaderId !== undefined ? runtime.getParamSchema?.(materialShaderId) : undefined);
553
619
  applyParamSnapshotToUbo(slotPayloadF32, schema, mat.paramSnapshot);
620
+ const processValue = (
621
+ globalThis as {
622
+ process?: { env?: Record<string, string | undefined> };
623
+ }
624
+ ).process;
625
+ if (
626
+ processValue?.env?.FORGEAX_MATERIAL_PIPELINE_DIAGNOSTICS === '1' &&
627
+ (materialShaderId === 'forgeax::default-standard-pbr' ||
628
+ materialShaderId === 'forgeax::pbr-skin')
629
+ ) {
630
+ console.error(
631
+ `[render-material] UBO receipt: ${JSON.stringify({
632
+ entityIndex: entry.renderableIndex,
633
+ materialHandle: mat.materialHandle,
634
+ materialShaderId,
635
+ clearcoat: slotPayloadF32[32],
636
+ clearcoatRoughness: slotPayloadF32[33],
637
+ clearcoatNormalScale: slotPayloadF32[34],
638
+ paramSnapshot: mat.paramSnapshot,
639
+ })}`,
640
+ );
641
+ }
554
642
  applyMaterialTextureUvScales(slotPayloadF32, mat, world);
555
643
  // Missing-texture detection: structural debug-pink fallback overrides
556
644
  // the baseColor/colorTint slot when a bound baseColorTexture handle
@@ -79,12 +79,18 @@ export function extractEntryResourceHandle(entry: {
79
79
  return v as object;
80
80
  }
81
81
 
82
+ // Object-only terminal marker keeps a shorter resource chain from colliding
83
+ // with a longer chain that shares the same prefix.
84
+ const BIND_GROUP_CHAIN_LEAF_KEY: object = {};
85
+
82
86
  /**
83
87
  * feat-20260622-handle-to-id-allocator-elimination M2 / w7: walks a nested
84
88
  * WeakMap chain to find or create a BindGroup leaf. Each handle in the
85
- * `handles` array is a chain node; the final leaf is a Map<string, BindGroup>
86
- * keyed by `variant` (D-2). Chain keys are always object references, never
87
- * numeric ids GC reclaims entries for dead handles automatically.
89
+ * `handles` array is a chain node; a dedicated object terminal marker points
90
+ * to the final Map<string, BindGroup> keyed by `variant` (D-2). The marker
91
+ * prevents variable-depth chains with a shared prefix from treating a nested
92
+ * WeakMap as a leaf. Chain keys are always object references, never numeric
93
+ * ids — GC reclaims entries for dead handles automatically.
88
94
  *
89
95
  * On cache hit returns the cached BindGroup. On miss calls `factory`,
90
96
  * bumps `bindGroupCounts.createBindGroup`, stores the result at the leaf,
@@ -99,9 +105,7 @@ export function getOrCreateFromChain(
99
105
  counts: { createBindGroup: number; keys: string[] },
100
106
  ): BindGroup {
101
107
  let node = root;
102
- for (let i = 0; i < handles.length - 1; i++) {
103
- // biome-ignore lint/style/noNonNullAssertion: handles length guards the index
104
- const h = handles[i]!;
108
+ for (const h of handles) {
105
109
  let next = node.get(h) as WeakMap<object, unknown> | undefined;
106
110
  if (next === undefined) {
107
111
  next = new WeakMap();
@@ -109,12 +113,10 @@ export function getOrCreateFromChain(
109
113
  }
110
114
  node = next;
111
115
  }
112
- // biome-ignore lint/style/noNonNullAssertion: known non-empty at call sites
113
- const last = handles[handles.length - 1]!;
114
- let leaf = node.get(last) as Map<string, BindGroup> | undefined;
116
+ let leaf = node.get(BIND_GROUP_CHAIN_LEAF_KEY) as Map<string, BindGroup> | undefined;
115
117
  if (leaf === undefined) {
116
118
  leaf = new Map();
117
- node.set(last, leaf);
119
+ node.set(BIND_GROUP_CHAIN_LEAF_KEY, leaf);
118
120
  }
119
121
  const hit = leaf.get(variant);
120
122
  if (hit !== undefined) return hit;
@@ -125,6 +127,26 @@ export function getOrCreateFromChain(
125
127
  return bg;
126
128
  }
127
129
 
130
+ /**
131
+ * Read a cached bind group using the same variable-depth chain contract as
132
+ * `getOrCreateFromChain`. Readers must traverse the terminal marker instead
133
+ * of assuming that the last handle directly stores the variant map.
134
+ */
135
+ export function findFromChain(
136
+ root: WeakMap<object, unknown>,
137
+ handles: readonly object[],
138
+ variant: string,
139
+ ): BindGroup | undefined {
140
+ let node = root;
141
+ for (const handle of handles) {
142
+ const next = node.get(handle);
143
+ if (next === undefined) return undefined;
144
+ node = next as WeakMap<object, unknown>;
145
+ }
146
+ const leaf = node.get(BIND_GROUP_CHAIN_LEAF_KEY) as Map<string, BindGroup> | undefined;
147
+ return leaf?.get(variant);
148
+ }
149
+
128
150
  /**
129
151
  * feat-20260622-handle-to-id-allocator-elimination M2 / w8: per-entity bind
130
152
  * group lookup-or-create helper (D-2). Two-step lookup: outer Map.get(outerKey)