@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
@@ -1,14 +1,15 @@
1
1
  import { TRANSPARENT_SORT_MODE_LAYER_Z, TRANSPARENT_SORT_MODE_LAYER_Y } from './chunk-RXZ3RGOU.mjs';
2
- import { DEFAULT_CLUSTER_GRID, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT, GPU_TEXTURE_USAGE_COPY_SRC, EquirectProjectionFailedError, SHADOW_ATLAS_DEFAULT_LAYERS, SHADOW_ATLAS_DEFAULT_FACE_SIZE, ShadowAtlas, PointShadowAtlasUninitializedError, PointShadowAtlasBoundsViolationError, DEFAULT_STANDARD_PROFILE, StandardClusterTransportUnavailableError, createIblKernelCache, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING, GPU_TEXTURE_USAGE_COPY_DST, GPU_TEXTURE_USAGE_TEXTURE_BINDING, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, MAX_LIGHTS, CLUSTER_GRID_STRIDE_U32, LIGHT_INDEX_LIST_CAPACITY, StandardLightBudgetExceededError, createStandardClusterLayout, RenderTargetDescriptorInvalidError, RendererOperationError, StandardClusterIndexOverflowError, FXAA_POST_PROCESS_ID, VideoUploadUnsupportedError, VertexColorVariantConflictError } from './chunk-6OPXDQ2E.mjs';
3
- import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from './chunk-VFPLTKCU.mjs';
4
- import { worldEntityKey } from './chunk-BXBQQKFQ.mjs';
5
- import { ok, derive, err as err$1, toShared, ASSET_ERROR_HINTS, deriveTextureLayout, KNOWN_PASS_KINDS, resolveMaterialTextureCoordinates, handleSlot } from '@forgeax/engine-types';
2
+ import { DEFAULT_CLUSTER_GRID, DEFAULT_STANDARD_PROFILE, createIblKernelCache, MAX_LIGHTS, CLUSTER_GRID_STRIDE_U32, LIGHT_INDEX_LIST_CAPACITY, createStandardClusterLayout } from './chunk-TFH5I7JJ.mjs';
3
+ import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from './chunk-YKB5DQV7.mjs';
4
+ import { GPU_SHADER_STAGE_COMPUTE, GPU_BUFFER_USAGE_STORAGE, GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_UNIFORM, createHdrpBindGroupLayoutDescriptor, IES_SLICE_WIDTH, IES_SLICE_HEIGHT, COOKIE_SLICE_SIZE, GPU_SHADER_STAGE_VERTEX, GPU_SHADER_STAGE_FRAGMENT, GPU_BUFFER_USAGE_INDEX, GPU_BUFFER_USAGE_VERTEX, pbrSkinMeshDynamicOffsets, worldEntityKey, buildPbrMaterialUserRegionEntries, buildBindGroupLayoutDescriptor, isStandardPbrMaterialShader, SHADOW_CASTER_SHADER_ID, shadowCasterVariantSet, COOKIE_MATRIX_BYTES, matchPass, createPbrSkinMeshBindGroupEntries, getOpaqueResourceIdentity, SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET, physicalTextureFields, isCanonicalStandardPbrMaterialShader, isStandardPbrSkinMaterialShader, SKIN_MATERIAL_SHADER_ID, PROBE_BLEND_RECORD_BYTE_SIZE, resolveRenderTargetMaterialSource, probeBlendRecordOffset, PROBE_BLEND_RECORD_STRIDE, materialBindGroupLayoutIdentity } from './chunk-KZFTK6DO.mjs';
5
+ import { GPU_TEXTURE_USAGE_RENDER_ATTACHMENT, GPU_TEXTURE_USAGE_COPY_SRC, VertexColorVariantConflictError, EquirectProjectionFailedError, SHADOW_ATLAS_DEFAULT_LAYERS, SHADOW_ATLAS_DEFAULT_FACE_SIZE, ShadowAtlas, PointShadowAtlasUninitializedError, PointShadowAtlasBoundsViolationError, StandardClusterTransportUnavailableError, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING, GPU_TEXTURE_USAGE_COPY_DST, GPU_TEXTURE_USAGE_TEXTURE_BINDING, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, StandardLightBudgetExceededError, RendererOperationError, StandardClusterIndexOverflowError, FXAA_POST_PROCESS_ID, VideoUploadUnsupportedError } from './chunk-RR5L4SRS.mjs';
6
+ import { derive, ok, err as err$1, toShared, ASSET_ERROR_HINTS, deriveTextureLayout, handleSlot, KNOWN_PASS_KINDS, resolveMaterialTextureCoordinates } from '@forgeax/engine-types';
6
7
  import { ok as ok$1, err, RhiError } from '@forgeax/engine-rhi';
7
8
  import { mat3, vec3, mat4 } from '@forgeax/engine-math';
8
- import { DEFAULT_STANDARD_PBR_PARAM_SCHEMA, DEFAULT_MSDF_TEXT_PARAM_SCHEMA, DEFAULT_SPRITE_PARAM_SCHEMA, DEFAULT_UNLIT_PARAM_SCHEMA } from '@forgeax/engine-shader';
9
9
  import { deriveVertexLayoutProjection, deriveVertexBufferLayout } from '@forgeax/engine-geometry';
10
10
  import { resolveAssetHandle } from '@forgeax/engine-assets-runtime';
11
11
  import { probeVideoHighPerfUpload, VIDEO_ELEMENT_PROVIDER_KEY } from '@forgeax/engine-graphics-extras';
12
+ import { DEFAULT_STANDARD_PBR_PARAM_SCHEMA, STANDARD_PIPELINE_PARAM_SCHEMA, DEFAULT_MSDF_TEXT_PARAM_SCHEMA, DEFAULT_SPRITE_PARAM_SCHEMA, DEFAULT_UNLIT_PARAM_SCHEMA } from '@forgeax/engine-shader';
12
13
  import { isCompressedFormat, bytesPerRow, blockParamsForFormat } from '@forgeax/engine-codec';
13
14
 
14
15
  function resolveOutputDither(config) {
@@ -83,66 +84,62 @@ function importRenderPipelineSurface(graph, topology) {
83
84
  }
84
85
  });
85
86
  }
