@forgeax/engine-render 0.1.2

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 (1172) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +1019 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/analytic-sky-contract.test.d.ts +2 -0
  5. package/dist/__tests__/analytic-sky-contract.test.d.ts.map +1 -0
  6. package/dist/__tests__/analytic-sky-falsify.test.d.ts +2 -0
  7. package/dist/__tests__/analytic-sky-falsify.test.d.ts.map +1 -0
  8. package/dist/__tests__/assets-runtime-handoff.integration.test.d.ts +2 -0
  9. package/dist/__tests__/assets-runtime-handoff.integration.test.d.ts.map +1 -0
  10. package/dist/__tests__/authoring-path.unit.test.d.ts +2 -0
  11. package/dist/__tests__/authoring-path.unit.test.d.ts.map +1 -0
  12. package/dist/__tests__/backend-selection-rhi-capture.test-d.d.ts +2 -0
  13. package/dist/__tests__/backend-selection-rhi-capture.test-d.d.ts.map +1 -0
  14. package/dist/__tests__/backend-selection.integration.test.d.ts +2 -0
  15. package/dist/__tests__/backend-selection.integration.test.d.ts.map +1 -0
  16. package/dist/__tests__/callback-inference.test-d.d.ts +2 -0
  17. package/dist/__tests__/callback-inference.test-d.d.ts.map +1 -0
  18. package/dist/__tests__/camera-temporal.test-d.d.ts +2 -0
  19. package/dist/__tests__/camera-temporal.test-d.d.ts.map +1 -0
  20. package/dist/__tests__/camera-temporal.test.d.ts +2 -0
  21. package/dist/__tests__/camera-temporal.test.d.ts.map +1 -0
  22. package/dist/__tests__/cluster-binner.test.d.ts +2 -0
  23. package/dist/__tests__/cluster-binner.test.d.ts.map +1 -0
  24. package/dist/__tests__/cluster-grid-default-surface.unit.test.d.ts +2 -0
  25. package/dist/__tests__/cluster-grid-default-surface.unit.test.d.ts.map +1 -0
  26. package/dist/__tests__/construct-renderer-identity.unit.test.d.ts +2 -0
  27. package/dist/__tests__/construct-renderer-identity.unit.test.d.ts.map +1 -0
  28. package/dist/__tests__/direct-extract-consumer-contract.test-d.d.ts +2 -0
  29. package/dist/__tests__/direct-extract-consumer-contract.test-d.d.ts.map +1 -0
  30. package/dist/__tests__/direct-light-snapshot.unit.test.d.ts +2 -0
  31. package/dist/__tests__/direct-light-snapshot.unit.test.d.ts.map +1 -0
  32. package/dist/__tests__/directional-shadow-probe-atlas-layout.unit.test.d.ts +2 -0
  33. package/dist/__tests__/directional-shadow-probe-atlas-layout.unit.test.d.ts.map +1 -0
  34. package/dist/__tests__/environment-inspection.test.d.ts +2 -0
  35. package/dist/__tests__/environment-inspection.test.d.ts.map +1 -0
  36. package/dist/__tests__/factory-contract.integration.test.d.ts +2 -0
  37. package/dist/__tests__/factory-contract.integration.test.d.ts.map +1 -0
  38. package/dist/__tests__/fallback-row-stride-surface.unit.test.d.ts +2 -0
  39. package/dist/__tests__/fallback-row-stride-surface.unit.test.d.ts.map +1 -0
  40. package/dist/__tests__/fog-ai-user.fixture.d.ts +2 -0
  41. package/dist/__tests__/fog-ai-user.fixture.d.ts.map +1 -0
  42. package/dist/__tests__/fog-custom-render-feature.fixture.d.ts +16 -0
  43. package/dist/__tests__/fog-custom-render-feature.fixture.d.ts.map +1 -0
  44. package/dist/__tests__/fog-custom-render-feature.integration.test.d.ts +2 -0
  45. package/dist/__tests__/fog-custom-render-feature.integration.test.d.ts.map +1 -0
  46. package/dist/__tests__/fog-inspection.integration.test.d.ts +2 -0
  47. package/dist/__tests__/fog-inspection.integration.test.d.ts.map +1 -0
  48. package/dist/__tests__/fog-owner-state.integration.test.d.ts +2 -0
  49. package/dist/__tests__/fog-owner-state.integration.test.d.ts.map +1 -0
  50. package/dist/__tests__/fog-pipeline-topology.integration.test.d.ts +2 -0
  51. package/dist/__tests__/fog-pipeline-topology.integration.test.d.ts.map +1 -0
  52. package/dist/__tests__/fog-public-surface.integration.test.d.ts +2 -0
  53. package/dist/__tests__/fog-public-surface.integration.test.d.ts.map +1 -0
  54. package/dist/__tests__/fog-ray-contract.integration.test.d.ts +2 -0
  55. package/dist/__tests__/fog-ray-contract.integration.test.d.ts.map +1 -0
  56. package/dist/__tests__/fog-source-absence.integration.test.d.ts +2 -0
  57. package/dist/__tests__/fog-source-absence.integration.test.d.ts.map +1 -0
  58. package/dist/__tests__/fog-temporal.integration.test.d.ts +2 -0
  59. package/dist/__tests__/fog-temporal.integration.test.d.ts.map +1 -0
  60. package/dist/__tests__/frame-chain.unit.test.d.ts +2 -0
  61. package/dist/__tests__/frame-chain.unit.test.d.ts.map +1 -0
  62. package/dist/__tests__/frame-observation.contract.test.d.ts +2 -0
  63. package/dist/__tests__/frame-observation.contract.test.d.ts.map +1 -0
  64. package/dist/__tests__/glyph-text-layout-recovery.unit.test.d.ts +2 -0
  65. package/dist/__tests__/glyph-text-layout-recovery.unit.test.d.ts.map +1 -0
  66. package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts +6 -0
  67. package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts.map +1 -0
  68. package/dist/__tests__/gpu-driven-indirect-raster.browser.test.d.ts +2 -0
  69. package/dist/__tests__/gpu-driven-indirect-raster.browser.test.d.ts.map +1 -0
  70. package/dist/__tests__/gpu-driven-indirect-raster.dawn.test.d.ts +2 -0
  71. package/dist/__tests__/gpu-driven-indirect-raster.dawn.test.d.ts.map +1 -0
  72. package/dist/__tests__/gpu-driven-production.integration.test.d.ts +2 -0
  73. package/dist/__tests__/gpu-driven-production.integration.test.d.ts.map +1 -0
  74. package/dist/__tests__/gpu-driven-scaling.unit.test.d.ts +2 -0
  75. package/dist/__tests__/gpu-driven-scaling.unit.test.d.ts.map +1 -0
  76. package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts +12 -0
  77. package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts.map +1 -0
  78. package/dist/__tests__/gpu-driven-view-graph.integration.test.d.ts +2 -0
  79. package/dist/__tests__/gpu-driven-view-graph.integration.test.d.ts.map +1 -0
  80. package/dist/__tests__/gpu-driven-view.browser.test.d.ts +2 -0
  81. package/dist/__tests__/gpu-driven-view.browser.test.d.ts.map +1 -0
  82. package/dist/__tests__/gpu-driven-view.dawn.test.d.ts +2 -0
  83. package/dist/__tests__/gpu-driven-view.dawn.test.d.ts.map +1 -0
  84. package/dist/__tests__/gpu-driven-view.unit.test.d.ts +2 -0
  85. package/dist/__tests__/gpu-driven-view.unit.test.d.ts.map +1 -0
  86. package/dist/__tests__/gpu-resource-store-stride.unit.test.d.ts +2 -0
  87. package/dist/__tests__/gpu-resource-store-stride.unit.test.d.ts.map +1 -0
  88. package/dist/__tests__/gpu-scene-field-type-owner.test-d.d.ts +2 -0
  89. package/dist/__tests__/gpu-scene-field-type-owner.test-d.d.ts.map +1 -0
  90. package/dist/__tests__/gpu-scene-render-graph-gpu.d.ts +7 -0
  91. package/dist/__tests__/gpu-scene-render-graph-gpu.d.ts.map +1 -0
  92. package/dist/__tests__/gpu-scene-render-graph.browser.test.d.ts +2 -0
  93. package/dist/__tests__/gpu-scene-render-graph.browser.test.d.ts.map +1 -0
  94. package/dist/__tests__/gpu-scene-render-graph.dawn.test.d.ts +2 -0
  95. package/dist/__tests__/gpu-scene-render-graph.dawn.test.d.ts.map +1 -0
  96. package/dist/__tests__/gpu-scene-schema.unit.test.d.ts +2 -0
  97. package/dist/__tests__/gpu-scene-schema.unit.test.d.ts.map +1 -0
  98. package/dist/__tests__/gpu-scene.dawn.test.d.ts +2 -0
  99. package/dist/__tests__/gpu-scene.dawn.test.d.ts.map +1 -0
  100. package/dist/__tests__/gpu-scene.unit.test.d.ts +2 -0
  101. package/dist/__tests__/gpu-scene.unit.test.d.ts.map +1 -0
  102. package/dist/__tests__/hdrp-pipeline.unit.test.d.ts +2 -0
  103. package/dist/__tests__/hdrp-pipeline.unit.test.d.ts.map +1 -0
  104. package/dist/__tests__/ibl/ibl-fallback.unit.test.d.ts +2 -0
  105. package/dist/__tests__/ibl/ibl-fallback.unit.test.d.ts.map +1 -0
  106. package/dist/__tests__/infinite-grid-no-vertex-probe.unit.test.d.ts +2 -0
  107. package/dist/__tests__/infinite-grid-no-vertex-probe.unit.test.d.ts.map +1 -0
  108. package/dist/__tests__/injection-kind-owner.test-d.d.ts +2 -0
  109. package/dist/__tests__/injection-kind-owner.test-d.d.ts.map +1 -0
  110. package/dist/__tests__/ktx2-basis-gpu-consumer.dawn.test.d.ts +2 -0
  111. package/dist/__tests__/ktx2-basis-gpu-consumer.dawn.test.d.ts.map +1 -0
  112. package/dist/__tests__/light-buffer-layout.unit.test.d.ts +2 -0
  113. package/dist/__tests__/light-buffer-layout.unit.test.d.ts.map +1 -0
  114. package/dist/__tests__/material-contract-inventory.unit.test.d.ts +2 -0
  115. package/dist/__tests__/material-contract-inventory.unit.test.d.ts.map +1 -0
  116. package/dist/__tests__/material-cooked-projection.unit.test.d.ts +2 -0
  117. package/dist/__tests__/material-cooked-projection.unit.test.d.ts.map +1 -0
  118. package/dist/__tests__/material-derived-error-stop-draw.integration.test.d.ts +2 -0
  119. package/dist/__tests__/material-derived-error-stop-draw.integration.test.d.ts.map +1 -0
  120. package/dist/__tests__/material-derived-payload.integration.test.d.ts +2 -0
  121. package/dist/__tests__/material-derived-payload.integration.test.d.ts.map +1 -0
  122. package/dist/__tests__/material-snapshot-mutation.integration.test.d.ts +2 -0
  123. package/dist/__tests__/material-snapshot-mutation.integration.test.d.ts.map +1 -0
  124. package/dist/__tests__/material-static-dynamic-route.unit.test.d.ts +2 -0
  125. package/dist/__tests__/material-static-dynamic-route.unit.test.d.ts.map +1 -0
  126. package/dist/__tests__/material-stride-surface.unit.test.d.ts +2 -0
  127. package/dist/__tests__/material-stride-surface.unit.test.d.ts.map +1 -0
  128. package/dist/__tests__/materials.unit.test.d.ts +2 -0
  129. package/dist/__tests__/materials.unit.test.d.ts.map +1 -0
  130. package/dist/__tests__/mesh-buffer-usage-surface.unit.test.d.ts +2 -0
  131. package/dist/__tests__/mesh-buffer-usage-surface.unit.test.d.ts.map +1 -0
  132. package/dist/__tests__/mesh-material-bindings.unit.test.d.ts +2 -0
  133. package/dist/__tests__/mesh-material-bindings.unit.test.d.ts.map +1 -0
  134. package/dist/__tests__/mesh-stride-surface.unit.test.d.ts +2 -0
  135. package/dist/__tests__/mesh-stride-surface.unit.test.d.ts.map +1 -0
  136. package/dist/__tests__/morph-culling-reentry.dawn.test.d.ts +2 -0
  137. package/dist/__tests__/morph-culling-reentry.dawn.test.d.ts.map +1 -0
  138. package/dist/__tests__/morph-feature-gpu-work.integration.test.d.ts +2 -0
  139. package/dist/__tests__/morph-feature-gpu-work.integration.test.d.ts.map +1 -0
  140. package/dist/__tests__/morph-visual-evidence.integration.test.d.ts +2 -0
  141. package/dist/__tests__/morph-visual-evidence.integration.test.d.ts.map +1 -0
  142. package/dist/__tests__/nineslice-handle-surface.unit.test.d.ts +2 -0
  143. package/dist/__tests__/nineslice-handle-surface.unit.test.d.ts.map +1 -0
  144. package/dist/__tests__/param-schema-projection-census.unit.test.d.ts +2 -0
  145. package/dist/__tests__/param-schema-projection-census.unit.test.d.ts.map +1 -0
  146. package/dist/__tests__/persistent-render-scene.unit.test.d.ts +2 -0
  147. package/dist/__tests__/persistent-render-scene.unit.test.d.ts.map +1 -0
  148. package/dist/__tests__/pipeline-error-policy-owner.unit.test.d.ts +2 -0
  149. package/dist/__tests__/pipeline-error-policy-owner.unit.test.d.ts.map +1 -0
  150. package/dist/__tests__/pipeline-pass-trace.test.d.ts +2 -0
  151. package/dist/__tests__/pipeline-pass-trace.test.d.ts.map +1 -0
  152. package/dist/__tests__/pipeline-stage-owner.test-d.d.ts +2 -0
  153. package/dist/__tests__/pipeline-stage-owner.test-d.d.ts.map +1 -0
  154. package/dist/__tests__/plugin.test.d.ts +2 -0
  155. package/dist/__tests__/plugin.test.d.ts.map +1 -0
  156. package/dist/__tests__/post-process-error-policy-owner.unit.test.d.ts +2 -0
  157. package/dist/__tests__/post-process-error-policy-owner.unit.test.d.ts.map +1 -0
  158. package/dist/__tests__/prepared-buffer-kind-owner.test-d.d.ts +2 -0
  159. package/dist/__tests__/prepared-buffer-kind-owner.test-d.d.ts.map +1 -0
  160. package/dist/__tests__/prepared-graphics-backend-matrix.unit.test.d.ts +2 -0
  161. package/dist/__tests__/prepared-graphics-backend-matrix.unit.test.d.ts.map +1 -0
  162. package/dist/__tests__/prepared-graphics-capability.unit.test.d.ts +2 -0
  163. package/dist/__tests__/prepared-graphics-capability.unit.test.d.ts.map +1 -0
  164. package/dist/__tests__/prepared-graphics-generation.unit.test.d.ts +2 -0
  165. package/dist/__tests__/prepared-graphics-generation.unit.test.d.ts.map +1 -0
  166. package/dist/__tests__/prepared-graphics-lifecycle.unit.test.d.ts +2 -0
  167. package/dist/__tests__/prepared-graphics-lifecycle.unit.test.d.ts.map +1 -0
  168. package/dist/__tests__/prepared-graphics-pipeline-warmup.unit.test.d.ts +2 -0
  169. package/dist/__tests__/prepared-graphics-pipeline-warmup.unit.test.d.ts.map +1 -0
  170. package/dist/__tests__/prepared-graphics-resolver-recording.unit.test.d.ts +2 -0
  171. package/dist/__tests__/prepared-graphics-resolver-recording.unit.test.d.ts.map +1 -0
  172. package/dist/__tests__/prepared-graphics-resolver.unit.test.d.ts +2 -0
  173. package/dist/__tests__/prepared-graphics-resolver.unit.test.d.ts.map +1 -0
  174. package/dist/__tests__/prepared-graphics-snapshot.unit.test.d.ts +2 -0
  175. package/dist/__tests__/prepared-graphics-snapshot.unit.test.d.ts.map +1 -0
  176. package/dist/__tests__/prepared-graphics-store.unit.test.d.ts +2 -0
  177. package/dist/__tests__/prepared-graphics-store.unit.test.d.ts.map +1 -0
  178. package/dist/__tests__/prepared-graphics-validation.unit.test.d.ts +2 -0
  179. package/dist/__tests__/prepared-graphics-validation.unit.test.d.ts.map +1 -0
  180. package/dist/__tests__/profiler-default-off.test.d.ts +2 -0
  181. package/dist/__tests__/profiler-default-off.test.d.ts.map +1 -0
  182. package/dist/__tests__/profiler-phase-catalog.test.d.ts +2 -0
  183. package/dist/__tests__/profiler-phase-catalog.test.d.ts.map +1 -0
  184. package/dist/__tests__/profiler-phase-integration.test.d.ts +2 -0
  185. package/dist/__tests__/profiler-phase-integration.test.d.ts.map +1 -0
  186. package/dist/__tests__/profiler-phase-token.test.d.ts +2 -0
  187. package/dist/__tests__/profiler-phase-token.test.d.ts.map +1 -0
  188. package/dist/__tests__/public-pipeline-id.test-d.d.ts +2 -0
  189. package/dist/__tests__/public-pipeline-id.test-d.d.ts.map +1 -0
  190. package/dist/__tests__/public-surface.test-d.d.ts +2 -0
  191. package/dist/__tests__/public-surface.test-d.d.ts.map +1 -0
  192. package/dist/__tests__/recover-error-policy-owner.unit.test.d.ts +2 -0
  193. package/dist/__tests__/recover-error-policy-owner.unit.test.d.ts.map +1 -0
  194. package/dist/__tests__/render-dispatch-membership.unit.test.d.ts +2 -0
  195. package/dist/__tests__/render-dispatch-membership.unit.test.d.ts.map +1 -0
  196. package/dist/__tests__/render-error-code-owner.test-d.d.ts +2 -0
  197. package/dist/__tests__/render-error-code-owner.test-d.d.ts.map +1 -0
  198. package/dist/__tests__/render-error-detail-ssot.test-d.d.ts +2 -0
  199. package/dist/__tests__/render-error-detail-ssot.test-d.d.ts.map +1 -0
  200. package/dist/__tests__/render-error-exhaustive.test-d.d.ts +2 -0
  201. package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -0
  202. package/dist/__tests__/render-feature-assembly.test-d.d.ts +2 -0
  203. package/dist/__tests__/render-feature-assembly.test-d.d.ts.map +1 -0
  204. package/dist/__tests__/render-feature-construction.unit.test.d.ts +2 -0
  205. package/dist/__tests__/render-feature-construction.unit.test.d.ts.map +1 -0
  206. package/dist/__tests__/render-feature-context.test-d.d.ts +2 -0
  207. package/dist/__tests__/render-feature-context.test-d.d.ts.map +1 -0
  208. package/dist/__tests__/render-feature-contract.test-d.d.ts +2 -0
  209. package/dist/__tests__/render-feature-contract.test-d.d.ts.map +1 -0
  210. package/dist/__tests__/render-feature-diagnostics.unit.test.d.ts +2 -0
  211. package/dist/__tests__/render-feature-diagnostics.unit.test.d.ts.map +1 -0
  212. package/dist/__tests__/render-feature-docs.unit.test.d.ts +2 -0
  213. package/dist/__tests__/render-feature-docs.unit.test.d.ts.map +1 -0
  214. package/dist/__tests__/render-feature-error-code-owner.test-d.d.ts +2 -0
  215. package/dist/__tests__/render-feature-error-code-owner.test-d.d.ts.map +1 -0
  216. package/dist/__tests__/render-feature-errors.unit.test.d.ts +2 -0
  217. package/dist/__tests__/render-feature-errors.unit.test.d.ts.map +1 -0
  218. package/dist/__tests__/render-feature-gpu-buffer-usage-owner.test-d.d.ts +2 -0
  219. package/dist/__tests__/render-feature-gpu-buffer-usage-owner.test-d.d.ts.map +1 -0
  220. package/dist/__tests__/render-feature-gpu-work.integration.test.d.ts +2 -0
  221. package/dist/__tests__/render-feature-gpu-work.integration.test.d.ts.map +1 -0
  222. package/dist/__tests__/render-feature-graph.integration.test.d.ts +2 -0
  223. package/dist/__tests__/render-feature-graph.integration.test.d.ts.map +1 -0
  224. package/dist/__tests__/render-feature-graphics-staging.unit.test.d.ts +2 -0
  225. package/dist/__tests__/render-feature-graphics-staging.unit.test.d.ts.map +1 -0
  226. package/dist/__tests__/render-feature-order.unit.test.d.ts +2 -0
  227. package/dist/__tests__/render-feature-order.unit.test.d.ts.map +1 -0
  228. package/dist/__tests__/render-feature-prepared-diagnostics.unit.test.d.ts +2 -0
  229. package/dist/__tests__/render-feature-prepared-diagnostics.unit.test.d.ts.map +1 -0
  230. package/dist/__tests__/render-feature-prepared-errors.test-d.d.ts +2 -0
  231. package/dist/__tests__/render-feature-prepared-errors.test-d.d.ts.map +1 -0
  232. package/dist/__tests__/render-feature-prepared-graphics-docs.unit.test.d.ts +2 -0
  233. package/dist/__tests__/render-feature-prepared-graphics-docs.unit.test.d.ts.map +1 -0
  234. package/dist/__tests__/render-feature-prepared-graphics-isolation.unit.test.d.ts +2 -0
  235. package/dist/__tests__/render-feature-prepared-graphics-isolation.unit.test.d.ts.map +1 -0
  236. package/dist/__tests__/render-feature-prepared-graphics-negative.test-d.d.ts +2 -0
  237. package/dist/__tests__/render-feature-prepared-graphics-negative.test-d.d.ts.map +1 -0
  238. package/dist/__tests__/render-feature-prepared-graphics-recording.unit.test.d.ts +2 -0
  239. package/dist/__tests__/render-feature-prepared-graphics-recording.unit.test.d.ts.map +1 -0
  240. package/dist/__tests__/render-feature-prepared-graphics.test-d.d.ts +2 -0
  241. package/dist/__tests__/render-feature-prepared-graphics.test-d.d.ts.map +1 -0
  242. package/dist/__tests__/render-feature-prepared-graphics.unit.test.d.ts +2 -0
  243. package/dist/__tests__/render-feature-prepared-graphics.unit.test.d.ts.map +1 -0
  244. package/dist/__tests__/render-feature-public-surface.test-d.d.ts +2 -0
  245. package/dist/__tests__/render-feature-public-surface.test-d.d.ts.map +1 -0
  246. package/dist/__tests__/render-feature-retry.unit.test.d.ts +2 -0
  247. package/dist/__tests__/render-feature-retry.unit.test.d.ts.map +1 -0
  248. package/dist/__tests__/render-feature-scheduling.unit.test.d.ts +2 -0
  249. package/dist/__tests__/render-feature-scheduling.unit.test.d.ts.map +1 -0
  250. package/dist/__tests__/render-feature-stage-failed-policy-owner.unit.test.d.ts +2 -0
  251. package/dist/__tests__/render-feature-stage-failed-policy-owner.unit.test.d.ts.map +1 -0
  252. package/dist/__tests__/render-feature-stage-fault-recovery.unit.test.d.ts +2 -0
  253. package/dist/__tests__/render-feature-stage-fault-recovery.unit.test.d.ts.map +1 -0
  254. package/dist/__tests__/render-feature-staging.unit.test.d.ts +2 -0
  255. package/dist/__tests__/render-feature-staging.unit.test.d.ts.map +1 -0
  256. package/dist/__tests__/render-feature-visibility-context.test-d.d.ts +2 -0
  257. package/dist/__tests__/render-feature-visibility-context.test-d.d.ts.map +1 -0
  258. package/dist/__tests__/render-feature-visibility-context.unit.test.d.ts +2 -0
  259. package/dist/__tests__/render-feature-visibility-context.unit.test.d.ts.map +1 -0
  260. package/dist/__tests__/render-feature-zero-work.unit.test.d.ts +2 -0
  261. package/dist/__tests__/render-feature-zero-work.unit.test.d.ts.map +1 -0
  262. package/dist/__tests__/render-graph-compute-raster-gpu.d.ts +7 -0
  263. package/dist/__tests__/render-graph-compute-raster-gpu.d.ts.map +1 -0
  264. package/dist/__tests__/render-graph-compute-raster.browser.test.d.ts +2 -0
  265. package/dist/__tests__/render-graph-compute-raster.browser.test.d.ts.map +1 -0
  266. package/dist/__tests__/render-graph-compute-raster.dawn.test.d.ts +2 -0
  267. package/dist/__tests__/render-graph-compute-raster.dawn.test.d.ts.map +1 -0
  268. package/dist/__tests__/render-graph-hzb-gpu.d.ts +7 -0
  269. package/dist/__tests__/render-graph-hzb-gpu.d.ts.map +1 -0
  270. package/dist/__tests__/render-graph-hzb.browser.test.d.ts +2 -0
  271. package/dist/__tests__/render-graph-hzb.browser.test.d.ts.map +1 -0
  272. package/dist/__tests__/render-graph-hzb.dawn.test.d.ts +2 -0
  273. package/dist/__tests__/render-graph-hzb.dawn.test.d.ts.map +1 -0
  274. package/dist/__tests__/render-graph-primitives.unit.test.d.ts +2 -0
  275. package/dist/__tests__/render-graph-primitives.unit.test.d.ts.map +1 -0
  276. package/dist/__tests__/render-graph-storage-indirect-gpu.d.ts +12 -0
  277. package/dist/__tests__/render-graph-storage-indirect-gpu.d.ts.map +1 -0
  278. package/dist/__tests__/render-graph-storage-indirect.browser.test.d.ts +2 -0
  279. package/dist/__tests__/render-graph-storage-indirect.browser.test.d.ts.map +1 -0
  280. package/dist/__tests__/render-graph-storage-indirect.dawn.test.d.ts +2 -0
  281. package/dist/__tests__/render-graph-storage-indirect.dawn.test.d.ts.map +1 -0
  282. package/dist/__tests__/render-scene-projection.unit.test.d.ts +2 -0
  283. package/dist/__tests__/render-scene-projection.unit.test.d.ts.map +1 -0
  284. package/dist/__tests__/renderer-factory-material-contract.unit.test.d.ts +2 -0
  285. package/dist/__tests__/renderer-factory-material-contract.unit.test.d.ts.map +1 -0
  286. package/dist/__tests__/renderer-lifecycle.integration.test.d.ts +2 -0
  287. package/dist/__tests__/renderer-lifecycle.integration.test.d.ts.map +1 -0
  288. package/dist/__tests__/renderer-render-scene-public.test-d.d.ts +2 -0
  289. package/dist/__tests__/renderer-render-scene-public.test-d.d.ts.map +1 -0
  290. package/dist/__tests__/rhi-backend-package-boundary.unit.test.d.ts +2 -0
  291. package/dist/__tests__/rhi-backend-package-boundary.unit.test.d.ts.map +1 -0
  292. package/dist/__tests__/scene-temporal-coverage.unit.test.d.ts +2 -0
  293. package/dist/__tests__/scene-temporal-coverage.unit.test.d.ts.map +1 -0
  294. package/dist/__tests__/scriptable-pack-consumer.test-d.d.ts +2 -0
  295. package/dist/__tests__/scriptable-pack-consumer.test-d.d.ts.map +1 -0
  296. package/dist/__tests__/shader-stage-surface.unit.test.d.ts +2 -0
  297. package/dist/__tests__/shader-stage-surface.unit.test.d.ts.map +1 -0
  298. package/dist/__tests__/ssao-kernel-surface.unit.test.d.ts +2 -0
  299. package/dist/__tests__/ssao-kernel-surface.unit.test.d.ts.map +1 -0
  300. package/dist/__tests__/sun-snapshot-consumers.test.d.ts +2 -0
  301. package/dist/__tests__/sun-snapshot-consumers.test.d.ts.map +1 -0
  302. package/dist/__tests__/surface-backend-kind-owner.test-d.d.ts +2 -0
  303. package/dist/__tests__/surface-backend-kind-owner.test-d.d.ts.map +1 -0
  304. package/dist/__tests__/surface-configuration.unit.test.d.ts +2 -0
  305. package/dist/__tests__/surface-configuration.unit.test.d.ts.map +1 -0
  306. package/dist/__tests__/taa-history-store.unit.test.d.ts +2 -0
  307. package/dist/__tests__/taa-history-store.unit.test.d.ts.map +1 -0
  308. package/dist/__tests__/temporal-frame-transaction.integration.test.d.ts +2 -0
  309. package/dist/__tests__/temporal-frame-transaction.integration.test.d.ts.map +1 -0
  310. package/dist/__tests__/temporal-inspection.unit.test.d.ts +2 -0
  311. package/dist/__tests__/temporal-inspection.unit.test.d.ts.map +1 -0
  312. package/dist/__tests__/texture-usage-surface.unit.test.d.ts +2 -0
  313. package/dist/__tests__/texture-usage-surface.unit.test.d.ts.map +1 -0
  314. package/dist/__tests__/typed-pipeline-topology.unit.test.d.ts +2 -0
  315. package/dist/__tests__/typed-pipeline-topology.unit.test.d.ts.map +1 -0
  316. package/dist/__tests__/urp-pipeline-targets.unit.test.d.ts +2 -0
  317. package/dist/__tests__/urp-pipeline-targets.unit.test.d.ts.map +1 -0
  318. package/dist/__tests__/urp-pipeline.unit.test.d.ts +2 -0
  319. package/dist/__tests__/urp-pipeline.unit.test.d.ts.map +1 -0
  320. package/dist/__tests__/vertex-color-pipeline.unit.test.d.ts +2 -0
  321. package/dist/__tests__/vertex-color-pipeline.unit.test.d.ts.map +1 -0
  322. package/dist/__tests__/vertex-layout-consumer.unit.test.d.ts +2 -0
  323. package/dist/__tests__/vertex-layout-consumer.unit.test.d.ts.map +1 -0
  324. package/dist/__tests__/visibility-diagnostics.integration.test.d.ts +2 -0
  325. package/dist/__tests__/visibility-diagnostics.integration.test.d.ts.map +1 -0
  326. package/dist/__tests__/visibility-extract.integration.test.d.ts +2 -0
  327. package/dist/__tests__/visibility-extract.integration.test.d.ts.map +1 -0
  328. package/dist/__tests__/visibility-extract.perf.test.d.ts +2 -0
  329. package/dist/__tests__/visibility-extract.perf.test.d.ts.map +1 -0
  330. package/dist/__tests__/visibility-feature-dedup.integration.test.d.ts +2 -0
  331. package/dist/__tests__/visibility-feature-dedup.integration.test.d.ts.map +1 -0
  332. package/dist/__tests__/visibility-hidden-report.unit.test.d.ts +2 -0
  333. package/dist/__tests__/visibility-hidden-report.unit.test.d.ts.map +1 -0
  334. package/dist/__tests__/visibility-instances.integration.test.d.ts +2 -0
  335. package/dist/__tests__/visibility-instances.integration.test.d.ts.map +1 -0
  336. package/dist/__tests__/visibility-multi-world.integration.test.d.ts +2 -0
  337. package/dist/__tests__/visibility-multi-world.integration.test.d.ts.map +1 -0
  338. package/dist/__tests__/visibility-producer-matrix.integration.test.d.ts +2 -0
  339. package/dist/__tests__/visibility-producer-matrix.integration.test.d.ts.map +1 -0
  340. package/dist/__tests__/visibility-producer-matrix.unit.test.d.ts +2 -0
  341. package/dist/__tests__/visibility-producer-matrix.unit.test.d.ts.map +1 -0
  342. package/dist/__tests__/visibility-public-surface.test-d.d.ts +2 -0
  343. package/dist/__tests__/visibility-public-surface.test-d.d.ts.map +1 -0
  344. package/dist/__tests__/visibility-resolver.unit.test.d.ts +2 -0
  345. package/dist/__tests__/visibility-resolver.unit.test.d.ts.map +1 -0
  346. package/dist/__tests__/visibility-snapshot-contract.unit.test.d.ts +2 -0
  347. package/dist/__tests__/visibility-snapshot-contract.unit.test.d.ts.map +1 -0
  348. package/dist/__tests__/vocabulary.unit.test.d.ts +2 -0
  349. package/dist/__tests__/vocabulary.unit.test.d.ts.map +1 -0
  350. package/dist/assets/__tests__/asset-decoders.unit.test.d.ts +2 -0
  351. package/dist/assets/__tests__/asset-decoders.unit.test.d.ts.map +1 -0
  352. package/dist/assets/__tests__/mipmap-generator.unit.test.d.ts +2 -0
  353. package/dist/assets/__tests__/mipmap-generator.unit.test.d.ts.map +1 -0
  354. package/dist/assets/__tests__/render-asset-port.unit.test.d.ts +2 -0
  355. package/dist/assets/__tests__/render-asset-port.unit.test.d.ts.map +1 -0
  356. package/dist/assets/__tests__/resolve-render-asset.unit.test.d.ts +2 -0
  357. package/dist/assets/__tests__/resolve-render-asset.unit.test.d.ts.map +1 -0
  358. package/dist/assets/asset-decoders.d.ts +5 -0
  359. package/dist/assets/asset-decoders.d.ts.map +1 -0
  360. package/dist/assets/render-asset-port.d.ts +17 -0
  361. package/dist/assets/render-asset-port.d.ts.map +1 -0
  362. package/dist/assets/resolve-render-asset.d.ts +6 -0
  363. package/dist/assets/resolve-render-asset.d.ts.map +1 -0
  364. package/dist/authoring.d.ts +12 -0
  365. package/dist/authoring.d.ts.map +1 -0
  366. package/dist/authoring.mjs +5 -0
  367. package/dist/authoring.mjs.map +1 -0
  368. package/dist/chunk-33UF5SAO.mjs +7397 -0
  369. package/dist/chunk-33UF5SAO.mjs.map +1 -0
  370. package/dist/chunk-A5QCJSNL.mjs +29913 -0
  371. package/dist/chunk-A5QCJSNL.mjs.map +1 -0
  372. package/dist/chunk-JMSB7AR4.mjs +3 -0
  373. package/dist/chunk-JMSB7AR4.mjs.map +1 -0
  374. package/dist/chunk-LXNGO6AO.mjs +569 -0
  375. package/dist/chunk-LXNGO6AO.mjs.map +1 -0
  376. package/dist/chunk-QRXPSX4X.mjs +659 -0
  377. package/dist/chunk-QRXPSX4X.mjs.map +1 -0
  378. package/dist/chunk-YAKEQPWO.mjs +1439 -0
  379. package/dist/chunk-YAKEQPWO.mjs.map +1 -0
  380. package/dist/cluster-binner.d.ts +152 -0
  381. package/dist/cluster-binner.d.ts.map +1 -0
  382. package/dist/components/__tests__/atmosphere-red.test.d.ts +2 -0
  383. package/dist/components/__tests__/atmosphere-red.test.d.ts.map +1 -0
  384. package/dist/components/__tests__/atmosphere.test.d.ts +2 -0
  385. package/dist/components/__tests__/atmosphere.test.d.ts.map +1 -0
  386. package/dist/components/__tests__/fog.test-d.d.ts +2 -0
  387. package/dist/components/__tests__/fog.test-d.d.ts.map +1 -0
  388. package/dist/components/__tests__/fog.test.d.ts +2 -0
  389. package/dist/components/__tests__/fog.test.d.ts.map +1 -0
  390. package/dist/components/__tests__/schema-parity.unit.test.d.ts +2 -0
  391. package/dist/components/__tests__/schema-parity.unit.test.d.ts.map +1 -0
  392. package/dist/components/__tests__/visibility-schema.unit.test.d.ts +2 -0
  393. package/dist/components/__tests__/visibility-schema.unit.test.d.ts.map +1 -0
  394. package/dist/components/atmosphere.d.ts +18 -0
  395. package/dist/components/atmosphere.d.ts.map +1 -0
  396. package/dist/components/camera.d.ts +311 -0
  397. package/dist/components/camera.d.ts.map +1 -0
  398. package/dist/components/directional-light.d.ts +76 -0
  399. package/dist/components/directional-light.d.ts.map +1 -0
  400. package/dist/components/environment.d.ts +116 -0
  401. package/dist/components/environment.d.ts.map +1 -0
  402. package/dist/components/glyph-text.d.ts +26 -0
  403. package/dist/components/glyph-text.d.ts.map +1 -0
  404. package/dist/components/index.d.ts +22 -0
  405. package/dist/components/index.d.ts.map +1 -0
  406. package/dist/components/instances.d.ts +41 -0
  407. package/dist/components/instances.d.ts.map +1 -0
  408. package/dist/components/layer.d.ts +34 -0
  409. package/dist/components/layer.d.ts.map +1 -0
  410. package/dist/components/light-helpers.d.ts +53 -0
  411. package/dist/components/light-helpers.d.ts.map +1 -0
  412. package/dist/components/mesh-filter.d.ts +24 -0
  413. package/dist/components/mesh-filter.d.ts.map +1 -0
  414. package/dist/components/mesh-renderer.d.ts +33 -0
  415. package/dist/components/mesh-renderer.d.ts.map +1 -0
  416. package/dist/components/point-light-shadow.d.ts +27 -0
  417. package/dist/components/point-light-shadow.d.ts.map +1 -0
  418. package/dist/components/point-light.d.ts +35 -0
  419. package/dist/components/point-light.d.ts.map +1 -0
  420. package/dist/components/post-process-params.d.ts +5 -0
  421. package/dist/components/post-process-params.d.ts.map +1 -0
  422. package/dist/components/scene-instance.d.ts +140 -0
  423. package/dist/components/scene-instance.d.ts.map +1 -0
  424. package/dist/components/sort-key.d.ts +44 -0
  425. package/dist/components/sort-key.d.ts.map +1 -0
  426. package/dist/components/spot-light.d.ts +81 -0
  427. package/dist/components/spot-light.d.ts.map +1 -0
  428. package/dist/components/sprite-animation.d.ts +75 -0
  429. package/dist/components/sprite-animation.d.ts.map +1 -0
  430. package/dist/components/sprite-instances.d.ts +51 -0
  431. package/dist/components/sprite-instances.d.ts.map +1 -0
  432. package/dist/components/sprite-playback-mode.d.ts +37 -0
  433. package/dist/components/sprite-playback-mode.d.ts.map +1 -0
  434. package/dist/components/sprite-region-override.d.ts +47 -0
  435. package/dist/components/sprite-region-override.d.ts.map +1 -0
  436. package/dist/components/tile-layer.d.ts +101 -0
  437. package/dist/components/tile-layer.d.ts.map +1 -0
  438. package/dist/components/tilemap.d.ts +25 -0
  439. package/dist/components/tilemap.d.ts.map +1 -0
  440. package/dist/components/visibility.d.ts +22 -0
  441. package/dist/components/visibility.d.ts.map +1 -0
  442. package/dist/construct-renderer.d.ts +2 -0
  443. package/dist/construct-renderer.d.ts.map +1 -0
  444. package/dist/construct-renderer.mjs +6 -0
  445. package/dist/construct-renderer.mjs.map +1 -0
  446. package/dist/debug-draw-glue.d.ts +56 -0
  447. package/dist/debug-draw-glue.d.ts.map +1 -0
  448. package/dist/deferred-lighting.d.ts +13 -0
  449. package/dist/deferred-lighting.d.ts.map +1 -0
  450. package/dist/dynamic-texture-store.d.ts +42 -0
  451. package/dist/dynamic-texture-store.d.ts.map +1 -0
  452. package/dist/engine-metrics.d.ts +23 -0
  453. package/dist/engine-metrics.d.ts.map +1 -0
  454. package/dist/environment/__tests__/failure-injection.test.d.ts +2 -0
  455. package/dist/environment/__tests__/failure-injection.test.d.ts.map +1 -0
  456. package/dist/environment/__tests__/generation-red.test.d.ts +2 -0
  457. package/dist/environment/__tests__/generation-red.test.d.ts.map +1 -0
  458. package/dist/environment/__tests__/generation.test.d.ts +2 -0
  459. package/dist/environment/__tests__/generation.test.d.ts.map +1 -0
  460. package/dist/environment/__tests__/graph-transaction.test.d.ts +2 -0
  461. package/dist/environment/__tests__/graph-transaction.test.d.ts.map +1 -0
  462. package/dist/environment/__tests__/inspection-red.test.d.ts +2 -0
  463. package/dist/environment/__tests__/inspection-red.test.d.ts.map +1 -0
  464. package/dist/environment/__tests__/lifecycle-evidence.test.d.ts +2 -0
  465. package/dist/environment/__tests__/lifecycle-evidence.test.d.ts.map +1 -0
  466. package/dist/environment/__tests__/pipeline-topology.test.d.ts +2 -0
  467. package/dist/environment/__tests__/pipeline-topology.test.d.ts.map +1 -0
  468. package/dist/environment/__tests__/readiness.test.d.ts +2 -0
  469. package/dist/environment/__tests__/readiness.test.d.ts.map +1 -0
  470. package/dist/environment/__tests__/recovery.test.d.ts +2 -0
  471. package/dist/environment/__tests__/recovery.test.d.ts.map +1 -0
  472. package/dist/environment/__tests__/resize-dispose.test.d.ts +2 -0
  473. package/dist/environment/__tests__/resize-dispose.test.d.ts.map +1 -0
  474. package/dist/environment/__tests__/signature-red.test.d.ts +2 -0
  475. package/dist/environment/__tests__/signature-red.test.d.ts.map +1 -0
  476. package/dist/environment/__tests__/signature.test.d.ts +2 -0
  477. package/dist/environment/__tests__/signature.test.d.ts.map +1 -0
  478. package/dist/environment/descriptors.d.ts +2 -0
  479. package/dist/environment/descriptors.d.ts.map +1 -0
  480. package/dist/environment/extracted-environment.d.ts +53 -0
  481. package/dist/environment/extracted-environment.d.ts.map +1 -0
  482. package/dist/environment/generation.d.ts +62 -0
  483. package/dist/environment/generation.d.ts.map +1 -0
  484. package/dist/environment/gpu-bundle.d.ts +23 -0
  485. package/dist/environment/gpu-bundle.d.ts.map +1 -0
  486. package/dist/environment/graph.d.ts +108 -0
  487. package/dist/environment/graph.d.ts.map +1 -0
  488. package/dist/environment/inspection.d.ts +48 -0
  489. package/dist/environment/inspection.d.ts.map +1 -0
  490. package/dist/environment/lifecycle.d.ts +22 -0
  491. package/dist/environment/lifecycle.d.ts.map +1 -0
  492. package/dist/environment/readiness.d.ts +29 -0
  493. package/dist/environment/readiness.d.ts.map +1 -0
  494. package/dist/environment/signature.d.ts +70 -0
  495. package/dist/environment/signature.d.ts.map +1 -0
  496. package/dist/errors/ecs-validation.d.ts +3 -0
  497. package/dist/errors/ecs-validation.d.ts.map +1 -0
  498. package/dist/errors/index.d.ts +4 -0
  499. package/dist/errors/index.d.ts.map +1 -0
  500. package/dist/errors/recover.d.ts +78 -0
  501. package/dist/errors/recover.d.ts.map +1 -0
  502. package/dist/errors/render.d.ts +633 -0
  503. package/dist/errors/render.d.ts.map +1 -0
  504. package/dist/extract/__tests__/directional-csm-domain.unit.test.d.ts +2 -0
  505. package/dist/extract/__tests__/directional-csm-domain.unit.test.d.ts.map +1 -0
  506. package/dist/extract/__tests__/environment-source.test.d.ts +2 -0
  507. package/dist/extract/__tests__/environment-source.test.d.ts.map +1 -0
  508. package/dist/extract/__tests__/schema-readers.unit.test.d.ts +2 -0
  509. package/dist/extract/__tests__/schema-readers.unit.test.d.ts.map +1 -0
  510. package/dist/extract/directional-csm.d.ts +129 -0
  511. package/dist/extract/directional-csm.d.ts.map +1 -0
  512. package/dist/extract/index.d.ts +5 -0
  513. package/dist/extract/index.d.ts.map +1 -0
  514. package/dist/extract/material-interface.d.ts +56 -0
  515. package/dist/extract/material-interface.d.ts.map +1 -0
  516. package/dist/extract/schema-readers.d.ts +20 -0
  517. package/dist/extract/schema-readers.d.ts.map +1 -0
  518. package/dist/extract/visibility.d.ts +26 -0
  519. package/dist/extract/visibility.d.ts.map +1 -0
  520. package/dist/features/graph-contribution.d.ts +64 -0
  521. package/dist/features/graph-contribution.d.ts.map +1 -0
  522. package/dist/features/host.d.ts +100 -0
  523. package/dist/features/host.d.ts.map +1 -0
  524. package/dist/features/index.d.ts +21 -0
  525. package/dist/features/index.d.ts.map +1 -0
  526. package/dist/features/morph/index.d.ts +3 -0
  527. package/dist/features/morph/index.d.ts.map +1 -0
  528. package/dist/features/morph/morph-bounds.d.ts +42 -0
  529. package/dist/features/morph/morph-bounds.d.ts.map +1 -0
  530. package/dist/features/morph/morph-feature.d.ts +45 -0
  531. package/dist/features/morph/morph-feature.d.ts.map +1 -0
  532. package/dist/features/prepared-gpu-work.d.ts +113 -0
  533. package/dist/features/prepared-gpu-work.d.ts.map +1 -0
  534. package/dist/features/prepared-graphics-store.d.ts +65 -0
  535. package/dist/features/prepared-graphics-store.d.ts.map +1 -0
  536. package/dist/features/prepared-graphics.d.ts +225 -0
  537. package/dist/features/prepared-graphics.d.ts.map +1 -0
  538. package/dist/features/render-graph-compute.d.ts +13 -0
  539. package/dist/features/render-graph-compute.d.ts.map +1 -0
  540. package/dist/features/render-graph-contribution.d.ts +15 -0
  541. package/dist/features/render-graph-contribution.d.ts.map +1 -0
  542. package/dist/features/render-graph-raster.d.ts +34 -0
  543. package/dist/features/render-graph-raster.d.ts.map +1 -0
  544. package/dist/features/render-graph-resources.d.ts +11 -0
  545. package/dist/features/render-graph-resources.d.ts.map +1 -0
  546. package/dist/features/scene-temporal.d.ts +45 -0
  547. package/dist/features/scene-temporal.d.ts.map +1 -0
  548. package/dist/features/taa-resolve.d.ts +21 -0
  549. package/dist/features/taa-resolve.d.ts.map +1 -0
  550. package/dist/features/targets.d.ts +32 -0
  551. package/dist/features/targets.d.ts.map +1 -0
  552. package/dist/features/types.d.ts +247 -0
  553. package/dist/features/types.d.ts.map +1 -0
  554. package/dist/frame-chain.d.ts +14 -0
  555. package/dist/frame-chain.d.ts.map +1 -0
  556. package/dist/fullscreen-post-process-pass.d.ts +178 -0
  557. package/dist/fullscreen-post-process-pass.d.ts.map +1 -0
  558. package/dist/glyph-text-layout-system.d.ts +18 -0
  559. package/dist/glyph-text-layout-system.d.ts.map +1 -0
  560. package/dist/gpu-driven/batch-topology.d.ts +124 -0
  561. package/dist/gpu-driven/batch-topology.d.ts.map +1 -0
  562. package/dist/gpu-driven/production-raster.d.ts +150 -0
  563. package/dist/gpu-driven/production-raster.d.ts.map +1 -0
  564. package/dist/gpu-driven/view-gpu.d.ts +141 -0
  565. package/dist/gpu-driven/view-gpu.d.ts.map +1 -0
  566. package/dist/gpu-driven/view-reference.d.ts +15 -0
  567. package/dist/gpu-driven/view-reference.d.ts.map +1 -0
  568. package/dist/gpu-resource-store.d.ts +294 -0
  569. package/dist/gpu-resource-store.d.ts.map +1 -0
  570. package/dist/gpu-resource.d.ts +73 -0
  571. package/dist/gpu-resource.d.ts.map +1 -0
  572. package/dist/gpu-scene-schema.d.ts +171 -0
  573. package/dist/gpu-scene-schema.d.ts.map +1 -0
  574. package/dist/gpu-scene.d.ts +81 -0
  575. package/dist/gpu-scene.d.ts.map +1 -0
  576. package/dist/gpu-stage.d.ts +4 -0
  577. package/dist/gpu-stage.d.ts.map +1 -0
  578. package/dist/gpu-texture-usage.d.ts +6 -0
  579. package/dist/gpu-texture-usage.d.ts.map +1 -0
  580. package/dist/gpu-usage.d.ts +9 -0
  581. package/dist/gpu-usage.d.ts.map +1 -0
  582. package/dist/hdrp-buffers.d.ts +213 -0
  583. package/dist/hdrp-buffers.d.ts.map +1 -0
  584. package/dist/hdrp-cluster-membership.d.ts +2 -0
  585. package/dist/hdrp-cluster-membership.d.ts.map +1 -0
  586. package/dist/hdrp-pipeline.d.ts +36 -0
  587. package/dist/hdrp-pipeline.d.ts.map +1 -0
  588. package/dist/ibl/IblPipelineCache.d.ts +274 -0
  589. package/dist/ibl/IblPipelineCache.d.ts.map +1 -0
  590. package/dist/ibl/cubemap-projection.d.ts +23 -0
  591. package/dist/ibl/cubemap-projection.d.ts.map +1 -0
  592. package/dist/ibl/face-uniforms.d.ts +65 -0
  593. package/dist/ibl/face-uniforms.d.ts.map +1 -0
  594. package/dist/ibl/skylight-bind-group.d.ts +130 -0
  595. package/dist/ibl/skylight-bind-group.d.ts.map +1 -0
  596. package/dist/index.d.ts +57 -0
  597. package/dist/index.d.ts.map +1 -0
  598. package/dist/index.mjs +5 -0
  599. package/dist/index.mjs.map +1 -0
  600. package/dist/instance-buffer-cache.d.ts +59 -0
  601. package/dist/instance-buffer-cache.d.ts.map +1 -0
  602. package/dist/internal.d.ts +74 -0
  603. package/dist/internal.d.ts.map +1 -0
  604. package/dist/internal.mjs +45 -0
  605. package/dist/internal.mjs.map +1 -0
  606. package/dist/lifecycle.d.ts +28 -0
  607. package/dist/lifecycle.d.ts.map +1 -0
  608. package/dist/light-buffer-layout.d.ts +164 -0
  609. package/dist/light-buffer-layout.d.ts.map +1 -0
  610. package/dist/materials.d.ts +50 -0
  611. package/dist/materials.d.ts.map +1 -0
  612. package/dist/mesh-material-bindings.d.ts +44 -0
  613. package/dist/mesh-material-bindings.d.ts.map +1 -0
  614. package/dist/mesh-update-errors.d.ts +71 -0
  615. package/dist/mesh-update-errors.d.ts.map +1 -0
  616. package/dist/mipmap-generator.d.ts +39 -0
  617. package/dist/mipmap-generator.d.ts.map +1 -0
  618. package/dist/param-schema-projection-census.d.ts +18 -0
  619. package/dist/param-schema-projection-census.d.ts.map +1 -0
  620. package/dist/pbr-pipeline.d.ts +253 -0
  621. package/dist/pbr-pipeline.d.ts.map +1 -0
  622. package/dist/persistent-render-scene.d.ts +158 -0
  623. package/dist/persistent-render-scene.d.ts.map +1 -0
  624. package/dist/pipeline/index.d.ts +11 -0
  625. package/dist/pipeline/index.d.ts.map +1 -0
  626. package/dist/pipeline-builder.d.ts +122 -0
  627. package/dist/pipeline-builder.d.ts.map +1 -0
  628. package/dist/pipeline-errors.d.ts +110 -0
  629. package/dist/pipeline-errors.d.ts.map +1 -0
  630. package/dist/pipeline-spec.d.ts +506 -0
  631. package/dist/pipeline-spec.d.ts.map +1 -0
  632. package/dist/plugin.d.ts +17 -0
  633. package/dist/plugin.d.ts.map +1 -0
  634. package/dist/post-process-errors.d.ts +182 -0
  635. package/dist/post-process-errors.d.ts.map +1 -0
  636. package/dist/prepare/__tests__/schema-readers.unit.test.d.ts +2 -0
  637. package/dist/prepare/__tests__/schema-readers.unit.test.d.ts.map +1 -0
  638. package/dist/prepare/index.d.ts +4 -0
  639. package/dist/prepare/index.d.ts.map +1 -0
  640. package/dist/prepare/prepared-graphics-resolver.d.ts +62 -0
  641. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -0
  642. package/dist/prepare/schema-readers.d.ts +11 -0
  643. package/dist/prepare/schema-readers.d.ts.map +1 -0
  644. package/dist/record/__tests__/environment-record.test.d.ts +2 -0
  645. package/dist/record/__tests__/environment-record.test.d.ts.map +1 -0
  646. package/dist/record/__tests__/frame-targets.unit.test.d.ts +2 -0
  647. package/dist/record/__tests__/frame-targets.unit.test.d.ts.map +1 -0
  648. package/dist/record/__tests__/material-bg-cache.unit.test.d.ts +2 -0
  649. package/dist/record/__tests__/material-bg-cache.unit.test.d.ts.map +1 -0
  650. package/dist/record/__tests__/material-slot-plan.unit.test.d.ts +2 -0
  651. package/dist/record/__tests__/material-slot-plan.unit.test.d.ts.map +1 -0
  652. package/dist/record/__tests__/membership-timing.unit.test.d.ts +2 -0
  653. package/dist/record/__tests__/membership-timing.unit.test.d.ts.map +1 -0
  654. package/dist/record/__tests__/render-submission.integration.test.d.ts +2 -0
  655. package/dist/record/__tests__/render-submission.integration.test.d.ts.map +1 -0
  656. package/dist/record/__tests__/render-submission.perf.test.d.ts +2 -0
  657. package/dist/record/__tests__/render-submission.perf.test.d.ts.map +1 -0
  658. package/dist/record/__tests__/shadow-submit-mode.unit.test.d.ts +2 -0
  659. package/dist/record/__tests__/shadow-submit-mode.unit.test.d.ts.map +1 -0
  660. package/dist/record/__tests__/video-texture-view.unit.test.d.ts +2 -0
  661. package/dist/record/__tests__/video-texture-view.unit.test.d.ts.map +1 -0
  662. package/dist/record/__tests__/view-ubo-layout.unit.test.d.ts +2 -0
  663. package/dist/record/__tests__/view-ubo-layout.unit.test.d.ts.map +1 -0
  664. package/dist/record/frame-lighting.d.ts +187 -0
  665. package/dist/record/frame-lighting.d.ts.map +1 -0
  666. package/dist/record/frame-observation.d.ts +38 -0
  667. package/dist/record/frame-observation.d.ts.map +1 -0
  668. package/dist/record/frame-snapshot.d.ts +429 -0
  669. package/dist/record/frame-snapshot.d.ts.map +1 -0
  670. package/dist/record/frame-targets.d.ts +53 -0
  671. package/dist/record/frame-targets.d.ts.map +1 -0
  672. package/dist/record/frame.d.ts +59 -0
  673. package/dist/record/frame.d.ts.map +1 -0
  674. package/dist/record/helpers.d.ts +101 -0
  675. package/dist/record/helpers.d.ts.map +1 -0
  676. package/dist/record/index.d.ts +15 -0
  677. package/dist/record/index.d.ts.map +1 -0
  678. package/dist/record/main-pass-geometry.d.ts +20 -0
  679. package/dist/record/main-pass-geometry.d.ts.map +1 -0
  680. package/dist/record/main-pass-material.d.ts +173 -0
  681. package/dist/record/main-pass-material.d.ts.map +1 -0
  682. package/dist/record/main-pass-skin.d.ts +46 -0
  683. package/dist/record/main-pass-skin.d.ts.map +1 -0
  684. package/dist/record/main-pass-sprite-draws.d.ts +18 -0
  685. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -0
  686. package/dist/record/main-pass-sprite.d.ts +69 -0
  687. package/dist/record/main-pass-sprite.d.ts.map +1 -0
  688. package/dist/record/main-pass.d.ts +20 -0
  689. package/dist/record/main-pass.d.ts.map +1 -0
  690. package/dist/record/material-payload.d.ts +43 -0
  691. package/dist/record/material-payload.d.ts.map +1 -0
  692. package/dist/record/material-slot-plan.d.ts +26 -0
  693. package/dist/record/material-slot-plan.d.ts.map +1 -0
  694. package/dist/record/membership-timing.d.ts +232 -0
  695. package/dist/record/membership-timing.d.ts.map +1 -0
  696. package/dist/record/mesh-ssbo.d.ts +173 -0
  697. package/dist/record/mesh-ssbo.d.ts.map +1 -0
  698. package/dist/record/shadow-caster-uniform.d.ts +8 -0
  699. package/dist/record/shadow-caster-uniform.d.ts.map +1 -0
  700. package/dist/record/shadow-pass.d.ts +125 -0
  701. package/dist/record/shadow-pass.d.ts.map +1 -0
  702. package/dist/record/shadow-submit-mode.d.ts +66 -0
  703. package/dist/record/shadow-submit-mode.d.ts.map +1 -0
  704. package/dist/record/skybox-post-pass.d.ts +33 -0
  705. package/dist/record/skybox-post-pass.d.ts.map +1 -0
  706. package/dist/record/typed-frame-graph.d.ts +13 -0
  707. package/dist/record/typed-frame-graph.d.ts.map +1 -0
  708. package/dist/record/view-ubo.d.ts +39 -0
  709. package/dist/record/view-ubo.d.ts.map +1 -0
  710. package/dist/render-data.d.ts +143 -0
  711. package/dist/render-data.d.ts.map +1 -0
  712. package/dist/render-dispatch-membership.d.ts +47 -0
  713. package/dist/render-dispatch-membership.d.ts.map +1 -0
  714. package/dist/render-graph-primitives.d.ts +424 -0
  715. package/dist/render-graph-primitives.d.ts.map +1 -0
  716. package/dist/render-pipeline-context.d.ts +289 -0
  717. package/dist/render-pipeline-context.d.ts.map +1 -0
  718. package/dist/render-pipeline-target.d.ts +16 -0
  719. package/dist/render-pipeline-target.d.ts.map +1 -0
  720. package/dist/render-pipeline.d.ts +149 -0
  721. package/dist/render-pipeline.d.ts.map +1 -0
  722. package/dist/render-scene-projection.d.ts +174 -0
  723. package/dist/render-scene-projection.d.ts.map +1 -0
  724. package/dist/render-system-extract.d.ts +1096 -0
  725. package/dist/render-system-extract.d.ts.map +1 -0
  726. package/dist/render-system-fold.d.ts +266 -0
  727. package/dist/render-system-fold.d.ts.map +1 -0
  728. package/dist/render-system.d.ts +814 -0
  729. package/dist/render-system.d.ts.map +1 -0
  730. package/dist/renderer/asset-service.d.ts +10 -0
  731. package/dist/renderer/asset-service.d.ts.map +1 -0
  732. package/dist/renderer/backend-selection.d.ts +26 -0
  733. package/dist/renderer/backend-selection.d.ts.map +1 -0
  734. package/dist/renderer/environment-classify.d.ts +6 -0
  735. package/dist/renderer/environment-classify.d.ts.map +1 -0
  736. package/dist/renderer/environment-error.d.ts +13 -0
  737. package/dist/renderer/environment-error.d.ts.map +1 -0
  738. package/dist/renderer/material/assembly.d.ts +22 -0
  739. package/dist/renderer/material/assembly.d.ts.map +1 -0
  740. package/dist/renderer/material/pipeline-projection.d.ts +91 -0
  741. package/dist/renderer/material/pipeline-projection.d.ts.map +1 -0
  742. package/dist/renderer/renderer-factory.d.ts +283 -0
  743. package/dist/renderer/renderer-factory.d.ts.map +1 -0
  744. package/dist/renderer.d.ts +771 -0
  745. package/dist/renderer.d.ts.map +1 -0
  746. package/dist/scene-instances/post-spawn-resolve-joints.d.ts +33 -0
  747. package/dist/scene-instances/post-spawn-resolve-joints.d.ts.map +1 -0
  748. package/dist/scene-utils/collect-subtree.d.ts +11 -0
  749. package/dist/scene-utils/collect-subtree.d.ts.map +1 -0
  750. package/dist/shadow-atlas.d.ts +117 -0
  751. package/dist/shadow-atlas.d.ts.map +1 -0
  752. package/dist/ssao-buffers.d.ts +31 -0
  753. package/dist/ssao-buffers.d.ts.map +1 -0
  754. package/dist/ssao-config.d.ts +28 -0
  755. package/dist/ssao-config.d.ts.map +1 -0
  756. package/dist/ssao-data.d.ts +34 -0
  757. package/dist/ssao-data.d.ts.map +1 -0
  758. package/dist/surface-format.d.ts +10 -0
  759. package/dist/surface-format.d.ts.map +1 -0
  760. package/dist/systems/active-camera.d.ts +53 -0
  761. package/dist/systems/active-camera.d.ts.map +1 -0
  762. package/dist/systems/pass-selector.d.ts +24 -0
  763. package/dist/systems/pass-selector.d.ts.map +1 -0
  764. package/dist/systems/skin-palette-allocator.d.ts +51 -0
  765. package/dist/systems/skin-palette-allocator.d.ts.map +1 -0
  766. package/dist/systems/transparent-sort-config.d.ts +91 -0
  767. package/dist/systems/transparent-sort-config.d.ts.map +1 -0
  768. package/dist/taa-history-store.d.ts +88 -0
  769. package/dist/taa-history-store.d.ts.map +1 -0
  770. package/dist/temporal-coverage.d.ts +35 -0
  771. package/dist/temporal-coverage.d.ts.map +1 -0
  772. package/dist/temporal-frame-transaction.d.ts +92 -0
  773. package/dist/temporal-frame-transaction.d.ts.map +1 -0
  774. package/dist/temporal-inspection.d.ts +57 -0
  775. package/dist/temporal-inspection.d.ts.map +1 -0
  776. package/dist/temporal-view.d.ts +43 -0
  777. package/dist/temporal-view.d.ts.map +1 -0
  778. package/dist/tilemap/resolve-tileset-runtime.d.ts +23 -0
  779. package/dist/tilemap/resolve-tileset-runtime.d.ts.map +1 -0
  780. package/dist/tilemap-chunk-extract-system.d.ts +142 -0
  781. package/dist/tilemap-chunk-extract-system.d.ts.map +1 -0
  782. package/dist/typed-render-graph-primitives.d.ts +59 -0
  783. package/dist/typed-render-graph-primitives.d.ts.map +1 -0
  784. package/dist/typed-shadow-passes.d.ts +11 -0
  785. package/dist/typed-shadow-passes.d.ts.map +1 -0
  786. package/dist/urp-pipeline.d.ts +4 -0
  787. package/dist/urp-pipeline.d.ts.map +1 -0
  788. package/package.json +94 -0
  789. package/src/__tests__/analytic-sky-contract.test.ts +27 -0
  790. package/src/__tests__/analytic-sky-falsify-carriers.mjs +43 -0
  791. package/src/__tests__/analytic-sky-falsify.test.ts +94 -0
  792. package/src/__tests__/assets-runtime-handoff.integration.test.ts +36 -0
  793. package/src/__tests__/authoring-path.unit.test.ts +14 -0
  794. package/src/__tests__/backend-selection-rhi-capture.test-d.ts +21 -0
  795. package/src/__tests__/backend-selection.integration.test.ts +11 -0
  796. package/src/__tests__/callback-inference.test-d.ts +5 -0
  797. package/src/__tests__/camera-temporal.test-d.ts +8 -0
  798. package/src/__tests__/camera-temporal.test.ts +188 -0
  799. package/src/__tests__/cluster-binner.test.ts +272 -0
  800. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +17 -0
  801. package/src/__tests__/construct-renderer-identity.unit.test.ts +10 -0
  802. package/src/__tests__/direct-extract-consumer-contract.test-d.ts +34 -0
  803. package/src/__tests__/direct-light-snapshot.unit.test.ts +71 -0
  804. package/src/__tests__/directional-shadow-probe-atlas-layout.unit.test.ts +18 -0
  805. package/src/__tests__/environment-inspection.test.ts +45 -0
  806. package/src/__tests__/factory-contract.integration.test.ts +331 -0
  807. package/src/__tests__/fallback-row-stride-surface.unit.test.ts +18 -0
  808. package/src/__tests__/fog-ai-user.fixture.ts +350 -0
  809. package/src/__tests__/fog-custom-render-feature.fixture.ts +33 -0
  810. package/src/__tests__/fog-custom-render-feature.integration.test.ts +76 -0
  811. package/src/__tests__/fog-inspection.integration.test.ts +127 -0
  812. package/src/__tests__/fog-owner-state.integration.test.ts +112 -0
  813. package/src/__tests__/fog-pipeline-topology.integration.test.ts +93 -0
  814. package/src/__tests__/fog-public-surface.integration.test.ts +60 -0
  815. package/src/__tests__/fog-ray-contract.integration.test.ts +73 -0
  816. package/src/__tests__/fog-source-absence.integration.test.ts +51 -0
  817. package/src/__tests__/fog-temporal.integration.test.ts +115 -0
  818. package/src/__tests__/frame-chain.unit.test.ts +26 -0
  819. package/src/__tests__/frame-observation.contract.test.ts +103 -0
  820. package/src/__tests__/glyph-text-layout-recovery.unit.test.ts +172 -0
  821. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +347 -0
  822. package/src/__tests__/gpu-driven-indirect-raster.browser.test.ts +12 -0
  823. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +16 -0
  824. package/src/__tests__/gpu-driven-production.integration.test.ts +609 -0
  825. package/src/__tests__/gpu-driven-scaling.unit.test.ts +90 -0
  826. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +192 -0
  827. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +128 -0
  828. package/src/__tests__/gpu-driven-view.browser.test.ts +19 -0
  829. package/src/__tests__/gpu-driven-view.dawn.test.ts +17 -0
  830. package/src/__tests__/gpu-driven-view.unit.test.ts +455 -0
  831. package/src/__tests__/gpu-resource-store-stride.unit.test.ts +59 -0
  832. package/src/__tests__/gpu-scene-field-type-owner.test-d.ts +27 -0
  833. package/src/__tests__/gpu-scene-render-graph-gpu.ts +309 -0
  834. package/src/__tests__/gpu-scene-render-graph.browser.test.ts +19 -0
  835. package/src/__tests__/gpu-scene-render-graph.dawn.test.ts +19 -0
  836. package/src/__tests__/gpu-scene-schema.unit.test.ts +16 -0
  837. package/src/__tests__/gpu-scene.dawn.test.ts +209 -0
  838. package/src/__tests__/gpu-scene.unit.test.ts +161 -0
  839. package/src/__tests__/hdrp-pipeline.unit.test.ts +100 -0
  840. package/src/__tests__/ibl/ibl-fallback.unit.test.ts +23 -0
  841. package/src/__tests__/infinite-grid-no-vertex-probe.unit.test.ts +124 -0
  842. package/src/__tests__/injection-kind-owner.test-d.ts +14 -0
  843. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +719 -0
  844. package/src/__tests__/light-buffer-layout.unit.test.ts +85 -0
  845. package/src/__tests__/material-contract-inventory.unit.test.ts +24 -0
  846. package/src/__tests__/material-cooked-projection.unit.test.ts +177 -0
  847. package/src/__tests__/material-derived-error-stop-draw.integration.test.ts +69 -0
  848. package/src/__tests__/material-derived-payload.integration.test.ts +164 -0
  849. package/src/__tests__/material-snapshot-mutation.integration.test.ts +58 -0
  850. package/src/__tests__/material-static-dynamic-route.unit.test.ts +113 -0
  851. package/src/__tests__/material-stride-surface.unit.test.ts +32 -0
  852. package/src/__tests__/materials.unit.test.ts +25 -0
  853. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +107 -0
  854. package/src/__tests__/mesh-material-bindings.unit.test.ts +89 -0
  855. package/src/__tests__/mesh-stride-surface.unit.test.ts +16 -0
  856. package/src/__tests__/morph-culling-reentry.dawn.test.ts +166 -0
  857. package/src/__tests__/morph-feature-gpu-work.integration.test.ts +78 -0
  858. package/src/__tests__/morph-visual-evidence.integration.test.ts +66 -0
  859. package/src/__tests__/nineslice-handle-surface.unit.test.ts +14 -0
  860. package/src/__tests__/param-schema-projection-census.unit.test.ts +40 -0
  861. package/src/__tests__/persistent-render-scene.unit.test.ts +1311 -0
  862. package/src/__tests__/pipeline-error-policy-owner.unit.test.ts +167 -0
  863. package/src/__tests__/pipeline-pass-trace.test.ts +92 -0
  864. package/src/__tests__/pipeline-stage-owner.test-d.ts +13 -0
  865. package/src/__tests__/plugin.test.ts +25 -0
  866. package/src/__tests__/post-process-error-policy-owner.unit.test.ts +208 -0
  867. package/src/__tests__/prepared-buffer-kind-owner.test-d.ts +29 -0
  868. package/src/__tests__/prepared-graphics-backend-matrix.unit.test.ts +129 -0
  869. package/src/__tests__/prepared-graphics-capability.unit.test.ts +140 -0
  870. package/src/__tests__/prepared-graphics-generation.unit.test.ts +175 -0
  871. package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +206 -0
  872. package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +29 -0
  873. package/src/__tests__/prepared-graphics-resolver-recording.unit.test.ts +180 -0
  874. package/src/__tests__/prepared-graphics-resolver.unit.test.ts +287 -0
  875. package/src/__tests__/prepared-graphics-snapshot.unit.test.ts +105 -0
  876. package/src/__tests__/prepared-graphics-store.unit.test.ts +154 -0
  877. package/src/__tests__/prepared-graphics-validation.unit.test.ts +176 -0
  878. package/src/__tests__/profiler-default-off.test.ts +22 -0
  879. package/src/__tests__/profiler-phase-catalog.test.ts +66 -0
  880. package/src/__tests__/profiler-phase-integration.test.ts +70 -0
  881. package/src/__tests__/profiler-phase-token.test.ts +84 -0
  882. package/src/__tests__/public-pipeline-id.test-d.ts +6 -0
  883. package/src/__tests__/public-surface.test-d.ts +32 -0
  884. package/src/__tests__/recover-error-policy-owner.unit.test.ts +127 -0
  885. package/src/__tests__/render-dispatch-membership.unit.test.ts +117 -0
  886. package/src/__tests__/render-error-code-owner.test-d.ts +115 -0
  887. package/src/__tests__/render-error-detail-ssot.test-d.ts +234 -0
  888. package/src/__tests__/render-error-exhaustive.test-d.ts +84 -0
  889. package/src/__tests__/render-feature-assembly.test-d.ts +51 -0
  890. package/src/__tests__/render-feature-construction.unit.test.ts +124 -0
  891. package/src/__tests__/render-feature-context.test-d.ts +81 -0
  892. package/src/__tests__/render-feature-contract.test-d.ts +58 -0
  893. package/src/__tests__/render-feature-diagnostics.unit.test.ts +151 -0
  894. package/src/__tests__/render-feature-docs.unit.test.ts +88 -0
  895. package/src/__tests__/render-feature-error-code-owner.test-d.ts +27 -0
  896. package/src/__tests__/render-feature-errors.unit.test.ts +47 -0
  897. package/src/__tests__/render-feature-gpu-buffer-usage-owner.test-d.ts +46 -0
  898. package/src/__tests__/render-feature-gpu-work.integration.test.ts +377 -0
  899. package/src/__tests__/render-feature-graph.integration.test.ts +205 -0
  900. package/src/__tests__/render-feature-graphics-staging.unit.test.ts +200 -0
  901. package/src/__tests__/render-feature-order.unit.test.ts +66 -0
  902. package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +101 -0
  903. package/src/__tests__/render-feature-prepared-errors.test-d.ts +82 -0
  904. package/src/__tests__/render-feature-prepared-graphics-docs.unit.test.ts +141 -0
  905. package/src/__tests__/render-feature-prepared-graphics-isolation.unit.test.ts +122 -0
  906. package/src/__tests__/render-feature-prepared-graphics-negative.test-d.ts +59 -0
  907. package/src/__tests__/render-feature-prepared-graphics-recording.unit.test.ts +107 -0
  908. package/src/__tests__/render-feature-prepared-graphics.test-d.ts +96 -0
  909. package/src/__tests__/render-feature-prepared-graphics.unit.test.ts +121 -0
  910. package/src/__tests__/render-feature-public-surface.test-d.ts +75 -0
  911. package/src/__tests__/render-feature-retry.unit.test.ts +128 -0
  912. package/src/__tests__/render-feature-scheduling.unit.test.ts +122 -0
  913. package/src/__tests__/render-feature-stage-failed-policy-owner.unit.test.ts +146 -0
  914. package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +236 -0
  915. package/src/__tests__/render-feature-staging.unit.test.ts +94 -0
  916. package/src/__tests__/render-feature-visibility-context.test-d.ts +38 -0
  917. package/src/__tests__/render-feature-visibility-context.unit.test.ts +38 -0
  918. package/src/__tests__/render-feature-zero-work.unit.test.ts +82 -0
  919. package/src/__tests__/render-graph-compute-raster-gpu.ts +342 -0
  920. package/src/__tests__/render-graph-compute-raster.browser.test.ts +16 -0
  921. package/src/__tests__/render-graph-compute-raster.dawn.test.ts +22 -0
  922. package/src/__tests__/render-graph-hzb-gpu.ts +239 -0
  923. package/src/__tests__/render-graph-hzb.browser.test.ts +16 -0
  924. package/src/__tests__/render-graph-hzb.dawn.test.ts +23 -0
  925. package/src/__tests__/render-graph-primitives.unit.test.ts +22 -0
  926. package/src/__tests__/render-graph-storage-indirect-gpu.ts +162 -0
  927. package/src/__tests__/render-graph-storage-indirect.browser.test.ts +21 -0
  928. package/src/__tests__/render-graph-storage-indirect.dawn.test.ts +28 -0
  929. package/src/__tests__/render-scene-projection.unit.test.ts +206 -0
  930. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +125 -0
  931. package/src/__tests__/renderer-lifecycle.integration.test.ts +10 -0
  932. package/src/__tests__/renderer-render-scene-public.test-d.ts +27 -0
  933. package/src/__tests__/rhi-backend-package-boundary.unit.test.ts +11 -0
  934. package/src/__tests__/scene-temporal-coverage.unit.test.ts +259 -0
  935. package/src/__tests__/scriptable-pack-consumer.test-d.ts +14 -0
  936. package/src/__tests__/shader-stage-surface.unit.test.ts +38 -0
  937. package/src/__tests__/ssao-kernel-surface.unit.test.ts +15 -0
  938. package/src/__tests__/sun-snapshot-consumers.test.ts +35 -0
  939. package/src/__tests__/surface-backend-kind-owner.test-d.ts +42 -0
  940. package/src/__tests__/surface-configuration.unit.test.ts +92 -0
  941. package/src/__tests__/taa-history-store.unit.test.ts +196 -0
  942. package/src/__tests__/temporal-frame-transaction.integration.test.ts +537 -0
  943. package/src/__tests__/temporal-inspection.unit.test.ts +138 -0
  944. package/src/__tests__/texture-usage-surface.unit.test.ts +61 -0
  945. package/src/__tests__/typed-pipeline-topology.unit.test.ts +389 -0
  946. package/src/__tests__/urp-pipeline-targets.unit.test.ts +52 -0
  947. package/src/__tests__/urp-pipeline.unit.test.ts +14 -0
  948. package/src/__tests__/vertex-color-pipeline.unit.test.ts +124 -0
  949. package/src/__tests__/vertex-layout-consumer.unit.test.ts +25 -0
  950. package/src/__tests__/visibility-diagnostics.integration.test.ts +66 -0
  951. package/src/__tests__/visibility-extract.integration.test.ts +89 -0
  952. package/src/__tests__/visibility-extract.perf.test.ts +96 -0
  953. package/src/__tests__/visibility-feature-dedup.integration.test.ts +57 -0
  954. package/src/__tests__/visibility-hidden-report.unit.test.ts +37 -0
  955. package/src/__tests__/visibility-instances.integration.test.ts +50 -0
  956. package/src/__tests__/visibility-multi-world.integration.test.ts +54 -0
  957. package/src/__tests__/visibility-producer-matrix.integration.test.ts +191 -0
  958. package/src/__tests__/visibility-producer-matrix.unit.test.ts +96 -0
  959. package/src/__tests__/visibility-public-surface.test-d.ts +26 -0
  960. package/src/__tests__/visibility-resolver.unit.test.ts +84 -0
  961. package/src/__tests__/visibility-snapshot-contract.unit.test.ts +59 -0
  962. package/src/__tests__/vocabulary.unit.test.ts +21 -0
  963. package/src/assets/__tests__/asset-decoders.unit.test.ts +92 -0
  964. package/src/assets/__tests__/mipmap-generator.unit.test.ts +52 -0
  965. package/src/assets/__tests__/render-asset-port.unit.test.ts +54 -0
  966. package/src/assets/__tests__/resolve-render-asset.unit.test.ts +47 -0
  967. package/src/assets/asset-decoders.ts +158 -0
  968. package/src/assets/render-asset-port.ts +44 -0
  969. package/src/assets/resolve-render-asset.ts +13 -0
  970. package/src/authoring.ts +24 -0
  971. package/src/cluster-binner.ts +700 -0
  972. package/src/components/__tests__/atmosphere-red.test.ts +51 -0
  973. package/src/components/__tests__/atmosphere.test.ts +79 -0
  974. package/src/components/__tests__/fog.test-d.ts +49 -0
  975. package/src/components/__tests__/fog.test.ts +60 -0
  976. package/src/components/__tests__/schema-parity.unit.test.ts +25 -0
  977. package/src/components/__tests__/visibility-schema.unit.test.ts +35 -0
  978. package/src/components/atmosphere.ts +64 -0
  979. package/src/components/camera.ts +512 -0
  980. package/src/components/directional-light.ts +120 -0
  981. package/src/components/environment.ts +206 -0
  982. package/src/components/glyph-text.ts +63 -0
  983. package/src/components/index.ts +49 -0
  984. package/src/components/instances.ts +207 -0
  985. package/src/components/layer.ts +69 -0
  986. package/src/components/light-helpers.ts +188 -0
  987. package/src/components/mesh-filter.ts +59 -0
  988. package/src/components/mesh-renderer.ts +61 -0
  989. package/src/components/point-light-shadow.ts +43 -0
  990. package/src/components/point-light.ts +65 -0
  991. package/src/components/post-process-params.ts +35 -0
  992. package/src/components/scene-instance.ts +193 -0
  993. package/src/components/sort-key.ts +75 -0
  994. package/src/components/spot-light.ts +124 -0
  995. package/src/components/sprite-animation.ts +169 -0
  996. package/src/components/sprite-instances.ts +179 -0
  997. package/src/components/sprite-playback-mode.ts +73 -0
  998. package/src/components/sprite-region-override.ts +106 -0
  999. package/src/components/tile-layer.ts +161 -0
  1000. package/src/components/tilemap.ts +48 -0
  1001. package/src/components/visibility.ts +53 -0
  1002. package/src/construct-renderer.ts +4 -0
  1003. package/src/debug-draw-glue.ts +169 -0
  1004. package/src/deferred-lighting.ts +92 -0
  1005. package/src/dynamic-texture-store.ts +111 -0
  1006. package/src/engine-metrics.ts +89 -0
  1007. package/src/environment/README.md +76 -0
  1008. package/src/environment/__tests__/failure-injection.test.ts +50 -0
  1009. package/src/environment/__tests__/generation-red.test.ts +64 -0
  1010. package/src/environment/__tests__/generation.test.ts +37 -0
  1011. package/src/environment/__tests__/graph-transaction.test.ts +85 -0
  1012. package/src/environment/__tests__/inspection-red.test.ts +53 -0
  1013. package/src/environment/__tests__/lifecycle-evidence.test.ts +40 -0
  1014. package/src/environment/__tests__/pipeline-topology.test.ts +33 -0
  1015. package/src/environment/__tests__/readiness.test.ts +42 -0
  1016. package/src/environment/__tests__/recovery.test.ts +62 -0
  1017. package/src/environment/__tests__/resize-dispose.test.ts +30 -0
  1018. package/src/environment/__tests__/signature-red.test.ts +45 -0
  1019. package/src/environment/__tests__/signature.test.ts +60 -0
  1020. package/src/environment/descriptors.ts +7 -0
  1021. package/src/environment/extracted-environment.ts +239 -0
  1022. package/src/environment/generation.ts +158 -0
  1023. package/src/environment/gpu-bundle.ts +144 -0
  1024. package/src/environment/graph.ts +588 -0
  1025. package/src/environment/inspection.ts +107 -0
  1026. package/src/environment/lifecycle.ts +124 -0
  1027. package/src/environment/readiness.ts +76 -0
  1028. package/src/environment/signature.ts +185 -0
  1029. package/src/errors/ecs-validation.ts +16 -0
  1030. package/src/errors/index.ts +3 -0
  1031. package/src/errors/recover.ts +119 -0
  1032. package/src/errors/render.ts +1244 -0
  1033. package/src/extract/__tests__/directional-csm-domain.unit.test.ts +391 -0
  1034. package/src/extract/__tests__/environment-source.test.ts +105 -0
  1035. package/src/extract/__tests__/schema-readers.unit.test.ts +16 -0
  1036. package/src/extract/directional-csm.ts +959 -0
  1037. package/src/extract/index.ts +10 -0
  1038. package/src/extract/material-interface.ts +604 -0
  1039. package/src/extract/schema-readers.ts +27 -0
  1040. package/src/extract/visibility.ts +120 -0
  1041. package/src/features/graph-contribution.ts +577 -0
  1042. package/src/features/host.ts +1097 -0
  1043. package/src/features/index.ts +100 -0
  1044. package/src/features/morph/index.ts +24 -0
  1045. package/src/features/morph/morph-bounds.ts +123 -0
  1046. package/src/features/morph/morph-feature.ts +287 -0
  1047. package/src/features/prepared-gpu-work.ts +902 -0
  1048. package/src/features/prepared-graphics-store.ts +435 -0
  1049. package/src/features/prepared-graphics.ts +479 -0
  1050. package/src/features/render-graph-compute.ts +60 -0
  1051. package/src/features/render-graph-contribution.ts +95 -0
  1052. package/src/features/render-graph-raster.ts +308 -0
  1053. package/src/features/render-graph-resources.ts +36 -0
  1054. package/src/features/scene-temporal.ts +274 -0
  1055. package/src/features/taa-resolve.ts +244 -0
  1056. package/src/features/targets.ts +83 -0
  1057. package/src/features/types.ts +291 -0
  1058. package/src/frame-chain.ts +21 -0
  1059. package/src/fullscreen-post-process-pass.ts +406 -0
  1060. package/src/glyph-text-layout-system.ts +447 -0
  1061. package/src/gpu-driven/batch-topology.ts +423 -0
  1062. package/src/gpu-driven/production-raster.ts +1705 -0
  1063. package/src/gpu-driven/view-gpu.ts +849 -0
  1064. package/src/gpu-driven/view-reference.ts +110 -0
  1065. package/src/gpu-resource-store.ts +1921 -0
  1066. package/src/gpu-resource.ts +159 -0
  1067. package/src/gpu-scene-schema.ts +224 -0
  1068. package/src/gpu-scene.ts +855 -0
  1069. package/src/gpu-stage.ts +9 -0
  1070. package/src/gpu-texture-usage.ts +12 -0
  1071. package/src/gpu-usage.ts +14 -0
  1072. package/src/hdrp-buffers.ts +689 -0
  1073. package/src/hdrp-cluster-membership.ts +70 -0
  1074. package/src/hdrp-pipeline.ts +525 -0
  1075. package/src/ibl/IblPipelineCache.ts +1246 -0
  1076. package/src/ibl/cubemap-projection.ts +60 -0
  1077. package/src/ibl/face-uniforms.ts +150 -0
  1078. package/src/ibl/skylight-bind-group.ts +513 -0
  1079. package/src/index.ts +271 -0
  1080. package/src/instance-buffer-cache.ts +123 -0
  1081. package/src/internal.ts +88 -0
  1082. package/src/lifecycle.ts +123 -0
  1083. package/src/light-buffer-layout.ts +346 -0
  1084. package/src/materials.ts +236 -0
  1085. package/src/mesh-material-bindings.ts +104 -0
  1086. package/src/mesh-update-errors.ts +113 -0
  1087. package/src/mipmap-generator.ts +244 -0
  1088. package/src/param-schema-projection-census.ts +73 -0
  1089. package/src/pbr-pipeline.ts +749 -0
  1090. package/src/persistent-render-scene.ts +1752 -0
  1091. package/src/pipeline/index.ts +40 -0
  1092. package/src/pipeline-builder.ts +386 -0
  1093. package/src/pipeline-errors.ts +172 -0
  1094. package/src/pipeline-spec.ts +1768 -0
  1095. package/src/plugin.ts +137 -0
  1096. package/src/post-process-errors.ts +304 -0
  1097. package/src/prepare/__tests__/schema-readers.unit.test.ts +16 -0
  1098. package/src/prepare/index.ts +8 -0
  1099. package/src/prepare/prepared-graphics-resolver.ts +455 -0
  1100. package/src/prepare/schema-readers.ts +20 -0
  1101. package/src/record/__tests__/environment-record.test.ts +35 -0
  1102. package/src/record/__tests__/frame-targets.unit.test.ts +54 -0
  1103. package/src/record/__tests__/material-bg-cache.unit.test.ts +41 -0
  1104. package/src/record/__tests__/material-slot-plan.unit.test.ts +76 -0
  1105. package/src/record/__tests__/membership-timing.unit.test.ts +524 -0
  1106. package/src/record/__tests__/render-submission.integration.test.ts +76 -0
  1107. package/src/record/__tests__/render-submission.perf.test.ts +141 -0
  1108. package/src/record/__tests__/shadow-submit-mode.unit.test.ts +88 -0
  1109. package/src/record/__tests__/video-texture-view.unit.test.ts +90 -0
  1110. package/src/record/__tests__/view-ubo-layout.unit.test.ts +101 -0
  1111. package/src/record/frame-lighting.ts +1183 -0
  1112. package/src/record/frame-observation.ts +122 -0
  1113. package/src/record/frame-snapshot.ts +515 -0
  1114. package/src/record/frame-targets.ts +372 -0
  1115. package/src/record/frame.ts +1874 -0
  1116. package/src/record/helpers.ts +289 -0
  1117. package/src/record/index.ts +15 -0
  1118. package/src/record/main-pass-geometry.ts +1014 -0
  1119. package/src/record/main-pass-material.ts +716 -0
  1120. package/src/record/main-pass-skin.ts +51 -0
  1121. package/src/record/main-pass-sprite-draws.ts +1056 -0
  1122. package/src/record/main-pass-sprite.ts +166 -0
  1123. package/src/record/main-pass.ts +588 -0
  1124. package/src/record/material-payload.ts +292 -0
  1125. package/src/record/material-slot-plan.ts +68 -0
  1126. package/src/record/membership-timing.ts +1012 -0
  1127. package/src/record/mesh-ssbo.ts +406 -0
  1128. package/src/record/shadow-caster-uniform.ts +37 -0
  1129. package/src/record/shadow-pass.ts +2387 -0
  1130. package/src/record/shadow-submit-mode.ts +277 -0
  1131. package/src/record/skybox-post-pass.ts +594 -0
  1132. package/src/record/typed-frame-graph.ts +499 -0
  1133. package/src/record/view-ubo.ts +206 -0
  1134. package/src/render-data.ts +385 -0
  1135. package/src/render-dispatch-membership.ts +155 -0
  1136. package/src/render-graph-primitives.ts +1586 -0
  1137. package/src/render-pipeline-context.ts +332 -0
  1138. package/src/render-pipeline-target.ts +96 -0
  1139. package/src/render-pipeline.ts +343 -0
  1140. package/src/render-scene-projection.ts +1738 -0
  1141. package/src/render-system-extract.ts +5203 -0
  1142. package/src/render-system-fold.ts +531 -0
  1143. package/src/render-system.ts +3258 -0
  1144. package/src/renderer/asset-service.ts +32 -0
  1145. package/src/renderer/backend-selection.ts +121 -0
  1146. package/src/renderer/environment-classify.ts +30 -0
  1147. package/src/renderer/environment-error.ts +19 -0
  1148. package/src/renderer/material/assembly.ts +53 -0
  1149. package/src/renderer/material/pipeline-projection.ts +140 -0
  1150. package/src/renderer/renderer-factory.ts +9062 -0
  1151. package/src/renderer.ts +940 -0
  1152. package/src/scene-instances/post-spawn-resolve-joints.ts +168 -0
  1153. package/src/scene-utils/collect-subtree.ts +40 -0
  1154. package/src/shadow-atlas.ts +244 -0
  1155. package/src/ssao-buffers.ts +175 -0
  1156. package/src/ssao-config.ts +61 -0
  1157. package/src/ssao-data.ts +106 -0
  1158. package/src/surface-format.ts +37 -0
  1159. package/src/systems/active-camera.ts +116 -0
  1160. package/src/systems/pass-selector.ts +49 -0
  1161. package/src/systems/skin-palette-allocator.ts +294 -0
  1162. package/src/systems/transparent-sort-config.ts +211 -0
  1163. package/src/taa-history-store.ts +429 -0
  1164. package/src/temporal-coverage.ts +106 -0
  1165. package/src/temporal-frame-transaction.ts +301 -0
  1166. package/src/temporal-inspection.ts +194 -0
  1167. package/src/temporal-view.ts +174 -0
  1168. package/src/tilemap/resolve-tileset-runtime.ts +72 -0
  1169. package/src/tilemap-chunk-extract-system.ts +1307 -0
  1170. package/src/typed-render-graph-primitives.ts +530 -0
  1171. package/src/typed-shadow-passes.ts +195 -0
  1172. package/src/urp-pipeline.ts +340 -0
