@forgeax/engine-render 0.1.23 → 0.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/README.md +231 -10
  2. package/dist/assembly/bundler-contract.d.ts +9 -0
  3. package/dist/assembly/bundler-contract.d.ts.map +1 -0
  4. package/dist/assembly/device-feature-admission.d.ts +8 -0
  5. package/dist/assembly/device-feature-admission.d.ts.map +1 -1
  6. package/dist/assembly/extended-lighting-fallback.d.ts +13 -0
  7. package/dist/assembly/extended-lighting-fallback.d.ts.map +1 -0
  8. package/dist/assembly/factory.d.ts +7 -243
  9. package/dist/assembly/factory.d.ts.map +1 -1
  10. package/dist/assembly/material/assembly.d.ts +3 -2
  11. package/dist/assembly/material/assembly.d.ts.map +1 -1
  12. package/dist/assembly/material/standard-layer-projection.d.ts +10 -0
  13. package/dist/assembly/material/standard-layer-projection.d.ts.map +1 -0
  14. package/dist/assembly/material/surface-projection.d.ts +24 -0
  15. package/dist/assembly/material/surface-projection.d.ts.map +1 -0
  16. package/dist/assembly/material-shader-policy.d.ts +188 -0
  17. package/dist/assembly/material-shader-policy.d.ts.map +1 -0
  18. package/dist/assembly/material-shader-prewarm.d.ts +21 -0
  19. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -0
  20. package/dist/assembly/mesh-ssbo-grow.d.ts +88 -0
  21. package/dist/assembly/mesh-ssbo-grow.d.ts.map +1 -0
  22. package/dist/assembly/recovery/device-loss-fanout.d.ts +14 -0
  23. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -0
  24. package/dist/assembly/recovery/generation.d.ts +46 -0
  25. package/dist/assembly/recovery/generation.d.ts.map +1 -0
  26. package/dist/assembly/recovery/recovery-attempt.d.ts +63 -0
  27. package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -0
  28. package/dist/assembly/recovery/renderer-recover.d.ts +115 -0
  29. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -0
  30. package/dist/assembly/renderer-facade.d.ts +10 -0
  31. package/dist/assembly/renderer-facade.d.ts.map +1 -0
  32. package/dist/assembly/renderer-frame-transaction.d.ts +17 -0
  33. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  34. package/dist/assembly/renderer-helpers.d.ts +19 -0
  35. package/dist/assembly/renderer-helpers.d.ts.map +1 -0
  36. package/dist/assembly/renderer-lifecycle.d.ts +34 -0
  37. package/dist/assembly/renderer-lifecycle.d.ts.map +1 -1
  38. package/dist/assembly/shader-prewarm-policy.d.ts +15 -0
  39. package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -0
  40. package/dist/assembly/webgpu-ready-contract.d.ts +9 -0
  41. package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -0
  42. package/dist/assembly/webgpu-ready.d.ts +55 -0
  43. package/dist/assembly/webgpu-ready.d.ts.map +1 -0
  44. package/dist/assembly/webgpu-renderer.d.ts +152 -0
  45. package/dist/assembly/webgpu-renderer.d.ts.map +1 -0
  46. package/dist/assembly/webgpu-vertex-layouts.d.ts +89 -0
  47. package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -0
  48. package/dist/assets/asset-decoders.d.ts.map +1 -1
  49. package/dist/authoring.mjs +1 -1
  50. package/dist/{chunk-FF7K3FSO.mjs → chunk-ADHHVYLW.mjs} +89 -89
  51. package/dist/chunk-ADHHVYLW.mjs.map +1 -0
  52. package/dist/{chunk-YALPS44A.mjs → chunk-E34VL5VI.mjs} +47 -47
  53. package/dist/chunk-E34VL5VI.mjs.map +1 -0
  54. package/dist/{chunk-4L3P6DT5.mjs → chunk-GE3SDD72.mjs} +37 -5
  55. package/dist/chunk-GE3SDD72.mjs.map +1 -0
  56. package/dist/{chunk-HZUPSYW7.mjs → chunk-HKXTW355.mjs} +105 -15
  57. package/dist/chunk-HKXTW355.mjs.map +1 -0
  58. package/dist/chunk-JSEDTZYS.mjs +413 -0
  59. package/dist/chunk-JSEDTZYS.mjs.map +1 -0
  60. package/dist/{chunk-FBC4R44J.mjs → chunk-KM2NOX2I.mjs} +57 -57
  61. package/dist/chunk-KM2NOX2I.mjs.map +1 -0
  62. package/dist/{chunk-DIYW3M5D.mjs → chunk-N3RT2EUR.mjs} +2288 -2238
  63. package/dist/chunk-N3RT2EUR.mjs.map +1 -0
  64. package/dist/{chunk-ZUWKAZ2I.mjs → chunk-TCLDX7SZ.mjs} +1167 -368
  65. package/dist/chunk-TCLDX7SZ.mjs.map +1 -0
  66. package/dist/{chunk-QODA5UKU.mjs → chunk-ZZ4YQ474.mjs} +2 -2
  67. package/dist/chunk-ZZ4YQ474.mjs.map +1 -0
  68. package/dist/components/instances.d.ts +13 -31
  69. package/dist/components/instances.d.ts.map +1 -1
  70. package/dist/construct-renderer.mjs +20688 -16314
  71. package/dist/construct-renderer.mjs.map +1 -1
  72. package/dist/device/device-scope.d.ts +2 -0
  73. package/dist/device/device-scope.d.ts.map +1 -1
  74. package/dist/device/gpu-residency.d.ts +24 -4
  75. package/dist/device/gpu-residency.d.ts.map +1 -1
  76. package/dist/environment/frame.d.ts +5 -0
  77. package/dist/environment/frame.d.ts.map +1 -1
  78. package/dist/environment/inspection.d.ts +7 -0
  79. package/dist/environment/inspection.d.ts.map +1 -1
  80. package/dist/environment/lifecycle.d.ts +20 -2
  81. package/dist/environment/lifecycle.d.ts.map +1 -1
  82. package/dist/errors/recover.d.ts +58 -2
  83. package/dist/errors/recover.d.ts.map +1 -1
  84. package/dist/errors/render.d.ts +13 -0
  85. package/dist/errors/render.d.ts.map +1 -1
  86. package/dist/extract/contracts.d.ts +899 -0
  87. package/dist/extract/contracts.d.ts.map +1 -0
  88. package/dist/features/host.d.ts +2 -0
  89. package/dist/features/host.d.ts.map +1 -1
  90. package/dist/features/prepared-gpu-work.d.ts +3 -0
  91. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  92. package/dist/features/prepared-graphics-store.d.ts +2 -0
  93. package/dist/features/prepared-graphics-store.d.ts.map +1 -1
  94. package/dist/gpu-driven/production-raster.d.ts +34 -1
  95. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  96. package/dist/gpu-scene.d.ts +5 -0
  97. package/dist/gpu-scene.d.ts.map +1 -1
  98. package/dist/ibl/IblPipelineCache.d.ts +7 -6
  99. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  100. package/dist/ibl/skylight-bind-group.d.ts +7 -1
  101. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  102. package/dist/index.d.ts +2 -1
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.mjs +27 -9
  105. package/dist/index.mjs.map +1 -1
  106. package/dist/instance-buffer-cache.d.ts +7 -1
  107. package/dist/instance-buffer-cache.d.ts.map +1 -1
  108. package/dist/instances.d.ts +101 -0
  109. package/dist/instances.d.ts.map +1 -0
  110. package/dist/internal.mjs +6 -6
  111. package/dist/light-buffer-layout.d.ts +3 -0
  112. package/dist/light-buffer-layout.d.ts.map +1 -1
  113. package/dist/materials.d.ts +96 -11
  114. package/dist/materials.d.ts.map +1 -1
  115. package/dist/mesh-material-bindings.d.ts +56 -0
  116. package/dist/mesh-material-bindings.d.ts.map +1 -1
  117. package/dist/occlusion-proxy.d.ts +4 -0
  118. package/dist/occlusion-proxy.d.ts.map +1 -0
  119. package/dist/pbr-pipeline.d.ts +32 -9
  120. package/dist/pbr-pipeline.d.ts.map +1 -1
  121. package/dist/pipeline-builder.d.ts.map +1 -1
  122. package/dist/pipeline-spec.d.ts.map +1 -1
  123. package/dist/points-lines/prepare.d.ts +3 -0
  124. package/dist/points-lines/prepare.d.ts.map +1 -1
  125. package/dist/points-lines/standard-owner.d.ts +33 -0
  126. package/dist/points-lines/standard-owner.d.ts.map +1 -0
  127. package/dist/record/frame-snapshot.d.ts +18 -2
  128. package/dist/record/frame-snapshot.d.ts.map +1 -1
  129. package/dist/record/frame-targets.d.ts.map +1 -1
  130. package/dist/record/frame.d.ts +14 -2
  131. package/dist/record/frame.d.ts.map +1 -1
  132. package/dist/record/main-pass-geometry.d.ts +36 -4
  133. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  134. package/dist/record/main-pass-material.d.ts +7 -17
  135. package/dist/record/main-pass-material.d.ts.map +1 -1
  136. package/dist/record/main-pass.d.ts.map +1 -1
  137. package/dist/record/mesh-ssbo.d.ts +11 -3
  138. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  139. package/dist/record/recovery-pipeline.d.ts +30 -0
  140. package/dist/record/recovery-pipeline.d.ts.map +1 -0
  141. package/dist/record/render-context.d.ts +28 -5
  142. package/dist/record/render-context.d.ts.map +1 -1
  143. package/dist/record/shadow-pass.d.ts +11 -1
  144. package/dist/record/shadow-pass.d.ts.map +1 -1
  145. package/dist/record/typed-frame-graph.d.ts +1 -1
  146. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  147. package/dist/recovery/render-system-candidate.d.ts +65 -0
  148. package/dist/recovery/render-system-candidate.d.ts.map +1 -0
  149. package/dist/render-contract.d.ts +68 -7
  150. package/dist/render-contract.d.ts.map +1 -1
  151. package/dist/render-system-extract-tail.d.ts +16 -0
  152. package/dist/render-system-extract-tail.d.ts.map +1 -0
  153. package/dist/render-system-extract.d.ts +180 -22
  154. package/dist/render-system-extract.d.ts.map +1 -1
  155. package/dist/render-system.d.ts +131 -4
  156. package/dist/render-system.d.ts.map +1 -1
  157. package/dist/scene/render-scene-types.d.ts +2 -0
  158. package/dist/scene/render-scene-types.d.ts.map +1 -1
  159. package/dist/scene/render-scene.d.ts +31 -1
  160. package/dist/scene/render-scene.d.ts.map +1 -1
  161. package/dist/systems/skin-palette-allocator.d.ts +6 -0
  162. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  163. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  164. package/dist/temporal/index.mjs +6 -6
  165. package/dist/temporal/taa-history-store.d.ts +15 -1
  166. package/dist/temporal/taa-history-store.d.ts.map +1 -1
  167. package/dist/temporal/transaction.d.ts +5 -0
  168. package/dist/temporal/transaction.d.ts.map +1 -1
  169. package/package.json +20 -20
  170. package/src/__tests__/device-feature-admission.unit.test.ts +6 -0
  171. package/src/__tests__/device-loss-fanout.unit.test.ts +107 -0
  172. package/src/__tests__/device-scope-lifecycle.unit.test.ts +21 -0
  173. package/src/__tests__/environment-generation.integration.test.ts +23 -0
  174. package/src/__tests__/expose-renderer-errors.unit.test.ts +34 -0
  175. package/src/__tests__/factory-contract.integration.test.ts +122 -7
  176. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +4 -1
  177. package/src/__tests__/gpu-scene.unit.test.ts +38 -2
  178. package/src/__tests__/ibl/face-uniforms.unit.test.ts +2 -0
  179. package/src/__tests__/ibl-residency-publication.unit.test.ts +113 -0
  180. package/src/__tests__/instances-store.unit.test.ts +46 -0
  181. package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
  182. package/src/__tests__/material-contract-inventory.unit.test.ts +29 -22
  183. package/src/__tests__/material-cooked-projection.unit.test.ts +19 -1
  184. package/src/__tests__/material-pass-policy.unit.test.ts +69 -0
  185. package/src/__tests__/material-snapshot-mutation.integration.test.ts +69 -0
  186. package/src/__tests__/material-static-dynamic-route.unit.test.ts +29 -0
  187. package/src/__tests__/material-stride-surface.unit.test.ts +7 -3
  188. package/src/__tests__/material-surface-authoring.test-d.ts +62 -0
  189. package/src/__tests__/materials-standard-contract.unit.test.ts +104 -0
  190. package/src/__tests__/materials-transmission-validation.unit.test.ts +24 -0
  191. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -5
  192. package/src/__tests__/physical-clearcoat.integration.test.ts +89 -0
  193. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +1 -1
  194. package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +1 -1
  195. package/src/__tests__/recover-error-policy-owner.unit.test.ts +6 -3
  196. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +140 -0
  197. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +127 -18
  198. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +47 -0
  199. package/src/__tests__/renderer-generation-assembly.unit.test.ts +77 -0
  200. package/src/__tests__/renderer-generation-failure.unit.test.ts +108 -0
  201. package/src/__tests__/renderer-producer-root-matrix.integration.test.ts +33 -0
  202. package/src/__tests__/renderer-recovery-capability-matrix.unit.test.ts +59 -0
  203. package/src/__tests__/renderer-recovery-contract.unit.test.ts +73 -0
  204. package/src/__tests__/renderer-recovery-lifecycle.unit.test.ts +216 -0
  205. package/src/__tests__/renderer-target-history-recovery.integration.test.ts +80 -0
  206. package/src/__tests__/shader-manifest-fixture.ts +13 -16
  207. package/src/__tests__/shader-stage-surface.unit.test.ts +1 -1
  208. package/src/__tests__/skin-motion-regression.unit.test.ts +33 -0
  209. package/src/__tests__/skinned-shadow-caster.test.ts +30 -2
  210. package/src/__tests__/standard-layer-projection.unit.test.ts +23 -0
  211. package/src/__tests__/surface-adjacent-regressions.integration.test.ts +60 -0
  212. package/src/__tests__/surface-standard-pipeline.browser.test.ts +20 -0
  213. package/src/__tests__/surface-standard-pipeline.dawn.test.ts +20 -0
  214. package/src/__tests__/surface-standard-pipeline.fixture.ts +132 -0
  215. package/src/__tests__/surface-variant-provenance.integration.test.ts +33 -0
  216. package/src/__tests__/transmission-prewarm.unit.test.ts +95 -85
  217. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +1 -1
  218. package/src/__tests__/volumetric-fog-topology.unit.test.ts +3 -1
  219. package/src/assembly/bundler-contract.ts +9 -0
  220. package/src/assembly/device-feature-admission.ts +15 -5
  221. package/src/assembly/extended-lighting-fallback.ts +216 -0
  222. package/src/assembly/factory.ts +83 -10088
  223. package/src/assembly/material/__tests__/standard-layer-plan-projection.integration.test.ts +18 -0
  224. package/src/assembly/material/__tests__/surface-pass-projection.integration.test.ts +62 -0
  225. package/src/assembly/material/assembly.ts +18 -2
  226. package/src/assembly/material/standard-layer-projection.ts +16 -0
  227. package/src/assembly/material/surface-projection.ts +90 -0
  228. package/src/assembly/material-shader-policy.ts +932 -0
  229. package/src/assembly/material-shader-prewarm.ts +88 -0
  230. package/src/assembly/mesh-ssbo-grow.ts +270 -0
  231. package/src/assembly/recovery/device-loss-fanout.ts +89 -0
  232. package/src/assembly/recovery/generation.ts +141 -0
  233. package/src/assembly/recovery/recovery-attempt.ts +800 -0
  234. package/src/assembly/recovery/renderer-recover.ts +906 -0
  235. package/src/assembly/renderer-facade.ts +342 -0
  236. package/src/assembly/renderer-frame-transaction.ts +45 -0
  237. package/src/assembly/renderer-helpers.ts +99 -0
  238. package/src/assembly/renderer-lifecycle.ts +159 -0
  239. package/src/assembly/shader-prewarm-policy.ts +157 -0
  240. package/src/assembly/webgpu-ready-contract.ts +14 -0
  241. package/src/assembly/webgpu-ready.ts +4066 -0
  242. package/src/assembly/webgpu-renderer.ts +4027 -0
  243. package/src/assembly/webgpu-vertex-layouts.ts +132 -0
  244. package/src/assets/asset-decoders.ts +27 -4
  245. package/src/components/camera.ts +1 -1
  246. package/src/components/instances.ts +14 -197
  247. package/src/debug-draw-glue.ts +1 -1
  248. package/src/device/device-scope.ts +35 -6
  249. package/src/device/gpu-residency.ts +194 -26
  250. package/src/environment/frame.ts +6 -0
  251. package/src/environment/inspection.ts +10 -0
  252. package/src/environment/lifecycle.ts +114 -8
  253. package/src/errors/recover.ts +76 -5
  254. package/src/errors/render.ts +17 -0
  255. package/src/extract/contracts.ts +974 -0
  256. package/src/features/host.ts +16 -0
  257. package/src/features/prepared-gpu-work.ts +20 -2
  258. package/src/features/prepared-graphics-store.ts +17 -0
  259. package/src/gpu-driven/production-raster.ts +131 -3
  260. package/src/gpu-scene.ts +87 -31
  261. package/src/ibl/IblPipelineCache.ts +134 -50
  262. package/src/ibl/skylight-bind-group.ts +26 -0
  263. package/src/index.ts +10 -1
  264. package/src/instance-buffer-cache.ts +14 -1
  265. package/src/instances.ts +288 -0
  266. package/src/light-buffer-layout.ts +3 -0
  267. package/src/materials.ts +365 -53
  268. package/src/mesh-material-bindings.ts +59 -0
  269. package/src/occlusion-proxy.ts +64 -0
  270. package/src/pbr-pipeline.ts +139 -15
  271. package/src/pipeline-builder.ts +14 -10
  272. package/src/pipeline-spec.ts +6 -14
  273. package/src/points-lines/__tests__/cache-recovery.unit.test.ts +22 -0
  274. package/src/points-lines/prepare.ts +9 -0
  275. package/src/points-lines/standard-owner.ts +403 -0
  276. package/src/record/__tests__/frame-targets-recovery.unit.test.ts +60 -0
  277. package/src/record/__tests__/main-pass-geometry.unit.test.ts +26 -1
  278. package/src/record/__tests__/mesh-ssbo.unit.test.ts +38 -0
  279. package/src/record/__tests__/recovery-pipeline.unit.test.ts +124 -0
  280. package/src/record/__tests__/sprite-material-abi.unit.test.ts +28 -13
  281. package/src/record/__tests__/standard-pbr-ubo-layout.unit.test.ts +47 -4
  282. package/src/record/__tests__/temporal-payload.unit.test.ts +22 -43
  283. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -1
  284. package/src/record/frame-snapshot.ts +34 -2
  285. package/src/record/frame-targets.ts +11 -1
  286. package/src/record/frame.ts +37 -4
  287. package/src/record/helpers.ts +1 -1
  288. package/src/record/main-pass-geometry.ts +727 -63
  289. package/src/record/main-pass-material.ts +316 -209
  290. package/src/record/main-pass-sprite-draws.ts +7 -8
  291. package/src/record/main-pass.ts +94 -6
  292. package/src/record/mesh-ssbo.ts +32 -10
  293. package/src/record/recovery-pipeline.ts +695 -0
  294. package/src/record/render-context.ts +39 -4
  295. package/src/record/shadow-pass.ts +46 -99
  296. package/src/record/typed-frame-graph.ts +45 -1
  297. package/src/record/view-ubo.ts +3 -3
  298. package/src/recovery/render-system-candidate.ts +876 -0
  299. package/src/render-contract.ts +69 -7
  300. package/src/render-system-extract-tail.ts +3039 -0
  301. package/src/render-system-extract.ts +177 -3003
  302. package/src/render-system.ts +906 -662
  303. package/src/scene/render-scene-types.ts +2 -0
  304. package/src/scene/render-scene.ts +703 -37
  305. package/src/systems/skin-palette-allocator.ts +129 -7
  306. package/src/temporal/frame-coordinator.ts +4 -1
  307. package/src/temporal/taa-history-store.ts +46 -2
  308. package/src/temporal/temporal-view.ts +1 -1
  309. package/src/temporal/transaction.ts +9 -0
  310. package/src/tilemap-chunk-extract-system.ts +1 -1
  311. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +19 -12
  312. package/src/transmission/__tests__/surface-compatibility.integration.test.ts +32 -0
  313. package/dist/chunk-4L3P6DT5.mjs.map +0 -1
  314. package/dist/chunk-4NPUASZR.mjs +0 -203
  315. package/dist/chunk-4NPUASZR.mjs.map +0 -1
  316. package/dist/chunk-DIYW3M5D.mjs.map +0 -1
  317. package/dist/chunk-FBC4R44J.mjs.map +0 -1
  318. package/dist/chunk-FF7K3FSO.mjs.map +0 -1
  319. package/dist/chunk-HZUPSYW7.mjs.map +0 -1
  320. package/dist/chunk-QODA5UKU.mjs.map +0 -1
  321. package/dist/chunk-YALPS44A.mjs.map +0 -1
  322. package/dist/chunk-ZUWKAZ2I.mjs.map +0 -1