86
-
87
- // src/gpu-stage.ts
88
- var GPU_SHADER_STAGE_VERTEX = 1;
89
- var GPU_SHADER_STAGE_FRAGMENT = 2;
90
- var GPU_SHADER_STAGE_COMPUTE = 4;
91
-
92
- // src/gpu-usage.ts
93
- var GPU_BUFFER_USAGE_VERTEX = 32;
94
- var GPU_BUFFER_USAGE_INDEX = 16;
95
- var GPU_BUFFER_USAGE_COPY_DST = 8;
96
- var GPU_BUFFER_USAGE_COPY_SRC = 4;
97
- var GPU_BUFFER_USAGE_UNIFORM = 64;
98
- var GPU_BUFFER_USAGE_STORAGE = 128;
99
- var GPU_BUFFER_USAGE_INDIRECT = 256;
100
- var GPU_BUFFER_USAGE_MAP_READ = 1;
101
- var GPU_BUFFER_USAGE_QUERY_RESOLVE = 512;
102
- var BYTES_PER_LIGHT_SLOT = 64;
103
- var LightSlotKind = {
87
+ var STORAGE_BUFFER_MIN_REQUIRED = 4;
88
+ var BYTES_PER_DIRECT_LIGHT_SLOT = 80;
89
+ var DirectLightSlotKind = {
104
90
  POINT: 0,
105
- SPOT: 1
91
+ SPOT: 1,
92
+ RECT_AREA: 2
106
93
  };
107
- var STORAGE_BUFFER_MIN_REQUIRED = 4;
108
- var POINT_LIGHT_STD430_BYTES = 32;
109
- var SPOT_LIGHT_STD430_BYTES = 80;
110
- var SPOT_LIGHT_SHADOW_TILE_SENTINEL = -1;
111
- var SPOT_LIGHT_PROJECTOR_FLAG = 1073741824;
112
- var SPOT_LIGHT_TILE_MASK = SPOT_LIGHT_PROJECTOR_FLAG - 1;
113
- var LIGHT_ARRAY_HEADER_BYTES = 16;
114
- var LIGHT_ARRAY_MAX_SLOTS = 4;
115
- var POINT_LIGHT_SHADOW_LAYER_SENTINEL = -1;
116
- function packLightSlot(snap, shadow, projectorSelected) {
117
- const out = new Float32Array(16);
118
- out[0] = snap.position[0] ?? 0;
119
- out[1] = snap.position[1] ?? 0;
120
- out[2] = snap.position[2] ?? 0;
121
- out[3] = snap.invRangeSquared;
122
- out[4] = snap.color[0] ?? 0;
123
- out[5] = snap.color[1] ?? 0;
124
- out[6] = snap.color[2] ?? 0;
125
- out[7] = snap.kind === "point" ? 1 : snap.cosInner;
126
- out[8] = snap.kind === "point" ? 0 : snap.direction[0] ?? 0;
127
- out[9] = snap.kind === "point" ? 0 : snap.direction[1] ?? 0;
128
- out[10] = snap.kind === "point" ? 0 : snap.direction[2] ?? 0;
129
- out[11] = snap.kind === "point" ? 0 : snap.cosOuter;
130
- const u32 = new Uint32Array(out.buffer);
131
- u32[12] = snap.kind === "point" ? LightSlotKind.POINT : LightSlotKind.SPOT;
132
- const i32 = new Int32Array(out.buffer);
133
- if (snap.kind === "point" && shadow !== void 0) {
134
- i32[13] = shadow.shadowAtlasLayer;
135
- out[14] = shadow.near;
136
- out[15] = shadow.far;
137
- } else if (snap.kind === "spot") {
138
- const hasProjector = projectorSelected ?? (snap.projectorAsset !== void 0 && snap.lightViewProj !== void 0);
139
- const tile = snap.shadowAtlasTile >= 0 ? snap.shadowAtlasTile : 0;
140
- i32[13] = hasProjector ? SPOT_LIGHT_PROJECTOR_FLAG | tile & SPOT_LIGHT_TILE_MASK : snap.shadowAtlasTile >= 0 ? snap.shadowAtlasTile : SPOT_LIGHT_SHADOW_TILE_SENTINEL;
141
- out[14] = snap.shadowIntensity ?? 1;
142
- out[15] = snap.pcfKernelSize ?? 3;
94
+ var DIRECT_LIGHT_SLOT_METADATA_SENTINEL = 4294967295;
95
+ var DIRECT_LIGHT_SLOT_PROJECTOR_FLAG = 1073741824;
96
+ var DIRECT_LIGHT_SLOT_TILE_MASK = DIRECT_LIGHT_SLOT_PROJECTOR_FLAG - 1;
97
+ var DIRECT_LIGHT_SLOT_FLOAT_COUNT = BYTES_PER_DIRECT_LIGHT_SLOT / Float32Array.BYTES_PER_ELEMENT;
98
+ function directLightMetadata(value) {
99
+ return value === void 0 ? DIRECT_LIGHT_SLOT_METADATA_SENTINEL : value >>> 0;
100
+ }
101
+ function packDirectLightSlot(snapshot, projectorSelected) {
102
+ const out = new Float32Array(DIRECT_LIGHT_SLOT_FLOAT_COUNT);
103
+ out[0] = snapshot.position[0] ?? 0;
104
+ out[1] = snapshot.position[1] ?? 0;
105
+ out[2] = snapshot.position[2] ?? 0;
106
+ out[3] = snapshot.invRangeSquared;
107
+ out[4] = snapshot.color[0] ?? 0;
108
+ out[5] = snapshot.color[1] ?? 0;
109
+ out[6] = snapshot.color[2] ?? 0;
110
+ if (snapshot.kind === "point") {
111
+ out[7] = 1;
112
+ } else if (snapshot.kind === "spot") {
113
+ out[7] = snapshot.cosInner;
114
+ out[8] = snapshot.direction[0] ?? 0;
115
+ out[9] = snapshot.direction[1] ?? 0;
116
+ out[10] = snapshot.direction[2] ?? 0;
117
+ out[11] = snapshot.cosOuter;
118
+ out[12] = snapshot.depthBias ?? 5e-3;
119
+ out[13] = snapshot.normalBias ?? 0.05;
120
+ out[14] = snapshot.shadowIntensity ?? 1;
121
+ out[15] = snapshot.rollDeg ?? 0;
143
122
  } else {
144
- i32[13] = POINT_LIGHT_SHADOW_LAYER_SENTINEL;
145
- }
123
+ out[7] = snapshot.halfWidth;
124
+ out[8] = snapshot.axisY[0] ?? 0;
125
+ out[9] = snapshot.axisY[1] ?? 0;
126
+ out[10] = snapshot.axisY[2] ?? 0;
127
+ out[11] = snapshot.halfHeight;
128
+ out[12] = snapshot.axisX[0] ?? 0;
129
+ out[13] = snapshot.axisX[1] ?? 0;
130
+ out[14] = snapshot.axisX[2] ?? 0;
131
+ }
132
+ const metadata = new Uint32Array(out.buffer);
133
+ metadata[16] = snapshot.kind === "point" ? DirectLightSlotKind.POINT : snapshot.kind === "spot" ? DirectLightSlotKind.SPOT : DirectLightSlotKind.RECT_AREA;
134
+ const spotProjectorSelected = snapshot.kind === "spot" && (projectorSelected ?? (snapshot.projectorAsset !== void 0 && snapshot.lightViewProj !== void 0));
135
+ const spotShadowMetadata = snapshot.kind === "spot" ? spotProjectorSelected ? DIRECT_LIGHT_SLOT_PROJECTOR_FLAG | (snapshot.shadowAtlasTile >= 0 ? snapshot.shadowAtlasTile : 0) & DIRECT_LIGHT_SLOT_TILE_MASK : directLightMetadata(snapshot.shadowAtlasTile) : void 0;
136
+ metadata[17] = directLightMetadata(
137
+ snapshot.kind === "point" ? snapshot.shadowAtlasLayer : snapshot.kind === "spot" ? spotShadowMetadata : void 0
138
+ );
139
+ metadata[18] = directLightMetadata(snapshot.iesProfileSlice);
140
+ metadata[19] = directLightMetadata(
141
+ spotProjectorSelected && snapshot.kind === "spot" ? snapshot.projectorSlice : snapshot.cookieSlice
142
+ );
146
143
  return out;
147
144
  }
148
145
  function assertStorageBufferCap(cap) {
@@ -155,8 +152,8 @@ function assertStorageBufferCap(cap) {
155
152
  return err(
156
153
  new RhiError({
157
154
  code: "limit-exceeded",
158
- expected: `device.limits.maxStorageBuffersPerShaderStage >= ${STORAGE_BUFFER_MIN_REQUIRED}, or exactly 0 for uniform-fallback path`,
159
- hint: `device.limits.maxStorageBuffersPerShaderStage = ${cap} < ${STORAGE_BUFFER_MIN_REQUIRED} and != 0. WebGPU spec default minimum is 8; this may be a downlevel adapter with partial storage support. Wait for todo-125 multi-light-pack which collapses point + spot into a single cluster storage buffer (1 entry instead of 2) for 2-3 buffer environments.`,
155
+ expected: `device.limits.maxStorageBuffersPerShaderStage >= ${STORAGE_BUFFER_MIN_REQUIRED}, or exactly 0 for the mesh-only fallback`,
156
+ hint: `device.limits.maxStorageBuffersPerShaderStage = ${cap} < ${STORAGE_BUFFER_MIN_REQUIRED} and != 0. WebGPU spec default minimum is 8; this adapter cannot run the unified Cluster local-light path.`,
160
157
  detail: {
161
158
  maxStorageBufferBindingSize: cap,
162
159
  requestedBytes: STORAGE_BUFFER_MIN_REQUIRED
@@ -164,621 +161,6 @@ function assertStorageBufferCap(cap) {
164
161
  })
165
162
  );
166
163
  }
167
- var BGL_ONLY_SPEC_STUB = Object.freeze({
168
- shader: { id: "", passKind: "forward", variantSet: void 0 },
169
- attachments: { colorFormats: [], depthFormat: void 0, sampleCount: 1 },
170
- geometry: { topology: "triangle-list", vertexLayout: {} },
171
- renderState: void 0
172
- });
173
- function buildPbrMaterialUserRegionEntries(paramSchema = DEFAULT_STANDARD_PBR_PARAM_SCHEMA) {
174
- const derived = derive(paramSchema);
175
- const entries = derived.bglEntries.map(
176
- (entry) => ({
177
- binding: entry.binding,
178
- visibility: entry.visibility,
179
- ...entry.buffer === void 0 ? {} : { buffer: entry.buffer },
180
- ...entry.sampler === void 0 ? {} : { sampler: entry.sampler },
181
- ...entry.texture === void 0 ? {} : { texture: entry.texture },
182
- ...entry.storageTexture === void 0 ? {} : { storageTexture: entry.storageTexture }
183
- })
184
- );
185
- const ubo = entries[0];
186
- if (ubo !== void 0 && ubo.binding === 0 && ubo.buffer?.type === "uniform") {
187
- entries[0] = {
188
- binding: 0,
189
- visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
190
- buffer: { type: "uniform", hasDynamicOffset: true }
191
- };
192
- }
193
- return entries;
194
- }
195
- function appendInjection(bgl, kind) {
196
- const start = bgl.length;
197
- switch (kind) {
198
- case "ibl":
199
- return [
200
- // binding start+0: irradianceMap (texture_cube)
201
- {
202
- binding: start,
203
- visibility: GPU_SHADER_STAGE_FRAGMENT,
204
- texture: { sampleType: "float", viewDimension: "cube" }
205
- },
206
- // binding start+1: irradianceSampler
207
- {
208
- binding: start + 1,
209
- visibility: GPU_SHADER_STAGE_FRAGMENT,
210
- sampler: { type: "filtering" }
211
- },
212
- // binding start+2: prefilterMap (texture_cube)
213
- {
214
- binding: start + 2,
215
- visibility: GPU_SHADER_STAGE_FRAGMENT,
216
- texture: { sampleType: "float", viewDimension: "cube" }
217
- },
218
- // binding start+3: prefilterSampler
219
- {
220
- binding: start + 3,
221
- visibility: GPU_SHADER_STAGE_FRAGMENT,
222
- sampler: { type: "filtering" }
223
- },
224
- // binding start+4: brdfLut (texture_2d)
225
- {
226
- binding: start + 4,
227
- visibility: GPU_SHADER_STAGE_FRAGMENT,
228
- texture: { sampleType: "float", viewDimension: "2d" }
229
- },
230
- // binding start+5: brdfLutSampler
231
- {
232
- binding: start + 5,
233
- visibility: GPU_SHADER_STAGE_FRAGMENT,
234
- sampler: { type: "filtering" }
235
- },
236
- // binding start+6: uniform { intensity: f32 }
237
- {
238
- binding: start + 6,
239
- visibility: GPU_SHADER_STAGE_FRAGMENT,
240
- buffer: { type: "uniform" }
241
- }
242
- ];
243
- case "lightmap":
244
- return [
245
- // emissive sampler + texture pair
246
- {
247
- binding: start,
248
- visibility: GPU_SHADER_STAGE_FRAGMENT,
249
- sampler: { type: "filtering" }
250
- },
251
- {
252
- binding: start + 1,
253
- visibility: GPU_SHADER_STAGE_FRAGMENT,
254
- texture: { sampleType: "float", viewDimension: "2d" }
255
- },
256
- // occlusion sampler + texture pair
257
- {
258
- binding: start + 2,
259
- visibility: GPU_SHADER_STAGE_FRAGMENT,
260
- sampler: { type: "filtering" }
261
- },
262
- {
263
- binding: start + 3,
264
- visibility: GPU_SHADER_STAGE_FRAGMENT,
265
- texture: { sampleType: "float", viewDimension: "2d" }
266
- }
267
- ];
268
- case "shadow":
269
- return [
270
- {
271
- binding: start,
272
- visibility: GPU_SHADER_STAGE_FRAGMENT,
273
- sampler: { type: "comparison" }
274
- },
275
- {
276
- binding: start + 1,
277
- visibility: GPU_SHADER_STAGE_FRAGMENT,
278
- texture: { sampleType: "depth", viewDimension: "2d" }
279
- }
280
- ];
281
- case "transmission":
282
- return [
283
- {
284
- binding: start,
285
- visibility: GPU_SHADER_STAGE_FRAGMENT,
286
- sampler: { type: "filtering" }
287
- },
288
- {
289
- binding: start + 1,
290
- visibility: GPU_SHADER_STAGE_FRAGMENT,
291
- texture: { sampleType: "float", viewDimension: "2d" }
292
- }
293
- ];
294
- }
295
- }
296
- function buildPbrViewBglEntries(caps) {
297
- const lightBufType = caps.storageBuffer ? "read-only-storage" : "uniform";
298
- const entries = [
299
- {
300
- binding: 0,
301
- visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
302
- // View captures use the same bind group with a per-face view slot.
303
- // Keeping this dynamic is also valid for the display slot at offset 0.
304
- buffer: { type: "uniform", hasDynamicOffset: true }
305
- },
306
- {
307
- binding: 1,
308
- visibility: GPU_SHADER_STAGE_FRAGMENT,
309
- buffer: { type: lightBufType }
310
- },
311
- {
312
- binding: 2,
313
- visibility: GPU_SHADER_STAGE_FRAGMENT,
314
- buffer: { type: lightBufType }
315
- },
316
- {
317
- binding: 3,
318
- visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
319
- texture: { sampleType: "depth", viewDimension: "2d" }
320
- },
321
- {
322
- binding: 4,
323
- visibility: GPU_SHADER_STAGE_FRAGMENT,
324
- sampler: { type: "comparison" }
325
- },
326
- // feat-20260612-point-light-shadows-urp-hdrp Round-2 F-1: point shadow
327
- // cube_array depth atlas. Bound to either the real ShadowAtlas
328
- // cube_array view (when point shadows are active) or a 1x1x6 fallback
329
- // cube_array view cleared to 1.0 (fully lit). Visibility is FRAGMENT
330
- // only -- the directional binding 3 is VERTEX|FRAGMENT for shadow
331
- // probe-debug sampling, but the cube atlas has no host-side probe path.
332
- {
333
- binding: 5,
334
- visibility: GPU_SHADER_STAGE_FRAGMENT,
335
- texture: { sampleType: "depth", viewDimension: "cube-array" }
336
- },
337
- // feat-20260612-point-light-shadows-urp-hdrp Round-2 F-1: point shadow
338
- // params UBO. Carries `array<vec4<f32>, 4>` = 64 B with one lane per
339
- // shadow-casting point light slot (shadowAtlasLayer in [0, 4)). Each
340
- // lane stores `(near, far, 1/(far-near), 0)` so the fragment-shader
341
- // depth-ref reconstruction (lighting-punctual.wgsl evalPointShadowed)
342
- // can avoid sampling LightSlot for the URP path. HDRP rides the same
343
- // constants on `LightSlot.kind_and_pad.zw` so binding 6 is unused on
344
- // HDRP shaders even though the BGL declares it (charter P4 single SSOT
345
- // BGL across pipelines; the HDRP variant simply doesn't reference the
346
- // binding in WGSL, which is allowed).
347
- {
348
- binding: 6,
349
- visibility: GPU_SHADER_STAGE_FRAGMENT,
350
- buffer: { type: "uniform" }
351
- },
352
- // feat-20260613-csm-cascaded-shadow-maps M5 / w28: shadowCasterCascade
353
- // UBO (16 B). Carries the 0-based cascade index of the shadow pass
354
- // currently being rasterized so `shadow_caster.wgsl` can index into
355
- // `view.lightViewProj_X` per cascade. Shifted from binding 5 to
356
- // binding 7 on 2026-06-13 to make room for point-shadow bindings 5/6
357
- // (they predate this feat in main; CSM's slot was the optimal-yield
358
- // give since point-shadow needs FRAGMENT-only and the cascade UBO
359
- // needs vertex-stage too — keeping cascade higher avoids interleaving
360
- // visibility flags within the contiguous shadow-binding cluster).
361
- {
362
- binding: 7,
363
- visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
364
- buffer: { type: "uniform" }
365
- },
366
- // feat-20260625-spot-light-shadow-mapping M3 / w14 (D-5): spot shadow
367
- // atlas. A single `texture_depth_2d` holding up to 4 spot shadows in a 2x2
368
- // tile grid (urp-pipeline.ts spotShadowDepth). FRAGMENT-only — the spot
369
- // shadow factor is reconstructed at fragment time via perspective-divide in
370
- // `evalSpotShadowed` (lighting-punctual.wgsl).
371
- //
372
- // ALWAYS-ON (no caps gate, unlike the point cube_array atlas at binding 5
373
- // which rides POINT_SHADOW_AVAILABLE): spot uses `texture_depth_2d` which is
374
- // compat-safe in every WebGPU profile, so the binding is unconditionally
375
- // declared. The matching WGSL declaration is `spotShadowMap` at @group(0)
376
- // binding 8 in common.wgsl (also unconditional) — the two must stay in
377
- // lock-step or WebGPU validation rejects the bind group at smoke time
378
- // (memory: BGL shape mismatch is a browser-path-only bug). No binding 9
379
- // sampler: spot reuses the comparison sampler at binding 4.
380
- // feat-20260625-spot-light-shadow-mapping M3 / w14 (D-5): spot shadow 2D
381
- // atlas, the LAST view-BG binding. The per-spot fragment-read perspective
382
- // lightViewProj matrices that w24 originally declared at a standalone
383
- // binding 9 uniform buffer were folded into the View UBO (binding 0,
384
- // `view.spotLightViewProj`) in w25 (scope-amend webkit-fallback): the
385
- // standalone binding pushed the WebGL2 fallback fragment uniform-buffer
386
- // count to 12, over GLES 3.0's `max_uniform_buffers_per_shader_stage = 11`,
387
- // crashing pipeline-layout creation on the compat path (this feat's target).
388
- {
389
- binding: 8,
390
- visibility: GPU_SHADER_STAGE_FRAGMENT,
391
- texture: { sampleType: "depth", viewDimension: "2d" }
392
- }
393
- ];
394
- if (caps.projectorAvailable !== false) {
395
- entries.push(
396
- {
397
- binding: 11,
398
- visibility: GPU_SHADER_STAGE_FRAGMENT,
399
- texture: { sampleType: "float", viewDimension: "2d" }
400
- },
401
- {
402
- binding: 12,
403
- visibility: GPU_SHADER_STAGE_FRAGMENT,
404
- sampler: { type: "filtering" }
405
- }
406
- );
407
- }
408
- entries.push({
409
- binding: 10,
410
- visibility: GPU_SHADER_STAGE_VERTEX,
411
- buffer: { type: "uniform", hasDynamicOffset: true }
412
- });
413
- return entries;
414
- }
415
- function buildPbrPipelineLayouts(device, caps) {
416
- const viewBglRes = device.createBindGroupLayout(
417
- buildBindGroupLayoutDescriptor(BGL_ONLY_SPEC_STUB, { kind: "pbr-view", caps })
418
- );
419
- if (!viewBglRes.ok) throw viewBglRes.error;
420
- const materialBglRes = device.createBindGroupLayout(
421
- buildBindGroupLayoutDescriptor(BGL_ONLY_SPEC_STUB, { kind: "pbr-material-merged" })
422
- );
423
- if (!materialBglRes.ok) throw materialBglRes.error;
424
- const meshArrayBglRes = device.createBindGroupLayout(
425
- buildBindGroupLayoutDescriptor(BGL_ONLY_SPEC_STUB, { kind: "pbr-mesh-array", caps })
426
- );
427
- if (!meshArrayBglRes.ok) throw meshArrayBglRes.error;
428
- const instancesBglRes = device.createBindGroupLayout(
429
- buildBindGroupLayoutDescriptor(BGL_ONLY_SPEC_STUB, { kind: "pbr-instances", caps })
430
- );
431
- if (!instancesBglRes.ok) throw instancesBglRes.error;
432
- const layouts = [
433
- viewBglRes.value,
434
- materialBglRes.value,
435
- meshArrayBglRes.value,
436
- instancesBglRes.value
437
- ];
438
- const pipelineLayoutRes = device.createPipelineLayout({
439
- label: "pbr-pl",
440
- bindGroupLayouts: layouts
441
- });
442
- if (!pipelineLayoutRes.ok) throw pipelineLayoutRes.error;
443
- return {
444
- pipelineLayout: pipelineLayoutRes.value,
445
- viewBgl: viewBglRes.value,
446
- materialBgl: materialBglRes.value,
447
- meshArrayBgl: meshArrayBglRes.value,
448
- instancesBgl: instancesBglRes.value,
449
- bindGroupLayouts: layouts
450
- };
451
- }
452
- var SKIN_MATERIAL_SHADER_ID = "forgeax::pbr-skin";
453
- var SHADOW_CASTER_SHADER_ID = "forgeax::default-shadow-caster";
454
- function shadowCasterVariantSet(storageBuffer, skinned) {
455
- const skinningDisabled = !skinned;
456
- if (storageBuffer && skinningDisabled) return "";
457
- return `SKINNING_DISABLED=${String(skinningDisabled)}+STORAGE_BUFFER_AVAILABLE=${String(storageBuffer)}`;
458
- }
459
- function resolvePipelineGroup2Contract(source) {
460
- const withoutComments = source.replace(/\/\*[\s\S]*?\*\//gu, "").replace(/\/\/.*$/gmu, "");
461
- const bindings = [
462
- ...withoutComments.matchAll(/@group\s*\(\s*2\s*\)\s*@binding\s*\(\s*(\d+)\s*\)/gu)
463
- ].map((match) => Number(match[1]));
464
- const hasSkinBindings = bindings.includes(1) || bindings.includes(2);
465
- const hasClusterBindings = bindings.some((binding) => binding >= 3);
466
- if (hasSkinBindings && hasClusterBindings) return "skin-cluster";
467
- if (hasSkinBindings) return "skin";
468
- if (hasClusterBindings) return "cluster";
469
- return "mesh";
470
- }
471
- function isStandardPbrMaterialShader(shaderId) {
472
- return shaderId === "forgeax::default-standard-pbr" || shaderId === SKIN_MATERIAL_SHADER_ID || shaderId === "forgeax::default-standard-pbr-skin";
473
- }
474
- function buildPbrSkinLayouts(device, caps, pbr) {
475
- const skinMeshArrayBglRes = device.createBindGroupLayout(
476
- buildBindGroupLayoutDescriptor(BGL_ONLY_SPEC_STUB, {
477
- kind: "pbr-skin-mesh-array",
478
- caps
479
- })
480
- );
481
- if (!skinMeshArrayBglRes.ok) throw skinMeshArrayBglRes.error;
482
- const layouts = [
483
- pbr.viewBgl,
484
- pbr.materialBgl,
485
- skinMeshArrayBglRes.value,
486
- pbr.instancesBgl
487
- ];
488
- const pipelineLayoutRes = device.createPipelineLayout({
489
- label: "pbr-skin-pl",
490
- bindGroupLayouts: layouts
491
- });
492
- if (!pipelineLayoutRes.ok) throw pipelineLayoutRes.error;
493
- return {
494
- pipelineLayout: pipelineLayoutRes.value,
495
- viewBgl: pbr.viewBgl,
496
- materialBgl: pbr.materialBgl,
497
- meshArrayBgl: skinMeshArrayBglRes.value,
498
- instancesBgl: pbr.instancesBgl,
499
- bindGroupLayouts: layouts
500
- };
501
- }
502
- function createPbrSkinMeshBindGroupEntries(meshBuffer, meshSize, paletteBuffer, paletteWindowBytes) {
503
- return [
504
- {
505
- binding: 0,
506
- resource: { kind: "buffer", value: { buffer: meshBuffer, offset: 0, size: meshSize } }
507
- },
508
- ...[1, 2].map((binding) => ({
509
- binding,
510
- resource: {
511
- kind: "buffer",
512
- value: {
513
- buffer: paletteBuffer,
514
- offset: 0,
515
- size: paletteWindowBytes
516
- }
517
- }
518
- }))
519
- ];
520
- }
521
- function pbrSkinMeshDynamicOffsets(meshOffset, paletteOffset) {
522
- return [meshOffset, paletteOffset, paletteOffset];
523
- }
524
- var SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET = "";
525
- function createHdrpBindGroupLayoutDescriptor() {
526
- const meshBufType = "read-only-storage";
527
- const clusterBufType = "read-only-storage";
528
- return {
529
- label: "hdrp-unified-bgl-group2",
530
- entries: [
531
- {
532
- binding: 0,
533
- // Standard fragment lighting (transmission/refraction and clustered
534
- // evaluation) reads the same mesh transform window as the vertex
535
- // stage. Keep this visibility in the HDRP descriptor aligned with
536
- // the URP mesh-array descriptor; restricting it to vertex makes the
537
- // HDRP pipeline invalid when fs_main accesses `meshes[0]`.
538
- visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
539
- buffer: { type: meshBufType, hasDynamicOffset: true }
540
- },
541
- // Bindings 1 and 2 stay absent for the URP physical isolation gap.
542
- {
543
- binding: 3,
544
- visibility: GPU_SHADER_STAGE_FRAGMENT,
545
- buffer: { type: clusterBufType, hasDynamicOffset: false }
546
- },
547
- {
548
- binding: 4,
549
- visibility: GPU_SHADER_STAGE_FRAGMENT,
550
- buffer: { type: clusterBufType, hasDynamicOffset: false }
551
- },
552
- {
553
- binding: 5,
554
- visibility: GPU_SHADER_STAGE_FRAGMENT,
555
- buffer: { type: clusterBufType, hasDynamicOffset: false }
556
- },
557
- {
558
- binding: 6,
559
- visibility: GPU_SHADER_STAGE_FRAGMENT,
560
- buffer: { type: "uniform", hasDynamicOffset: false }
561
- },
562
- {
563
- binding: 7,
564
- visibility: GPU_SHADER_STAGE_FRAGMENT,
565
- texture: { sampleType: "float", viewDimension: "2d", multisampled: false }
566
- },
567
- {
568
- binding: 8,
569
- visibility: GPU_SHADER_STAGE_FRAGMENT,
570
- sampler: { type: "filtering" }
571
- }
572
- ]
573
- };
574
- }
575
- function createHdrpSkinBindGroupLayoutDescriptor() {
576
- const base = createHdrpBindGroupLayoutDescriptor();
577
- const baseEntries = base.entries;
578
- if (baseEntries === void 0 || baseEntries[0] === void 0) {
579
- throw new Error("HDRP base group(2) layout must declare mesh binding 0");
580
- }
581
- const paletteType = "read-only-storage";
582
- return {
583
- label: "hdrp-skin-unified-bgl-group2",
584
- entries: [
585
- baseEntries[0],
586
- {
587
- binding: 1,
588
- visibility: GPU_SHADER_STAGE_VERTEX,
589
- buffer: { type: paletteType, hasDynamicOffset: true }
590
- },
591
- {
592
- binding: 2,
593
- visibility: GPU_SHADER_STAGE_VERTEX,
594
- buffer: { type: paletteType, hasDynamicOffset: true }
595
- },
596
- ...baseEntries.slice(1)
597
- ]
598
- };
599
- }
600
- function resolveMaterialParamSchema(spec, options) {
601
- if (options.materialParamSchema !== void 0) return options.materialParamSchema;
602
- if (options.registry !== void 0) {
603
- const lookup = options.registry.findMaterialArtifact(spec.shader.id);
604
- if (lookup.ok) return lookup.value.paramSchema;
605
- }
606
- return void 0;
607
- }
608
- function buildBindGroupLayoutDescriptor(spec, options) {
609
- switch (options.kind) {
610
- case "pbr-view": {
611
- const caps = options.caps ?? { storageBuffer: true };
612
- return {
613
- label: "pbr-view-bgl",
614
- entries: buildPbrViewBglEntries(caps)
615
- };
616
- }
617
- case "pbr-mesh-array": {
618
- const caps = options.caps ?? { storageBuffer: true };
619
- const meshBufType = caps.storageBuffer ? "read-only-storage" : "uniform";
620
- return {
621
- label: "pbr-mesh-array-bgl",
622
- entries: [
623
- {
624
- binding: 0,
625
- visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
626
- buffer: { type: meshBufType, hasDynamicOffset: true }
627
- }
628
- ]
629
- };
630
- }
631
- case "pbr-instances": {
632
- const caps = options.caps ?? { storageBuffer: true };
633
- const meshBufType = caps.storageBuffer ? "read-only-storage" : "uniform";
634
- return {
635
- label: "pbr-instances-bgl",
636
- entries: [
637
- {
638
- binding: 0,
639
- // Standard transmission reads the same per-instance transform in
640
- // fs_main to convert glTF unit-space thickness into world metres.
641
- // Keep one shared instances BGL and widen its visibility instead
642
- // of introducing a second transform upload or six new varyings.
643
- visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
644
- buffer: { type: meshBufType, hasDynamicOffset: false }
645
- }
646
- ]
647
- };
648
- }
649
- case "pbr-skin-mesh-array": {
650
- const caps = options.caps ?? { storageBuffer: true };
651
- const meshBufType = caps.storageBuffer ? "read-only-storage" : "uniform";
652
- return {
653
- label: "pbr-skin-mesh-array-bgl",
654
- entries: [
655
- {
656
- binding: 0,
657
- visibility: GPU_SHADER_STAGE_VERTEX,
658
- buffer: { type: meshBufType, hasDynamicOffset: true }
659
- },
660
- {
661
- binding: 1,
662
- visibility: GPU_SHADER_STAGE_VERTEX,
663
- buffer: { type: meshBufType, hasDynamicOffset: true }
664
- },
665
- {
666
- binding: 2,
667
- visibility: GPU_SHADER_STAGE_VERTEX,
668
- buffer: { type: meshBufType, hasDynamicOffset: true }
669
- }
670
- ]
671
- };
672
- }
673
- case "pbr-material-merged": {
674
- const resolvedSchema = resolveMaterialParamSchema(spec, options);
675
- const userRegion = buildPbrMaterialUserRegionEntries(resolvedSchema);
676
- const afterIbl = [...userRegion, ...appendInjection(userRegion, "ibl")];
677
- const merged = [...afterIbl, ...appendInjection(afterIbl, "transmission")];
678
- return {
679
- label: "pbr-material-skylight-bgl",
680
- entries: merged
681
- };
682
- }
683
- case "unlit-material": {
684
- const resolvedSchema = resolveMaterialParamSchema(spec, options);
685
- return {
686
- label: "unlit-material-bgl",
687
- entries: buildPbrMaterialUserRegionEntries(resolvedSchema)
688
- };
689
- }
690
- case "hdrp-7-slot": {
691
- const desc = createHdrpBindGroupLayoutDescriptor();
692
- return {
693
- label: desc.label ?? "hdrp-unified-bgl-group2",
694
- entries: [...desc.entries ?? []]
695
- };
696
- }
697
- case "fullscreen-post": {
698
- return {
699
- label: "fullscreen-post-bgl",
700
- entries: buildFullscreenPostInputEntries(spec)
701
- };
702
- }
703
- case "fullscreen-post-with-params": {
704
- return {
705
- label: "fullscreen-post-with-params-bgl",
706
- entries: [
707
- ...buildFullscreenPostInputEntries(spec),
708
- {
709
- binding: 2,
710
- visibility: GPU_SHADER_STAGE_FRAGMENT,
711
- buffer: { type: "uniform" }
712
- }
713
- ]
714
- };
715
- }
716
- case "fullscreen-post-with-scene-depth": {
717
- return {
718
- label: "fullscreen-post-with-scene-depth-bgl",
719
- entries: [
720
- ...buildFullscreenPostInputEntries(spec),
721
- {
722
- binding: 2,
723
- visibility: GPU_SHADER_STAGE_FRAGMENT,
724
- buffer: { type: "uniform" }
725
- },
726
- {
727
- binding: 3,
728
- visibility: GPU_SHADER_STAGE_FRAGMENT,
729
- texture: { sampleType: "depth", viewDimension: "2d" }
730
- },
731
- {
732
- binding: 4,
733
- visibility: GPU_SHADER_STAGE_FRAGMENT,
734
- sampler: { type: "non-filtering" }
735
- }
736
- ]
737
- };
738
- }
739
- case "fullscreen-post-with-scene-depth-msaa": {
740
- return {
741
- label: "fullscreen-post-with-scene-depth-msaa-bgl",
742
- entries: [
743
- ...buildFullscreenPostInputEntries(spec),
744
- {
745
- binding: 2,
746
- visibility: GPU_SHADER_STAGE_FRAGMENT,
747
- buffer: { type: "uniform" }
748
- },
749
- {
750
- binding: 3,
751
- visibility: GPU_SHADER_STAGE_FRAGMENT,
752
- texture: { sampleType: "depth", viewDimension: "2d", multisampled: true }
753
- },
754
- {
755
- binding: 4,
756
- visibility: GPU_SHADER_STAGE_FRAGMENT,
757
- sampler: { type: "non-filtering" }
758
- }
759
- ]
760
- };
761
- }
762
- }
763
- }
764
- function buildFullscreenPostInputEntries(spec) {
765
- const inputFormat = spec.attachments.depthFormat ?? spec.attachments.colorFormats[0];
766
- const sampleType = inputFormat === "depth32float" || inputFormat === "depth24plus" || inputFormat === "depth24plus-stencil8" || inputFormat === "depth16unorm" ? "depth" : inputFormat === "r32float" ? "unfilterable-float" : "float";
767
- return [
768
- {
769
- binding: 0,
770
- visibility: GPU_SHADER_STAGE_FRAGMENT,
771
- texture: { sampleType, viewDimension: "2d" }
772
- },
773
- {
774
- binding: 1,
775
- visibility: GPU_SHADER_STAGE_FRAGMENT,
776
- sampler: {
777
- type: sampleType === "depth" ? "comparison" : "filtering"
778
- }
779
- }
780
- ];
781
- }
782
164
  var DEFERRED_COLOR_FORMATS = [
783
165
  "rgba16float",
784
166
  "rgba8unorm",
@@ -880,12 +262,15 @@ function standardCapabilityVariantSet(clustered, storageBuffer, vertexColorAvail
880
262
  if (clustered && storageBuffer && vertexColorAvailable) return "";
881
263
  return `CLUSTER_FORWARD_AVAILABLE=${clustered}+STORAGE_BUFFER_AVAILABLE=${storageBuffer}`;
882
264
  }
883
- function standardTopologyVariantSet(topology, storageBuffer, vertexColorAvailable) {
884
- return standardCapabilityVariantSet(
265
+ function standardTopologyVariantSet(topology, storageBuffer, vertexColorAvailable, probeBlendAvailable = false) {
266
+ const capabilityVariantSet = standardCapabilityVariantSet(
885
267
  topology?.kind === "clustered",
886
268
  storageBuffer,
887
269
  vertexColorAvailable
888
270
  );
271
+ if (!probeBlendAvailable || !storageBuffer) return capabilityVariantSet;
272
+ const explicitCapabilityVariantSet = capabilityVariantSet === "" ? "CLUSTER_FORWARD_AVAILABLE=true+STORAGE_BUFFER_AVAILABLE=true" : capabilityVariantSet;
273
+ return `${explicitCapabilityVariantSet}+PROBE_BLEND_AVAILABLE=true`;
889
274
  }
890
275
  function standardTopologyBindGroupReady(topology, clusterBindGroup) {
891
276
  return topology?.kind !== "clustered" || clusterBindGroup !== null && clusterBindGroup !== void 0;
@@ -1454,10 +839,10 @@ function extractEntryResourceHandle(entry) {
1454
839
  }
1455
840
  return v;
1456
841
  }
842
+ var BIND_GROUP_CHAIN_LEAF_KEY = {};
1457
843
  function getOrCreateFromChain(root, handles, variant, factory, counts) {
1458
844
  let node = root;
1459
- for (let i = 0; i < handles.length - 1; i++) {
1460
- const h = handles[i];
845
+ for (const h of handles) {
1461
846
  let next = node.get(h);
1462
847
  if (next === void 0) {
1463
848
  next = /* @__PURE__ */ new WeakMap();
@@ -1465,11 +850,10 @@ function getOrCreateFromChain(root, handles, variant, factory, counts) {
1465
850
  }
1466
851
  node = next;
1467
852
  }
1468
- const last = handles[handles.length - 1];
1469
- let leaf = node.get(last);
853
+ let leaf = node.get(BIND_GROUP_CHAIN_LEAF_KEY);
1470
854
  if (leaf === void 0) {
1471
855
  leaf = /* @__PURE__ */ new Map();
1472
- node.set(last, leaf);
856
+ node.set(BIND_GROUP_CHAIN_LEAF_KEY, leaf);
1473
857
  }
1474
858
  const hit = leaf.get(variant);
1475
859
  if (hit !== void 0) return hit;
@@ -1525,52 +909,62 @@ function isMeshSsboDevMode() {
1525
909
  return true;
1526
910
  }
1527
911
  var _meshSsboScratch = new Uint8Array(0);
912
+ var _meshSsboScratchFloats = new Float32Array(0);
1528
913
  function uploadMeshSsboBatch(queue, meshStorageBuffer, validatedOrdered, foldDispatchPlan) {
1529
914
  const slotCount = validatedOrdered.length;
1530
915
  const neededBytes = slotCount * MESH_PER_ENTITY_STRIDE;
1531
916
  if (_meshSsboScratch.length < neededBytes) {
1532
917
  _meshSsboScratch = new Uint8Array(neededBytes);
918
+ _meshSsboScratchFloats = new Float32Array(_meshSsboScratch.buffer);
1533
919
  } else {
1534
920
  _meshSsboScratch.fill(0, 0, neededBytes);
1535
921
  }
1536
922
  const normalMatrixScratch = mat3.create();
923
+ const strideFloats = MESH_PER_ENTITY_STRIDE / Float32Array.BYTES_PER_ELEMENT;
1537
924
  for (let i = 0; i < slotCount; i++) {
1538
925
  const entry = validatedOrdered[i];
1539
926
  if (entry === void 0) continue;
1540
- const slot = new Float32Array(_meshSsboScratch.buffer, i * MESH_PER_ENTITY_STRIDE, 48);
927
+ const slot = i * strideFloats;
1541
928
  const isFoldHead = foldDispatchPlan?.headBuckets.has(i) === true;
1542
929
  if (isFoldHead) {
1543
- slot[0] = 1;
1544
- slot[5] = 1;
1545
- slot[10] = 1;
1546
- slot[15] = 1;
1547
- slot[16] = 1;
1548
- slot[20] = 1;
1549
- slot[24] = 1;
1550
- slot[28] = 1;
1551
- slot[33] = 1;
1552
- slot[38] = 1;
1553
- slot[43] = 1;
1554
- slot[44] = 1;
930
+ _meshSsboScratchFloats[slot] = 1;
931
+ _meshSsboScratchFloats[slot + 5] = 1;
932
+ _meshSsboScratchFloats[slot + 10] = 1;
933
+ _meshSsboScratchFloats[slot + 15] = 1;
934
+ _meshSsboScratchFloats[slot + 16] = 1;
935
+ _meshSsboScratchFloats[slot + 20] = 1;
936
+ _meshSsboScratchFloats[slot + 24] = 1;
937
+ _meshSsboScratchFloats[slot + 28] = 1;
938
+ _meshSsboScratchFloats[slot + 33] = 1;
939
+ _meshSsboScratchFloats[slot + 38] = 1;
940
+ _meshSsboScratchFloats[slot + 43] = 1;
941
+ _meshSsboScratchFloats[slot + 44] = 1;
1555
942
  } else {
1556
943
  const worldFromLocal = entry.source.transform.world;
1557
- for (let k = 0; k < 16; k++) slot[k] = worldFromLocal[k] ?? 0;
944
+ for (let k = 0; k < 16; k++) _meshSsboScratchFloats[slot + k] = worldFromLocal[k] ?? 0;
1558
945
  const normal = mat3.normalMatrix(normalMatrixScratch, worldFromLocal);
1559
- slot[16] = normal[0] ?? 0;
1560
- slot[17] = normal[1] ?? 0;
1561
- slot[18] = normal[2] ?? 0;
1562
- slot[20] = normal[3] ?? 0;
1563
- slot[21] = normal[4] ?? 0;
1564
- slot[22] = normal[5] ?? 0;
1565
- slot[24] = normal[6] ?? 0;
1566
- slot[25] = normal[7] ?? 0;
1567
- slot[26] = normal[8] ?? 0;
946
+ _meshSsboScratchFloats[slot + 16] = normal[0] ?? 0;
947
+ _meshSsboScratchFloats[slot + 17] = normal[1] ?? 0;
948
+ _meshSsboScratchFloats[slot + 18] = normal[2] ?? 0;
949
+ _meshSsboScratchFloats[slot + 20] = normal[3] ?? 0;
950
+ _meshSsboScratchFloats[slot + 21] = normal[4] ?? 0;
951
+ _meshSsboScratchFloats[slot + 22] = normal[5] ?? 0;
952
+ _meshSsboScratchFloats[slot + 24] = normal[6] ?? 0;
953
+ _meshSsboScratchFloats[slot + 25] = normal[7] ?? 0;
954
+ _meshSsboScratchFloats[slot + 26] = normal[8] ?? 0;
1568
955
  const previousWorld = entry.source.temporal?.previousTransform.world ?? worldFromLocal;
1569
- for (let k = 0; k < 16; k++) slot[28 + k] = previousWorld[k] ?? 0;
1570
- const reactiveMaterial = entry.source.materials.some(
1571
- (material) => material.transparent === true || material.materialShaderId === "forgeax::sprite" || material.materialShaderId === "forgeax::sprite-lit" || material.materialShaderId !== void 0 && !material.materialShaderId.startsWith("forgeax::")
1572
- );
1573
- slot[44] = entry.source.temporal?.reactive === true || reactiveMaterial ? 1 : 0;
956
+ for (let k = 0; k < 16; k++) {
957
+ _meshSsboScratchFloats[slot + 28 + k] = previousWorld[k] ?? 0;
958
+ }
959
+ let reactiveMaterial = false;
960
+ for (const material of entry.source.materials) {
961
+ const shader = material.materialShaderId;
962
+ if (material.transparent === true || shader === "forgeax::sprite" || shader === "forgeax::sprite-lit" || shader !== void 0 && !shader.startsWith("forgeax::")) {
963
+ reactiveMaterial = true;
964
+ break;
965
+ }
966
+ }
967
+ _meshSsboScratchFloats[slot + 44] = entry.source.temporal?.reactive === true || reactiveMaterial ? 1 : 0;
1574
968
  }
1575
969
  }
1576
970
  if (neededBytes > 0) {
@@ -1965,7 +1359,7 @@ function getOrCreateHdrpBuffers(runtime, grid = DEFAULT_CLUSTER_GRID) {
1965
1359
  }
1966
1360
  const device = runtime.device;
1967
1361
  const clusterUniformBytes = 32;
1968
- const lightDataBytes = MAX_LIGHTS * BYTES_PER_LIGHT_SLOT;
1362
+ const lightDataBytes = MAX_LIGHTS * BYTES_PER_DIRECT_LIGHT_SLOT;
1969
1363
  const clusterCells = grid.x * grid.y * grid.z;
1970
1364
  const clusterGridBytes = clusterCells * CLUSTER_GRID_STRIDE_U32 * 4;
1971
1365
  const lightIndexListBytes = LIGHT_INDEX_LIST_CAPACITY * 4;
@@ -2690,7 +2084,7 @@ function selectStandardClusterTransport(capabilities, prepared) {
2690
2084
  occupiedClusterCount: occupiedClusterCount(prepared),
2691
2085
  clusterGridBytes: prepared.layout.clusterGridU32Length * Uint32Array.BYTES_PER_ELEMENT,
2692
2086
  lightIndexListBytes: prepared.membershipEntryCount * Uint32Array.BYTES_PER_ELEMENT,
2693
- lightDataBytes: requested * 64,
2087
+ lightDataBytes: requested * BYTES_PER_DIRECT_LIGHT_SLOT,
2694
2088
  lightBoundsBytes: prepared.lightBounds.byteLength
2695
2089
  });
2696
2090
  }
@@ -2865,8 +2259,8 @@ function computeProjectionMatrix(camera) {
2865
2259
  proj,
2866
2260
  camera.orthoLeft,
2867
2261
  camera.orthoRight,
2868
- camera.orthoBottom,
2869
2262
  camera.orthoTop,
2263
+ camera.orthoBottom,
2870
2264
  camera.near,
2871
2265
  camera.far
2872
2266
  );
@@ -2939,12 +2333,9 @@ function writePointsLinesViewUbo(queue, buffer, camera, width, height, model, st
2939
2333
  const uploaded = queue.writeBuffer(buffer, byteOffset, payload);
2940
2334
  if (!uploaded.ok) throw uploaded.error;
2941
2335
  }
2942
- function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShadowSnapshots, fogOrTemporalOrOffset, temporal, projectorSpotIndex) {
2943
- const byteOffset = typeof fogOrTemporalOrOffset === "number" ? fogOrTemporalOrOffset : 0;
2944
- const fogOrTemporal = typeof fogOrTemporalOrOffset === "number" ? void 0 : fogOrTemporalOrOffset;
2945
- const isTemporal = fogOrTemporal !== void 0 && ("currentJitterUv" in fogOrTemporal || "currentUnjitteredViewProjection" in fogOrTemporal);
2946
- const fog = isTemporal ? void 0 : fogOrTemporal;
2947
- const resolvedTemporal = temporal ?? (isTemporal ? fogOrTemporal : void 0);
2336
+ function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShadowSnapshots, temporalOrOffset, projectorSpotIndex) {
2337
+ const byteOffset = typeof temporalOrOffset === "number" ? temporalOrOffset : 0;
2338
+ const resolvedTemporal = typeof temporalOrOffset === "number" ? void 0 : temporalOrOffset;
2948
2339
  const projMatrix = computeProjectionMatrix(camera);
2949
2340
  const viewMatrix = computeViewMatrix(camera);
2950
2341
  const unjitteredViewProjection = mat4.create();
@@ -2963,9 +2354,9 @@ function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShado
2963
2354
  const VIEW_PAYLOAD_FLOATS = 240;
2964
2355
  const viewPayload = new Float32Array(VIEW_PAYLOAD_FLOATS);
2965
2356
  for (let i = 0; i < 16; i++) viewPayload[i] = mainProjection[i] ?? 0;
2966
- viewPayload[16] = (light.direction[0] ?? 0) * light.intensity;
2967
- viewPayload[17] = (light.direction[1] ?? -1) * light.intensity;
2968
- viewPayload[18] = (light.direction[2] ?? 0) * light.intensity;
2357
+ viewPayload[16] = light.direction[0] ?? 0;
2358
+ viewPayload[17] = light.direction[1] ?? -1;
2359
+ viewPayload[18] = light.direction[2] ?? 0;
2969
2360
  viewPayload[20] = light.color[0] ?? 0;
2970
2361
  viewPayload[21] = light.color[1] ?? 0;
2971
2362
  viewPayload[22] = light.color[2] ?? 0;
@@ -2995,6 +2386,13 @@ function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShado
2995
2386
  viewPayload[228] = camera.near;
2996
2387
  viewPayload[229] = camera.far;
2997
2388
  viewPayload[230] = camera.projection === "orthographic" ? 1 : 0;
2389
+ const previousCameraPosition = resolvedTemporal !== void 0 && "previousCameraPosition" in resolvedTemporal ? resolvedTemporal.previousCameraPosition : void 0;
2390
+ if (previousCameraPosition !== void 0) {
2391
+ viewPayload[232] = previousCameraPosition[0] ?? 0;
2392
+ viewPayload[233] = previousCameraPosition[1] ?? 0;
2393
+ viewPayload[234] = previousCameraPosition[2] ?? 0;
2394
+ viewPayload[235] = 1;
2395
+ }
2998
2396
  if (lights.splitPlanes !== void 0) {
2999
2397
  for (let s = 0; s < 4; s++) {
3000
2398
  for (let lane = 0; lane < 4; lane++) {
@@ -3030,14 +2428,6 @@ function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShado
3030
2428
  }
3031
2429
  }
3032
2430
  }
3033
- if (fog !== void 0) {
3034
- viewPayload[232] = fog.color[0] ?? 0;
3035
- viewPayload[233] = fog.color[1] ?? 0;
3036
- viewPayload[234] = fog.color[2] ?? 0;
3037
- viewPayload[235] = fog.density;
3038
- viewPayload[236] = fog.heightFalloff;
3039
- viewPayload[237] = fog.maxOpacity;
3040
- }
3041
2431
  const viewUploadResult = queue.writeBuffer(viewUniformBuffer, byteOffset, viewPayload);
3042
2432
  if (!viewUploadResult.ok) throw viewUploadResult.error;
3043
2433
  for (const snapshot of lights.pointShadow ?? []) {
@@ -3112,6 +2502,36 @@ function buildPerFrameBindGroups(internals, frameState, pipelineState, hasValida
3112
2502
  const b5View = atlasViewMaybe !== null ? atlasViewMaybe : pipelineState.shadowAtlasFallbackTextureView;
3113
2503
  const graphSpotShadowView = graphTargets?.spotShadow;
3114
2504
  const b8View = graphSpotShadowView !== void 0 ? graphSpotShadowView : pipelineState.shadowFallbackTextureView;
2505
+ const extendedLighting = pipelineState.extendedLightingAvailable ?? true;
2506
+ const iesProfileTextureView = pipelineState.iesProfileTextureView;
2507
+ const cookieTextureView = pipelineState.cookieTextureView;
2508
+ const cookieMatrixBuffer = pipelineState.cookieMatrixBuffer;
2509
+ const ltcLambertTextureView = pipelineState.ltcLambertTextureView;
2510
+ const ltcGgxTextureView = pipelineState.ltcGgxTextureView;
2511
+ const extendedLightingResources = extendedLighting && iesProfileTextureView !== void 0 && cookieTextureView !== void 0 && cookieMatrixBuffer !== void 0 && ltcLambertTextureView !== void 0 && ltcGgxTextureView !== void 0 ? {
2512
+ sampler: pipelineState.defaultSampler,
2513
+ iesProfileTextureView,
2514
+ cookieTextureView,
2515
+ ltcLambertTextureView,
2516
+ ltcGgxTextureView,
2517
+ cookieMatrixBuffer
2518
+ } : void 0;
2519
+ if (extendedLighting && extendedLightingResources === void 0) {
2520
+ return {
2521
+ viewBindGroup: null,
2522
+ meshBindGroup: null,
2523
+ hdrpClusterBindGroup: null,
2524
+ hdrpClusterMembershipBindGroup
2525
+ };
2526
+ }
2527
+ const extendedLightingViews = extendedLightingResources !== void 0 ? [
2528
+ extendedLightingResources.sampler,
2529
+ extendedLightingResources.iesProfileTextureView,
2530
+ extendedLightingResources.cookieTextureView,
2531
+ extendedLightingResources.ltcLambertTextureView,
2532
+ extendedLightingResources.ltcGgxTextureView,
2533
+ extendedLightingResources.cookieMatrixBuffer
2534
+ ] : [];
3115
2535
  const projectorAvailable = pipelineState.projectorAvailable !== false;
3116
2536
  const b11View = graphTargets?.projector ?? pipelineState.defaultWhiteTextureView;
3117
2537
  const b12Sampler = graphTargets?.projectorSampler ?? pipelineState.defaultSampler;
@@ -3119,14 +2539,13 @@ function buildPerFrameBindGroups(internals, frameState, pipelineState, hasValida
3119
2539
  frameState.viewBindGroupCache,
3120
2540
  [
3121
2541
  pipelineState.viewUniformBuffer,
3122
- pipelineState.pointLightsBuffer,
3123
- pipelineState.spotLightsBuffer,
3124
2542
  b3View,
3125
2543
  shadowSampler,
3126
2544
  b5View,
3127
2545
  pipelineState.shadowParamsBuffer,
3128
2546
  b8View,
3129
- ...projectorAvailable ? [b11View, b12Sampler] : [],
2547
+ ...extendedLightingViews,
2548
+ ...extendedLightingResources === void 0 && projectorAvailable ? [b11View, b12Sampler] : [],
3130
2549
  pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer
3131
2550
  ],
3132
2551
  "view-main",
@@ -3139,20 +2558,6 @@ function buildPerFrameBindGroups(internals, frameState, pipelineState, hasValida
3139
2558
  value: { buffer: pipelineState.viewUniformBuffer, size: VIEW_UNIFORM_BYTES }
3140
2559
  }
3141
2560
  },
3142
- {
3143
- binding: 1,
3144
- resource: {
3145
- kind: "buffer",
3146
- value: { buffer: pipelineState.pointLightsBuffer }
3147
- }
3148
- },
3149
- {
3150
- binding: 2,
3151
- resource: {
3152
- kind: "buffer",
3153
- value: { buffer: pipelineState.spotLightsBuffer }
3154
- }
3155
- },
3156
2561
  {
3157
2562
  binding: 3,
3158
2563
  resource: {
@@ -3222,9 +2627,56 @@ function buildPerFrameBindGroups(internals, frameState, pipelineState, hasValida
3222
2627
  }
3223
2628
  }
3224
2629
  },
3225
- ...projectorAvailable ? [
2630
+ ...extendedLightingResources === void 0 && projectorAvailable ? [
3226
2631
  { binding: 11, resource: { kind: "textureView", value: b11View } },
3227
2632
  { binding: 12, resource: { kind: "sampler", value: b12Sampler } }
2633
+ ] : [],
2634
+ ...extendedLightingResources !== void 0 ? [
2635
+ {
2636
+ binding: 9,
2637
+ resource: {
2638
+ kind: "sampler",
2639
+ value: extendedLightingResources.sampler
2640
+ }
2641
+ },
2642
+ {
2643
+ binding: 11,
2644
+ resource: {
2645
+ kind: "textureView",
2646
+ value: extendedLightingResources.iesProfileTextureView
2647
+ }
2648
+ },
2649
+ {
2650
+ binding: 12,
2651
+ resource: {
2652
+ kind: "textureView",
2653
+ value: extendedLightingResources.cookieTextureView
2654
+ }
2655
+ },
2656
+ {
2657
+ binding: 13,
2658
+ resource: {
2659
+ kind: "textureView",
2660
+ value: extendedLightingResources.ltcLambertTextureView
2661
+ }
2662
+ },
2663
+ {
2664
+ binding: 14,
2665
+ resource: {
2666
+ kind: "textureView",
2667
+ value: extendedLightingResources.ltcGgxTextureView
2668
+ }
2669
+ },
2670
+ {
2671
+ binding: 15,
2672
+ resource: {
2673
+ kind: "buffer",
2674
+ value: {
2675
+ buffer: extendedLightingResources.cookieMatrixBuffer,
2676
+ size: COOKIE_MATRIX_BYTES
2677
+ }
2678
+ }
2679
+ }
3228
2680
  ] : []
3229
2681
  // feat-20260625-spot-light-shadow-mapping w25: the per-spot
3230
2682
  // fragment-read lightViewProj matrices fold into the View UBO
@@ -3334,13 +2786,14 @@ function prepareFrameLighting(internals, frameState, lights, camera, pipelineSta
3334
2786
  const directionalLight = lights.directional;
3335
2787
  const pointLights = lights.point;
3336
2788
  const spotLights = lights.spot;
2789
+ const rectLights = lights.rect;
3337
2790
  const light = directionalLight ?? {
3338
2791
  kind: "directional",
3339
2792
  direction: vec3.create(0, -1, 0),
3340
2793
  color: vec3.create(0, 0, 0),
3341
2794
  intensity: 0
3342
2795
  };
3343
- const totalLightCount = (directionalLight !== void 0 ? 1 : 0) + pointLights.length + spotLights.length;
2796
+ const totalLightCount = (directionalLight !== void 0 ? 1 : 0) + pointLights.length + spotLights.length + rectLights.length;
3344
2797
  const clusterGrid = frameState.installedPipelineConfig?.clusterGrid ?? DEFAULT_CLUSTER_GRID;
3345
2798
  const local = [
3346
2799
  ...pointLights.map((source) => ({
@@ -3356,6 +2809,13 @@ function prepareFrameLighting(internals, frameState, lights, camera, pipelineSta
3356
2809
  position: source.position,
3357
2810
  range: Number.isFinite(source.invRangeSquared) && source.invRangeSquared > 0 ? Math.sqrt(1 / source.invRangeSquared) : 1e3,
3358
2811
  source
2812
+ })),
2813
+ ...rectLights.map((source) => ({
2814
+ kind: "rect-area",
2815
+ shadowed: false,
2816
+ position: source.position,
2817
+ range: (Number.isFinite(source.invRangeSquared) && source.invRangeSquared > 0 ? Math.sqrt(1 / source.invRangeSquared) : 1e3) + Math.hypot(source.halfWidth, source.halfHeight),
2818
+ source
3359
2819
  }))
3360
2820
  ];
3361
2821
  const prepared = prepareStandardLighting({
@@ -3375,6 +2835,7 @@ function prepareFrameLighting(internals, frameState, lights, camera, pipelineSta
3375
2835
  light,
3376
2836
  pointLights,
3377
2837
  spotLights,
2838
+ rectLights,
3378
2839
  totalLightCount,
3379
2840
  standard: { kind: "no-local-lights", prepared: prepared.value }
3380
2841
  });
@@ -3392,10 +2853,96 @@ function prepareFrameLighting(internals, frameState, lights, camera, pipelineSta
3392
2853
  light,
3393
2854
  pointLights,
3394
2855
  spotLights,
2856
+ rectLights,
3395
2857
  totalLightCount,
3396
2858
  standard: { kind: "clustered", prepared: prepared.value, transport: transport.value }
3397
2859
  });
3398
2860
  }
2861
+ function modifierDigest(data) {
2862
+ let hash = 2166136261;
2863
+ for (let index = 0; index < data.length; index += 1) {
2864
+ hash ^= data[index] ?? 0;
2865
+ hash = Math.imul(hash, 16777619);
2866
+ }
2867
+ return hash >>> 0;
2868
+ }
2869
+ function writeSpotModifierTextures(internals, pipelineState, spots) {
2870
+ const iesTexture = pipelineState.iesProfileTexture;
2871
+ const cookieTexture = pipelineState.cookieTexture;
2872
+ const cookieMatrixBuffer = pipelineState.cookieMatrixBuffer;
2873
+ if (iesTexture === void 0 && cookieTexture === void 0 && cookieMatrixBuffer === void 0)
2874
+ return;
2875
+ let state = pipelineState.spotModifierUploadState;
2876
+ if (state === void 0) {
2877
+ state = { ies: /* @__PURE__ */ new Map(), cookie: /* @__PURE__ */ new Map(), cookieMatrix: /* @__PURE__ */ new Map() };
2878
+ pipelineState.spotModifierUploadState = state;
2879
+ } else if (state.cookieMatrix === void 0) {
2880
+ state = { ...state, cookieMatrix: /* @__PURE__ */ new Map() };
2881
+ pipelineState.spotModifierUploadState = state;
2882
+ }
2883
+ for (const spot of spots) {
2884
+ if (spot.iesProfileSlice !== void 0 && spot.iesProfileData !== void 0 && iesTexture) {
2885
+ if (spot.iesProfileData.byteLength === IES_SLICE_WIDTH * IES_SLICE_HEIGHT * 2) {
2886
+ const digest = modifierDigest(spot.iesProfileData);
2887
+ if (state.ies.get(spot.iesProfileSlice) !== digest) {
2888
+ const result = internals.device.queue.writeTexture(
2889
+ { texture: iesTexture, mipLevel: 0, origin: { x: 0, y: 0, z: spot.iesProfileSlice } },
2890
+ spot.iesProfileData,
2891
+ { offset: 0, bytesPerRow: IES_SLICE_WIDTH * 2, rowsPerImage: IES_SLICE_HEIGHT },
2892
+ { width: IES_SLICE_WIDTH, height: IES_SLICE_HEIGHT, depthOrArrayLayers: 1 }
2893
+ );
2894
+ if (!result.ok) throw result.error;
2895
+ state.ies.set(spot.iesProfileSlice, digest);
2896
+ }
2897
+ }
2898
+ }
2899
+ for (const modifier of [
2900
+ {
2901
+ slice: spot.cookieSlice,
2902
+ data: spot.cookieData,
2903
+ matrix: spot.cookieMatrix
2904
+ },
2905
+ {
2906
+ slice: spot.projectorSlice,
2907
+ data: spot.projectorData,
2908
+ matrix: spot.projectorMatrix
2909
+ }
2910
+ ]) {
2911
+ if (modifier.slice !== void 0 && modifier.data !== void 0 && cookieTexture) {
2912
+ if (modifier.data.byteLength === COOKIE_SLICE_SIZE * COOKIE_SLICE_SIZE * 4) {
2913
+ const digest = modifierDigest(modifier.data);
2914
+ if (state.cookie.get(modifier.slice) !== digest) {
2915
+ const result = internals.device.queue.writeTexture(
2916
+ { texture: cookieTexture, mipLevel: 0, origin: { x: 0, y: 0, z: modifier.slice } },
2917
+ modifier.data,
2918
+ { offset: 0, bytesPerRow: COOKIE_SLICE_SIZE * 4, rowsPerImage: COOKIE_SLICE_SIZE },
2919
+ { width: COOKIE_SLICE_SIZE, height: COOKIE_SLICE_SIZE, depthOrArrayLayers: 1 }
2920
+ );
2921
+ if (!result.ok) throw result.error;
2922
+ state.cookie.set(modifier.slice, digest);
2923
+ }
2924
+ }
2925
+ }
2926
+ if (modifier.slice !== void 0 && modifier.matrix !== void 0 && cookieMatrixBuffer) {
2927
+ const matrixBytes = new Uint8Array(
2928
+ modifier.matrix.buffer,
2929
+ modifier.matrix.byteOffset,
2930
+ modifier.matrix.byteLength
2931
+ );
2932
+ const digest = modifierDigest(matrixBytes);
2933
+ if (state.cookieMatrix.get(modifier.slice) !== digest) {
2934
+ const result = internals.device.queue.writeBuffer(
2935
+ cookieMatrixBuffer,
2936
+ modifier.slice * 16 * Float32Array.BYTES_PER_ELEMENT,
2937
+ modifier.matrix
2938
+ );
2939
+ if (!result.ok) throw result.error;
2940
+ state.cookieMatrix.set(modifier.slice, digest);
2941
+ }
2942
+ }
2943
+ }
2944
+ }
2945
+ }
3399
2946
  function writeShadowParamsBuffer(internals, frameState, pipelineState) {
3400
2947
  const SHADOW_PARAMS_LANE_COUNT = 4;
3401
2948
  const SHADOW_PARAMS_FLOATS_PER_LANE = 4;
@@ -3437,7 +2984,7 @@ function warnZeroLightStandard(frameState, renderables, skylight, totalLightCoun
3437
2984
  }
3438
2985
  }
3439
2986
  }
3440
- function writeHdrpClusterAndSsaoBuffers(internals, frameState, camera, prepared, transport, profilePhase, gpuMembershipPipelineReady = false, membershipBindGroupLayout = null, projectorLightSlotIndex) {
2987
+ function writeHdrpClusterAndSsaoBuffers(internals, frameState, camera, prepared, transport, profilePhase, gpuMembershipPipelineReady = false, membershipBindGroupLayout = null, projectorLightSlotIndex, pipelineState) {
3441
2988
  const configuredClusterGrid = prepared.layout.grid;
3442
2989
  const gpuMembership = transport.producer === "gpu";
3443
2990
  if (gpuMembership && (!gpuMembershipPipelineReady || membershipBindGroupLayout === null)) {
@@ -3485,6 +3032,13 @@ function writeHdrpClusterAndSsaoBuffers(internals, frameState, camera, prepared,
3485
3032
  clusterGridBuffer: hdrpBuffers.clusterGridBuffer
3486
3033
  };
3487
3034
  }
3035
+ if (pipelineState !== void 0) {
3036
+ const preparedSpots = [];
3037
+ for (const local of prepared.local) {
3038
+ if (local.source?.kind === "spot") preparedSpots.push(local.source);
3039
+ }
3040
+ writeSpotModifierTextures(internals, pipelineState, preparedSpots);
3041
+ }
3488
3042
  const gridX = prepared.layout.grid.x;
3489
3043
  const gridY = prepared.layout.grid.y;
3490
3044
  const gridZ = prepared.layout.grid.z;
@@ -3497,22 +3051,16 @@ function writeHdrpClusterAndSsaoBuffers(internals, frameState, camera, prepared,
3497
3051
  profilePhase,
3498
3052
  "record/scene-state/hdrp-cluster/payload-packing",
3499
3053
  () => {
3500
- const lightDataPayload = new Float32Array(prepared.layout.lightDataSlotCount * 16);
3054
+ const lightDataPayload = new Float32Array(
3055
+ prepared.layout.lightDataSlotCount * DIRECT_LIGHT_SLOT_FLOAT_COUNT
3056
+ );
3501
3057
  let slotIdx = 0;
3502
3058
  for (const local of prepared.local) {
3503
3059
  const source = local.source;
3504
3060
  if (source === void 0) return null;
3505
- const projectorSelected = projectorLightSlotIndex !== void 0 && projectorLightSlotIndex === slotIdx;
3506
- const packed = source.kind === "point" && source.shadowAtlasLayer !== void 0 && source.shadowAtlasLayer >= 0 && source.shadowNear !== void 0 && source.shadowFar !== void 0 ? packLightSlot(
3507
- source,
3508
- {
3509
- shadowAtlasLayer: source.shadowAtlasLayer,
3510
- near: source.shadowNear,
3511
- far: source.shadowFar
3512
- },
3513
- projectorSelected
3514
- ) : packLightSlot(source, void 0, projectorSelected);
3515
- lightDataPayload.set(packed, slotIdx * 16);
3061
+ const projectorSelected = projectorLightSlotIndex === void 0 ? void 0 : projectorLightSlotIndex === slotIdx;
3062
+ const packed = packDirectLightSlot(source, projectorSelected);
3063
+ lightDataPayload.set(packed, slotIdx * DIRECT_LIGHT_SLOT_FLOAT_COUNT);
3516
3064
  slotIdx += 1;
3517
3065
  }
3518
3066
  const clusterSsaoConfig = frameState.installedPipelineConfig?.ssao;
@@ -3526,7 +3074,10 @@ function writeHdrpClusterAndSsaoBuffers(internals, frameState, camera, prepared,
3526
3074
  );
3527
3075
  return {
3528
3076
  hdrpBuffers,
3529
- lightDataUploadPayload: lightDataPayload.subarray(0, slotIdx * 16),
3077
+ lightDataUploadPayload: lightDataPayload.subarray(
3078
+ 0,
3079
+ slotIdx * DIRECT_LIGHT_SLOT_FLOAT_COUNT
3080
+ ),
3530
3081
  clusterUniformPayload: new Uint8Array(clusterUniformPayload)
3531
3082
  };
3532
3083
  }
@@ -4055,10 +3606,15 @@ function iblPipelineError(stage, code, cause) {
4055
3606
  detail: { stage, ...{} }
4056
3607
  };
4057
3608
  }
4058
- function runIblPrecompute(opts) {
3609
+ async function runIblPrecompute(opts) {
4059
3610
  const { device, scope } = opts;
4060
3611
  const cache3 = getOrCreateIblCache(scope);
4061
3612
  const outputFormat = cache3.outputFormat ?? "rgba16float";
3613
+ const generation = scope.generation;
3614
+ const candidate = {};
3615
+ const fail = (stage, cause) => {
3616
+ return Promise.resolve(err(badAlloc(stage, cause)));
3617
+ };
4062
3618
  if (cache3.equirectToCubePipeline === void 0 || cache3.irradiancePipeline === void 0 || cache3.prefilterPipeline === void 0 || cache3.brdfLutPipeline === void 0) {
4063
3619
  return err({
4064
3620
  code: "ibl-precompute-not-dispatched",
@@ -4067,7 +3623,7 @@ function runIblPrecompute(opts) {
4067
3623
  detail: { stage: "pipeline-cache" }
4068
3624
  });
4069
3625
  }
4070
- if (cache3.irradianceTexture === void 0) {
3626
+ {
4071
3627
  const textureResult = device.createTexture({
4072
3628
  label: "ibl-irradiance-cube",
4073
3629
  size: { width: IRRADIANCE_SIZE, height: IRRADIANCE_SIZE, depthOrArrayLayers: 6 },
@@ -4079,15 +3635,15 @@ function runIblPrecompute(opts) {
4079
3635
  viewFormats: [],
4080
3636
  textureBindingViewDimension: void 0
4081
3637
  });
4082
- if (!textureResult.ok) return err(badAlloc("irradiance-texture", textureResult.error));
4083
- cache3.irradianceTexture = textureResult.value;
3638
+ if (!textureResult.ok) return fail("irradiance-texture", textureResult.error);
3639
+ candidate.irradianceTexture = adoptIblTexture(scope, device, textureResult.value);
4084
3640
  const cubeViewResult = device.createTextureView(textureResult.value, {
4085
3641
  label: "ibl-irradiance-cube-view",
4086
3642
  dimension: "cube",
4087
3643
  arrayLayerCount: 6
4088
3644
  });
4089
- if (!cubeViewResult.ok) return err(badAlloc("irradiance-view", cubeViewResult.error));
4090
- cache3.irradianceView = cubeViewResult.value;
3645
+ if (!cubeViewResult.ok) return fail("irradiance-view", cubeViewResult.error);
3646
+ candidate.irradianceView = cubeViewResult.value;
4091
3647
  const faceViews = [];
4092
3648
  for (let f = 0; f < 6; f++) {
4093
3649
  const viewResult = device.createTextureView(textureResult.value, {
@@ -4096,12 +3652,12 @@ function runIblPrecompute(opts) {
4096
3652
  baseArrayLayer: f,
4097
3653
  arrayLayerCount: 1
4098
3654
  });
4099
- if (!viewResult.ok) return err(badAlloc("irradiance-face-view", viewResult.error));
3655
+ if (!viewResult.ok) return fail("irradiance-face-view", viewResult.error);
4100
3656
  faceViews.push(viewResult.value);
4101
3657
  }
4102
- cache3.irradianceFaceViews = faceViews;
3658
+ candidate.irradianceFaceViews = faceViews;
4103
3659
  }
4104
- if (cache3.prefilterTexture === void 0) {
3660
+ {
4105
3661
  const textureResult = device.createTexture({
4106
3662
  label: "ibl-prefilter-cube",
4107
3663
  size: { width: PREFILTER_SIZE, height: PREFILTER_SIZE, depthOrArrayLayers: 6 },
@@ -4113,8 +3669,8 @@ function runIblPrecompute(opts) {
4113
3669
  viewFormats: [],
4114
3670
  textureBindingViewDimension: void 0
4115
3671
  });
4116
- if (!textureResult.ok) return err(badAlloc("prefilter-texture", textureResult.error));
4117
- cache3.prefilterTexture = textureResult.value;
3672
+ if (!textureResult.ok) return fail("prefilter-texture", textureResult.error);
3673
+ candidate.prefilterTexture = adoptIblTexture(scope, device, textureResult.value);
4118
3674
  const cubeViewResult = device.createTextureView(textureResult.value, {
4119
3675
  label: "ibl-prefilter-cube-view",
4120
3676
  dimension: "cube",
@@ -4122,8 +3678,8 @@ function runIblPrecompute(opts) {
4122
3678
  baseMipLevel: 0,
4123
3679
  mipLevelCount: PREFILTER_MIP_LEVELS
4124
3680
  });
4125
- if (!cubeViewResult.ok) return err(badAlloc("prefilter-view", cubeViewResult.error));
4126
- cache3.prefilterView = cubeViewResult.value;
3681
+ if (!cubeViewResult.ok) return fail("prefilter-view", cubeViewResult.error);
3682
+ candidate.prefilterView = cubeViewResult.value;
4127
3683
  const mipViews = [];
4128
3684
  for (let m = 0; m < PREFILTER_MIP_LEVELS; m++) {
4129
3685
  const faces = [];
@@ -4136,14 +3692,14 @@ function runIblPrecompute(opts) {
4136
3692
  baseArrayLayer: f,
4137
3693
  arrayLayerCount: 1
4138
3694
  });
4139
- if (!viewResult.ok) return err(badAlloc("prefilter-face-view", viewResult.error));
3695
+ if (!viewResult.ok) return fail("prefilter-face-view", viewResult.error);
4140
3696
  faces.push(viewResult.value);
4141
3697
  }
4142
3698
  mipViews.push(faces);
4143
3699
  }
4144
- cache3.prefilterFaceViewsByMip = mipViews;
3700
+ candidate.prefilterFaceViewsByMip = mipViews;
4145
3701
  }
4146
- if (cache3.brdfLutTexture === void 0) {
3702
+ {
4147
3703
  const textureResult = device.createTexture({
4148
3704
  label: "ibl-brdf-lut",
4149
3705
  size: { width: BRDF_LUT_SIZE, height: BRDF_LUT_SIZE, depthOrArrayLayers: 1 },
@@ -4155,14 +3711,14 @@ function runIblPrecompute(opts) {
4155
3711
  viewFormats: [],
4156
3712
  textureBindingViewDimension: void 0
4157
3713
  });
4158
- if (!textureResult.ok) return err(badAlloc("brdf-lut-texture", textureResult.error));
4159
- cache3.brdfLutTexture = textureResult.value;
3714
+ if (!textureResult.ok) return fail("brdf-lut-texture", textureResult.error);
3715
+ candidate.brdfLutTexture = adoptIblTexture(scope, device, textureResult.value);
4160
3716
  const viewResult = device.createTextureView(textureResult.value, {
4161
3717
  label: "ibl-brdf-lut-view",
4162
3718
  dimension: "2d"
4163
3719
  });
4164
- if (!viewResult.ok) return err(badAlloc("brdf-lut-view", viewResult.error));
4165
- cache3.brdfLutView = viewResult.value;
3720
+ if (!viewResult.ok) return fail("brdf-lut-view", viewResult.error);
3721
+ candidate.brdfLutView = viewResult.value;
4166
3722
  }
4167
3723
  const samplerResult = device.createSampler({
4168
3724
  label: "ibl-precompute-sampler",
@@ -4173,11 +3729,33 @@ function runIblPrecompute(opts) {
4173
3729
  addressModeV: "clamp-to-edge",
4174
3730
  addressModeW: "clamp-to-edge"
4175
3731
  });
4176
- if (!samplerResult.ok) return err(badAlloc("sampler", samplerResult.error));
3732
+ if (!samplerResult.ok) return fail("sampler", samplerResult.error);
4177
3733
  const sampler = samplerResult.value;
4178
- const encoderResult = device.createCommandEncoder({ label: "ibl-precompute-encoder" });
4179
- if (!encoderResult.ok) return err(badAlloc("encoder", encoderResult.error));
4180
- const encoder = encoderResult.value;
3734
+ let encoderResult = device.createCommandEncoder({
3735
+ label: "ibl-precompute-encoder-equirect-to-cube"
3736
+ });
3737
+ if (!encoderResult.ok) return fail("encoder-equirect-to-cube", encoderResult.error);
3738
+ let encoder = encoderResult.value;
3739
+ const submitStage = async (stage, createNextEncoder) => {
3740
+ const finishRes = encoder.finish();
3741
+ if (!finishRes.ok) return err(badAlloc(`${stage}-finish`, finishRes.error));
3742
+ const submitRes = device.queue.submit([finishRes.value]);
3743
+ if (!submitRes.ok) return err(badAlloc(`${stage}-submit`, submitRes.error));
3744
+ try {
3745
+ await device.queue.onSubmittedWorkDone();
3746
+ } catch {
3747
+ return err(badAlloc(`${stage}-fence`));
3748
+ }
3749
+ if (!scope.isAlive() || scope.generation !== generation) {
3750
+ return err(badAlloc(`${stage}-device-scope-generation`));
3751
+ }
3752
+ if (createNextEncoder) {
3753
+ encoderResult = device.createCommandEncoder({ label: `ibl-precompute-encoder-${stage}` });
3754
+ if (!encoderResult.ok) return err(badAlloc(`${stage}-next-encoder`, encoderResult.error));
3755
+ encoder = encoderResult.value;
3756
+ }
3757
+ return ok$1(true);
3758
+ };
4181
3759
  const faceUniformsBgl = cache3.faceUniformsBgl;
4182
3760
  const equirectGroup1Bgl = cache3.equirectGroup1Bgl;
4183
3761
  const cubeGroup1Bgl = cache3.cubeGroup1Bgl;
@@ -4186,11 +3764,16 @@ function runIblPrecompute(opts) {
4186
3764
  const irradiancePipeline = cache3.irradiancePipeline;
4187
3765
  const prefilterPipeline = cache3.prefilterPipeline;
4188
3766
  const brdfLutPipeline = cache3.brdfLutPipeline;
4189
- const irrFaceViews = cache3.irradianceFaceViews;
4190
- const prefMipViews = cache3.prefilterFaceViewsByMip;
4191
- const brdfLutView = cache3.brdfLutView;
4192
- if (faceUniformsBgl === void 0 || equirectGroup1Bgl === void 0 || cubeGroup1Bgl === void 0 || prefilterGroup0Bgl === void 0 || equirectPipeline === void 0 || irradiancePipeline === void 0 || prefilterPipeline === void 0 || brdfLutPipeline === void 0 || irrFaceViews === void 0 || prefMipViews === void 0 || brdfLutView === void 0) {
4193
- return err(badAlloc("ibl-cache-resources"));
3767
+ const candidateIrradianceTexture = candidate.irradianceTexture;
3768
+ const candidateIrradianceView = candidate.irradianceView;
3769
+ const irrFaceViews = candidate.irradianceFaceViews;
3770
+ const candidatePrefilterTexture = candidate.prefilterTexture;
3771
+ const candidatePrefilterView = candidate.prefilterView;
3772
+ const prefMipViews = candidate.prefilterFaceViewsByMip;
3773
+ const candidateBrdfLutTexture = candidate.brdfLutTexture;
3774
+ const brdfLutView = candidate.brdfLutView;
3775
+ if (faceUniformsBgl === void 0 || equirectGroup1Bgl === void 0 || cubeGroup1Bgl === void 0 || prefilterGroup0Bgl === void 0 || equirectPipeline === void 0 || irradiancePipeline === void 0 || prefilterPipeline === void 0 || brdfLutPipeline === void 0 || candidateIrradianceTexture === void 0 || candidateIrradianceView === void 0 || irrFaceViews === void 0 || candidatePrefilterTexture === void 0 || candidatePrefilterView === void 0 || prefMipViews === void 0 || candidateBrdfLutTexture === void 0 || brdfLutView === void 0) {
3776
+ return fail("ibl-cache-resources");
4194
3777
  }
4195
3778
  const equirectBgResult = device.createBindGroup({
4196
3779
  label: "ibl-equirect-bg",
@@ -4200,7 +3783,7 @@ function runIblPrecompute(opts) {
4200
3783
  { binding: 1, resource: { kind: "sampler", value: sampler } }
4201
3784
  ]
4202
3785
  });
4203
- if (!equirectBgResult.ok) return err(badAlloc("equirect-bg", equirectBgResult.error));
3786
+ if (!equirectBgResult.ok) return fail("equirect-bg", equirectBgResult.error);
4204
3787
  const equirectBg = equirectBgResult.value;
4205
3788
  const cubeBgResult = device.createBindGroup({
4206
3789
  label: "ibl-cube-bg",
@@ -4210,12 +3793,12 @@ function runIblPrecompute(opts) {
4210
3793
  { binding: 1, resource: { kind: "sampler", value: sampler } }
4211
3794
  ]
4212
3795
  });
4213
- if (!cubeBgResult.ok) return err(badAlloc("cube-bg", cubeBgResult.error));
3796
+ if (!cubeBgResult.ok) return fail("cube-bg", cubeBgResult.error);
4214
3797
  const cubeBg = cubeBgResult.value;
4215
3798
  const cubeFaceViews = opts.cubeFaceViews;
4216
3799
  for (let face = 0; face < 6; face++) {
4217
3800
  const cubeFaceView = cubeFaceViews[face];
4218
- if (cubeFaceView === void 0) return err(badAlloc("cube-face-view"));
3801
+ if (cubeFaceView === void 0) return fail("cube-face-view");
4219
3802
  const pass = encoder.beginRenderPass({
4220
3803
  label: "ibl-equirect-to-cube",
4221
3804
  colorAttachments: [
@@ -4244,7 +3827,7 @@ function runIblPrecompute(opts) {
4244
3827
  }
4245
3828
  ]
4246
3829
  });
4247
- if (!faceBgResult.ok) return err(badAlloc("face-bg", faceBgResult.error));
3830
+ if (!faceBgResult.ok) return fail("face-bg", faceBgResult.error);
4248
3831
  pass.setPipeline(equirectPipeline);
4249
3832
  pass.setBindGroup(0, faceBgResult.value);
4250
3833
  pass.setBindGroup(1, equirectBg);
@@ -4252,9 +3835,11 @@ function runIblPrecompute(opts) {
4252
3835
  pass.draw(6, 1, face * 6, 0);
4253
3836
  pass.end();
4254
3837
  }
3838
+ const cubeStage = await submitStage("equirect-to-cube", true);
3839
+ if (!cubeStage.ok) return err(cubeStage.error);
4255
3840
  for (let face = 0; face < 6; face++) {
4256
3841
  const irrFaceView = irrFaceViews[face];
4257
- if (irrFaceView === void 0) return err(badAlloc("irradiance-face-view"));
3842
+ if (irrFaceView === void 0) return fail("irradiance-face-view");
4258
3843
  const pass = encoder.beginRenderPass({
4259
3844
  label: "ibl-irradiance",
4260
3845
  colorAttachments: [
@@ -4283,21 +3868,23 @@ function runIblPrecompute(opts) {
4283
3868
  }
4284
3869
  ]
4285
3870
  });
4286
- if (!faceBgResult.ok) return err(badAlloc("irr-face-bg", faceBgResult.error));
3871
+ if (!faceBgResult.ok) return fail("irr-face-bg", faceBgResult.error);
4287
3872
  pass.setPipeline(irradiancePipeline);
4288
3873
  pass.setBindGroup(0, faceBgResult.value);
4289
3874
  pass.setBindGroup(1, cubeBg);
4290
3875
  pass.setVertexBuffer(0, opts.cubeVertexBuffer);
4291
3876
  pass.draw(6, 1, face * 6, 0);
4292
3877
  pass.end();
3878
+ const irradianceFaceStage = await submitStage(`irradiance-face-${face}`, true);
3879
+ if (!irradianceFaceStage.ok) return err(irradianceFaceStage.error);
4293
3880
  }
4294
3881
  for (let mip = 0; mip < PREFILTER_MIP_LEVELS; mip++) {
4295
3882
  const mipFaceViews = prefMipViews[mip];
4296
- if (mipFaceViews === void 0) return err(badAlloc("prefilter-mip-views"));
3883
+ if (mipFaceViews === void 0) return fail("prefilter-mip-views");
4297
3884
  for (let face = 0; face < 6; face++) {
4298
3885
  const subIdx = mip * 6 + face;
4299
3886
  const mipFaceView = mipFaceViews[face];
4300
- if (mipFaceView === void 0) return err(badAlloc("prefilter-face-view"));
3887
+ if (mipFaceView === void 0) return fail("prefilter-face-view");
4301
3888
  const pass = encoder.beginRenderPass({
4302
3889
  label: "ibl-prefilter",
4303
3890
  colorAttachments: [
@@ -4337,7 +3924,7 @@ function runIblPrecompute(opts) {
4337
3924
  }
4338
3925
  ]
4339
3926
  });
4340
- if (!bgResult.ok) return err(badAlloc("pref-bg", bgResult.error));
3927
+ if (!bgResult.ok) return fail("pref-bg", bgResult.error);
4341
3928
  pass.setPipeline(prefilterPipeline);
4342
3929
  pass.setBindGroup(0, bgResult.value);
4343
3930
  pass.setBindGroup(1, cubeBg);
@@ -4346,6 +3933,8 @@ function runIblPrecompute(opts) {
4346
3933
  pass.end();
4347
3934
  }
4348
3935
  }
3936
+ const prefilterStage = await submitStage("prefilter", true);
3937
+ if (!prefilterStage.ok) return err(prefilterStage.error);
4349
3938
  {
4350
3939
  const pass = encoder.beginRenderPass({
4351
3940
  label: "ibl-brdf-lut",
@@ -4362,16 +3951,37 @@ function runIblPrecompute(opts) {
4362
3951
  pass.draw(3, 1, 0, 0);
4363
3952
  pass.end();
4364
3953
  }
4365
- const finishRes = encoder.finish();
4366
- if (!finishRes.ok) return err(badAlloc("finish", finishRes.error));
4367
- const cmdBuffer = finishRes.value;
4368
- const submitRes = device.queue.submit([cmdBuffer]);
4369
- if (!submitRes.ok) return err(badAlloc("queue.submit", submitRes.error));
3954
+ const brdfStage = await submitStage("brdf-lut", false);
3955
+ if (!brdfStage.ok) return err(brdfStage.error);
3956
+ const promoted = {
3957
+ irradianceTexture: candidateIrradianceTexture,
3958
+ irradianceView: candidateIrradianceView,
3959
+ irradianceFaceViews: irrFaceViews,
3960
+ prefilterTexture: candidatePrefilterTexture,
3961
+ prefilterView: candidatePrefilterView,
3962
+ prefilterFaceViewsByMip: prefMipViews,
3963
+ brdfLutTexture: candidateBrdfLutTexture,
3964
+ brdfLutView
3965
+ };
3966
+ cache3.irradianceTexture = promoted.irradianceTexture;
3967
+ cache3.irradianceView = promoted.irradianceView;
3968
+ cache3.irradianceFaceViews = promoted.irradianceFaceViews;
3969
+ cache3.prefilterTexture = promoted.prefilterTexture;
3970
+ cache3.prefilterView = promoted.prefilterView;
3971
+ cache3.prefilterFaceViewsByMip = promoted.prefilterFaceViewsByMip;
3972
+ cache3.brdfLutTexture = promoted.brdfLutTexture;
3973
+ cache3.brdfLutView = promoted.brdfLutView;
4370
3974
  cache3.irradianceBakeCount += 1;
4371
3975
  cache3.prefilterBakeCount += 1;
4372
3976
  cache3.brdfLutBakeCount += 1;
4373
3977
  return ok$1({ submitted: true });
4374
3978
  }
3979
+ function adoptIblTexture(scope, device, texture) {
3980
+ scope._adopt("texture", texture, (value) => {
3981
+ device.destroyTexture(value);
3982
+ });
3983
+ return texture;
3984
+ }
4375
3985
  function badAlloc(stage, cause) {
4376
3986
  return {
4377
3987
  code: "ibl-precompute-not-dispatched",
@@ -4483,7 +4093,7 @@ var GpuTexture = class {
4483
4093
 
4484
4094
  // src/ibl/skylight-bind-group.ts
4485
4095
  var FALLBACK_BYTES_PER_ROW = 256;
4486
- function assembleMaterialWithSkylightEntries(materialEntries, skylight, transmission) {
4096
+ function assembleMaterialWithSkylightEntries(materialEntries, skylight, transmission, textureInjections = []) {
4487
4097
  const iblStart = materialEntries.length;
4488
4098
  const result = [
4489
4099
  ...materialEntries,
@@ -4529,6 +4139,22 @@ function assembleMaterialWithSkylightEntries(materialEntries, skylight, transmis
4529
4139
  resource: { kind: "textureView", value: transmissionView }
4530
4140
  }
4531
4141
  );
4142
+ const clearcoatStart = result.length;
4143
+ for (let index = 0; index < textureInjections.length; index += 1) {
4144
+ const resource = textureInjections[index];
4145
+ if (resource === void 0) continue;
4146
+ const binding = clearcoatStart + resource.slot * 2;
4147
+ result.push(
4148
+ {
4149
+ binding,
4150
+ resource: { kind: "sampler", value: resource.sampler }
4151
+ },
4152
+ {
4153
+ binding: binding + 1,
4154
+ resource: { kind: "textureView", value: resource.view }
4155
+ }
4156
+ );
4157
+ }
4532
4158
  return result;
4533
4159
  }
4534
4160
  function createSkylightFallback(device, queue) {
@@ -4792,53 +4418,8 @@ function deriveRenderDataCubemap(source) {
4792
4418
  cubeUsage: GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST | GPU_TEXTURE_USAGE_COPY_SRC
4793
4419
  });
4794
4420
  }
4795
-
4796
- // src/targets/material-source.ts
4797
- var sourceBindings = /* @__PURE__ */ new WeakMap();
4798
- function sourceToken() {
4799
- return Object.freeze({});
4800
- }
4801
- function resolveRenderTargetMaterialSource(source) {
4802
- return sourceBindings.get(source);
4803
- }
4804
- function invalid(field, value, expected) {
4805
- return {
4806
- ok: false,
4807
- error: new RenderTargetDescriptorInvalidError({ field, value, expected })
4808
- };
4809
- }
4810
- function createRenderTargetMaterialSource(target, descriptor, options) {
4811
- if (options.aspect !== "color") return invalid("aspect", options.aspect, "color");
4812
- if (options.dimension !== descriptor.shape) {
4813
- return invalid("dimension", options.dimension, `dimension matches ${descriptor.shape}`);
4814
- }
4815
- const mipCount = descriptor.mipLevels === 1 ? 1 : Math.floor(Math.log2(Math.max(descriptor.width, descriptor.height))) + 1;
4816
- if (!Number.isInteger(options.mipLevel) || options.mipLevel < 0 || options.mipLevel >= mipCount) {
4817
- return invalid("mipLevel", options.mipLevel, `an admitted mip level below ${mipCount}`);
4818
- }
4819
- if (!Number.isInteger(options.generation) || options.generation < 0) {
4820
- return invalid("generation", options.generation, "a non-negative device generation");
4821
- }
4822
- const result = {
4823
- ok: true,
4824
- value: Object.freeze({
4825
- source: sourceToken(),
4826
- target,
4827
- generation: options.generation,
4828
- shape: descriptor.shape,
4829
- format: descriptor.format,
4830
- view: Object.freeze({
4831
- dimension: options.dimension,
4832
- mipLevel: options.mipLevel,
4833
- resolveRequired: descriptor.sampleCount === 4
4834
- })
4835
- })
4836
- };
4837
- sourceBindings.set(result.value.source, result.value);
4838
- return result;
4839
- }
4840
- var STANDARD_PBR_UBO_SIZE = 384;
4841
- var MATERIAL_PER_ENTITY_STRIDE = 512;
4421
+ var STANDARD_PBR_UBO_SIZE = derive(DEFAULT_STANDARD_PBR_PARAM_SCHEMA).uboLayout.totalBytes;
4422
+ var MATERIAL_PER_ENTITY_STRIDE = Math.ceil(STANDARD_PBR_UBO_SIZE / 256) * 256;
4842
4423
  function resolveSurfaceProfile(storageFormat, viewFormat, facts) {
4843
4424
  if (facts.surfaceViewFormats) {
4844
4425
  return ok$1({
@@ -4997,17 +4578,64 @@ function isImageError(error) {
4997
4578
  const code = error.code;
4998
4579
  return typeof code === "string" && code.startsWith("image-");
4999
4580
  }
4581
+ function materialDiagnosticsEnabled() {
4582
+ if (typeof globalThis !== "object" || globalThis === null || !("process" in globalThis)) {
4583
+ return false;
4584
+ }
4585
+ const processValue = globalThis.process;
4586
+ return processValue?.env?.FORGEAX_MATERIAL_DIAGNOSTICS === "1";
4587
+ }
5000
4588
  function residentTextureView(world, store, runtime, handle, worldId = world) {
5001
4589
  const podRes = resolveAssetHandle(world, handle);
5002
- if (!podRes.ok) return void 0;
4590
+ const diagnosticsEnabled = materialDiagnosticsEnabled();
4591
+ if (!podRes.ok) {
4592
+ if (diagnosticsEnabled) {
4593
+ console.error(
4594
+ `[render-material] texture resolve failed: ${JSON.stringify({ handle: handleSlot(handle), error: podRes.error })}`
4595
+ );
4596
+ }
4597
+ return void 0;
4598
+ }
5003
4599
  const residentRes = store.ensureResident(handle, podRes.value, worldId);
5004
4600
  if (!residentRes.ok) {
5005
4601
  if (residentRes.error instanceof RhiError || isImageError(residentRes.error)) {
5006
4602
  runtime.errorRegistry.fire(residentRes.error);
5007
4603
  }
4604
+ if (diagnosticsEnabled) {
4605
+ console.error(
4606
+ `[render-material] texture residency failed: ${JSON.stringify({
4607
+ handle: handleSlot(handle),
4608
+ pod: {
4609
+ kind: podRes.value.kind,
4610
+ shape: podRes.value.shape,
4611
+ format: podRes.value.format,
4612
+ dataByteLength: podRes.value.data.byteLength,
4613
+ mips: podRes.value.mips
4614
+ },
4615
+ error: residentRes.error
4616
+ })}`
4617
+ );
4618
+ }
5008
4619
  return void 0;
5009
4620
  }
5010
- return store.getTextureGpuView(handle, worldId);
4621
+ const view = store.getTextureGpuView(handle, worldId);
4622
+ if (diagnosticsEnabled) {
4623
+ console.error(
4624
+ `[render-material] texture residency ready: ${JSON.stringify({
4625
+ handle: handleSlot(handle),
4626
+ pod: {
4627
+ kind: podRes.value.kind,
4628
+ shape: podRes.value.shape,
4629
+ format: podRes.value.format,
4630
+ dataByteLength: podRes.value.data.byteLength,
4631
+ mips: podRes.value.mips
4632
+ },
4633
+ receipt: "receipt" in residentRes.value ? residentRes.value.receipt : void 0,
4634
+ viewReady: view !== void 0
4635
+ })}`
4636
+ );
4637
+ }
4638
+ return view;
5011
4639
  }
5012
4640
  function residentSampler(world, store, runtime, handle, worldId = world) {
5013
4641
  const podRes = resolveAssetHandle(world, handle);
@@ -5074,24 +4702,12 @@ var BUILTIN_USER_REGION_TEXTURE_FIELDS = [
5074
4702
  "baseColorTexture",
5075
4703
  "metallicRoughnessTexture",
5076
4704
  "normalTexture",
5077
- "specularTintTexture",
5078
4705
  "emissiveTexture",
5079
4706
  "occlusionTexture",
5080
4707
  "transmissionTexture",
5081
4708
  "thicknessTexture"
5082
4709
  ];
5083
4710
  var LEGACY_MATERIAL_TEXTURE_SCALE_OFFSET = 80;
5084
- var STANDARD_PBR_TEXTURE_SCALE_OFFSET = 128;
5085
- var MATERIAL_TEXTURE_SCALE_FIELDS = [
5086
- "baseColorTexture",
5087
- "metallicRoughnessTexture",
5088
- "normalTexture",
5089
- "specularTintTexture",
5090
- "emissiveTexture",
5091
- "occlusionTexture",
5092
- "transmissionTexture",
5093
- "thicknessTexture"
5094
- ];
5095
4711
  var LEGACY_MATERIAL_TEXTURE_SCALE_FIELDS = [
5096
4712
  "baseColorTexture",
5097
4713
  "metallicRoughnessTexture",
@@ -5099,75 +4715,7 @@ var LEGACY_MATERIAL_TEXTURE_SCALE_FIELDS = [
5099
4715
  "emissiveTexture",
5100
4716
  "occlusionTexture"
5101
4717
  ];
5102
- var STANDARD_PBR_TEXTURE_COORDINATE_OFFSET = 128;
5103
- var STANDARD_PBR_TEXTURE_COORDINATE_STRIDE = 8;
5104
4718
  var DEFAULT_LEGACY_TEXTURE_SCALES = new Float32Array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
5105
- var DEFAULT_STANDARD_TEXTURE_COORDINATES = new Float32Array([
5106
- 0,
5107
- 0,
5108
- 1,
5109
- 1,
5110
- 0,
5111
- 0,
5112
- 1,
5113
- 1,
5114
- 0,
5115
- 0,
5116
- 1,
5117
- 1,
5118
- 0,
5119
- 0,
5120
- 1,
5121
- 1,
5122
- 0,
5123
- 0,
5124
- 1,
5125
- 1,
5126
- 0,
5127
- 0,
5128
- 1,
5129
- 1,
5130
- 0,
5131
- 0,
5132
- 1,
5133
- 1,
5134
- 0,
5135
- 0,
5136
- 1,
5137
- 1,
5138
- 0,
5139
- 0,
5140
- 1,
5141
- 1,
5142
- 0,
5143
- 0,
5144
- 1,
5145
- 1,
5146
- 0,
5147
- 0,
5148
- 1,
5149
- 1,
5150
- 0,
5151
- 0,
5152
- 1,
5153
- 1,
5154
- 0,
5155
- 0,
5156
- 1,
5157
- 1,
5158
- 0,
5159
- 0,
5160
- 1,
5161
- 1,
5162
- 0,
5163
- 0,
5164
- 1,
5165
- 1,
5166
- 0,
5167
- 0,
5168
- 1,
5169
- 1
5170
- ]);
5171
4719
  function materialTextureUvScale(texture) {
5172
4720
  if (texture === void 0) return [1, 1];
5173
4721
  return deriveTextureExtent(
@@ -5215,17 +4763,13 @@ function applyMaterialTextureUvScales(payload, material, world) {
5215
4763
  }
5216
4764
  return;
5217
4765
  }
5218
- const textureScaleOffset = isStandardPbrMaterialShader(material.materialShaderId) ? STANDARD_PBR_TEXTURE_SCALE_OFFSET : LEGACY_MATERIAL_TEXTURE_SCALE_OFFSET;
5219
- const isStandardPbr = textureScaleOffset === STANDARD_PBR_TEXTURE_SCALE_OFFSET;
4766
+ const textureScaleOffset = LEGACY_MATERIAL_TEXTURE_SCALE_OFFSET;
5220
4767
  const hasTextureMetadata = material.textureCoordinates !== void 0 || material.textureHandles !== void 0 || material.videoTextureFields !== void 0 || material.baseColorTexture !== void 0 || material.metallicRoughnessTexture !== void 0 || material.normalTexture !== void 0 || material.emissiveTexture !== void 0 || material.occlusionTexture !== void 0;
5221
4768
  if (!hasTextureMetadata) {
5222
- f32.set(
5223
- isStandardPbr ? DEFAULT_STANDARD_TEXTURE_COORDINATES : DEFAULT_LEGACY_TEXTURE_SCALES,
5224
- textureScaleOffset / 4
5225
- );
4769
+ f32.set(DEFAULT_LEGACY_TEXTURE_SCALES, textureScaleOffset / 4);
5226
4770
  return;
5227
4771
  }
5228
- const fields = isStandardPbr ? MATERIAL_TEXTURE_SCALE_FIELDS : LEGACY_MATERIAL_TEXTURE_SCALE_FIELDS;
4772
+ const fields = LEGACY_MATERIAL_TEXTURE_SCALE_FIELDS;
5229
4773
  for (let index = 0; index < fields.length; index++) {
5230
4774
  const field = fields[index];
5231
4775
  if (field === void 0) continue;
@@ -5233,32 +4777,16 @@ function applyMaterialTextureUvScales(payload, material, world) {
5233
4777
  const resolved = handle === void 0 ? void 0 : resolveAssetHandle(world, handle);
5234
4778
  const texture = resolved?.ok === true ? resolved.value : void 0;
5235
4779
  const [u, v] = materialTextureUvScale(texture);
5236
- if (isStandardPbr) {
5237
- const coordinates = material.textureCoordinates?.get(field);
5238
- const transform = coordinates?.transform;
5239
- const offset = STANDARD_PBR_TEXTURE_COORDINATE_OFFSET / 4 + index * STANDARD_PBR_TEXTURE_COORDINATE_STRIDE;
5240
- f32[offset] = transform?.offset?.[0] ?? 0;
5241
- f32[offset + 1] = transform?.offset?.[1] ?? 0;
5242
- f32[offset + 2] = transform?.scale?.[0] ?? 1;
5243
- f32[offset + 3] = transform?.scale?.[1] ?? 1;
5244
- f32[offset + 4] = coordinates?.set ?? 0;
5245
- f32[offset + 5] = transform?.rotation ?? 0;
5246
- f32[offset + 6] = u;
5247
- f32[offset + 7] = v;
5248
- } else {
5249
- const offset = textureScaleOffset / 4 + index * 2;
5250
- f32[offset] = u;
5251
- f32[offset + 1] = v;
5252
- }
4780
+ const offset = textureScaleOffset / 4 + index * 2;
4781
+ f32[offset] = u;
4782
+ f32[offset + 1] = v;
5253
4783
  }
5254
4784
  }
5255
4785
  function materialCoordinateSchema(material) {
5256
- if (isStandardPbrMaterialShader(material.materialShaderId) && material.paramSnapshot !== void 0) {
5257
- return DEFAULT_STANDARD_PBR_PARAM_SCHEMA;
5258
- }
5259
4786
  if (material.materialParamSchema !== void 0 && material.materialParamSchema.length > 0) {
5260
4787
  return material.materialParamSchema;
5261
4788
  }
4789
+ if (isStandardPbrMaterialShader(material.materialShaderId)) return STANDARD_PIPELINE_PARAM_SCHEMA;
5262
4790
  switch (material.materialShaderId) {
5263
4791
  case "forgeax::default-unlit":
5264
4792
  return DEFAULT_UNLIT_PARAM_SCHEMA;
@@ -5283,6 +4811,9 @@ function defaultViewForUserRegionField(field, pipelineState, schema) {
5283
4811
  }
5284
4812
  if (field === "normalTexture") return pipelineState.defaultNormalTextureView;
5285
4813
  if (field === "baseColorTexture") return pipelineState.fallbackTextureView;
4814
+ if (field === "anisotropyTexture") {
4815
+ return pipelineState.defaultAnisotropyTextureView ?? pipelineState.defaultNormalTextureView;
4816
+ }
5286
4817
  return pipelineState.defaultWhiteTextureView;
5287
4818
  }
5288
4819
  function detectNineSliceScaleTooSmall(transformWorld, slices, renderableIndex, seenIndices, metrics) {
@@ -5307,58 +4838,54 @@ function writePbrMaterialUboPayload(buf, material) {
5307
4838
  }
5308
4839
  buf.fill(0);
5309
4840
  const f32 = materialUboFloatView(buf);
5310
- f32[0] = material.baseColor[0] ?? 0;
5311
- f32[1] = material.baseColor[1] ?? 0;
5312
- f32[2] = material.baseColor[2] ?? 0;
5313
- f32[3] = 1;
5314
- f32[4] = material.metallic;
5315
- f32[5] = material.roughness;
5316
- f32[6] = 2;
5317
- f32[7] = 1;
5318
- f32[8] = 0;
5319
- f32[9] = 0;
5320
- const em = material.emissive;
5321
- f32[12] = em?.[0] ?? 0;
5322
- f32[13] = em?.[1] ?? 0;
5323
- f32[14] = em?.[2] ?? 0;
5324
- f32[15] = material.emissiveIntensity ?? 0;
5325
- f32[16] = material.occlusionStrength ?? 1;
5326
- const alphaCutoff = material.paramSnapshot?.alphaCutoff;
5327
- f32[17] = typeof alphaCutoff === "number" ? alphaCutoff : 0;
5328
- f32[18] = material.clearcoat ?? 0;
5329
- f32[19] = material.clearcoatRoughness ?? 0.5;
5330
- const specularTint = material.specularTint;
5331
- f32[20] = specularTint?.[0] ?? 1;
5332
- f32[21] = specularTint?.[1] ?? 1;
5333
- f32[22] = specularTint?.[2] ?? 1;
5334
- const snapshotNumber = (name, fallback) => {
5335
- const value = material.paramSnapshot?.[name];
5336
- return typeof value === "number" ? value : fallback;
5337
- };
5338
- const snapshotVec3 = (name, fallback) => {
5339
- const value = material.paramSnapshot?.[name];
5340
- return Array.isArray(value) ? [value[0] ?? fallback[0], value[1] ?? fallback[1], value[2] ?? fallback[2]] : fallback;
4841
+ const schema = material.materialParamSchema === void 0 || material.materialParamSchema.length === 0 ? STANDARD_PIPELINE_PARAM_SCHEMA : material.materialParamSchema;
4842
+ const snapshot = material.paramSnapshot;
4843
+ const fallback = (name, entry) => {
4844
+ switch (name) {
4845
+ case "baseColor":
4846
+ return [
4847
+ material.baseColor[0] ?? 1,
4848
+ material.baseColor[1] ?? 1,
4849
+ material.baseColor[2] ?? 1,
4850
+ 1
4851
+ ];
4852
+ case "metallic":
4853
+ return material.metallic;
4854
+ case "roughness":
4855
+ return material.roughness;
4856
+ case "specularColor":
4857
+ return material.specularColor ?? [1, 1, 1];
4858
+ case "normalScale":
4859
+ return material.normalScale ?? 1;
4860
+ case "emissive":
4861
+ return material.emissive ?? [0, 0, 0];
4862
+ case "emissiveIntensity":
4863
+ return material.emissiveIntensity ?? 0;
4864
+ case "occlusionStrength":
4865
+ return material.occlusionStrength ?? 1;
4866
+ default:
4867
+ return entry.default;
4868
+ }
5341
4869
  };
5342
- f32[23] = material.normalScale ?? snapshotNumber("normalScale", 1);
5343
- f32[24] = snapshotNumber("transmission", 0);
5344
- f32[25] = snapshotNumber("ior", 1.5);
5345
- f32[26] = snapshotNumber("thickness", 0);
5346
- const attenuationColor = snapshotVec3("attenuationColor", [1, 1, 1]);
5347
- f32[28] = attenuationColor[0];
5348
- f32[29] = attenuationColor[1];
5349
- f32[30] = attenuationColor[2];
5350
- f32[31] = snapshotNumber("attenuationDistance", 0);
5351
- const paramSnap = material.paramSnapshot;
5352
- if (paramSnap !== void 0) {
5353
- material.materialShaderId;
4870
+ for (const entry of derivedParamSchema(schema).uboLayout.entries) {
4871
+ const value = snapshot?.[entry.name] ?? fallback(entry.name, schema.find((item) => item.name === entry.name) ?? entry);
4872
+ const offset = entry.offset / 4;
4873
+ const width = entry.size / 4;
4874
+ if (typeof value === "number") {
4875
+ f32[offset] = value;
4876
+ } else if (Array.isArray(value)) {
4877
+ for (let index = 0; index < Math.min(width, value.length); index += 1) {
4878
+ const component = value[index];
4879
+ if (typeof component === "number") f32[offset + index] = component;
4880
+ }
4881
+ }
5354
4882
  }
5355
4883
  }
5356
4884
  function applyParamSnapshotToUbo(payload, paramSchema, paramSnapshot) {
5357
4885
  if (paramSchema === void 0) return;
5358
4886
  if (paramSnapshot === void 0) return;
5359
4887
  const f32 = materialUboFloatView(payload);
5360
- const effectiveSchema = Object.hasOwn(paramSnapshot, "transmission") && Object.hasOwn(paramSnapshot, "ior") && Object.hasOwn(paramSnapshot, "thickness") ? DEFAULT_STANDARD_PBR_PARAM_SCHEMA : paramSchema;
5361
- const { uboLayout } = derivedParamSchema(effectiveSchema);
4888
+ const { uboLayout } = derivedParamSchema(paramSchema);
5362
4889
  for (const entry of uboLayout.entries) {
5363
4890
  const value = paramSnapshot[entry.name];
5364
4891
  if (value === void 0) continue;
@@ -5378,6 +4905,36 @@ function applyParamSnapshotToUbo(payload, paramSchema, paramSnapshot) {
5378
4905
  }
5379
4906
  }
5380
4907
  }
4908
+ function recordIblMaterialBinding(frameState, materialBgl, bindGroup, entries, iblStart, cache3) {
4909
+ const receipt = frameState.iblBindingInspection;
4910
+ if (receipt === void 0) return;
4911
+ const skylightBindingStart = entries.findIndex((entry) => entry.binding === iblStart);
4912
+ if (skylightBindingStart < 0) return;
4913
+ const iblEntries = entries.slice(skylightBindingStart, skylightBindingStart + 7);
4914
+ const projected = [];
4915
+ for (const entry of iblEntries) {
4916
+ if (entry.resource.kind === "externalTexture") continue;
4917
+ const value = entry.resource.kind === "buffer" ? entry.resource.value.buffer : entry.resource.value;
4918
+ if (typeof value !== "object" || value === null) continue;
4919
+ projected.push({
4920
+ binding: entry.binding,
4921
+ kind: entry.resource.kind,
4922
+ resourceIdentity: getOpaqueResourceIdentity(value)
4923
+ });
4924
+ }
4925
+ frameState.iblBindingInspection = {
4926
+ ...receipt,
4927
+ material: {
4928
+ bindGroupIdentity: getOpaqueResourceIdentity(bindGroup),
4929
+ materialBglIdentity: getOpaqueResourceIdentity(materialBgl),
4930
+ cache: cache3,
4931
+ skylightBindingStart: iblEntries[0]?.binding ?? 0,
4932
+ entries: projected,
4933
+ reflectionBindings: projected.map((entry) => entry.binding)
4934
+ },
4935
+ errors: []
4936
+ };
4937
+ }
5381
4938
  function isMaterialBgAssemblyCacheHit(cached, material, materialBgl, materialBuffer, skylightResources, materialResourceEpoch, transmissionBackdropView) {
5382
4939
  if (transmissionBackdropView != null) return false;
5383
4940
  return cached?.material === material && cached.materialResourceEpoch === materialResourceEpoch && cached.materialBgl === materialBgl && cached.materialBuffer === materialBuffer && cached.skylightResources.irradianceView === skylightResources.irradianceView && cached.skylightResources.irradianceSampler === skylightResources.irradianceSampler && cached.skylightResources.prefilterView === skylightResources.prefilterView && cached.skylightResources.prefilterSampler === skylightResources.prefilterSampler && cached.skylightResources.brdfLutView === skylightResources.brdfLutView && cached.skylightResources.brdfLutSampler === skylightResources.brdfLutSampler && cached.skylightResources.intensityBuffer === skylightResources.intensityBuffer;
@@ -5395,16 +4952,22 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5395
4952
  materialBgShared,
5396
4953
  materialBgAssemblyCache,
5397
4954
  transmissionBackdropView,
5398
- bindGroupCounts
4955
+ bindGroupCounts,
4956
+ frameState
5399
4957
  } = deps;
5400
4958
  const effectiveSkylightResources = resolveReflectionProbeResources?.(materialWorld, entityKey) ?? skylightResources;
5401
4959
  const smMaterialHandle = submeshMaterial.materialHandle;
5402
4960
  const materialCacheKey = smMaterialHandle === void 0 ? void 0 : `${materialWorld.identity}:${smMaterialHandle}`;
5403
4961
  const smHasVideoFields = (submeshMaterial.videoTextureFields?.size ?? 0) > 0;
5404
4962
  const smShaderId = submeshMaterial.materialShaderId;
5405
- const smPerShaderBgl = smShaderId !== void 0 ? runtime.getMaterialBindGroupLayout?.(smShaderId) : void 0;
4963
+ const smSchema = (smShaderId !== void 0 ? runtime.getParamSchema?.(smShaderId) : void 0) ?? submeshMaterial.materialParamSchema;
4964
+ const smPerShaderBgl = smShaderId !== void 0 ? runtime.getMaterialBindGroupLayout?.(smShaderId, smSchema) : void 0;
5406
4965
  const smMaterialBgl = smPerShaderBgl ?? pipelineState.materialBindGroupLayout;
4966
+ const standardMaterial = isStandardPbrMaterialShader(smShaderId);
4967
+ const physicalFields = standardMaterial && smSchema !== void 0 ? physicalTextureFields(smSchema) : [];
4968
+ const smUserRegionFields = smPerShaderBgl === void 0 || isCanonicalStandardPbrMaterialShader(smShaderId) ? BUILTIN_USER_REGION_TEXTURE_FIELDS : userRegionTextureFieldOrder(smSchema).filter((field) => !physicalFields.includes(field));
5407
4969
  const hasActiveTransmissionBackdrop = transmissionBackdropView != null;
4970
+ const diagnosticsEnabled = materialDiagnosticsEnabled();
5408
4971
  if (smMaterialHandle !== void 0 && !smHasVideoFields && !hasActiveTransmissionBackdrop) {
5409
4972
  const cached = materialCacheKey === void 0 ? void 0 : materialBgAssemblyCache.get(materialCacheKey);
5410
4973
  if (isMaterialBgAssemblyCacheHit(
@@ -5416,11 +4979,48 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5416
4979
  store.materialResourceEpoch,
5417
4980
  transmissionBackdropView
5418
4981
  )) {
4982
+ if (diagnosticsEnabled) {
4983
+ recordIblMaterialBinding(
4984
+ frameState,
4985
+ smMaterialBgl,
4986
+ cached.bindGroup,
4987
+ [
4988
+ {
4989
+ binding: 1 + smUserRegionFields.length * 2,
4990
+ resource: { kind: "textureView", value: skylightResources.irradianceView }
4991
+ },
4992
+ {
4993
+ binding: 2 + smUserRegionFields.length * 2,
4994
+ resource: { kind: "sampler", value: skylightResources.irradianceSampler }
4995
+ },
4996
+ {
4997
+ binding: 3 + smUserRegionFields.length * 2,
4998
+ resource: { kind: "textureView", value: skylightResources.prefilterView }
4999
+ },
5000
+ {
5001
+ binding: 4 + smUserRegionFields.length * 2,
5002
+ resource: { kind: "sampler", value: skylightResources.prefilterSampler }
5003
+ },
5004
+ {
5005
+ binding: 5 + smUserRegionFields.length * 2,
5006
+ resource: { kind: "textureView", value: skylightResources.brdfLutView }
5007
+ },
5008
+ {
5009
+ binding: 6 + smUserRegionFields.length * 2,
5010
+ resource: { kind: "sampler", value: skylightResources.brdfLutSampler }
5011
+ },
5012
+ {
5013
+ binding: 7 + smUserRegionFields.length * 2,
5014
+ resource: { kind: "buffer", value: { buffer: skylightResources.intensityBuffer } }
5015
+ }
5016
+ ],
5017
+ 1 + smUserRegionFields.length * 2,
5018
+ "hit"
5019
+ );
5020
+ }
5419
5021
  return cached.bindGroup;
5420
5022
  }
5421
5023
  }
5422
- const smSchema = submeshMaterial.materialParamSchema ?? (smShaderId !== void 0 ? runtime.getParamSchema?.(smShaderId) : void 0);
5423
- const smUserRegionFields = smPerShaderBgl === void 0 || isStandardPbrMaterialShader(smShaderId) ? BUILTIN_USER_REGION_TEXTURE_FIELDS : userRegionTextureFieldOrder(smSchema);
5424
5024
  let materialResourcesResident = true;
5425
5025
  const smSamplerForField = (field) => {
5426
5026
  const handle = field === void 0 ? void 0 : submeshMaterial.samplerHandles?.get(field);
@@ -5486,6 +5086,33 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5486
5086
  }
5487
5087
  );
5488
5088
  }
5089
+ const physicalTextureInjections = [];
5090
+ for (let slot = 0; slot < physicalFields.length; slot += 1) {
5091
+ const field = physicalFields[slot];
5092
+ if (field === void 0) continue;
5093
+ let view = defaultViewForUserRegionField(field, pipelineState, smSchema);
5094
+ const targetSource = materialRenderTargetSourceForField(
5095
+ submeshMaterial,
5096
+ field,
5097
+ resolveRenderTargetTextureSource
5098
+ );
5099
+ if (targetSource !== void 0) {
5100
+ if (targetSource.textureView !== void 0) view = targetSource.textureView;
5101
+ else materialResourcesResident = false;
5102
+ } else {
5103
+ const handle = submeshMaterial.textureHandles?.get(field);
5104
+ if (handle !== void 0) {
5105
+ const resident = residentTextureView(materialWorld, store, runtime, handle);
5106
+ if (resident !== void 0) view = resident;
5107
+ else materialResourcesResident = false;
5108
+ }
5109
+ }
5110
+ physicalTextureInjections.push({
5111
+ slot,
5112
+ sampler: smSamplerForField(field),
5113
+ view
5114
+ });
5115
+ }
5489
5116
  const transmissionSampler = transmissionBackdropView != null ? effectiveSkylightResources.prefilterSampler : pipelineState.defaultSampler;
5490
5117
  const smMergedEntries = assembleMaterialWithSkylightEntries(
5491
5118
  smBaseEntries,
@@ -5493,7 +5120,8 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5493
5120
  {
5494
5121
  sampler: transmissionSampler,
5495
5122
  ...transmissionBackdropView === void 0 ? {} : { backdropView: transmissionBackdropView }
5496
- }
5123
+ },
5124
+ physicalTextureInjections
5497
5125
  );
5498
5126
  const createBindGroup = () => {
5499
5127
  const result = runtime.device.createBindGroup({
@@ -5504,14 +5132,51 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5504
5132
  if (!result.ok) throw result.error;
5505
5133
  return result.value;
5506
5134
  };
5135
+ const materialCacheHandles = smMergedEntries.map((entry) => extractEntryResourceHandle(entry));
5136
+ if (diagnosticsEnabled && submeshMaterial.textureHandles === void 0 && smMaterialHandle !== void 0) {
5137
+ console.error(
5138
+ `[render-material] scalar material cache handles: ${JSON.stringify({
5139
+ entityKey,
5140
+ materialHandle: smMaterialHandle,
5141
+ shader: smShaderId,
5142
+ paramSnapshot: submeshMaterial.paramSnapshot,
5143
+ handleTypes: materialCacheHandles.map((handle) => typeof handle)
5144
+ })}`
5145
+ );
5146
+ }
5147
+ if (diagnosticsEnabled && materialCacheHandles.some((handle) => typeof handle !== "object" || handle === null)) {
5148
+ console.error(
5149
+ `[render-material] invalid material cache handle: ${JSON.stringify({
5150
+ entityKey,
5151
+ materialHandle: smMaterialHandle,
5152
+ shader: smShaderId,
5153
+ entries: smMergedEntries.map((entry, index) => ({
5154
+ index,
5155
+ binding: entry.binding,
5156
+ kind: entry.resource.kind,
5157
+ handleType: typeof materialCacheHandles[index]
5158
+ }))
5159
+ })}`
5160
+ );
5161
+ }
5507
5162
  const smBindGroup = hasActiveTransmissionBackdrop ? createBindGroup() : getOrCreatePerEntity(
5508
5163
  materialBgShared,
5509
5164
  smShaderId ?? "",
5510
- smMergedEntries.map((e) => extractEntryResourceHandle(e)),
5165
+ materialCacheHandles,
5511
5166
  "material-shared",
5512
5167
  createBindGroup,
5513
5168
  bindGroupCounts
5514
5169
  );
5170
+ if (diagnosticsEnabled) {
5171
+ recordIblMaterialBinding(
5172
+ frameState,
5173
+ smMaterialBgl,
5174
+ smBindGroup,
5175
+ smMergedEntries,
5176
+ smBaseEntries.length,
5177
+ "miss"
5178
+ );
5179
+ }
5515
5180
  if (smMaterialHandle !== void 0 && !smHasVideoFields && materialResourcesResident && !hasActiveTransmissionBackdrop) {
5516
5181
  if (materialCacheKey !== void 0) {
5517
5182
  materialBgAssemblyCache.set(materialCacheKey, {
@@ -5528,6 +5193,85 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5528
5193
  }
5529
5194
 
5530
5195
  // src/record/main-pass-geometry.ts
5196
+ function materialDiagnosticsEnabled2() {
5197
+ if (typeof globalThis !== "object" || globalThis === null || !("process" in globalThis)) {
5198
+ return false;
5199
+ }
5200
+ const processValue = globalThis.process;
5201
+ return processValue?.env?.FORGEAX_MATERIAL_DIAGNOSTICS === "1";
5202
+ }
5203
+ function requestsStandardTransmissionVariant(material) {
5204
+ return material.materialShaderId === "forgeax::default-standard-pbr" && material.paramSnapshot?.transmission !== void 0;
5205
+ }
5206
+ function recordIblPipelineBinding(c, material, pipeline, materialGroup) {
5207
+ const receipt = c.frameState.iblBindingInspection;
5208
+ if (receipt === void 0 || !isStandardPbrMaterialShader(material.materialShaderId)) return;
5209
+ const materialBgl = c.runtime.getMaterialBindGroupLayout?.(
5210
+ material.materialShaderId ?? "forgeax::default-standard-pbr"
5211
+ ) ?? c.pipelineState.materialBindGroupLayout;
5212
+ c.frameState.iblBindingInspection = {
5213
+ ...receipt,
5214
+ pipeline: {
5215
+ pipelineIdentity: getOpaqueResourceIdentity(pipeline),
5216
+ effectiveMaterialLayoutIdentity: effectiveMaterialLayoutIdentity(
5217
+ material.materialShaderId ?? "forgeax::default-standard-pbr",
5218
+ material.materialParamSchema
5219
+ ),
5220
+ materialBglIdentity: getOpaqueResourceIdentity(materialBgl),
5221
+ bindGroupIdentity: getOpaqueResourceIdentity(materialGroup),
5222
+ drawFrameId: c.frameState.frameNumber
5223
+ }
5224
+ };
5225
+ const materialReceipt = c.frameState.iblBindingInspection.material;
5226
+ const errors = [];
5227
+ if (materialReceipt !== void 0 && materialReceipt.materialBglIdentity !== getOpaqueResourceIdentity(materialBgl)) {
5228
+ errors.push("material-pipeline-bgl-identity-mismatch");
5229
+ }
5230
+ if (materialReceipt !== void 0 && materialReceipt.bindGroupIdentity !== getOpaqueResourceIdentity(materialGroup)) {
5231
+ errors.push("material-bind-group-identity-mismatch");
5232
+ }
5233
+ if (materialReceipt !== void 0) {
5234
+ const expectedResourceIdentities = [
5235
+ receipt.resources.irradiance.viewIdentity,
5236
+ receipt.resources.irradiance.samplerIdentity,
5237
+ receipt.resources.prefilter.viewIdentity,
5238
+ receipt.resources.prefilter.samplerIdentity,
5239
+ receipt.resources.brdfLut.viewIdentity,
5240
+ receipt.resources.brdfLut.samplerIdentity,
5241
+ receipt.resources.intensityBufferIdentity
5242
+ ];
5243
+ const actualResourceIdentities = materialReceipt.entries.filter((entry) => entry.binding >= materialReceipt.skylightBindingStart).slice(0, expectedResourceIdentities.length).map((entry) => entry.resourceIdentity);
5244
+ if (actualResourceIdentities.length !== expectedResourceIdentities.length || actualResourceIdentities.some(
5245
+ (identity, index) => identity !== expectedResourceIdentities[index]
5246
+ )) {
5247
+ errors.push("material-ibl-resource-identity-mismatch");
5248
+ }
5249
+ }
5250
+ c.frameState.iblBindingInspection = {
5251
+ ...c.frameState.iblBindingInspection,
5252
+ status: errors.length === 0 ? "binding-chain-consistent" : "binding-chain-mismatch",
5253
+ errors
5254
+ };
5255
+ }
5256
+ var SHARED_BOOT_MATERIAL_SHADER_IDS = /* @__PURE__ */ new Set([
5257
+ "forgeax::default-unlit",
5258
+ "forgeax::default-shadow-caster",
5259
+ "forgeax::sprite",
5260
+ "forgeax::sprite-lit",
5261
+ "forgeax::default-sprite",
5262
+ "forgeax::msdf-text",
5263
+ "forgeax::default-standard-pbr",
5264
+ "forgeax::pbr-skin",
5265
+ "forgeax::default-standard-pbr-skin"
5266
+ ]);
5267
+ function effectiveMaterialLayoutIdentity(materialShaderId, materialParamSchema) {
5268
+ if (materialParamSchema === void 0) return void 0;
5269
+ const hasStandardPhysicalMaps = isStandardPbrMaterialShader(materialShaderId) && physicalTextureFields(materialParamSchema).length > 0;
5270
+ if (SHARED_BOOT_MATERIAL_SHADER_IDS.has(materialShaderId) && !hasStandardPhysicalMaps) {
5271
+ return void 0;
5272
+ }
5273
+ return materialBindGroupLayoutIdentity(materialShaderId, materialParamSchema);
5274
+ }
5531
5275
  function geometryRenderStateForPass(base, passKind) {
5532
5276
  if (passKind !== "temporal") return base;
5533
5277
  return {
@@ -5550,9 +5294,14 @@ function submitSubmeshDraws(pass, state, pipeline, instanceDraws, indexed, index
5550
5294
  state.pipeline = pipeline;
5551
5295
  }
5552
5296
  for (const instanceDraw of instanceDraws) {
5553
- if (instanceDraw.instanceBuffer !== state.instancesBuffer) {
5554
- pass.setBindGroup(3, instanceDraw.instanceBindGroup);
5297
+ if (instanceDraw.instanceBuffer !== state.instancesBuffer || instanceDraw.probeOffset !== state.probeOffset) {
5298
+ if (instanceDraw.probeOffset < 0) {
5299
+ pass.setBindGroup(3, instanceDraw.instanceBindGroup);
5300
+ } else {
5301
+ pass.setBindGroup(3, instanceDraw.instanceBindGroup, [instanceDraw.probeOffset]);
5302
+ }
5555
5303
  state.instancesBuffer = instanceDraw.instanceBuffer;
5304
+ state.probeOffset = instanceDraw.probeOffset;
5556
5305
  }
5557
5306
  if (indexed) {
5558
5307
  pass.drawIndexed(indexCount, instanceDraw.instanceCount, indexOffset, 0, 0);
@@ -5583,6 +5332,7 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5583
5332
  viewBindGroup,
5584
5333
  viewBindGroupDynamicOffset = 0
5585
5334
  } = c;
5335
+ const diagnosticsEnabled = materialDiagnosticsEnabled2();
5586
5336
  const colorFormatOverride = c.transparentColorFormat;
5587
5337
  const clusteredLighting = c.standardLighting?.kind === "clustered";
5588
5338
  const isHdrTarget = tonemapActive || colorFormatOverride === "rgba16float";
@@ -5590,13 +5340,15 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5590
5340
  let lastIndexBuffer = null;
5591
5341
  const bindingState = {
5592
5342
  pipeline: null,
5593
- instancesBuffer: null
5343
+ instancesBuffer: null,
5344
+ probeOffset: -1
5594
5345
  };
5595
5346
  const identityInstanceBuffer = pipelineState.identityInstanceBuffer;
5596
5347
  const identityInstanceDraws = [
5597
5348
  {
5598
5349
  instanceBuffer: identityInstanceBuffer,
5599
- instanceBindGroup: resolveGeometryInstancesBindGroup(c, identityInstanceBuffer),
5350
+ instanceBindGroup: resolveGeometryInstancesBindGroup(c, identityInstanceBuffer, void 0),
5351
+ probeOffset: -1,
5600
5352
  instanceCount: 1
5601
5353
  }
5602
5354
  ];
@@ -5605,13 +5357,17 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5605
5357
  const meshGroup2DynamicOffsets = [0];
5606
5358
  let lastStencilReference = null;
5607
5359
  const materialPipelineLookupCache = /* @__PURE__ */ new Map();
5360
+ const recordOcclusionCandidate = (draw) => {
5361
+ draw();
5362
+ };
5608
5363
  const resolveMaterialPipeline = (materialShaderId, renderState, topology, indexFormat, variantSet, colorFormat, layoutProjection) => {
5364
+ const reflectionFallbackFormats = materialShaderId === "forgeax::default-standard-pbr" && c.reflectionFallbackColorFormat !== void 0 ? [c.reflectionFallbackColorFormat] : void 0;
5609
5365
  const renderStateKey = renderStateHash(renderState);
5610
5366
  const shaderLookupCache = materialPipelineLookupCache.get(materialShaderId);
5611
5367
  const cachedLookups = shaderLookupCache?.get(renderStateKey);
5612
5368
  if (cachedLookups !== void 0) {
5613
5369
  for (const cached of cachedLookups) {
5614
- if (cached.isHdrTarget === isHdrTarget && cached.renderStateKey === renderStateKey && cached.topology === topology && cached.indexFormat === indexFormat && cached.variantSet === variantSet && cached.passKind === passKind && cached.layoutProjection.digest === layoutProjection.digest && cached.sampleCount === sampleCount && cached.colorFormatOverride === colorFormat) {
5370
+ if (cached.isHdrTarget === isHdrTarget && cached.renderStateKey === renderStateKey && cached.topology === topology && cached.indexFormat === indexFormat && cached.variantSet === variantSet && cached.passKind === passKind && cached.layoutProjection.digest === layoutProjection.digest && cached.sampleCount === sampleCount && cached.colorFormatOverride === colorFormat && cached.additionalColorFormats?.join(",") === reflectionFallbackFormats?.join(",")) {
5615
5371
  return cached.handle;
5616
5372
  }
5617
5373
  }
@@ -5622,13 +5378,14 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5622
5378
  return null;
5623
5379
  }
5624
5380
  const resolvedVariantSet = resolvedVariantSetResult.value;
5381
+ const fallbackVariantSet = materialShaderId !== "forgeax::default-standard-pbr" ? resolvedVariantSet : c.reflectionFallbackColorFormat !== void 0 && resolvedVariantSet === "" ? "" : `${resolvedVariantSet === void 0 || resolvedVariantSet === "" ? "" : `${resolvedVariantSet}+`}REFLECTION_FALLBACK_AVAILABLE=${c.reflectionFallbackColorFormat !== void 0}`;
5625
5382
  const resolvePipeline = () => runtime.getMaterialShaderPipelineEntry?.(
5626
5383
  materialShaderId,
5627
5384
  isHdrTarget,
5628
5385
  renderState,
5629
5386
  topology,
5630
5387
  indexFormat,
5631
- resolvedVariantSet,
5388
+ fallbackVariantSet,
5632
5389
  passKind,
5633
5390
  void 0,
5634
5391
  sampleCount,
@@ -5636,7 +5393,10 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5636
5393
  void 0,
5637
5394
  void 0,
5638
5395
  void 0,
5639
- layoutProjection
5396
+ layoutProjection,
5397
+ void 0,
5398
+ void 0,
5399
+ reflectionFallbackFormats
5640
5400
  ) ?? null;
5641
5401
  const handle = profilePhase === void 0 ? resolvePipeline() : profileGeometrySegment(c, passKind, "pipeline-selection", resolvePipeline);
5642
5402
  if (handle !== null) {
@@ -5651,6 +5411,7 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5651
5411
  layoutProjection,
5652
5412
  sampleCount,
5653
5413
  colorFormatOverride: colorFormat,
5414
+ additionalColorFormats: reflectionFallbackFormats,
5654
5415
  handle
5655
5416
  };
5656
5417
  if (shaderLookupCache === void 0) {
@@ -5672,7 +5433,12 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5672
5433
  const pointsLinesSubmission = c.pointsLines?.prepare(entry, clusteredLighting);
5673
5434
  if (entry.source.pointsLines !== void 0 && pointsLinesSubmission?.plan.drawCount !== 1)
5674
5435
  continue;
5675
- if (passKind === "forward" && c.gpuDrivenEntityKeys.has(worldEntityKey(entry.source.worldId, entry.source.entityKey))) {
5436
+ if (passKind === "forward" && c.gpuDrivenEntityKeys.has(
5437
+ worldEntityKey(
5438
+ c.gpuDrivenWorldKeys?.[entry.source.worldId] ?? entry.source.worldId,
5439
+ entry.source.entityKey
5440
+ )
5441
+ )) {
5676
5442
  continue;
5677
5443
  }
5678
5444
  if (matchedMaterials !== null && !matchedMaterials.has(entry.renderableIndex)) continue;
@@ -5735,11 +5501,9 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5735
5501
  const pointsLinesMeshGroup = meshBindGroup ?? meshGroup2;
5736
5502
  if (pointsLinesMeshGroup === null) continue;
5737
5503
  pass.setBindGroup(2, pointsLinesMeshGroup, meshGroup2DynamicOffsets);
5738
- pass.setBindGroup(
5739
- 3,
5740
- identityInstanceDraws[0]?.instanceBindGroup ?? resolveGeometryInstancesBindGroup(c, identityInstanceBuffer)
5741
- );
5742
- recordPointsLinesDraw(pass, pointsLinesSubmission.plan);
5504
+ const pointsLinesInstancesBg = identityInstanceDraws[0]?.instanceBindGroup ?? resolveGeometryInstancesBindGroup(c, identityInstanceBuffer, void 0);
5505
+ pass.setBindGroup(3, pointsLinesInstancesBg);
5506
+ recordOcclusionCandidate(() => recordPointsLinesDraw(pass, pointsLinesSubmission.plan));
5743
5507
  pass.setBindGroup(0, viewBindGroup, [viewBindGroupDynamicOffset, 0]);
5744
5508
  continue;
5745
5509
  }
@@ -5754,15 +5518,19 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5754
5518
  }
5755
5519
  }
5756
5520
  dispatchCounts.unlit += 1;
5757
- const instanceDraws = resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws);
5521
+ const instanceDraws = resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws, false);
5758
5522
  if (instanceDraws === null) continue;
5759
5523
  let group2BindGroup = meshGroup2;
5760
5524
  meshGroup2DynamicOffsets[0] = i * MESH_PER_ENTITY_STRIDE;
5761
5525
  let group2DynamicOffsets = meshGroup2DynamicOffsets;
5762
5526
  const isSkinEntry = entry.source.skin !== void 0;
5527
+ const probeBlendRecordAvailable = runtime.device.caps.storageBuffer && entry.source.probeBlendRecord !== void 0;
5763
5528
  const skinAllocator = pipelineState.skinPaletteAllocator;
5764
5529
  const skinSlice = entry.source.skin;
5765
- const clusteredSkin = clusteredLighting;
5530
+ const clusteredSkin = clusteredLighting && entry.source.materials.every((material) => {
5531
+ const shaderId = material.materialShaderId;
5532
+ return !isStandardPbrSkinMaterialShader(shaderId) || isCanonicalStandardPbrMaterialShader(shaderId);
5533
+ });
5766
5534
  const skinMeshBindGroupLayout = clusteredSkin ? pipelineState.hdrpSkinMeshBindGroupLayout : pipelineState.pbrSkinMeshBindGroupLayout;
5767
5535
  const hdrpSkinBuffers = isSkinEntry && clusteredSkin ? getOrCreateHdrpBuffers(runtime, frameState.installedPipelineConfig?.clusterGrid) : null;
5768
5536
  const skinResources = isSkinEntry && skinMeshBindGroupLayout !== null && skinAllocator !== null && skinSlice !== void 0 && (!clusteredSkin || hdrpSkinBuffers !== null) ? {
@@ -5776,7 +5544,8 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5776
5544
  standardTopologyVariantSet(
5777
5545
  c.standardLighting,
5778
5546
  runtime.device.caps.storageBuffer,
5779
- entry.mesh.layoutProjection.attributes.some((attribute) => attribute.key === "color")
5547
+ entry.mesh.layoutProjection.attributes.some((attribute) => attribute.key === "color"),
5548
+ probeBlendRecordAvailable
5780
5549
  )
5781
5550
  );
5782
5551
  if (!skinVariantSetResult.ok) runtime.errorRegistry.fire(skinVariantSetResult.error);
@@ -5796,7 +5565,10 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5796
5565
  void 0,
5797
5566
  void 0,
5798
5567
  void 0,
5799
- entry.mesh.layoutProjection
5568
+ entry.mesh.layoutProjection,
5569
+ void 0,
5570
+ void 0,
5571
+ void 0
5800
5572
  ) ?? null : null;
5801
5573
  if (skinResources !== null && skinPsoProbe !== null) {
5802
5574
  const meshBindSize = runtime.device.caps.storageBuffer ? MESH_SSBO_BYTES : MESH_UBO_FULL_ARRAY_BYTES;
@@ -5859,6 +5631,19 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5859
5631
  entry.source.skin?.byteOffset ?? 0
5860
5632
  );
5861
5633
  } else if (isSkinEntry) {
5634
+ if (diagnosticsEnabled) {
5635
+ console.error(
5636
+ `[render-material] skin draw skipped: ${JSON.stringify({
5637
+ entityKey: entry.source.entityKey,
5638
+ materialHandle: entry.source.material.materialHandle,
5639
+ reason: "skin-pipeline-unavailable",
5640
+ skinResourcesReady: skinResources !== null,
5641
+ skinPsoReady: skinPsoProbe !== null,
5642
+ paletteBufferReady: skinSlice?.buffer !== void 0,
5643
+ paletteByteOffset: skinSlice?.byteOffset
5644
+ })}`
5645
+ );
5646
+ }
5862
5647
  continue;
5863
5648
  }
5864
5649
  let perSubmeshBg = null;
@@ -5877,6 +5662,18 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5877
5662
  continue;
5878
5663
  }
5879
5664
  const materialSlot = materialSlotIndices[i]?.[matSlotIdx] ?? materialSlotIndices[i]?.[0] ?? 0;
5665
+ const processValue = globalThis.process;
5666
+ if (processValue?.env?.FORGEAX_MATERIAL_PIPELINE_DIAGNOSTICS === "1" && (submeshMaterial.materialShaderId === "forgeax::default-standard-pbr" || submeshMaterial.materialShaderId === "forgeax::pbr-skin")) {
5667
+ console.error(
5668
+ `[render-material] draw receipt: ${JSON.stringify({
5669
+ entityIndex: entry.renderableIndex,
5670
+ materialSlot,
5671
+ materialHandle: submeshMaterial.materialHandle,
5672
+ clearcoat: submeshMaterial.paramSnapshot?.clearcoat,
5673
+ shader: submeshMaterial.materialShaderId
5674
+ })}`
5675
+ );
5676
+ }
5880
5677
  perSubmeshBg = profilePhase === void 0 ? resolveMaterialBindGroup(
5881
5678
  materialSlot,
5882
5679
  submeshMaterial,
@@ -5896,7 +5693,8 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5896
5693
  materialGroup1DynamicOffsets[0] = materialSlot * MATERIAL_PER_ENTITY_STRIDE;
5897
5694
  pass.setBindGroup(1, perSubmeshBg, materialGroup1DynamicOffsets, 0, 1);
5898
5695
  const smTopology = sm.topology;
5899
- const smMaterialShaderId = entry.source.skin !== void 0 ? SKIN_MATERIAL_SHADER_ID : submeshMaterial.materialShaderId;
5696
+ const smMaterialShaderId = entry.source.skin !== void 0 ? isStandardPbrSkinMaterialShader(submeshMaterial.materialShaderId) ? submeshMaterial.materialShaderId : SKIN_MATERIAL_SHADER_ID : submeshMaterial.materialShaderId;
5697
+ const probeBlendAvailable = probeBlendRecordAvailable && isStandardPbrMaterialShader(smMaterialShaderId);
5900
5698
  const isSpriteShader = smMaterialShaderId === "forgeax::sprite" || smMaterialShaderId === "forgeax::sprite-lit";
5901
5699
  const basePipelineRenderState = isSpriteShader ? {
5902
5700
  ...submeshMaterial.renderState,
@@ -5942,20 +5740,18 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5942
5740
  const capabilityVariantSet = standardTopologyVariantSet(
5943
5741
  c.standardLighting,
5944
5742
  runtime.device.caps.storageBuffer,
5945
- hasVertexColor
5743
+ hasVertexColor,
5744
+ probeBlendAvailable
5946
5745
  );
5947
- const variantSet = isSpriteShader ? smMaterialShaderId === "forgeax::sprite" && entry.source.spriteInstances !== void 0 ? "" : void 0 : entry.variantSet === void 0 ? capabilityVariantSet : capabilityVariantSet === "" ? entry.variantSet : `${capabilityVariantSet}+${entry.variantSet}`;
5948
- const projectionVariantSetResult = isSpriteShader ? { ok: true, value: variantSet } : variantSetFromVertexLayoutProjection(entry.mesh.layoutProjection, variantSet);
5949
- if (!projectionVariantSetResult.ok) {
5950
- runtime.errorRegistry.fire(projectionVariantSetResult.error);
5951
- continue;
5952
- }
5746
+ const transmissionAvailable = requestsStandardTransmissionVariant(submeshMaterial);
5747
+ const materialCapabilityVariantSet = transmissionAvailable && !capabilityVariantSet.includes("TRANSMISSION_AVAILABLE=") ? `${capabilityVariantSet === "" ? "" : `${capabilityVariantSet}+`}TRANSMISSION_AVAILABLE=true` : capabilityVariantSet;
5748
+ const variantSet = isSpriteShader ? smMaterialShaderId === "forgeax::sprite" && entry.source.spriteInstances !== void 0 ? "" : void 0 : entry.variantSet === void 0 ? materialCapabilityVariantSet : capabilityVariantSet === "" ? entry.variantSet : `${capabilityVariantSet}+${entry.variantSet}`;
5953
5749
  const cachedPipeline = resolveMaterialPipeline(
5954
5750
  smMaterialShaderId,
5955
5751
  pipelineRenderState,
5956
5752
  smTopology,
5957
5753
  entry.mesh.indexFormat,
5958
- projectionVariantSetResult.value,
5754
+ variantSet,
5959
5755
  colorFormatOverride,
5960
5756
  entry.mesh.layoutProjection
5961
5757
  );
@@ -5965,8 +5761,25 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5965
5761
  smPipelineHandle = selectGeometryPipeline(pipelineState, isHdrTarget, msaaActive);
5966
5762
  }
5967
5763
  if (smPipelineHandle === null) {
5764
+ if (diagnosticsEnabled) {
5765
+ console.error(
5766
+ `[render-material] draw skipped: ${JSON.stringify({
5767
+ entityKey: entry.source.entityKey,
5768
+ materialHandle: submeshMaterial.materialHandle,
5769
+ shader: smMaterialShaderId,
5770
+ reason: "pipeline-unavailable"
5771
+ })}`
5772
+ );
5773
+ }
5968
5774
  continue;
5969
5775
  }
5776
+ const instanceDraws2 = resolveGeometryInstanceBuffer(
5777
+ c,
5778
+ entry,
5779
+ identityInstanceDraws,
5780
+ probeBlendAvailable
5781
+ );
5782
+ if (instanceDraws2 === null) continue;
5970
5783
  if (!isSkinEntry) {
5971
5784
  materialGroup2Contract = materialPipelineEntry?.group2Contract;
5972
5785
  if (materialGroup2Contract === void 0) continue;
@@ -5978,42 +5791,91 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5978
5791
  if (selectedGroup === null) continue;
5979
5792
  group2BindGroup = selectedGroup;
5980
5793
  }
5794
+ if (diagnosticsEnabled && submeshMaterial.textureHandles !== void 0) {
5795
+ console.error(
5796
+ `[render-material] draw submitted: ${JSON.stringify({
5797
+ entityKey: entry.source.entityKey,
5798
+ materialHandle: submeshMaterial.materialHandle,
5799
+ shader: smMaterialShaderId,
5800
+ textureHandles: [...submeshMaterial.textureHandles.entries()].map(
5801
+ ([field, handle]) => ({
5802
+ field,
5803
+ handle
5804
+ })
5805
+ ),
5806
+ skin: isSkinEntry ? {
5807
+ resourcesReady: skinResources !== null,
5808
+ psoReady: skinPsoProbe !== null,
5809
+ paletteBufferReady: skinSlice?.buffer !== void 0,
5810
+ paletteByteOffset: skinSlice?.byteOffset,
5811
+ dynamicOffsets: [...group2DynamicOffsets]
5812
+ } : void 0,
5813
+ pipelineReady: true
5814
+ })}`
5815
+ );
5816
+ }
5981
5817
  pass.setBindGroup(2, group2BindGroup, group2DynamicOffsets);
5982
5818
  if (profilePhase === void 0) {
5983
- submitSubmeshDraws(
5984
- pass,
5985
- bindingState,
5986
- smPipelineHandle,
5987
- instanceDraws,
5988
- entry.mesh.indexed,
5989
- sm.indexCount,
5990
- sm.vertexCount,
5991
- sm.indexOffset
5992
- );
5993
- } else {
5994
- profileGeometrySegment(
5995
- c,
5996
- passKind,
5997
- "draw-submit",
5819
+ recordOcclusionCandidate(
5998
5820
  () => submitSubmeshDraws(
5999
5821
  pass,
6000
5822
  bindingState,
6001
5823
  smPipelineHandle,
6002
- instanceDraws,
5824
+ instanceDraws2,
6003
5825
  entry.mesh.indexed,
6004
5826
  sm.indexCount,
6005
5827
  sm.vertexCount,
6006
5828
  sm.indexOffset
6007
5829
  )
6008
5830
  );
5831
+ } else {
5832
+ profileGeometrySegment(
5833
+ c,
5834
+ passKind,
5835
+ "draw-submit",
5836
+ () => recordOcclusionCandidate(
5837
+ () => submitSubmeshDraws(
5838
+ pass,
5839
+ bindingState,
5840
+ smPipelineHandle,
5841
+ instanceDraws2,
5842
+ entry.mesh.indexed,
5843
+ sm.indexCount,
5844
+ sm.vertexCount,
5845
+ sm.indexOffset
5846
+ )
5847
+ )
5848
+ );
5849
+ }
5850
+ if (diagnosticsEnabled && perSubmeshBg !== null) {
5851
+ recordIblPipelineBinding(c, submeshMaterial, smPipelineHandle, perSubmeshBg);
6009
5852
  }
6010
5853
  }
6011
5854
  }
6012
5855
  }
6013
- function resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws) {
5856
+ function resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws, useProbeBlend) {
6014
5857
  const { runtime, pipelineState, frameState } = c;
6015
5858
  const inst = entry.source.instances;
6016
- if (inst === void 0) return identityInstanceDraws;
5859
+ const probeBinding = !useProbeBlend || !runtime.device.caps.storageBuffer || entry.source.probeBlendRecord === void 0 ? void 0 : resolveProbeBlendBuffer(
5860
+ c,
5861
+ entry.source.probeBlendRecord,
5862
+ worldEntityKey(entry.source.worldId, entry.source.entityKey)
5863
+ );
5864
+ if (inst === void 0) {
5865
+ const identity = identityInstanceDraws[0];
5866
+ if (identity === void 0) return null;
5867
+ return [
5868
+ {
5869
+ ...identity,
5870
+ instanceBindGroup: resolveGeometryInstancesBindGroup(
5871
+ c,
5872
+ pipelineState.identityInstanceBuffer,
5873
+ probeBinding?.buffer
5874
+ ),
5875
+ probeOffset: probeBinding?.offset ?? -1
5876
+ }
5877
+ ];
5878
+ }
6017
5879
  if (inst.instanceCount === 0) return [];
6018
5880
  let instanceBuffer = pipelineState.identityInstanceBuffer;
6019
5881
  let instanceCount = 1;
@@ -6047,7 +5909,12 @@ function resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws) {
6047
5909
  }
6048
5910
  draws.push({
6049
5911
  instanceBuffer: buffer.handle,
6050
- instanceBindGroup: resolveGeometryInstancesBindGroup(c, buffer.handle),
5912
+ instanceBindGroup: resolveGeometryInstancesBindGroup(
5913
+ c,
5914
+ buffer.handle,
5915
+ probeBinding?.buffer
5916
+ ),
5917
+ probeOffset: probeBinding?.offset ?? -1,
6051
5918
  instanceCount: count
6052
5919
  });
6053
5920
  }
@@ -6124,21 +5991,57 @@ function resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws) {
6124
5991
  return [
6125
5992
  {
6126
5993
  instanceBuffer,
6127
- instanceBindGroup: resolveGeometryInstancesBindGroup(c, instanceBuffer),
5994
+ instanceBindGroup: resolveGeometryInstancesBindGroup(c, instanceBuffer, probeBinding?.buffer),
5995
+ probeOffset: probeBinding?.offset ?? -1,
6128
5996
  instanceCount
6129
5997
  }
6130
5998
  ];
6131
5999
  }
6132
- function resolveGeometryInstancesBindGroup(c, instanceBuffer) {
6000
+ function resolveProbeBlendBuffer(c, record, entityKey) {
6001
+ const { runtime, frameState } = c;
6002
+ const usage = runtime.device.caps.storageBuffer ? GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST : GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST;
6003
+ if (record === void 0) throw new Error("probe blend record is required for probe allocation");
6004
+ if (entityKey === void 0) throw new Error("probe blend record requires an entity key");
6005
+ const offset = probeBlendRecordOffset(record.objectKey);
6006
+ const requiredCapacity = record.objectKey + 2;
6007
+ let buffer = frameState.probeBlendRecordBuffer;
6008
+ if (buffer === void 0 || frameState.probeBlendRecordBufferCapacity < requiredCapacity) {
6009
+ const created = runtime.device.createBuffer({
6010
+ label: "probe-blend-records",
6011
+ size: requiredCapacity * PROBE_BLEND_RECORD_STRIDE,
6012
+ usage,
6013
+ mappedAtCreation: false
6014
+ });
6015
+ if (!created.ok) throw created.error;
6016
+ buffer = new GpuBuffer(runtime.device, created.value);
6017
+ frameState.probeBlendRecordBuffer = buffer;
6018
+ frameState.probeBlendRecordBufferCapacity = requiredCapacity;
6019
+ frameState.probeBlendBuffers.clear();
6020
+ }
6021
+ const cached = frameState.probeBlendBuffers.get(entityKey);
6022
+ const unchanged = cached !== void 0 && cached.generation === record.generation && cached.bytes.length === record.bytes.length && cached.bytes.every((value, index) => value === record.bytes[index]);
6023
+ if (!unchanged) {
6024
+ const uploaded = runtime.device.queue.writeBuffer(buffer.handle, offset, record.bytes);
6025
+ if (!uploaded.ok) throw uploaded.error;
6026
+ frameState.probeBlendBuffers.set(entityKey, {
6027
+ generation: record.generation,
6028
+ bytes: new Uint8Array(record.bytes)
6029
+ });
6030
+ }
6031
+ return { buffer: buffer.handle, offset };
6032
+ }
6033
+ function resolveGeometryInstancesBindGroup(c, instanceBuffer, probeBuffer) {
6133
6034
  const { runtime, pipelineState, frameState, bindGroupCounts } = c;
6035
+ const layout = probeBuffer === void 0 ? pipelineState.instancesBindGroupLayout : pipelineState.probeInstancesBindGroupLayout;
6036
+ if (layout === void 0) throw new Error("probe instances bind-group layout is unavailable");
6134
6037
  return getOrCreateFromChain(
6135
6038
  frameState.instancesBgShared,
6136
- [pipelineState.instancesBindGroupLayout, instanceBuffer],
6137
- "instances",
6039
+ probeBuffer === void 0 ? [layout, instanceBuffer] : [layout, instanceBuffer, probeBuffer],
6040
+ probeBuffer === void 0 ? "instances-no-probe" : "instances-probe",
6138
6041
  () => {
6139
6042
  const result = runtime.device.createBindGroup({
6140
6043
  label: "pbr-instances-bg",
6141
- layout: pipelineState.instancesBindGroupLayout,
6044
+ layout,
6142
6045
  entries: [
6143
6046
  {
6144
6047
  binding: 0,
@@ -6146,7 +6049,16 @@ function resolveGeometryInstancesBindGroup(c, instanceBuffer) {
6146
6049
  kind: "buffer",
6147
6050
  value: { buffer: instanceBuffer }
6148
6051
  }
6149
- }
6052
+ },
6053
+ ...probeBuffer === void 0 ? [] : [
6054
+ {
6055
+ binding: 1,
6056
+ resource: {
6057
+ kind: "buffer",
6058
+ value: { buffer: probeBuffer, offset: 0, size: PROBE_BLEND_RECORD_BYTE_SIZE }
6059
+ }
6060
+ }
6061
+ ]
6150
6062
  ]
6151
6063
  });
6152
6064
  if (!result.ok) throw result.error;
@@ -6359,7 +6271,7 @@ function recordSpritePass(c, pass, matchedIndices, materialSlotIndices, sampleCo
6359
6271
  return geometryPassEnded;
6360
6272
  }
6361
6273
  function recordSpriteTransparentPbrDraws(c, spritePass, matchedIndices, materialSlotIndices, sampleCount, resolveMaterialBindGroup, meshGroup2) {
6362
- const { runtime, pipelineState, frameState, bindGroupCounts, validatedOrdered } = c;
6274
+ const { runtime, pipelineState, validatedOrdered } = c;
6363
6275
  const isHdrTarget = c.tonemapActive || transparentPassColorFormat(c, pipelineState) === "rgba16float";
6364
6276
  let lastPbrSubPipelineHandle = null;
6365
6277
  let lastPbrSubVertexBuffer = null;
@@ -6382,45 +6294,6 @@ function recordSpriteTransparentPbrDraws(c, spritePass, matchedIndices, material
6382
6294
  spritePass.setIndexBuffer(entry.mesh.indexBuffer.handle, entry.mesh.indexFormat);
6383
6295
  lastPbrSubIndexBuffer = entry.mesh.indexBuffer;
6384
6296
  }
6385
- const identityInstBg = getOrCreatePerEntity(
6386
- frameState.instancesBgPerEntity,
6387
- worldEntityKey(entry.source.worldId, entry.source.entityKey),
6388
- [pipelineState.identityInstanceBuffer],
6389
- "instances",
6390
- () => {
6391
- const result = runtime.device.createBindGroup({
6392
- label: "pbr-transparent-sub-instances-bg",
6393
- layout: pipelineState.instancesBindGroupLayout,
6394
- entries: [
6395
- {
6396
- binding: 0,
6397
- resource: {
6398
- kind: "buffer",
6399
- value: { buffer: pipelineState.identityInstanceBuffer }
6400
- }
6401
- }
6402
- ]
6403
- });
6404
- if (!result.ok) throw result.error;
6405
- return result.value;
6406
- },
6407
- bindGroupCounts
6408
- );
6409
- spritePass.setBindGroup(3, identityInstBg);
6410
- const subVariantSet = standardTopologyVariantSet(
6411
- c.standardLighting,
6412
- runtime.device.caps.storageBuffer,
6413
- entry.mesh.layoutProjection.attributes.some((attribute) => attribute.key === "color")
6414
- );
6415
- const projectionVariantSetResult = variantSetFromVertexLayoutProjection(
6416
- entry.mesh.layoutProjection,
6417
- subVariantSet
6418
- );
6419
- if (!projectionVariantSetResult.ok) {
6420
- runtime.errorRegistry.fire(projectionVariantSetResult.error);
6421
- continue;
6422
- }
6423
- const projectionVariantSet = projectionVariantSetResult.value;
6424
6297
  for (let smIdx = 0; smIdx < entry.mesh.submeshes.length; smIdx++) {
6425
6298
  const sm = entry.mesh.submeshes[smIdx];
6426
6299
  if (sm === void 0) continue;
@@ -6429,6 +6302,34 @@ function recordSpriteTransparentPbrDraws(c, spritePass, matchedIndices, material
6429
6302
  if (submeshMaterial.transparent !== true) continue;
6430
6303
  const smShaderId = submeshMaterial.materialShaderId;
6431
6304
  if (smShaderId === void 0) continue;
6305
+ const probeBlendAvailable = runtime.device.caps.storageBuffer && entry.source.probeBlendRecord !== void 0 && isStandardPbrMaterialShader(smShaderId);
6306
+ const probeBinding = probeBlendAvailable ? resolveProbeBlendBuffer(
6307
+ c,
6308
+ entry.source.probeBlendRecord,
6309
+ worldEntityKey(entry.source.worldId, entry.source.entityKey)
6310
+ ) : void 0;
6311
+ const identityInstBg = resolveGeometryInstancesBindGroup(
6312
+ c,
6313
+ pipelineState.identityInstanceBuffer,
6314
+ probeBinding?.buffer
6315
+ );
6316
+ if (probeBinding === void 0) spritePass.setBindGroup(3, identityInstBg);
6317
+ else spritePass.setBindGroup(3, identityInstBg, [probeBinding.offset]);
6318
+ const subVariantSet = standardTopologyVariantSet(
6319
+ c.standardLighting,
6320
+ runtime.device.caps.storageBuffer,
6321
+ entry.mesh.layoutProjection.attributes.some((attribute) => attribute.key === "color"),
6322
+ probeBlendAvailable
6323
+ );
6324
+ const projectionVariantSetResult = variantSetFromVertexLayoutProjection(
6325
+ entry.mesh.layoutProjection,
6326
+ subVariantSet
6327
+ );
6328
+ if (!projectionVariantSetResult.ok) {
6329
+ runtime.errorRegistry.fire(projectionVariantSetResult.error);
6330
+ continue;
6331
+ }
6332
+ const projectionVariantSet = projectionVariantSetResult.value;
6432
6333
  const subPipelineEntry = c.runtime.getMaterialShaderPipelineEntry?.(
6433
6334
  smShaderId,
6434
6335
  isHdrTarget,
@@ -6528,9 +6429,10 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
6528
6429
  spritePass.setVertexBuffer(0, spriteEntry.mesh.vertexBuffer.handle);
6529
6430
  lastSpriteVertexBuffer = spriteEntry.mesh.vertexBuffer;
6530
6431
  }
6531
- if (spriteEntry.mesh.indexBuffer !== null && spriteEntry.mesh.indexBuffer !== lastSpriteIndexBuffer) {
6532
- spritePass.setIndexBuffer(spriteEntry.mesh.indexBuffer.handle, spriteEntry.mesh.indexFormat);
6533
- lastSpriteIndexBuffer = spriteEntry.mesh.indexBuffer;
6432
+ const indexBuffer = spriteEntry.mesh.indexBuffer;
6433
+ if (indexBuffer !== null && indexBuffer !== lastSpriteIndexBuffer) {
6434
+ spritePass.setIndexBuffer(indexBuffer.handle, spriteEntry.mesh.indexFormat);
6435
+ lastSpriteIndexBuffer = indexBuffer;
6534
6436
  }
6535
6437
  let spriteInstanceBuffer = pipelineState.identityInstanceBuffer;
6536
6438
  let spriteInstanceCount = 1;
@@ -6599,21 +6501,12 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
6599
6501
  );
6600
6502
  spriteInstanceCount = spriteInst.instanceCount;
6601
6503
  spriteInstanceBuffer = pipelineState.identityInstanceBuffer;
6602
- const spriteInstBgResult = runtime.device.createBindGroup({
6603
- label: "sprite-pass-instances-bg",
6604
- layout: pipelineState.instancesBindGroupLayout,
6605
- entries: [
6606
- {
6607
- binding: 0,
6608
- resource: {
6609
- kind: "buffer",
6610
- value: { buffer: spriteInstanceBuffer }
6611
- }
6612
- }
6613
- ]
6614
- });
6615
- if (!spriteInstBgResult.ok) throw spriteInstBgResult.error;
6616
- spritePass.setBindGroup(3, spriteInstBgResult.value);
6504
+ const spriteInstBg = resolveGeometryInstancesBindGroup(
6505
+ c,
6506
+ spriteInstanceBuffer,
6507
+ void 0
6508
+ );
6509
+ spritePass.setBindGroup(3, spriteInstBg);
6617
6510
  spritePass.drawIndexed(spriteEntry.mesh.indexCount, spriteInstanceCount, 0, 0, 0);
6618
6511
  continue;
6619
6512
  }
@@ -6692,31 +6585,9 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
6692
6585
  spriteInstanceBuffer = _si.buffer;
6693
6586
  spriteInstanceCount = _si.count;
6694
6587
  const spriteGroup2 = entityShaderId === "forgeax::sprite-lit" && c.standardLighting?.kind === "clustered" ? c.hdrpClusterBindGroup : meshBindGroup;
6695
- const spriteInstancesBg = getOrCreatePerEntity(
6696
- frameState.instancesBgPerEntity,
6697
- worldEntityKey(spriteEntry.source.worldId, spriteEntry.source.entityKey),
6698
- [spriteInstanceBuffer],
6699
- "instances",
6700
- () => {
6701
- const result = runtime.device.createBindGroup({
6702
- label: "sprite-pass-instances-bg",
6703
- layout: pipelineState.instancesBindGroupLayout,
6704
- entries: [
6705
- {
6706
- binding: 0,
6707
- resource: {
6708
- kind: "buffer",
6709
- value: { buffer: spriteInstanceBuffer }
6710
- }
6711
- }
6712
- ]
6713
- });
6714
- if (!result.ok) throw result.error;
6715
- return result.value;
6716
- },
6717
- bindGroupCounts
6718
- );
6588
+ const spriteInstancesBg = resolveGeometryInstancesBindGroup(c, spriteInstanceBuffer, void 0);
6719
6589
  spritePass.setBindGroup(2, spriteGroup2, [i * MESH_PER_ENTITY_STRIDE]);
6590
+ const materialSlot = materialSlotIndices[i]?.[0] ?? 0;
6720
6591
  const spriteTexHandle = spriteEntry.source.material.baseColorTexture;
6721
6592
  let spriteTexView = pipelineState.defaultWhiteTextureView;
6722
6593
  if (spriteTexHandle !== void 0) {
@@ -6747,7 +6618,6 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
6747
6618
  },
6748
6619
  bindGroupCounts
6749
6620
  );
6750
- const materialSlot = materialSlotIndices[i]?.[0] ?? 0;
6751
6621
  spritePass.setBindGroup(1, spritePassBg, [materialSlot * MATERIAL_PER_ENTITY_STRIDE]);
6752
6622
  spritePass.setBindGroup(3, spriteInstancesBg);
6753
6623
  spritePass.drawIndexed(spriteEntry.mesh.indexCount, spriteInstanceCount, 0, 0, 0);
@@ -6833,26 +6703,6 @@ function transparentPassColorFormat(c, pipelineState = c.pipelineState) {
6833
6703
  if (!c.runtime.device.caps.storageBuffer) return pipelineState.colorAttachmentFormat;
6834
6704
  return pipelineState.format;
6835
6705
  }
6836
-
6837
- // src/systems/pass-selector.ts
6838
- function matchPass(tags, selector) {
6839
- if (Object.keys(selector).length === 0) return true;
6840
- for (const [key, allowedValues] of Object.entries(selector)) {
6841
- const passValue = tags[key];
6842
- if (passValue === void 0) return false;
6843
- if (allowedValues.length === 0) return false;
6844
- if (!allowedValues.includes(passValue)) return false;
6845
- }
6846
- return true;
6847
- }
6848
- function selectPasses(passes, selector) {
6849
- if (Object.keys(selector).length === 0) return passes;
6850
- return passes.filter(
6851
- (p) => matchPass(p.renderState?.tags ?? {}, selector)
6852
- );
6853
- }
6854
-
6855
- // src/record/shadow-pass.ts
6856
6706
  var SHADOW_CASTER_SLOT_STRIDE = 256;
6857
6707
  var SHADOW_CASTER_BUFFER_SIZE = SHADOW_CASTER_SLOT_STRIDE * 8;
6858
6708
  function directionalShadowCasterOffset(cascadeIndex) {
@@ -6884,21 +6734,69 @@ function ensureTypedShadowViewBg(c, viewOffset, cascadeOffset, variant) {
6884
6734
  const { runtime, frameState, pipelineState } = c;
6885
6735
  const shadowSampler = pipelineState.perPassResources.shadowSampler;
6886
6736
  if (shadowSampler === null) return null;
6737
+ const extendedLighting = pipelineState.extendedLightingAvailable ?? false;
6738
+ const iesProfileTextureView = pipelineState.iesProfileTextureView;
6739
+ const cookieTextureView = pipelineState.cookieTextureView;
6740
+ const cookieMatrixBuffer = pipelineState.cookieMatrixBuffer;
6741
+ const ltcLambertTextureView = pipelineState.ltcLambertTextureView;
6742
+ const ltcGgxTextureView = pipelineState.ltcGgxTextureView;
6743
+ const extendedLightingCacheKeys = [];
6744
+ const extendedLightingEntries = [];
6745
+ if (extendedLighting) {
6746
+ if (iesProfileTextureView === void 0 || cookieTextureView === void 0 || cookieMatrixBuffer === void 0 || ltcLambertTextureView === void 0 || ltcGgxTextureView === void 0) {
6747
+ return null;
6748
+ }
6749
+ extendedLightingCacheKeys.push(
6750
+ pipelineState.defaultSampler,
6751
+ iesProfileTextureView,
6752
+ cookieTextureView,
6753
+ ltcLambertTextureView,
6754
+ ltcGgxTextureView,
6755
+ cookieMatrixBuffer
6756
+ );
6757
+ extendedLightingEntries.push(
6758
+ {
6759
+ binding: 9,
6760
+ resource: { kind: "sampler", value: pipelineState.defaultSampler }
6761
+ },
6762
+ {
6763
+ binding: 11,
6764
+ resource: { kind: "textureView", value: iesProfileTextureView }
6765
+ },
6766
+ {
6767
+ binding: 12,
6768
+ resource: { kind: "textureView", value: cookieTextureView }
6769
+ },
6770
+ {
6771
+ binding: 13,
6772
+ resource: { kind: "textureView", value: ltcLambertTextureView }
6773
+ },
6774
+ {
6775
+ binding: 14,
6776
+ resource: { kind: "textureView", value: ltcGgxTextureView }
6777
+ },
6778
+ {
6779
+ binding: 15,
6780
+ resource: {
6781
+ kind: "buffer",
6782
+ value: { buffer: cookieMatrixBuffer, size: COOKIE_MATRIX_BYTES }
6783
+ }
6784
+ }
6785
+ );
6786
+ }
6887
6787
  const projectorAvailable = pipelineState.projectorAvailable !== false;
6888
6788
  try {
6889
6789
  return getOrCreateFromChain(
6890
6790
  frameState.viewBindGroupCache,
6891
6791
  [
6892
6792
  pipelineState.viewUniformBuffer,
6893
- pipelineState.pointLightsBuffer,
6894
- pipelineState.spotLightsBuffer,
6895
6793
  pipelineState.shadowFallbackTextureView,
6896
6794
  shadowSampler,
6897
6795
  pipelineState.shadowAtlasFallbackTextureView,
6898
6796
  pipelineState.shadowParamsBuffer,
6899
6797
  pipelineState.shadowCasterCascadeBuffer,
6900
6798
  pipelineState.shadowFallbackTextureView,
6901
- ...projectorAvailable ? [pipelineState.defaultWhiteTextureView, pipelineState.defaultSampler] : [],
6799
+ ...extendedLighting ? extendedLightingCacheKeys : projectorAvailable ? [pipelineState.defaultWhiteTextureView, pipelineState.defaultSampler] : [],
6902
6800
  pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer
6903
6801
  ],
6904
6802
  variant,
@@ -6918,14 +6816,6 @@ function ensureTypedShadowViewBg(c, viewOffset, cascadeOffset, variant) {
6918
6816
  }
6919
6817
  }
6920
6818
  },
6921
- {
6922
- binding: 1,
6923
- resource: { kind: "buffer", value: { buffer: pipelineState.pointLightsBuffer } }
6924
- },
6925
- {
6926
- binding: 2,
6927
- resource: { kind: "buffer", value: { buffer: pipelineState.spotLightsBuffer } }
6928
- },
6929
6819
  {
6930
6820
  binding: 3,
6931
6821
  resource: { kind: "textureView", value: pipelineState.shadowFallbackTextureView }
@@ -6960,6 +6850,7 @@ function ensureTypedShadowViewBg(c, viewOffset, cascadeOffset, variant) {
6960
6850
  binding: 8,
6961
6851
  resource: { kind: "textureView", value: pipelineState.shadowFallbackTextureView }
6962
6852
  },
6853
+ ...extendedLightingEntries,
6963
6854
  {
6964
6855
  binding: 10,
6965
6856
  resource: {
@@ -6970,7 +6861,7 @@ function ensureTypedShadowViewBg(c, viewOffset, cascadeOffset, variant) {
6970
6861
  }
6971
6862
  }
6972
6863
  },
6973
- ...projectorAvailable ? [
6864
+ ...!extendedLighting && projectorAvailable ? [
6974
6865
  {
6975
6866
  binding: 11,
6976
6867
  resource: {
@@ -7299,30 +7190,7 @@ function recordShadowCasterDraws(c, shadowPass, shadowPipeline2, shadowMeshBindG
7299
7190
  }
7300
7191
  }
7301
7192
  }
7302
- const shadowInstancesBg = getOrCreatePerEntity(
7303
- c.frameState.instancesBgPerEntity,
7304
- worldEntityKey(entry.source.worldId, entry.source.entityKey),
7305
- [shadowInstanceBuffer],
7306
- "shadow-instances",
7307
- () => {
7308
- const result = runtime.device.createBindGroup({
7309
- label: "shadow-instances-bg",
7310
- layout: pipelineState.instancesBindGroupLayout,
7311
- entries: [
7312
- {
7313
- binding: 0,
7314
- resource: {
7315
- kind: "buffer",
7316
- value: { buffer: shadowInstanceBuffer }
7317
- }
7318
- }
7319
- ]
7320
- });
7321
- if (!result.ok) throw result.error;
7322
- return result.value;
7323
- },
7324
- c.bindGroupCounts
7325
- );
7193
+ const shadowInstancesBg = resolveGeometryInstancesBindGroup(c, shadowInstanceBuffer);
7326
7194
  shadowPass.setBindGroup(3, shadowInstancesBg);
7327
7195
  for (const sm of shadowSubmeshes) {
7328
7196
  if (sm.topology !== "triangle-list" && sm.topology !== "triangle-strip") {
@@ -7420,7 +7288,13 @@ function ensureSpotShadowMaterialBg(c) {
7420
7288
 
7421
7289
  // src/record/main-pass.ts
7422
7290
  var ZERO_SKYLIGHT_PAYLOAD = new Float32Array([0, 0, 0, 0, 0, 0, 0, 1]);
7423
- var TRANSMISSION_VARIANT_SET = "TRANSMISSION_AVAILABLE=true";
7291
+ function materialDiagnosticsEnabled3() {
7292
+ if (typeof globalThis !== "object" || globalThis === null || !("process" in globalThis)) {
7293
+ return false;
7294
+ }
7295
+ const processValue = globalThis.process;
7296
+ return processValue?.env?.FORGEAX_MATERIAL_DIAGNOSTICS === "1";
7297
+ }
7424
7298
  function isTransmissionMaterial(material) {
7425
7299
  return material.materialShaderId === "forgeax::default-standard-pbr" && typeof material.paramSnapshot?.transmission === "number" && material.paramSnapshot.transmission > 0;
7426
7300
  }
@@ -7540,11 +7414,7 @@ function recordMainPass(c, selector, options, graphPass) {
7540
7414
  const matchedMaterials = matchedMaterialsForRecordMode(c, selectorMatchedMaterials, recordMode);
7541
7415
  const recordContext = recordMode === void 0 ? c : {
7542
7416
  ...c,
7543
- splitLdrSprite: recordMode === "transparent" ? false : c.splitLdrSprite,
7544
- validatedOrdered: c.validatedOrdered.map((entry) => ({
7545
- ...entry,
7546
- ...recordMode === "transmission" ? { variantSet: TRANSMISSION_VARIANT_SET } : {}
7547
- }))
7417
+ splitLdrSprite: recordMode === "transparent" ? false : c.splitLdrSprite
7548
7418
  };
7549
7419
  if (clusteredBindGroupMissing) {
7550
7420
  runtime.errorRegistry.fire(
@@ -7598,6 +7468,62 @@ function recordMainPass(c, selector, options, graphPass) {
7598
7468
  brdfLutSampler: skylightFallback.sampler,
7599
7469
  intensityBuffer: skylightFallback.intensityBuffer
7600
7470
  };
7471
+ if (materialDiagnosticsEnabled3()) {
7472
+ const activeCache = activeViews === void 0 ? void 0 : getOrCreateIblCache(runtime.deviceScope);
7473
+ const prefilterViewMipCount = activeCache?.prefilterFaceViewsByMip?.length ?? (activeViews === void 0 ? 1 : 0);
7474
+ frameState.iblBindingInspection = {
7475
+ status: "binding-chain-consistent",
7476
+ frameId: frameState.frameNumber,
7477
+ deviceGeneration: runtime.deviceScope.generation,
7478
+ active: activeViews === void 0 ? "fallback" : "active",
7479
+ cache: {
7480
+ identity: getOpaqueResourceIdentity(activeCache ?? skylightFallback),
7481
+ generation: runtime.deviceScope.generation,
7482
+ prefilterMipCount: prefilterViewMipCount,
7483
+ prefilterViewMipCount
7484
+ },
7485
+ sampler: {
7486
+ expected: {
7487
+ magFilter: "linear",
7488
+ minFilter: "linear",
7489
+ mipmapFilter: "linear",
7490
+ addressModeU: "clamp-to-edge",
7491
+ addressModeV: "clamp-to-edge",
7492
+ addressModeW: "clamp-to-edge"
7493
+ },
7494
+ identities: [
7495
+ getOpaqueResourceIdentity(skylightResources.irradianceSampler),
7496
+ getOpaqueResourceIdentity(skylightResources.prefilterSampler),
7497
+ getOpaqueResourceIdentity(skylightResources.brdfLutSampler)
7498
+ ]
7499
+ },
7500
+ resources: {
7501
+ irradiance: {
7502
+ viewIdentity: getOpaqueResourceIdentity(skylightResources.irradianceView),
7503
+ samplerIdentity: getOpaqueResourceIdentity(
7504
+ skylightResources.irradianceSampler
7505
+ ),
7506
+ deviceGeneration: runtime.deviceScope.generation
7507
+ },
7508
+ prefilter: {
7509
+ viewIdentity: getOpaqueResourceIdentity(skylightResources.prefilterView),
7510
+ samplerIdentity: getOpaqueResourceIdentity(
7511
+ skylightResources.prefilterSampler
7512
+ ),
7513
+ deviceGeneration: runtime.deviceScope.generation
7514
+ },
7515
+ brdfLut: {
7516
+ viewIdentity: getOpaqueResourceIdentity(skylightResources.brdfLutView),
7517
+ samplerIdentity: getOpaqueResourceIdentity(skylightResources.brdfLutSampler),
7518
+ deviceGeneration: runtime.deviceScope.generation
7519
+ },
7520
+ intensityBufferIdentity: getOpaqueResourceIdentity(
7521
+ skylightResources.intensityBuffer
7522
+ )
7523
+ },
7524
+ errors: []
7525
+ };
7526
+ }
7601
7527
  const perSubmeshMaterialBgDeps = {
7602
7528
  runtime,
7603
7529
  pipelineState,
@@ -7633,6 +7559,7 @@ function recordMainPass(c, selector, options, graphPass) {
7633
7559
  },
7634
7560
  materialBgShared: frameState.materialBgShared,
7635
7561
  materialBgAssemblyCache: c.materialBgAssemblyCache,
7562
+ frameState,
7636
7563
  bindGroupCounts,
7637
7564
  ...transmissionBackdropView === void 0 ? {} : { transmissionBackdropView }
7638
7565
  };
@@ -7662,6 +7589,20 @@ function recordMainPass(c, selector, options, graphPass) {
7662
7589
  const materialShaderId = mat.materialShaderId;
7663
7590
  const schema = mat.materialParamSchema ?? (materialShaderId !== void 0 ? runtime.getParamSchema?.(materialShaderId) : void 0);
7664
7591
  applyParamSnapshotToUbo(slotPayloadF32, schema, mat.paramSnapshot);
7592
+ const processValue = globalThis.process;
7593
+ if (processValue?.env?.FORGEAX_MATERIAL_PIPELINE_DIAGNOSTICS === "1" && (materialShaderId === "forgeax::default-standard-pbr" || materialShaderId === "forgeax::pbr-skin")) {
7594
+ console.error(
7595
+ `[render-material] UBO receipt: ${JSON.stringify({
7596
+ entityIndex: entry.renderableIndex,
7597
+ materialHandle: mat.materialHandle,
7598
+ materialShaderId,
7599
+ clearcoat: slotPayloadF32[32],
7600
+ clearcoatRoughness: slotPayloadF32[33],
7601
+ clearcoatNormalScale: slotPayloadF32[34],
7602
+ paramSnapshot: mat.paramSnapshot
7603
+ })}`
7604
+ );
7605
+ }
7665
7606
  applyMaterialTextureUvScales(slotPayloadF32, mat, world);
7666
7607
  {
7667
7608
  const matHandleRaw = mat.baseColorTexture;
@@ -9079,6 +9020,7 @@ function addTypedScenePass(graph, options) {
9079
9020
  stencilLoadOp: options.depthLoadOp ?? "clear",
9080
9021
  stencilStoreOp: "store"
9081
9022
  },
9023
+ occlusionQuerySet: options.occlusion?.querySet,
9082
9024
  encode: ({ pass, frame, resources }) => {
9083
9025
  const colorViews = colorTargets.map((target) => resolvedView(resources, target.view));
9084
9026
  const depthView = resolvedView(resources, options.depth.view);
@@ -9108,13 +9050,16 @@ function addTypedScenePass(graph, options) {
9108
9050
  if (groups.viewBindGroup !== null) {
9109
9051
  options.gpuDriven?.encode(groups.viewBindGroup, pass, resources);
9110
9052
  }
9053
+ const activeOcclusion = options.occlusion;
9111
9054
  encodeMainPass(
9112
9055
  {
9113
9056
  ...internal,
9057
+ ...activeOcclusion === void 0 ? {} : { occlusion: activeOcclusion },
9114
9058
  geometryColorView: colorViews[0] ?? null,
9115
9059
  geometryDepthView: depthView,
9116
9060
  geometryColorResolveView: resolveView,
9117
9061
  transparentColorFormat: colorTargets[0]?.format,
9062
+ ...colorTargets[1]?.format === "rgba16float" ? { reflectionFallbackColorFormat: "rgba16float" } : {},
9118
9063
  msaaActive: colorTargets[0]?.sampleCount === 4,
9119
9064
  viewBindGroup: groups.viewBindGroup,
9120
9065
  meshBindGroup: groups.meshBindGroup,
@@ -9134,6 +9079,10 @@ function addTypedScenePass(graph, options) {
9134
9079
  ...options.transmissionBackdrop === void 0 ? {} : { transmissionBackdropView: transmissionBackdrop }
9135
9080
  }
9136
9081
  );
9082
+ if (activeOcclusion !== void 0) {
9083
+ const proxyResult = activeOcclusion.encodeProxyBounds(pass);
9084
+ if (!proxyResult.ok) throw proxyResult.error;
9085
+ }
9137
9086
  }
9138
9087
  });
9139
9088
  }
@@ -9160,6 +9109,29 @@ function addTypedFrameObservationPass(graph, target, pipelineId) {
9160
9109
  }
9161
9110
  });
9162
9111
  }
9112
+ function addReflectionFallbackObservationPass(graph, target) {
9113
+ return graph.addCopyPass("reflection-fallback-observation", {
9114
+ accesses: [{ resource: target.view, usage: "copy-src" }],
9115
+ encode: ({ frame, resources }) => {
9116
+ const internal = frame;
9117
+ const texture = resources.texture(target.texture);
9118
+ if (!texture.ok) throw texture.error;
9119
+ internal.frameState.reflectionFallbackObservationSource = {
9120
+ texture: texture.value,
9121
+ descriptor: {
9122
+ texture: texture.value,
9123
+ format: target.format,
9124
+ size: { width: frame.targetW, height: frame.targetH },
9125
+ usage: GPU_TEXTURE_USAGE_COPY_SRC | GPU_TEXTURE_USAGE_RENDER_ATTACHMENT | GPU_TEXTURE_USAGE_TEXTURE_BINDING,
9126
+ sample: target.sampleCount
9127
+ },
9128
+ frameId: internal.frameState.frameNumber,
9129
+ pipelineId: "forgeax::standard",
9130
+ backendId: internal.runtime.device.caps.backendKind
9131
+ };
9132
+ }
9133
+ });
9134
+ }
9163
9135
  var TYPED_BLOOM_PASS_NAMES = {
9164
9136
  bright: "bloom-bright",
9165
9137
  blurH: "bloom-blur-h",
@@ -9590,6 +9562,6 @@ function addTaaResolvePass(graph, current, currentTemporal, previousColor, previ
9590
9562
  });
9591
9563
  }
9592
9564
 
9593
- export { CAPTURE_VIEW_PROJS, CUBEMAP_FACE_VERTICES, CUBE_CAPTURE_VIEW_SLOT_BASE, DEFERRED_COLOR_FORMATS, DEPTH_MIN_PARAMS_BYTE_SIZE, FALLBACK_BYTES_PER_ROW, GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_COPY_SRC, GPU_BUFFER_USAGE_INDEX, GPU_BUFFER_USAGE_INDIRECT, GPU_BUFFER_USAGE_MAP_READ, GPU_BUFFER_USAGE_QUERY_RESOLVE, GPU_BUFFER_USAGE_STORAGE, GPU_BUFFER_USAGE_UNIFORM, GPU_BUFFER_USAGE_VERTEX, GPU_SHADER_STAGE_FRAGMENT, GPU_SHADER_STAGE_VERTEX, GpuBuffer, GpuTexture, LIGHT_ARRAY_HEADER_BYTES, LIGHT_ARRAY_MAX_SLOTS, MATERIAL_PER_ENTITY_STRIDE, POINTS_LINES_MATERIAL_SHADER_ID, POINTS_LINES_VIEW_BUFFER_SIZE, POINT_LIGHT_STD430_BYTES, PREFILTER_MIP_LEVELS, PREFILTER_SIZE, PipelineSpecError, PostProcessError, REFLECTION_PROBE_VIEW_SLOT_BASE, SHADOW_CASTER_BUFFER_SIZE, SKIN_MATERIAL_SHADER_ID, SPOT_LIGHT_STD430_BYTES, TYPED_BLOOM_PASS_NAMES, VIEW_UNIFORM_BUFFER_SIZE, VIEW_UNIFORM_BYTES, VIEW_UNIFORM_SLOT_STRIDE, abortTemporalGpuSubmit, addStandardSceneDataPass, addTaaResolvePass, addTypedBloomPasses, addTypedCompositePostEffects, addTypedFrameObservationPass, addTypedFullscreenPass, addTypedOutputTransformPass, addTypedScenePass, addTypedSkyboxPass, addTypedSsaoPasses, addTypedTemporalResolvePass, aggregateTemporalDemand, applyParamSnapshotToUbo, assembleMaterialWithSkylightEntries, assertStorageBufferCap, buildBindGroupLayoutDescriptor, buildFoldDispatchPlan, buildFullscreenPostProcessPass, buildLinearLdrMaterialSpecTable, buildPbrMaterialUserRegionEntries, buildPbrPipelineLayouts, buildPbrSkinLayouts, buildPerFrameBindGroups, buildSpecConstTable, cacheKeyOf, cleanPerEntityCache, colorFormatsForPassKind, commitTemporalGpuSubmit, computeProjectionMatrix, computeSplitLdrSprite, computeViewMatrix, configureSurface, createClusterBinScratch, createFullscreenBindGroup, createHdrpBindGroupLayoutDescriptor, createHdrpClusterMembershipBindGroupLayoutDescriptor, createHdrpSkinBindGroupLayoutDescriptor, createIblPipelines, createPointsLinesLaneAdapter, createPointsLinesLaneContract, createRenderPipelineTarget, createRenderTargetMaterialSource, createSkylightFallback, createStandardSceneDataTarget, deriveRenderDataCubemap, deriveRenderDataMesh, deriveRenderDataTexture, describeTemporalDemand, driveLazyEquirectProjection, encodeDirectionalShadowPass, encodeMainPass, encodePointShadowPass, encodeSpotShadowPass, ensureMeshSsboCapacity, entryHasDepthRead, evaluateFoldBucketUniformCap, foldDispatchBuckets, geometryRenderStateForTopology, getOrBuildPipeline, getOrCreateHdrpBuffers, getOrCreateIblCache, getTemporalGpuState, hasPendingTemporalGpuSubmit, importRenderPipelineSurface, incrementFoldedDrawsMetric, isStandardPbrMaterialShader, isTemporalFullscreenBinding, postProcessShaderEntrySignature, postProcessShaderModuleLabel, postProcessShaderPipelineLabel, prepareFrameLighting, resetHdrpBuffers, residentTextureView, resolveOutputDither, resolvePipelineGroup2Contract, resolveRenderTargetMaterialSource, resolveSkyboxActive, resolveSsaoParameters, resolveSurfaceFormatPair, resolveSurfaceProfile, retireTemporalGpuState, retireTemporalGpuStateAfterFence, runIblPrecompute, selectLazyEquirectHandle, selectPasses, selectSwapChainFormat, setIblComposedShaders, standardTemporalLaneAdmission, standardTemporalPostOrder, temporalReadIndex, temporalWriteIndex, typedFrameClearColor, uploadMeshSsboBatch, variantSetFromDefines, warnMultiSkybox, warnMultiSkylight, warnZeroLightStandard, writeHdrpClusterAndSsaoBuffers, writePointsLinesViewUbo, writeShadowCasterUniforms, writeShadowParamsBuffer, writeViewUbo };
9594
- //# sourceMappingURL=chunk-5YADUCA7.mjs.map
9595
- //# sourceMappingURL=chunk-5YADUCA7.mjs.map
9565
+ export { BYTES_PER_DIRECT_LIGHT_SLOT, CAPTURE_VIEW_PROJS, CUBEMAP_FACE_VERTICES, CUBE_CAPTURE_VIEW_SLOT_BASE, DEFERRED_COLOR_FORMATS, DEPTH_MIN_PARAMS_BYTE_SIZE, FALLBACK_BYTES_PER_ROW, GpuBuffer, GpuTexture, MATERIAL_PER_ENTITY_STRIDE, MESH_SSBO_BYTES, POINTS_LINES_MATERIAL_SHADER_ID, POINTS_LINES_VIEW_BUFFER_SIZE, PREFILTER_MIP_LEVELS, PREFILTER_SIZE, PipelineSpecError, PostProcessError, REFLECTION_PROBE_VIEW_SLOT_BASE, SHADOW_CASTER_BUFFER_SIZE, TYPED_BLOOM_PASS_NAMES, VIEW_UNIFORM_BUFFER_SIZE, VIEW_UNIFORM_BYTES, VIEW_UNIFORM_SLOT_STRIDE, abortTemporalGpuSubmit, addReflectionFallbackObservationPass, addStandardSceneDataPass, addTaaResolvePass, addTypedBloomPasses, addTypedCompositePostEffects, addTypedFrameObservationPass, addTypedFullscreenPass, addTypedOutputTransformPass, addTypedScenePass, addTypedSkyboxPass, addTypedSsaoPasses, addTypedTemporalResolvePass, aggregateTemporalDemand, applyParamSnapshotToUbo, assembleMaterialWithSkylightEntries, assertStorageBufferCap, buildFoldDispatchPlan, buildFullscreenPostProcessPass, buildLinearLdrMaterialSpecTable, buildPerFrameBindGroups, buildSpecConstTable, cacheKeyOf, cleanPerEntityCache, colorFormatsForPassKind, commitTemporalGpuSubmit, computeProjectionMatrix, computeSplitLdrSprite, computeViewMatrix, configureSurface, createClusterBinScratch, createFullscreenBindGroup, createHdrpClusterMembershipBindGroupLayoutDescriptor, createIblPipelines, createPointsLinesLaneAdapter, createPointsLinesLaneContract, createRenderPipelineTarget, createSkylightFallback, createStandardSceneDataTarget, deriveRenderDataCubemap, deriveRenderDataMesh, deriveRenderDataTexture, describeTemporalDemand, driveLazyEquirectProjection, encodeDirectionalShadowPass, encodeMainPass, encodePointShadowPass, encodeSpotShadowPass, ensureMeshSsboCapacity, entryHasDepthRead, evaluateFoldBucketUniformCap, foldDispatchBuckets, geometryRenderStateForPass, geometryRenderStateForTopology, getOrBuildPipeline, getOrCreateHdrpBuffers, getOrCreateIblCache, getTemporalBindGroupResources, getTemporalGpuState, getTemporalParamsBuffer, hasPendingTemporalGpuSubmit, importRenderPipelineSurface, incrementFoldedDrawsMetric, isTemporalFullscreenBinding, passKindPolicyTable, postProcessShaderEntrySignature, postProcessShaderModuleLabel, postProcessShaderPipelineLabel, prepareFrameLighting, resetHdrpBuffers, residentTextureView, resolveOutputDither, resolveSkyboxActive, resolveSsaoParameters, resolveSurfaceFormatPair, resolveSurfaceProfile, retireTemporalGpuState, retireTemporalGpuStateAfterFence, runIblPrecompute, selectLazyEquirectHandle, selectSwapChainFormat, setIblComposedShaders, standardStorageVariantSet, standardTemporalLaneAdmission, standardTemporalPostOrder, standardTopologyVariantSet, temporalReadIndex, temporalWriteIndex, typedFrameClearColor, uploadMeshSsboBatch, variantSetFromDefines, variantSetFromVertexLayoutProjection, warnMultiSkybox, warnMultiSkylight, warnZeroLightStandard, writeHdrpClusterAndSsaoBuffers, writePointsLinesViewUbo, writeShadowCasterUniforms, writeShadowParamsBuffer, writeSpotModifierTextures, writeViewUbo };
9566
+ //# sourceMappingURL=chunk-YRUYGA4A.mjs.map
9567
+ //# sourceMappingURL=chunk-YRUYGA4A.mjs.map