@@ -0,0 +1,1874 @@
1
+ // @forgeax/engine-runtime - RenderSystem record stage: frame.
2
+ // Extracted from render-system-record.ts (feat-20260704 M3/w17, pure move).
3
+
4
+ import type { World } from '@forgeax/engine-ecs';
5
+ import { createWorldProjection } from '@forgeax/engine-ecs/projection';
6
+ import { type RhiCommandEncoder, RhiError, type TextureView } from '@forgeax/engine-rhi';
7
+ import type { MaterialRenderState, MeshAsset } from '@forgeax/engine-types';
8
+ import { toShared } from '@forgeax/engine-types';
9
+ import { resolveRenderAsset } from '../assets/resolve-render-asset';
10
+ import type { EngineMetrics } from '../engine-metrics';
11
+ import type { ExtractedEnvironment } from '../environment/extracted-environment';
12
+ import {
13
+ beginEnvironmentGeneration,
14
+ environmentConsumerSnapshot,
15
+ publishEnvironmentGeneration,
16
+ } from '../environment/generation';
17
+ import { createEnvironmentGpuBundle } from '../environment/gpu-bundle';
18
+ import type { EnvironmentTransactionAttempt } from '../environment/graph';
19
+ import {
20
+ completeEnvironmentQueue,
21
+ destroyCompletedEnvironmentGenerations,
22
+ resizeEnvironmentGeneration,
23
+ } from '../environment/lifecycle';
24
+ import {
25
+ deriveEnvironmentBundleDescriptors,
26
+ environmentBundleBytes,
27
+ environmentSignature,
28
+ } from '../environment/signature';
29
+ import { buildSubmissionCandidateMembership } from '../gpu-driven/batch-topology';
30
+ import type { GpuDrivenProduction, PreparedGpuDrivenFrame } from '../gpu-driven/production-raster';
31
+ import type { CpuMultiViewVisibilityResult } from '../gpu-driven/view-gpu';
32
+ import { disposeTransientInstanceBuffers } from '../instance-buffer-cache';
33
+ import type { PersistentGpuDrivenState } from '../persistent-render-scene';
34
+ import type { _InternalRenderPipelineContext } from '../render-pipeline-context';
35
+ import type { RenderSceneProjection, RenderSceneSubmissionDelta } from '../render-scene-projection';
36
+ import type { MeshGpuHandles, RenderSystemInternals } from '../render-system';
37
+ import type {
38
+ CameraSnapshot,
39
+ DispatchEntry,
40
+ ExtractedLights,
41
+ FogSnapshot,
42
+ MaterialSnapshot,
43
+ RenderableSnapshot,
44
+ } from '../render-system-extract';
45
+ // feat-20260622-chunk-gpu-instancing-sprite-tilemap M1 / w4 (D-1 record-stage
46
+ // fold operator). Pure linear-scan helper that groups transparent-dispatch
47
+ // entries with equal (Layer.value, sortKey, materialHandle) into FoldBucket
48
+ // descriptors. The drawIndexed swap (1 instanced draw per bucket vs N
49
+ // per-entity draws) hooks into the sprite-pass dispatch loop below
50
+ // (w4-record-swap), using {@link buildFoldDispatchPlan} to translate
51
+ // renderableIndex-keyed buckets into validatedOrdered-index-keyed
52
+ // head/skip maps.
53
+ import {
54
+ buildFoldDispatchPlan,
55
+ evaluateFoldBucketUniformCap,
56
+ type FoldBucket,
57
+ type FoldDispatchPlan,
58
+ foldDispatchBuckets,
59
+ incrementFoldedDrawsMetric,
60
+ } from '../render-system-fold';
61
+ import type { RenderRecordPhase } from '../renderer';
62
+ import {
63
+ getTransparentSortConfig,
64
+ TRANSPARENT_SORT_MODE_LAYER_Y,
65
+ TRANSPARENT_SORT_MODE_LAYER_Z,
66
+ } from '../systems/transparent-sort-config';
67
+ import type { TemporalCoverageSummary } from '../temporal-coverage';
68
+ import type { TemporalFrameAttempt, TemporalFrameTransaction } from '../temporal-frame-transaction';
69
+ import {
70
+ buildPerFrameBindGroups,
71
+ createEnvironmentConsumerSnapshot,
72
+ prepareFrameLighting,
73
+ resolveSkyboxActive,
74
+ warnZeroLightStandard,
75
+ writeHdrpClusterAndSsaoBuffers,
76
+ writePointSpotLightBuffers,
77
+ writeShadowParamsBuffer,
78
+ } from './frame-lighting';
79
+ import {
80
+ type BindGroupCounts,
81
+ clampPcfKernelSize,
82
+ type DirectionalShadowCache,
83
+ type DirectionalShadowWorldState,
84
+ type DispatchCounts,
85
+ makeZeroCameraFallbackSnapshot,
86
+ type RasterItemKey,
87
+ type RenderFrameState,
88
+ type ValidatedRenderable,
89
+ worldEntityKey,
90
+ } from './frame-snapshot';
91
+ import {
92
+ acquireSwapChainTarget,
93
+ graphExecutionPhase,
94
+ resolveShadowMapSize,
95
+ resolveSpotShadowMapSize,
96
+ } from './frame-targets';
97
+ import {
98
+ computeViewMatrix,
99
+ driveLazyEquirectProjection,
100
+ selectLazyEquirectHandle,
101
+ variantSetFromDefines,
102
+ warnMultiSkybox,
103
+ warnMultiSkylight,
104
+ } from './helpers';
105
+ import { computeSplitLdrSprite } from './main-pass-sprite';
106
+ import {
107
+ buildMaterialSlotPlan,
108
+ findRenderablePrefixForSlotCapacity,
109
+ materialSlotCountForPrefix,
110
+ } from './material-slot-plan';
111
+ import { cleanPerEntityCache, ensureMeshSsboCapacity, uploadMeshSsboBatch } from './mesh-ssbo';
112
+ import { writeShadowCasterUniforms } from './shadow-caster-uniform';
113
+ import { ensureCompiledFrameGraph, executeCompiledFrameGraph } from './typed-frame-graph';
114
+ import { writeViewUbo } from './view-ubo';
115
+
116
+ export type RecordProfileRunner = <T>(phase: RenderRecordPhase, action: () => T) => T;
117
+
118
+ const EMPTY_RASTER_ITEM_KEYS: ReadonlySet<RasterItemKey> = new Set<RasterItemKey>();
119
+
120
+ type ShadowSceneSlot = PersistentGpuDrivenState['slots'][number];
121
+
122
+ // PersistentRenderScene publishes the production O(1) stable-id join. The
123
+ // compatibility path below deliberately rebuilds its small index per token
124
+ // calculation: custom/test doubles may mutate a slots array in place, so an
125
+ // identity cache would be able to return a stale slot and create a false hit.
126
+ function shadowSlotIndex(slots: readonly ShadowSceneSlot[]): ReadonlyMap<number, ShadowSceneSlot> {
127
+ const index = new Map<number, ShadowSceneSlot>();
128
+ for (const slot of slots) index.set(slot.slot, slot);
129
+ return index;
130
+ }
131
+
132
+ function sortedUniqueStableIds(values: readonly number[]): number[] {
133
+ const sorted = [...values].sort((left, right) => left - right);
134
+ let write = 0;
135
+ for (const value of sorted) {
136
+ if (write > 0 && sorted[write - 1] === value) continue;
137
+ sorted[write] = value;
138
+ write += 1;
139
+ }
140
+ sorted.length = write;
141
+ return sorted;
142
+ }
143
+
144
+ function visibilityCandidateIndices(
145
+ visibility: CpuMultiViewVisibilityResult | undefined,
146
+ kind: 'main' | 'cascade',
147
+ cascadeIndex?: number,
148
+ ): ReadonlySet<number> | undefined {
149
+ const view = visibility?.views.find(
150
+ (candidate) =>
151
+ candidate.kind === kind && (kind === 'main' || candidate.cascadeIndex === cascadeIndex),
152
+ );
153
+ if (view === undefined) return undefined;
154
+ return new Set(
155
+ view.compactVisible.flatMap((candidate) =>
156
+ candidate.candidateIndex === undefined ? [] : [candidate.candidateIndex],
157
+ ),
158
+ );
159
+ }
160
+
161
+ function runRecordProfilePhase<T>(
162
+ runner: RecordProfileRunner | undefined,
163
+ phase: RenderRecordPhase,
164
+ action: () => T,
165
+ ): T {
166
+ return runner === undefined ? action() : runner(phase, action);
167
+ }
168
+
169
+ const ANALYTIC_SKY_QUALITY = Object.freeze({ skySize: 128, irradianceSize: 32, prefilterMips: 5 });
170
+ const ANALYTIC_SKY_ALGORITHM = 'preetham-perez-1999-r1';
171
+
172
+ function beginProductionEnvironmentGeneration(
173
+ internals: RenderSystemInternals,
174
+ frameState: RenderFrameState,
175
+ environment: ExtractedEnvironment,
176
+ ): EnvironmentTransactionAttempt | undefined {
177
+ if (environment.kind !== 'atmosphere') {
178
+ const state = frameState.environmentGeneration;
179
+ const oldHandles = [state.active?.handle, state.candidate?.handle].filter(
180
+ (handle): handle is NonNullable<typeof handle> => handle !== undefined,
181
+ );
182
+ if (oldHandles.length > 0) {
183
+ resizeEnvironmentGeneration(state);
184
+ void internals.device.queue.onSubmittedWorkDone().then(() => {
185
+ for (const handle of oldHandles) {
186
+ if (typeof handle === 'object' && handle !== null && 'destroy' in handle) {
187
+ (handle as { readonly destroy: (device: typeof internals.device) => unknown }).destroy(
188
+ internals.device,
189
+ );
190
+ }
191
+ }
192
+ });
193
+ }
194
+ return undefined;
195
+ }
196
+ const quality = ANALYTIC_SKY_QUALITY;
197
+ const signature = environmentSignature({
198
+ atmosphere: {
199
+ turbidity: environment.atmosphere.turbidity,
200
+ rayleigh: environment.atmosphere.rayleigh,
201
+ mieCoefficient: environment.atmosphere.mieCoefficient,
202
+ mieDirectionalG: environment.atmosphere.mieDirectionalG,
203
+ sunAngularRadius: environment.atmosphere.sunAngularRadius,
204
+ },
205
+ sun: {
206
+ direction: environment.atmosphere.sun.direction,
207
+ color: environment.atmosphere.sun.color,
208
+ illuminance: environment.atmosphere.sun.illuminance,
209
+ },
210
+ quality,
211
+ algorithmRevision: ANALYTIC_SKY_ALGORITHM,
212
+ worldId: environment.worldId,
213
+ entity: environment.atmosphere.entity,
214
+ deviceGeneration:
215
+ typeof frameState.environmentGeneration.deviceGeneration === 'number'
216
+ ? frameState.environmentGeneration.deviceGeneration
217
+ : 0,
218
+ outputFormat: 'rgba16float',
219
+ });
220
+ const state = frameState.environmentGeneration;
221
+ if (state.active?.signature === signature) {
222
+ return undefined;
223
+ }
224
+ if (state.candidate !== undefined) return undefined;
225
+ const descriptors = deriveEnvironmentBundleDescriptors(quality, 'rgba16float');
226
+ const attempt = beginEnvironmentGeneration(state, {
227
+ signature,
228
+ bytes: environmentBundleBytes(descriptors),
229
+ });
230
+ const bundle = createEnvironmentGpuBundle(internals.device, descriptors);
231
+ if (!bundle.ok) {
232
+ attempt.fail('readiness');
233
+ internals.errorRegistry.fire(bundle.error);
234
+ return undefined;
235
+ }
236
+ const candidateSnapshot = createEnvironmentConsumerSnapshot({
237
+ generation: attempt.generation,
238
+ signature: String(attempt.desired.signature),
239
+ direction: [
240
+ environment.atmosphere.sun.direction[0] ?? 0,
241
+ environment.atmosphere.sun.direction[1] ?? 0,
242
+ environment.atmosphere.sun.direction[2] ?? 0,
243
+ ],
244
+ color: [
245
+ environment.atmosphere.sun.color[0] ?? 0,
246
+ environment.atmosphere.sun.color[1] ?? 0,
247
+ environment.atmosphere.sun.color[2] ?? 0,
248
+ ],
249
+ illuminance: environment.atmosphere.sun.illuminance,
250
+ atmosphere: {
251
+ turbidity: environment.atmosphere.turbidity,
252
+ rayleigh: environment.atmosphere.rayleigh,
253
+ mieCoefficient: environment.atmosphere.mieCoefficient,
254
+ mieDirectionalG: environment.atmosphere.mieDirectionalG,
255
+ sunAngularRadius: environment.atmosphere.sunAngularRadius,
256
+ },
257
+ source: { worldId: environment.worldId, entity: environment.atmosphere.entity },
258
+ });
259
+ const candidate = attempt.complete(bundle.value, candidateSnapshot);
260
+ const queueCompletion = `analytic-sky:${candidate.generation}:${frameState.frameNumber}`;
261
+ let settled = false;
262
+ const abandonCandidate = (stage: 'execute' | 'finish' | 'submit'): void => {
263
+ if (settled) return;
264
+ settled = true;
265
+ attempt.fail(stage);
266
+ bundle.value.destroy(internals.device);
267
+ };
268
+ return {
269
+ abort: abandonCandidate,
270
+ publish: () => {
271
+ if (settled) return;
272
+ settled = true;
273
+ publishEnvironmentGeneration(state, candidate, queueCompletion);
274
+ void internals.device.queue.onSubmittedWorkDone().then(() => {
275
+ completeEnvironmentQueue(state, queueCompletion);
276
+ destroyCompletedEnvironmentGenerations(state, internals.device, queueCompletion);
277
+ });
278
+ },
279
+ };
280
+ }
281
+
282
+ export function recordFrame(
283
+ internals: RenderSystemInternals,
284
+ world: World,
285
+ cameras: CameraSnapshot[],
286
+ lights: ExtractedLights,
287
+ renderables: RenderableSnapshot[],
288
+ transparentDispatch: readonly DispatchEntry[],
289
+ frameState: RenderFrameState,
290
+ dispatchCounts: DispatchCounts,
291
+ bindGroupCounts: BindGroupCounts,
292
+ environment: ExtractedEnvironment,
293
+ postProcessParams: ReadonlyMap<string, Uint8Array>,
294
+ // feat-20260709-editor-world-partition ENGINE-fix-round2 (defect 2): the
295
+ // The ordered worlds[] remains the shadow-composition input. Renderer-owned
296
+ // snapshots use stable WorldRenderId values, so production passes the
297
+ // worldForRenderId lookup below instead of treating those ids as indexes.
298
+ // Direct unit callers without that lookup retain the historical array-index
299
+ // behavior.
300
+ worlds: readonly World[] = [world],
301
+ profilePhase?: RecordProfileRunner,
302
+ gpuDriven?: {
303
+ readonly owner: GpuDrivenProduction;
304
+ readonly scene: PersistentGpuDrivenState | undefined;
305
+ readonly onCoverage: (ownsAll: boolean) => void;
306
+ },
307
+ worldForRenderId?: (worldId: number) => World | undefined,
308
+ temporal?: {
309
+ readonly transaction: TemporalFrameTransaction;
310
+ readonly projection: RenderSceneProjection;
311
+ readonly coverage: TemporalCoverageSummary;
312
+ readonly reactiveRevision?: number | undefined;
313
+ readonly onSceneSubmitted?: ((delta: RenderSceneSubmissionDelta) => void) | undefined;
314
+ },
315
+ fog?: FogSnapshot | undefined,
316
+ ): boolean {
317
+ const skylight =
318
+ environment.kind === 'none' || environment.kind === 'image' ? environment.skylight : undefined;
319
+ const skylightCount = environment.skylightCount;
320
+ const skybox = environment.kind === 'image' ? environment.skybox : undefined;
321
+ const skyboxCount = environment.skyboxCount;
322
+ // The `try / finally` wrapper advances `frameState.frameNumber` exactly
323
+ // once per `recordFrame` invocation regardless of which early-return
324
+ // branch is taken (camera missing, cap exceeded, pipeline pending,
325
+ // swap-chain unavailable, etc).
326
+ // The boolean result reports whether the shared frame encoder reached queue
327
+ // submission; prepared graphics uses it to distinguish in-flight from
328
+ // unsubmitted buffer leases.
329
+ // Case B: 0 Camera => fire onError diagnostic. After
330
+ // feat-20260608-create-app-param-surface-trim / M1 / D-8, the frame
331
+ // is NOT skipped: a synthetic CameraSnapshot is injected so the
332
+ // downstream clear-pass-only path (Case E softening, D-Q7) still
333
+ // paints the swap-chain with the `ZERO_CAMERA_CLEAR_FALLBACK` color
334
+ // (`[0, 0, 0, 1]` opaque black; AC-05). The synthetic camera carries
335
+ // identity-shaped projection / view inputs (fov=PI/4, aspect=1,
336
+ // near=0.1, far=100) so the existing matrix math stays numerically
337
+ // stable; no MeshRenderer entity will pass validation in this state
338
+ // (no Camera = no scene), so geometry submission is a no-op and only
339
+ // the swap-chain clear lands.
340
+ let activeCameras = cameras;
341
+ let temporalAttempt: TemporalFrameAttempt | undefined;
342
+ let environmentAttempt: EnvironmentTransactionAttempt | undefined;
343
+ let frameSubmitted = false;
344
+ if (activeCameras.length === 0) {
345
+ internals.errorRegistry.fire(
346
+ new RhiError({
347
+ code: 'render-system-no-camera',
348
+ expected: 'world has at least one entity with Transform + Camera',
349
+ hint: 'world.spawn({ component: Transform, data: { pos: [x, y, z], quat: [x, y, z, w], scale: [x, y, z] } }, { component: Camera, data: { fov, aspect, near, far, clearColor: [r, g, b, a] } }) before renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 })',
350
+ }),
351
+ );
352
+ activeCameras = [makeZeroCameraFallbackSnapshot()];
353
+ }
354
+ try {
355
+ // Case D: N>1 Camera => fire onError, use first hit (D-S7).
356
+ if (activeCameras.length > 1) {
357
+ internals.errorRegistry.fire(
358
+ new RhiError({
359
+ code: 'render-system-multi-camera',
360
+ expected: 'world has exactly one entity with Transform + Camera',
361
+ hint: 'remove duplicate Camera entities or wait for feat-future-multi-viewport',
362
+ }),
363
+ );
364
+ }
365
+ const camera = activeCameras[0];
366
+ if (!camera) return false;
367
+ const pipelineState = internals.getPipelineState();
368
+ if (pipelineState === null) return false;
369
+ frameState.currentFrameObservationSource = undefined;
370
+ frameState.currentDirectionalShadowView = null;
371
+ frameState.currentSpotShadowView = null;
372
+ environmentAttempt = beginProductionEnvironmentGeneration(internals, frameState, environment);
373
+ // `beginProductionEnvironmentGeneration` owns the producer snapshot and
374
+ // updates the active snapshot only after a successful submit. This keeps
375
+ // direct light/CSM consumers on the previous LKG while a replacement is
376
+ // still a candidate.
377
+
378
+ // Record-stage fold operator linear scan (groups transparent-sort entries
379
+ // into fold buckets + records the fold-eligible count metric). Extracted to
380
+ // computeFoldBuckets (M3/w18).
381
+ const { foldBuckets, light } = runRecordProfilePhase(profilePhase, 'record/scene-state', () => {
382
+ const foldBuckets = runRecordProfilePhase(
383
+ profilePhase,
384
+ 'record/scene-state/fold-buckets',
385
+ () => computeFoldBuckets(world, frameState, transparentDispatch, renderables),
386
+ );
387
+
388
+ // Multi-light warnings + point/spot shadow-snapshot pin + point-shadow atlas
389
+ // ensure + ExtractedLights three-arm destructure (directional fallback,
390
+ // point/spot arrays, totalLightCount). Extracted to prepareFrameLighting
391
+ // (M3/w18) so recordFrame stays a skeleton.
392
+ const { light, pointLights, spotLights, totalLightCount } = runRecordProfilePhase(
393
+ profilePhase,
394
+ 'record/scene-state/lighting-prep',
395
+ () => prepareFrameLighting(internals, frameState, lights),
396
+ );
397
+
398
+ runRecordProfilePhase(profilePhase, 'record/scene-state/ambient-resolution', () => {
399
+ // feat-20260520-skylight-ibl-cubemap M4 / t27 (AC-10 + F-4 nit):
400
+ // 0-light three-condition conjunction (plan-strategy D-5):
401
+ // no Skylight (skylight === undefined)
402
+ // AND 0 direct light (totalLightCount === 0)
403
+ // AND StandardMaterial (renderables.some materialShaderId !== 'forgeax::default-unlit')
404
+ // All three true -> black + warn. Any one false -> no warn.
405
+ //
406
+ // Multi-Skylight warn (F-4 nit + feat-20260630 M3 / w19): >1 Skylight
407
+ // entity -> warn ONCE per RenderSystem lifetime (not per frame), naming the
408
+ // winning entity handle so the scene author can tell which Skylight is used
409
+ // (F-8: warn carries conflicting entity info). First Skylight (by archetype
410
+ // order) wins.
411
+ warnMultiSkylight(frameState, skylightCount, skylight?.entityHandle ?? 0);
412
+
413
+ // Multi-SkyboxBackground warn (feat-20260630 M3 / w19): mirror the Skylight
414
+ // once-warn + winning-entity-handle pattern. First SkyboxBackground (by
415
+ // archetype order) wins.
416
+ warnMultiSkybox(frameState, skyboxCount, skybox?.entityHandle ?? 0);
417
+
418
+ // feat-20260630-equirect-kind-internalized-ibl-declarative-skyligh M3 / w18:
419
+ // lazy equirect-to-cubemap projection trigger (the single per-frame driver;
420
+ // plan-strategy D-4 + sequence diagram). The Skylight (or, when present
421
+ // without one, the SkyboxBackground) supplies the equirect handle; both
422
+ // reuse the same handle so a single projection serves IBL ambient + skybox.
423
+ // - handle 0 -> no equirect (solid-color ambient); skip
424
+ // - caps.rgba16float
425
+ // Renderable false -> permanent white fallback; never project (AC-06,
426
+ // the only IBL gate; no UA guard)
427
+ // - status undefined -> first sight: resolve POD + fire-and-forget launch
428
+ // (does NOT await; the store writes status:'pending'
429
+ // synchronously so this launches exactly once)
430
+ // - status pending -> projection in flight; white fallback this frame
431
+ // (normal transition, not an error -- no fire)
432
+ // - status ready -> real IBL bound by the recordMainPass cache check
433
+ // - status failed -> fire EquirectProjectionFailedError ONCE per
434
+ // handle (R-2/AC-09: store records failed
435
+ // permanently and never retries; the latch keeps
436
+ // the channel from flooding)
437
+ const lazyEquirectHandle = selectLazyEquirectHandle(skylight, skybox);
438
+ if (lazyEquirectHandle !== 0) {
439
+ driveLazyEquirectProjection(internals, world, frameState, lazyEquirectHandle);
440
+ }
441
+ });
442
+
443
+ // feat-20260608-cluster-lighting M5 / w21 + M6 / w23: HDRP per-frame CPU
444
+ // cluster binning + light-data / cluster-grid / SSAO buffer uploads. Runs
445
+ // only when the HDRP pipeline is active and there is at least one punctual
446
+ // light. Extracted to a sub-function (M3/w18) so recordFrame stays a
447
+ // skeleton; fail-soft error semantics are preserved verbatim.
448
+ runRecordProfilePhase(profilePhase, 'record/scene-state/hdrp-cluster', () => {
449
+ writeHdrpClusterAndSsaoBuffers(
450
+ internals,
451
+ frameState,
452
+ camera,
453
+ pointLights,
454
+ spotLights,
455
+ profilePhase,
456
+ pipelineState.hdrpClusterMembershipPipeline !== null &&
457
+ internals.device.caps?.compute === true &&
458
+ internals.membershipTiming?.usesCpuControl() !== true,
459
+ pipelineState.hdrpClusterMembershipBindGroupLayout,
460
+ );
461
+ });
462
+
463
+ // Zero-light standard-material once-warn (no Skylight + 0 direct light +
464
+ // >=1 lit material -> black). Extracted to warnZeroLightStandard (M3/w18).
465
+ warnZeroLightStandard(frameState, renderables, skylight, totalLightCount);
466
+
467
+ return { foldBuckets, light };
468
+ });
469
+
470
+ void activeCameras; // referenced below
471
+
472
+ // Case E (this commit): 0 renderables = legitimate scene (LO §1.1
473
+ // hello-window minimum semantic). Mirrors the Case C softening for
474
+ // 0 DirectionalLight (line 134 above; D-Q7). recordFrame() falls
475
+ // through to encode + submit a clear-pass-only render pass so the
476
+ // canvas is painted with `clearColor` even when no entity carries
477
+ // MeshFilter + MeshRenderer. Geometry submission (mat4 uploads,
478
+ // bind-group construction, vertex/index binding, drawIndexed) is
479
+ // conditional on `validatedOrdered.length > 0` further down.
480
+
481
+ // Point-shadow params UBO write (per-layer near/far/invSpan). Extracted to
482
+ // writeShadowParamsBuffer (M3/w18).
483
+ writeShadowParamsBuffer(internals, frameState, pipelineState);
484
+
485
+ // feat-20260625-spot-light-shadow-mapping w25 (scope-amend webkit-fallback):
486
+ // the per-spot perspective `lightViewProj` matrices fold into the View UBO
487
+ // tail (`view.spotLightViewProj`, floats 132..195 / bytes 528..784) and are
488
+ // written as part of the per-frame viewPayload below — no standalone binding
489
+ // 9 uniform buffer (it overflowed the WebGL2 fallback fragment uniform-buffer
490
+ // budget). See the viewPayload construction (VIEW_PAYLOAD_FLOATS = 196).
491
+
492
+ const effectiveShadowMapSize = resolveShadowMapSize(internals, lights);
493
+ const effectiveSpotShadowMapSize = resolveSpotShadowMapSize(internals, lights);
494
+ const graphShadowMapSize = effectiveShadowMapSize ?? effectiveSpotShadowMapSize;
495
+
496
+ const validated = runRecordProfilePhase(profilePhase, 'record/validation', () =>
497
+ validateRenderables(
498
+ internals,
499
+ world,
500
+ worlds,
501
+ renderables,
502
+ transparentDispatch,
503
+ worldForRenderId,
504
+ ),
505
+ );
506
+ const residentMeshes = new Map<number, MeshGpuHandles>();
507
+ const meshWorlds = new Map<number, number>();
508
+ const ambiguousMeshHandles = new Set<number>();
509
+ for (const entry of validated) {
510
+ const handle = entry.source.assetHandle;
511
+ if (ambiguousMeshHandles.has(handle)) continue;
512
+ const priorWorld = meshWorlds.get(handle);
513
+ if (priorWorld !== undefined && priorWorld !== entry.source.worldId) {
514
+ residentMeshes.delete(handle);
515
+ ambiguousMeshHandles.add(handle);
516
+ continue;
517
+ }
518
+ meshWorlds.set(handle, entry.source.worldId);
519
+ residentMeshes.set(handle, entry.mesh);
520
+ }
521
+
522
+ let preparedGpuDriven: PreparedGpuDrivenFrame | undefined;
523
+ if (gpuDriven !== undefined) {
524
+ const prepared = gpuDriven.owner.prepare({
525
+ scene: gpuDriven.scene,
526
+ camera,
527
+ meshes: residentMeshes,
528
+ viewBindGroupLayout: pipelineState.viewBindGroupLayout,
529
+ materialBindGroupLayout: pipelineState.materialBindGroupLayout,
530
+ meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
531
+ hdrp: frameState.isHdrpActive,
532
+ });
533
+ if (prepared.ok) preparedGpuDriven = prepared.value;
534
+ else if (prepared.error.code !== 'rhi-not-available')
535
+ internals.errorRegistry.fire(prepared.error);
536
+ }
537
+ const cpuVisibility = gpuDriven?.owner.inspect().cpuVisibility;
538
+ const multiViewSubmissions = gpuDriven?.owner.multiViewSubmissions() ?? [];
539
+
540
+ // The GPU lane owns only the primary opaque raster today. Shadow membership
541
+ // is owned by the batch-aligned multi-view submissions below; the existing
542
+ // shadow encoder consumes them through a one-way direct-RHI adapter.
543
+ const cpuShadowCastersActive =
544
+ (effectiveShadowMapSize ?? 0) > 0 ||
545
+ lights.pointShadow.length > 0 ||
546
+ lights.spot.some((spot) => spot.castShadow);
547
+ const gpuOwnsAllPrimaryRaster = preparedGpuDriven?.ownsAllForwardItems === true;
548
+ gpuDriven?.onCoverage(gpuOwnsAllPrimaryRaster && !cpuShadowCastersActive);
549
+
550
+ const activeEnvironmentSignature =
551
+ frameState.environmentGeneration.candidate?.signature ??
552
+ frameState.environmentGeneration.active?.signature;
553
+ const preflightGraph = runRecordProfilePhase(profilePhase, 'record/render-graph', () =>
554
+ ensureCompiledFrameGraph(
555
+ internals,
556
+ frameState,
557
+ pipelineState,
558
+ camera,
559
+ lights,
560
+ Math.max(1, internals.canvas.width),
561
+ Math.max(1, internals.canvas.height),
562
+ graphShadowMapSize,
563
+ preparedGpuDriven,
564
+ temporal?.transaction,
565
+ ),
566
+ );
567
+ if (preflightGraph === null) return false;
568
+ temporalAttempt = temporal?.transaction.begin({
569
+ camera,
570
+ surfaceWidth: Math.max(1, internals.canvas.width),
571
+ surfaceHeight: Math.max(1, internals.canvas.height),
572
+ projection: temporal.projection,
573
+ visible: renderables,
574
+ coverage: temporal.coverage,
575
+ ...(Object.hasOwn(temporal, 'reactiveRevision')
576
+ ? { reactiveRevision: temporal.reactiveRevision }
577
+ : {}),
578
+ environmentSignature:
579
+ typeof activeEnvironmentSignature === 'string' ? activeEnvironmentSignature : undefined,
580
+ ...(temporal.onSceneSubmitted === undefined
581
+ ? {}
582
+ : { onSceneSubmitted: temporal.onSceneSubmitted }),
583
+ });
584
+
585
+ // Acquire the swap-chain texture + colour view + target dimensions (with
586
+ // one reconfigure-and-retry on surface-outdated). Extracted to
587
+ // acquireSwapChainTarget (M3/w18); returns null on unrecoverable failure
588
+ // (context null / double getCurrentTexture fail / view creation fail), in
589
+ // which case recordFrame bails after the finally-block frame advance.
590
+ const swapTarget = runRecordProfilePhase(profilePhase, 'record/swapchain', () =>
591
+ acquireSwapChainTarget(internals, pipelineState),
592
+ );
593
+ if (swapTarget === null) return false;
594
+ const currentTexture = swapTarget.currentTexture;
595
+ const view = swapTarget.view;
596
+ const targetW = swapTarget.targetW;
597
+ const targetH = swapTarget.targetH;
598
+
599
+ const depthView: TextureView | null = null;
600
+ // feat-20260709 M3 / D-3: clear-color read from the active CameraSnapshot's
601
+ // single `clearColor` array field (first-archetype-hit per OOS-2). When the
602
+ // world had zero Camera entities, `camera` here is the synthetic fallback
603
+ // snapshot built above (Case B), which carries
604
+ // `ZERO_CAMERA_CLEAR_FALLBACK = [0, 0, 0, 1]`.
605
+ const clear: readonly [number, number, number, number] = [
606
+ camera.clearColor[0],
607
+ camera.clearColor[1],
608
+ camera.clearColor[2],
609
+ camera.clearColor[3],
610
+ ];
611
+
612
+ pipelineState.perPassResources.shadowMapSize = graphShadowMapSize ?? 0;
613
+ pipelineState.perPassResources.shadowCascadeCount = lights.cascadeCount ?? 0;
614
+ pipelineState.perPassResources.shadowDepthBias = lights.depthBias ?? 0.005;
615
+ pipelineState.perPassResources.shadowNormalBias = lights.normalBias ?? 0.05;
616
+ pipelineState.perPassResources.shadowPcfKernelSize = clampPcfKernelSize(lights.pcfKernelSize);
617
+ const environmentSunDirection = environmentConsumerSnapshot(frameState.environmentGeneration)
618
+ ?.csm.direction;
619
+ const environmentShadowDirection =
620
+ environmentSunDirection === undefined
621
+ ? undefined
622
+ : [
623
+ -(environmentSunDirection[0] ?? 0),
624
+ -(environmentSunDirection[1] ?? 0),
625
+ -(environmentSunDirection[2] ?? 0),
626
+ ];
627
+ pipelineState.perPassResources.shadowLightDirection =
628
+ environmentShadowDirection !== undefined
629
+ ? new Float32Array(environmentShadowDirection)
630
+ : lights.directional === undefined
631
+ ? null
632
+ : new Float32Array([
633
+ lights.directional.direction[0] ?? 0,
634
+ lights.directional.direction[1] ?? 0,
635
+ lights.directional.direction[2] ?? 0,
636
+ ]);
637
+ const firstDirectionalShadowMatrix = lights.lightViewProj?.[0];
638
+ pipelineState.perPassResources.shadowLightSpaceMatrix =
639
+ firstDirectionalShadowMatrix === undefined
640
+ ? null
641
+ : new Float32Array(firstDirectionalShadowMatrix);
642
+ if (lights.lightViewProj === undefined) {
643
+ pipelineState.perPassResources.shadowCsmLightViewProj = null;
644
+ pipelineState.perPassResources.shadowCsmSelection = null;
645
+ } else {
646
+ const csmMatrices = new Float32Array(64);
647
+ for (let cascade = 0; cascade < 4; cascade += 1) {
648
+ const matrix = lights.lightViewProj[cascade];
649
+ if (matrix !== undefined) csmMatrices.set(matrix, cascade * 16);
650
+ }
651
+ pipelineState.perPassResources.shadowCsmLightViewProj = csmMatrices;
652
+ pipelineState.perPassResources.shadowCsmSelection =
653
+ lights.splitPlanes === undefined
654
+ ? null
655
+ : {
656
+ viewMatrix: new Float32Array(computeViewMatrix(camera)),
657
+ splitPlanes: new Float32Array(lights.splitPlanes),
658
+ cascadeBlend: lights.cascadeBlend ?? 0,
659
+ };
660
+ }
661
+
662
+ // Tonemap + skybox-active resolution (skybox requires tonemap HDR target +
663
+ // a resident cubemap view). Extracted to resolveSkyboxActive (M3/w18).
664
+ const { tonemapActive, skyboxActive } = resolveSkyboxActive(
665
+ internals,
666
+ frameState,
667
+ camera,
668
+ skybox,
669
+ );
670
+ const atmosphereActive =
671
+ frameState.environmentGeneration.active !== undefined ||
672
+ frameState.environmentGeneration.candidate !== undefined;
673
+
674
+ // feat-20260604-learn-render-4.10-anti-aliasing-msaa M2 / w9 (D-6, C-9):
675
+ // MSAA is a per-Camera switch derived from `camera.antialias`, never
676
+ // stored separately. When active the geometry pass writes a count=4
677
+ // multisample colour target and resolves to a single-sample output; the
678
+ // record stage selects the `*Msaa` pipeline variants and the geometry
679
+ // pass attaches the resolve target. When inactive every attachment +
680
+ // pipeline stays single-sample (the pre-MSAA path is byte-for-byte
681
+ // unchanged).
682
+ // Geometry colour / depth / resolve / sprite-split target view resolution
683
+ // (MSAA + tonemap routing) + MSAA writeback to perPassResources. Extracted
684
+ // to resolveGeometryTargetViews (M3/w18) so recordFrame stays a skeleton.
685
+ const msaaActive =
686
+ camera.antialias === 'msaa' && internals.device.caps.backendKind !== 'wgpu-webgl2';
687
+ const geometryColorView = view;
688
+ const geometryDepthView = depthView;
689
+ const geometryDepthKey: string | null = null;
690
+ const geometryColorResolveView: TextureView | null = null;
691
+ const ldrSpriteColorView: TextureView | null = null;
692
+
693
+ // Validate renderable handles + collect render plan. Empty `renderables`
694
+ // input or all-unregistered handles both produce `validatedOrdered.length === 0`,
695
+ // which is the Case E (clear-pass-only) path.
696
+ //
697
+ // feat-20260520-2d-sprite-layer-mvp M-3 / w25 (@new-surface): per-entry
698
+ // `renderableIndex` tracks the position into the original renderables[]
699
+ // array so the bucket-aware reordering below can correlate
700
+ // transparent-sort output (TransparentEntry.renderableIndex) back to
701
+ // the validated row.
702
+ // bug-20260527-renderstate-pipeline-dispatch-gap D-4:
703
+ // build a renderableIndex -> renderState map from dispatch entries
704
+ // so each ValidatedRenderable carries its per-material renderState
705
+ // override without an O(n^2) back-scan in the draw loop.
706
+ const gpuOwnedItemKeys = preparedGpuDriven?.ownedItemKeys ?? EMPTY_RASTER_ITEM_KEYS;
707
+ gpuDriven?.owner.recordCpuValidation(validated, gpuOwnedItemKeys);
708
+
709
+ // Per-frame cache clean-up: drop per-entity BG cache entries + instance
710
+ // buffers whose entityKey is absent from the current validated set (despawn
711
+ // eviction). Extracted to cleanPerFrameCaches (M3/w18).
712
+ cleanPerFrameCaches(internals, frameState, validated);
713
+
714
+ // Dispatch-ordered render plan: reorder validated renderables to dispatch
715
+ // order, run the mesh-SSBO capacity gate (graceful truncation), and build
716
+ // the fold dispatch plan. Extracted to buildDispatchPlan (M3/w18).
717
+ const dispatchPlan = runRecordProfilePhase(profilePhase, 'record/dispatch-plan', () =>
718
+ buildDispatchPlan(internals, validated, transparentDispatch, foldBuckets),
719
+ );
720
+ const validatedOrdered = dispatchPlan.validatedOrdered;
721
+ // Build the candidate-index owner once at the frame boundary. Directional
722
+ // CPU fallback consumes the same multi-view membership contract as the
723
+ // indirect path and must not rediscover selected casters by scanning the
724
+ // complete dispatch for every cascade.
725
+ const validatedByCandidateIndex = new Map<number, ValidatedRenderable>();
726
+ const validatedIndexByCandidateIndex = new Map<number, number>();
727
+ for (let validatedIndex = 0; validatedIndex < validatedOrdered.length; validatedIndex += 1) {
728
+ const entry = validatedOrdered[validatedIndex];
729
+ if (entry === undefined) continue;
730
+ const candidateIndex = entry.source.candidateIndex ?? entry.renderableIndex;
731
+ if (!validatedByCandidateIndex.has(candidateIndex)) {
732
+ validatedByCandidateIndex.set(candidateIndex, entry);
733
+ validatedIndexByCandidateIndex.set(candidateIndex, validatedIndex);
734
+ }
735
+ }
736
+ // Build the compatibility shadow geometry owner once. Directional CSM
737
+ // uses its compact per-cascade submission; point/spot fallback passes
738
+ // need the main-view domain and the original validated slot for their
739
+ // dynamic mesh offset. Keeping both facts here removes a full
740
+ // validatedOrdered walk from every point face and spot tile while
741
+ // preserving the existing overflow-conservative behavior.
742
+ const mainSubmission = multiViewSubmissions.find((submission) => submission.kind === 'main');
743
+ const mainShadowCandidateIndices =
744
+ mainSubmission === undefined || mainSubmission.overflow
745
+ ? undefined
746
+ : mainSubmission.candidateIndices;
747
+ const validatedShadowEntries = Object.freeze(
748
+ validatedOrdered.flatMap((entry, validatedIndex) => {
749
+ const candidateIndex = entry.source.candidateIndex ?? entry.renderableIndex;
750
+ if (
751
+ mainShadowCandidateIndices !== undefined &&
752
+ !mainShadowCandidateIndices.has(candidateIndex)
753
+ ) {
754
+ return [];
755
+ }
756
+ return [{ entry, validatedIndex }];
757
+ }),
758
+ );
759
+ const foldDispatchPlan = dispatchPlan.foldDispatchPlan;
760
+ const materialSlotIndices = dispatchPlan.materialSlotIndices;
761
+ const materialSlots = dispatchPlan.materialSlots;
762
+ const materialSlotOwners = dispatchPlan.materialSlotOwners;
763
+ const materialSlotCount = dispatchPlan.materialSlotCount;
764
+
765
+ // D-2 (bug-20260527): LDR sprite pass split, generalised feat-20260625
766
+ // M2 / w7 via {@link computeSplitLdrSprite}; M3 w13 finalised by deleting
767
+ // the legacy shadingModel arm — transparent is the single SSOT. AC-05
768
+ // (non-sprite shader carrying transparent:true) trips the split too;
769
+ // the unit suite render-system-record.test.ts 'transparent decouples
770
+ // from sprite shader' locks the contract.
771
+ //
772
+ // When the LDR path (tonemapActive=false) has transparent entities in
773
+ // the validated draw list, the render is split into two serial passes
774
+ // sharing one color attachment. The transparent pass format is resolved
775
+ // by transparentPassColorFormat: native linear-LDR frames use the
776
+ // graph-owned `ldrColor` attachment (which may be rgba16float), while
777
+ // the swap-chain fallback uses its raw storage view. The encoder and
778
+ // sprite PSO must use the same resolved format because WebGPU requires
779
+ // attachment and pipeline target formats to match.
780
+ const splitLdrSprite = computeSplitLdrSprite(validatedOrdered, tonemapActive);
781
+ let ldrSpritePassView: TextureView | null = null;
782
+ if (splitLdrSprite) {
783
+ const unormViewRes = internals.device.createTextureView(currentTexture, {});
784
+ if (!unormViewRes.ok) {
785
+ internals.errorRegistry.fire(unormViewRes.error);
786
+ return false;
787
+ }
788
+ ldrSpritePassView = unormViewRes.value;
789
+ }
790
+
791
+ // View / mesh uniform uploads are only needed when geometry will be drawn,
792
+ // OR when a skybox is active (skybox pass reads inverseViewProj from
793
+ // the View UBO). Skip the writeBuffer round-trips on the Case E
794
+ // (clear-pass-only) path only when neither condition is met.
795
+ // feat-20260531-skybox-env-background M2 / w6: gate relaxed from
796
+ // `validatedOrdered.length > 0` to include skybox-only frames
797
+ // (plan-strategy D-3, R-3). Feature-only passes (for example VFX) also
798
+ // consume the View UBO through their group(0) contract even when the
799
+ // scene validation list is empty; keep that payload authoritative before
800
+ // the graph executes those passes.
801
+ const renderFeaturesActive = (internals.featureHost?.size ?? 0) > 0;
802
+ if (validatedOrdered.length > 0 || skyboxActive || atmosphereActive || renderFeaturesActive) {
803
+ runRecordProfilePhase(profilePhase, 'record/uploads', () => {
804
+ // View UBO + CSM/spot-shadow matrix pack: assembled + uploaded in one
805
+ // queue.writeBuffer round-trip. Extracted to view-ubo.ts (M3/w18) so
806
+ // recordFrame stays an orchestration skeleton (D-2).
807
+ writeViewUbo(
808
+ internals.device.queue,
809
+ pipelineState.viewUniformBuffer,
810
+ camera,
811
+ light,
812
+ lights,
813
+ frameState.spotShadowSnapshots,
814
+ temporalAttempt?.view,
815
+ fog,
816
+ );
817
+ writeShadowCasterUniforms(
818
+ internals.device.queue,
819
+ pipelineState.shadowCasterCascadeBuffer,
820
+ lights,
821
+ );
822
+
823
+ // Per-frame full rewrite of the PointLight + SpotLight std430 storage
824
+ // buffers (header + first-slice cap N=4 slots). Extracted to
825
+ // writePointSpotLightBuffers (M3/w18).
826
+ writePointSpotLightBuffers(internals, pipelineState, lights);
827
+
828
+ // Per-renderable entity_world upload (batched): all N mat4+normalMatrix
829
+ // slots assembled into a single contiguous scratch buffer, then flushed
830
+ // as one writeBuffer call. Extracted to mesh-ssbo.ts (M3/w21) so its
831
+ // module-scoped scratch buffer co-locates with the other mesh-SSBO lets.
832
+ uploadMeshSsboBatch(
833
+ internals.device.queue,
834
+ pipelineState.meshStorageBuffer,
835
+ validatedOrdered,
836
+ foldDispatchPlan,
837
+ temporalAttempt?.reactiveRevisionChanged ?? false,
838
+ );
839
+ });
840
+ }
841
+
842
+ const preparedDirectionalShadowCache = runRecordProfilePhase(
843
+ profilePhase,
844
+ 'record/scene-state/directional-shadow-cache',
845
+ () =>
846
+ prepareDirectionalShadowCache(
847
+ internals,
848
+ frameState,
849
+ worlds,
850
+ lights,
851
+ effectiveShadowMapSize,
852
+ validatedOrdered,
853
+ cpuVisibility,
854
+ gpuDriven?.scene,
855
+ ),
856
+ );
857
+ const directionalShadowTileClearPipeline =
858
+ internals.getDirectionalShadowTileClearPipeline?.() ?? null;
859
+ const directionalShadowCache =
860
+ !preparedDirectionalShadowCache.reuse &&
861
+ preparedDirectionalShadowCache.hitMask !== 0 &&
862
+ directionalShadowTileClearPipeline === null
863
+ ? { ...preparedDirectionalShadowCache, hitMask: 0 }
864
+ : preparedDirectionalShadowCache;
865
+ frameState.directionalShadowCacheRecorded = false;
866
+
867
+ const encoderResult = internals.device.createCommandEncoder({ label: 'render-system-frame' });
868
+ if (!encoderResult.ok) {
869
+ internals.errorRegistry.fire(encoderResult.error);
870
+ return false;
871
+ }
872
+ const encoder: RhiCommandEncoder = encoderResult.value;
873
+
874
+ // Per-frame bind group cache resolution (view / mesh / HDRP-cluster).
875
+ // Extracted to buildPerFrameBindGroups (M3/w18) so recordFrame stays a
876
+ // skeleton; returns null groups on the Case E (0-validated) path.
877
+ const { viewBindGroup, meshBindGroup, hdrpClusterBindGroup, hdrpClusterMembershipBindGroup } =
878
+ runRecordProfilePhase(profilePhase, 'record/bind-groups', () =>
879
+ buildPerFrameBindGroups(
880
+ internals,
881
+ frameState,
882
+ pipelineState,
883
+ validated.length > 0 || preparedGpuDriven !== undefined,
884
+ bindGroupCounts,
885
+ undefined,
886
+ false,
887
+ ),
888
+ );
889
+
890
+ // ── feat-20260529-rendergraph-pass-abstraction M4 / w13b ───────────
891
+ // The compiled typed graph is the sole per-frame pass owner.
892
+ //
893
+ // Every pass records into the shared encoder. The graph owns ordering and
894
+ // the renderer finishes and submits exactly once after graph execution.
895
+ // feat-20260601 M2 / w12: the per-frame shared state is the clean
896
+ // `RenderPipelineContext` - `internals` is replaced by the named `assets`
897
+ // (CPU POD) / `store` (GPU residency) / `pipelineState` / `runtime` (device +
898
+ // errorRegistry + shader-cache lookups) surfaces (`internals` itself satisfies
899
+ // `RenderSystemRuntime` so `runtime: internals` is a zero-cost reference). The
900
+ // 0-consumed `skyboxCount` residual is dropped.
901
+ const csmPlan = lights.directionalCsm;
902
+ const mainMembership = visibilityCandidateIndices(cpuVisibility, 'main');
903
+ const mainDispatch =
904
+ mainMembership === undefined
905
+ ? transparentDispatch
906
+ : transparentDispatch.filter((entry) => {
907
+ const source = renderables[entry.renderableIndex];
908
+ const candidateIndex = source?.candidateIndex ?? entry.renderableIndex;
909
+ return mainMembership.has(candidateIndex);
910
+ });
911
+
912
+ const passCtx: _InternalRenderPipelineContext = {
913
+ assets: internals.assets,
914
+ world,
915
+ gpuDrivenItemKeys: gpuOwnedItemKeys,
916
+ gpuDrivenOwnsAllForwardItems: gpuOwnsAllPrimaryRaster,
917
+ validatedByCandidateIndex,
918
+ validatedIndexByCandidateIndex,
919
+ validatedShadowEntries,
920
+ store: internals.gpuStore,
921
+ pipelineState,
922
+ runtime: internals,
923
+ temporalFrame: temporalAttempt,
924
+ encoder,
925
+ view,
926
+ clear,
927
+ targetW,
928
+ targetH,
929
+ currentTexture,
930
+ camera,
931
+ tonemapActive,
932
+ geometryColorView,
933
+ geometryDepthView,
934
+ geometryDepthKey,
935
+ validated,
936
+ validatedOrdered,
937
+ viewBindGroup,
938
+ meshBindGroup,
939
+ frameState,
940
+ dispatchCounts,
941
+ bindGroupCounts,
942
+ skylight,
943
+ skylightCount,
944
+ skyboxActive,
945
+ skybox,
946
+ splitLdrSprite,
947
+ ldrSpritePassView,
948
+ msaaActive,
949
+ geometryColorResolveView,
950
+ ldrSpriteColorView,
951
+ postProcessParams,
952
+ dispatch: mainDispatch,
953
+ shadowDispatch: transparentDispatch,
954
+ directionalCsmPlan: csmPlan,
955
+ shadowDebugObservation: internals.shadowDebugObservation,
956
+ hdrpClusterBindGroup,
957
+ hdrpClusterMembershipBindGroup,
958
+ foldDispatchPlan,
959
+ materialSlotIndices,
960
+ materialSlots,
961
+ materialSlotOwners,
962
+ materialSlotCount,
963
+ materialBgAssemblyCache: frameState.materialBgAssemblyCache,
964
+ directionalShadowCacheReuse: directionalShadowCache.reuse,
965
+ directionalShadowCacheTokens: directionalShadowCache.cascadeTokens,
966
+ directionalShadowCacheHitMask: directionalShadowCache.hitMask,
967
+ multiViewSubmissions,
968
+ ...(profilePhase !== undefined ? { profilePhase } : {}),
969
+ };
970
+ frameSubmitted = runRecordProfilePhase(profilePhase, 'record/graph-execute', () =>
971
+ executeCompiledFrameGraph(
972
+ internals,
973
+ frameState,
974
+ passCtx,
975
+ encoder,
976
+ profilePhase === undefined
977
+ ? undefined
978
+ : (pass, encode) => profilePhase(graphExecutionPhase(pass.name), encode),
979
+ temporalAttempt,
980
+ environmentAttempt,
981
+ ),
982
+ );
983
+ if (frameSubmitted && !directionalShadowCache.reuse) {
984
+ frameState.directionalShadowCache = frameState.directionalShadowCacheRecorded
985
+ ? directionalShadowCache.next
986
+ : null;
987
+ }
988
+ return frameSubmitted;
989
+ } finally {
990
+ temporalAttempt?.abort();
991
+ if (!frameSubmitted) environmentAttempt?.abort('execute');
992
+ frameState.frameNumber += 1;
993
+ // feat-20260608-cluster-lighting M5 / w22: clear HDRP once-per-frame fired
994
+ // set so the next frame re-fires if the condition persists.
995
+ frameState.hdrpOncePerFrameFired.clear();
996
+ }
997
+ }
998
+
999
+ interface DirectionalShadowCacheDecision {
1000
+ readonly reuse: boolean;
1001
+ readonly next: DirectionalShadowCache | null;
1002
+ readonly cascadeTokens: readonly string[];
1003
+ readonly hitMask: number;
1004
+ }
1005
+
1006
+ export type DirectionalShadowCacheMissReason =
1007
+ | 'no-entry'
1008
+ | 'shadow-map-size'
1009
+ | 'cascade-count'
1010
+ | 'pipeline'
1011
+ | 'graph-topology'
1012
+ | 'scene-mode'
1013
+ | 'asset-catalog'
1014
+ | 'mesh-residency'
1015
+ | 'world-state'
1016
+ | 'cascade-token';
1017
+
1018
+ function observeDirectionalShadowTokenBuild(
1019
+ metrics: EngineMetrics,
1020
+ enabled: boolean,
1021
+ built: { readonly tokens: readonly string[]; readonly productionSceneToken: boolean },
1022
+ ): void {
1023
+ if (!enabled || built.tokens.length === 0) return;
1024
+ metrics.increment('shadow.csm.token-build');
1025
+ metrics.increment(
1026
+ built.productionSceneToken
1027
+ ? 'shadow.csm.token-production-scene'
1028
+ : 'shadow.csm.token-validated-fallback',
1029
+ );
1030
+ for (const _token of built.tokens) metrics.increment('shadow.csm.token-cascade');
1031
+ }
1032
+
1033
+ function observeDirectionalShadowCacheDecision(
1034
+ metrics: EngineMetrics,
1035
+ enabled: boolean,
1036
+ decision: Pick<DirectionalShadowCacheDecision, 'reuse' | 'cascadeTokens' | 'hitMask'>,
1037
+ missReason: DirectionalShadowCacheMissReason | undefined,
1038
+ ): void {
1039
+ if (!enabled || decision.cascadeTokens.length === 0) return;
1040
+ metrics.increment(decision.reuse ? 'shadow.csm.cache.reuse' : 'shadow.csm.cache.refresh');
1041
+ if (decision.hitMask !== 0 && !decision.reuse) {
1042
+ metrics.increment('shadow.csm.cache.partial-hit');
1043
+ }
1044
+ for (let index = 0; index < decision.cascadeTokens.length; index += 1) {
1045
+ metrics.increment(
1046
+ (decision.hitMask & (1 << index)) !== 0
1047
+ ? 'shadow.csm.cache.cascade-hit'
1048
+ : 'shadow.csm.cache.cascade-miss',
1049
+ );
1050
+ }
1051
+ if (missReason !== undefined) metrics.increment(`shadow.csm.cache.miss.${missReason}`);
1052
+ }
1053
+
1054
+ function classifyDirectionalShadowCacheMiss(input: {
1055
+ readonly cached: DirectionalShadowCache | null;
1056
+ readonly built: { readonly productionSceneToken: boolean };
1057
+ readonly shadowMapSize: number | undefined;
1058
+ readonly cascadeCount: number | undefined;
1059
+ readonly pipelineHandle: number;
1060
+ readonly graphTopologyKey: string | null;
1061
+ readonly assetCatalogEpoch: number;
1062
+ readonly meshResidencyEpoch: number;
1063
+ readonly worldStateMatches: boolean;
1064
+ }): DirectionalShadowCacheMissReason {
1065
+ const cached = input.cached;
1066
+ if (cached === null) return 'no-entry';
1067
+ if (cached.shadowMapSize !== input.shadowMapSize) return 'shadow-map-size';
1068
+ if (cached.cascadeCount !== input.cascadeCount) return 'cascade-count';
1069
+ if (cached.pipelineHandle !== input.pipelineHandle) return 'pipeline';
1070
+ if (cached.graphTopologyKey !== input.graphTopologyKey) return 'graph-topology';
1071
+ if (cached.productionSceneToken !== input.built.productionSceneToken) return 'scene-mode';
1072
+ if (cached.assetCatalogEpoch !== input.assetCatalogEpoch) return 'asset-catalog';
1073
+ if (cached.meshResidencyEpoch !== input.meshResidencyEpoch) return 'mesh-residency';
1074
+ if (!input.built.productionSceneToken && !input.worldStateMatches) return 'world-state';
1075
+ return 'cascade-token';
1076
+ }
1077
+
1078
+ /**
1079
+ * @internal Pure cache decision owner used by recordFrame and its semantic
1080
+ * invalidation tests. Keeping the comparison separate makes the dangerous
1081
+ * path explicit: a hit is only reusable when every cascade token matches and
1082
+ * all frame/resource owners agree; a forced redraw is represented by
1083
+ * `reuse:false` even when one cascade happens to match.
1084
+ */
1085
+ export function evaluateDirectionalShadowCache(input: {
1086
+ readonly cached: DirectionalShadowCache | null;
1087
+ readonly cascadeTokens: readonly string[];
1088
+ readonly productionSceneToken: boolean;
1089
+ readonly shadowMapSize: number | undefined;
1090
+ readonly cascadeCount: number | undefined;
1091
+ readonly pipelineHandle: number;
1092
+ readonly graphTopologyKey: string | null;
1093
+ readonly assetCatalogEpoch: number;
1094
+ readonly meshResidencyEpoch: number;
1095
+ readonly worldStateMatches: boolean;
1096
+ }): {
1097
+ readonly reuse: boolean;
1098
+ readonly hitMask: number;
1099
+ readonly missReason: DirectionalShadowCacheMissReason | undefined;
1100
+ } {
1101
+ const cached = input.cached;
1102
+ const commonHit =
1103
+ cached !== null &&
1104
+ cached.shadowMapSize === input.shadowMapSize &&
1105
+ cached.cascadeCount === input.cascadeCount &&
1106
+ cached.pipelineHandle === input.pipelineHandle &&
1107
+ cached.graphTopologyKey === input.graphTopologyKey &&
1108
+ cached.productionSceneToken === input.productionSceneToken &&
1109
+ cached.assetCatalogEpoch === input.assetCatalogEpoch &&
1110
+ cached.meshResidencyEpoch === input.meshResidencyEpoch &&
1111
+ (input.productionSceneToken || input.worldStateMatches);
1112
+ let hitMask = 0;
1113
+ if (commonHit && cached !== null) {
1114
+ for (let index = 0; index < input.cascadeTokens.length; index += 1) {
1115
+ if (cached.cascadeTokens[index] === input.cascadeTokens[index]) hitMask |= 1 << index;
1116
+ }
1117
+ }
1118
+ const reuse = input.cascadeTokens.length > 0 && hitMask === (1 << input.cascadeTokens.length) - 1;
1119
+ return {
1120
+ reuse,
1121
+ hitMask,
1122
+ missReason: reuse
1123
+ ? undefined
1124
+ : classifyDirectionalShadowCacheMiss({
1125
+ cached,
1126
+ built: { productionSceneToken: input.productionSceneToken },
1127
+ shadowMapSize: input.shadowMapSize,
1128
+ cascadeCount: input.cascadeCount,
1129
+ pipelineHandle: input.pipelineHandle,
1130
+ graphTopologyKey: input.graphTopologyKey,
1131
+ assetCatalogEpoch: input.assetCatalogEpoch,
1132
+ meshResidencyEpoch: input.meshResidencyEpoch,
1133
+ worldStateMatches: input.worldStateMatches,
1134
+ }),
1135
+ };
1136
+ }
1137
+
1138
+ const SHADOW_TOKEN_NUMBER = new DataView(new ArrayBuffer(8));
1139
+
1140
+ function hashShadowTokenByte(hash: number, value: number): number {
1141
+ return Math.imul((hash ^ value) >>> 0, 16_777_619) >>> 0;
1142
+ }
1143
+
1144
+ function hashShadowTokenNumber(hash: number, value: number | undefined): number {
1145
+ SHADOW_TOKEN_NUMBER.setFloat64(0, value ?? Number.NaN, true);
1146
+ let next = hash;
1147
+ for (let index = 0; index < 8; index += 1) {
1148
+ next = hashShadowTokenByte(next, SHADOW_TOKEN_NUMBER.getUint8(index));
1149
+ }
1150
+ return next;
1151
+ }
1152
+
1153
+ function hashShadowTokenString(hash: number, value: string): number {
1154
+ let next = hash;
1155
+ for (let index = 0; index < value.length; index += 1) {
1156
+ const code = value.charCodeAt(index);
1157
+ next = hashShadowTokenByte(next, code & 0xff);
1158
+ next = hashShadowTokenByte(next, code >>> 8);
1159
+ }
1160
+ return next;
1161
+ }
1162
+
1163
+ function hashShadowTokenNumbers(hash: number, values: ArrayLike<number>): number {
1164
+ let next = hash;
1165
+ for (let index = 0; index < values.length; index += 1) {
1166
+ next = hashShadowTokenNumber(next, values[index]);
1167
+ }
1168
+ return next;
1169
+ }
1170
+
1171
+ /** @internal Deterministic cascade-local invalidation token producer. */
1172
+ export function buildCascadeLocalCacheTokens(input: {
1173
+ readonly lights: ExtractedLights;
1174
+ readonly visibility: CpuMultiViewVisibilityResult | undefined;
1175
+ readonly scene: PersistentGpuDrivenState | undefined;
1176
+ readonly validated: readonly ValidatedRenderable[];
1177
+ }): { readonly tokens: readonly string[]; readonly productionSceneToken: boolean } {
1178
+ const plan = input.lights.directionalCsm;
1179
+ if (plan === undefined) return { tokens: [], productionSceneToken: false };
1180
+ const productionSceneToken = input.visibility !== undefined && input.scene !== undefined;
1181
+ // PersistentRenderScene already owns O(1) slot lookup. The fallback map is
1182
+ // retained only for older test/custom state doubles that do not expose the
1183
+ // projection owner; it is intentionally not identity-cached because those
1184
+ // doubles may mutate their slots array in place.
1185
+ const slotByStableId =
1186
+ input.scene === undefined || input.scene.projection !== undefined
1187
+ ? undefined
1188
+ : shadowSlotIndex(input.scene.slots);
1189
+ const submissionMembership =
1190
+ input.scene === undefined ? undefined : buildSubmissionCandidateMembership(input.scene.plan);
1191
+ const cascadeCount = Math.min(input.lights.cascadeCount ?? 0, plan.cascades.length);
1192
+ const tokens: string[] = [];
1193
+ for (let cascadeIndex = 0; cascadeIndex < cascadeCount; cascadeIndex += 1) {
1194
+ const cascade = plan.cascades[cascadeIndex];
1195
+ if (cascade === undefined) continue;
1196
+ let hash = 2_166_136_261;
1197
+ hash = hashShadowTokenString(hash, 'csm-cache-v2');
1198
+ hash = hashShadowTokenNumber(hash, cascadeIndex);
1199
+ hash = hashShadowTokenNumber(hash, cascade.revision);
1200
+ hash = hashShadowTokenNumbers(hash, cascade.lightViewProj);
1201
+ hash = hashShadowTokenNumbers(hash, cascade.clipLightBounds);
1202
+ hash = hashShadowTokenNumbers(hash, cascade.casterBroadphaseWorldAabb);
1203
+ hash = hashShadowTokenNumber(hash, cascade.rasterViewport.x);
1204
+ hash = hashShadowTokenNumber(hash, cascade.rasterViewport.y);
1205
+ hash = hashShadowTokenNumber(hash, cascade.rasterViewport.w);
1206
+ hash = hashShadowTokenNumber(hash, cascade.rasterViewport.h);
1207
+
1208
+ const visibilityView = input.visibility?.views.find(
1209
+ (view) => view.kind === 'cascade' && view.cascadeIndex === cascadeIndex,
1210
+ );
1211
+ if (
1212
+ productionSceneToken &&
1213
+ visibilityView !== undefined &&
1214
+ input.scene !== undefined &&
1215
+ (slotByStableId !== undefined || input.scene.projection !== undefined)
1216
+ ) {
1217
+ const visibleStableIds = sortedUniqueStableIds(visibilityView.stableIds);
1218
+ for (const stableId of visibleStableIds) {
1219
+ const slot = input.scene.projection?.slotByIndex(stableId) ?? slotByStableId?.get(stableId);
1220
+ if (slot === undefined) continue;
1221
+ const revisions = slot.fact.revisions;
1222
+ hash = hashShadowTokenNumber(hash, stableId);
1223
+ hash = hashShadowTokenNumber(hash, slot.generation);
1224
+ hash = hashShadowTokenNumber(hash, revisions.transform);
1225
+ hash = hashShadowTokenNumber(hash, revisions.geometry);
1226
+ hash = hashShadowTokenNumber(hash, revisions.material);
1227
+ hash = hashShadowTokenNumber(hash, revisions.eligibility);
1228
+ hash = hashShadowTokenNumber(hash, revisions.space);
1229
+ hash = hashShadowTokenNumber(hash, revisions.batch);
1230
+ hash = hashShadowTokenNumber(hash, revisions.resource);
1231
+ }
1232
+ const visibleBatchIds = new Set<number>();
1233
+ if (submissionMembership !== undefined) {
1234
+ for (const stableId of visibleStableIds) {
1235
+ for (const entry of submissionMembership.byPrimitiveIndex.get(stableId) ?? []) {
1236
+ visibleBatchIds.add(entry.batchId);
1237
+ }
1238
+ }
1239
+ // SubmissionPlan owns a stable numeric batch order. Normalize the
1240
+ // token input explicitly instead of inheriting Set insertion order
1241
+ // from primitive/cascade traversal; a reorder must be a conservative
1242
+ // miss at most, never a semantic token change by accident.
1243
+ for (const batchId of [...visibleBatchIds].sort((left, right) => left - right)) {
1244
+ const batch = submissionMembership.byBatchId.get(batchId);
1245
+ if (batch === undefined) continue;
1246
+ hash = hashShadowTokenNumber(hash, batch.batchId);
1247
+ hash = hashShadowTokenNumber(hash, batch.generation);
1248
+ hash = hashShadowTokenNumber(hash, batch.key.resourceRevision);
1249
+ hash = hashShadowTokenNumber(hash, batch.key.materialRevision);
1250
+ hash = hashShadowTokenString(hash, batch.key.pipelineClass);
1251
+ hash = hashShadowTokenString(hash, batch.key.materialClass);
1252
+ hash = hashShadowTokenString(hash, batch.key.materialResourceClass);
1253
+ }
1254
+ }
1255
+ } else {
1256
+ hash = hashShadowTokenNumbers(hash, cascade.casterRenderableIndices);
1257
+ const casterMembership = new Set(cascade.casterRenderableIndices);
1258
+ for (const entry of input.validated) {
1259
+ const candidateIndex = entry.source.candidateIndex ?? entry.renderableIndex;
1260
+ if (!casterMembership.has(candidateIndex)) continue;
1261
+ const revisions = entry.source.sceneFact?.revisions;
1262
+ hash = hashShadowTokenNumber(hash, candidateIndex);
1263
+ hash = hashShadowTokenNumber(hash, revisions?.transform);
1264
+ hash = hashShadowTokenNumber(hash, revisions?.geometry);
1265
+ hash = hashShadowTokenNumber(hash, revisions?.material);
1266
+ hash = hashShadowTokenNumber(hash, revisions?.eligibility);
1267
+ hash = hashShadowTokenNumber(hash, revisions?.space);
1268
+ hash = hashShadowTokenNumber(hash, revisions?.batch);
1269
+ hash = hashShadowTokenNumber(hash, revisions?.resource);
1270
+ }
1271
+ }
1272
+ tokens.push(`csm-${cascadeIndex}-${hash.toString(16).padStart(8, '0')}`);
1273
+ }
1274
+ return { tokens: Object.freeze(tokens), productionSceneToken };
1275
+ }
1276
+
1277
+ function snapshotWorldState(world: World): DirectionalShadowWorldState {
1278
+ return { projection: createWorldProjection(world) };
1279
+ }
1280
+
1281
+ function sameWorldState(cached: DirectionalShadowCache, worlds: readonly World[]): boolean {
1282
+ if (cached.worlds.length !== worlds.length) return false;
1283
+ for (let index = 0; index < worlds.length; index += 1) {
1284
+ const world = worlds[index];
1285
+ if (world === undefined || cached.worlds[index] !== world) return false;
1286
+ const cachedState = cached.worldStateTokens[index];
1287
+ if (cachedState === undefined) return false;
1288
+ const evidence = cachedState.projection.poll();
1289
+ if (evidence.status !== 'delta' || evidence.changes.length > 0) return false;
1290
+ }
1291
+ return true;
1292
+ }
1293
+
1294
+ function prepareDirectionalShadowCache(
1295
+ internals: RenderSystemInternals,
1296
+ frameState: RenderFrameState,
1297
+ worlds: readonly World[],
1298
+ lights: ExtractedLights,
1299
+ shadowMapSize: number | undefined,
1300
+ validatedOrdered: readonly ValidatedRenderable[],
1301
+ visibility: CpuMultiViewVisibilityResult | undefined,
1302
+ scene: PersistentGpuDrivenState | undefined,
1303
+ ): DirectionalShadowCacheDecision {
1304
+ const observeShadowCensus = internals.shadowDebugObservation === true;
1305
+ const cascadeCount = lights.cascadeCount;
1306
+ const built = buildCascadeLocalCacheTokens({
1307
+ lights,
1308
+ visibility,
1309
+ scene,
1310
+ validated: validatedOrdered,
1311
+ });
1312
+ observeDirectionalShadowTokenBuild(internals.metrics, observeShadowCensus, built);
1313
+ if (
1314
+ frameState.compiledFrameGraph === null ||
1315
+ frameState.compiledFrameGraphTopologyKey === null ||
1316
+ shadowMapSize === undefined ||
1317
+ shadowMapSize <= 0 ||
1318
+ cascadeCount === undefined ||
1319
+ cascadeCount <= 0 ||
1320
+ built.tokens.length === 0 ||
1321
+ validatedOrdered.length === 0
1322
+ ) {
1323
+ if (observeShadowCensus && built.tokens.length > 0) {
1324
+ internals.metrics.increment('shadow.csm.cache.precondition');
1325
+ }
1326
+ return { reuse: false, next: null, cascadeTokens: built.tokens, hitMask: 0 };
1327
+ }
1328
+
1329
+ const cached = frameState.directionalShadowCache;
1330
+ const assetCatalogEpoch = internals.assets.epoch;
1331
+ const meshResidencyEpoch = internals.gpuStore.meshResidencyEpoch;
1332
+ const evaluation = evaluateDirectionalShadowCache({
1333
+ cached,
1334
+ cascadeTokens: built.tokens,
1335
+ productionSceneToken: built.productionSceneToken,
1336
+ shadowMapSize,
1337
+ cascadeCount,
1338
+ pipelineHandle: frameState.installedPipelineHandle,
1339
+ graphTopologyKey: frameState.compiledFrameGraphTopologyKey,
1340
+ assetCatalogEpoch,
1341
+ meshResidencyEpoch,
1342
+ worldStateMatches:
1343
+ cached !== null ? built.productionSceneToken || sameWorldState(cached, worlds) : false,
1344
+ });
1345
+ if (evaluation.reuse) {
1346
+ const decision = {
1347
+ reuse: true,
1348
+ next: null,
1349
+ cascadeTokens: built.tokens,
1350
+ hitMask: evaluation.hitMask,
1351
+ };
1352
+ observeDirectionalShadowCacheDecision(
1353
+ internals.metrics,
1354
+ observeShadowCensus,
1355
+ decision,
1356
+ undefined,
1357
+ );
1358
+ return decision;
1359
+ }
1360
+
1361
+ const decision = {
1362
+ reuse: false,
1363
+ next: null,
1364
+ cascadeTokens: built.tokens,
1365
+ hitMask: evaluation.hitMask,
1366
+ };
1367
+ observeDirectionalShadowCacheDecision(
1368
+ internals.metrics,
1369
+ observeShadowCensus,
1370
+ decision,
1371
+ observeShadowCensus ? evaluation.missReason : undefined,
1372
+ );
1373
+
1374
+ const worldStateTokens = worlds.map((world) => snapshotWorldState(world));
1375
+ if (worldStateTokens.some((state) => state === null)) {
1376
+ // A world that does not expose the ECS mutation tokens cannot safely
1377
+ // participate in a static-shadow reuse decision. Keep the conservative
1378
+ // path for custom/test worlds instead of collapsing token indexes with a
1379
+ // filter and risking a false-positive match.
1380
+ if (observeShadowCensus) {
1381
+ internals.metrics.increment('shadow.csm.cache.world-state-unavailable');
1382
+ }
1383
+ return decision;
1384
+ }
1385
+
1386
+ return {
1387
+ reuse: false,
1388
+ cascadeTokens: built.tokens,
1389
+ hitMask: evaluation.hitMask,
1390
+ next: {
1391
+ worlds: worlds.slice(),
1392
+ worldStateTokens,
1393
+ assetCatalogEpoch,
1394
+ pipelineHandle: frameState.installedPipelineHandle,
1395
+ graphTopologyKey: frameState.compiledFrameGraphTopologyKey,
1396
+ shadowMapSize,
1397
+ cascadeCount,
1398
+ meshResidencyEpoch,
1399
+ cascadeTokens: built.tokens,
1400
+ productionSceneToken: built.productionSceneToken,
1401
+ },
1402
+ };
1403
+ }
1404
+
1405
+ /**
1406
+ * feat-20260704 M3/w18: validate renderable handles + collect the render plan,
1407
+ * extracted verbatim from `recordFrame`. Empty `renderables` input or all-
1408
+ * unregistered handles both yield an empty result (the Case E clear-pass-only
1409
+ * path). Builds per-entry renderState / stencilReference overlays from the
1410
+ * transparent-dispatch entries, resolves each MeshFilter.assetHandle through
1411
+ * the AssetRegistry + GPU store (with the sprite 9-slice mesh swap), and fires
1412
+ * structured `asset-not-registered` errors for handles that fail to resolve.
1413
+ *
1414
+ * @internal
1415
+ */
1416
+ function validateRenderables(
1417
+ internals: RenderSystemInternals,
1418
+ world: World,
1419
+ // feat-20260709-editor-world-partition ENGINE-fix-round2 (defect 2): the full
1420
+ // Each renderable's mesh handle is a slot in its OWN world's
1421
+ // sharedRefs; resolving against a foreign world either misses or resolves
1422
+ // the wrong slot payload. Production resolves stable WorldRenderId through
1423
+ // the renderer-owned lookup. Direct tests without that lookup retain the
1424
+ // historical ordered-array fallback.
1425
+ worlds: readonly World[],
1426
+ renderables: readonly RenderableSnapshot[],
1427
+ transparentDispatch: readonly DispatchEntry[],
1428
+ worldForRenderId?: (worldId: number) => World | undefined,
1429
+ ): ValidatedRenderable[] {
1430
+ // bug-20260527-renderstate-pipeline-dispatch-gap D-4:
1431
+ // build a renderableIndex -> renderState map from dispatch entries
1432
+ // so each ValidatedRenderable carries its per-material renderState
1433
+ // override without an O(n^2) back-scan in the draw loop.
1434
+ const renderStateByRenderableIdx = new Map<number, MaterialRenderState | undefined>();
1435
+ // w10: also build a renderableIndex -> stencilReference map from
1436
+ // dispatch entries for per-draw setStencilReference calls.
1437
+ const stencilRefByRenderableIdx = new Map<number, number | undefined>();
1438
+ const variantSetByRenderableIdx = new Map<number, string | undefined>();
1439
+ // Keep these three independent maps because each overlay has its own
1440
+ // consumer and the last dispatch entry remains authoritative. Populate
1441
+ // them in one pass: the old code walked the same dispatch list three times
1442
+ // and repeated the same renderableIndex branch / hash writes.
1443
+ for (const de of transparentDispatch) {
1444
+ const renderableIndex = de.renderableIndex;
1445
+ if (renderableIndex === undefined) continue;
1446
+ renderStateByRenderableIdx.set(renderableIndex, de.renderState);
1447
+ stencilRefByRenderableIdx.set(renderableIndex, de.stencilReference);
1448
+ variantSetByRenderableIdx.set(renderableIndex, variantSetFromDefines(de.defines));
1449
+ }
1450
+ const validated: ValidatedRenderable[] = [];
1451
+ for (let rIdx = 0; rIdx < renderables.length; rIdx++) {
1452
+ const r = renderables[rIdx];
1453
+ if (r === undefined) continue;
1454
+ // feat-20260709-editor-world-partition ENGINE-fix-round2 (defect 2): resolve
1455
+ // this renderable's mesh against the world it was EXTRACTED from — NOT the
1456
+ // single resource-owner `world`.
1457
+ const renderableWorld =
1458
+ worldForRenderId === undefined ? (worlds[r.worldId] ?? world) : worldForRenderId(r.worldId);
1459
+ if (renderableWorld === undefined) {
1460
+ internals.errorRegistry.fire(
1461
+ new RhiError({
1462
+ code: 'asset-not-registered',
1463
+ expected: `attached renderer WorldRenderId ${r.worldId}`,
1464
+ hint: 'keep every World passed to renderer.draw attached until its frame retires',
1465
+ detail: { assetHandle: r.assetHandle },
1466
+ }),
1467
+ );
1468
+ continue;
1469
+ }
1470
+ const assetRes = resolveRenderAsset<MeshAsset>(
1471
+ renderableWorld,
1472
+ toShared<'MeshAsset'>(r.assetHandle),
1473
+ );
1474
+ if (!assetRes.ok) {
1475
+ internals.errorRegistry.fire(
1476
+ new RhiError({
1477
+ code: 'asset-not-registered',
1478
+ expected: 'MeshFilter.assetHandle in the owning World sharedRefs',
1479
+ hint: 'allocate or intern the MeshAsset in the same World before assigning MeshFilter.assetHandle',
1480
+ detail: { assetHandle: r.assetHandle },
1481
+ }),
1482
+ );
1483
+ continue;
1484
+ }
1485
+ const meshAssetHandle = toShared<'MeshAsset'>(r.assetHandle);
1486
+ let meshHandles = internals.gpuStore.getMeshGpuHandles(meshAssetHandle, r.worldId);
1487
+ if (meshHandles === undefined) {
1488
+ const residentRes = internals.gpuStore.ensureResident(
1489
+ meshAssetHandle,
1490
+ assetRes.value,
1491
+ r.worldId,
1492
+ );
1493
+ if (residentRes.ok) {
1494
+ meshHandles = residentRes.value;
1495
+ } else if (residentRes.error instanceof RhiError) {
1496
+ internals.errorRegistry.fire(residentRes.error);
1497
+ }
1498
+ }
1499
+ if (meshHandles === undefined) {
1500
+ internals.errorRegistry.fire(
1501
+ new RhiError({
1502
+ code: 'asset-not-registered',
1503
+ expected: 'GPU mesh buffers uploaded for assetHandle',
1504
+ hint: 'await renderer.ready before draw; keep the MeshAsset live in the renderable World',
1505
+ detail: { assetHandle: r.assetHandle },
1506
+ }),
1507
+ );
1508
+ continue;
1509
+ }
1510
+ validated.push({
1511
+ source: r,
1512
+ mesh: meshHandles,
1513
+ renderableIndex: rIdx,
1514
+ renderState: renderStateByRenderableIdx.get(rIdx),
1515
+ variantSet: variantSetByRenderableIdx.get(rIdx),
1516
+ stencilReference: stencilRefByRenderableIdx.get(rIdx),
1517
+ });
1518
+ }
1519
+ return validated;
1520
+ }
1521
+
1522
+ /**
1523
+ * feat-20260704 M3/w18: build the dispatch-ordered render plan, extracted
1524
+ * verbatim from `recordFrame`. (1) M3/w26 dispatch-ordered reorder: reorder
1525
+ * `validated` to follow the transparent-dispatch order (extract-order fallback
1526
+ * for unmatched entries). (2) feat-20260608 mesh-SSBO capacity gate: size the
1527
+ * mesh-SSBO + material-UBO pair to the larger of entity vs cumulative
1528
+ * material-slot count, truncating on ceiling (graceful degradation). (3)
1529
+ * feat-20260622 fold dispatch plan: build + apply the WebGL2 uniform-cap
1530
+ * fallback + bump the folded-draws metric.
1531
+ *
1532
+ * @internal
1533
+ */
1534
+ function buildDispatchPlan(
1535
+ internals: RenderSystemInternals,
1536
+ validated: readonly ValidatedRenderable[],
1537
+ transparentDispatch: readonly DispatchEntry[],
1538
+ foldBuckets: readonly FoldBucket[],
1539
+ ): {
1540
+ validatedOrdered: readonly ValidatedRenderable[];
1541
+ foldDispatchPlan: FoldDispatchPlan | null;
1542
+ materialSlotIndices: readonly (readonly number[])[];
1543
+ materialSlots: readonly MaterialSnapshot[];
1544
+ materialSlotOwners: readonly number[];
1545
+ materialSlotCount: number;
1546
+ } {
1547
+ // M3 / w26: dispatch-ordered render. The dispatch list is pre-sorted
1548
+ // by queue (ascending, stable) by the extract stage per plan-strategy D-3.
1549
+ // Reorder validated renderables to follow the dispatch order, falling
1550
+ // back to extract order for renderables with no matching dispatch entry.
1551
+ let dispatchOrderMatchesValidated = transparentDispatch.length === validated.length;
1552
+ if (dispatchOrderMatchesValidated) {
1553
+ for (let i = 0; i < validated.length; i++) {
1554
+ if (transparentDispatch[i]?.renderableIndex !== validated[i]?.renderableIndex) {
1555
+ dispatchOrderMatchesValidated = false;
1556
+ break;
1557
+ }
1558
+ }
1559
+ }
1560
+ let validatedOrdered: readonly ValidatedRenderable[] = dispatchOrderMatchesValidated
1561
+ ? validated
1562
+ : [...validated];
1563
+ if (transparentDispatch.length > 0 && !dispatchOrderMatchesValidated) {
1564
+ const validatedByRenderableIdx = new Map<number, ValidatedRenderable>();
1565
+ const seen = new Set<number>();
1566
+ for (const v of validated) {
1567
+ validatedByRenderableIdx.set(v.renderableIndex, v);
1568
+ }
1569
+ const ordered: ValidatedRenderable[] = [];
1570
+ for (const de of transparentDispatch) {
1571
+ if (de.renderableIndex === undefined) continue;
1572
+ const v = validatedByRenderableIdx.get(de.renderableIndex);
1573
+ if (v !== undefined && !seen.has(de.renderableIndex)) {
1574
+ seen.add(de.renderableIndex);
1575
+ ordered.push(v);
1576
+ }
1577
+ }
1578
+ // Append any renderables not in the dispatch list (e.g. default-material entities)
1579
+ for (const v of validated) {
1580
+ if (!seen.has(v.renderableIndex)) {
1581
+ ordered.push(v);
1582
+ }
1583
+ }
1584
+ validatedOrdered = ordered;
1585
+ }
1586
+
1587
+ const materialGroups = validatedOrdered.map((entry): readonly MaterialSnapshot[] => {
1588
+ const shaderId = entry.source.material.materialShaderId;
1589
+ if (shaderId === 'forgeax::sprite' || shaderId === 'forgeax::sprite-lit') {
1590
+ return [entry.source.material];
1591
+ }
1592
+ return entry.source.materials.length > 0 ? entry.source.materials : [entry.source.material];
1593
+ });
1594
+ const materialSlotPlan = buildMaterialSlotPlan(materialGroups);
1595
+ let materialSlotIndices = materialSlotPlan.slotIndices;
1596
+ let materialSlots = materialSlotPlan.slots;
1597
+ let materialSlotOwners = materialSlotPlan.slotOwners;
1598
+ let materialSlotCount = materialSlots.length;
1599
+
1600
+ // feat-20260608-mesh-ssbo-dynamic-grow-l1-lift-1024-entity-cap M3 / T-M3-04:
1601
+ // ensure the mesh-SSBO + material-UBO buffer pair is large enough to hold
1602
+ // the render plan BEFORE the first per-entity writeBuffer.
1603
+ // On `ok:false` the controller has already fired a structured RuntimeError
1604
+ // (`mesh-ssbo-ceiling-reached` / `mesh-ssbo-capacity-exceeded`); we truncate
1605
+ // the draw list to the largest complete entity prefix whose cumulative
1606
+ // material slots fit `degradedToSlotCount` (graceful degradation per
1607
+ // plan-strategy D-2): render the subset that fits, discard overflow, no
1608
+ // black frame. The helper is idempotent across same-frame re-calls (AC-09)
1609
+ // and short-circuits on length=0 / length<=slotCount (boundary table).
1610
+ //
1611
+ // bug-20260609: feat-20260608 M5 amend made the material UBO indexed by
1612
+ // cumulative *material-slot* count (one slot per submesh material),
1613
+ // which is >= entity count once any entity carries `materials.length>1`.
1614
+ // The mesh + material buffer pair share `slotCount` (single allocator),
1615
+ // so we size against the larger of the two requirements: entity count
1616
+ // (mesh-SSBO consumer) vs cumulative material-slot count (material-UBO
1617
+ // consumer). Sprite entities collapse to 1 slot in the material table,
1618
+ // mirroring the same rule (sprite per-submesh OOS-1;
1619
+ // post-w13 judgement key migrated to materialShaderId).
1620
+ //
1621
+ // feat-20260624 M1' / t7: sprite-lit treated identically to sprite
1622
+ // for material-slot accounting (paramSchema mirror, t4).
1623
+ const neededSlots = Math.max(validatedOrdered.length, materialSlotCount);
1624
+ const meshSsboCapResult = ensureMeshSsboCapacity(internals, neededSlots);
1625
+ if (!meshSsboCapResult.ok) {
1626
+ // Graceful degradation: the controller reports slots, but this stage
1627
+ // consumes entities. Find a complete prefix instead of slicing at the
1628
+ // numeric slot count; a multi-material entity can consume several slots.
1629
+ const degradedRenderableCount = findRenderablePrefixForSlotCapacity(
1630
+ materialSlotIndices,
1631
+ meshSsboCapResult.degradedToSlotCount,
1632
+ );
1633
+ materialSlotCount = materialSlotCountForPrefix(materialSlotIndices, degradedRenderableCount);
1634
+ materialSlotIndices = materialSlotIndices.slice(0, degradedRenderableCount);
1635
+ materialSlots = materialSlots.slice(0, materialSlotCount);
1636
+ materialSlotOwners = materialSlotOwners.slice(0, materialSlotCount);
1637
+ validatedOrdered = validatedOrdered.slice(0, degradedRenderableCount);
1638
+ }
1639
+
1640
+ // feat-20260622-chunk-gpu-instancing-sprite-tilemap M1 / w4-record-swap
1641
+ // (D-1): build the fold dispatch plan once `validatedOrdered` is final
1642
+ // (post truncation by mesh-SSBO capacity gate). The plan re-keys each
1643
+ // non-singleton bucket from `renderableIndex` to the validated-ordered
1644
+ // index `i` consumed by the dispatch loops; the loops use it to skip
1645
+ // non-head bucket members and emit one instanced drawIndexed per
1646
+ // bucket head. Empty plan (no fold-eligible buckets) is a byte-
1647
+ // identical no-op for the dispatch loops below (charter P3: silent
1648
+ // pass-through, no error path).
1649
+ let renderableToValidatedIdx: Map<number, number> | null = null;
1650
+ let foldDispatchPlan: FoldDispatchPlan | null = null;
1651
+ if (foldBuckets.length > 0) {
1652
+ renderableToValidatedIdx = new Map<number, number>();
1653
+ for (let i = 0; i < validatedOrdered.length; i++) {
1654
+ const e = validatedOrdered[i];
1655
+ if (e === undefined) continue;
1656
+ renderableToValidatedIdx.set(e.renderableIndex, i);
1657
+ }
1658
+ foldDispatchPlan = buildFoldDispatchPlan(foldBuckets, renderableToValidatedIdx);
1659
+
1660
+ // feat-20260622 M2 / w11 (D-2 + D-9 + AC-05): WebGL2 uniform-fallback
1661
+ // per-bucket instance-count cap. When caps.storageBuffer===false AND
1662
+ // a fold bucket carries more than FOLD_UNIFORM_INSTANCE_CAP (128)
1663
+ // instances, fire RhiError({code:'instancing-exceeds-uniform-cap'})
1664
+ // AND remove the bucket from the dispatch plan so its members fall
1665
+ // through to the per-entity drawIndexed exit (the same exit the
1666
+ // mode-gate bypass uses — D-9 "shared fallback exit"). The frame
1667
+ // stays visually correct (charter proposition 9 graceful
1668
+ // degradation: no identity-collapse / black screen) while the cap
1669
+ // event surfaces structurally for AI users (proposition 4 explicit
1670
+ // failure on .code).
1671
+ //
1672
+ // Scope discrimination: tilemap-chunk-extract-system encodes
1673
+ // Layer.value = (layerOrder<<20) | (chunkIndex & 0xfffff), so a
1674
+ // bucket whose head entry carries non-zero low-20-bits is
1675
+ // definitively a tilemap-chunk dispatch site. Plain sprite buckets
1676
+ // use SPRITE_LAYER_VALUE = layerOrder<<20 (low-20 zero) by the
1677
+ // documented convention (apps/hello/asi-world main.ts pattern).
1678
+ // The chunkIndex===0 edge case maps to 'sprite' (the helper's
1679
+ // default branch) — a one-bucket ambiguity per layerOrder that is
1680
+ // acceptable for the AI-user affordance level (the error semantics
1681
+ // — "this bucket exceeded the cap" — is the actionable signal;
1682
+ // scope=sprite vs tilemap-chunk only refines the recovery hint).
1683
+ if (foldDispatchPlan.headBuckets.size > 0 && !internals.device.caps.storageBuffer) {
1684
+ const filteredHeads = new Map<number, FoldBucket>(foldDispatchPlan.headBuckets);
1685
+ const filteredSkips = new Set<number>(foldDispatchPlan.skipIndices);
1686
+ let filteredCount = foldDispatchPlan.foldedBucketCount;
1687
+ for (const [headIdx, bucket] of foldDispatchPlan.headBuckets) {
1688
+ const scope: 'sprite' | 'tilemap-chunk' =
1689
+ (bucket.layer & 0xfffff) !== 0 ? 'tilemap-chunk' : 'sprite';
1690
+ const decision = evaluateFoldBucketUniformCap(bucket, internals.device.caps, scope);
1691
+ if (decision.fallback && decision.error !== undefined) {
1692
+ internals.errorRegistry.fire(decision.error);
1693
+ filteredHeads.delete(headIdx);
1694
+ filteredCount -= 1;
1695
+ for (let j = 1; j < bucket.entries.length; j++) {
1696
+ const memberEntry = bucket.entries[j];
1697
+ if (memberEntry === undefined) continue;
1698
+ const memberValidatedIdx = renderableToValidatedIdx.get(memberEntry.renderableIndex);
1699
+ if (memberValidatedIdx !== undefined) {
1700
+ filteredSkips.delete(memberValidatedIdx);
1701
+ }
1702
+ }
1703
+ }
1704
+ }
1705
+ if (filteredCount !== foldDispatchPlan.foldedBucketCount) {
1706
+ foldDispatchPlan = {
1707
+ headBuckets: filteredHeads,
1708
+ skipIndices: filteredSkips,
1709
+ foldedBucketCount: filteredCount,
1710
+ };
1711
+ }
1712
+ }
1713
+
1714
+ // feat-20260622-chunk-gpu-instancing-sprite-tilemap M3 / w13 (D-3 +
1715
+ // AC-06): increment `render.instancing.foldedDraws` once per fold-
1716
+ // eligible head bucket retained after the cap-fallback filter above.
1717
+ // The metric tracks instanced drawIndexed call count for this frame
1718
+ // — cap-overrun buckets routed through the per-entity fallback exit
1719
+ // are removed from `foldDispatchPlan` and therefore not counted, by
1720
+ // construction (M2 / w11 cap-fallback + plan-strategy D-3 semantics).
1721
+ // Singleton buckets (mode-bypass under D-5, or non-foldable under
1722
+ // mode 0) carry `bucketSize === 1` and never enter `headBuckets`, so
1723
+ // the per-entity drawIndexed path correctly does not count.
1724
+ // SSOT helper lives in `render-system-fold.ts` — engine code never
1725
+ // hardcodes the metric key string.
1726
+ incrementFoldedDrawsMetric(foldDispatchPlan, internals.metrics);
1727
+ }
1728
+
1729
+ return {
1730
+ validatedOrdered,
1731
+ foldDispatchPlan,
1732
+ materialSlotIndices,
1733
+ materialSlots,
1734
+ materialSlotOwners,
1735
+ materialSlotCount,
1736
+ };
1737
+ }
1738
+
1739
+ /**
1740
+ * feat-20260704 M3/w18: per-frame cache clean-up (despawn eviction), extracted
1741
+ * verbatim from `recordFrame`.
1742
+ *
1743
+ * feat-20260531-per-frame-bind-group-cache M4 / w14 (D-5): drop per-entity BG
1744
+ * cache entries (materialBgPerEntity / instancesBgPerEntity) + instance buffers
1745
+ * whose outer-Map entityKey (packed Entity u32) is absent from the current
1746
+ * validated set, preventing unbounded growth after entity despawn. view + mesh
1747
+ * caches are frame-shared (keyed by GPU resource handle objects, WeakMap chains
1748
+ * naturally bounded) and are not touched. feat-20260619 M4 / F11: destroy the
1749
+ * GPU instance buffer before Map.delete (D-6 symmetric release); failure fires
1750
+ * errorRegistry + continues the sweep.
1751
+ *
1752
+ * @internal
1753
+ */
1754
+ function cleanPerFrameCaches(
1755
+ internals: RenderSystemInternals,
1756
+ frameState: RenderFrameState,
1757
+ validated: readonly ValidatedRenderable[],
1758
+ retainedEntityKeys: ReadonlySet<number> = new Set<number>(),
1759
+ ): void {
1760
+ if (frameState.transientInstanceBuffers !== undefined) {
1761
+ disposeTransientInstanceBuffers(frameState.transientInstanceBuffers, internals.errorRegistry);
1762
+ }
1763
+ // Build a Set<number> of worldEntityKey composites from the validated
1764
+ // renderables. D-1a #4: validatedEntityKeys are worldEntityKey(worldId, entityKey)
1765
+ // composites matching the write-side keys of #1-#3 — cross-world false eviction
1766
+ // is prevented because worldEntityKey(0, k) !== worldEntityKey(1, k).
1767
+ const validatedEntityKeys = new Set<number>(retainedEntityKeys);
1768
+ for (const v of validated) {
1769
+ validatedEntityKeys.add(worldEntityKey(v.source.worldId, v.source.entityKey));
1770
+ }
1771
+
1772
+ // Clean per-entity material BG cache: drop outer-Map entries whose
1773
+ // entityKey is absent from the current validated set. The shared and
1774
+ // singleton material caches have no entityKey and are not touched here.
1775
+ cleanPerEntityCache(frameState.materialBgPerEntity, validatedEntityKeys);
1776
+
1777
+ // Clean per-entity instances BG cache.
1778
+ cleanPerEntityCache(frameState.instancesBgPerEntity, validatedEntityKeys);
1779
+
1780
+ // D-5 retrofit: instanceBuffers clean-up. The instanceBuffers Map is
1781
+ // keyed by cacheKey (packed Entity u32, same as entityKey on
1782
+ // RenderableSnapshot). Drop entries whose key is no longer in the
1783
+ // validated set (OQ-3 / R-4). feat-20260619 M4 / F11: destroy the GPU
1784
+ // buffer before Map.delete so despawned entities release their
1785
+ // instance-buffer backing memory symmetrically (D-6).
1786
+ //
1787
+ // D-1a #1: instanceBuffers keys on the positive half (>= 0) are
1788
+ // worldEntityKey composites matching the write side. Negative half
1789
+ // fold-bucket keys (< 0) are NOT worldEntityKey; they are
1790
+ // material-handle-based and cross-world collision is semantically
1791
+ // correct (same material renders in same fold bucket).
1792
+ for (const [key, entry] of frameState.instanceBuffers.entries()) {
1793
+ if (!validatedEntityKeys.has(key)) {
1794
+ if (!entry.buffer.isDestroyed) {
1795
+ const r = entry.buffer.destroy();
1796
+ if (!r.ok) internals.errorRegistry.fire(r.error);
1797
+ }
1798
+ frameState.instanceBuffers.delete(key);
1799
+ }
1800
+ }
1801
+ }
1802
+
1803
+ /**
1804
+ * feat-20260704 M3/w18: record-stage fold operator linear scan, extracted
1805
+ * verbatim from `recordFrame`.
1806
+ *
1807
+ * feat-20260622-chunk-gpu-instancing-sprite-tilemap M1 / w4 + w5 (D-1, D-5):
1808
+ * groups transparent-sort-ordered dispatch entries with equal (Layer.value,
1809
+ * sortKey, materialHandle) into fold buckets. Mode-gate (D-5 extended): modes 0
1810
+ * (LAYER_Z) and 1 (LAYER_Y) fold using the pos z/y lanes; modes 2/3 bypass per-entity
1811
+ * (each entry a singleton bucket). Records `frameState.lastFoldBucketCount` =
1812
+ * fold-eligible buckets (bucketSize > 1) for the AC-06 metric. Empty dispatch
1813
+ * short-circuits (test fixtures pass null world).
1814
+ *
1815
+ * @internal
1816
+ */
1817
+ function computeFoldBuckets(
1818
+ world: World,
1819
+ frameState: RenderFrameState,
1820
+ transparentDispatch: readonly DispatchEntry[],
1821
+ renderables: readonly RenderableSnapshot[],
1822
+ ): readonly FoldBucket[] {
1823
+ if (transparentDispatch.length === 0) {
1824
+ frameState.lastFoldBucketCount = 0;
1825
+ return [];
1826
+ }
1827
+ const transparentSortCfg = getTransparentSortConfig(world);
1828
+
1829
+ // Only layer-Z / layer-Y modes can produce non-singleton fold buckets.
1830
+ // The other modes make one inert singleton per entry, which is discarded by
1831
+ // buildFoldDispatchPlan. Returning the same empty plan here avoids allocating
1832
+ // a transform matrix for every renderable when folding is disabled by mode.
1833
+ if (
1834
+ transparentSortCfg.mode !== TRANSPARENT_SORT_MODE_LAYER_Z &&
1835
+ transparentSortCfg.mode !== TRANSPARENT_SORT_MODE_LAYER_Y
1836
+ ) {
1837
+ frameState.lastFoldBucketCount = 0;
1838
+ return [];
1839
+ }
1840
+
1841
+ // `transparentDispatch` is the legacy name for the complete sorted dispatch
1842
+ // list. Opaque entries can never participate in the transparent-only fold
1843
+ // plan, yet foldDispatchBuckets must preserve its general helper contract and
1844
+ // therefore materializes an inert singleton (including a Float32Array(16))
1845
+ // for each one. Filter only at this private production call site so the fold
1846
+ // helper keeps its testable semantics while record avoids work whose result
1847
+ // is provably discarded.
1848
+ let transparentCount = 0;
1849
+ for (const entry of transparentDispatch) {
1850
+ if (renderables[entry.renderableIndex]?.material.transparent === true) {
1851
+ transparentCount += 1;
1852
+ }
1853
+ }
1854
+ if (transparentCount === 0) {
1855
+ frameState.lastFoldBucketCount = 0;
1856
+ return [];
1857
+ }
1858
+ const foldCandidates =
1859
+ transparentCount === transparentDispatch.length
1860
+ ? transparentDispatch
1861
+ : transparentDispatch.filter(
1862
+ (entry) => renderables[entry.renderableIndex]?.material.transparent === true,
1863
+ );
1864
+ const foldBuckets = foldDispatchBuckets(foldCandidates, transparentSortCfg.mode, renderables);
1865
+ // Count only fold-eligible buckets (bucketSize > 1) so the metric
1866
+ // surfaces fold actually reducing draws — singleton buckets under
1867
+ // mode bypass do not change draw count, so they do not contribute.
1868
+ let foldEligibleCount = 0;
1869
+ for (const b of foldBuckets) {
1870
+ if (b.bucketSize > 1) foldEligibleCount += 1;
1871
+ }
1872
+ frameState.lastFoldBucketCount = foldEligibleCount;
1873
+ return foldBuckets;
1874
+ }