@forgeax/engine-render 0.1.23 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/README.md +131 -9
  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-shader-policy.d.ts +168 -0
  15. package/dist/assembly/material-shader-policy.d.ts.map +1 -0
  16. package/dist/assembly/material-shader-prewarm.d.ts +21 -0
  17. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -0
  18. package/dist/assembly/mesh-ssbo-grow.d.ts +88 -0
  19. package/dist/assembly/mesh-ssbo-grow.d.ts.map +1 -0
  20. package/dist/assembly/recovery/device-loss-fanout.d.ts +14 -0
  21. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -0
  22. package/dist/assembly/recovery/generation.d.ts +46 -0
  23. package/dist/assembly/recovery/generation.d.ts.map +1 -0
  24. package/dist/assembly/recovery/recovery-attempt.d.ts +63 -0
  25. package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -0
  26. package/dist/assembly/recovery/renderer-recover.d.ts +115 -0
  27. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -0
  28. package/dist/assembly/renderer-facade.d.ts +10 -0
  29. package/dist/assembly/renderer-facade.d.ts.map +1 -0
  30. package/dist/assembly/renderer-frame-transaction.d.ts +17 -0
  31. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  32. package/dist/assembly/renderer-helpers.d.ts +19 -0
  33. package/dist/assembly/renderer-helpers.d.ts.map +1 -0
  34. package/dist/assembly/renderer-lifecycle.d.ts +34 -0
  35. package/dist/assembly/renderer-lifecycle.d.ts.map +1 -1
  36. package/dist/assembly/shader-prewarm-policy.d.ts +15 -0
  37. package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -0
  38. package/dist/assembly/webgpu-ready-contract.d.ts +9 -0
  39. package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -0
  40. package/dist/assembly/webgpu-ready.d.ts +55 -0
  41. package/dist/assembly/webgpu-ready.d.ts.map +1 -0
  42. package/dist/assembly/webgpu-renderer.d.ts +152 -0
  43. package/dist/assembly/webgpu-renderer.d.ts.map +1 -0
  44. package/dist/authoring.mjs +1 -1
  45. package/dist/{chunk-4L3P6DT5.mjs → chunk-GE3SDD72.mjs} +37 -5
  46. package/dist/chunk-GE3SDD72.mjs.map +1 -0
  47. package/dist/{chunk-HZUPSYW7.mjs → chunk-KZFTK6DO.mjs} +102 -15
  48. package/dist/chunk-KZFTK6DO.mjs.map +1 -0
  49. package/dist/{chunk-FF7K3FSO.mjs → chunk-Q752SHI5.mjs} +89 -89
  50. package/dist/{chunk-FF7K3FSO.mjs.map → chunk-Q752SHI5.mjs.map} +1 -1
  51. package/dist/{chunk-QODA5UKU.mjs → chunk-RR5L4SRS.mjs} +2 -2
  52. package/dist/chunk-RR5L4SRS.mjs.map +1 -0
  53. package/dist/{chunk-YALPS44A.mjs → chunk-TFH5I7JJ.mjs} +47 -47
  54. package/dist/chunk-TFH5I7JJ.mjs.map +1 -0
  55. package/dist/{chunk-FBC4R44J.mjs → chunk-UQYS7FRM.mjs} +57 -57
  56. package/dist/chunk-UQYS7FRM.mjs.map +1 -0
  57. package/dist/{chunk-4NPUASZR.mjs → chunk-YKB5DQV7.mjs} +161 -19
  58. package/dist/chunk-YKB5DQV7.mjs.map +1 -0
  59. package/dist/{chunk-ZUWKAZ2I.mjs → chunk-YRUYGA4A.mjs} +615 -237
  60. package/dist/chunk-YRUYGA4A.mjs.map +1 -0
  61. package/dist/{chunk-DIYW3M5D.mjs → chunk-Z3YYH2MB.mjs} +2210 -2213
  62. package/dist/chunk-Z3YYH2MB.mjs.map +1 -0
  63. package/dist/construct-renderer.mjs +19841 -16190
  64. package/dist/construct-renderer.mjs.map +1 -1
  65. package/dist/device/device-scope.d.ts +2 -0
  66. package/dist/device/device-scope.d.ts.map +1 -1
  67. package/dist/device/gpu-residency.d.ts +24 -4
  68. package/dist/device/gpu-residency.d.ts.map +1 -1
  69. package/dist/environment/frame.d.ts +5 -0
  70. package/dist/environment/frame.d.ts.map +1 -1
  71. package/dist/environment/inspection.d.ts +7 -0
  72. package/dist/environment/inspection.d.ts.map +1 -1
  73. package/dist/environment/lifecycle.d.ts +20 -2
  74. package/dist/environment/lifecycle.d.ts.map +1 -1
  75. package/dist/errors/recover.d.ts +58 -2
  76. package/dist/errors/recover.d.ts.map +1 -1
  77. package/dist/errors/render.d.ts +13 -0
  78. package/dist/errors/render.d.ts.map +1 -1
  79. package/dist/extract/contracts.d.ts +899 -0
  80. package/dist/extract/contracts.d.ts.map +1 -0
  81. package/dist/features/host.d.ts +2 -0
  82. package/dist/features/host.d.ts.map +1 -1
  83. package/dist/features/prepared-gpu-work.d.ts +3 -0
  84. package/dist/features/prepared-gpu-work.d.ts.map +1 -1
  85. package/dist/features/prepared-graphics-store.d.ts +2 -0
  86. package/dist/features/prepared-graphics-store.d.ts.map +1 -1
  87. package/dist/gpu-driven/production-raster.d.ts +28 -0
  88. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  89. package/dist/gpu-scene.d.ts +1 -0
  90. package/dist/gpu-scene.d.ts.map +1 -1
  91. package/dist/ibl/IblPipelineCache.d.ts +7 -6
  92. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  93. package/dist/ibl/skylight-bind-group.d.ts +7 -1
  94. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  95. package/dist/index.d.ts +1 -1
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.mjs +6 -6
  98. package/dist/internal.mjs +6 -6
  99. package/dist/light-buffer-layout.d.ts +3 -0
  100. package/dist/light-buffer-layout.d.ts.map +1 -1
  101. package/dist/materials.d.ts +34 -10
  102. package/dist/materials.d.ts.map +1 -1
  103. package/dist/mesh-material-bindings.d.ts +56 -0
  104. package/dist/mesh-material-bindings.d.ts.map +1 -1
  105. package/dist/occlusion-proxy.d.ts +4 -0
  106. package/dist/occlusion-proxy.d.ts.map +1 -0
  107. package/dist/pbr-pipeline.d.ts +32 -9
  108. package/dist/pbr-pipeline.d.ts.map +1 -1
  109. package/dist/points-lines/prepare.d.ts +3 -0
  110. package/dist/points-lines/prepare.d.ts.map +1 -1
  111. package/dist/points-lines/standard-owner.d.ts +33 -0
  112. package/dist/points-lines/standard-owner.d.ts.map +1 -0
  113. package/dist/record/frame-snapshot.d.ts +5 -0
  114. package/dist/record/frame-snapshot.d.ts.map +1 -1
  115. package/dist/record/frame-targets.d.ts.map +1 -1
  116. package/dist/record/frame.d.ts +14 -2
  117. package/dist/record/frame.d.ts.map +1 -1
  118. package/dist/record/main-pass-geometry.d.ts +14 -1
  119. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  120. package/dist/record/main-pass-material.d.ts +7 -17
  121. package/dist/record/main-pass-material.d.ts.map +1 -1
  122. package/dist/record/main-pass.d.ts.map +1 -1
  123. package/dist/record/mesh-ssbo.d.ts +11 -3
  124. package/dist/record/mesh-ssbo.d.ts.map +1 -1
  125. package/dist/record/recovery-pipeline.d.ts +30 -0
  126. package/dist/record/recovery-pipeline.d.ts.map +1 -0
  127. package/dist/record/render-context.d.ts +26 -4
  128. package/dist/record/render-context.d.ts.map +1 -1
  129. package/dist/record/typed-frame-graph.d.ts +1 -1
  130. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  131. package/dist/recovery/render-system-candidate.d.ts +65 -0
  132. package/dist/recovery/render-system-candidate.d.ts.map +1 -0
  133. package/dist/render-contract.d.ts +64 -7
  134. package/dist/render-contract.d.ts.map +1 -1
  135. package/dist/render-system-extract-tail.d.ts +12 -0
  136. package/dist/render-system-extract-tail.d.ts.map +1 -0
  137. package/dist/render-system-extract.d.ts +156 -22
  138. package/dist/render-system-extract.d.ts.map +1 -1
  139. package/dist/render-system.d.ts +128 -4
  140. package/dist/render-system.d.ts.map +1 -1
  141. package/dist/scene/render-scene.d.ts +19 -0
  142. package/dist/scene/render-scene.d.ts.map +1 -1
  143. package/dist/temporal/frame-coordinator.d.ts.map +1 -1
  144. package/dist/temporal/index.mjs +6 -6
  145. package/dist/temporal/taa-history-store.d.ts +15 -1
  146. package/dist/temporal/taa-history-store.d.ts.map +1 -1
  147. package/dist/temporal/transaction.d.ts +5 -0
  148. package/dist/temporal/transaction.d.ts.map +1 -1
  149. package/package.json +20 -20
  150. package/src/__tests__/device-feature-admission.unit.test.ts +6 -0
  151. package/src/__tests__/device-loss-fanout.unit.test.ts +107 -0
  152. package/src/__tests__/device-scope-lifecycle.unit.test.ts +21 -0
  153. package/src/__tests__/environment-generation.integration.test.ts +23 -0
  154. package/src/__tests__/expose-renderer-errors.unit.test.ts +34 -0
  155. package/src/__tests__/factory-contract.integration.test.ts +122 -7
  156. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +4 -1
  157. package/src/__tests__/ibl/face-uniforms.unit.test.ts +2 -0
  158. package/src/__tests__/ibl-residency-publication.unit.test.ts +113 -0
  159. package/src/__tests__/material-contract-inventory.unit.test.ts +24 -9
  160. package/src/__tests__/material-cooked-projection.unit.test.ts +19 -1
  161. package/src/__tests__/material-static-dynamic-route.unit.test.ts +29 -0
  162. package/src/__tests__/material-stride-surface.unit.test.ts +7 -3
  163. package/src/__tests__/materials-standard-contract.unit.test.ts +104 -0
  164. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
  165. package/src/__tests__/physical-clearcoat.integration.test.ts +89 -0
  166. package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +1 -1
  167. package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +1 -1
  168. package/src/__tests__/recover-error-policy-owner.unit.test.ts +6 -3
  169. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +140 -0
  170. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +85 -18
  171. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +47 -0
  172. package/src/__tests__/renderer-generation-assembly.unit.test.ts +77 -0
  173. package/src/__tests__/renderer-generation-failure.unit.test.ts +108 -0
  174. package/src/__tests__/renderer-producer-root-matrix.integration.test.ts +33 -0
  175. package/src/__tests__/renderer-recovery-capability-matrix.unit.test.ts +59 -0
  176. package/src/__tests__/renderer-recovery-contract.unit.test.ts +73 -0
  177. package/src/__tests__/renderer-recovery-lifecycle.unit.test.ts +216 -0
  178. package/src/__tests__/renderer-target-history-recovery.integration.test.ts +80 -0
  179. package/src/__tests__/shader-manifest-fixture.ts +13 -16
  180. package/src/__tests__/shader-stage-surface.unit.test.ts +1 -1
  181. package/src/__tests__/standard-layer-projection.unit.test.ts +23 -0
  182. package/src/__tests__/transmission-prewarm.unit.test.ts +95 -85
  183. package/src/__tests__/volumetric-fog-topology.unit.test.ts +3 -1
  184. package/src/assembly/bundler-contract.ts +9 -0
  185. package/src/assembly/device-feature-admission.ts +15 -5
  186. package/src/assembly/extended-lighting-fallback.ts +216 -0
  187. package/src/assembly/factory.ts +81 -10088
  188. package/src/assembly/material/assembly.ts +18 -2
  189. package/src/assembly/material/standard-layer-projection.ts +16 -0
  190. package/src/assembly/material-shader-policy.ts +854 -0
  191. package/src/assembly/material-shader-prewarm.ts +88 -0
  192. package/src/assembly/mesh-ssbo-grow.ts +270 -0
  193. package/src/assembly/recovery/device-loss-fanout.ts +89 -0
  194. package/src/assembly/recovery/generation.ts +141 -0
  195. package/src/assembly/recovery/recovery-attempt.ts +800 -0
  196. package/src/assembly/recovery/renderer-recover.ts +906 -0
  197. package/src/assembly/renderer-facade.ts +342 -0
  198. package/src/assembly/renderer-frame-transaction.ts +45 -0
  199. package/src/assembly/renderer-helpers.ts +99 -0
  200. package/src/assembly/renderer-lifecycle.ts +159 -0
  201. package/src/assembly/shader-prewarm-policy.ts +157 -0
  202. package/src/assembly/webgpu-ready-contract.ts +14 -0
  203. package/src/assembly/webgpu-ready.ts +4079 -0
  204. package/src/assembly/webgpu-renderer.ts +4095 -0
  205. package/src/components/camera.ts +1 -1
  206. package/src/debug-draw-glue.ts +1 -1
  207. package/src/device/device-scope.ts +35 -6
  208. package/src/device/gpu-residency.ts +194 -26
  209. package/src/environment/frame.ts +6 -0
  210. package/src/environment/inspection.ts +10 -0
  211. package/src/environment/lifecycle.ts +114 -8
  212. package/src/errors/recover.ts +76 -5
  213. package/src/errors/render.ts +17 -0
  214. package/src/extract/contracts.ts +974 -0
  215. package/src/features/host.ts +16 -0
  216. package/src/features/prepared-gpu-work.ts +20 -2
  217. package/src/features/prepared-graphics-store.ts +17 -0
  218. package/src/gpu-driven/production-raster.ts +97 -2
  219. package/src/gpu-scene.ts +3 -2
  220. package/src/ibl/IblPipelineCache.ts +134 -50
  221. package/src/ibl/skylight-bind-group.ts +26 -0
  222. package/src/index.ts +9 -1
  223. package/src/light-buffer-layout.ts +3 -0
  224. package/src/materials.ts +262 -45
  225. package/src/mesh-material-bindings.ts +59 -0
  226. package/src/occlusion-proxy.ts +64 -0
  227. package/src/pbr-pipeline.ts +139 -15
  228. package/src/points-lines/__tests__/cache-recovery.unit.test.ts +22 -0
  229. package/src/points-lines/prepare.ts +9 -0
  230. package/src/points-lines/standard-owner.ts +403 -0
  231. package/src/record/__tests__/frame-targets-recovery.unit.test.ts +60 -0
  232. package/src/record/__tests__/main-pass-geometry.unit.test.ts +26 -1
  233. package/src/record/__tests__/mesh-ssbo.unit.test.ts +38 -0
  234. package/src/record/__tests__/recovery-pipeline.unit.test.ts +124 -0
  235. package/src/record/__tests__/sprite-material-abi.unit.test.ts +28 -13
  236. package/src/record/__tests__/standard-pbr-ubo-layout.unit.test.ts +47 -4
  237. package/src/record/__tests__/temporal-payload.unit.test.ts +22 -43
  238. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -1
  239. package/src/record/frame-snapshot.ts +15 -0
  240. package/src/record/frame-targets.ts +11 -1
  241. package/src/record/frame.ts +6 -2
  242. package/src/record/helpers.ts +1 -1
  243. package/src/record/main-pass-geometry.ts +230 -5
  244. package/src/record/main-pass-material.ts +316 -209
  245. package/src/record/main-pass-sprite-draws.ts +6 -7
  246. package/src/record/main-pass.ts +94 -6
  247. package/src/record/mesh-ssbo.ts +32 -10
  248. package/src/record/recovery-pipeline.ts +695 -0
  249. package/src/record/render-context.ts +35 -4
  250. package/src/record/typed-frame-graph.ts +45 -1
  251. package/src/record/view-ubo.ts +3 -3
  252. package/src/recovery/render-system-candidate.ts +839 -0
  253. package/src/render-contract.ts +65 -7
  254. package/src/render-system-extract-tail.ts +2983 -0
  255. package/src/render-system-extract.ts +124 -2977
  256. package/src/render-system.ts +868 -659
  257. package/src/scene/render-scene.ts +124 -1
  258. package/src/temporal/frame-coordinator.ts +4 -1
  259. package/src/temporal/taa-history-store.ts +46 -2
  260. package/src/temporal/temporal-view.ts +1 -1
  261. package/src/temporal/transaction.ts +9 -0
  262. package/src/tilemap-chunk-extract-system.ts +1 -1
  263. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +19 -12
  264. package/dist/chunk-4L3P6DT5.mjs.map +0 -1
  265. package/dist/chunk-4NPUASZR.mjs.map +0 -1
  266. package/dist/chunk-DIYW3M5D.mjs.map +0 -1
  267. package/dist/chunk-FBC4R44J.mjs.map +0 -1
  268. package/dist/chunk-HZUPSYW7.mjs.map +0 -1
  269. package/dist/chunk-QODA5UKU.mjs.map +0 -1
  270. package/dist/chunk-YALPS44A.mjs.map +0 -1
  271. 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-TFH5I7JJ.mjs';
