@forgeax/engine-render 0.1.21 → 0.1.24

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 (584) hide show
  1. package/README.md +281 -38
  2. package/dist/assembly/backend-contract.d.ts +13 -0
  3. package/dist/assembly/backend-contract.d.ts.map +1 -1
  4. package/dist/assembly/bundler-contract.d.ts +9 -0
  5. package/dist/assembly/bundler-contract.d.ts.map +1 -0
  6. package/dist/assembly/device-feature-admission.d.ts +8 -0
  7. package/dist/assembly/device-feature-admission.d.ts.map +1 -1
  8. package/dist/assembly/extended-lighting-fallback.d.ts +13 -0
  9. package/dist/assembly/extended-lighting-fallback.d.ts.map +1 -0
  10. package/dist/assembly/factory.d.ts +7 -235
  11. package/dist/assembly/factory.d.ts.map +1 -1
  12. package/dist/assembly/host-contract.d.ts +10 -0
  13. package/dist/assembly/host-contract.d.ts.map +1 -1
  14. package/dist/assembly/material/assembly.d.ts +3 -2
  15. package/dist/assembly/material/assembly.d.ts.map +1 -1
  16. package/dist/assembly/material/standard-layer-projection.d.ts +10 -0
  17. package/dist/assembly/material/standard-layer-projection.d.ts.map +1 -0
  18. package/dist/assembly/material-shader-policy.d.ts +168 -0
  19. package/dist/assembly/material-shader-policy.d.ts.map +1 -0
  20. package/dist/assembly/material-shader-prewarm.d.ts +21 -0
  21. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -0
  22. package/dist/assembly/mesh-ssbo-grow.d.ts +88 -0
  23. package/dist/assembly/mesh-ssbo-grow.d.ts.map +1 -0
  24. package/dist/assembly/recovery/device-loss-fanout.d.ts +14 -0
  25. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -0
  26. package/dist/assembly/recovery/generation.d.ts +46 -0
  27. package/dist/assembly/recovery/generation.d.ts.map +1 -0
  28. package/dist/assembly/recovery/recovery-attempt.d.ts +63 -0
  29. package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -0
  30. package/dist/assembly/recovery/renderer-recover.d.ts +115 -0
  31. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -0
  32. package/dist/assembly/renderer-facade.d.ts +10 -0
  33. package/dist/assembly/renderer-facade.d.ts.map +1 -0
  34. package/dist/assembly/renderer-frame-transaction.d.ts +17 -0
  35. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  36. package/dist/assembly/renderer-helpers.d.ts +19 -0
  37. package/dist/assembly/renderer-helpers.d.ts.map +1 -0
  38. package/dist/assembly/renderer-lifecycle.d.ts +34 -0
  39. package/dist/assembly/renderer-lifecycle.d.ts.map +1 -1
  40. package/dist/assembly/shader-prewarm-policy.d.ts +15 -0
  41. package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -0
  42. package/dist/assembly/webgpu-ready-contract.d.ts +9 -0
  43. package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -0
  44. package/dist/assembly/webgpu-ready.d.ts +55 -0
  45. package/dist/assembly/webgpu-ready.d.ts.map +1 -0
  46. package/dist/assembly/webgpu-renderer.d.ts +152 -0
  47. package/dist/assembly/webgpu-renderer.d.ts.map +1 -0
  48. package/dist/authoring.mjs +1 -1
  49. package/dist/{chunk-PCHBPYML.mjs → chunk-GE3SDD72.mjs} +37 -5
  50. package/dist/chunk-GE3SDD72.mjs.map +1 -0
  51. package/dist/chunk-KZFTK6DO.mjs +1191 -0
  52. package/dist/chunk-KZFTK6DO.mjs.map +1 -0
  53. package/dist/{chunk-FS2KNUQH.mjs → chunk-Q752SHI5.mjs} +332 -568
  54. package/dist/chunk-Q752SHI5.mjs.map +1 -0
  55. package/dist/{chunk-6OPXDQ2E.mjs → chunk-RR5L4SRS.mjs} +33 -294
  56. package/dist/chunk-RR5L4SRS.mjs.map +1 -0
  57. package/dist/chunk-TFH5I7JJ.mjs +296 -0
  58. package/dist/chunk-TFH5I7JJ.mjs.map +1 -0
  59. package/dist/chunk-UQYS7FRM.mjs +1003 -0
  60. package/dist/chunk-UQYS7FRM.mjs.map +1 -0
  61. package/dist/chunk-YKB5DQV7.mjs +345 -0
  62. package/dist/chunk-YKB5DQV7.mjs.map +1 -0
  63. package/dist/{chunk-5YADUCA7.mjs → chunk-YRUYGA4A.mjs} +1189 -1217
  64. package/dist/chunk-YRUYGA4A.mjs.map +1 -0
  65. package/dist/chunk-Z3YYH2MB.mjs +5082 -0
  66. package/dist/chunk-Z3YYH2MB.mjs.map +1 -0
  67. package/dist/cluster-binner.d.ts +5 -0
  68. package/dist/cluster-binner.d.ts.map +1 -1
  69. package/dist/components/index.d.ts +3 -0
  70. package/dist/components/index.d.ts.map +1 -1
  71. package/dist/components/light-helpers.d.ts +4 -0
  72. package/dist/components/light-helpers.d.ts.map +1 -1
  73. package/dist/components/light-probe.d.ts +9 -0
  74. package/dist/components/light-probe.d.ts.map +1 -0
  75. package/dist/components/rect-area-light.d.ts +12 -0
  76. package/dist/components/rect-area-light.d.ts.map +1 -0
  77. package/dist/components/scene-instance.d.ts +4 -0
  78. package/dist/components/scene-instance.d.ts.map +1 -1
  79. package/dist/components/spot-light.d.ts +3 -0
  80. package/dist/components/spot-light.d.ts.map +1 -1
  81. package/dist/construct-renderer.mjs +27276 -23232
  82. package/dist/construct-renderer.mjs.map +1 -1
  83. package/dist/device/device-scope.d.ts +2 -0
  84. package/dist/device/device-scope.d.ts.map +1 -1
  85. package/dist/device/gpu-residency.d.ts +32 -5
  86. package/dist/device/gpu-residency.d.ts.map +1 -1
  87. package/dist/device/lifecycle-transaction.d.ts +2 -0
  88. package/dist/device/lifecycle-transaction.d.ts.map +1 -0
  89. package/dist/environment/frame.d.ts +5 -0
  90. package/dist/environment/frame.d.ts.map +1 -1
  91. package/dist/environment/inspection.d.ts +7 -0
  92. package/dist/environment/inspection.d.ts.map +1 -1
  93. package/dist/environment/lifecycle.d.ts +20 -2
  94. package/dist/environment/lifecycle.d.ts.map +1 -1
  95. package/dist/errors/gpu-driven.d.ts +26 -0
  96. package/dist/errors/gpu-driven.d.ts.map +1 -0
  97. package/dist/errors/index.d.ts +1 -0
  98. package/dist/errors/index.d.ts.map +1 -1
  99. package/dist/errors/recover.d.ts +67 -3
  100. package/dist/errors/recover.d.ts.map +1 -1
  101. package/dist/errors/render.d.ts +39 -1
  102. package/dist/errors/render.d.ts.map +1 -1
  103. package/dist/extract/contracts.d.ts +899 -0
  104. package/dist/extract/contracts.d.ts.map +1 -0
  105. package/dist/extract/gpu-driven.d.ts +50 -0
  106. package/dist/extract/gpu-driven.d.ts.map +1 -0
  107. package/dist/extract/visibility.d.ts.map +1 -1
  108. package/dist/features/host.d.ts +2 -0
  109. package/dist/features/host.d.ts.map +1 -1
  110. package/dist/features/prepared-gpu-work.d.ts +3 -0
  111. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  112. package/dist/features/prepared-graphics-store.d.ts +2 -0
  113. package/dist/features/prepared-graphics-store.d.ts.map +1 -1
  114. package/dist/gpu-driven/batch-topology.d.ts +34 -0
  115. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  116. package/dist/gpu-driven/prepared-draw.d.ts +63 -0
  117. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -0
  118. package/dist/gpu-driven/production-raster.d.ts +126 -2
  119. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  120. package/dist/gpu-driven/view-gpu.d.ts +64 -1
  121. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  122. package/dist/gpu-scene-schema.d.ts +14 -0
  123. package/dist/gpu-scene-schema.d.ts.map +1 -1
  124. package/dist/gpu-scene.d.ts +9 -1
  125. package/dist/gpu-scene.d.ts.map +1 -1
  126. package/dist/gpu-usage.d.ts +0 -1
  127. package/dist/gpu-usage.d.ts.map +1 -1
  128. package/dist/hdrp-buffers.d.ts +3 -3
  129. package/dist/hdrp-buffers.d.ts.map +1 -1
  130. package/dist/ibl/IblPipelineCache.d.ts +7 -6
  131. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  132. package/dist/ibl/skylight-bind-group.d.ts +7 -1
  133. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  134. package/dist/index.d.ts +13 -2
  135. package/dist/index.d.ts.map +1 -1
  136. package/dist/index.mjs +90 -5
  137. package/dist/index.mjs.map +1 -1
  138. package/dist/inspection-types.d.ts +163 -1
  139. package/dist/inspection-types.d.ts.map +1 -1
  140. package/dist/internal.d.ts +9 -0
  141. package/dist/internal.d.ts.map +1 -1
  142. package/dist/internal.mjs +23 -2
  143. package/dist/internal.mjs.map +1 -1
  144. package/dist/light-buffer-layout.d.ts +60 -148
  145. package/dist/light-buffer-layout.d.ts.map +1 -1
  146. package/dist/materials.d.ts +34 -10
  147. package/dist/materials.d.ts.map +1 -1
  148. package/dist/mesh-material-bindings.d.ts +56 -0
  149. package/dist/mesh-material-bindings.d.ts.map +1 -1
  150. package/dist/occlusion-proxy.d.ts +4 -0
  151. package/dist/occlusion-proxy.d.ts.map +1 -0
  152. package/dist/pbr-pipeline.d.ts +58 -21
  153. package/dist/pbr-pipeline.d.ts.map +1 -1
  154. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  155. package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -1
  156. package/dist/pipeline/standard-lighting/light-frame.d.ts +3 -3
  157. package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -1
  158. package/dist/pipeline/standard-lighting/topology.d.ts +1 -1
  159. package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -1
  160. package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -1
  161. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  162. package/dist/pipeline-builder.d.ts +2 -0
  163. package/dist/pipeline-builder.d.ts.map +1 -1
  164. package/dist/pipeline-spec.d.ts +1 -1
  165. package/dist/pipeline-spec.d.ts.map +1 -1
  166. package/dist/plugin.d.ts.map +1 -1
  167. package/dist/points-lines/prepare.d.ts +3 -0
  168. package/dist/points-lines/prepare.d.ts.map +1 -1
  169. package/dist/points-lines/standard-owner.d.ts +33 -0
  170. package/dist/points-lines/standard-owner.d.ts.map +1 -0
  171. package/dist/prepare/extended-lighting/ltc-resources.d.ts +31 -0
  172. package/dist/prepare/extended-lighting/ltc-resources.d.ts.map +1 -0
  173. package/dist/prepare/extended-lighting/resources.d.ts +72 -0
  174. package/dist/prepare/extended-lighting/resources.d.ts.map +1 -0
  175. package/dist/prepare/extended-lighting/spot-modifiers.d.ts +42 -0
  176. package/dist/prepare/extended-lighting/spot-modifiers.d.ts.map +1 -0
  177. package/dist/prepare/extended-lighting/state.d.ts +24 -0
  178. package/dist/prepare/extended-lighting/state.d.ts.map +1 -0
  179. package/dist/record/frame-lighting.d.ts +8 -6
  180. package/dist/record/frame-lighting.d.ts.map +1 -1
  181. package/dist/record/frame-snapshot.d.ts +55 -10
  182. package/dist/record/frame-snapshot.d.ts.map +1 -1
  183. package/dist/record/frame-targets.d.ts.map +1 -1
  184. package/dist/record/frame.d.ts +21 -5
  185. package/dist/record/frame.d.ts.map +1 -1
  186. package/dist/record/gpu-timing.d.ts +19 -7
  187. package/dist/record/gpu-timing.d.ts.map +1 -1
  188. package/dist/record/helpers.d.ts +0 -20
  189. package/dist/record/helpers.d.ts.map +1 -1
  190. package/dist/record/main-pass-geometry.d.ts +20 -2
  191. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  192. package/dist/record/main-pass-material.d.ts +10 -18
  193. package/dist/record/main-pass-material.d.ts.map +1 -1
  194. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  195. package/dist/record/main-pass.d.ts.map +1 -1
  196. package/dist/record/mesh-ssbo.d.ts +11 -3
  197. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  198. package/dist/record/recovery-pipeline.d.ts +30 -0
  199. package/dist/record/recovery-pipeline.d.ts.map +1 -0
  200. package/dist/record/render-context.d.ts +82 -9
  201. package/dist/record/render-context.d.ts.map +1 -1
  202. package/dist/record/shadow-pass.d.ts.map +1 -1
  203. package/dist/record/typed-frame-graph.d.ts +49 -2
  204. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  205. package/dist/record/view-ubo.d.ts +1 -2
  206. package/dist/record/view-ubo.d.ts.map +1 -1
  207. package/dist/recovery/render-system-candidate.d.ts +65 -0
  208. package/dist/recovery/render-system-candidate.d.ts.map +1 -0
  209. package/dist/reflection/inspection.d.ts +10 -0
  210. package/dist/reflection/inspection.d.ts.map +1 -0
  211. package/dist/reflection/projection.d.ts +49 -0
  212. package/dist/reflection/projection.d.ts.map +1 -1
  213. package/dist/reflection/record-owner.d.ts +60 -0
  214. package/dist/reflection/record-owner.d.ts.map +1 -0
  215. package/dist/render-contract.d.ts +78 -15
  216. package/dist/render-contract.d.ts.map +1 -1
  217. package/dist/render-pipeline.d.ts +7 -0
  218. package/dist/render-pipeline.d.ts.map +1 -1
  219. package/dist/render-system-extract-tail.d.ts +12 -0
  220. package/dist/render-system-extract-tail.d.ts.map +1 -0
  221. package/dist/render-system-extract.d.ts +252 -30
  222. package/dist/render-system-extract.d.ts.map +1 -1
  223. package/dist/render-system.d.ts +140 -5
  224. package/dist/render-system.d.ts.map +1 -1
  225. package/dist/scene/probe-blend-record.d.ts +22 -0
  226. package/dist/scene/probe-blend-record.d.ts.map +1 -0
  227. package/dist/scene/probe-blend.d.ts +142 -0
  228. package/dist/scene/probe-blend.d.ts.map +1 -0
  229. package/dist/scene/render-scene-types.d.ts +0 -6
  230. package/dist/scene/render-scene-types.d.ts.map +1 -1
  231. package/dist/scene/render-scene.d.ts +122 -9
  232. package/dist/scene/render-scene.d.ts.map +1 -1
  233. package/dist/scene/visibility/bounds.d.ts +20 -0
  234. package/dist/scene/visibility/bounds.d.ts.map +1 -0
  235. package/dist/scene/visibility/budget.d.ts +16 -0
  236. package/dist/scene/visibility/budget.d.ts.map +1 -0
  237. package/dist/scene/visibility/facet.d.ts +73 -0
  238. package/dist/scene/visibility/facet.d.ts.map +1 -0
  239. package/dist/scene/visibility/gpu-frame-timing.d.ts +41 -0
  240. package/dist/scene/visibility/gpu-frame-timing.d.ts.map +1 -0
  241. package/dist/scene/visibility/gpu-lod.d.ts +46 -0
  242. package/dist/scene/visibility/gpu-lod.d.ts.map +1 -0
  243. package/dist/scene/visibility/inspection.d.ts +132 -0
  244. package/dist/scene/visibility/inspection.d.ts.map +1 -0
  245. package/dist/scene/visibility/lod-selector.d.ts +25 -0
  246. package/dist/scene/visibility/lod-selector.d.ts.map +1 -0
  247. package/dist/scene/visibility/occlusion-confidence.d.ts +81 -0
  248. package/dist/scene/visibility/occlusion-confidence.d.ts.map +1 -0
  249. package/dist/scene/visibility/occlusion-pass.d.ts +57 -0
  250. package/dist/scene/visibility/occlusion-pass.d.ts.map +1 -0
  251. package/dist/scene/visibility/occlusion-query-pool.d.ts +57 -0
  252. package/dist/scene/visibility/occlusion-query-pool.d.ts.map +1 -0
  253. package/dist/scene/visibility/occlusion-runtime.d.ts +96 -0
  254. package/dist/scene/visibility/occlusion-runtime.d.ts.map +1 -0
  255. package/dist/scene/visibility/types.d.ts +34 -0
  256. package/dist/scene/visibility/types.d.ts.map +1 -0
  257. package/dist/ssr/admission.d.ts +122 -0
  258. package/dist/ssr/admission.d.ts.map +1 -0
  259. package/dist/ssr/composition.d.ts +32 -0
  260. package/dist/ssr/composition.d.ts.map +1 -0
  261. package/dist/ssr/errors.d.ts +39 -0
  262. package/dist/ssr/errors.d.ts.map +1 -0
  263. package/dist/ssr/identity.d.ts +13 -0
  264. package/dist/ssr/identity.d.ts.map +1 -0
  265. package/dist/systems/skin-palette-allocator.d.ts +1 -1
  266. package/dist/temporal/frame-coordinator.d.ts +13 -0
  267. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  268. package/dist/temporal/frame.d.ts +2 -0
  269. package/dist/temporal/frame.d.ts.map +1 -1
  270. package/dist/temporal/index.mjs +6 -5
  271. package/dist/temporal/taa-history-store.d.ts +15 -1
  272. package/dist/temporal/taa-history-store.d.ts.map +1 -1
  273. package/dist/temporal/transaction.d.ts +5 -0
  274. package/dist/temporal/transaction.d.ts.map +1 -1
  275. package/dist/temporal/view.d.ts +7 -0
  276. package/dist/temporal/view.d.ts.map +1 -1
  277. package/dist/tilemap-chunk-extract-system.d.ts +1 -1
  278. package/dist/typed-render-graph-primitives.d.ts +4 -0
  279. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  280. package/dist/volume/passes.d.ts +8 -4
  281. package/dist/volume/passes.d.ts.map +1 -1
  282. package/package.json +20 -20
  283. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +5 -0
  284. package/src/__tests__/detached-inspection.integration.test.ts +6 -0
  285. package/src/__tests__/device-feature-admission.unit.test.ts +19 -1
  286. package/src/__tests__/device-loss-fanout.unit.test.ts +107 -0
  287. package/src/__tests__/device-scope-lifecycle.unit.test.ts +21 -0
  288. package/src/__tests__/direct-light-snapshot.unit.test.ts +59 -3
  289. package/src/__tests__/environment-generation.integration.test.ts +39 -0
  290. package/src/__tests__/evidence-identity.ts +13 -0
  291. package/src/__tests__/evidence-identity.unit.test.ts +24 -0
  292. package/src/__tests__/expose-renderer-errors.unit.test.ts +34 -0
  293. package/src/__tests__/extended-lighting-recovery.browser.test.ts +14 -0
  294. package/src/__tests__/extended-lighting-recovery.dawn.test.ts +14 -0
  295. package/src/__tests__/extended-lighting-recovery.integration.test.ts +81 -0
  296. package/src/__tests__/extended-lighting-resources.unit.test.ts +51 -0
  297. package/src/__tests__/extended-lighting-zero-feature.integration.test.ts +52 -0
  298. package/src/__tests__/factory-contract.integration.test.ts +154 -6
  299. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +4 -1
  300. package/src/__tests__/frame-observation.contract.test.ts +15 -0
  301. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +146 -0
  302. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +1 -0
  303. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +1 -0
  304. package/src/__tests__/gpu-driven-production.integration.test.ts +572 -9
  305. package/src/__tests__/gpu-driven-scaling.unit.test.ts +66 -2
  306. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +26 -1
  307. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +89 -2
  308. package/src/__tests__/gpu-driven-view.browser.test.ts +3 -3
  309. package/src/__tests__/gpu-driven-view.dawn.test.ts +3 -3
  310. package/src/__tests__/gpu-driven-view.unit.test.ts +50 -0
  311. package/src/__tests__/gpu-lod-evidence.ts +188 -0
  312. package/src/__tests__/gpu-lod-fallback.integration.test.ts +10 -0
  313. package/src/__tests__/gpu-lod-indirect.integration.test.ts +40 -0
  314. package/src/__tests__/gpu-lod-parity.dawn.test.ts +29 -0
  315. package/src/__tests__/gpu-lod-readiness.integration.test.ts +52 -0
  316. package/src/__tests__/gpu-lod-schema.unit.test.ts +66 -0
  317. package/src/__tests__/gpu-lod-selector.unit.test.ts +28 -0
  318. package/src/__tests__/gpu-lod-silhouette.browser.test.ts +13 -0
  319. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +66 -1
  320. package/src/__tests__/gpu-scene.dawn.test.ts +8 -1
  321. package/src/__tests__/gpu-scene.unit.test.ts +49 -3
  322. package/src/__tests__/ibl/face-uniforms.unit.test.ts +2 -0
  323. package/src/__tests__/ibl-residency-publication.unit.test.ts +113 -0
  324. package/src/__tests__/light-buffer-layout.unit.test.ts +113 -3
  325. package/src/__tests__/light-inspection.unit.test.ts +55 -0
  326. package/src/__tests__/light-resource-error.unit.test.ts +43 -0
  327. package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +109 -0
  328. package/src/__tests__/lod-bounds.unit.test.ts +42 -0
  329. package/src/__tests__/lod-cpu-lane.integration.test.ts +55 -0
  330. package/src/__tests__/lod-cpu-no-change.perf.test.ts +19 -0
  331. package/src/__tests__/lod-gpu-frame-timing.unit.test.ts +49 -0
  332. package/src/__tests__/lod-multi-view.integration.test.ts +63 -0
  333. package/src/__tests__/lod-occlusion-device-loss.integration.test.ts +27 -0
  334. package/src/__tests__/lod-occlusion-final-decision.integration.test.ts +47 -0
  335. package/src/__tests__/lod-occlusion-inspection.integration.test.ts +217 -0
  336. package/src/__tests__/lod-occlusion-inspection.schema.test-d.ts +14 -0
  337. package/src/__tests__/lod-occlusion-inspection.unit.test.ts +72 -0
  338. package/src/__tests__/lod-occlusion-visual-evidence.ts +91 -0
  339. package/src/__tests__/lod-occlusion.browser.test.ts +15 -0
  340. package/src/__tests__/lod-occlusion.dawn.test.ts +20 -0
  341. package/src/__tests__/lod-selector.profile.test.ts +35 -0
  342. package/src/__tests__/lod-selector.unit.test.ts +68 -0
  343. package/src/__tests__/lod-visibility-identity.unit.test.ts +198 -0
  344. package/src/__tests__/lod-visibility-lifecycle.integration.test.ts +132 -0
  345. package/src/__tests__/ltc-resources.unit.test.ts +32 -0
  346. package/src/__tests__/material-contract-inventory.unit.test.ts +41 -9
  347. package/src/__tests__/material-cooked-projection.unit.test.ts +19 -1
  348. package/src/__tests__/material-static-dynamic-route.unit.test.ts +29 -0
  349. package/src/__tests__/material-stride-surface.unit.test.ts +7 -3
  350. package/src/__tests__/materials-standard-contract.unit.test.ts +104 -0
  351. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
  352. package/src/__tests__/occlusion-confidence.unit.test.ts +168 -0
  353. package/src/__tests__/occlusion-invalidation.integration.test.ts +37 -0
  354. package/src/__tests__/occlusion-pass-routing.unit.test.ts +16 -0
  355. package/src/__tests__/occlusion-pass.dawn.test.ts +20 -0
  356. package/src/__tests__/occlusion-pass.integration.test.ts +43 -0
  357. package/src/__tests__/occlusion-pass.rhi-null.test.ts +22 -0
  358. package/src/__tests__/occlusion-query-pool.rhi-null.test.ts +21 -0
  359. package/src/__tests__/occlusion-query-pool.unit.test.ts +95 -0
  360. package/src/__tests__/occlusion-query-ticket.integration.test.ts +47 -0
  361. package/src/__tests__/occlusion-query.dawn.test.ts +16 -0
  362. package/src/__tests__/occlusion-query.evidence.ts +178 -0
  363. package/src/__tests__/occlusion-query.rhi-null.test.ts +31 -0
  364. package/src/__tests__/occlusion-runtime.unit.test.ts +120 -0
  365. package/src/__tests__/physical-clearcoat.integration.test.ts +89 -0
  366. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +196 -0
  367. package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +1 -1
  368. package/src/__tests__/probe-bindings.unit.test.ts +200 -0
  369. package/src/__tests__/probe-blend-numeric.unit.test.ts +67 -0
  370. package/src/__tests__/probe-blend-oracle.unit.test.ts +136 -0
  371. package/src/__tests__/probe-capacity.integration.test.ts +97 -0
  372. package/src/__tests__/probe-recovery.integration.test.ts +104 -0
  373. package/src/__tests__/probe-scene-dirty.integration.test.ts +211 -0
  374. package/src/__tests__/profiler-phase-catalog.test.ts +4 -1
  375. package/src/__tests__/profiler-phase-token.test.ts +1 -0
  376. package/src/__tests__/recover-error-policy-owner.unit.test.ts +6 -3
  377. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +140 -0
  378. package/src/__tests__/rect-area-boundary.unit.test.ts +24 -0
  379. package/src/__tests__/reflection-fallback-graph.unit.test.ts +67 -0
  380. package/src/__tests__/reflection-fallback-inspection.unit.test.ts +77 -0
  381. package/src/__tests__/reflection-fallback-lifecycle.integration.test.ts +21 -0
  382. package/src/__tests__/reflection-fallback-projection.unit.test.ts +90 -0
  383. package/src/__tests__/reflection-fallback-source.unit.test.ts +32 -0
  384. package/src/__tests__/reflection-probe-owner-placement.unit.test.ts +39 -0
  385. package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +120 -0
  386. package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
  387. package/src/__tests__/render-feature-docs.unit.test.ts +12 -7
  388. package/src/__tests__/render-graph-fallback.integration.test.ts +24 -0
  389. package/src/__tests__/render-perf/extended-lighting.bench.test.ts +66 -0
  390. package/src/__tests__/render-scene-projection.unit.test.ts +447 -18
  391. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +160 -17
  392. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +47 -0
  393. package/src/__tests__/renderer-generation-assembly.unit.test.ts +77 -0
  394. package/src/__tests__/renderer-generation-failure.unit.test.ts +108 -0
  395. package/src/__tests__/renderer-producer-root-matrix.integration.test.ts +33 -0
  396. package/src/__tests__/renderer-recovery-capability-matrix.unit.test.ts +59 -0
  397. package/src/__tests__/renderer-recovery-contract.unit.test.ts +73 -0
  398. package/src/__tests__/renderer-recovery-lifecycle.unit.test.ts +216 -0
  399. package/src/__tests__/renderer-target-history-recovery.integration.test.ts +80 -0
  400. package/src/__tests__/shader-manifest-fixture.ts +13 -16
  401. package/src/__tests__/shader-stage-surface.unit.test.ts +1 -1
  402. package/src/__tests__/skin-motion-regression.unit.test.ts +22 -0
  403. package/src/__tests__/skinned-shadow-caster.test.ts +2 -2
  404. package/src/__tests__/spot-modifier-projection.unit.test.ts +70 -0
  405. package/src/__tests__/ssr-admission-matrix.integration.test.ts +206 -0
  406. package/src/__tests__/ssr-composition.unit.test.ts +46 -0
  407. package/src/__tests__/ssr-fallback-zero-work.bench.test.ts +94 -0
  408. package/src/__tests__/ssr-recovery-errors.unit.test.ts +58 -0
  409. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +67 -0
  410. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +7 -0
  411. package/src/__tests__/standard-layer-projection.unit.test.ts +23 -0
  412. package/src/__tests__/standard-lighting-inspection.integration.test.ts +1 -1
  413. package/src/__tests__/standard-lighting-write.unit.test.ts +66 -2
  414. package/src/__tests__/temporal-fallback-generation.integration.test.ts +16 -0
  415. package/src/__tests__/temporal-view.unit.test.ts +3 -3
  416. package/src/__tests__/transmission-prewarm.unit.test.ts +111 -32
  417. package/src/__tests__/visibility-budget.integration.test.ts +140 -0
  418. package/src/__tests__/visibility-budget.unit.test.ts +70 -0
  419. package/src/__tests__/visibility-extract.integration.test.ts +11 -6
  420. package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +6 -3
  421. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +31 -28
  422. package/src/__tests__/volumetric-fog-topology.unit.test.ts +3 -1
  423. package/src/assembly/backend-contract.ts +13 -0
  424. package/src/assembly/bundler-contract.ts +9 -0
  425. package/src/assembly/device-feature-admission.ts +26 -9
  426. package/src/assembly/extended-lighting-fallback.ts +216 -0
  427. package/src/assembly/factory.ts +81 -9634
  428. package/src/assembly/host-contract.ts +10 -0
  429. package/src/assembly/material/assembly.ts +18 -2
  430. package/src/assembly/material/standard-layer-projection.ts +16 -0
  431. package/src/assembly/material-shader-policy.ts +854 -0
  432. package/src/assembly/material-shader-prewarm.ts +88 -0
  433. package/src/assembly/mesh-ssbo-grow.ts +270 -0
  434. package/src/assembly/recovery/device-loss-fanout.ts +89 -0
  435. package/src/assembly/recovery/generation.ts +141 -0
  436. package/src/assembly/recovery/recovery-attempt.ts +800 -0
  437. package/src/assembly/recovery/renderer-recover.ts +906 -0
  438. package/src/assembly/renderer-facade.ts +342 -0
  439. package/src/assembly/renderer-frame-transaction.ts +45 -0
  440. package/src/assembly/renderer-helpers.ts +99 -0
  441. package/src/assembly/renderer-lifecycle.ts +159 -0
  442. package/src/assembly/shader-prewarm-policy.ts +157 -0
  443. package/src/assembly/webgpu-ready-contract.ts +14 -0
  444. package/src/assembly/webgpu-ready.ts +4079 -0
  445. package/src/assembly/webgpu-renderer.ts +4095 -0
  446. package/src/cluster-binner.ts +31 -0
  447. package/src/components/__tests__/lighting-authoring.unit.test.ts +39 -0
  448. package/src/components/__tests__/rect-area-light.unit.test.ts +33 -0
  449. package/src/components/camera.ts +1 -1
  450. package/src/components/index.ts +3 -0
  451. package/src/components/light-helpers.ts +52 -1
  452. package/src/components/light-probe.ts +13 -0
  453. package/src/components/rect-area-light.ts +15 -0
  454. package/src/components/scene-instance.ts +6 -0
  455. package/src/components/spot-light.ts +3 -0
  456. package/src/debug-draw-glue.ts +1 -1
  457. package/src/device/__tests__/extended-lighting-recovery.unit.test.ts +24 -0
  458. package/src/device/device-scope.ts +35 -6
  459. package/src/device/gpu-residency.ts +334 -35
  460. package/src/device/lifecycle-transaction.ts +12 -0
  461. package/src/environment/frame.ts +6 -0
  462. package/src/environment/inspection.ts +10 -0
  463. package/src/environment/lifecycle.ts +122 -9
  464. package/src/errors/gpu-driven.ts +92 -0
  465. package/src/errors/index.ts +1 -0
  466. package/src/errors/recover.ts +108 -5
  467. package/src/errors/render.ts +72 -1
  468. package/src/extract/camera.ts +3 -3
  469. package/src/extract/contracts.ts +974 -0
  470. package/src/extract/gpu-driven.ts +108 -0
  471. package/src/extract/visibility.ts +0 -23
  472. package/src/features/host.ts +16 -0
  473. package/src/features/prepared-gpu-work.ts +20 -2
  474. package/src/features/prepared-graphics-store.ts +17 -0
  475. package/src/gpu-driven/batch-topology.ts +206 -28
  476. package/src/gpu-driven/prepared-draw.ts +208 -0
  477. package/src/gpu-driven/production-raster.ts +1046 -39
  478. package/src/gpu-driven/view-gpu.ts +411 -29
  479. package/src/gpu-scene-schema.ts +13 -0
  480. package/src/gpu-scene.ts +137 -67
  481. package/src/gpu-usage.ts +0 -1
  482. package/src/hdrp-buffers.ts +6 -6
  483. package/src/ibl/IblPipelineCache.ts +134 -50
  484. package/src/ibl/skylight-bind-group.ts +26 -0
  485. package/src/index.ts +83 -1
  486. package/src/inspection-types.ts +220 -1
  487. package/src/internal.ts +38 -0
  488. package/src/light-buffer-layout.ts +157 -324
  489. package/src/materials.ts +278 -47
  490. package/src/mesh-material-bindings.ts +59 -0
  491. package/src/occlusion-proxy.ts +64 -0
  492. package/src/pbr-pipeline.ts +290 -60
  493. package/src/pipeline/standard-forward-lane.ts +47 -0
  494. package/src/pipeline/standard-lighting/graph.ts +2 -1
  495. package/src/pipeline/standard-lighting/light-frame.ts +3 -2
  496. package/src/pipeline/standard-lighting/topology.ts +1 -1
  497. package/src/pipeline/standard-lighting/transport.ts +2 -1
  498. package/src/pipeline/standard-pipeline.ts +2 -0
  499. package/src/pipeline-builder.ts +5 -2
  500. package/src/pipeline-spec.ts +12 -1
  501. package/src/plugin.ts +4 -0
  502. package/src/points-lines/__tests__/cache-recovery.unit.test.ts +22 -0
  503. package/src/points-lines/prepare.ts +9 -0
  504. package/src/points-lines/standard-owner.ts +403 -0
  505. package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +56 -0
  506. package/src/prepare/extended-lighting/ltc-resources.ts +136 -0
  507. package/src/prepare/extended-lighting/resources.ts +389 -0
  508. package/src/prepare/extended-lighting/spot-modifiers.ts +194 -0
  509. package/src/prepare/extended-lighting/state.ts +92 -0
  510. package/src/record/__tests__/frame-targets-recovery.unit.test.ts +60 -0
  511. package/src/record/__tests__/gpu-timing.unit.test.ts +177 -0
  512. package/src/record/__tests__/main-pass-geometry.unit.test.ts +26 -1
  513. package/src/record/__tests__/mesh-ssbo.unit.test.ts +38 -0
  514. package/src/record/__tests__/recovery-pipeline.unit.test.ts +124 -0
  515. package/src/record/__tests__/sprite-material-abi.unit.test.ts +28 -13
  516. package/src/record/__tests__/standard-pbr-ubo-layout.unit.test.ts +47 -4
  517. package/src/record/__tests__/temporal-payload.unit.test.ts +29 -28
  518. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +21 -2
  519. package/src/record/frame-lighting.ts +252 -53
  520. package/src/record/frame-snapshot.ts +72 -12
  521. package/src/record/frame-targets.ts +11 -1
  522. package/src/record/frame.ts +416 -95
  523. package/src/record/gpu-timing.ts +108 -44
  524. package/src/record/helpers.ts +1 -59
  525. package/src/record/main-pass-geometry.ts +434 -44
  526. package/src/record/main-pass-material.ts +324 -210
  527. package/src/record/main-pass-sprite-draws.ts +58 -92
  528. package/src/record/main-pass.ts +94 -6
  529. package/src/record/mesh-ssbo.ts +79 -46
  530. package/src/record/recovery-pipeline.ts +695 -0
  531. package/src/record/render-context.ts +99 -7
  532. package/src/record/shadow-pass.ts +66 -40
  533. package/src/record/typed-frame-graph.ts +270 -84
  534. package/src/record/view-ubo.ts +17 -27
  535. package/src/recovery/render-system-candidate.ts +839 -0
  536. package/src/reflection/inspection.ts +67 -0
  537. package/src/reflection/projection.ts +196 -1
  538. package/src/reflection/record-owner.ts +1283 -0
  539. package/src/render-contract.ts +82 -12
  540. package/src/render-pipeline.ts +5 -0
  541. package/src/render-system-extract-tail.ts +2983 -0
  542. package/src/render-system-extract.ts +290 -2784
  543. package/src/render-system.ts +1597 -1192
  544. package/src/scene/__tests__/changed-block-summary.candidate.test.ts +36 -0
  545. package/src/scene/probe-blend-record.ts +54 -0
  546. package/src/scene/probe-blend.ts +813 -0
  547. package/src/scene/render-scene-types.ts +0 -7
  548. package/src/scene/render-scene.ts +972 -184
  549. package/src/scene/visibility/bounds.ts +57 -0
  550. package/src/scene/visibility/budget.ts +40 -0
  551. package/src/scene/visibility/facet.ts +400 -0
  552. package/src/scene/visibility/gpu-frame-timing.ts +101 -0
  553. package/src/scene/visibility/gpu-lod.ts +119 -0
  554. package/src/scene/visibility/inspection.ts +313 -0
  555. package/src/scene/visibility/lod-selector.ts +84 -0
  556. package/src/scene/visibility/occlusion-confidence.ts +478 -0
  557. package/src/scene/visibility/occlusion-pass.ts +165 -0
  558. package/src/scene/visibility/occlusion-query-pool.ts +178 -0
  559. package/src/scene/visibility/occlusion-runtime.ts +672 -0
  560. package/src/scene/visibility/types.ts +50 -0
  561. package/src/ssr/admission.ts +451 -0
  562. package/src/ssr/composition.ts +73 -0
  563. package/src/ssr/errors.ts +76 -0
  564. package/src/ssr/identity.ts +12 -0
  565. package/src/systems/skin-palette-allocator.ts +1 -1
  566. package/src/temporal/frame-coordinator.ts +30 -1
  567. package/src/temporal/frame.ts +14 -1
  568. package/src/temporal/taa-history-store.ts +46 -2
  569. package/src/temporal/temporal-view.ts +1 -1
  570. package/src/temporal/transaction.ts +9 -0
  571. package/src/temporal/view.ts +11 -0
  572. package/src/tilemap-chunk-extract-system.ts +5 -5
  573. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +19 -12
  574. package/src/typed-render-graph-primitives.ts +48 -0
  575. package/src/volume/passes.ts +46 -49
  576. package/dist/.tsbuildinfo +0 -1
  577. package/dist/chunk-5YADUCA7.mjs.map +0 -1
  578. package/dist/chunk-6OPXDQ2E.mjs.map +0 -1
  579. package/dist/chunk-BXBQQKFQ.mjs +0 -67
  580. package/dist/chunk-BXBQQKFQ.mjs.map +0 -1
  581. package/dist/chunk-FS2KNUQH.mjs.map +0 -1
  582. package/dist/chunk-PCHBPYML.mjs.map +0 -1
  583. package/dist/chunk-VFPLTKCU.mjs +0 -192
  584. package/dist/chunk-VFPLTKCU.mjs.map +0 -1