@@ -1,15 +1,15 @@
1
1
  import { TRANSPARENT_SORT_MODE_LAYER_Z, TRANSPARENT_SORT_MODE_LAYER_Y } from './chunk-RXZ3RGOU.mjs';
2
- import { DEFAULT_CLUSTER_GRID, DEFAULT_STANDARD_PROFILE, createIblKernelCache, MAX_LIGHTS, CLUSTER_GRID_STRIDE_U32, LIGHT_INDEX_LIST_CAPACITY, createStandardClusterLayout } from './chunk-YALPS44A.mjs';
3
- import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from './chunk-4NPUASZR.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, SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET, SKIN_MATERIAL_SHADER_ID, PROBE_BLEND_RECORD_BYTE_SIZE, resolveRenderTargetMaterialSource, probeBlendRecordOffset, PROBE_BLEND_RECORD_STRIDE } from './chunk-HZUPSYW7.mjs';
5
- import { GPU_TEXTURE_USAGE_RENDER_ATTACHMENT, GPU_TEXTURE_USAGE_COPY_SRC, 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, VertexColorVariantConflictError } from './chunk-QODA5UKU.mjs';
6
- import { ok, err as err$1, toShared, ASSET_ERROR_HINTS, deriveTextureLayout, KNOWN_PASS_KINDS, derive, 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-E34VL5VI.mjs';
3
+ import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from './chunk-JSEDTZYS.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, worldEntityKey, instanceCollectionCacheKey, probeBlendRecordOffset, PROBE_BLEND_RECORD_STRIDE, pbrSkinMeshDynamicOffsets, buildPbrMaterialUserRegionEntries, buildBindGroupLayoutDescriptor, isStandardPbrMaterialShader, PROBE_BLEND_RECORD_BYTE_SIZE, 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, resolveRenderTargetMaterialSource, materialBindGroupLayoutIdentity } from './chunk-HKXTW355.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-ZZ4YQ474.mjs';
6
+ import { derive, ok, err as err$1, toShared, ASSET_ERROR_HINTS, deriveTextureLayout, handleSlot, KNOWN_PASS_KINDS, resolveMaterialTextureCoordinates } from '@forgeax/engine-types';
7
7
  import { ok as ok$1, err, RhiError } from '@forgeax/engine-rhi';
8
8
  import { mat3, vec3, mat4 } from '@forgeax/engine-math';
9
- import { deriveVertexLayoutProjection, deriveVertexBufferLayout } from '@forgeax/engine-geometry';
9
+ import { deriveVertexLayoutProjection, deriveVertexBufferLayout, deriveVertexBufferLayoutFromProjection } 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, DEFAULT_MSDF_TEXT_PARAM_SCHEMA, DEFAULT_SPRITE_PARAM_SCHEMA, DEFAULT_UNLIT_PARAM_SCHEMA } from '@forgeax/engine-shader';
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';
13
13
  import { isCompressedFormat, bytesPerRow, blockParamsForFormat } from '@forgeax/engine-codec';
14
14
 