3
+ import { SPRITE_PREMULTIPLIED_ALPHA_BLEND } from './chunk-YKB5DQV7.mjs';
4
+ import { GPU_SHADER_STAGE_COMPUTE, GPU_BUFFER_USAGE_STORAGE, GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_UNIFORM, createHdrpBindGroupLayoutDescriptor, IES_SLICE_WIDTH, IES_SLICE_HEIGHT, COOKIE_SLICE_SIZE, GPU_SHADER_STAGE_VERTEX, GPU_SHADER_STAGE_FRAGMENT, GPU_BUFFER_USAGE_INDEX, GPU_BUFFER_USAGE_VERTEX, pbrSkinMeshDynamicOffsets, worldEntityKey, buildPbrMaterialUserRegionEntries, buildBindGroupLayoutDescriptor, isStandardPbrMaterialShader, SHADOW_CASTER_SHADER_ID, shadowCasterVariantSet, COOKIE_MATRIX_BYTES, matchPass, createPbrSkinMeshBindGroupEntries, getOpaqueResourceIdentity, SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET, physicalTextureFields, isCanonicalStandardPbrMaterialShader, isStandardPbrSkinMaterialShader, SKIN_MATERIAL_SHADER_ID, PROBE_BLEND_RECORD_BYTE_SIZE, resolveRenderTargetMaterialSource, probeBlendRecordOffset, PROBE_BLEND_RECORD_STRIDE, materialBindGroupLayoutIdentity } from './chunk-KZFTK6DO.mjs';
5
+ import { GPU_TEXTURE_USAGE_RENDER_ATTACHMENT, GPU_TEXTURE_USAGE_COPY_SRC, VertexColorVariantConflictError, EquirectProjectionFailedError, SHADOW_ATLAS_DEFAULT_LAYERS, SHADOW_ATLAS_DEFAULT_FACE_SIZE, ShadowAtlas, PointShadowAtlasUninitializedError, PointShadowAtlasBoundsViolationError, StandardClusterTransportUnavailableError, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING, GPU_TEXTURE_USAGE_COPY_DST, GPU_TEXTURE_USAGE_TEXTURE_BINDING, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, StandardLightBudgetExceededError, RendererOperationError, StandardClusterIndexOverflowError, FXAA_POST_PROCESS_ID, VideoUploadUnsupportedError } from './chunk-RR5L4SRS.mjs';
6
+ import { derive, ok, err as err$1, toShared, ASSET_ERROR_HINTS, deriveTextureLayout, handleSlot, KNOWN_PASS_KINDS, resolveMaterialTextureCoordinates } from '@forgeax/engine-types';
7
7
  import { ok as ok$1, err, RhiError } from '@forgeax/engine-rhi';
8
8
  import { mat3, vec3, mat4 } from '@forgeax/engine-math';
9
9
  import { deriveVertexLayoutProjection, deriveVertexBufferLayout } from '@forgeax/engine-geometry';
10
10
  import { resolveAssetHandle } from '@forgeax/engine-assets-runtime';
11
11
  import { probeVideoHighPerfUpload, VIDEO_ELEMENT_PROVIDER_KEY } from '@forgeax/engine-graphics-extras';
12
- import { DEFAULT_STANDARD_PBR_PARAM_SCHEMA, 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) {
@@ -839,10 +839,10 @@ function extractEntryResourceHandle(entry) {
839
839
  }
840
840
  return v;
841
841
  }