@@ -10,7 +10,7 @@ import {
10
10
  RhiError,
11
11
  type RhiRenderPassEncoder,
12
12
  } from '@forgeax/engine-rhi';
13
- import type { PassKind, PrimitiveTopology } from '@forgeax/engine-types';
13
+ import type { ParamSchemaEntry, PassKind, PrimitiveTopology } from '@forgeax/engine-types';
14
14
  import { GpuBuffer } from '../gpu-resource';
15
15
  import {
16
16
  GPU_BUFFER_USAGE_COPY_DST,
@@ -22,7 +22,12 @@ import type { InstanceBufferCacheEntry } from '../instance-buffer-cache';
22
22
  import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from '../materials';
23
23
  import {
24
24
  createPbrSkinMeshBindGroupEntries,
25
+ isCanonicalStandardPbrMaterialShader,
26
+ isStandardPbrMaterialShader,
27
+ isStandardPbrSkinMaterialShader,
28
+ materialBindGroupLayoutIdentity,
25
29
  pbrSkinMeshDynamicOffsets,
30
+ physicalTextureFields,
26
31
  SKIN_MATERIAL_SHADER_ID,
27
32
  } from '../pbr-pipeline';
28
33
  import {
@@ -35,7 +40,12 @@ import type { PointsLinesRecordPlan } from '../points-lines/record';
35
40
  import { POINTS_LINES_MATERIAL_SHADER_ID } from '../points-lines/record';
36
41
  import type { RenderRecordPhase } from '../render-contract';
37
42
  import type { MaterialSnapshot } from '../render-system-extract';
38
- import { worldEntityKey } from './frame-snapshot';
43
+ import {
44
+ PROBE_BLEND_RECORD_BYTE_SIZE,
45
+ PROBE_BLEND_RECORD_STRIDE,
46
+ probeBlendRecordOffset,
47
+ } from '../scene/probe-blend-record';
48
+ import { getOpaqueResourceIdentity, worldEntityKey } from './frame-snapshot';
39
49
  import {
40
50
  geometryRenderStateForTopology,
41
51
  isEntityFullyTransparent,
@@ -58,14 +68,29 @@ import { POINTS_LINES_VIEW_SLOT_STRIDE, writePointsLinesViewUbo } from './view-u
58
68
  type GeometryInstanceDraw = {
59
69
  readonly instanceBuffer: Buffer;
60
70
  readonly instanceBindGroup: BindGroup;
71
+ /** -1 means the no-probe layout and therefore no dynamic offset. */
72
+ readonly probeOffset: number;
61
73
  readonly instanceCount: number;
62
74
  };
63
75
 
64
76
  type GeometryBindingState = {
65
77
  pipeline: RenderPipeline | null;
66
78
  instancesBuffer: Buffer | null;
79
+ probeOffset: number;
67
80
  };
68
81
 
82
+ function materialDiagnosticsEnabled(): boolean {
83
+ if (typeof globalThis !== 'object' || globalThis === null || !('process' in globalThis)) {
84
+ return false;
85
+ }
86
+ const processValue = (
87
+ globalThis as {
88
+ readonly process?: { readonly env?: Record<string, string | undefined> };
89
+ }
90
+ ).process;
91
+ return processValue?.env?.FORGEAX_MATERIAL_DIAGNOSTICS === '1';
92
+ }
93
+
69
94
  type MaterialPipelineLookup = {
70
95
  readonly materialShaderId: string;
71
96
  readonly isHdrTarget: boolean;
@@ -77,6 +102,7 @@ type MaterialPipelineLookup = {
77
102
  readonly layoutProjection: import('@forgeax/engine-geometry').VertexLayoutProjection;
78
103
  readonly sampleCount: number;
79
104
  readonly colorFormatOverride: GPUTextureFormat | undefined;
105
+ readonly additionalColorFormats: readonly GPUTextureFormat[] | undefined;
80
106
  readonly handle: MaterialShaderPipelineEntry | null;
81
107
  };
82
108
 
@@ -84,6 +110,121 @@ type GeometryRecordProfileSegment = 'material-bind-groups' | 'pipeline-selection
84
110
 
85
111
  type GeometryRenderState = ReturnType<typeof geometryRenderStateForTopology>;
86
112
 
113
+ /**
114
+ * Select the transmission shader axis from authored material data, not the
115
+ * canonical boot schema. The latter reserves transmission fields for the
116
+ * shared Standard ABI even when a material remains base-only.
117
+ */
118
+ export function requestsStandardTransmissionVariant(
119
+ material: Pick<MaterialSnapshot, 'materialShaderId' | 'paramSnapshot'>,
120
+ ): boolean {
121
+ return (
122
+ material.materialShaderId === 'forgeax::default-standard-pbr' &&
123
+ material.paramSnapshot?.transmission !== undefined
124
+ );
125
+ }
126
+
127
+ function recordIblPipelineBinding(
128
+ c: _InternalRenderPipelineContext,
129
+ material: MaterialSnapshot,
130
+ pipeline: RenderPipeline,
131
+ materialGroup: BindGroup,
132
+ ): void {
133
+ const receipt = c.frameState.iblBindingInspection;
134
+ if (receipt === undefined || !isStandardPbrMaterialShader(material.materialShaderId)) return;
135
+ const materialBgl =
136
+ c.runtime.getMaterialBindGroupLayout?.(
137
+ material.materialShaderId ?? 'forgeax::default-standard-pbr',
138
+ ) ?? c.pipelineState.materialBindGroupLayout;
139
+ c.frameState.iblBindingInspection = {
140
+ ...receipt,
141
+ pipeline: {
142
+ pipelineIdentity: getOpaqueResourceIdentity(pipeline as object),
143
+ effectiveMaterialLayoutIdentity: effectiveMaterialLayoutIdentity(
144
+ material.materialShaderId ?? 'forgeax::default-standard-pbr',
145
+ material.materialParamSchema,
146
+ ),
147
+ materialBglIdentity: getOpaqueResourceIdentity(materialBgl as object),
148
+ bindGroupIdentity: getOpaqueResourceIdentity(materialGroup as object),
149
+ drawFrameId: c.frameState.frameNumber,
150
+ },
151
+ };
152
+ const materialReceipt = c.frameState.iblBindingInspection.material;
153
+ const errors: string[] = [];
154
+ if (
155
+ materialReceipt !== undefined &&
156
+ materialReceipt.materialBglIdentity !== getOpaqueResourceIdentity(materialBgl as object)
157
+ ) {
158
+ errors.push('material-pipeline-bgl-identity-mismatch');
159
+ }
160
+ if (
161
+ materialReceipt !== undefined &&
162
+ materialReceipt.bindGroupIdentity !== getOpaqueResourceIdentity(materialGroup as object)
163
+ ) {
164
+ errors.push('material-bind-group-identity-mismatch');
165
+ }
166
+ if (materialReceipt !== undefined) {
167
+ const expectedResourceIdentities = [
168
+ receipt.resources.irradiance.viewIdentity,
169
+ receipt.resources.irradiance.samplerIdentity,
170
+ receipt.resources.prefilter.viewIdentity,
171
+ receipt.resources.prefilter.samplerIdentity,
172
+ receipt.resources.brdfLut.viewIdentity,
173
+ receipt.resources.brdfLut.samplerIdentity,
174
+ receipt.resources.intensityBufferIdentity,
175
+ ];
176
+ const actualResourceIdentities = materialReceipt.entries
177
+ .filter((entry) => entry.binding >= materialReceipt.skylightBindingStart)
178
+ .slice(0, expectedResourceIdentities.length)
179
+ .map((entry) => entry.resourceIdentity);
180
+ if (
181
+ actualResourceIdentities.length !== expectedResourceIdentities.length ||
182
+ actualResourceIdentities.some(
183
+ (identity, index) => identity !== expectedResourceIdentities[index],
184
+ )
185
+ ) {
186
+ errors.push('material-ibl-resource-identity-mismatch');
187
+ }
188
+ }
189
+ c.frameState.iblBindingInspection = {
190
+ ...c.frameState.iblBindingInspection,
191
+ status: errors.length === 0 ? 'binding-chain-consistent' : 'binding-chain-mismatch',
192
+ errors,
193
+ };
194
+ }
195
+
196
+ const SHARED_BOOT_MATERIAL_SHADER_IDS = new Set([
197
+ 'forgeax::default-unlit',
198
+ 'forgeax::default-shadow-caster',
199
+ 'forgeax::sprite',
200
+ 'forgeax::sprite-lit',
201
+ 'forgeax::default-sprite',
202
+ 'forgeax::msdf-text',
203
+ 'forgeax::default-standard-pbr',
204
+ 'forgeax::pbr-skin',
205
+ 'forgeax::default-standard-pbr-skin',
206
+ ]);
207
+
208
+ /**
209
+ * Returns the layout identity that can distinguish a material PSO in the
210
+ * record cache. Shared boot shaders keep the boot layout identity even when a
211
+ * manifest exposes a schema; Standard PBR only opts into a schema-derived
212
+ * identity when authored clearcoat texture bindings extend that layout.
213
+ */
214
+ export function effectiveMaterialLayoutIdentity(
215
+ materialShaderId: string,
216
+ materialParamSchema?: readonly ParamSchemaEntry[],
217
+ ): string | undefined {
218
+ if (materialParamSchema === undefined) return undefined;
219
+ const hasStandardPhysicalMaps =
220
+ isStandardPbrMaterialShader(materialShaderId) &&
221
+ physicalTextureFields(materialParamSchema).length > 0;
222
+ if (SHARED_BOOT_MATERIAL_SHADER_IDS.has(materialShaderId) && !hasStandardPhysicalMaps) {
223
+ return undefined;
224
+ }
225
+ return materialBindGroupLayoutIdentity(materialShaderId, materialParamSchema);
226
+ }
227
+
87
228
  /**
88
229
  * Resolve the depth contract for a geometry pass without changing the base
89
230
  * material state. Temporal scene-data shares the main pass depth attachment:
@@ -138,9 +279,17 @@ function submitSubmeshDraws(
138
279
  state.pipeline = pipeline;
139
280
  }
140
281
  for (const instanceDraw of instanceDraws) {
141
- if (instanceDraw.instanceBuffer !== state.instancesBuffer) {
142
- pass.setBindGroup(3, instanceDraw.instanceBindGroup);
282
+ if (
283
+ instanceDraw.instanceBuffer !== state.instancesBuffer ||
284
+ instanceDraw.probeOffset !== state.probeOffset
285
+ ) {
286
+ if (instanceDraw.probeOffset < 0) {
287
+ pass.setBindGroup(3, instanceDraw.instanceBindGroup);
288
+ } else {
289
+ pass.setBindGroup(3, instanceDraw.instanceBindGroup, [instanceDraw.probeOffset]);
290
+ }
143
291
  state.instancesBuffer = instanceDraw.instanceBuffer;
292
+ state.probeOffset = instanceDraw.probeOffset;
144
293
  }
145
294
  if (indexed) {
146
295
  pass.drawIndexed(indexCount, instanceDraw.instanceCount, indexOffset, 0, 0);
@@ -210,6 +359,7 @@ export function recordGeometryDraws(
210
359
  viewBindGroup,
211
360
  viewBindGroupDynamicOffset = 0,
212
361
  } = c;
362
+ const diagnosticsEnabled = materialDiagnosticsEnabled();
213
363
  const colorFormatOverride = c.transparentColorFormat as GPUTextureFormat | undefined;
214
364
  // The prepared Standard topology is the frame authority. A bind-group
215
365
  // identity is only a resource result; using it to infer the shader lane
@@ -226,12 +376,14 @@ export function recordGeometryDraws(
226
376
  const bindingState: GeometryBindingState = {
227
377
  pipeline: null,
228
378
  instancesBuffer: null,
379
+ probeOffset: -1,
229
380
  };
230
381
  const identityInstanceBuffer = pipelineState.identityInstanceBuffer;
231
382
  const identityInstanceDraws: readonly GeometryInstanceDraw[] = [
232
383
  {
233
384
  instanceBuffer: identityInstanceBuffer,
234
- instanceBindGroup: resolveGeometryInstancesBindGroup(c, identityInstanceBuffer),
385
+ instanceBindGroup: resolveGeometryInstancesBindGroup(c, identityInstanceBuffer, undefined),
386
+ probeOffset: -1,
235
387
  instanceCount: 1,
236
388
  },
237
389
  ];
@@ -253,6 +405,9 @@ export function recordGeometryDraws(
253
405
  // renderer's authoritative cache still owns lifetime/invalidation, while
254
406
  // this frame-local table removes the hot call/lookup overhead.
255
407
  const materialPipelineLookupCache = new Map<string, Map<string, MaterialPipelineLookup[]>>();
408
+ const recordOcclusionCandidate = (draw: () => void): void => {
409
+ draw();
410
+ };
256
411
  const resolveMaterialPipeline = (
257
412
  materialShaderId: string,
258
413
  renderState: MaterialSnapshot['renderState'],
@@ -262,6 +417,19 @@ export function recordGeometryDraws(
262
417
  colorFormat: GPUTextureFormat | undefined,
263
418
  layoutProjection: import('@forgeax/engine-geometry').VertexLayoutProjection,
264
419
  ): MaterialShaderPipelineEntry | null => {
420
+ // The fallback MRT is an extension of the built-in Standard PBR output
421
+ // contract only. The render pass may carry a second attachment whenever
422
+ // another renderable needs S_fallback, but shaders such as unlit, sprite,
423
+ // and the skin PBR variant still expose only @location(0). Passing that
424
+ // attachment into their PSO would make WebGPU reject the pipeline before
425
+ // the first draw ("Color target has no corresponding fragment stage
426
+ // output"). Keep the graph attachment shared while deriving pipeline
427
+ // targets from the shader's actual output contract.
428
+ const reflectionFallbackFormats =
429
+ materialShaderId === 'forgeax::default-standard-pbr' &&
430
+ c.reflectionFallbackColorFormat !== undefined
431
+ ? [c.reflectionFallbackColorFormat]
432
+ : undefined;
265
433
  const renderStateKey = renderStateHash(renderState);
266
434
  const shaderLookupCache = materialPipelineLookupCache.get(materialShaderId);
267
435
  const cachedLookups = shaderLookupCache?.get(renderStateKey);
@@ -276,7 +444,8 @@ export function recordGeometryDraws(
276
444
  cached.passKind === passKind &&
277
445
  cached.layoutProjection.digest === layoutProjection.digest &&
278
446
  cached.sampleCount === sampleCount &&
279
- cached.colorFormatOverride === colorFormat
447
+ cached.colorFormatOverride === colorFormat &&
448
+ cached.additionalColorFormats?.join(',') === reflectionFallbackFormats?.join(',')
280
449
  ) {
281
450
  return cached.handle;
282
451
  }
@@ -292,6 +461,12 @@ export function recordGeometryDraws(
292
461
  return null;
293
462
  }
294
463
  const resolvedVariantSet = resolvedVariantSetResult.value;
464
+ const fallbackVariantSet =
465
+ materialShaderId !== 'forgeax::default-standard-pbr'
466
+ ? resolvedVariantSet
467
+ : c.reflectionFallbackColorFormat !== undefined && resolvedVariantSet === ''
468
+ ? ''
469
+ : `${resolvedVariantSet === undefined || resolvedVariantSet === '' ? '' : `${resolvedVariantSet}+`}REFLECTION_FALLBACK_AVAILABLE=${c.reflectionFallbackColorFormat !== undefined}`;
295
470
  const resolvePipeline = () =>
296
471
  runtime.getMaterialShaderPipelineEntry?.(
297
472
  materialShaderId,
@@ -299,7 +474,7 @@ export function recordGeometryDraws(
299
474
  renderState,
300
475
  topology,
301
476
  indexFormat,
302
- resolvedVariantSet,
477
+ fallbackVariantSet,
303
478
  passKind,
304
479
  undefined,
305
480
  sampleCount,
@@ -308,6 +483,9 @@ export function recordGeometryDraws(
308
483
  undefined,
309
484
  undefined,
310
485
  layoutProjection,
486
+ undefined,
487
+ undefined,
488
+ reflectionFallbackFormats,
311
489
  ) ?? null;
312
490
  const handle =
313
491
  profilePhase === undefined
@@ -332,6 +510,7 @@ export function recordGeometryDraws(
332
510
  layoutProjection,
333
511
  sampleCount,
334
512
  colorFormatOverride: colorFormat,
513
+ additionalColorFormats: reflectionFallbackFormats,
335
514
  handle,
336
515
  };
337
516
  if (shaderLookupCache === undefined) {
@@ -361,7 +540,12 @@ export function recordGeometryDraws(
361
540
 
362
541
  if (
363
542
  passKind === 'forward' &&
364
- c.gpuDrivenEntityKeys.has(worldEntityKey(entry.source.worldId, entry.source.entityKey))
543
+ c.gpuDrivenEntityKeys.has(
544
+ worldEntityKey(
545
+ c.gpuDrivenWorldKeys?.[entry.source.worldId] ?? entry.source.worldId,
546
+ entry.source.entityKey,
547
+ ),
548
+ )
365
549
  ) {
366
550
  continue;
367
551
  }
@@ -454,12 +638,11 @@ export function recordGeometryDraws(
454
638
  const pointsLinesMeshGroup = meshBindGroup ?? meshGroup2;
455
639
  if (pointsLinesMeshGroup === null) continue;
456
640
  pass.setBindGroup(2, pointsLinesMeshGroup, meshGroup2DynamicOffsets);
457
- pass.setBindGroup(
458
- 3,
641
+ const pointsLinesInstancesBg =
459
642
  identityInstanceDraws[0]?.instanceBindGroup ??
460
- resolveGeometryInstancesBindGroup(c, identityInstanceBuffer),
461
- );
462
- recordPointsLinesDraw(pass, pointsLinesSubmission.plan);
643
+ resolveGeometryInstancesBindGroup(c, identityInstanceBuffer, undefined);
644
+ pass.setBindGroup(3, pointsLinesInstancesBg);
645
+ recordOcclusionCandidate(() => recordPointsLinesDraw(pass, pointsLinesSubmission.plan));
463
646
  pass.setBindGroup(0, viewBindGroup as BindGroup, [viewBindGroupDynamicOffset, 0]);
464
647
  continue;
465
648
  }
@@ -487,7 +670,7 @@ export function recordGeometryDraws(
487
670
  // length instead.
488
671
  if (pipelineTag === 'unlit') dispatchCounts.unlit += 1;
489
672
 
490
- const instanceDraws = resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws);
673
+ const instanceDraws = resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws, false);
491
674
  if (instanceDraws === null) continue;
492
675
 
493
676
  // feat-20260611 R2 / M8 / w28 (IS-14): skin entries need a 3-binding
@@ -518,6 +701,8 @@ export function recordGeometryDraws(
518
701
  meshGroup2DynamicOffsets[0] = i * MESH_PER_ENTITY_STRIDE;
519
702
  let group2DynamicOffsets: readonly number[] = meshGroup2DynamicOffsets;
520
703
  const isSkinEntry = entry.source.skin !== undefined;
704
+ const probeBlendRecordAvailable =
705
+ runtime.device.caps.storageBuffer && entry.source.probeBlendRecord !== undefined;
521
706
  // feat-20260612-skin-palette-per-frame-upload M1 / m1-3 + M6: the
522
707
  // record stage reads the GPU buffer reference through
523
708
  // `entry.source.skin.buffer` (per-slice carrier set at extract time
@@ -534,7 +719,22 @@ export function recordGeometryDraws(
534
719
  // and walks 0, 1536, 3072, ... on the storage path.
535
720
  const skinAllocator = pipelineState.skinPaletteAllocator;
536
721
  const skinSlice = entry.source.skin;
537
- const clusteredSkin = clusteredLighting;
722
+ // Authored Standard skin aliases deliberately publish the physical root
723
+ // with only their STORAGE_BUFFER capability axis. Their composed WGSL is
724
+ // the direct-lighting/palette (`skin`) ABI, even when the frame's
725
+ // canonical Standard lane uses clustered lighting. Only the canonical
726
+ // engine skin artifact can consume the HDRP unified skin group(2) layout;
727
+ // selecting it from the frame topology alone would bind
728
+ // `hdrp-skin-unified-bg-group2` to a three-entry URP skin PSO.
729
+ const clusteredSkin =
730
+ clusteredLighting &&
731
+ entry.source.materials.every((material) => {
732
+ const shaderId = material.materialShaderId;
733
+ return (
734
+ !isStandardPbrSkinMaterialShader(shaderId) ||
735
+ isCanonicalStandardPbrMaterialShader(shaderId)
736
+ );
737
+ });
538
738
  const skinMeshBindGroupLayout = clusteredSkin
539
739
  ? pipelineState.hdrpSkinMeshBindGroupLayout
540
740
  : pipelineState.pbrSkinMeshBindGroupLayout;
@@ -567,6 +767,7 @@ export function recordGeometryDraws(
567
767
  c.standardLighting,
568
768
  runtime.device.caps.storageBuffer,
569
769
  entry.mesh.layoutProjection.attributes.some((attribute) => attribute.key === 'color'),
770
+ probeBlendRecordAvailable,
570
771
  ),
571
772
  );
572
773
  if (!skinVariantSetResult.ok) runtime.errorRegistry.fire(skinVariantSetResult.error);
@@ -588,6 +789,9 @@ export function recordGeometryDraws(
588
789
  undefined,
589
790
  undefined,
590
791
  entry.mesh.layoutProjection,
792
+ undefined,
793
+ undefined,
794
+ undefined,
591
795
  ) ?? null)
592
796
  : null;
593
797
  if (skinResources !== null && skinPsoProbe !== null) {
@@ -677,6 +881,19 @@ export function recordGeometryDraws(
677
881
  // the async PSO compile resolves the cache hits and the next frame
678
882
  // routes the skin BG + skin pipeline together. Mirrors the uniform
679
883
  // null skip-draw shape (M6-T1, charter P3 explicit failure).
884
+ if (diagnosticsEnabled) {
885
+ console.error(
886
+ `[render-material] skin draw skipped: ${JSON.stringify({
887
+ entityKey: entry.source.entityKey,
888
+ materialHandle: entry.source.material.materialHandle,
889
+ reason: 'skin-pipeline-unavailable',
890
+ skinResourcesReady: skinResources !== null,
891
+ skinPsoReady: skinPsoProbe !== null,
892
+ paletteBufferReady: skinSlice?.buffer !== undefined,
893
+ paletteByteOffset: skinSlice?.byteOffset,
894
+ })}`,
895
+ );
896
+ }
680
897
  continue;
681
898
  }
682
899
  // feat-20260520-2d-sprite-layer-mvp M-3 / w25 (@fallback sprite
@@ -771,6 +988,26 @@ export function recordGeometryDraws(
771
988
  // injection, and Skylight merge; deduped cross-entity via the
772
989
  // shaderId-outer `materialBgShared` cache.
773
990
  const materialSlot = materialSlotIndices[i]?.[matSlotIdx] ?? materialSlotIndices[i]?.[0] ?? 0;
991
+ const processValue = (
992
+ globalThis as {
993
+ process?: { env?: Record<string, string | undefined> };
994
+ }
995
+ ).process;
996
+ if (
997
+ processValue?.env?.FORGEAX_MATERIAL_PIPELINE_DIAGNOSTICS === '1' &&
998
+ (submeshMaterial.materialShaderId === 'forgeax::default-standard-pbr' ||
999
+ submeshMaterial.materialShaderId === 'forgeax::pbr-skin')
1000
+ ) {
1001
+ console.error(
1002
+ `[render-material] draw receipt: ${JSON.stringify({
1003
+ entityIndex: entry.renderableIndex,
1004
+ materialSlot,
1005
+ materialHandle: submeshMaterial.materialHandle,
1006
+ clearcoat: submeshMaterial.paramSnapshot?.clearcoat,
1007
+ shader: submeshMaterial.materialShaderId,
1008
+ })}`,
1009
+ );
1010
+ }
774
1011
  perSubmeshBg =
775
1012
  profilePhase === undefined
776
1013
  ? resolveMaterialBindGroup(
@@ -792,8 +1029,12 @@ export function recordGeometryDraws(
792
1029
  const smTopology = sm.topology;
793
1030
  const smMaterialShaderId =
794
1031
  entry.source.skin !== undefined
795
- ? SKIN_MATERIAL_SHADER_ID
1032
+ ? isStandardPbrSkinMaterialShader(submeshMaterial.materialShaderId)
1033
+ ? submeshMaterial.materialShaderId
1034
+ : SKIN_MATERIAL_SHADER_ID
796
1035
  : submeshMaterial.materialShaderId;
1036
+ const probeBlendAvailable =
1037
+ probeBlendRecordAvailable && isStandardPbrMaterialShader(smMaterialShaderId);
797
1038
  const isSpriteShader =
798
1039
  smMaterialShaderId === 'forgeax::sprite' || smMaterialShaderId === 'forgeax::sprite-lit';
799
1040
  // Preserve the dedicated sprite-pass state when the linear-LDR graph
@@ -869,7 +1110,13 @@ export function recordGeometryDraws(
869
1110
  c.standardLighting,
870
1111
  runtime.device.caps.storageBuffer,
871
1112
  hasVertexColor,
1113
+ probeBlendAvailable,
872
1114
  );
1115
+ const transmissionAvailable = requestsStandardTransmissionVariant(submeshMaterial);
1116
+ const materialCapabilityVariantSet =
1117
+ transmissionAvailable && !capabilityVariantSet.includes('TRANSMISSION_AVAILABLE=')
1118
+ ? `${capabilityVariantSet === '' ? '' : `${capabilityVariantSet}+`}TRANSMISSION_AVAILABLE=true`
1119
+ : capabilityVariantSet;
873
1120
  // Sprite's base request must retain the boot-selected artifact. Only
874
1121
  // SpriteInstances uses the explicit empty key for the
875
1122
  // PER_INSTANCE_REGION=true variant; synthesizing the generic PBR
@@ -880,23 +1127,16 @@ export function recordGeometryDraws(
880
1127
  ? ''
881
1128
  : undefined
882
1129
  : entry.variantSet === undefined
883
- ? capabilityVariantSet
1130
+ ? materialCapabilityVariantSet
884
1131
  : capabilityVariantSet === ''
885
1132
  ? entry.variantSet
886
1133
  : `${capabilityVariantSet}+${entry.variantSet}`;
887
- const projectionVariantSetResult = isSpriteShader
888
- ? ({ ok: true, value: variantSet } as const)
889
- : variantSetFromVertexLayoutProjection(entry.mesh.layoutProjection, variantSet);
890
- if (!projectionVariantSetResult.ok) {
891
- runtime.errorRegistry.fire(projectionVariantSetResult.error);
892
- continue;
893
- }
894
1134
  const cachedPipeline = resolveMaterialPipeline(
895
1135
  smMaterialShaderId,
896
1136
  pipelineRenderState,
897
1137
  smTopology,
898
1138
  entry.mesh.indexFormat,
899
- projectionVariantSetResult.value,
1139
+ variantSet,
900
1140
  colorFormatOverride,
901
1141
  entry.mesh.layoutProjection,
902
1142
  );
@@ -920,8 +1160,30 @@ export function recordGeometryDraws(
920
1160
  }
921
1161
 
922
1162
  if (smPipelineHandle === null) {
1163
+ if (diagnosticsEnabled) {
1164
+ console.error(
1165
+ `[render-material] draw skipped: ${JSON.stringify({
1166
+ entityKey: entry.source.entityKey,
1167
+ materialHandle: submeshMaterial.materialHandle,
1168
+ shader: smMaterialShaderId,
1169
+ reason: 'pipeline-unavailable',
1170
+ })}`,
1171
+ );
1172
+ }
923
1173
  continue;
924
1174
  }
1175
+ // The probe record is an ABI extension of the built-in Standard PBR
1176
+ // shaders only. RenderScene may retain a record on every projected
1177
+ // renderable, but unlit/custom shaders still use the one-binding
1178
+ // instances BGL and must never receive the probe bind group. Resolve
1179
+ // this per submesh so mixed-material meshes cannot cross the layouts.
1180
+ const instanceDraws = resolveGeometryInstanceBuffer(
1181
+ c,
1182
+ entry,
1183
+ identityInstanceDraws,
1184
+ probeBlendAvailable,
1185
+ );
1186
+ if (instanceDraws === null) continue;
925
1187
  // Standard clustered PBR variants declare the unified cluster/SSAO
926
1188
  // group(2) layout. Unlit and other URP-layout materials must bind the
927
1189
  // ordinary mesh group instead; choosing one group for the whole pass
@@ -938,21 +1200,35 @@ export function recordGeometryDraws(
938
1200
  if (selectedGroup === null) continue;
939
1201
  group2BindGroup = selectedGroup;
940
1202
  }
1203
+ if (diagnosticsEnabled && submeshMaterial.textureHandles !== undefined) {
1204
+ console.error(
1205
+ `[render-material] draw submitted: ${JSON.stringify({
1206
+ entityKey: entry.source.entityKey,
1207
+ materialHandle: submeshMaterial.materialHandle,
1208
+ shader: smMaterialShaderId,
1209
+ textureHandles: [...submeshMaterial.textureHandles.entries()].map(
1210
+ ([field, handle]) => ({
1211
+ field,
1212
+ handle,
1213
+ }),
1214
+ ),
1215
+ skin: isSkinEntry
1216
+ ? {
1217
+ resourcesReady: skinResources !== null,
1218
+ psoReady: skinPsoProbe !== null,
1219
+ paletteBufferReady: skinSlice?.buffer !== undefined,
1220
+ paletteByteOffset: skinSlice?.byteOffset,
1221
+ dynamicOffsets: [...group2DynamicOffsets],
1222
+ }
1223
+ : undefined,
1224
+ pipelineReady: true,
1225
+ })}`,
1226
+ );
1227
+ }
941
1228
  pass.setBindGroup(2, group2BindGroup, group2DynamicOffsets);
942
1229
 
943
1230
  if (profilePhase === undefined) {
944
- submitSubmeshDraws(
945
- pass,
946
- bindingState,
947
- smPipelineHandle,
948
- instanceDraws,
949
- entry.mesh.indexed,
950
- sm.indexCount,
951
- sm.vertexCount,
952
- sm.indexOffset,
953
- );
954
- } else {
955
- profileGeometrySegment(c, passKind, 'draw-submit', () =>
1231
+ recordOcclusionCandidate(() =>
956
1232
  submitSubmeshDraws(
957
1233
  pass,
958
1234
  bindingState,
@@ -964,6 +1240,24 @@ export function recordGeometryDraws(
964
1240
  sm.indexOffset,
965
1241
  ),
966
1242
  );
1243
+ } else {
1244
+ profileGeometrySegment(c, passKind, 'draw-submit', () =>
1245
+ recordOcclusionCandidate(() =>
1246
+ submitSubmeshDraws(
1247
+ pass,
1248
+ bindingState,
1249
+ smPipelineHandle,
1250
+ instanceDraws,
1251
+ entry.mesh.indexed,
1252
+ sm.indexCount,
1253
+ sm.vertexCount,
1254
+ sm.indexOffset,
1255
+ ),
1256
+ ),
1257
+ );
1258
+ }
1259
+ if (diagnosticsEnabled && perSubmeshBg !== null) {
1260
+ recordIblPipelineBinding(c, submeshMaterial, smPipelineHandle, perSubmeshBg);
967
1261
  }
968
1262
  }
969
1263
  }
@@ -978,10 +1272,35 @@ function resolveGeometryInstanceBuffer(
978
1272
  c: _InternalRenderPipelineContext,
979
1273
  entry: _InternalRenderPipelineContext['validatedOrdered'][number],
980
1274
  identityInstanceDraws: readonly GeometryInstanceDraw[],
1275
+ useProbeBlend: boolean,
981
1276
  ): readonly GeometryInstanceDraw[] | null {
982
1277
  const { runtime, pipelineState, frameState } = c;
983
1278
  const inst = entry.source.instances;
984
- if (inst === undefined) return identityInstanceDraws;
1279
+ const probeBinding =
1280
+ !useProbeBlend ||
1281
+ !runtime.device.caps.storageBuffer ||
1282
+ entry.source.probeBlendRecord === undefined
1283
+ ? undefined
1284
+ : resolveProbeBlendBuffer(
1285
+ c,
1286
+ entry.source.probeBlendRecord,
1287
+ worldEntityKey(entry.source.worldId, entry.source.entityKey),
1288
+ );
1289
+ if (inst === undefined) {
1290
+ const identity = identityInstanceDraws[0];
1291
+ if (identity === undefined) return null;
1292
+ return [
1293
+ {
1294
+ ...identity,
1295
+ instanceBindGroup: resolveGeometryInstancesBindGroup(
1296
+ c,
1297
+ pipelineState.identityInstanceBuffer,
1298
+ probeBinding?.buffer,
1299
+ ),
1300
+ probeOffset: probeBinding?.offset ?? -1,
1301
+ },
1302
+ ];
1303
+ }
985
1304
  // A present, empty Instances array means zero draws. Do not fall back to
986
1305
  // the shared identity buffer, which would silently render one instance.
987
1306
  if (inst.instanceCount === 0) return [];
@@ -1022,7 +1341,12 @@ function resolveGeometryInstanceBuffer(
1022
1341
  }
1023
1342
  draws.push({
1024
1343
  instanceBuffer: buffer.handle,
1025
- instanceBindGroup: resolveGeometryInstancesBindGroup(c, buffer.handle),
1344
+ instanceBindGroup: resolveGeometryInstancesBindGroup(
1345
+ c,
1346
+ buffer.handle,
1347
+ probeBinding?.buffer,
1348
+ ),
1349
+ probeOffset: probeBinding?.offset ?? -1,
1026
1350
  instanceCount: count,
1027
1351
  });
1028
1352
  }
@@ -1114,25 +1438,80 @@ function resolveGeometryInstanceBuffer(
1114
1438
  return [
1115
1439
  {
1116
1440
  instanceBuffer,
1117
- instanceBindGroup: resolveGeometryInstancesBindGroup(c, instanceBuffer),
1441
+ instanceBindGroup: resolveGeometryInstancesBindGroup(c, instanceBuffer, probeBinding?.buffer),
1442
+ probeOffset: probeBinding?.offset ?? -1,
1118
1443
  instanceCount,
1119
1444
  },
1120
1445
  ];
1121
1446
  }
1122
1447
 
1123
- function resolveGeometryInstancesBindGroup(
1448
+ export function resolveProbeBlendBuffer(
1449
+ c: _InternalRenderPipelineContext,
1450
+ record: import('../scene/probe-blend-record').ProbeBlendRecord | undefined,
1451
+ entityKey?: number,
1452
+ ): { readonly buffer: Buffer; readonly offset: number } {
1453
+ const { runtime, frameState } = c;
1454
+ const usage = runtime.device.caps.storageBuffer
1455
+ ? GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST
1456
+ : GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST;
1457
+ if (record === undefined) throw new Error('probe blend record is required for probe allocation');
1458
+ if (entityKey === undefined) throw new Error('probe blend record requires an entity key');
1459
+ const offset = probeBlendRecordOffset(record.objectKey);
1460
+ const requiredCapacity = record.objectKey + 2;
1461
+ let buffer = frameState.probeBlendRecordBuffer;
1462
+ if (buffer === undefined || frameState.probeBlendRecordBufferCapacity < requiredCapacity) {
1463
+ const created = runtime.device.createBuffer({
1464
+ label: 'probe-blend-records',
1465
+ size: requiredCapacity * PROBE_BLEND_RECORD_STRIDE,
1466
+ usage,
1467
+ mappedAtCreation: false,
1468
+ });
1469
+ if (!created.ok) throw created.error;
1470
+ buffer = new GpuBuffer(runtime.device, created.value);
1471
+ frameState.probeBlendRecordBuffer = buffer;
1472
+ frameState.probeBlendRecordBufferCapacity = requiredCapacity;
1473
+ // The cache is tied to the previous backing allocation. A new buffer has
1474
+ // no valid record contents, so every later object must upload its full
1475
+ // 160B payload even when generation/bytes match the old allocation.
1476
+ frameState.probeBlendBuffers.clear();
1477
+ }
1478
+ const cached = frameState.probeBlendBuffers.get(entityKey);
1479
+ const unchanged =
1480
+ cached !== undefined &&
1481
+ cached.generation === record.generation &&
1482
+ cached.bytes.length === record.bytes.length &&
1483
+ cached.bytes.every((value, index) => value === record.bytes[index]);
1484
+ if (!unchanged) {
1485
+ const uploaded = runtime.device.queue.writeBuffer(buffer.handle, offset, record.bytes);
1486
+ if (!uploaded.ok) throw uploaded.error;
1487
+ // Publish the generation only after the complete 160B queue write succeeds.
1488
+ frameState.probeBlendBuffers.set(entityKey, {
1489
+ generation: record.generation,
1490
+ bytes: new Uint8Array(record.bytes),
1491
+ });
1492
+ }
1493
+ return { buffer: buffer.handle, offset };
1494
+ }
1495
+
1496
+ export function resolveGeometryInstancesBindGroup(
1124
1497
  c: _InternalRenderPipelineContext,
1125
1498
  instanceBuffer: Buffer,
1499
+ probeBuffer?: Buffer,
1126
1500
  ): BindGroup {
1127
1501
  const { runtime, pipelineState, frameState, bindGroupCounts } = c;
1502
+ const layout =
1503
+ probeBuffer === undefined
1504
+ ? pipelineState.instancesBindGroupLayout
1505
+ : pipelineState.probeInstancesBindGroupLayout;
1506
+ if (layout === undefined) throw new Error('probe instances bind-group layout is unavailable');
1128
1507
  return getOrCreateFromChain(
1129
1508
  frameState.instancesBgShared,
1130
- [pipelineState.instancesBindGroupLayout, instanceBuffer],
1131
- 'instances',
1509
+ probeBuffer === undefined ? [layout, instanceBuffer] : [layout, instanceBuffer, probeBuffer],
1510
+ probeBuffer === undefined ? 'instances-no-probe' : 'instances-probe',
1132
1511
  () => {
1133
1512
  const result = runtime.device.createBindGroup({
1134
1513
  label: 'pbr-instances-bg',
1135
- layout: pipelineState.instancesBindGroupLayout,
1514
+ layout,
1136
1515
  entries: [
1137
1516
  {
1138
1517
  binding: 0,
@@ -1141,6 +1520,17 @@ function resolveGeometryInstancesBindGroup(
1141
1520
  value: { buffer: instanceBuffer },
1142
1521
  },
1143
1522
  },
1523
+ ...(probeBuffer === undefined
1524
+ ? []
1525
+ : [
1526
+ {
1527
+ binding: 1,
1528
+ resource: {
1529
+ kind: 'buffer' as const,
1530
+ value: { buffer: probeBuffer, offset: 0, size: PROBE_BLEND_RECORD_BYTE_SIZE },
1531
+ },
1532
+ },
1533
+ ]),
1144
1534
  ],
1145
1535
  });
1146
1536
  if (!result.ok) throw result.error;