15
15
  function resolveOutputDither(config) {
@@ -283,18 +283,9 @@ function buildPipelineDescriptor(spec, modules) {
283
283
  const { attachments, geometry, renderState } = spec;
284
284
  const vertexBuffers = geometry.vertexLayoutProjection === void 0 ? deriveVertexBufferLayout(geometry.vertexLayout, {
285
285
  ...geometry.shaderUvSetCount !== void 0 ? { shaderUvSetCount: geometry.shaderUvSetCount } : {}
286
- }) : geometry.vertexLayoutProjection.attributes.length === 0 ? [] : [
287
- {
288
- arrayStride: geometry.vertexLayoutProjection.arrayStride,
289
- attributes: geometry.vertexLayoutProjection.attributes.map(
290
- ({ shaderLocation, offset, format }) => ({
291
- shaderLocation,
292
- offset,
293
- format
294
- })
295
- )
296
- }
297
- ];
286
+ }) : deriveVertexBufferLayoutFromProjection(geometry.vertexLayoutProjection, {
287
+ ...geometry.shaderUvSetCount !== void 0 ? { shaderUvSetCount: geometry.shaderUvSetCount } : {}
288
+ });
298
289
  const descriptor = {
299
290
  vertex: {
300
291
  module: modules.vertex,
@@ -839,10 +830,10 @@ function extractEntryResourceHandle(entry) {
839
830
  }
840
831
  return v;
841
832
  }
833
+ var BIND_GROUP_CHAIN_LEAF_KEY = {};
842
834
  function getOrCreateFromChain(root, handles, variant, factory, counts) {
843
835
  let node = root;
844
- for (let i = 0; i < handles.length - 1; i++) {
845
- const h = handles[i];
836
+ for (const h of handles) {
846
837
  let next = node.get(h);
847
838
  if (next === void 0) {
848
839
  next = /* @__PURE__ */ new WeakMap();
@@ -850,11 +841,10 @@ function getOrCreateFromChain(root, handles, variant, factory, counts) {
850
841
  }
851
842
  node = next;
852
843
  }
853
- const last = handles[handles.length - 1];
854
- let leaf = node.get(last);
844
+ let leaf = node.get(BIND_GROUP_CHAIN_LEAF_KEY);
855
845
  if (leaf === void 0) {
856
846
  leaf = /* @__PURE__ */ new Map();
857
- node.set(last, leaf);
847
+ node.set(BIND_GROUP_CHAIN_LEAF_KEY, leaf);
858
848
  }
859
849
  const hit = leaf.get(variant);
860
850
  if (hit !== void 0) return hit;
@@ -2260,8 +2250,8 @@ function computeProjectionMatrix(camera) {
2260
2250
  proj,
2261
2251
  camera.orthoLeft,
2262
2252
  camera.orthoRight,
2263
- camera.orthoBottom,
2264
2253
  camera.orthoTop,
2254
+ camera.orthoBottom,
2265
2255
  camera.near,
2266
2256
  camera.far
2267
2257
  );
@@ -2355,9 +2345,9 @@ function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShado
2355
2345
  const VIEW_PAYLOAD_FLOATS = 240;
2356
2346
  const viewPayload = new Float32Array(VIEW_PAYLOAD_FLOATS);
2357
2347
  for (let i = 0; i < 16; i++) viewPayload[i] = mainProjection[i] ?? 0;
2358
- viewPayload[16] = (light.direction[0] ?? 0) * light.intensity;
2359
- viewPayload[17] = (light.direction[1] ?? -1) * light.intensity;
2360
- viewPayload[18] = (light.direction[2] ?? 0) * light.intensity;
2348
+ viewPayload[16] = light.direction[0] ?? 0;
2349
+ viewPayload[17] = light.direction[1] ?? -1;
2350
+ viewPayload[18] = light.direction[2] ?? 0;
2361
2351
  viewPayload[20] = light.color[0] ?? 0;
2362
2352
  viewPayload[21] = light.color[1] ?? 0;
2363
2353
  viewPayload[22] = light.color[2] ?? 0;
@@ -3607,10 +3597,15 @@ function iblPipelineError(stage, code, cause) {
3607
3597
  detail: { stage, ...{} }
3608
3598
  };
3609
3599
  }
3610
- function runIblPrecompute(opts) {
3600
+ async function runIblPrecompute(opts) {
3611
3601
  const { device, scope } = opts;
3612
3602
  const cache3 = getOrCreateIblCache(scope);
3613
3603
  const outputFormat = cache3.outputFormat ?? "rgba16float";
3604
+ const generation = scope.generation;
3605
+ const candidate = {};
3606
+ const fail = (stage, cause) => {
3607
+ return Promise.resolve(err(badAlloc(stage, cause)));
3608
+ };
3614
3609
  if (cache3.equirectToCubePipeline === void 0 || cache3.irradiancePipeline === void 0 || cache3.prefilterPipeline === void 0 || cache3.brdfLutPipeline === void 0) {
3615
3610
  return err({
3616
3611
  code: "ibl-precompute-not-dispatched",
@@ -3619,7 +3614,7 @@ function runIblPrecompute(opts) {
3619
3614
  detail: { stage: "pipeline-cache" }
3620
3615
  });
3621
3616
  }
3622
- if (cache3.irradianceTexture === void 0) {
3617
+ {
3623
3618
  const textureResult = device.createTexture({
3624
3619
  label: "ibl-irradiance-cube",
3625
3620
  size: { width: IRRADIANCE_SIZE, height: IRRADIANCE_SIZE, depthOrArrayLayers: 6 },
@@ -3631,15 +3626,15 @@ function runIblPrecompute(opts) {
3631
3626
  viewFormats: [],
3632
3627
  textureBindingViewDimension: void 0
3633
3628
  });
3634
- if (!textureResult.ok) return err(badAlloc("irradiance-texture", textureResult.error));
3635
- cache3.irradianceTexture = textureResult.value;
3629
+ if (!textureResult.ok) return fail("irradiance-texture", textureResult.error);
3630
+ candidate.irradianceTexture = adoptIblTexture(scope, device, textureResult.value);
3636
3631
  const cubeViewResult = device.createTextureView(textureResult.value, {
3637
3632
  label: "ibl-irradiance-cube-view",
3638
3633
  dimension: "cube",
3639
3634
  arrayLayerCount: 6
3640
3635
  });
3641
- if (!cubeViewResult.ok) return err(badAlloc("irradiance-view", cubeViewResult.error));
3642
- cache3.irradianceView = cubeViewResult.value;
3636
+ if (!cubeViewResult.ok) return fail("irradiance-view", cubeViewResult.error);
3637
+ candidate.irradianceView = cubeViewResult.value;
3643
3638
  const faceViews = [];
3644
3639
  for (let f = 0; f < 6; f++) {
3645
3640
  const viewResult = device.createTextureView(textureResult.value, {
@@ -3648,12 +3643,12 @@ function runIblPrecompute(opts) {
3648
3643
  baseArrayLayer: f,
3649
3644
  arrayLayerCount: 1
3650
3645
  });
3651
- if (!viewResult.ok) return err(badAlloc("irradiance-face-view", viewResult.error));
3646
+ if (!viewResult.ok) return fail("irradiance-face-view", viewResult.error);
3652
3647
  faceViews.push(viewResult.value);
3653
3648
  }
3654
- cache3.irradianceFaceViews = faceViews;
3649
+ candidate.irradianceFaceViews = faceViews;
3655
3650
  }
3656
- if (cache3.prefilterTexture === void 0) {
3651
+ {
3657
3652
  const textureResult = device.createTexture({
3658
3653
  label: "ibl-prefilter-cube",
3659
3654
  size: { width: PREFILTER_SIZE, height: PREFILTER_SIZE, depthOrArrayLayers: 6 },
@@ -3665,8 +3660,8 @@ function runIblPrecompute(opts) {
3665
3660
  viewFormats: [],
3666
3661
  textureBindingViewDimension: void 0
3667
3662
  });
3668
- if (!textureResult.ok) return err(badAlloc("prefilter-texture", textureResult.error));
3669
- cache3.prefilterTexture = textureResult.value;
3663
+ if (!textureResult.ok) return fail("prefilter-texture", textureResult.error);
3664
+ candidate.prefilterTexture = adoptIblTexture(scope, device, textureResult.value);
3670
3665
  const cubeViewResult = device.createTextureView(textureResult.value, {
3671
3666
  label: "ibl-prefilter-cube-view",
3672
3667
  dimension: "cube",
@@ -3674,8 +3669,8 @@ function runIblPrecompute(opts) {
3674
3669
  baseMipLevel: 0,
3675
3670
  mipLevelCount: PREFILTER_MIP_LEVELS
3676
3671
  });
3677
- if (!cubeViewResult.ok) return err(badAlloc("prefilter-view", cubeViewResult.error));
3678
- cache3.prefilterView = cubeViewResult.value;
3672
+ if (!cubeViewResult.ok) return fail("prefilter-view", cubeViewResult.error);
3673
+ candidate.prefilterView = cubeViewResult.value;
3679
3674
  const mipViews = [];
3680
3675
  for (let m = 0; m < PREFILTER_MIP_LEVELS; m++) {
3681
3676
  const faces = [];
@@ -3688,14 +3683,14 @@ function runIblPrecompute(opts) {
3688
3683
  baseArrayLayer: f,
3689
3684
  arrayLayerCount: 1
3690
3685
  });
3691
- if (!viewResult.ok) return err(badAlloc("prefilter-face-view", viewResult.error));
3686
+ if (!viewResult.ok) return fail("prefilter-face-view", viewResult.error);
3692
3687
  faces.push(viewResult.value);
3693
3688
  }
3694
3689
  mipViews.push(faces);
3695
3690
  }
3696
- cache3.prefilterFaceViewsByMip = mipViews;
3691
+ candidate.prefilterFaceViewsByMip = mipViews;
3697
3692
  }
3698
- if (cache3.brdfLutTexture === void 0) {
3693
+ {
3699
3694
  const textureResult = device.createTexture({
3700
3695
  label: "ibl-brdf-lut",
3701
3696
  size: { width: BRDF_LUT_SIZE, height: BRDF_LUT_SIZE, depthOrArrayLayers: 1 },
@@ -3707,14 +3702,14 @@ function runIblPrecompute(opts) {
3707
3702
  viewFormats: [],
3708
3703
  textureBindingViewDimension: void 0
3709
3704
  });
3710
- if (!textureResult.ok) return err(badAlloc("brdf-lut-texture", textureResult.error));
3711
- cache3.brdfLutTexture = textureResult.value;
3705
+ if (!textureResult.ok) return fail("brdf-lut-texture", textureResult.error);
3706
+ candidate.brdfLutTexture = adoptIblTexture(scope, device, textureResult.value);
3712
3707
  const viewResult = device.createTextureView(textureResult.value, {
3713
3708
  label: "ibl-brdf-lut-view",
3714
3709
  dimension: "2d"
3715
3710
  });
3716
- if (!viewResult.ok) return err(badAlloc("brdf-lut-view", viewResult.error));
3717
- cache3.brdfLutView = viewResult.value;
3711
+ if (!viewResult.ok) return fail("brdf-lut-view", viewResult.error);
3712
+ candidate.brdfLutView = viewResult.value;
3718
3713
  }
3719
3714
  const samplerResult = device.createSampler({
3720
3715
  label: "ibl-precompute-sampler",
@@ -3725,11 +3720,33 @@ function runIblPrecompute(opts) {
3725
3720
  addressModeV: "clamp-to-edge",
3726
3721
  addressModeW: "clamp-to-edge"
3727
3722
  });
3728
- if (!samplerResult.ok) return err(badAlloc("sampler", samplerResult.error));
3723
+ if (!samplerResult.ok) return fail("sampler", samplerResult.error);
3729
3724
  const sampler = samplerResult.value;
3730
- const encoderResult = device.createCommandEncoder({ label: "ibl-precompute-encoder" });
3731
- if (!encoderResult.ok) return err(badAlloc("encoder", encoderResult.error));
3732
- const encoder = encoderResult.value;
3725
+ let encoderResult = device.createCommandEncoder({
3726
+ label: "ibl-precompute-encoder-equirect-to-cube"
3727
+ });
3728
+ if (!encoderResult.ok) return fail("encoder-equirect-to-cube", encoderResult.error);
3729
+ let encoder = encoderResult.value;
3730
+ const submitStage = async (stage, createNextEncoder) => {
3731
+ const finishRes = encoder.finish();
3732
+ if (!finishRes.ok) return err(badAlloc(`${stage}-finish`, finishRes.error));
3733
+ const submitRes = device.queue.submit([finishRes.value]);
3734
+ if (!submitRes.ok) return err(badAlloc(`${stage}-submit`, submitRes.error));
3735
+ try {
3736
+ await device.queue.onSubmittedWorkDone();
3737
+ } catch {
3738
+ return err(badAlloc(`${stage}-fence`));
3739
+ }
3740
+ if (!scope.isAlive() || scope.generation !== generation) {
3741
+ return err(badAlloc(`${stage}-device-scope-generation`));
3742
+ }
3743
+ if (createNextEncoder) {
3744
+ encoderResult = device.createCommandEncoder({ label: `ibl-precompute-encoder-${stage}` });
3745
+ if (!encoderResult.ok) return err(badAlloc(`${stage}-next-encoder`, encoderResult.error));
3746
+ encoder = encoderResult.value;
3747
+ }
3748
+ return ok$1(true);
3749
+ };
3733
3750
  const faceUniformsBgl = cache3.faceUniformsBgl;
3734
3751
  const equirectGroup1Bgl = cache3.equirectGroup1Bgl;
3735
3752
  const cubeGroup1Bgl = cache3.cubeGroup1Bgl;
@@ -3738,11 +3755,16 @@ function runIblPrecompute(opts) {
3738
3755
  const irradiancePipeline = cache3.irradiancePipeline;
3739
3756
  const prefilterPipeline = cache3.prefilterPipeline;
3740
3757
  const brdfLutPipeline = cache3.brdfLutPipeline;
3741
- const irrFaceViews = cache3.irradianceFaceViews;
3742
- const prefMipViews = cache3.prefilterFaceViewsByMip;
3743
- const brdfLutView = cache3.brdfLutView;
3744
- 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) {
3745
- return err(badAlloc("ibl-cache-resources"));
3758
+ const candidateIrradianceTexture = candidate.irradianceTexture;
3759
+ const candidateIrradianceView = candidate.irradianceView;
3760
+ const irrFaceViews = candidate.irradianceFaceViews;
3761
+ const candidatePrefilterTexture = candidate.prefilterTexture;
3762
+ const candidatePrefilterView = candidate.prefilterView;
3763
+ const prefMipViews = candidate.prefilterFaceViewsByMip;
3764
+ const candidateBrdfLutTexture = candidate.brdfLutTexture;
3765
+ const brdfLutView = candidate.brdfLutView;
3766
+ 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) {
3767
+ return fail("ibl-cache-resources");
3746
3768
  }
3747
3769
  const equirectBgResult = device.createBindGroup({
3748
3770
  label: "ibl-equirect-bg",
@@ -3752,7 +3774,7 @@ function runIblPrecompute(opts) {
3752
3774
  { binding: 1, resource: { kind: "sampler", value: sampler } }
3753
3775
  ]
3754
3776
  });
3755
- if (!equirectBgResult.ok) return err(badAlloc("equirect-bg", equirectBgResult.error));
3777
+ if (!equirectBgResult.ok) return fail("equirect-bg", equirectBgResult.error);
3756
3778
  const equirectBg = equirectBgResult.value;
3757
3779
  const cubeBgResult = device.createBindGroup({
3758
3780
  label: "ibl-cube-bg",
@@ -3762,12 +3784,12 @@ function runIblPrecompute(opts) {
3762
3784
  { binding: 1, resource: { kind: "sampler", value: sampler } }
3763
3785
  ]
3764
3786
  });
3765
- if (!cubeBgResult.ok) return err(badAlloc("cube-bg", cubeBgResult.error));
3787
+ if (!cubeBgResult.ok) return fail("cube-bg", cubeBgResult.error);
3766
3788
  const cubeBg = cubeBgResult.value;
3767
3789
  const cubeFaceViews = opts.cubeFaceViews;
3768
3790
  for (let face = 0; face < 6; face++) {
3769
3791
  const cubeFaceView = cubeFaceViews[face];
3770
- if (cubeFaceView === void 0) return err(badAlloc("cube-face-view"));
3792
+ if (cubeFaceView === void 0) return fail("cube-face-view");
3771
3793
  const pass = encoder.beginRenderPass({
3772
3794
  label: "ibl-equirect-to-cube",
3773
3795
  colorAttachments: [
@@ -3796,7 +3818,7 @@ function runIblPrecompute(opts) {
3796
3818
  }
3797
3819
  ]
3798
3820
  });
3799
- if (!faceBgResult.ok) return err(badAlloc("face-bg", faceBgResult.error));
3821
+ if (!faceBgResult.ok) return fail("face-bg", faceBgResult.error);
3800
3822
  pass.setPipeline(equirectPipeline);
3801
3823
  pass.setBindGroup(0, faceBgResult.value);
3802
3824
  pass.setBindGroup(1, equirectBg);
@@ -3804,9 +3826,11 @@ function runIblPrecompute(opts) {
3804
3826
  pass.draw(6, 1, face * 6, 0);
3805
3827
  pass.end();
3806
3828
  }
3829
+ const cubeStage = await submitStage("equirect-to-cube", true);
3830
+ if (!cubeStage.ok) return err(cubeStage.error);
3807
3831
  for (let face = 0; face < 6; face++) {
3808
3832
  const irrFaceView = irrFaceViews[face];
3809
- if (irrFaceView === void 0) return err(badAlloc("irradiance-face-view"));
3833
+ if (irrFaceView === void 0) return fail("irradiance-face-view");
3810
3834
  const pass = encoder.beginRenderPass({
3811
3835
  label: "ibl-irradiance",
3812
3836
  colorAttachments: [
@@ -3835,21 +3859,23 @@ function runIblPrecompute(opts) {
3835
3859
  }
3836
3860
  ]
3837
3861
  });
3838
- if (!faceBgResult.ok) return err(badAlloc("irr-face-bg", faceBgResult.error));
3862
+ if (!faceBgResult.ok) return fail("irr-face-bg", faceBgResult.error);
3839
3863
  pass.setPipeline(irradiancePipeline);
3840
3864
  pass.setBindGroup(0, faceBgResult.value);
3841
3865
  pass.setBindGroup(1, cubeBg);
3842
3866
  pass.setVertexBuffer(0, opts.cubeVertexBuffer);
3843
3867
  pass.draw(6, 1, face * 6, 0);
3844
3868
  pass.end();
3869
+ const irradianceFaceStage = await submitStage(`irradiance-face-${face}`, true);
3870
+ if (!irradianceFaceStage.ok) return err(irradianceFaceStage.error);
3845
3871
  }
3846
3872
  for (let mip = 0; mip < PREFILTER_MIP_LEVELS; mip++) {
3847
3873
  const mipFaceViews = prefMipViews[mip];
3848
- if (mipFaceViews === void 0) return err(badAlloc("prefilter-mip-views"));
3874
+ if (mipFaceViews === void 0) return fail("prefilter-mip-views");
3849
3875
  for (let face = 0; face < 6; face++) {
3850
3876
  const subIdx = mip * 6 + face;
3851
3877
  const mipFaceView = mipFaceViews[face];
3852
- if (mipFaceView === void 0) return err(badAlloc("prefilter-face-view"));
3878
+ if (mipFaceView === void 0) return fail("prefilter-face-view");
3853
3879
  const pass = encoder.beginRenderPass({
3854
3880
  label: "ibl-prefilter",
3855
3881
  colorAttachments: [
@@ -3889,7 +3915,7 @@ function runIblPrecompute(opts) {
3889
3915
  }
3890
3916
  ]
3891
3917
  });
3892
- if (!bgResult.ok) return err(badAlloc("pref-bg", bgResult.error));
3918
+ if (!bgResult.ok) return fail("pref-bg", bgResult.error);
3893
3919
  pass.setPipeline(prefilterPipeline);
3894
3920
  pass.setBindGroup(0, bgResult.value);
3895
3921
  pass.setBindGroup(1, cubeBg);
@@ -3898,6 +3924,8 @@ function runIblPrecompute(opts) {
3898
3924
  pass.end();
3899
3925
  }
3900
3926
  }
3927
+ const prefilterStage = await submitStage("prefilter", true);
3928
+ if (!prefilterStage.ok) return err(prefilterStage.error);
3901
3929
  {
3902
3930
  const pass = encoder.beginRenderPass({
3903
3931
  label: "ibl-brdf-lut",
@@ -3914,16 +3942,37 @@ function runIblPrecompute(opts) {
3914
3942
  pass.draw(3, 1, 0, 0);
3915
3943
  pass.end();
3916
3944
  }
3917
- const finishRes = encoder.finish();
3918
- if (!finishRes.ok) return err(badAlloc("finish", finishRes.error));
3919
- const cmdBuffer = finishRes.value;
3920
- const submitRes = device.queue.submit([cmdBuffer]);
3921
- if (!submitRes.ok) return err(badAlloc("queue.submit", submitRes.error));
3945
+ const brdfStage = await submitStage("brdf-lut", false);
3946
+ if (!brdfStage.ok) return err(brdfStage.error);
3947
+ const promoted = {
3948
+ irradianceTexture: candidateIrradianceTexture,
3949
+ irradianceView: candidateIrradianceView,
3950
+ irradianceFaceViews: irrFaceViews,
3951
+ prefilterTexture: candidatePrefilterTexture,
3952
+ prefilterView: candidatePrefilterView,
3953
+ prefilterFaceViewsByMip: prefMipViews,
3954
+ brdfLutTexture: candidateBrdfLutTexture,
3955
+ brdfLutView
3956
+ };
3957
+ cache3.irradianceTexture = promoted.irradianceTexture;
3958
+ cache3.irradianceView = promoted.irradianceView;
3959
+ cache3.irradianceFaceViews = promoted.irradianceFaceViews;
3960
+ cache3.prefilterTexture = promoted.prefilterTexture;
3961
+ cache3.prefilterView = promoted.prefilterView;
3962
+ cache3.prefilterFaceViewsByMip = promoted.prefilterFaceViewsByMip;
3963
+ cache3.brdfLutTexture = promoted.brdfLutTexture;
3964
+ cache3.brdfLutView = promoted.brdfLutView;
3922
3965
  cache3.irradianceBakeCount += 1;
3923
3966
  cache3.prefilterBakeCount += 1;
3924
3967
  cache3.brdfLutBakeCount += 1;
3925
3968
  return ok$1({ submitted: true });
3926
3969
  }
3970
+ function adoptIblTexture(scope, device, texture) {
3971
+ scope._adopt("texture", texture, (value) => {
3972
+ device.destroyTexture(value);
3973
+ });
3974
+ return texture;
3975
+ }
3927
3976
  function badAlloc(stage, cause) {
3928
3977
  return {
3929
3978
  code: "ibl-precompute-not-dispatched",
@@ -4033,9 +4082,153 @@ var GpuTexture = class {
4033
4082
  }
4034
4083
  };
4035
4084
 
4085
+ // src/instances.ts
4086
+ var MATRIX_STRIDE = 16;
4087
+ function appendRange(ranges, start, end) {
4088
+ if (start >= end) return;
4089
+ let index = 0;
4090
+ while (index < ranges.length) {
4091
+ const existing = ranges[index];
4092
+ if (existing === void 0 || existing.end >= start) break;
4093
+ index += 1;
4094
+ }
4095
+ while (index < ranges.length) {
4096
+ const existing = ranges[index];
4097
+ if (existing === void 0 || existing.start > end) break;
4098
+ start = Math.min(start, existing.start);
4099
+ end = Math.max(end, existing.end);
4100
+ ranges.splice(index, 1);
4101
+ }
4102
+ ranges.splice(index, 0, { start, end });
4103
+ }
4104
+ var InstanceProjectionStore = class {
4105
+ nextId = 1;
4106
+ identities = /* @__PURE__ */ new WeakMap();
4107
+ records = /* @__PURE__ */ new Map();
4108
+ project(world, entity, transforms) {
4109
+ let entities = this.identities.get(world);
4110
+ if (entities === void 0) {
4111
+ entities = /* @__PURE__ */ new Map();
4112
+ this.identities.set(world, entities);
4113
+ }
4114
+ let collectionId = entities.get(entity);
4115
+ if (collectionId === void 0) {
4116
+ collectionId = this.nextId++;
4117
+ entities.set(entity, collectionId);
4118
+ }
4119
+ const previous = this.records.get(collectionId);
4120
+ const unchanged = previous !== void 0 && previous.transforms.length === transforms.length && previous.transforms.every((value, index) => Object.is(value, transforms[index]));
4121
+ const record = unchanged ? previous : {
4122
+ collectionId,
4123
+ world,
4124
+ entity,
4125
+ transforms: new Float32Array(transforms),
4126
+ count: transforms.length / MATRIX_STRIDE,
4127
+ revision: (previous?.revision ?? 0) + 1
4128
+ };
4129
+ this.records.set(collectionId, record);
4130
+ return { ...this.info(record), transforms: record.transforms };
4131
+ }
4132
+ retain(ids) {
4133
+ for (const [id, record] of this.records) {
4134
+ if (ids.has(id)) continue;
4135
+ this.identities.get(record.world)?.delete(record.entity);
4136
+ this.records.delete(id);
4137
+ }
4138
+ }
4139
+ /** Release one producer-owned collection when its render identity is removed. */
4140
+ release(world, entity) {
4141
+ const entities = this.identities.get(world);
4142
+ const collectionId = entities?.get(entity);
4143
+ if (collectionId === void 0) return;
4144
+ entities?.delete(entity);
4145
+ this.records.delete(collectionId);
4146
+ }
4147
+ dispose() {
4148
+ this.records.clear();
4149
+ this.identities = /* @__PURE__ */ new WeakMap();
4150
+ }
4151
+ /**
4152
+ * Return detached residency facts for every live collection.
4153
+ *
4154
+ * @internal Joins CPU projection identity with the active device residency;
4155
+ * callers only receive immutable PODs and never GPU handles.
4156
+ */
4157
+ _inspections(residency, frameNumber) {
4158
+ return Object.freeze(
4159
+ [...this.records.values()].map(
4160
+ (record) => this.projectInspection(record, residency?.get(record.collectionId), frameNumber)
4161
+ )
4162
+ );
4163
+ }
4164
+ info(record) {
4165
+ return Object.freeze({
4166
+ collectionId: record.collectionId,
4167
+ count: record.count,
4168
+ revision: record.revision
4169
+ });
4170
+ }
4171
+ projectInspection(record, residency, frameNumber) {
4172
+ return Object.freeze({
4173
+ ...this.info(record),
4174
+ residentGeneration: residency?.residentGeneration,
4175
+ lane: residency?.lane ?? "unresident",
4176
+ uploadRanges: Object.freeze(
4177
+ residency?.frameNumber === frameNumber ? residency.uploadRanges.map((range) => ({ ...range })) : []
4178
+ ),
4179
+ uploadedBytes: residency?.frameNumber === frameNumber ? residency.uploadedBytes : 0,
4180
+ requestedBytes: residency?.requestedBytes ?? record.count * MATRIX_STRIDE * 4,
4181
+ supportedBytes: residency?.supportedBytes,
4182
+ backend: residency?.backend ?? "unknown",
4183
+ owner: "renderer.instances",
4184
+ error: residency?.error
4185
+ });
4186
+ }
4187
+ };
4188
+ function recordInstanceResidency(residency, input) {
4189
+ const previous = residency.get(input.collectionId);
4190
+ const sameFrame = previous !== void 0 && previous.frameNumber === input.frameNumber && previous.error === void 0;
4191
+ const uploadRanges = sameFrame ? [...previous.uploadRanges].reduce(
4192
+ (ranges, range) => {
4193
+ appendRange(ranges, range.start, range.end);
4194
+ return ranges;
4195
+ },
4196
+ input.uploadRanges.map((range) => ({ ...range }))
4197
+ ) : input.uploadRanges.map((range) => ({ ...range }));
4198
+ residency.set(input.collectionId, {
4199
+ frameNumber: input.frameNumber,
4200
+ residentGeneration: input.residentGeneration,
4201
+ lane: input.lane,
4202
+ uploadRanges,
4203
+ uploadedBytes: sameFrame ? previous.uploadedBytes + input.uploadedBytes : input.uploadedBytes,
4204
+ requestedBytes: input.requestedBytes,
4205
+ supportedBytes: input.supportedBytes,
4206
+ backend: input.backend,
4207
+ error: void 0
4208
+ });
4209
+ }
4210
+ function recordInstanceFailure(residency, input) {
4211
+ residency.set(input.collectionId, {
4212
+ frameNumber: -1,
4213
+ residentGeneration: void 0,
4214
+ lane: "unavailable",
4215
+ uploadRanges: [],
4216
+ uploadedBytes: 0,
4217
+ requestedBytes: input.facts.requestedBytes,
4218
+ supportedBytes: input.facts.supportedBytes,
4219
+ backend: input.facts.backend,
4220
+ error: {
4221
+ code: input.code,
4222
+ expected: input.expected,
4223
+ hint: input.hint,
4224
+ detail: { ...input.facts }
4225
+ }
4226
+ });
4227
+ }
4228
+
4036
4229
  // src/ibl/skylight-bind-group.ts
4037
4230
  var FALLBACK_BYTES_PER_ROW = 256;
4038
- function assembleMaterialWithSkylightEntries(materialEntries, skylight, transmission) {
4231
+ function assembleMaterialWithSkylightEntries(materialEntries, skylight, transmission, textureInjections = []) {
4039
4232
  const iblStart = materialEntries.length;
4040
4233
  const result = [
4041
4234
  ...materialEntries,
@@ -4081,6 +4274,22 @@ function assembleMaterialWithSkylightEntries(materialEntries, skylight, transmis
4081
4274
  resource: { kind: "textureView", value: transmissionView }
4082
4275
  }
4083
4276
  );
4277
+ const clearcoatStart = result.length;
4278
+ for (let index = 0; index < textureInjections.length; index += 1) {
4279
+ const resource = textureInjections[index];
4280
+ if (resource === void 0) continue;
4281
+ const binding = clearcoatStart + resource.slot * 2;
4282
+ result.push(
4283
+ {
4284
+ binding,
4285
+ resource: { kind: "sampler", value: resource.sampler }
4286
+ },
4287
+ {
4288
+ binding: binding + 1,
4289
+ resource: { kind: "textureView", value: resource.view }
4290
+ }
4291
+ );
4292
+ }
4084
4293
  return result;
4085
4294
  }
4086
4295
  function createSkylightFallback(device, queue) {
@@ -4344,8 +4553,8 @@ function deriveRenderDataCubemap(source) {
4344
4553
  cubeUsage: GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST | GPU_TEXTURE_USAGE_COPY_SRC
4345
4554
  });
4346
4555
  }
4347
- var STANDARD_PBR_UBO_SIZE = 384;
4348
- var MATERIAL_PER_ENTITY_STRIDE = 512;
4556
+ var STANDARD_PBR_UBO_SIZE = derive(DEFAULT_STANDARD_PBR_PARAM_SCHEMA).uboLayout.totalBytes;
4557
+ var MATERIAL_PER_ENTITY_STRIDE = Math.ceil(STANDARD_PBR_UBO_SIZE / 256) * 256;
4349
4558
  function resolveSurfaceProfile(storageFormat, viewFormat, facts) {
4350
4559
  if (facts.surfaceViewFormats) {
4351
4560
  return ok$1({
@@ -4504,17 +4713,64 @@ function isImageError(error) {
4504
4713
  const code = error.code;
4505
4714
  return typeof code === "string" && code.startsWith("image-");
4506
4715
  }
4716
+ function materialDiagnosticsEnabled() {
4717
+ if (typeof globalThis !== "object" || globalThis === null || !("process" in globalThis)) {
4718
+ return false;
4719
+ }
4720
+ const processValue = globalThis.process;
4721
+ return processValue?.env?.FORGEAX_MATERIAL_DIAGNOSTICS === "1";
4722
+ }
4507
4723
  function residentTextureView(world, store, runtime, handle, worldId = world) {
4508
4724
  const podRes = resolveAssetHandle(world, handle);
4509
- if (!podRes.ok) return void 0;
4725
+ const diagnosticsEnabled = materialDiagnosticsEnabled();
4726
+ if (!podRes.ok) {
4727
+ if (diagnosticsEnabled) {
4728
+ console.error(
4729
+ `[render-material] texture resolve failed: ${JSON.stringify({ handle: handleSlot(handle), error: podRes.error })}`
4730
+ );
4731
+ }
4732
+ return void 0;
4733
+ }
4510
4734
  const residentRes = store.ensureResident(handle, podRes.value, worldId);
4511
4735
  if (!residentRes.ok) {
4512
4736
  if (residentRes.error instanceof RhiError || isImageError(residentRes.error)) {
4513
4737
  runtime.errorRegistry.fire(residentRes.error);
4514
4738
  }
4739
+ if (diagnosticsEnabled) {
4740
+ console.error(
4741
+ `[render-material] texture residency failed: ${JSON.stringify({
4742
+ handle: handleSlot(handle),
4743
+ pod: {
4744
+ kind: podRes.value.kind,
4745
+ shape: podRes.value.shape,
4746
+ format: podRes.value.format,
4747
+ dataByteLength: podRes.value.data.byteLength,
4748
+ mips: podRes.value.mips
4749
+ },
4750
+ error: residentRes.error
4751
+ })}`
4752
+ );
4753
+ }
4515
4754
  return void 0;
4516
4755
  }
4517
- return store.getTextureGpuView(handle, worldId);
4756
+ const view = store.getTextureGpuView(handle, worldId);
4757
+ if (diagnosticsEnabled) {
4758
+ console.error(
4759
+ `[render-material] texture residency ready: ${JSON.stringify({
4760
+ handle: handleSlot(handle),
4761
+ pod: {
4762
+ kind: podRes.value.kind,
4763
+ shape: podRes.value.shape,
4764
+ format: podRes.value.format,
4765
+ dataByteLength: podRes.value.data.byteLength,
4766
+ mips: podRes.value.mips
4767
+ },
4768
+ receipt: "receipt" in residentRes.value ? residentRes.value.receipt : void 0,
4769
+ viewReady: view !== void 0
4770
+ })}`
4771
+ );
4772
+ }
4773
+ return view;
4518
4774
  }
4519
4775
  function residentSampler(world, store, runtime, handle, worldId = world) {
4520
4776
  const podRes = resolveAssetHandle(world, handle);
@@ -4581,24 +4837,12 @@ var BUILTIN_USER_REGION_TEXTURE_FIELDS = [
4581
4837
  "baseColorTexture",
4582
4838
  "metallicRoughnessTexture",
4583
4839
  "normalTexture",
4584
- "specularTintTexture",
4585
4840
  "emissiveTexture",
4586
4841
  "occlusionTexture",
4587
4842
  "transmissionTexture",
4588
4843
  "thicknessTexture"
4589
4844
  ];
4590
4845
  var LEGACY_MATERIAL_TEXTURE_SCALE_OFFSET = 80;
4591
- var STANDARD_PBR_TEXTURE_SCALE_OFFSET = 128;
4592
- var MATERIAL_TEXTURE_SCALE_FIELDS = [
4593
- "baseColorTexture",
4594
- "metallicRoughnessTexture",
4595
- "normalTexture",
4596
- "specularTintTexture",
4597
- "emissiveTexture",
4598
- "occlusionTexture",
4599
- "transmissionTexture",
4600
- "thicknessTexture"
4601
- ];
4602
4846
  var LEGACY_MATERIAL_TEXTURE_SCALE_FIELDS = [
4603
4847
  "baseColorTexture",
4604
4848
  "metallicRoughnessTexture",
@@ -4606,75 +4850,7 @@ var LEGACY_MATERIAL_TEXTURE_SCALE_FIELDS = [
4606
4850
  "emissiveTexture",
4607
4851
  "occlusionTexture"
4608
4852
  ];
4609
- var STANDARD_PBR_TEXTURE_COORDINATE_OFFSET = 128;
4610
- var STANDARD_PBR_TEXTURE_COORDINATE_STRIDE = 8;
4611
4853
  var DEFAULT_LEGACY_TEXTURE_SCALES = new Float32Array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
4612
- var DEFAULT_STANDARD_TEXTURE_COORDINATES = new Float32Array([
4613
- 0,
4614
- 0,
4615
- 1,
4616
- 1,
4617
- 0,
4618
- 0,
4619
- 1,
4620
- 1,
4621
- 0,
4622
- 0,
4623
- 1,
4624
- 1,
4625
- 0,
4626
- 0,
4627
- 1,
4628
- 1,
4629
- 0,
4630
- 0,
4631
- 1,
4632
- 1,
4633
- 0,
4634
- 0,
4635
- 1,
4636
- 1,
4637
- 0,
4638
- 0,
4639
- 1,
4640
- 1,
4641
- 0,
4642
- 0,
4643
- 1,
4644
- 1,
4645
- 0,
4646
- 0,
4647
- 1,
4648
- 1,
4649
- 0,
4650
- 0,
4651
- 1,
4652
- 1,
4653
- 0,
4654
- 0,
4655
- 1,
4656
- 1,
4657
- 0,
4658
- 0,
4659
- 1,
4660
- 1,
4661
- 0,
4662
- 0,
4663
- 1,
4664
- 1,
4665
- 0,
4666
- 0,
4667
- 1,
4668
- 1,
4669
- 0,
4670
- 0,
4671
- 1,
4672
- 1,
4673
- 0,
4674
- 0,
4675
- 1,
4676
- 1
4677
- ]);
4678
4854
  function materialTextureUvScale(texture) {
4679
4855
  if (texture === void 0) return [1, 1];
4680
4856
  return deriveTextureExtent(
@@ -4722,17 +4898,13 @@ function applyMaterialTextureUvScales(payload, material, world) {
4722
4898
  }
4723
4899
  return;
4724
4900
  }
4725
- const textureScaleOffset = isStandardPbrMaterialShader(material.materialShaderId) ? STANDARD_PBR_TEXTURE_SCALE_OFFSET : LEGACY_MATERIAL_TEXTURE_SCALE_OFFSET;
4726
- const isStandardPbr = textureScaleOffset === STANDARD_PBR_TEXTURE_SCALE_OFFSET;
4901
+ const textureScaleOffset = LEGACY_MATERIAL_TEXTURE_SCALE_OFFSET;
4727
4902
  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;
4728
4903
  if (!hasTextureMetadata) {
4729
- f32.set(
4730
- isStandardPbr ? DEFAULT_STANDARD_TEXTURE_COORDINATES : DEFAULT_LEGACY_TEXTURE_SCALES,
4731
- textureScaleOffset / 4
4732
- );
4904
+ f32.set(DEFAULT_LEGACY_TEXTURE_SCALES, textureScaleOffset / 4);
4733
4905
  return;
4734
4906
  }
4735
- const fields = isStandardPbr ? MATERIAL_TEXTURE_SCALE_FIELDS : LEGACY_MATERIAL_TEXTURE_SCALE_FIELDS;
4907
+ const fields = LEGACY_MATERIAL_TEXTURE_SCALE_FIELDS;
4736
4908
  for (let index = 0; index < fields.length; index++) {
4737
4909
  const field = fields[index];
4738
4910
  if (field === void 0) continue;
@@ -4740,32 +4912,16 @@ function applyMaterialTextureUvScales(payload, material, world) {
4740
4912
  const resolved = handle === void 0 ? void 0 : resolveAssetHandle(world, handle);
4741
4913
  const texture = resolved?.ok === true ? resolved.value : void 0;
4742
4914
  const [u, v] = materialTextureUvScale(texture);
4743
- if (isStandardPbr) {
4744
- const coordinates = material.textureCoordinates?.get(field);
4745
- const transform = coordinates?.transform;
4746
- const offset = STANDARD_PBR_TEXTURE_COORDINATE_OFFSET / 4 + index * STANDARD_PBR_TEXTURE_COORDINATE_STRIDE;
4747
- f32[offset] = transform?.offset?.[0] ?? 0;
4748
- f32[offset + 1] = transform?.offset?.[1] ?? 0;
4749
- f32[offset + 2] = transform?.scale?.[0] ?? 1;
4750
- f32[offset + 3] = transform?.scale?.[1] ?? 1;
4751
- f32[offset + 4] = coordinates?.set ?? 0;
4752
- f32[offset + 5] = transform?.rotation ?? 0;
4753
- f32[offset + 6] = u;
4754
- f32[offset + 7] = v;
4755
- } else {
4756
- const offset = textureScaleOffset / 4 + index * 2;
4757
- f32[offset] = u;
4758
- f32[offset + 1] = v;
4759
- }
4915
+ const offset = textureScaleOffset / 4 + index * 2;
4916
+ f32[offset] = u;
4917
+ f32[offset + 1] = v;
4760
4918
  }
4761
4919
  }
4762
4920
  function materialCoordinateSchema(material) {
4763
- if (isStandardPbrMaterialShader(material.materialShaderId) && material.paramSnapshot !== void 0) {
4764
- return DEFAULT_STANDARD_PBR_PARAM_SCHEMA;
4765
- }
4766
4921
  if (material.materialParamSchema !== void 0 && material.materialParamSchema.length > 0) {
4767
4922
  return material.materialParamSchema;
4768
4923
  }
4924
+ if (isStandardPbrMaterialShader(material.materialShaderId)) return STANDARD_PIPELINE_PARAM_SCHEMA;
4769
4925
  switch (material.materialShaderId) {
4770
4926
  case "forgeax::default-unlit":
4771
4927
  return DEFAULT_UNLIT_PARAM_SCHEMA;
@@ -4790,6 +4946,9 @@ function defaultViewForUserRegionField(field, pipelineState, schema) {
4790
4946
  }
4791
4947
  if (field === "normalTexture") return pipelineState.defaultNormalTextureView;
4792
4948
  if (field === "baseColorTexture") return pipelineState.fallbackTextureView;
4949
+ if (field === "anisotropyTexture") {
4950
+ return pipelineState.defaultAnisotropyTextureView ?? pipelineState.defaultNormalTextureView;
4951
+ }
4793
4952
  return pipelineState.defaultWhiteTextureView;
4794
4953
  }
4795
4954
  function detectNineSliceScaleTooSmall(transformWorld, slices, renderableIndex, seenIndices, metrics) {
@@ -4814,58 +4973,54 @@ function writePbrMaterialUboPayload(buf, material) {
4814
4973
  }
4815
4974
  buf.fill(0);
4816
4975
  const f32 = materialUboFloatView(buf);
4817
- f32[0] = material.baseColor[0] ?? 0;
4818
- f32[1] = material.baseColor[1] ?? 0;
4819
- f32[2] = material.baseColor[2] ?? 0;
4820
- f32[3] = 1;
4821
- f32[4] = material.metallic;
4822
- f32[5] = material.roughness;
4823
- f32[6] = 2;
4824
- f32[7] = 1;
4825
- f32[8] = 0;
4826
- f32[9] = 0;
4827
- const em = material.emissive;
4828
- f32[12] = em?.[0] ?? 0;
4829
- f32[13] = em?.[1] ?? 0;
4830
- f32[14] = em?.[2] ?? 0;
4831
- f32[15] = material.emissiveIntensity ?? 0;
4832
- f32[16] = material.occlusionStrength ?? 1;
4833
- const alphaCutoff = material.paramSnapshot?.alphaCutoff;
4834
- f32[17] = typeof alphaCutoff === "number" ? alphaCutoff : 0;
4835
- f32[18] = material.clearcoat ?? 0;
4836
- f32[19] = material.clearcoatRoughness ?? 0.5;
4837
- const specularTint = material.specularTint;
4838
- f32[20] = specularTint?.[0] ?? 1;
4839
- f32[21] = specularTint?.[1] ?? 1;
4840
- f32[22] = specularTint?.[2] ?? 1;
4841
- const snapshotNumber = (name, fallback) => {
4842
- const value = material.paramSnapshot?.[name];
4843
- return typeof value === "number" ? value : fallback;
4844
- };
4845
- const snapshotVec3 = (name, fallback) => {
4846
- const value = material.paramSnapshot?.[name];
4847
- return Array.isArray(value) ? [value[0] ?? fallback[0], value[1] ?? fallback[1], value[2] ?? fallback[2]] : fallback;
4976
+ const schema = material.materialParamSchema === void 0 || material.materialParamSchema.length === 0 ? STANDARD_PIPELINE_PARAM_SCHEMA : material.materialParamSchema;
4977
+ const snapshot = material.paramSnapshot;
4978
+ const fallback = (name, entry) => {
4979
+ switch (name) {
4980
+ case "baseColor":
4981
+ return [
4982
+ material.baseColor[0] ?? 1,
4983
+ material.baseColor[1] ?? 1,
4984
+ material.baseColor[2] ?? 1,
4985
+ 1
4986
+ ];
4987
+ case "metallic":
4988
+ return material.metallic;
4989
+ case "roughness":
4990
+ return material.roughness;
4991
+ case "specularColor":
4992
+ return material.specularColor ?? [1, 1, 1];
4993
+ case "normalScale":
4994
+ return material.normalScale ?? 1;
4995
+ case "emissive":
4996
+ return material.emissive ?? [0, 0, 0];
4997
+ case "emissiveIntensity":
4998
+ return material.emissiveIntensity ?? 0;
4999
+ case "occlusionStrength":
5000
+ return material.occlusionStrength ?? 1;
5001
+ default:
5002
+ return entry.default;
5003
+ }
4848
5004
  };
4849
- f32[23] = material.normalScale ?? snapshotNumber("normalScale", 1);
4850
- f32[24] = snapshotNumber("transmission", 0);
4851
- f32[25] = snapshotNumber("ior", 1.5);
4852
- f32[26] = snapshotNumber("thickness", 0);
4853
- const attenuationColor = snapshotVec3("attenuationColor", [1, 1, 1]);
4854
- f32[28] = attenuationColor[0];
4855
- f32[29] = attenuationColor[1];
4856
- f32[30] = attenuationColor[2];
4857
- f32[31] = snapshotNumber("attenuationDistance", 0);
4858
- const paramSnap = material.paramSnapshot;
4859
- if (paramSnap !== void 0) {
4860
- material.materialShaderId;
5005
+ for (const entry of derivedParamSchema(schema).uboLayout.entries) {
5006
+ const value = snapshot?.[entry.name] ?? fallback(entry.name, schema.find((item) => item.name === entry.name) ?? entry);
5007
+ const offset = entry.offset / 4;
5008
+ const width = entry.size / 4;
5009
+ if (typeof value === "number") {
5010
+ f32[offset] = value;
5011
+ } else if (Array.isArray(value)) {
5012
+ for (let index = 0; index < Math.min(width, value.length); index += 1) {
5013
+ const component = value[index];
5014
+ if (typeof component === "number") f32[offset + index] = component;
5015
+ }
5016
+ }
4861
5017
  }
4862
5018
  }
4863
5019
  function applyParamSnapshotToUbo(payload, paramSchema, paramSnapshot) {
4864
5020
  if (paramSchema === void 0) return;
4865
5021
  if (paramSnapshot === void 0) return;
4866
5022
  const f32 = materialUboFloatView(payload);
4867
- const effectiveSchema = Object.hasOwn(paramSnapshot, "transmission") && Object.hasOwn(paramSnapshot, "ior") && Object.hasOwn(paramSnapshot, "thickness") ? DEFAULT_STANDARD_PBR_PARAM_SCHEMA : paramSchema;
4868
- const { uboLayout } = derivedParamSchema(effectiveSchema);
5023
+ const { uboLayout } = derivedParamSchema(paramSchema);
4869
5024
  for (const entry of uboLayout.entries) {
4870
5025
  const value = paramSnapshot[entry.name];
4871
5026
  if (value === void 0) continue;
@@ -4885,6 +5040,36 @@ function applyParamSnapshotToUbo(payload, paramSchema, paramSnapshot) {
4885
5040
  }
4886
5041
  }
4887
5042
  }
5043
+ function recordIblMaterialBinding(frameState, materialBgl, bindGroup, entries, iblStart, cache3) {
5044
+ const receipt = frameState.iblBindingInspection;
5045
+ if (receipt === void 0) return;
5046
+ const skylightBindingStart = entries.findIndex((entry) => entry.binding === iblStart);
5047
+ if (skylightBindingStart < 0) return;
5048
+ const iblEntries = entries.slice(skylightBindingStart, skylightBindingStart + 7);
5049
+ const projected = [];
5050
+ for (const entry of iblEntries) {
5051
+ if (entry.resource.kind === "externalTexture") continue;
5052
+ const value = entry.resource.kind === "buffer" ? entry.resource.value.buffer : entry.resource.value;
5053
+ if (typeof value !== "object" || value === null) continue;
5054
+ projected.push({
5055
+ binding: entry.binding,
5056
+ kind: entry.resource.kind,
5057
+ resourceIdentity: getOpaqueResourceIdentity(value)
5058
+ });
5059
+ }
5060
+ frameState.iblBindingInspection = {
5061
+ ...receipt,
5062
+ material: {
5063
+ bindGroupIdentity: getOpaqueResourceIdentity(bindGroup),
5064
+ materialBglIdentity: getOpaqueResourceIdentity(materialBgl),
5065
+ cache: cache3,
5066
+ skylightBindingStart: iblEntries[0]?.binding ?? 0,
5067
+ entries: projected,
5068
+ reflectionBindings: projected.map((entry) => entry.binding)
5069
+ },
5070
+ errors: []
5071
+ };
5072
+ }
4888
5073
  function isMaterialBgAssemblyCacheHit(cached, material, materialBgl, materialBuffer, skylightResources, materialResourceEpoch, transmissionBackdropView) {
4889
5074
  if (transmissionBackdropView != null) return false;
4890
5075
  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;
@@ -4902,16 +5087,22 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
4902
5087
  materialBgShared,
4903
5088
  materialBgAssemblyCache,
4904
5089
  transmissionBackdropView,
4905
- bindGroupCounts
5090
+ bindGroupCounts,
5091
+ frameState
4906
5092
  } = deps;
4907
5093
  const effectiveSkylightResources = resolveReflectionProbeResources?.(materialWorld, entityKey) ?? skylightResources;
4908
5094
  const smMaterialHandle = submeshMaterial.materialHandle;
4909
5095
  const materialCacheKey = smMaterialHandle === void 0 ? void 0 : `${materialWorld.identity}:${smMaterialHandle}`;
4910
5096
  const smHasVideoFields = (submeshMaterial.videoTextureFields?.size ?? 0) > 0;
4911
5097
  const smShaderId = submeshMaterial.materialShaderId;
4912
- const smPerShaderBgl = smShaderId !== void 0 ? runtime.getMaterialBindGroupLayout?.(smShaderId) : void 0;
5098
+ const smSchema = (smShaderId !== void 0 ? runtime.getParamSchema?.(smShaderId) : void 0) ?? submeshMaterial.materialParamSchema;
5099
+ const smPerShaderBgl = smShaderId !== void 0 ? runtime.getMaterialBindGroupLayout?.(smShaderId, smSchema) : void 0;
4913
5100
  const smMaterialBgl = smPerShaderBgl ?? pipelineState.materialBindGroupLayout;
5101
+ const standardMaterial = isStandardPbrMaterialShader(smShaderId);
5102
+ const physicalFields = standardMaterial && smSchema !== void 0 ? physicalTextureFields(smSchema) : [];
5103
+ const smUserRegionFields = smPerShaderBgl === void 0 || isCanonicalStandardPbrMaterialShader(smShaderId) ? BUILTIN_USER_REGION_TEXTURE_FIELDS : userRegionTextureFieldOrder(smSchema).filter((field) => !physicalFields.includes(field));
4914
5104
  const hasActiveTransmissionBackdrop = transmissionBackdropView != null;
5105
+ const diagnosticsEnabled = materialDiagnosticsEnabled();
4915
5106
  if (smMaterialHandle !== void 0 && !smHasVideoFields && !hasActiveTransmissionBackdrop) {
4916
5107
  const cached = materialCacheKey === void 0 ? void 0 : materialBgAssemblyCache.get(materialCacheKey);
4917
5108
  if (isMaterialBgAssemblyCacheHit(
@@ -4923,11 +5114,48 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
4923
5114
  store.materialResourceEpoch,
4924
5115
  transmissionBackdropView
4925
5116
  )) {
5117
+ if (diagnosticsEnabled) {
5118
+ recordIblMaterialBinding(
5119
+ frameState,
5120
+ smMaterialBgl,
5121
+ cached.bindGroup,
5122
+ [
5123
+ {
5124
+ binding: 1 + smUserRegionFields.length * 2,
5125
+ resource: { kind: "textureView", value: skylightResources.irradianceView }
5126
+ },
5127
+ {
5128
+ binding: 2 + smUserRegionFields.length * 2,
5129
+ resource: { kind: "sampler", value: skylightResources.irradianceSampler }
5130
+ },
5131
+ {
5132
+ binding: 3 + smUserRegionFields.length * 2,
5133
+ resource: { kind: "textureView", value: skylightResources.prefilterView }
5134
+ },
5135
+ {
5136
+ binding: 4 + smUserRegionFields.length * 2,
5137
+ resource: { kind: "sampler", value: skylightResources.prefilterSampler }
5138
+ },
5139
+ {
5140
+ binding: 5 + smUserRegionFields.length * 2,
5141
+ resource: { kind: "textureView", value: skylightResources.brdfLutView }
5142
+ },
5143
+ {
5144
+ binding: 6 + smUserRegionFields.length * 2,
5145
+ resource: { kind: "sampler", value: skylightResources.brdfLutSampler }
5146
+ },
5147
+ {
5148
+ binding: 7 + smUserRegionFields.length * 2,
5149
+ resource: { kind: "buffer", value: { buffer: skylightResources.intensityBuffer } }
5150
+ }
5151
+ ],
5152
+ 1 + smUserRegionFields.length * 2,
5153
+ "hit"
5154
+ );
5155
+ }
4926
5156
  return cached.bindGroup;
4927
5157
  }
4928
5158
  }
4929
- const smSchema = submeshMaterial.materialParamSchema ?? (smShaderId !== void 0 ? runtime.getParamSchema?.(smShaderId) : void 0);
4930
- const smUserRegionFields = smPerShaderBgl === void 0 || isStandardPbrMaterialShader(smShaderId) ? BUILTIN_USER_REGION_TEXTURE_FIELDS : userRegionTextureFieldOrder(smSchema);
4931
5159
  let materialResourcesResident = true;
4932
5160
  const smSamplerForField = (field) => {
4933
5161
  const handle = field === void 0 ? void 0 : submeshMaterial.samplerHandles?.get(field);
@@ -4993,6 +5221,33 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
4993
5221
  }
4994
5222
  );
4995
5223
  }
5224
+ const physicalTextureInjections = [];
5225
+ for (let slot = 0; slot < physicalFields.length; slot += 1) {
5226
+ const field = physicalFields[slot];
5227
+ if (field === void 0) continue;
5228
+ let view = defaultViewForUserRegionField(field, pipelineState, smSchema);
5229
+ const targetSource = materialRenderTargetSourceForField(
5230
+ submeshMaterial,
5231
+ field,
5232
+ resolveRenderTargetTextureSource
5233
+ );
5234
+ if (targetSource !== void 0) {
5235
+ if (targetSource.textureView !== void 0) view = targetSource.textureView;
5236
+ else materialResourcesResident = false;
5237
+ } else {
5238
+ const handle = submeshMaterial.textureHandles?.get(field);
5239
+ if (handle !== void 0) {
5240
+ const resident = residentTextureView(materialWorld, store, runtime, handle);
5241
+ if (resident !== void 0) view = resident;
5242
+ else materialResourcesResident = false;
5243
+ }
5244
+ }
5245
+ physicalTextureInjections.push({
5246
+ slot,
5247
+ sampler: smSamplerForField(field),
5248
+ view
5249
+ });
5250
+ }
4996
5251
  const transmissionSampler = transmissionBackdropView != null ? effectiveSkylightResources.prefilterSampler : pipelineState.defaultSampler;
4997
5252
  const smMergedEntries = assembleMaterialWithSkylightEntries(
4998
5253
  smBaseEntries,
@@ -5000,7 +5255,8 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5000
5255
  {
5001
5256
  sampler: transmissionSampler,
5002
5257
  ...transmissionBackdropView === void 0 ? {} : { backdropView: transmissionBackdropView }
5003
- }
5258
+ },
5259
+ physicalTextureInjections
5004
5260
  );
5005
5261
  const createBindGroup = () => {
5006
5262
  const result = runtime.device.createBindGroup({
@@ -5011,14 +5267,51 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5011
5267
  if (!result.ok) throw result.error;
5012
5268
  return result.value;
5013
5269
  };
5270
+ const materialCacheHandles = smMergedEntries.map((entry) => extractEntryResourceHandle(entry));
5271
+ if (diagnosticsEnabled && submeshMaterial.textureHandles === void 0 && smMaterialHandle !== void 0) {
5272
+ console.error(
5273
+ `[render-material] scalar material cache handles: ${JSON.stringify({
5274
+ entityKey,
5275
+ materialHandle: smMaterialHandle,
5276
+ shader: smShaderId,
5277
+ paramSnapshot: submeshMaterial.paramSnapshot,
5278
+ handleTypes: materialCacheHandles.map((handle) => typeof handle)
5279
+ })}`
5280
+ );
5281
+ }
5282
+ if (diagnosticsEnabled && materialCacheHandles.some((handle) => typeof handle !== "object" || handle === null)) {
5283
+ console.error(
5284
+ `[render-material] invalid material cache handle: ${JSON.stringify({
5285
+ entityKey,
5286
+ materialHandle: smMaterialHandle,
5287
+ shader: smShaderId,
5288
+ entries: smMergedEntries.map((entry, index) => ({
5289
+ index,
5290
+ binding: entry.binding,
5291
+ kind: entry.resource.kind,
5292
+ handleType: typeof materialCacheHandles[index]
5293
+ }))
5294
+ })}`
5295
+ );
5296
+ }
5014
5297
  const smBindGroup = hasActiveTransmissionBackdrop ? createBindGroup() : getOrCreatePerEntity(
5015
5298
  materialBgShared,
5016
5299
  smShaderId ?? "",
5017
- smMergedEntries.map((e) => extractEntryResourceHandle(e)),
5300
+ materialCacheHandles,
5018
5301
  "material-shared",
5019
5302
  createBindGroup,
5020
5303
  bindGroupCounts
5021
5304
  );
5305
+ if (diagnosticsEnabled) {
5306
+ recordIblMaterialBinding(
5307
+ frameState,
5308
+ smMaterialBgl,
5309
+ smBindGroup,
5310
+ smMergedEntries,
5311
+ smBaseEntries.length,
5312
+ "miss"
5313
+ );
5314
+ }
5022
5315
  if (smMaterialHandle !== void 0 && !smHasVideoFields && materialResourcesResident && !hasActiveTransmissionBackdrop) {
5023
5316
  if (materialCacheKey !== void 0) {
5024
5317
  materialBgAssemblyCache.set(materialCacheKey, {
@@ -5035,6 +5328,85 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5035
5328
  }
5036
5329
 
5037
5330
  // src/record/main-pass-geometry.ts
5331
+ function materialDiagnosticsEnabled2() {
5332
+ if (typeof globalThis !== "object" || globalThis === null || !("process" in globalThis)) {
5333
+ return false;
5334
+ }
5335
+ const processValue = globalThis.process;
5336
+ return processValue?.env?.FORGEAX_MATERIAL_DIAGNOSTICS === "1";
5337
+ }
5338
+ function requestsStandardTransmissionVariant(material) {
5339
+ return material.materialShaderId === "forgeax::default-standard-pbr" && material.paramSnapshot?.transmission !== void 0;
5340
+ }
5341
+ function recordIblPipelineBinding(c, material, pipeline, materialGroup) {
5342
+ const receipt = c.frameState.iblBindingInspection;
5343
+ if (receipt === void 0 || !isStandardPbrMaterialShader(material.materialShaderId)) return;
5344
+ const materialBgl = c.runtime.getMaterialBindGroupLayout?.(
5345
+ material.materialShaderId ?? "forgeax::default-standard-pbr"
5346
+ ) ?? c.pipelineState.materialBindGroupLayout;
5347
+ c.frameState.iblBindingInspection = {
5348
+ ...receipt,
5349
+ pipeline: {
5350
+ pipelineIdentity: getOpaqueResourceIdentity(pipeline),
5351
+ effectiveMaterialLayoutIdentity: effectiveMaterialLayoutIdentity(
5352
+ material.materialShaderId ?? "forgeax::default-standard-pbr",
5353
+ material.materialParamSchema
5354
+ ),
5355
+ materialBglIdentity: getOpaqueResourceIdentity(materialBgl),
5356
+ bindGroupIdentity: getOpaqueResourceIdentity(materialGroup),
5357
+ drawFrameId: c.frameState.frameNumber
5358
+ }
5359
+ };
5360
+ const materialReceipt = c.frameState.iblBindingInspection.material;
5361
+ const errors = [];
5362
+ if (materialReceipt !== void 0 && materialReceipt.materialBglIdentity !== getOpaqueResourceIdentity(materialBgl)) {
5363
+ errors.push("material-pipeline-bgl-identity-mismatch");
5364
+ }
5365
+ if (materialReceipt !== void 0 && materialReceipt.bindGroupIdentity !== getOpaqueResourceIdentity(materialGroup)) {
5366
+ errors.push("material-bind-group-identity-mismatch");
5367
+ }
5368
+ if (materialReceipt !== void 0) {
5369
+ const expectedResourceIdentities = [
5370
+ receipt.resources.irradiance.viewIdentity,
5371
+ receipt.resources.irradiance.samplerIdentity,
5372
+ receipt.resources.prefilter.viewIdentity,
5373
+ receipt.resources.prefilter.samplerIdentity,
5374
+ receipt.resources.brdfLut.viewIdentity,
5375
+ receipt.resources.brdfLut.samplerIdentity,
5376
+ receipt.resources.intensityBufferIdentity
5377
+ ];
5378
+ const actualResourceIdentities = materialReceipt.entries.filter((entry) => entry.binding >= materialReceipt.skylightBindingStart).slice(0, expectedResourceIdentities.length).map((entry) => entry.resourceIdentity);
5379
+ if (actualResourceIdentities.length !== expectedResourceIdentities.length || actualResourceIdentities.some(
5380
+ (identity, index) => identity !== expectedResourceIdentities[index]
5381
+ )) {
5382
+ errors.push("material-ibl-resource-identity-mismatch");
5383
+ }
5384
+ }
5385
+ c.frameState.iblBindingInspection = {
5386
+ ...c.frameState.iblBindingInspection,
5387
+ status: errors.length === 0 ? "binding-chain-consistent" : "binding-chain-mismatch",
5388
+ errors
5389
+ };
5390
+ }
5391
+ var SHARED_BOOT_MATERIAL_SHADER_IDS = /* @__PURE__ */ new Set([
5392
+ "forgeax::default-unlit",
5393
+ "forgeax::default-shadow-caster",
5394
+ "forgeax::sprite",
5395
+ "forgeax::sprite-lit",
5396
+ "forgeax::default-sprite",
5397
+ "forgeax::msdf-text",
5398
+ "forgeax::default-standard-pbr",
5399
+ "forgeax::pbr-skin",
5400
+ "forgeax::default-standard-pbr-skin"
5401
+ ]);
5402
+ function effectiveMaterialLayoutIdentity(materialShaderId, materialParamSchema) {
5403
+ if (materialParamSchema === void 0) return void 0;
5404
+ const hasStandardPhysicalMaps = isStandardPbrMaterialShader(materialShaderId) && physicalTextureFields(materialParamSchema).length > 0;
5405
+ if (SHARED_BOOT_MATERIAL_SHADER_IDS.has(materialShaderId) && !hasStandardPhysicalMaps) {
5406
+ return void 0;
5407
+ }
5408
+ return materialBindGroupLayoutIdentity(materialShaderId, materialParamSchema);
5409
+ }
5038
5410
  function geometryRenderStateForPass(base, passKind) {
5039
5411
  if (passKind !== "temporal") return base;
5040
5412
  return {
@@ -5095,6 +5467,7 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5095
5467
  viewBindGroup,
5096
5468
  viewBindGroupDynamicOffset = 0
5097
5469
  } = c;
5470
+ const diagnosticsEnabled = materialDiagnosticsEnabled2();
5098
5471
  const colorFormatOverride = c.transparentColorFormat;
5099
5472
  const clusteredLighting = c.standardLighting?.kind === "clustered";
5100
5473
  const isHdrTarget = tonemapActive || colorFormatOverride === "rgba16float";
@@ -5289,7 +5662,10 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5289
5662
  const probeBlendRecordAvailable = runtime.device.caps.storageBuffer && entry.source.probeBlendRecord !== void 0;
5290
5663
  const skinAllocator = pipelineState.skinPaletteAllocator;
5291
5664
  const skinSlice = entry.source.skin;
5292
- const clusteredSkin = clusteredLighting;
5665
+ const clusteredSkin = clusteredLighting && entry.source.materials.every((material) => {
5666
+ const shaderId = material.materialShaderId;
5667
+ return !isStandardPbrSkinMaterialShader(shaderId) || isCanonicalStandardPbrMaterialShader(shaderId);
5668
+ });
5293
5669
  const skinMeshBindGroupLayout = clusteredSkin ? pipelineState.hdrpSkinMeshBindGroupLayout : pipelineState.pbrSkinMeshBindGroupLayout;
5294
5670
  const hdrpSkinBuffers = isSkinEntry && clusteredSkin ? getOrCreateHdrpBuffers(runtime, frameState.installedPipelineConfig?.clusterGrid) : null;
5295
5671
  const skinResources = isSkinEntry && skinMeshBindGroupLayout !== null && skinAllocator !== null && skinSlice !== void 0 && (!clusteredSkin || hdrpSkinBuffers !== null) ? {
@@ -5390,6 +5766,19 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5390
5766
  entry.source.skin?.byteOffset ?? 0
5391
5767
  );
5392
5768
  } else if (isSkinEntry) {
5769
+ if (diagnosticsEnabled) {
5770
+ console.error(
5771
+ `[render-material] skin draw skipped: ${JSON.stringify({
5772
+ entityKey: entry.source.entityKey,
5773
+ materialHandle: entry.source.material.materialHandle,
5774
+ reason: "skin-pipeline-unavailable",
5775
+ skinResourcesReady: skinResources !== null,
5776
+ skinPsoReady: skinPsoProbe !== null,
5777
+ paletteBufferReady: skinSlice?.buffer !== void 0,
5778
+ paletteByteOffset: skinSlice?.byteOffset
5779
+ })}`
5780
+ );
5781
+ }
5393
5782
  continue;
5394
5783
  }
5395
5784
  let perSubmeshBg = null;
@@ -5408,6 +5797,18 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5408
5797
  continue;
5409
5798
  }
5410
5799
  const materialSlot = materialSlotIndices[i]?.[matSlotIdx] ?? materialSlotIndices[i]?.[0] ?? 0;
5800
+ const processValue = globalThis.process;
5801
+ if (processValue?.env?.FORGEAX_MATERIAL_PIPELINE_DIAGNOSTICS === "1" && (submeshMaterial.materialShaderId === "forgeax::default-standard-pbr" || submeshMaterial.materialShaderId === "forgeax::pbr-skin")) {
5802
+ console.error(
5803
+ `[render-material] draw receipt: ${JSON.stringify({
5804
+ entityIndex: entry.renderableIndex,
5805
+ materialSlot,
5806
+ materialHandle: submeshMaterial.materialHandle,
5807
+ clearcoat: submeshMaterial.paramSnapshot?.clearcoat,
5808
+ shader: submeshMaterial.materialShaderId
5809
+ })}`
5810
+ );
5811
+ }
5411
5812
  perSubmeshBg = profilePhase === void 0 ? resolveMaterialBindGroup(
5412
5813
  materialSlot,
5413
5814
  submeshMaterial,
@@ -5427,7 +5828,7 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5427
5828
  materialGroup1DynamicOffsets[0] = materialSlot * MATERIAL_PER_ENTITY_STRIDE;
5428
5829
  pass.setBindGroup(1, perSubmeshBg, materialGroup1DynamicOffsets, 0, 1);
5429
5830
  const smTopology = sm.topology;
5430
- const smMaterialShaderId = entry.source.skin !== void 0 ? SKIN_MATERIAL_SHADER_ID : submeshMaterial.materialShaderId;
5831
+ const smMaterialShaderId = entry.source.skin !== void 0 ? isStandardPbrSkinMaterialShader(submeshMaterial.materialShaderId) ? submeshMaterial.materialShaderId : SKIN_MATERIAL_SHADER_ID : submeshMaterial.materialShaderId;
5431
5832
  const probeBlendAvailable = probeBlendRecordAvailable && isStandardPbrMaterialShader(smMaterialShaderId);
5432
5833
  const isSpriteShader = smMaterialShaderId === "forgeax::sprite" || smMaterialShaderId === "forgeax::sprite-lit";
5433
5834
  const basePipelineRenderState = isSpriteShader ? {
@@ -5477,7 +5878,9 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5477
5878
  hasVertexColor,
5478
5879
  probeBlendAvailable
5479
5880
  );
5480
- const variantSet = isSpriteShader ? smMaterialShaderId === "forgeax::sprite" && entry.source.spriteInstances !== void 0 ? "" : void 0 : entry.variantSet === void 0 ? capabilityVariantSet : capabilityVariantSet === "" ? entry.variantSet : `${capabilityVariantSet}+${entry.variantSet}`;
5881
+ const transmissionAvailable = requestsStandardTransmissionVariant(submeshMaterial);
5882
+ const materialCapabilityVariantSet = transmissionAvailable && !capabilityVariantSet.includes("TRANSMISSION_AVAILABLE=") ? `${capabilityVariantSet === "" ? "" : `${capabilityVariantSet}+`}TRANSMISSION_AVAILABLE=true` : capabilityVariantSet;
5883
+ const variantSet = isSpriteShader ? smMaterialShaderId === "forgeax::sprite" && entry.source.spriteInstances !== void 0 ? "" : void 0 : entry.variantSet === void 0 ? materialCapabilityVariantSet : capabilityVariantSet === "" ? entry.variantSet : `${capabilityVariantSet}+${entry.variantSet}`;
5481
5884
  const cachedPipeline = resolveMaterialPipeline(
5482
5885
  smMaterialShaderId,
5483
5886
  pipelineRenderState,
@@ -5493,6 +5896,16 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5493
5896
  smPipelineHandle = selectGeometryPipeline(pipelineState, isHdrTarget, msaaActive);
5494
5897
  }
5495
5898
  if (smPipelineHandle === null) {
5899
+ if (diagnosticsEnabled) {
5900
+ console.error(
5901
+ `[render-material] draw skipped: ${JSON.stringify({
5902
+ entityKey: entry.source.entityKey,
5903
+ materialHandle: submeshMaterial.materialHandle,
5904
+ shader: smMaterialShaderId,
5905
+ reason: "pipeline-unavailable"
5906
+ })}`
5907
+ );
5908
+ }
5496
5909
  continue;
5497
5910
  }
5498
5911
  const instanceDraws2 = resolveGeometryInstanceBuffer(
@@ -5513,6 +5926,29 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5513
5926
  if (selectedGroup === null) continue;
5514
5927
  group2BindGroup = selectedGroup;
5515
5928
  }
5929
+ if (diagnosticsEnabled && submeshMaterial.textureHandles !== void 0) {
5930
+ console.error(
5931
+ `[render-material] draw submitted: ${JSON.stringify({
5932
+ entityKey: entry.source.entityKey,
5933
+ materialHandle: submeshMaterial.materialHandle,
5934
+ shader: smMaterialShaderId,
5935
+ textureHandles: [...submeshMaterial.textureHandles.entries()].map(
5936
+ ([field, handle]) => ({
5937
+ field,
5938
+ handle
5939
+ })
5940
+ ),
5941
+ skin: isSkinEntry ? {
5942
+ resourcesReady: skinResources !== null,
5943
+ psoReady: skinPsoProbe !== null,
5944
+ paletteBufferReady: skinSlice?.buffer !== void 0,
5945
+ paletteByteOffset: skinSlice?.byteOffset,
5946
+ dynamicOffsets: [...group2DynamicOffsets]
5947
+ } : void 0,
5948
+ pipelineReady: true
5949
+ })}`
5950
+ );
5951
+ }
5516
5952
  pass.setBindGroup(2, group2BindGroup, group2DynamicOffsets);
5517
5953
  if (profilePhase === void 0) {
5518
5954
  recordOcclusionCandidate(
@@ -5546,8 +5982,218 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5546
5982
  )
5547
5983
  );
5548
5984
  }
5985
+ if (diagnosticsEnabled && perSubmeshBg !== null) {
5986
+ recordIblPipelineBinding(c, submeshMaterial, smPipelineHandle, perSubmeshBg);
5987
+ }
5988
+ }
5989
+ }
5990
+ }
5991
+ function uploadInstanceRanges(c, inst, buffer, ranges, storageBacked, sourceOffsetInstances = 0) {
5992
+ const strideBytes = (storageBacked ? INSTANCE_STORAGE_STRIDE_FLOATS : 16) * Float32Array.BYTES_PER_ELEMENT;
5993
+ const uploadedRanges = [];
5994
+ let uploadedBytes = 0;
5995
+ for (const range of ranges) {
5996
+ const start = Math.max(0, Math.min(inst.instanceCount, range.start));
5997
+ const end = Math.max(start, Math.min(inst.instanceCount, range.end));
5998
+ if (end <= start) continue;
5999
+ const source = inst.transforms.subarray(
6000
+ (sourceOffsetInstances + start) * 16,
6001
+ (sourceOffsetInstances + end) * 16
6002
+ );
6003
+ const payload = storageBacked ? packInstanceStorageBuffer(source) : source;
6004
+ const written = c.runtime.device.queue.writeBuffer(buffer, start * strideBytes, payload);
6005
+ if (!written.ok) {
6006
+ c.runtime.errorRegistry.fire(written.error);
6007
+ return { ok: false, ranges: uploadedRanges, bytes: uploadedBytes };
6008
+ }
6009
+ uploadedRanges.push({ start, end });
6010
+ uploadedBytes += payload.byteLength;
6011
+ }
6012
+ return { ok: true, ranges: uploadedRanges, bytes: uploadedBytes };
6013
+ }
6014
+ function reportInstanceResidency(c, inst, input) {
6015
+ if (inst.collectionId === void 0 || c.frameState.instanceResidency === void 0) return;
6016
+ recordInstanceResidency(c.frameState.instanceResidency, {
6017
+ collectionId: inst.collectionId,
6018
+ frameNumber: c.frameState.frameNumber,
6019
+ residentGeneration: c.runtime.deviceScope.generation,
6020
+ lane: input.lane,
6021
+ requestedBytes: input.requestedBytes,
6022
+ supportedBytes: input.supportedBytes,
6023
+ uploadRanges: input.uploadRanges,
6024
+ uploadedBytes: input.uploadedBytes,
6025
+ backend: c.runtime.device.caps.backendKind
6026
+ });
6027
+ }
6028
+ function reportInstanceFailure(c, inst, error, requestedBytes, supportedBytes) {
6029
+ if (inst.collectionId === void 0 || c.frameState.instanceResidency === void 0) return;
6030
+ recordInstanceFailure(c.frameState.instanceResidency, {
6031
+ collectionId: inst.collectionId,
6032
+ code: error.code,
6033
+ expected: error.expected,
6034
+ hint: error.hint,
6035
+ facts: {
6036
+ requestedBytes,
6037
+ supportedBytes,
6038
+ backend: c.runtime.device.caps.backendKind,
6039
+ owner: "renderer.instances",
6040
+ cause: error.code,
6041
+ recovery: error.hint
6042
+ }
6043
+ });
6044
+ }
6045
+ function clearInstanceChunksForOwner(c, ownerKey) {
6046
+ const chunks = c.frameState.instanceBufferChunks;
6047
+ if (chunks === void 0) return;
6048
+ const prefix = `${ownerKey}:`;
6049
+ for (const [key, entry] of chunks.entries()) {
6050
+ if (!key.startsWith(prefix)) continue;
6051
+ if (!entry.buffer.isDestroyed) {
6052
+ const destroyed = entry.buffer.destroy();
6053
+ if (!destroyed.ok) c.runtime.errorRegistry.fire(destroyed.error);
6054
+ }
6055
+ chunks.delete(key);
6056
+ }
6057
+ }
6058
+ function resolveStorageInstanceChunks(c, entry, inst, cap, probeBuffer, probeOffset) {
6059
+ const { runtime, frameState } = c;
6060
+ const strideBytes = INSTANCE_STORAGE_STRIDE_FLOATS * Float32Array.BYTES_PER_ELEMENT;
6061
+ const chunkCapacity = Math.floor(cap / strideBytes);
6062
+ if (chunkCapacity < 1) {
6063
+ reportInstanceFailure(
6064
+ c,
6065
+ inst,
6066
+ {
6067
+ code: "limit-exceeded",
6068
+ expected: `maxStorageBufferBindingSize (${cap}) >= ${strideBytes}`,
6069
+ hint: "use a backend with storage-buffer bindings large enough for one InstanceData record"
6070
+ },
6071
+ inst.instanceCount * strideBytes,
6072
+ cap
6073
+ );
6074
+ runtime.errorRegistry.fire(
6075
+ new RhiError({
6076
+ code: "limit-exceeded",
6077
+ expected: `maxStorageBufferBindingSize (${cap}) >= ${strideBytes}`,
6078
+ hint: "use a backend with storage-buffer bindings large enough for one InstanceData record",
6079
+ detail: { maxStorageBufferBindingSize: cap, requestedBytes: strideBytes }
6080
+ })
6081
+ );
6082
+ return null;
6083
+ }
6084
+ const ownerKey = instanceCollectionCacheKey(entry.source.worldId, inst);
6085
+ const chunks = frameState.instanceBufferChunks;
6086
+ const activeKeys = /* @__PURE__ */ new Set();
6087
+ const draws = [];
6088
+ const uploadedRanges = [];
6089
+ let uploadedBytes = 0;
6090
+ for (let start = 0; start < inst.instanceCount; start += chunkCapacity) {
6091
+ const end = Math.min(inst.instanceCount, start + chunkCapacity);
6092
+ const count = end - start;
6093
+ const chunkKey = `${ownerKey}:${start}`;
6094
+ activeKeys.add(chunkKey);
6095
+ const payloadBytes = count * strideBytes;
6096
+ const previous = chunks?.get(chunkKey);
6097
+ let active = previous !== void 0 && previous.uploadedArchVersion === inst.archVersion && previous.uploadedByteLength === payloadBytes ? previous : void 0;
6098
+ let activeIsNew = false;
6099
+ if (active === void 0) {
6100
+ const created = runtime.device.createBuffer({
6101
+ size: payloadBytes,
6102
+ usage: GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST,
6103
+ mappedAtCreation: false
6104
+ });
6105
+ if (!created.ok) {
6106
+ runtime.errorRegistry.fire(created.error);
6107
+ reportInstanceFailure(c, inst, created.error, inst.instanceCount * strideBytes, cap);
6108
+ return null;
6109
+ }
6110
+ if (previous !== void 0 && !previous.buffer.isDestroyed) {
6111
+ const destroyed = previous.buffer.destroy();
6112
+ if (!destroyed.ok) runtime.errorRegistry.fire(destroyed.error);
6113
+ }
6114
+ active = {
6115
+ buffer: new GpuBuffer(runtime.device, created.value),
6116
+ uploadedArchVersion: inst.archVersion,
6117
+ uploadedByteLength: payloadBytes
6118
+ };
6119
+ activeIsNew = true;
6120
+ chunks?.set(chunkKey, active);
6121
+ if (chunks === void 0) frameState.transientInstanceBuffers.push(active);
6122
+ }
6123
+ if (active === void 0) return null;
6124
+ const needsUpload = activeIsNew || active.uploadedRevision !== (inst.revision ?? inst.archVersion);
6125
+ const rangesToUpload = [
6126
+ {
6127
+ start: 0,
6128
+ end: count
6129
+ }
6130
+ ];
6131
+ if (needsUpload && rangesToUpload.length > 0) {
6132
+ const upload = uploadInstanceRanges(
6133
+ c,
6134
+ inst,
6135
+ active.buffer.handle,
6136
+ rangesToUpload,
6137
+ true,
6138
+ start
6139
+ );
6140
+ for (const range of upload.ranges) {
6141
+ uploadedRanges.push({ start: range.start + start, end: range.end + start });
6142
+ }
6143
+ uploadedBytes += upload.bytes;
6144
+ if (!upload.ok) {
6145
+ reportInstanceFailure(
6146
+ c,
6147
+ inst,
6148
+ {
6149
+ code: "queue-write-buffer-failed",
6150
+ expected: "the renderer-owned instance storage buffer accepts the complete instance payload",
6151
+ hint: "retry after the active device is healthy or recover the renderer"
6152
+ },
6153
+ inst.instanceCount * strideBytes,
6154
+ cap
6155
+ );
6156
+ return null;
6157
+ }
5549
6158
  }
6159
+ if (needsUpload) {
6160
+ {
6161
+ const published = { ...active, uploadedRevision: inst.revision ?? inst.archVersion };
6162
+ active = published;
6163
+ chunks?.set(chunkKey, published);
6164
+ }
6165
+ }
6166
+ const activeBuffer = active;
6167
+ draws.push({
6168
+ instanceBuffer: activeBuffer.buffer.handle,
6169
+ instanceBindGroup: resolveGeometryInstancesBindGroup(
6170
+ c,
6171
+ activeBuffer.buffer.handle,
6172
+ probeBuffer
6173
+ ),
6174
+ probeOffset,
6175
+ instanceCount: count
6176
+ });
5550
6177
  }
6178
+ if (chunks !== void 0) {
6179
+ const prefix = `${ownerKey}:`;
6180
+ for (const [key, stale] of chunks.entries()) {
6181
+ if (!key.startsWith(prefix) || activeKeys.has(key)) continue;
6182
+ if (!stale.buffer.isDestroyed) {
6183
+ const destroyed = stale.buffer.destroy();
6184
+ if (!destroyed.ok) runtime.errorRegistry.fire(destroyed.error);
6185
+ }
6186
+ chunks.delete(key);
6187
+ }
6188
+ }
6189
+ reportInstanceResidency(c, inst, {
6190
+ lane: "chunked-storage",
6191
+ requestedBytes: inst.instanceCount * strideBytes,
6192
+ supportedBytes: cap,
6193
+ uploadRanges: uploadedRanges,
6194
+ uploadedBytes
6195
+ });
6196
+ return draws;
5551
6197
  }
5552
6198
  function resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws, useProbeBlend) {
5553
6199
  const { runtime, pipelineState, frameState } = c;
@@ -5573,74 +6219,152 @@ function resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws, useProbe
5573
6219
  ];
5574
6220
  }
5575
6221
  if (inst.instanceCount === 0) return [];
6222
+ const entityCacheKey = instanceCollectionCacheKey(entry.source.worldId, inst);
6223
+ const revision = inst.revision ?? inst.archVersion;
6224
+ const sameRevision = (cached) => cached.uploadedRevision === revision;
6225
+ const fullRange = [{ start: 0, end: inst.instanceCount }];
5576
6226
  let instanceBuffer = pipelineState.identityInstanceBuffer;
5577
6227
  let instanceCount = 1;
5578
6228
  {
5579
6229
  const uniformFallback = runtime.device.caps.storageBuffer === false;
5580
6230
  let instanceBufferUsage = GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST;
5581
6231
  if (uniformFallback && inst.instanceCount > MAX_UNIFORM_INSTANCES) {
6232
+ const ownerKey = instanceCollectionCacheKey(entry.source.worldId, inst);
6233
+ const chunks = frameState.instanceBufferChunks;
6234
+ const activeKeys = /* @__PURE__ */ new Set();
5582
6235
  const draws = [];
6236
+ const uniformStrideBytes = 16 * Float32Array.BYTES_PER_ELEMENT;
6237
+ const uploadedRanges = [];
6238
+ let uploadedBytes = 0;
5583
6239
  for (let start = 0; start < inst.instanceCount; start += MAX_UNIFORM_INSTANCES) {
5584
6240
  const count = Math.min(MAX_UNIFORM_INSTANCES, inst.instanceCount - start);
5585
- const chunk = inst.transforms.subarray(start * 16, (start + count) * 16);
5586
- const bufRes = runtime.device.createBuffer({
5587
- size: INSTANCE_UBO_FULL_ARRAY_BYTES,
5588
- usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
5589
- mappedAtCreation: false
5590
- });
5591
- if (!bufRes.ok) {
5592
- runtime.errorRegistry.fire(bufRes.error);
5593
- return null;
6241
+ const chunkKey = `${ownerKey}:${start}`;
6242
+ activeKeys.add(chunkKey);
6243
+ const payloadBytes = count * uniformStrideBytes;
6244
+ const previous = chunks?.get(chunkKey);
6245
+ let active = previous !== void 0 && previous.uploadedArchVersion === inst.archVersion && previous.uploadedByteLength === payloadBytes ? previous : void 0;
6246
+ let activeIsNew = false;
6247
+ if (active === void 0) {
6248
+ const created = runtime.device.createBuffer({
6249
+ size: INSTANCE_UBO_FULL_ARRAY_BYTES,
6250
+ usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
6251
+ mappedAtCreation: false
6252
+ });
6253
+ if (!created.ok) {
6254
+ runtime.errorRegistry.fire(created.error);
6255
+ reportInstanceFailure(
6256
+ c,
6257
+ inst,
6258
+ created.error,
6259
+ inst.instanceCount * uniformStrideBytes,
6260
+ MAX_UNIFORM_INSTANCES * uniformStrideBytes
6261
+ );
6262
+ return null;
6263
+ }
6264
+ if (previous !== void 0 && !previous.buffer.isDestroyed) {
6265
+ const destroyed = previous.buffer.destroy();
6266
+ if (!destroyed.ok) runtime.errorRegistry.fire(destroyed.error);
6267
+ }
6268
+ active = {
6269
+ buffer: new GpuBuffer(runtime.device, created.value),
6270
+ uploadedArchVersion: inst.archVersion,
6271
+ uploadedByteLength: payloadBytes
6272
+ };
6273
+ activeIsNew = true;
6274
+ chunks?.set(chunkKey, active);
6275
+ if (chunks === void 0) frameState.transientInstanceBuffers.push(active);
5594
6276
  }
5595
- const buffer = new GpuBuffer(runtime.device, bufRes.value);
5596
- frameState.transientInstanceBuffers.push({
5597
- buffer,
5598
- uploadedArchVersion: inst.archVersion,
5599
- uploadedByteLength: chunk.byteLength
5600
- });
5601
- const writeRes = runtime.device.queue.writeBuffer(buffer.handle, 0, chunk);
5602
- if (!writeRes.ok) {
5603
- runtime.errorRegistry.fire(writeRes.error);
5604
- return null;
6277
+ const needsUpload = activeIsNew || !sameRevision(active);
6278
+ const rangesToUpload = [
6279
+ {
6280
+ start: 0,
6281
+ end: count
6282
+ }
6283
+ ];
6284
+ if (needsUpload && rangesToUpload.length > 0) {
6285
+ const upload = uploadInstanceRanges(
6286
+ c,
6287
+ inst,
6288
+ active.buffer.handle,
6289
+ rangesToUpload,
6290
+ false,
6291
+ start
6292
+ );
6293
+ for (const range of upload.ranges) {
6294
+ uploadedRanges.push({ start: range.start + start, end: range.end + start });
6295
+ }
6296
+ uploadedBytes += upload.bytes;
6297
+ if (!upload.ok) {
6298
+ reportInstanceFailure(
6299
+ c,
6300
+ inst,
6301
+ {
6302
+ code: "queue-write-buffer-failed",
6303
+ expected: "the renderer-owned instance uniform buffer accepts the complete instance payload",
6304
+ hint: "retry after the active device is healthy or recover the renderer"
6305
+ },
6306
+ inst.instanceCount * uniformStrideBytes,
6307
+ MAX_UNIFORM_INSTANCES * uniformStrideBytes
6308
+ );
6309
+ return null;
6310
+ }
6311
+ }
6312
+ if (needsUpload) {
6313
+ const published = { ...active, uploadedRevision: revision };
6314
+ active = published;
6315
+ chunks?.set(chunkKey, published);
5605
6316
  }
5606
6317
  draws.push({
5607
- instanceBuffer: buffer.handle,
6318
+ instanceBuffer: active.buffer.handle,
5608
6319
  instanceBindGroup: resolveGeometryInstancesBindGroup(
5609
6320
  c,
5610
- buffer.handle,
6321
+ active.buffer.handle,
5611
6322
  probeBinding?.buffer
5612
6323
  ),
5613
6324
  probeOffset: probeBinding?.offset ?? -1,
5614
6325
  instanceCount: count
5615
6326
  });
5616
6327
  }
6328
+ if (chunks !== void 0) {
6329
+ const prefix = `${ownerKey}:`;
6330
+ for (const [key, stale] of chunks.entries()) {
6331
+ if (!key.startsWith(prefix) || activeKeys.has(key)) continue;
6332
+ if (!stale.buffer.isDestroyed) {
6333
+ const destroyed = stale.buffer.destroy();
6334
+ if (!destroyed.ok) runtime.errorRegistry.fire(destroyed.error);
6335
+ }
6336
+ chunks.delete(key);
6337
+ }
6338
+ }
6339
+ reportInstanceResidency(c, inst, {
6340
+ lane: "chunked-uniform",
6341
+ requestedBytes: inst.instanceCount * uniformStrideBytes,
6342
+ supportedBytes: MAX_UNIFORM_INSTANCES * uniformStrideBytes,
6343
+ uploadRanges: uploadedRanges,
6344
+ uploadedBytes
6345
+ });
5617
6346
  return draws;
5618
6347
  }
5619
6348
  if (uniformFallback) {
5620
6349
  instanceBufferUsage = GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST;
5621
6350
  }
5622
6351
  {
5623
- const instancePayload = uniformFallback ? inst.transforms : packInstanceStorageBuffer(inst.transforms);
5624
- const requestedBytes = instancePayload.byteLength;
6352
+ const requestedBytes = inst.instanceCount * (uniformFallback ? 16 : INSTANCE_STORAGE_STRIDE_FLOATS) * Float32Array.BYTES_PER_ELEMENT;
5625
6353
  const cap = runtime.device.limits.maxStorageBufferBindingSize;
5626
6354
  if (!uniformFallback && typeof cap === "number" && cap > 0 && requestedBytes > cap) {
5627
- runtime.errorRegistry.fire(
5628
- new RhiError({
5629
- code: "limit-exceeded",
5630
- expected: `requestedBytes (${requestedBytes}) <= maxStorageBufferBindingSize (${cap})`,
5631
- hint: "reduce instance count to fit within device.limits.maxStorageBufferBindingSize, or split transforms across multiple Instances entries",
5632
- detail: {
5633
- maxStorageBufferBindingSize: cap,
5634
- requestedBytes
5635
- }
5636
- })
6355
+ const chunked = resolveStorageInstanceChunks(
6356
+ c,
6357
+ entry,
6358
+ inst,
6359
+ cap,
6360
+ probeBinding?.buffer,
6361
+ probeBinding?.offset ?? -1
5637
6362
  );
5638
- return null;
6363
+ return chunked;
5639
6364
  } else {
5640
- const cached = frameState.instanceBuffers.get(
5641
- worldEntityKey(entry.source.worldId, inst.cacheKey)
5642
- );
6365
+ const cached = frameState.instanceBuffers.get(entityCacheKey);
5643
6366
  let active = null;
6367
+ let activeIsNew = false;
5644
6368
  if (cached !== void 0 && cached.uploadedArchVersion === inst.archVersion && cached.uploadedByteLength === requestedBytes) {
5645
6369
  active = cached;
5646
6370
  } else if (requestedBytes > 0) {
@@ -5651,6 +6375,8 @@ function resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws, useProbe
5651
6375
  });
5652
6376
  if (!bufRes.ok) {
5653
6377
  runtime.errorRegistry.fire(bufRes.error);
6378
+ reportInstanceFailure(c, inst, bufRes.error, requestedBytes, cap);
6379
+ return null;
5654
6380
  } else {
5655
6381
  if (cached !== void 0 && !cached.buffer.isDestroyed) {
5656
6382
  const r = cached.buffer.destroy();
@@ -5662,24 +6388,57 @@ function resolveGeometryInstanceBuffer(c, entry, identityInstanceDraws, useProbe
5662
6388
  uploadedArchVersion: inst.archVersion,
5663
6389
  uploadedByteLength: requestedBytes
5664
6390
  };
5665
- frameState.instanceBuffers.set(
5666
- worldEntityKey(entry.source.worldId, inst.cacheKey),
5667
- active
5668
- );
6391
+ activeIsNew = true;
6392
+ if (active !== null) {
6393
+ frameState.instanceBuffers.set(entityCacheKey, active);
6394
+ }
5669
6395
  }
5670
6396
  }
5671
6397
  if (active !== null) {
5672
- const writeRes = runtime.device.queue.writeBuffer(
5673
- active.buffer.handle,
5674
- 0,
5675
- instancePayload
5676
- );
5677
- if (!writeRes.ok) {
5678
- runtime.errorRegistry.fire(writeRes.error);
5679
- } else {
5680
- instanceBuffer = active.buffer.handle;
5681
- instanceCount = Math.max(1, inst.instanceCount);
6398
+ const needsUpload = activeIsNew || !sameRevision(active);
6399
+ const ranges = [fullRange[0]];
6400
+ let uploadRanges = [];
6401
+ let uploadedBytes = 0;
6402
+ if (needsUpload && ranges.length > 0) {
6403
+ const upload = uploadInstanceRanges(
6404
+ c,
6405
+ inst,
6406
+ active.buffer.handle,
6407
+ ranges,
6408
+ !uniformFallback
6409
+ );
6410
+ uploadRanges = upload.ranges;
6411
+ uploadedBytes = upload.bytes;
6412
+ if (!upload.ok) {
6413
+ reportInstanceFailure(
6414
+ c,
6415
+ inst,
6416
+ {
6417
+ code: "queue-write-buffer-failed",
6418
+ expected: "the renderer-owned instance buffer accepts the complete instance payloads",
6419
+ hint: "retry after the active device is healthy or recover the renderer"
6420
+ },
6421
+ requestedBytes,
6422
+ uniformFallback ? MAX_UNIFORM_INSTANCES * Float32Array.BYTES_PER_ELEMENT * 16 : typeof cap === "number" ? cap : void 0
6423
+ );
6424
+ return null;
6425
+ }
5682
6426
  }
6427
+ if (needsUpload) {
6428
+ const published = { ...active, uploadedRevision: revision };
6429
+ frameState.instanceBuffers.set(entityCacheKey, published);
6430
+ active = published;
6431
+ }
6432
+ instanceBuffer = active.buffer.handle;
6433
+ instanceCount = Math.max(1, inst.instanceCount);
6434
+ reportInstanceResidency(c, inst, {
6435
+ lane: uniformFallback ? "direct-uniform" : "direct-storage",
6436
+ requestedBytes,
6437
+ supportedBytes: uniformFallback ? MAX_UNIFORM_INSTANCES * Float32Array.BYTES_PER_ELEMENT * 16 : typeof cap === "number" ? cap : void 0,
6438
+ uploadRanges,
6439
+ uploadedBytes
6440
+ });
6441
+ clearInstanceChunksForOwner(c, entityCacheKey);
5683
6442
  }
5684
6443
  }
5685
6444
  }
@@ -6125,9 +6884,10 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
6125
6884
  spritePass.setVertexBuffer(0, spriteEntry.mesh.vertexBuffer.handle);
6126
6885
  lastSpriteVertexBuffer = spriteEntry.mesh.vertexBuffer;
6127
6886
  }
6128
- if (spriteEntry.mesh.indexBuffer !== null && spriteEntry.mesh.indexBuffer !== lastSpriteIndexBuffer) {
6129
- spritePass.setIndexBuffer(spriteEntry.mesh.indexBuffer.handle, spriteEntry.mesh.indexFormat);
6130
- lastSpriteIndexBuffer = spriteEntry.mesh.indexBuffer;
6887
+ const indexBuffer = spriteEntry.mesh.indexBuffer;
6888
+ if (indexBuffer !== null && indexBuffer !== lastSpriteIndexBuffer) {
6889
+ spritePass.setIndexBuffer(indexBuffer.handle, spriteEntry.mesh.indexFormat);
6890
+ lastSpriteIndexBuffer = indexBuffer;
6131
6891
  }
6132
6892
  let spriteInstanceBuffer = pipelineState.identityInstanceBuffer;
6133
6893
  let spriteInstanceCount = 1;
@@ -6216,7 +6976,7 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
6216
6976
  new RhiError({
6217
6977
  code: "limit-exceeded",
6218
6978
  expected: `requestedBytes (${requestedBytes}) <= maxStorageBufferBindingSize (${cap})`,
6219
- hint: "reduce instance count to fit within device.limits.maxStorageBufferBindingSize, or split transforms across multiple Instances entries",
6979
+ hint: "reduce the sprite batch to fit within device.limits.maxStorageBufferBindingSize or use a storage-capable backend",
6220
6980
  detail: {
6221
6981
  maxStorageBufferBindingSize: cap,
6222
6982
  requestedBytes
@@ -6282,6 +7042,7 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
6282
7042
  const spriteGroup2 = entityShaderId === "forgeax::sprite-lit" && c.standardLighting?.kind === "clustered" ? c.hdrpClusterBindGroup : meshBindGroup;
6283
7043
  const spriteInstancesBg = resolveGeometryInstancesBindGroup(c, spriteInstanceBuffer, void 0);
6284
7044
  spritePass.setBindGroup(2, spriteGroup2, [i * MESH_PER_ENTITY_STRIDE]);
7045
+ const materialSlot = materialSlotIndices[i]?.[0] ?? 0;
6285
7046
  const spriteTexHandle = spriteEntry.source.material.baseColorTexture;
6286
7047
  let spriteTexView = pipelineState.defaultWhiteTextureView;
6287
7048
  if (spriteTexHandle !== void 0) {
@@ -6312,7 +7073,6 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
6312
7073
  },
6313
7074
  bindGroupCounts
6314
7075
  );
6315
- const materialSlot = materialSlotIndices[i]?.[0] ?? 0;
6316
7076
  spritePass.setBindGroup(1, spritePassBg, [materialSlot * MATERIAL_PER_ENTITY_STRIDE]);
6317
7077
  spritePass.setBindGroup(3, spriteInstancesBg);
6318
7078
  spritePass.drawIndexed(spriteEntry.mesh.indexCount, spriteInstanceCount, 0, 0, 0);
@@ -6593,9 +7353,16 @@ function shadowShaderMap(c) {
6593
7353
  byMaterial = /* @__PURE__ */ new Map();
6594
7354
  shaders.set(entry.renderableIndex, byMaterial);
6595
7355
  }
7356
+ const candidateSlots = c.materialSlotIndices[entry.renderableIndex] ?? c.materialSlotIndices.flat();
7357
+ const materialSlot = candidateSlots.find(
7358
+ (slot) => c.materialSlots[slot]?.materialHandle === entry.materialHandle
7359
+ );
6596
7360
  byMaterial.set(entry.materialHandle, {
6597
- materialShaderId: entry.materialShaderId,
6598
- renderState: entry.renderState
7361
+ materialHandle: entry.materialHandle,
7362
+ materialShaderId: entry.materialShaderId === "forgeax::default-standard-pbr" ? SHADOW_CASTER_SHADER_ID : entry.materialShaderId,
7363
+ renderState: entry.renderState,
7364
+ materialSlot,
7365
+ paramSnapshot: entry.paramSnapshot
6599
7366
  });
6600
7367
  }
6601
7368
  }
@@ -6826,67 +7593,23 @@ function recordShadowCasterDraws(c, shadowPass, shadowPipeline2, shadowMeshBindG
6826
7593
  } else {
6827
7594
  shadowPass.setBindGroup(2, shadowMeshBindGroup, [i * MESH_PER_ENTITY_STRIDE]);
6828
7595
  }
6829
- let shadowInstanceBuffer = pipelineState.identityInstanceBuffer;
6830
- let shadowInstanceCount = 1;
6831
- const shadowInst = entry.source.instances;
6832
- if (shadowInst?.instanceCount === 0) continue;
6833
- if (shadowInst !== void 0) {
6834
- const uniformFallback = runtime.device.caps.storageBuffer === false;
6835
- if (uniformFallback && shadowInst.instanceCount > MAX_UNIFORM_INSTANCES) {
6836
- shadowInstanceCount = shadowInst.instanceCount;
6837
- shadowInstanceBuffer = pipelineState.identityInstanceBuffer;
6838
- } else {
6839
- const bufUsage = uniformFallback ? GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST : GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST;
6840
- const instancePayload = uniformFallback ? shadowInst.transforms : packInstanceStorageBuffer(shadowInst.transforms);
6841
- const requestedBytes = instancePayload.byteLength;
6842
- const cached = c.frameState.instanceBuffers.get(
6843
- worldEntityKey(entry.source.worldId, shadowInst.cacheKey)
6844
- );
6845
- let active = null;
6846
- if (cached !== void 0 && cached.uploadedArchVersion === shadowInst.archVersion && cached.uploadedByteLength === requestedBytes) {
6847
- active = cached;
6848
- } else if (requestedBytes > 0) {
6849
- const bufRes = runtime.device.createBuffer({
6850
- size: requestedBytes,
6851
- usage: bufUsage,
6852
- mappedAtCreation: false
6853
- });
6854
- if (!bufRes.ok) {
6855
- runtime.errorRegistry.fire(bufRes.error);
6856
- } else {
6857
- if (cached !== void 0 && !cached.buffer.isDestroyed) {
6858
- const r = cached.buffer.destroy();
6859
- if (!r.ok) runtime.errorRegistry.fire(r.error);
6860
- }
6861
- const newBuffer = new GpuBuffer(runtime.device, bufRes.value);
6862
- active = {
6863
- buffer: newBuffer,
6864
- uploadedArchVersion: shadowInst.archVersion,
6865
- uploadedByteLength: requestedBytes
6866
- };
6867
- c.frameState.instanceBuffers.set(
6868
- worldEntityKey(entry.source.worldId, shadowInst.cacheKey),
6869
- active
6870
- );
6871
- }
6872
- }
6873
- if (active !== null) {
6874
- const writeRes = runtime.device.queue.writeBuffer(
6875
- active.buffer.handle,
6876
- 0,
6877
- instancePayload
6878
- );
6879
- if (!writeRes.ok) {
6880
- runtime.errorRegistry.fire(writeRes.error);
6881
- } else {
6882
- shadowInstanceBuffer = active.buffer.handle;
6883
- shadowInstanceCount = Math.max(1, shadowInst.instanceCount);
6884
- }
7596
+ const shadowInstanceDraws = resolveGeometryInstanceBuffer(
7597
+ c,
7598
+ entry,
7599
+ [
7600
+ {
7601
+ instanceBuffer: pipelineState.identityInstanceBuffer,
7602
+ instanceBindGroup: resolveGeometryInstancesBindGroup(
7603
+ c,
7604
+ pipelineState.identityInstanceBuffer
7605
+ ),
7606
+ instanceCount: 1,
7607
+ probeOffset: -1
6885
7608
  }
6886
- }
6887
- }
6888
- const shadowInstancesBg = resolveGeometryInstancesBindGroup(c, shadowInstanceBuffer);
6889
- shadowPass.setBindGroup(3, shadowInstancesBg);
7609
+ ],
7610
+ false
7611
+ );
7612
+ if (shadowInstanceDraws === null) continue;
6890
7613
  for (const sm of shadowSubmeshes) {
6891
7614
  if (sm.topology !== "triangle-list" && sm.topology !== "triangle-strip") {
6892
7615
  continue;
@@ -6916,10 +7639,13 @@ function recordShadowCasterDraws(c, shadowPass, shadowPipeline2, shadowMeshBindG
6916
7639
  shadowPass.setPipeline(entryShadowPipeline);
6917
7640
  shadowLastPipeline = entryShadowPipeline;
6918
7641
  }
6919
- if (entry.mesh.indexed) {
6920
- shadowPass.drawIndexed(sm.indexCount, shadowInstanceCount, sm.indexOffset, 0, 0);
6921
- } else {
6922
- shadowPass.draw(sm.vertexCount, shadowInstanceCount, 0, 0);
7642
+ for (const instanceDraw of shadowInstanceDraws) {
7643
+ shadowPass.setBindGroup(3, instanceDraw.instanceBindGroup);
7644
+ if (entry.mesh.indexed) {
7645
+ shadowPass.drawIndexed(sm.indexCount, instanceDraw.instanceCount, sm.indexOffset, 0, 0);
7646
+ } else {
7647
+ shadowPass.draw(sm.vertexCount, instanceDraw.instanceCount, 0, 0);
7648
+ }
6923
7649
  }
6924
7650
  }
6925
7651
  }
@@ -6983,7 +7709,13 @@ function ensureSpotShadowMaterialBg(c) {
6983
7709
 
6984
7710
  // src/record/main-pass.ts
6985
7711
  var ZERO_SKYLIGHT_PAYLOAD = new Float32Array([0, 0, 0, 0, 0, 0, 0, 1]);
6986
- var TRANSMISSION_VARIANT_SET = "TRANSMISSION_AVAILABLE=true";
7712
+ function materialDiagnosticsEnabled3() {
7713
+ if (typeof globalThis !== "object" || globalThis === null || !("process" in globalThis)) {
7714
+ return false;
7715
+ }
7716
+ const processValue = globalThis.process;
7717
+ return processValue?.env?.FORGEAX_MATERIAL_DIAGNOSTICS === "1";
7718
+ }
6987
7719
  function isTransmissionMaterial(material) {
6988
7720
  return material.materialShaderId === "forgeax::default-standard-pbr" && typeof material.paramSnapshot?.transmission === "number" && material.paramSnapshot.transmission > 0;
6989
7721
  }
@@ -7103,11 +7835,7 @@ function recordMainPass(c, selector, options, graphPass) {
7103
7835
  const matchedMaterials = matchedMaterialsForRecordMode(c, selectorMatchedMaterials, recordMode);
7104
7836
  const recordContext = recordMode === void 0 ? c : {
7105
7837
  ...c,
7106
- splitLdrSprite: recordMode === "transparent" ? false : c.splitLdrSprite,
7107
- validatedOrdered: c.validatedOrdered.map((entry) => ({
7108
- ...entry,
7109
- ...recordMode === "transmission" ? { variantSet: TRANSMISSION_VARIANT_SET } : {}
7110
- }))
7838
+ splitLdrSprite: recordMode === "transparent" ? false : c.splitLdrSprite
7111
7839
  };
7112
7840
  if (clusteredBindGroupMissing) {
7113
7841
  runtime.errorRegistry.fire(
@@ -7161,6 +7889,62 @@ function recordMainPass(c, selector, options, graphPass) {
7161
7889
  brdfLutSampler: skylightFallback.sampler,
7162
7890
  intensityBuffer: skylightFallback.intensityBuffer
7163
7891
  };
7892
+ if (materialDiagnosticsEnabled3()) {
7893
+ const activeCache = activeViews === void 0 ? void 0 : getOrCreateIblCache(runtime.deviceScope);
7894
+ const prefilterViewMipCount = activeCache?.prefilterFaceViewsByMip?.length ?? (activeViews === void 0 ? 1 : 0);
7895
+ frameState.iblBindingInspection = {
7896
+ status: "binding-chain-consistent",
7897
+ frameId: frameState.frameNumber,
7898
+ deviceGeneration: runtime.deviceScope.generation,
7899
+ active: activeViews === void 0 ? "fallback" : "active",
7900
+ cache: {
7901
+ identity: getOpaqueResourceIdentity(activeCache ?? skylightFallback),
7902
+ generation: runtime.deviceScope.generation,
7903
+ prefilterMipCount: prefilterViewMipCount,
7904
+ prefilterViewMipCount
7905
+ },
7906
+ sampler: {
7907
+ expected: {
7908
+ magFilter: "linear",
7909
+ minFilter: "linear",
7910
+ mipmapFilter: "linear",
7911
+ addressModeU: "clamp-to-edge",
7912
+ addressModeV: "clamp-to-edge",
7913
+ addressModeW: "clamp-to-edge"
7914
+ },
7915
+ identities: [
7916
+ getOpaqueResourceIdentity(skylightResources.irradianceSampler),
7917
+ getOpaqueResourceIdentity(skylightResources.prefilterSampler),
7918
+ getOpaqueResourceIdentity(skylightResources.brdfLutSampler)
7919
+ ]
7920
+ },
7921
+ resources: {
7922
+ irradiance: {
7923
+ viewIdentity: getOpaqueResourceIdentity(skylightResources.irradianceView),
7924
+ samplerIdentity: getOpaqueResourceIdentity(
7925
+ skylightResources.irradianceSampler
7926
+ ),
7927
+ deviceGeneration: runtime.deviceScope.generation
7928
+ },
7929
+ prefilter: {
7930
+ viewIdentity: getOpaqueResourceIdentity(skylightResources.prefilterView),
7931
+ samplerIdentity: getOpaqueResourceIdentity(
7932
+ skylightResources.prefilterSampler
7933
+ ),
7934
+ deviceGeneration: runtime.deviceScope.generation
7935
+ },
7936
+ brdfLut: {
7937
+ viewIdentity: getOpaqueResourceIdentity(skylightResources.brdfLutView),
7938
+ samplerIdentity: getOpaqueResourceIdentity(skylightResources.brdfLutSampler),
7939
+ deviceGeneration: runtime.deviceScope.generation
7940
+ },
7941
+ intensityBufferIdentity: getOpaqueResourceIdentity(
7942
+ skylightResources.intensityBuffer
7943
+ )
7944
+ },
7945
+ errors: []
7946
+ };
7947
+ }
7164
7948
  const perSubmeshMaterialBgDeps = {
7165
7949
  runtime,
7166
7950
  pipelineState,
@@ -7196,6 +7980,7 @@ function recordMainPass(c, selector, options, graphPass) {
7196
7980
  },
7197
7981
  materialBgShared: frameState.materialBgShared,
7198
7982
  materialBgAssemblyCache: c.materialBgAssemblyCache,
7983
+ frameState,
7199
7984
  bindGroupCounts,
7200
7985
  ...transmissionBackdropView === void 0 ? {} : { transmissionBackdropView }
7201
7986
  };
@@ -7225,6 +8010,20 @@ function recordMainPass(c, selector, options, graphPass) {
7225
8010
  const materialShaderId = mat.materialShaderId;
7226
8011
  const schema = mat.materialParamSchema ?? (materialShaderId !== void 0 ? runtime.getParamSchema?.(materialShaderId) : void 0);
7227
8012
  applyParamSnapshotToUbo(slotPayloadF32, schema, mat.paramSnapshot);
8013
+ const processValue = globalThis.process;
8014
+ if (processValue?.env?.FORGEAX_MATERIAL_PIPELINE_DIAGNOSTICS === "1" && (materialShaderId === "forgeax::default-standard-pbr" || materialShaderId === "forgeax::pbr-skin")) {
8015
+ console.error(
8016
+ `[render-material] UBO receipt: ${JSON.stringify({
8017
+ entityIndex: entry.renderableIndex,
8018
+ materialHandle: mat.materialHandle,
8019
+ materialShaderId,
8020
+ clearcoat: slotPayloadF32[32],
8021
+ clearcoatRoughness: slotPayloadF32[33],
8022
+ clearcoatNormalScale: slotPayloadF32[34],
8023
+ paramSnapshot: mat.paramSnapshot
8024
+ })}`
8025
+ );
8026
+ }
7228
8027
  applyMaterialTextureUvScales(slotPayloadF32, mat, world);
7229
8028
  {
7230
8029
  const matHandleRaw = mat.baseColorTexture;
@@ -9184,6 +9983,6 @@ function addTaaResolvePass(graph, current, currentTemporal, previousColor, previ
9184
9983
  });
9185
9984
  }
9186
9985
 
9187
- 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, geometryRenderStateForTopology, getOrBuildPipeline, getOrCreateHdrpBuffers, getOrCreateIblCache, getTemporalGpuState, hasPendingTemporalGpuSubmit, importRenderPipelineSurface, incrementFoldedDrawsMetric, isTemporalFullscreenBinding, postProcessShaderEntrySignature, postProcessShaderModuleLabel, postProcessShaderPipelineLabel, prepareFrameLighting, resetHdrpBuffers, residentTextureView, resolveOutputDither, resolveSkyboxActive, resolveSsaoParameters, resolveSurfaceFormatPair, resolveSurfaceProfile, retireTemporalGpuState, retireTemporalGpuStateAfterFence, runIblPrecompute, selectLazyEquirectHandle, selectSwapChainFormat, setIblComposedShaders, standardTemporalLaneAdmission, standardTemporalPostOrder, temporalReadIndex, temporalWriteIndex, typedFrameClearColor, uploadMeshSsboBatch, variantSetFromDefines, warnMultiSkybox, warnMultiSkylight, warnZeroLightStandard, writeHdrpClusterAndSsaoBuffers, writePointsLinesViewUbo, writeShadowCasterUniforms, writeShadowParamsBuffer, writeSpotModifierTextures, writeViewUbo };
9188
- //# sourceMappingURL=chunk-ZUWKAZ2I.mjs.map
9189
- //# sourceMappingURL=chunk-ZUWKAZ2I.mjs.map
9986
+ 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, InstanceProjectionStore, 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, resolveGeometryInstanceBuffer, 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 };
9987
+ //# sourceMappingURL=chunk-TCLDX7SZ.mjs.map
9988
+ //# sourceMappingURL=chunk-TCLDX7SZ.mjs.map