842
+ var BIND_GROUP_CHAIN_LEAF_KEY = {};
842
843
  function getOrCreateFromChain(root, handles, variant, factory, counts) {
843
844
  let node = root;
844
- for (let i = 0; i < handles.length - 1; i++) {
845
- const h = handles[i];
845
+ for (const h of handles) {
846
846
  let next = node.get(h);
847
847
  if (next === void 0) {
848
848
  next = /* @__PURE__ */ new WeakMap();
@@ -850,11 +850,10 @@ function getOrCreateFromChain(root, handles, variant, factory, counts) {
850
850
  }
851
851
  node = next;
852
852
  }
853
- const last = handles[handles.length - 1];
854
- let leaf = node.get(last);
853
+ let leaf = node.get(BIND_GROUP_CHAIN_LEAF_KEY);
855
854
  if (leaf === void 0) {
856
855
  leaf = /* @__PURE__ */ new Map();
857
- node.set(last, leaf);
856
+ node.set(BIND_GROUP_CHAIN_LEAF_KEY, leaf);
858
857
  }
859
858
  const hit = leaf.get(variant);
860
859
  if (hit !== void 0) return hit;
@@ -2260,8 +2259,8 @@ function computeProjectionMatrix(camera) {
2260
2259
  proj,
2261
2260
  camera.orthoLeft,
2262
2261
  camera.orthoRight,
2263
- camera.orthoBottom,
2264
2262
  camera.orthoTop,
2263
+ camera.orthoBottom,
2265
2264
  camera.near,
2266
2265
  camera.far
2267
2266
  );
@@ -2355,9 +2354,9 @@ function writeViewUbo(queue, viewUniformBuffer, camera, light, lights, spotShado
2355
2354
  const VIEW_PAYLOAD_FLOATS = 240;
2356
2355
  const viewPayload = new Float32Array(VIEW_PAYLOAD_FLOATS);
2357
2356
  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;
2357
+ viewPayload[16] = light.direction[0] ?? 0;
2358
+ viewPayload[17] = light.direction[1] ?? -1;
2359
+ viewPayload[18] = light.direction[2] ?? 0;
2361
2360
  viewPayload[20] = light.color[0] ?? 0;
2362
2361
  viewPayload[21] = light.color[1] ?? 0;
2363
2362
  viewPayload[22] = light.color[2] ?? 0;
@@ -3607,10 +3606,15 @@ function iblPipelineError(stage, code, cause) {
3607
3606
  detail: { stage, ...{} }
3608
3607
  };
3609
3608
  }
3610
- function runIblPrecompute(opts) {
3609
+ async function runIblPrecompute(opts) {
3611
3610
  const { device, scope } = opts;
3612
3611
  const cache3 = getOrCreateIblCache(scope);
3613
3612
  const outputFormat = cache3.outputFormat ?? "rgba16float";
3613
+ const generation = scope.generation;
3614
+ const candidate = {};
3615
+ const fail = (stage, cause) => {
3616
+ return Promise.resolve(err(badAlloc(stage, cause)));
3617
+ };
3614
3618
  if (cache3.equirectToCubePipeline === void 0 || cache3.irradiancePipeline === void 0 || cache3.prefilterPipeline === void 0 || cache3.brdfLutPipeline === void 0) {
3615
3619
  return err({
3616
3620
  code: "ibl-precompute-not-dispatched",
@@ -3619,7 +3623,7 @@ function runIblPrecompute(opts) {
3619
3623
  detail: { stage: "pipeline-cache" }
3620
3624
  });
3621
3625
  }
3622
- if (cache3.irradianceTexture === void 0) {
3626
+ {
3623
3627
  const textureResult = device.createTexture({
3624
3628
  label: "ibl-irradiance-cube",
3625
3629
  size: { width: IRRADIANCE_SIZE, height: IRRADIANCE_SIZE, depthOrArrayLayers: 6 },
@@ -3631,15 +3635,15 @@ function runIblPrecompute(opts) {
3631
3635
  viewFormats: [],
3632
3636
  textureBindingViewDimension: void 0
3633
3637
  });
3634
- if (!textureResult.ok) return err(badAlloc("irradiance-texture", textureResult.error));
3635
- cache3.irradianceTexture = textureResult.value;
3638
+ if (!textureResult.ok) return fail("irradiance-texture", textureResult.error);
3639
+ candidate.irradianceTexture = adoptIblTexture(scope, device, textureResult.value);
3636
3640
  const cubeViewResult = device.createTextureView(textureResult.value, {
3637
3641
  label: "ibl-irradiance-cube-view",
3638
3642
  dimension: "cube",
3639
3643
  arrayLayerCount: 6
3640
3644
  });
3641
- if (!cubeViewResult.ok) return err(badAlloc("irradiance-view", cubeViewResult.error));
3642
- cache3.irradianceView = cubeViewResult.value;
3645
+ if (!cubeViewResult.ok) return fail("irradiance-view", cubeViewResult.error);
3646
+ candidate.irradianceView = cubeViewResult.value;
3643
3647
  const faceViews = [];
3644
3648
  for (let f = 0; f < 6; f++) {
3645
3649
  const viewResult = device.createTextureView(textureResult.value, {
@@ -3648,12 +3652,12 @@ function runIblPrecompute(opts) {
3648
3652
  baseArrayLayer: f,
3649
3653
  arrayLayerCount: 1
3650
3654
  });
3651
- if (!viewResult.ok) return err(badAlloc("irradiance-face-view", viewResult.error));
3655
+ if (!viewResult.ok) return fail("irradiance-face-view", viewResult.error);
3652
3656
  faceViews.push(viewResult.value);
3653
3657
  }
3654
- cache3.irradianceFaceViews = faceViews;
3658
+ candidate.irradianceFaceViews = faceViews;
3655
3659
  }
3656
- if (cache3.prefilterTexture === void 0) {
3660
+ {
3657
3661
  const textureResult = device.createTexture({
3658
3662
  label: "ibl-prefilter-cube",
3659
3663
  size: { width: PREFILTER_SIZE, height: PREFILTER_SIZE, depthOrArrayLayers: 6 },
@@ -3665,8 +3669,8 @@ function runIblPrecompute(opts) {
3665
3669
  viewFormats: [],
3666
3670
  textureBindingViewDimension: void 0
3667
3671
  });
3668
- if (!textureResult.ok) return err(badAlloc("prefilter-texture", textureResult.error));
3669
- cache3.prefilterTexture = textureResult.value;
3672
+ if (!textureResult.ok) return fail("prefilter-texture", textureResult.error);
3673
+ candidate.prefilterTexture = adoptIblTexture(scope, device, textureResult.value);
3670
3674
  const cubeViewResult = device.createTextureView(textureResult.value, {
3671
3675
  label: "ibl-prefilter-cube-view",
3672
3676
  dimension: "cube",
@@ -3674,8 +3678,8 @@ function runIblPrecompute(opts) {
3674
3678
  baseMipLevel: 0,
3675
3679
  mipLevelCount: PREFILTER_MIP_LEVELS
3676
3680
  });
3677
- if (!cubeViewResult.ok) return err(badAlloc("prefilter-view", cubeViewResult.error));
3678
- cache3.prefilterView = cubeViewResult.value;
3681
+ if (!cubeViewResult.ok) return fail("prefilter-view", cubeViewResult.error);
3682
+ candidate.prefilterView = cubeViewResult.value;
3679
3683
  const mipViews = [];
3680
3684
  for (let m = 0; m < PREFILTER_MIP_LEVELS; m++) {
3681
3685
  const faces = [];
@@ -3688,14 +3692,14 @@ function runIblPrecompute(opts) {
3688
3692
  baseArrayLayer: f,
3689
3693
  arrayLayerCount: 1
3690
3694
  });
3691
- if (!viewResult.ok) return err(badAlloc("prefilter-face-view", viewResult.error));
3695
+ if (!viewResult.ok) return fail("prefilter-face-view", viewResult.error);
3692
3696
  faces.push(viewResult.value);
3693
3697
  }
3694
3698
  mipViews.push(faces);
3695
3699
  }
3696
- cache3.prefilterFaceViewsByMip = mipViews;
3700
+ candidate.prefilterFaceViewsByMip = mipViews;
3697
3701
  }
3698
- if (cache3.brdfLutTexture === void 0) {
3702
+ {
3699
3703
  const textureResult = device.createTexture({
3700
3704
  label: "ibl-brdf-lut",
3701
3705
  size: { width: BRDF_LUT_SIZE, height: BRDF_LUT_SIZE, depthOrArrayLayers: 1 },
@@ -3707,14 +3711,14 @@ function runIblPrecompute(opts) {
3707
3711
  viewFormats: [],
3708
3712
  textureBindingViewDimension: void 0
3709
3713
  });
3710
- if (!textureResult.ok) return err(badAlloc("brdf-lut-texture", textureResult.error));
3711
- cache3.brdfLutTexture = textureResult.value;
3714
+ if (!textureResult.ok) return fail("brdf-lut-texture", textureResult.error);
3715
+ candidate.brdfLutTexture = adoptIblTexture(scope, device, textureResult.value);
3712
3716
  const viewResult = device.createTextureView(textureResult.value, {
3713
3717
  label: "ibl-brdf-lut-view",
3714
3718
  dimension: "2d"
3715
3719
  });
3716
- if (!viewResult.ok) return err(badAlloc("brdf-lut-view", viewResult.error));
3717
- cache3.brdfLutView = viewResult.value;
3720
+ if (!viewResult.ok) return fail("brdf-lut-view", viewResult.error);
3721
+ candidate.brdfLutView = viewResult.value;
3718
3722
  }
3719
3723
  const samplerResult = device.createSampler({
3720
3724
  label: "ibl-precompute-sampler",
@@ -3725,11 +3729,33 @@ function runIblPrecompute(opts) {
3725
3729
  addressModeV: "clamp-to-edge",
3726
3730
  addressModeW: "clamp-to-edge"
3727
3731
  });
3728
- if (!samplerResult.ok) return err(badAlloc("sampler", samplerResult.error));
3732
+ if (!samplerResult.ok) return fail("sampler", samplerResult.error);
3729
3733
  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;
3734
+ let encoderResult = device.createCommandEncoder({
3735
+ label: "ibl-precompute-encoder-equirect-to-cube"
3736
+ });
3737
+ if (!encoderResult.ok) return fail("encoder-equirect-to-cube", encoderResult.error);
3738
+ let encoder = encoderResult.value;
3739
+ const submitStage = async (stage, createNextEncoder) => {
3740
+ const finishRes = encoder.finish();
3741
+ if (!finishRes.ok) return err(badAlloc(`${stage}-finish`, finishRes.error));
3742
+ const submitRes = device.queue.submit([finishRes.value]);
3743
+ if (!submitRes.ok) return err(badAlloc(`${stage}-submit`, submitRes.error));
3744
+ try {
3745
+ await device.queue.onSubmittedWorkDone();
3746
+ } catch {
3747
+ return err(badAlloc(`${stage}-fence`));
3748
+ }
3749
+ if (!scope.isAlive() || scope.generation !== generation) {
3750
+ return err(badAlloc(`${stage}-device-scope-generation`));
3751
+ }
3752
+ if (createNextEncoder) {
3753
+ encoderResult = device.createCommandEncoder({ label: `ibl-precompute-encoder-${stage}` });
3754
+ if (!encoderResult.ok) return err(badAlloc(`${stage}-next-encoder`, encoderResult.error));
3755
+ encoder = encoderResult.value;
3756
+ }
3757
+ return ok$1(true);
3758
+ };
3733
3759
  const faceUniformsBgl = cache3.faceUniformsBgl;
3734
3760
  const equirectGroup1Bgl = cache3.equirectGroup1Bgl;
3735
3761
  const cubeGroup1Bgl = cache3.cubeGroup1Bgl;
@@ -3738,11 +3764,16 @@ function runIblPrecompute(opts) {
3738
3764
  const irradiancePipeline = cache3.irradiancePipeline;
3739
3765
  const prefilterPipeline = cache3.prefilterPipeline;
3740
3766
  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"));
3767
+ const candidateIrradianceTexture = candidate.irradianceTexture;
3768
+ const candidateIrradianceView = candidate.irradianceView;
3769
+ const irrFaceViews = candidate.irradianceFaceViews;
3770
+ const candidatePrefilterTexture = candidate.prefilterTexture;
3771
+ const candidatePrefilterView = candidate.prefilterView;
3772
+ const prefMipViews = candidate.prefilterFaceViewsByMip;
3773
+ const candidateBrdfLutTexture = candidate.brdfLutTexture;
3774
+ const brdfLutView = candidate.brdfLutView;
3775
+ if (faceUniformsBgl === void 0 || equirectGroup1Bgl === void 0 || cubeGroup1Bgl === void 0 || prefilterGroup0Bgl === void 0 || equirectPipeline === void 0 || irradiancePipeline === void 0 || prefilterPipeline === void 0 || brdfLutPipeline === void 0 || candidateIrradianceTexture === void 0 || candidateIrradianceView === void 0 || irrFaceViews === void 0 || candidatePrefilterTexture === void 0 || candidatePrefilterView === void 0 || prefMipViews === void 0 || candidateBrdfLutTexture === void 0 || brdfLutView === void 0) {
3776
+ return fail("ibl-cache-resources");
3746
3777
  }
3747
3778
  const equirectBgResult = device.createBindGroup({
3748
3779
  label: "ibl-equirect-bg",
@@ -3752,7 +3783,7 @@ function runIblPrecompute(opts) {
3752
3783
  { binding: 1, resource: { kind: "sampler", value: sampler } }
3753
3784
  ]
3754
3785
  });
3755
- if (!equirectBgResult.ok) return err(badAlloc("equirect-bg", equirectBgResult.error));
3786
+ if (!equirectBgResult.ok) return fail("equirect-bg", equirectBgResult.error);
3756
3787
  const equirectBg = equirectBgResult.value;
3757
3788
  const cubeBgResult = device.createBindGroup({
3758
3789
  label: "ibl-cube-bg",
@@ -3762,12 +3793,12 @@ function runIblPrecompute(opts) {
3762
3793
  { binding: 1, resource: { kind: "sampler", value: sampler } }
3763
3794
  ]
3764
3795
  });
3765
- if (!cubeBgResult.ok) return err(badAlloc("cube-bg", cubeBgResult.error));
3796
+ if (!cubeBgResult.ok) return fail("cube-bg", cubeBgResult.error);
3766
3797
  const cubeBg = cubeBgResult.value;
3767
3798
  const cubeFaceViews = opts.cubeFaceViews;
3768
3799
  for (let face = 0; face < 6; face++) {
3769
3800
  const cubeFaceView = cubeFaceViews[face];
3770
- if (cubeFaceView === void 0) return err(badAlloc("cube-face-view"));
3801
+ if (cubeFaceView === void 0) return fail("cube-face-view");
3771
3802
  const pass = encoder.beginRenderPass({
3772
3803
  label: "ibl-equirect-to-cube",
3773
3804
  colorAttachments: [
@@ -3796,7 +3827,7 @@ function runIblPrecompute(opts) {
3796
3827
  }
3797
3828
  ]
3798
3829
  });
3799
- if (!faceBgResult.ok) return err(badAlloc("face-bg", faceBgResult.error));
3830
+ if (!faceBgResult.ok) return fail("face-bg", faceBgResult.error);
3800
3831
  pass.setPipeline(equirectPipeline);
3801
3832
  pass.setBindGroup(0, faceBgResult.value);
3802
3833
  pass.setBindGroup(1, equirectBg);
@@ -3804,9 +3835,11 @@ function runIblPrecompute(opts) {
3804
3835
  pass.draw(6, 1, face * 6, 0);
3805
3836
  pass.end();
3806
3837
  }
3838
+ const cubeStage = await submitStage("equirect-to-cube", true);
3839
+ if (!cubeStage.ok) return err(cubeStage.error);
3807
3840
  for (let face = 0; face < 6; face++) {
3808
3841
  const irrFaceView = irrFaceViews[face];
3809
- if (irrFaceView === void 0) return err(badAlloc("irradiance-face-view"));
3842
+ if (irrFaceView === void 0) return fail("irradiance-face-view");
3810
3843
  const pass = encoder.beginRenderPass({
3811
3844
  label: "ibl-irradiance",
3812
3845
  colorAttachments: [
@@ -3835,21 +3868,23 @@ function runIblPrecompute(opts) {
3835
3868
  }
3836
3869
  ]
3837
3870
  });
3838
- if (!faceBgResult.ok) return err(badAlloc("irr-face-bg", faceBgResult.error));
3871
+ if (!faceBgResult.ok) return fail("irr-face-bg", faceBgResult.error);
3839
3872
  pass.setPipeline(irradiancePipeline);
3840
3873
  pass.setBindGroup(0, faceBgResult.value);
3841
3874
  pass.setBindGroup(1, cubeBg);
3842
3875
  pass.setVertexBuffer(0, opts.cubeVertexBuffer);
3843
3876
  pass.draw(6, 1, face * 6, 0);
3844
3877
  pass.end();
3878
+ const irradianceFaceStage = await submitStage(`irradiance-face-${face}`, true);
3879
+ if (!irradianceFaceStage.ok) return err(irradianceFaceStage.error);
3845
3880
  }
3846
3881
  for (let mip = 0; mip < PREFILTER_MIP_LEVELS; mip++) {
3847
3882
  const mipFaceViews = prefMipViews[mip];
3848
- if (mipFaceViews === void 0) return err(badAlloc("prefilter-mip-views"));
3883
+ if (mipFaceViews === void 0) return fail("prefilter-mip-views");
3849
3884
  for (let face = 0; face < 6; face++) {
3850
3885
  const subIdx = mip * 6 + face;
3851
3886
  const mipFaceView = mipFaceViews[face];
3852
- if (mipFaceView === void 0) return err(badAlloc("prefilter-face-view"));
3887
+ if (mipFaceView === void 0) return fail("prefilter-face-view");
3853
3888
  const pass = encoder.beginRenderPass({
3854
3889
  label: "ibl-prefilter",
3855
3890
  colorAttachments: [
@@ -3889,7 +3924,7 @@ function runIblPrecompute(opts) {
3889
3924
  }
3890
3925
  ]
3891
3926
  });
3892
- if (!bgResult.ok) return err(badAlloc("pref-bg", bgResult.error));
3927
+ if (!bgResult.ok) return fail("pref-bg", bgResult.error);
3893
3928
  pass.setPipeline(prefilterPipeline);
3894
3929
  pass.setBindGroup(0, bgResult.value);
3895
3930
  pass.setBindGroup(1, cubeBg);
@@ -3898,6 +3933,8 @@ function runIblPrecompute(opts) {
3898
3933
  pass.end();
3899
3934
  }
3900
3935
  }
3936
+ const prefilterStage = await submitStage("prefilter", true);
3937
+ if (!prefilterStage.ok) return err(prefilterStage.error);
3901
3938
  {
3902
3939
  const pass = encoder.beginRenderPass({
3903
3940
  label: "ibl-brdf-lut",
@@ -3914,16 +3951,37 @@ function runIblPrecompute(opts) {
3914
3951
  pass.draw(3, 1, 0, 0);
3915
3952
  pass.end();
3916
3953
  }
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));
3954
+ const brdfStage = await submitStage("brdf-lut", false);
3955
+ if (!brdfStage.ok) return err(brdfStage.error);
3956
+ const promoted = {
3957
+ irradianceTexture: candidateIrradianceTexture,
3958
+ irradianceView: candidateIrradianceView,
3959
+ irradianceFaceViews: irrFaceViews,
3960
+ prefilterTexture: candidatePrefilterTexture,
3961
+ prefilterView: candidatePrefilterView,
3962
+ prefilterFaceViewsByMip: prefMipViews,
3963
+ brdfLutTexture: candidateBrdfLutTexture,
3964
+ brdfLutView
3965
+ };
3966
+ cache3.irradianceTexture = promoted.irradianceTexture;
3967
+ cache3.irradianceView = promoted.irradianceView;
3968
+ cache3.irradianceFaceViews = promoted.irradianceFaceViews;
3969
+ cache3.prefilterTexture = promoted.prefilterTexture;
3970
+ cache3.prefilterView = promoted.prefilterView;
3971
+ cache3.prefilterFaceViewsByMip = promoted.prefilterFaceViewsByMip;
3972
+ cache3.brdfLutTexture = promoted.brdfLutTexture;
3973
+ cache3.brdfLutView = promoted.brdfLutView;
3922
3974
  cache3.irradianceBakeCount += 1;
3923
3975
  cache3.prefilterBakeCount += 1;
3924
3976
  cache3.brdfLutBakeCount += 1;
3925
3977
  return ok$1({ submitted: true });
3926
3978
  }
3979
+ function adoptIblTexture(scope, device, texture) {
3980
+ scope._adopt("texture", texture, (value) => {
3981
+ device.destroyTexture(value);
3982
+ });
3983
+ return texture;
3984
+ }
3927
3985
  function badAlloc(stage, cause) {
3928
3986
  return {
3929
3987
  code: "ibl-precompute-not-dispatched",
@@ -4035,7 +4093,7 @@ var GpuTexture = class {
4035
4093
 
4036
4094
  // src/ibl/skylight-bind-group.ts
4037
4095
  var FALLBACK_BYTES_PER_ROW = 256;
4038
- function assembleMaterialWithSkylightEntries(materialEntries, skylight, transmission) {
4096
+ function assembleMaterialWithSkylightEntries(materialEntries, skylight, transmission, textureInjections = []) {
4039
4097
  const iblStart = materialEntries.length;
4040
4098
  const result = [
4041
4099
  ...materialEntries,
@@ -4081,6 +4139,22 @@ function assembleMaterialWithSkylightEntries(materialEntries, skylight, transmis
4081
4139
  resource: { kind: "textureView", value: transmissionView }
4082
4140
  }
4083
4141
  );
4142
+ const clearcoatStart = result.length;
4143
+ for (let index = 0; index < textureInjections.length; index += 1) {
4144
+ const resource = textureInjections[index];
4145
+ if (resource === void 0) continue;
4146
+ const binding = clearcoatStart + resource.slot * 2;
4147
+ result.push(
4148
+ {
4149
+ binding,
4150
+ resource: { kind: "sampler", value: resource.sampler }
4151
+ },
4152
+ {
4153
+ binding: binding + 1,
4154
+ resource: { kind: "textureView", value: resource.view }
4155
+ }
4156
+ );
4157
+ }
4084
4158
  return result;
4085
4159
  }
4086
4160
  function createSkylightFallback(device, queue) {
@@ -4344,8 +4418,8 @@ function deriveRenderDataCubemap(source) {
4344
4418
  cubeUsage: GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST | GPU_TEXTURE_USAGE_COPY_SRC
4345
4419
  });
4346
4420
  }
4347
- var STANDARD_PBR_UBO_SIZE = 384;
4348
- var MATERIAL_PER_ENTITY_STRIDE = 512;
4421
+ var STANDARD_PBR_UBO_SIZE = derive(DEFAULT_STANDARD_PBR_PARAM_SCHEMA).uboLayout.totalBytes;
4422
+ var MATERIAL_PER_ENTITY_STRIDE = Math.ceil(STANDARD_PBR_UBO_SIZE / 256) * 256;
4349
4423
  function resolveSurfaceProfile(storageFormat, viewFormat, facts) {
4350
4424
  if (facts.surfaceViewFormats) {
4351
4425
  return ok$1({
@@ -4504,17 +4578,64 @@ function isImageError(error) {
4504
4578
  const code = error.code;
4505
4579
  return typeof code === "string" && code.startsWith("image-");
4506
4580
  }
4581
+ function materialDiagnosticsEnabled() {
4582
+ if (typeof globalThis !== "object" || globalThis === null || !("process" in globalThis)) {
4583
+ return false;
4584
+ }
4585
+ const processValue = globalThis.process;
4586
+ return processValue?.env?.FORGEAX_MATERIAL_DIAGNOSTICS === "1";
4587
+ }
4507
4588
  function residentTextureView(world, store, runtime, handle, worldId = world) {
4508
4589
  const podRes = resolveAssetHandle(world, handle);
4509
- if (!podRes.ok) return void 0;
4590
+ const diagnosticsEnabled = materialDiagnosticsEnabled();
4591
+ if (!podRes.ok) {
4592
+ if (diagnosticsEnabled) {
4593
+ console.error(
4594
+ `[render-material] texture resolve failed: ${JSON.stringify({ handle: handleSlot(handle), error: podRes.error })}`
4595
+ );
4596
+ }
4597
+ return void 0;
4598
+ }
4510
4599
  const residentRes = store.ensureResident(handle, podRes.value, worldId);
4511
4600
  if (!residentRes.ok) {
4512
4601
  if (residentRes.error instanceof RhiError || isImageError(residentRes.error)) {
4513
4602
  runtime.errorRegistry.fire(residentRes.error);
4514
4603
  }
4604
+ if (diagnosticsEnabled) {
4605
+ console.error(
4606
+ `[render-material] texture residency failed: ${JSON.stringify({
4607
+ handle: handleSlot(handle),
4608
+ pod: {
4609
+ kind: podRes.value.kind,
4610
+ shape: podRes.value.shape,
4611
+ format: podRes.value.format,
4612
+ dataByteLength: podRes.value.data.byteLength,
4613
+ mips: podRes.value.mips
4614
+ },
4615
+ error: residentRes.error
4616
+ })}`
4617
+ );
4618
+ }
4515
4619
  return void 0;
4516
4620
  }
4517
- return store.getTextureGpuView(handle, worldId);
4621
+ const view = store.getTextureGpuView(handle, worldId);
4622
+ if (diagnosticsEnabled) {
4623
+ console.error(
4624
+ `[render-material] texture residency ready: ${JSON.stringify({
4625
+ handle: handleSlot(handle),
4626
+ pod: {
4627
+ kind: podRes.value.kind,
4628
+ shape: podRes.value.shape,
4629
+ format: podRes.value.format,
4630
+ dataByteLength: podRes.value.data.byteLength,
4631
+ mips: podRes.value.mips
4632
+ },
4633
+ receipt: "receipt" in residentRes.value ? residentRes.value.receipt : void 0,
4634
+ viewReady: view !== void 0
4635
+ })}`
4636
+ );
4637
+ }
4638
+ return view;
4518
4639
  }
4519
4640
  function residentSampler(world, store, runtime, handle, worldId = world) {
4520
4641
  const podRes = resolveAssetHandle(world, handle);
@@ -4581,24 +4702,12 @@ var BUILTIN_USER_REGION_TEXTURE_FIELDS = [
4581
4702
  "baseColorTexture",
4582
4703
  "metallicRoughnessTexture",
4583
4704
  "normalTexture",
4584
- "specularTintTexture",
4585
4705
  "emissiveTexture",
4586
4706
  "occlusionTexture",
4587
4707
  "transmissionTexture",
4588
4708
  "thicknessTexture"
4589
4709
  ];
4590
4710
  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
4711
  var LEGACY_MATERIAL_TEXTURE_SCALE_FIELDS = [
4603
4712
  "baseColorTexture",
4604
4713
  "metallicRoughnessTexture",
@@ -4606,75 +4715,7 @@ var LEGACY_MATERIAL_TEXTURE_SCALE_FIELDS = [
4606
4715
  "emissiveTexture",
4607
4716
  "occlusionTexture"
4608
4717
  ];
4609
- var STANDARD_PBR_TEXTURE_COORDINATE_OFFSET = 128;
4610
- var STANDARD_PBR_TEXTURE_COORDINATE_STRIDE = 8;
4611
4718
  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
4719
  function materialTextureUvScale(texture) {
4679
4720
  if (texture === void 0) return [1, 1];
4680
4721
  return deriveTextureExtent(
@@ -4722,17 +4763,13 @@ function applyMaterialTextureUvScales(payload, material, world) {
4722
4763
  }
4723
4764
  return;
4724
4765
  }
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;
4766
+ const textureScaleOffset = LEGACY_MATERIAL_TEXTURE_SCALE_OFFSET;
4727
4767
  const hasTextureMetadata = material.textureCoordinates !== void 0 || material.textureHandles !== void 0 || material.videoTextureFields !== void 0 || material.baseColorTexture !== void 0 || material.metallicRoughnessTexture !== void 0 || material.normalTexture !== void 0 || material.emissiveTexture !== void 0 || material.occlusionTexture !== void 0;
4728
4768
  if (!hasTextureMetadata) {
4729
- f32.set(
4730
- isStandardPbr ? DEFAULT_STANDARD_TEXTURE_COORDINATES : DEFAULT_LEGACY_TEXTURE_SCALES,
4731
- textureScaleOffset / 4
4732
- );
4769
+ f32.set(DEFAULT_LEGACY_TEXTURE_SCALES, textureScaleOffset / 4);
4733
4770
  return;
4734
4771
  }
4735
- const fields = isStandardPbr ? MATERIAL_TEXTURE_SCALE_FIELDS : LEGACY_MATERIAL_TEXTURE_SCALE_FIELDS;
4772
+ const fields = LEGACY_MATERIAL_TEXTURE_SCALE_FIELDS;
4736
4773
  for (let index = 0; index < fields.length; index++) {
4737
4774
  const field = fields[index];
4738
4775
  if (field === void 0) continue;
@@ -4740,32 +4777,16 @@ function applyMaterialTextureUvScales(payload, material, world) {
4740
4777
  const resolved = handle === void 0 ? void 0 : resolveAssetHandle(world, handle);
4741
4778
  const texture = resolved?.ok === true ? resolved.value : void 0;
4742
4779
  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
- }
4780
+ const offset = textureScaleOffset / 4 + index * 2;
4781
+ f32[offset] = u;
4782
+ f32[offset + 1] = v;
4760
4783
  }
4761
4784
  }
4762
4785
  function materialCoordinateSchema(material) {
4763
- if (isStandardPbrMaterialShader(material.materialShaderId) && material.paramSnapshot !== void 0) {
4764
- return DEFAULT_STANDARD_PBR_PARAM_SCHEMA;
4765
- }
4766
4786
  if (material.materialParamSchema !== void 0 && material.materialParamSchema.length > 0) {
4767
4787
  return material.materialParamSchema;
4768
4788
  }
4789
+ if (isStandardPbrMaterialShader(material.materialShaderId)) return STANDARD_PIPELINE_PARAM_SCHEMA;
4769
4790
  switch (material.materialShaderId) {
4770
4791
  case "forgeax::default-unlit":
4771
4792
  return DEFAULT_UNLIT_PARAM_SCHEMA;
@@ -4790,6 +4811,9 @@ function defaultViewForUserRegionField(field, pipelineState, schema) {
4790
4811
  }
4791
4812
  if (field === "normalTexture") return pipelineState.defaultNormalTextureView;
4792
4813
  if (field === "baseColorTexture") return pipelineState.fallbackTextureView;
4814
+ if (field === "anisotropyTexture") {
4815
+ return pipelineState.defaultAnisotropyTextureView ?? pipelineState.defaultNormalTextureView;
4816
+ }
4793
4817
  return pipelineState.defaultWhiteTextureView;
4794
4818
  }
4795
4819
  function detectNineSliceScaleTooSmall(transformWorld, slices, renderableIndex, seenIndices, metrics) {
@@ -4814,58 +4838,54 @@ function writePbrMaterialUboPayload(buf, material) {
4814
4838
  }
4815
4839
  buf.fill(0);
4816
4840
  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;
4841
+ const schema = material.materialParamSchema === void 0 || material.materialParamSchema.length === 0 ? STANDARD_PIPELINE_PARAM_SCHEMA : material.materialParamSchema;
4842
+ const snapshot = material.paramSnapshot;
4843
+ const fallback = (name, entry) => {
4844
+ switch (name) {
4845
+ case "baseColor":
4846
+ return [
4847
+ material.baseColor[0] ?? 1,
4848
+ material.baseColor[1] ?? 1,
4849
+ material.baseColor[2] ?? 1,
4850
+ 1
4851
+ ];
4852
+ case "metallic":
4853
+ return material.metallic;
4854
+ case "roughness":
4855
+ return material.roughness;
4856
+ case "specularColor":
4857
+ return material.specularColor ?? [1, 1, 1];
4858
+ case "normalScale":
4859
+ return material.normalScale ?? 1;
4860
+ case "emissive":
4861
+ return material.emissive ?? [0, 0, 0];
4862
+ case "emissiveIntensity":
4863
+ return material.emissiveIntensity ?? 0;
4864
+ case "occlusionStrength":
4865
+ return material.occlusionStrength ?? 1;
4866
+ default:
4867
+ return entry.default;
4868
+ }
4848
4869
  };
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;
4870
+ for (const entry of derivedParamSchema(schema).uboLayout.entries) {
4871
+ const value = snapshot?.[entry.name] ?? fallback(entry.name, schema.find((item) => item.name === entry.name) ?? entry);
4872
+ const offset = entry.offset / 4;
4873
+ const width = entry.size / 4;
4874
+ if (typeof value === "number") {
4875
+ f32[offset] = value;
4876
+ } else if (Array.isArray(value)) {
4877
+ for (let index = 0; index < Math.min(width, value.length); index += 1) {
4878
+ const component = value[index];
4879
+ if (typeof component === "number") f32[offset + index] = component;
4880
+ }
4881
+ }
4861
4882
  }
4862
4883
  }
4863
4884
  function applyParamSnapshotToUbo(payload, paramSchema, paramSnapshot) {
4864
4885
  if (paramSchema === void 0) return;
4865
4886
  if (paramSnapshot === void 0) return;
4866
4887
  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);
4888
+ const { uboLayout } = derivedParamSchema(paramSchema);
4869
4889
  for (const entry of uboLayout.entries) {
4870
4890
  const value = paramSnapshot[entry.name];
4871
4891
  if (value === void 0) continue;
@@ -4885,6 +4905,36 @@ function applyParamSnapshotToUbo(payload, paramSchema, paramSnapshot) {
4885
4905
  }
4886
4906
  }
4887
4907
  }
4908
+ function recordIblMaterialBinding(frameState, materialBgl, bindGroup, entries, iblStart, cache3) {
4909
+ const receipt = frameState.iblBindingInspection;
4910
+ if (receipt === void 0) return;
4911
+ const skylightBindingStart = entries.findIndex((entry) => entry.binding === iblStart);
4912
+ if (skylightBindingStart < 0) return;
4913
+ const iblEntries = entries.slice(skylightBindingStart, skylightBindingStart + 7);
4914
+ const projected = [];
4915
+ for (const entry of iblEntries) {
4916
+ if (entry.resource.kind === "externalTexture") continue;
4917
+ const value = entry.resource.kind === "buffer" ? entry.resource.value.buffer : entry.resource.value;
4918
+ if (typeof value !== "object" || value === null) continue;
4919
+ projected.push({
4920
+ binding: entry.binding,
4921
+ kind: entry.resource.kind,
4922
+ resourceIdentity: getOpaqueResourceIdentity(value)
4923
+ });
4924
+ }
4925
+ frameState.iblBindingInspection = {
4926
+ ...receipt,
4927
+ material: {
4928
+ bindGroupIdentity: getOpaqueResourceIdentity(bindGroup),
4929
+ materialBglIdentity: getOpaqueResourceIdentity(materialBgl),
4930
+ cache: cache3,
4931
+ skylightBindingStart: iblEntries[0]?.binding ?? 0,
4932
+ entries: projected,
4933
+ reflectionBindings: projected.map((entry) => entry.binding)
4934
+ },
4935
+ errors: []
4936
+ };
4937
+ }
4888
4938
  function isMaterialBgAssemblyCacheHit(cached, material, materialBgl, materialBuffer, skylightResources, materialResourceEpoch, transmissionBackdropView) {
4889
4939
  if (transmissionBackdropView != null) return false;
4890
4940
  return cached?.material === material && cached.materialResourceEpoch === materialResourceEpoch && cached.materialBgl === materialBgl && cached.materialBuffer === materialBuffer && cached.skylightResources.irradianceView === skylightResources.irradianceView && cached.skylightResources.irradianceSampler === skylightResources.irradianceSampler && cached.skylightResources.prefilterView === skylightResources.prefilterView && cached.skylightResources.prefilterSampler === skylightResources.prefilterSampler && cached.skylightResources.brdfLutView === skylightResources.brdfLutView && cached.skylightResources.brdfLutSampler === skylightResources.brdfLutSampler && cached.skylightResources.intensityBuffer === skylightResources.intensityBuffer;
@@ -4902,16 +4952,22 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
4902
4952
  materialBgShared,
4903
4953
  materialBgAssemblyCache,
4904
4954
  transmissionBackdropView,
4905
- bindGroupCounts
4955
+ bindGroupCounts,
4956
+ frameState
4906
4957
  } = deps;
4907
4958
  const effectiveSkylightResources = resolveReflectionProbeResources?.(materialWorld, entityKey) ?? skylightResources;
4908
4959
  const smMaterialHandle = submeshMaterial.materialHandle;
4909
4960
  const materialCacheKey = smMaterialHandle === void 0 ? void 0 : `${materialWorld.identity}:${smMaterialHandle}`;
4910
4961
  const smHasVideoFields = (submeshMaterial.videoTextureFields?.size ?? 0) > 0;
4911
4962
  const smShaderId = submeshMaterial.materialShaderId;
4912
- const smPerShaderBgl = smShaderId !== void 0 ? runtime.getMaterialBindGroupLayout?.(smShaderId) : void 0;
4963
+ const smSchema = (smShaderId !== void 0 ? runtime.getParamSchema?.(smShaderId) : void 0) ?? submeshMaterial.materialParamSchema;
4964
+ const smPerShaderBgl = smShaderId !== void 0 ? runtime.getMaterialBindGroupLayout?.(smShaderId, smSchema) : void 0;
4913
4965
  const smMaterialBgl = smPerShaderBgl ?? pipelineState.materialBindGroupLayout;
4966
+ const standardMaterial = isStandardPbrMaterialShader(smShaderId);
4967
+ const physicalFields = standardMaterial && smSchema !== void 0 ? physicalTextureFields(smSchema) : [];
4968
+ const smUserRegionFields = smPerShaderBgl === void 0 || isCanonicalStandardPbrMaterialShader(smShaderId) ? BUILTIN_USER_REGION_TEXTURE_FIELDS : userRegionTextureFieldOrder(smSchema).filter((field) => !physicalFields.includes(field));
4914
4969
  const hasActiveTransmissionBackdrop = transmissionBackdropView != null;
4970
+ const diagnosticsEnabled = materialDiagnosticsEnabled();
4915
4971
  if (smMaterialHandle !== void 0 && !smHasVideoFields && !hasActiveTransmissionBackdrop) {
4916
4972
  const cached = materialCacheKey === void 0 ? void 0 : materialBgAssemblyCache.get(materialCacheKey);
4917
4973
  if (isMaterialBgAssemblyCacheHit(
@@ -4923,11 +4979,48 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
4923
4979
  store.materialResourceEpoch,
4924
4980
  transmissionBackdropView
4925
4981
  )) {
4982
+ if (diagnosticsEnabled) {
4983
+ recordIblMaterialBinding(
4984
+ frameState,
4985
+ smMaterialBgl,
4986
+ cached.bindGroup,
4987
+ [
4988
+ {
4989
+ binding: 1 + smUserRegionFields.length * 2,
4990
+ resource: { kind: "textureView", value: skylightResources.irradianceView }
4991
+ },
4992
+ {
4993
+ binding: 2 + smUserRegionFields.length * 2,
4994
+ resource: { kind: "sampler", value: skylightResources.irradianceSampler }
4995
+ },
4996
+ {
4997
+ binding: 3 + smUserRegionFields.length * 2,
4998
+ resource: { kind: "textureView", value: skylightResources.prefilterView }
4999
+ },
5000
+ {
5001
+ binding: 4 + smUserRegionFields.length * 2,
5002
+ resource: { kind: "sampler", value: skylightResources.prefilterSampler }
5003
+ },
5004
+ {
5005
+ binding: 5 + smUserRegionFields.length * 2,
5006
+ resource: { kind: "textureView", value: skylightResources.brdfLutView }
5007
+ },
5008
+ {
5009
+ binding: 6 + smUserRegionFields.length * 2,
5010
+ resource: { kind: "sampler", value: skylightResources.brdfLutSampler }
5011
+ },
5012
+ {
5013
+ binding: 7 + smUserRegionFields.length * 2,
5014
+ resource: { kind: "buffer", value: { buffer: skylightResources.intensityBuffer } }
5015
+ }
5016
+ ],
5017
+ 1 + smUserRegionFields.length * 2,
5018
+ "hit"
5019
+ );
5020
+ }
4926
5021
  return cached.bindGroup;
4927
5022
  }
4928
5023
  }
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
5024
  let materialResourcesResident = true;
4932
5025
  const smSamplerForField = (field) => {
4933
5026
  const handle = field === void 0 ? void 0 : submeshMaterial.samplerHandles?.get(field);
@@ -4993,6 +5086,33 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
4993
5086
  }
4994
5087
  );
4995
5088
  }
5089
+ const physicalTextureInjections = [];
5090
+ for (let slot = 0; slot < physicalFields.length; slot += 1) {
5091
+ const field = physicalFields[slot];
5092
+ if (field === void 0) continue;
5093
+ let view = defaultViewForUserRegionField(field, pipelineState, smSchema);
5094
+ const targetSource = materialRenderTargetSourceForField(
5095
+ submeshMaterial,
5096
+ field,
5097
+ resolveRenderTargetTextureSource
5098
+ );
5099
+ if (targetSource !== void 0) {
5100
+ if (targetSource.textureView !== void 0) view = targetSource.textureView;
5101
+ else materialResourcesResident = false;
5102
+ } else {
5103
+ const handle = submeshMaterial.textureHandles?.get(field);
5104
+ if (handle !== void 0) {
5105
+ const resident = residentTextureView(materialWorld, store, runtime, handle);
5106
+ if (resident !== void 0) view = resident;
5107
+ else materialResourcesResident = false;
5108
+ }
5109
+ }
5110
+ physicalTextureInjections.push({
5111
+ slot,
5112
+ sampler: smSamplerForField(field),
5113
+ view
5114
+ });
5115
+ }
4996
5116
  const transmissionSampler = transmissionBackdropView != null ? effectiveSkylightResources.prefilterSampler : pipelineState.defaultSampler;
4997
5117
  const smMergedEntries = assembleMaterialWithSkylightEntries(
4998
5118
  smBaseEntries,
@@ -5000,7 +5120,8 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5000
5120
  {
5001
5121
  sampler: transmissionSampler,
5002
5122
  ...transmissionBackdropView === void 0 ? {} : { backdropView: transmissionBackdropView }
5003
- }
5123
+ },
5124
+ physicalTextureInjections
5004
5125
  );
5005
5126
  const createBindGroup = () => {
5006
5127
  const result = runtime.device.createBindGroup({
@@ -5011,14 +5132,51 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5011
5132
  if (!result.ok) throw result.error;
5012
5133
  return result.value;
5013
5134
  };
5135
+ const materialCacheHandles = smMergedEntries.map((entry) => extractEntryResourceHandle(entry));
5136
+ if (diagnosticsEnabled && submeshMaterial.textureHandles === void 0 && smMaterialHandle !== void 0) {
5137
+ console.error(
5138
+ `[render-material] scalar material cache handles: ${JSON.stringify({
5139
+ entityKey,
5140
+ materialHandle: smMaterialHandle,
5141
+ shader: smShaderId,
5142
+ paramSnapshot: submeshMaterial.paramSnapshot,
5143
+ handleTypes: materialCacheHandles.map((handle) => typeof handle)
5144
+ })}`
5145
+ );
5146
+ }
5147
+ if (diagnosticsEnabled && materialCacheHandles.some((handle) => typeof handle !== "object" || handle === null)) {
5148
+ console.error(
5149
+ `[render-material] invalid material cache handle: ${JSON.stringify({
5150
+ entityKey,
5151
+ materialHandle: smMaterialHandle,
5152
+ shader: smShaderId,
5153
+ entries: smMergedEntries.map((entry, index) => ({
5154
+ index,
5155
+ binding: entry.binding,
5156
+ kind: entry.resource.kind,
5157
+ handleType: typeof materialCacheHandles[index]
5158
+ }))
5159
+ })}`
5160
+ );
5161
+ }
5014
5162
  const smBindGroup = hasActiveTransmissionBackdrop ? createBindGroup() : getOrCreatePerEntity(
5015
5163
  materialBgShared,
5016
5164
  smShaderId ?? "",
5017
- smMergedEntries.map((e) => extractEntryResourceHandle(e)),
5165
+ materialCacheHandles,
5018
5166
  "material-shared",
5019
5167
  createBindGroup,
5020
5168
  bindGroupCounts
5021
5169
  );
5170
+ if (diagnosticsEnabled) {
5171
+ recordIblMaterialBinding(
5172
+ frameState,
5173
+ smMaterialBgl,
5174
+ smBindGroup,
5175
+ smMergedEntries,
5176
+ smBaseEntries.length,
5177
+ "miss"
5178
+ );
5179
+ }
5022
5180
  if (smMaterialHandle !== void 0 && !smHasVideoFields && materialResourcesResident && !hasActiveTransmissionBackdrop) {
5023
5181
  if (materialCacheKey !== void 0) {
5024
5182
  materialBgAssemblyCache.set(materialCacheKey, {
@@ -5035,6 +5193,85 @@ function buildPerSubmeshMaterialBg(deps, submeshMaterial, entityKey, materialWor
5035
5193
  }
5036
5194
 
5037
5195
  // src/record/main-pass-geometry.ts
5196
+ function materialDiagnosticsEnabled2() {
5197
+ if (typeof globalThis !== "object" || globalThis === null || !("process" in globalThis)) {
5198
+ return false;
5199
+ }
5200
+ const processValue = globalThis.process;
5201
+ return processValue?.env?.FORGEAX_MATERIAL_DIAGNOSTICS === "1";
5202
+ }
5203
+ function requestsStandardTransmissionVariant(material) {
5204
+ return material.materialShaderId === "forgeax::default-standard-pbr" && material.paramSnapshot?.transmission !== void 0;
5205
+ }
5206
+ function recordIblPipelineBinding(c, material, pipeline, materialGroup) {
5207
+ const receipt = c.frameState.iblBindingInspection;
5208
+ if (receipt === void 0 || !isStandardPbrMaterialShader(material.materialShaderId)) return;
5209
+ const materialBgl = c.runtime.getMaterialBindGroupLayout?.(
5210
+ material.materialShaderId ?? "forgeax::default-standard-pbr"
5211
+ ) ?? c.pipelineState.materialBindGroupLayout;
5212
+ c.frameState.iblBindingInspection = {
5213
+ ...receipt,
5214
+ pipeline: {
5215
+ pipelineIdentity: getOpaqueResourceIdentity(pipeline),
5216
+ effectiveMaterialLayoutIdentity: effectiveMaterialLayoutIdentity(
5217
+ material.materialShaderId ?? "forgeax::default-standard-pbr",
5218
+ material.materialParamSchema
5219
+ ),
5220
+ materialBglIdentity: getOpaqueResourceIdentity(materialBgl),
5221
+ bindGroupIdentity: getOpaqueResourceIdentity(materialGroup),
5222
+ drawFrameId: c.frameState.frameNumber
5223
+ }
5224
+ };
5225
+ const materialReceipt = c.frameState.iblBindingInspection.material;
5226
+ const errors = [];
5227
+ if (materialReceipt !== void 0 && materialReceipt.materialBglIdentity !== getOpaqueResourceIdentity(materialBgl)) {
5228
+ errors.push("material-pipeline-bgl-identity-mismatch");
5229
+ }
5230
+ if (materialReceipt !== void 0 && materialReceipt.bindGroupIdentity !== getOpaqueResourceIdentity(materialGroup)) {
5231
+ errors.push("material-bind-group-identity-mismatch");
5232
+ }
5233
+ if (materialReceipt !== void 0) {
5234
+ const expectedResourceIdentities = [
5235
+ receipt.resources.irradiance.viewIdentity,
5236
+ receipt.resources.irradiance.samplerIdentity,
5237
+ receipt.resources.prefilter.viewIdentity,
5238
+ receipt.resources.prefilter.samplerIdentity,
5239
+ receipt.resources.brdfLut.viewIdentity,
5240
+ receipt.resources.brdfLut.samplerIdentity,
5241
+ receipt.resources.intensityBufferIdentity
5242
+ ];
5243
+ const actualResourceIdentities = materialReceipt.entries.filter((entry) => entry.binding >= materialReceipt.skylightBindingStart).slice(0, expectedResourceIdentities.length).map((entry) => entry.resourceIdentity);
5244
+ if (actualResourceIdentities.length !== expectedResourceIdentities.length || actualResourceIdentities.some(
5245
+ (identity, index) => identity !== expectedResourceIdentities[index]
5246
+ )) {
5247
+ errors.push("material-ibl-resource-identity-mismatch");
5248
+ }
5249
+ }
5250
+ c.frameState.iblBindingInspection = {
5251
+ ...c.frameState.iblBindingInspection,
5252
+ status: errors.length === 0 ? "binding-chain-consistent" : "binding-chain-mismatch",
5253
+ errors
5254
+ };
5255
+ }
5256
+ var SHARED_BOOT_MATERIAL_SHADER_IDS = /* @__PURE__ */ new Set([
5257
+ "forgeax::default-unlit",
5258
+ "forgeax::default-shadow-caster",
5259
+ "forgeax::sprite",
5260
+ "forgeax::sprite-lit",
5261
+ "forgeax::default-sprite",
5262
+ "forgeax::msdf-text",
5263
+ "forgeax::default-standard-pbr",
5264
+ "forgeax::pbr-skin",
5265
+ "forgeax::default-standard-pbr-skin"
5266
+ ]);
5267
+ function effectiveMaterialLayoutIdentity(materialShaderId, materialParamSchema) {
5268
+ if (materialParamSchema === void 0) return void 0;
5269
+ const hasStandardPhysicalMaps = isStandardPbrMaterialShader(materialShaderId) && physicalTextureFields(materialParamSchema).length > 0;
5270
+ if (SHARED_BOOT_MATERIAL_SHADER_IDS.has(materialShaderId) && !hasStandardPhysicalMaps) {
5271
+ return void 0;
5272
+ }
5273
+ return materialBindGroupLayoutIdentity(materialShaderId, materialParamSchema);
5274
+ }
5038
5275
  function geometryRenderStateForPass(base, passKind) {
5039
5276
  if (passKind !== "temporal") return base;
5040
5277
  return {
@@ -5095,6 +5332,7 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5095
5332
  viewBindGroup,
5096
5333
  viewBindGroupDynamicOffset = 0
5097
5334
  } = c;
5335
+ const diagnosticsEnabled = materialDiagnosticsEnabled2();
5098
5336
  const colorFormatOverride = c.transparentColorFormat;
5099
5337
  const clusteredLighting = c.standardLighting?.kind === "clustered";
5100
5338
  const isHdrTarget = tonemapActive || colorFormatOverride === "rgba16float";
@@ -5289,7 +5527,10 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5289
5527
  const probeBlendRecordAvailable = runtime.device.caps.storageBuffer && entry.source.probeBlendRecord !== void 0;
5290
5528
  const skinAllocator = pipelineState.skinPaletteAllocator;
5291
5529
  const skinSlice = entry.source.skin;
5292
- const clusteredSkin = clusteredLighting;
5530
+ const clusteredSkin = clusteredLighting && entry.source.materials.every((material) => {
5531
+ const shaderId = material.materialShaderId;
5532
+ return !isStandardPbrSkinMaterialShader(shaderId) || isCanonicalStandardPbrMaterialShader(shaderId);
5533
+ });
5293
5534
  const skinMeshBindGroupLayout = clusteredSkin ? pipelineState.hdrpSkinMeshBindGroupLayout : pipelineState.pbrSkinMeshBindGroupLayout;
5294
5535
  const hdrpSkinBuffers = isSkinEntry && clusteredSkin ? getOrCreateHdrpBuffers(runtime, frameState.installedPipelineConfig?.clusterGrid) : null;
5295
5536
  const skinResources = isSkinEntry && skinMeshBindGroupLayout !== null && skinAllocator !== null && skinSlice !== void 0 && (!clusteredSkin || hdrpSkinBuffers !== null) ? {
@@ -5390,6 +5631,19 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5390
5631
  entry.source.skin?.byteOffset ?? 0
5391
5632
  );
5392
5633
  } else if (isSkinEntry) {
5634
+ if (diagnosticsEnabled) {
5635
+ console.error(
5636
+ `[render-material] skin draw skipped: ${JSON.stringify({
5637
+ entityKey: entry.source.entityKey,
5638
+ materialHandle: entry.source.material.materialHandle,
5639
+ reason: "skin-pipeline-unavailable",
5640
+ skinResourcesReady: skinResources !== null,
5641
+ skinPsoReady: skinPsoProbe !== null,
5642
+ paletteBufferReady: skinSlice?.buffer !== void 0,
5643
+ paletteByteOffset: skinSlice?.byteOffset
5644
+ })}`
5645
+ );
5646
+ }
5393
5647
  continue;
5394
5648
  }
5395
5649
  let perSubmeshBg = null;
@@ -5408,6 +5662,18 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5408
5662
  continue;
5409
5663
  }
5410
5664
  const materialSlot = materialSlotIndices[i]?.[matSlotIdx] ?? materialSlotIndices[i]?.[0] ?? 0;
5665
+ const processValue = globalThis.process;
5666
+ if (processValue?.env?.FORGEAX_MATERIAL_PIPELINE_DIAGNOSTICS === "1" && (submeshMaterial.materialShaderId === "forgeax::default-standard-pbr" || submeshMaterial.materialShaderId === "forgeax::pbr-skin")) {
5667
+ console.error(
5668
+ `[render-material] draw receipt: ${JSON.stringify({
5669
+ entityIndex: entry.renderableIndex,
5670
+ materialSlot,
5671
+ materialHandle: submeshMaterial.materialHandle,
5672
+ clearcoat: submeshMaterial.paramSnapshot?.clearcoat,
5673
+ shader: submeshMaterial.materialShaderId
5674
+ })}`
5675
+ );
5676
+ }
5411
5677
  perSubmeshBg = profilePhase === void 0 ? resolveMaterialBindGroup(
5412
5678
  materialSlot,
5413
5679
  submeshMaterial,
@@ -5427,7 +5693,7 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5427
5693
  materialGroup1DynamicOffsets[0] = materialSlot * MATERIAL_PER_ENTITY_STRIDE;
5428
5694
  pass.setBindGroup(1, perSubmeshBg, materialGroup1DynamicOffsets, 0, 1);
5429
5695
  const smTopology = sm.topology;
5430
- const smMaterialShaderId = entry.source.skin !== void 0 ? SKIN_MATERIAL_SHADER_ID : submeshMaterial.materialShaderId;
5696
+ const smMaterialShaderId = entry.source.skin !== void 0 ? isStandardPbrSkinMaterialShader(submeshMaterial.materialShaderId) ? submeshMaterial.materialShaderId : SKIN_MATERIAL_SHADER_ID : submeshMaterial.materialShaderId;
5431
5697
  const probeBlendAvailable = probeBlendRecordAvailable && isStandardPbrMaterialShader(smMaterialShaderId);
5432
5698
  const isSpriteShader = smMaterialShaderId === "forgeax::sprite" || smMaterialShaderId === "forgeax::sprite-lit";
5433
5699
  const basePipelineRenderState = isSpriteShader ? {
@@ -5477,7 +5743,9 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5477
5743
  hasVertexColor,
5478
5744
  probeBlendAvailable
5479
5745
  );
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}`;
5746
+ const transmissionAvailable = requestsStandardTransmissionVariant(submeshMaterial);
5747
+ const materialCapabilityVariantSet = transmissionAvailable && !capabilityVariantSet.includes("TRANSMISSION_AVAILABLE=") ? `${capabilityVariantSet === "" ? "" : `${capabilityVariantSet}+`}TRANSMISSION_AVAILABLE=true` : capabilityVariantSet;
5748
+ const variantSet = isSpriteShader ? smMaterialShaderId === "forgeax::sprite" && entry.source.spriteInstances !== void 0 ? "" : void 0 : entry.variantSet === void 0 ? materialCapabilityVariantSet : capabilityVariantSet === "" ? entry.variantSet : `${capabilityVariantSet}+${entry.variantSet}`;
5481
5749
  const cachedPipeline = resolveMaterialPipeline(
5482
5750
  smMaterialShaderId,
5483
5751
  pipelineRenderState,
@@ -5493,6 +5761,16 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5493
5761
  smPipelineHandle = selectGeometryPipeline(pipelineState, isHdrTarget, msaaActive);
5494
5762
  }
5495
5763
  if (smPipelineHandle === null) {
5764
+ if (diagnosticsEnabled) {
5765
+ console.error(
5766
+ `[render-material] draw skipped: ${JSON.stringify({
5767
+ entityKey: entry.source.entityKey,
5768
+ materialHandle: submeshMaterial.materialHandle,
5769
+ shader: smMaterialShaderId,
5770
+ reason: "pipeline-unavailable"
5771
+ })}`
5772
+ );
5773
+ }
5496
5774
  continue;
5497
5775
  }
5498
5776
  const instanceDraws2 = resolveGeometryInstanceBuffer(
@@ -5513,6 +5791,29 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5513
5791
  if (selectedGroup === null) continue;
5514
5792
  group2BindGroup = selectedGroup;
5515
5793
  }
5794
+ if (diagnosticsEnabled && submeshMaterial.textureHandles !== void 0) {
5795
+ console.error(
5796
+ `[render-material] draw submitted: ${JSON.stringify({
5797
+ entityKey: entry.source.entityKey,
5798
+ materialHandle: submeshMaterial.materialHandle,
5799
+ shader: smMaterialShaderId,
5800
+ textureHandles: [...submeshMaterial.textureHandles.entries()].map(
5801
+ ([field, handle]) => ({
5802
+ field,
5803
+ handle
5804
+ })
5805
+ ),
5806
+ skin: isSkinEntry ? {
5807
+ resourcesReady: skinResources !== null,
5808
+ psoReady: skinPsoProbe !== null,
5809
+ paletteBufferReady: skinSlice?.buffer !== void 0,
5810
+ paletteByteOffset: skinSlice?.byteOffset,
5811
+ dynamicOffsets: [...group2DynamicOffsets]
5812
+ } : void 0,
5813
+ pipelineReady: true
5814
+ })}`
5815
+ );
5816
+ }
5516
5817
  pass.setBindGroup(2, group2BindGroup, group2DynamicOffsets);
5517
5818
  if (profilePhase === void 0) {
5518
5819
  recordOcclusionCandidate(
@@ -5546,6 +5847,9 @@ function recordGeometryDraws(c, pass, matchedMaterials, materialSlotIndices, sam
5546
5847
  )
5547
5848
  );
5548
5849
  }
5850
+ if (diagnosticsEnabled && perSubmeshBg !== null) {
5851
+ recordIblPipelineBinding(c, submeshMaterial, smPipelineHandle, perSubmeshBg);
5852
+ }
5549
5853
  }
5550
5854
  }
5551
5855
  }
@@ -6125,9 +6429,10 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
6125
6429
  spritePass.setVertexBuffer(0, spriteEntry.mesh.vertexBuffer.handle);
6126
6430
  lastSpriteVertexBuffer = spriteEntry.mesh.vertexBuffer;
6127
6431
  }
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;
6432
+ const indexBuffer = spriteEntry.mesh.indexBuffer;
6433
+ if (indexBuffer !== null && indexBuffer !== lastSpriteIndexBuffer) {
6434
+ spritePass.setIndexBuffer(indexBuffer.handle, spriteEntry.mesh.indexFormat);
6435
+ lastSpriteIndexBuffer = indexBuffer;
6131
6436
  }
6132
6437
  let spriteInstanceBuffer = pipelineState.identityInstanceBuffer;
6133
6438
  let spriteInstanceCount = 1;
@@ -6282,6 +6587,7 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
6282
6587
  const spriteGroup2 = entityShaderId === "forgeax::sprite-lit" && c.standardLighting?.kind === "clustered" ? c.hdrpClusterBindGroup : meshBindGroup;
6283
6588
  const spriteInstancesBg = resolveGeometryInstancesBindGroup(c, spriteInstanceBuffer, void 0);
6284
6589
  spritePass.setBindGroup(2, spriteGroup2, [i * MESH_PER_ENTITY_STRIDE]);
6590
+ const materialSlot = materialSlotIndices[i]?.[0] ?? 0;
6285
6591
  const spriteTexHandle = spriteEntry.source.material.baseColorTexture;
6286
6592
  let spriteTexView = pipelineState.defaultWhiteTextureView;
6287
6593
  if (spriteTexHandle !== void 0) {
@@ -6312,7 +6618,6 @@ function recordSpriteEntityDraws(c, spritePass, matchedIndices, skylightResource
6312
6618
  },
6313
6619
  bindGroupCounts
6314
6620
  );
6315
- const materialSlot = materialSlotIndices[i]?.[0] ?? 0;
6316
6621
  spritePass.setBindGroup(1, spritePassBg, [materialSlot * MATERIAL_PER_ENTITY_STRIDE]);
6317
6622
  spritePass.setBindGroup(3, spriteInstancesBg);
6318
6623
  spritePass.drawIndexed(spriteEntry.mesh.indexCount, spriteInstanceCount, 0, 0, 0);
@@ -6983,7 +7288,13 @@ function ensureSpotShadowMaterialBg(c) {
6983
7288
 
6984
7289
  // src/record/main-pass.ts
6985
7290
  var ZERO_SKYLIGHT_PAYLOAD = new Float32Array([0, 0, 0, 0, 0, 0, 0, 1]);
6986
- var TRANSMISSION_VARIANT_SET = "TRANSMISSION_AVAILABLE=true";
7291
+ function materialDiagnosticsEnabled3() {
7292
+ if (typeof globalThis !== "object" || globalThis === null || !("process" in globalThis)) {
7293
+ return false;
7294
+ }
7295
+ const processValue = globalThis.process;
7296
+ return processValue?.env?.FORGEAX_MATERIAL_DIAGNOSTICS === "1";
7297
+ }
6987
7298
  function isTransmissionMaterial(material) {
6988
7299
  return material.materialShaderId === "forgeax::default-standard-pbr" && typeof material.paramSnapshot?.transmission === "number" && material.paramSnapshot.transmission > 0;
6989
7300
  }
@@ -7103,11 +7414,7 @@ function recordMainPass(c, selector, options, graphPass) {
7103
7414
  const matchedMaterials = matchedMaterialsForRecordMode(c, selectorMatchedMaterials, recordMode);
7104
7415
  const recordContext = recordMode === void 0 ? c : {
7105
7416
  ...c,
7106
- splitLdrSprite: recordMode === "transparent" ? false : c.splitLdrSprite,
7107
- validatedOrdered: c.validatedOrdered.map((entry) => ({
7108
- ...entry,
7109
- ...recordMode === "transmission" ? { variantSet: TRANSMISSION_VARIANT_SET } : {}
7110
- }))
7417
+ splitLdrSprite: recordMode === "transparent" ? false : c.splitLdrSprite
7111
7418
  };
7112
7419
  if (clusteredBindGroupMissing) {
7113
7420
  runtime.errorRegistry.fire(
@@ -7161,6 +7468,62 @@ function recordMainPass(c, selector, options, graphPass) {
7161
7468
  brdfLutSampler: skylightFallback.sampler,
7162
7469
  intensityBuffer: skylightFallback.intensityBuffer
7163
7470
  };
7471
+ if (materialDiagnosticsEnabled3()) {
7472
+ const activeCache = activeViews === void 0 ? void 0 : getOrCreateIblCache(runtime.deviceScope);
7473
+ const prefilterViewMipCount = activeCache?.prefilterFaceViewsByMip?.length ?? (activeViews === void 0 ? 1 : 0);
7474
+ frameState.iblBindingInspection = {
7475
+ status: "binding-chain-consistent",
7476
+ frameId: frameState.frameNumber,
7477
+ deviceGeneration: runtime.deviceScope.generation,
7478
+ active: activeViews === void 0 ? "fallback" : "active",
7479
+ cache: {
7480
+ identity: getOpaqueResourceIdentity(activeCache ?? skylightFallback),
7481
+ generation: runtime.deviceScope.generation,
7482
+ prefilterMipCount: prefilterViewMipCount,
7483
+ prefilterViewMipCount
7484
+ },
7485
+ sampler: {
7486
+ expected: {
7487
+ magFilter: "linear",
7488
+ minFilter: "linear",
7489
+ mipmapFilter: "linear",
7490
+ addressModeU: "clamp-to-edge",
7491
+ addressModeV: "clamp-to-edge",
7492
+ addressModeW: "clamp-to-edge"
7493
+ },
7494
+ identities: [
7495
+ getOpaqueResourceIdentity(skylightResources.irradianceSampler),
7496
+ getOpaqueResourceIdentity(skylightResources.prefilterSampler),
7497
+ getOpaqueResourceIdentity(skylightResources.brdfLutSampler)
7498
+ ]
7499
+ },
7500
+ resources: {
7501
+ irradiance: {
7502
+ viewIdentity: getOpaqueResourceIdentity(skylightResources.irradianceView),
7503
+ samplerIdentity: getOpaqueResourceIdentity(
7504
+ skylightResources.irradianceSampler
7505
+ ),
7506
+ deviceGeneration: runtime.deviceScope.generation
7507
+ },
7508
+ prefilter: {
7509
+ viewIdentity: getOpaqueResourceIdentity(skylightResources.prefilterView),
7510
+ samplerIdentity: getOpaqueResourceIdentity(
7511
+ skylightResources.prefilterSampler
7512
+ ),
7513
+ deviceGeneration: runtime.deviceScope.generation
7514
+ },
7515
+ brdfLut: {
7516
+ viewIdentity: getOpaqueResourceIdentity(skylightResources.brdfLutView),
7517
+ samplerIdentity: getOpaqueResourceIdentity(skylightResources.brdfLutSampler),
7518
+ deviceGeneration: runtime.deviceScope.generation
7519
+ },
7520
+ intensityBufferIdentity: getOpaqueResourceIdentity(
7521
+ skylightResources.intensityBuffer
7522
+ )
7523
+ },
7524
+ errors: []
7525
+ };
7526
+ }
7164
7527
  const perSubmeshMaterialBgDeps = {
7165
7528
  runtime,
7166
7529
  pipelineState,
@@ -7196,6 +7559,7 @@ function recordMainPass(c, selector, options, graphPass) {
7196
7559
  },
7197
7560
  materialBgShared: frameState.materialBgShared,
7198
7561
  materialBgAssemblyCache: c.materialBgAssemblyCache,
7562
+ frameState,
7199
7563
  bindGroupCounts,
7200
7564
  ...transmissionBackdropView === void 0 ? {} : { transmissionBackdropView }
7201
7565
  };
@@ -7225,6 +7589,20 @@ function recordMainPass(c, selector, options, graphPass) {
7225
7589
  const materialShaderId = mat.materialShaderId;
7226
7590
  const schema = mat.materialParamSchema ?? (materialShaderId !== void 0 ? runtime.getParamSchema?.(materialShaderId) : void 0);
7227
7591
  applyParamSnapshotToUbo(slotPayloadF32, schema, mat.paramSnapshot);
7592
+ const processValue = globalThis.process;
7593
+ if (processValue?.env?.FORGEAX_MATERIAL_PIPELINE_DIAGNOSTICS === "1" && (materialShaderId === "forgeax::default-standard-pbr" || materialShaderId === "forgeax::pbr-skin")) {
7594
+ console.error(
7595
+ `[render-material] UBO receipt: ${JSON.stringify({
7596
+ entityIndex: entry.renderableIndex,
7597
+ materialHandle: mat.materialHandle,
7598
+ materialShaderId,
7599
+ clearcoat: slotPayloadF32[32],
7600
+ clearcoatRoughness: slotPayloadF32[33],
7601
+ clearcoatNormalScale: slotPayloadF32[34],
7602
+ paramSnapshot: mat.paramSnapshot
7603
+ })}`
7604
+ );
7605
+ }
7228
7606
  applyMaterialTextureUvScales(slotPayloadF32, mat, world);
7229
7607
  {
7230
7608
  const matHandleRaw = mat.baseColorTexture;
@@ -9184,6 +9562,6 @@ function addTaaResolvePass(graph, current, currentTemporal, previousColor, previ
9184
9562
  });
9185
9563
  }
9186
9564
 
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
9565
+ export { BYTES_PER_DIRECT_LIGHT_SLOT, CAPTURE_VIEW_PROJS, CUBEMAP_FACE_VERTICES, CUBE_CAPTURE_VIEW_SLOT_BASE, DEFERRED_COLOR_FORMATS, DEPTH_MIN_PARAMS_BYTE_SIZE, FALLBACK_BYTES_PER_ROW, GpuBuffer, GpuTexture, MATERIAL_PER_ENTITY_STRIDE, MESH_SSBO_BYTES, POINTS_LINES_MATERIAL_SHADER_ID, POINTS_LINES_VIEW_BUFFER_SIZE, PREFILTER_MIP_LEVELS, PREFILTER_SIZE, PipelineSpecError, PostProcessError, REFLECTION_PROBE_VIEW_SLOT_BASE, SHADOW_CASTER_BUFFER_SIZE, TYPED_BLOOM_PASS_NAMES, VIEW_UNIFORM_BUFFER_SIZE, VIEW_UNIFORM_BYTES, VIEW_UNIFORM_SLOT_STRIDE, abortTemporalGpuSubmit, addReflectionFallbackObservationPass, addStandardSceneDataPass, addTaaResolvePass, addTypedBloomPasses, addTypedCompositePostEffects, addTypedFrameObservationPass, addTypedFullscreenPass, addTypedOutputTransformPass, addTypedScenePass, addTypedSkyboxPass, addTypedSsaoPasses, addTypedTemporalResolvePass, aggregateTemporalDemand, applyParamSnapshotToUbo, assembleMaterialWithSkylightEntries, assertStorageBufferCap, buildFoldDispatchPlan, buildFullscreenPostProcessPass, buildLinearLdrMaterialSpecTable, buildPerFrameBindGroups, buildSpecConstTable, cacheKeyOf, cleanPerEntityCache, colorFormatsForPassKind, commitTemporalGpuSubmit, computeProjectionMatrix, computeSplitLdrSprite, computeViewMatrix, configureSurface, createClusterBinScratch, createFullscreenBindGroup, createHdrpClusterMembershipBindGroupLayoutDescriptor, createIblPipelines, createPointsLinesLaneAdapter, createPointsLinesLaneContract, createRenderPipelineTarget, createSkylightFallback, createStandardSceneDataTarget, deriveRenderDataCubemap, deriveRenderDataMesh, deriveRenderDataTexture, describeTemporalDemand, driveLazyEquirectProjection, encodeDirectionalShadowPass, encodeMainPass, encodePointShadowPass, encodeSpotShadowPass, ensureMeshSsboCapacity, entryHasDepthRead, evaluateFoldBucketUniformCap, foldDispatchBuckets, geometryRenderStateForPass, geometryRenderStateForTopology, getOrBuildPipeline, getOrCreateHdrpBuffers, getOrCreateIblCache, getTemporalBindGroupResources, getTemporalGpuState, getTemporalParamsBuffer, hasPendingTemporalGpuSubmit, importRenderPipelineSurface, incrementFoldedDrawsMetric, isTemporalFullscreenBinding, passKindPolicyTable, postProcessShaderEntrySignature, postProcessShaderModuleLabel, postProcessShaderPipelineLabel, prepareFrameLighting, resetHdrpBuffers, residentTextureView, resolveOutputDither, resolveSkyboxActive, resolveSsaoParameters, resolveSurfaceFormatPair, resolveSurfaceProfile, retireTemporalGpuState, retireTemporalGpuStateAfterFence, runIblPrecompute, selectLazyEquirectHandle, selectSwapChainFormat, setIblComposedShaders, standardStorageVariantSet, standardTemporalLaneAdmission, standardTemporalPostOrder, standardTopologyVariantSet, temporalReadIndex, temporalWriteIndex, typedFrameClearColor, uploadMeshSsboBatch, variantSetFromDefines, variantSetFromVertexLayoutProjection, warnMultiSkybox, warnMultiSkylight, warnZeroLightStandard, writeHdrpClusterAndSsaoBuffers, writePointsLinesViewUbo, writeShadowCasterUniforms, writeShadowParamsBuffer, writeSpotModifierTextures, writeViewUbo };
9566
+ //# sourceMappingURL=chunk-YRUYGA4A.mjs.map
9567
+ //# sourceMappingURL=chunk-YRUYGA4A.mjs.map