@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,1307 @@
1
+ // @forgeax/engine-runtime - tilemap-chunk-extract-system.
2
+ //
3
+ // Walks every TileLayer attached to a Tilemap via ChildOf; when a layer is
4
+ // dirty (or has never been extracted), purges its previously-spawned derived
5
+ // per-cell entities and re-spawns one ECS entity per non-zero cell. Each
6
+ // derived entity carries Transform + MeshFilter (a World-owned unit-quad ref) + MeshRenderer
7
+ // + Layer + ChildOf { parent: layerEntity }. The ChildOf edge lets
8
+ // `world.despawn(tilemapEntity)` cascade-despawn the entire subtree
9
+ // (Tilemap -> TileLayer -> derived render entities) via the engine's
10
+ // default `linkedSpawn: true` (tweak-20260714 M2, requirements AC-02/03/04).
11
+ //
12
+ // M0 baseline: unit-cell 1x1 form, single-atlas, no UV inset.
13
+ // M2 extension (plan-strategy §D-2 + §D-7 step 3):
14
+ // M3 extension (plan-strategy §D-7 step 2 + §D-12):
15
+ // - resolveTilesetMaterial walks the 3-hop chain
16
+ // `tile.regionIndex -> regions[i].atlasIndex ?? 0 -> atlases[idx]`
17
+ // so multi-atlas tilesets route each region to the correct GPU
18
+ // texture handle (requirements AC-04 / AC-11). Cache key stays
19
+ // binary `(atlasHandle, regionIndex)` -- atlasHandle already
20
+ // encodes the atlas pick.
21
+ // - spawnDerivedRenderEntities scales the quad by widthCells x
22
+ // heightCells (defaults 1 x 1) and offsets the centre so the pivot
23
+ // lands at the (pivotX, pivotY) location inside the anchor cell.
24
+ // - basePivotForX = D ? pivotY : pivotX (and dually for Y), so the
25
+ // 90deg CW z-rotation correctly swaps which atlas-axis pivot maps
26
+ // to which world axis.
27
+ // - effectivePivotX = H ? (1 - basePivotForX) : basePivotForX (and
28
+ // dually for Y). The anchor pivot under H flip lands at the
29
+ // mirrored cell position (charter P4 - same semantics as Tiled's
30
+ // "pivot stays in atlas-local coords, flips with the texel grid").
31
+ // - posX = (cellX + effectivePivotX + (0.5 - effectivePivotX) * widthCells)
32
+ // * tileSizeX
33
+ // posY analogous with effectivePivotY + heightCells.
34
+ // - scaleX/scaleY: signed by H/V flip; magnitude is widthCells *
35
+ // tileSizeX / heightCells * tileSizeY (multi-cell scale).
36
+ // - resolveTilesetMaterial inset-shrinks the region UV rectangle by
37
+ // half a texel on every edge so GPU bilinear filtering at the
38
+ // atlas-tile boundary never bleeds into the adjacent tile
39
+ // (charter P3 - default behaviour avoids visual defects).
40
+ //
41
+ // M3 boundary: per-entity sort key with effectivePivotY is wired in
42
+ // render-system-extract (m3-t5), not here.
43
+ //
44
+ // Cache key for resolveTilesetMaterial is BINARY: (atlasHandle, regionIndex).
45
+ // AI users widthCells / pivot variations share the same material handle
46
+ // (charter P4 consistent abstraction; plan-strategy §D-9 / §D-12).
47
+ //
48
+ // Anchors: plan-tasks m0-t10 / m2-t2 / m2-t4; plan-strategy §D-1 +
49
+ // §D-2 (multi-cell + flip x pivot) + §D-5 (M0 baseline file-by-file
50
+ // ECS API adaptation) + §D-7 step 3 (half-texel UV inset).
51
+
52
+ import type { EntityHandle, World } from '@forgeax/engine-ecs';
53
+ import { createPrimitiveMesh } from '@forgeax/engine-geometry';
54
+ import { decodeTileBits } from '@forgeax/engine-graphics-extras';
55
+ import { type box3, frustum, mat4 } from '@forgeax/engine-math';
56
+ import { ChildOf, Children, Transform } from '@forgeax/engine-scene';
57
+ import {
58
+ type Handle,
59
+ type MaterialAsset,
60
+ type TilesetAsset,
61
+ type TilesetRegion,
62
+ type TilesetTileEntry,
63
+ unwrapHandle,
64
+ } from '@forgeax/engine-types';
65
+ import {
66
+ CAMERA_PROJECTION_ORTHOGRAPHIC,
67
+ Camera,
68
+ decodeSortScope,
69
+ Layer,
70
+ MeshFilter,
71
+ MeshRenderer,
72
+ type SortScope,
73
+ SpriteInstances,
74
+ TileLayer,
75
+ Tilemap,
76
+ } from './components';
77
+ import { materialTextureValue, SPRITE_PREMULTIPLIED_ALPHA_BLEND } from './materials';
78
+ import { resolveTilesetRuntime, type TilesetAtlasLookup } from './tilemap/resolve-tileset-runtime';
79
+
80
+ // Module-scoped caches (charter P5 — engine-side memoisation; AI users
81
+ // never reach in). Test harness can flush them via the reset helpers.
82
+ //
83
+ // Two material caches coexist because the extract system produces two
84
+ // kinds of derived entities, each with a different material-key granularity:
85
+ //
86
+ // 1. `atlasMaterialCache` — key `${atlasId}|${regionIndex}` → per-region
87
+ // material with the UV rectangle baked into `values.region`.
88
+ // Used by the per-cell entity path (sortScope='per-cell' object
89
+ // layers). Each tile graphic gets a distinct MaterialAsset.
90
+ //
91
+ // 2. `atlasOnlyMaterialCache` — key `${atlasId}` → per-atlas material
92
+ // with `values.region: [0,0,1,1]` placeholder. Used by the
93
+ // SpriteInstances batched path (sortScope='layer' terrain layers).
94
+ // The shader's PER_INSTANCE_REGION=true variant reads per-instance
95
+ // UV from the instance buffer, not from the material UBO, so the
96
+ // placeholder is ignored at draw time.
97
+ let atlasMaterialCache = new WeakMap<World, Map<string, number>>();
98
+ let atlasOnlyMaterialCache = new WeakMap<World, Map<string, number>>();
99
+ const unitQuadHandleByWorld = new WeakMap<World, Handle<'MeshAsset', 'shared'>>();
100
+
101
+ function unitQuadHandle(world: World): Handle<'MeshAsset', 'shared'> {
102
+ const existing = unitQuadHandleByWorld.get(world);
103
+ if (existing !== undefined) return existing;
104
+ const mesh = createPrimitiveMesh('quad').unwrap();
105
+ const handle = world.internSharedRef('MeshAsset', mesh);
106
+ unitQuadHandleByWorld.set(world, handle);
107
+ return handle;
108
+ }
109
+ // tweak-20260714 M3 (plan-strategy §2 D-3): the terrain (sortScope='layer')
110
+ // derived-entity tracker Map + first-frame-heuristic Set that used to live
111
+ // alongside these material caches were retired once every derived entity
112
+ // was attached as `ChildOf` child of its TileLayer (M2). The reverse
113
+ // mirror `Children.entities` on the TileLayer is now the SSOT for
114
+ // "which derived entities belong to this layer" — engine-maintained on
115
+ // `world.addComponent(ChildOf) / world.despawn`, deriving-not-duplicating
116
+ // (architecture-principles §2). `purgeDerivedEntities` reads that mirror
117
+ // directly; the "already built" gate uses `Children.entities.length > 0`,
118
+ // which naturally handles both first frames (empty ⇒ rebuild) and steady
119
+ // state (populated ⇒ skip) without a second parallel ledger.
120
+
121
+ // ─── Chunk-streaming state for sortScope='per-cell' (object) layers ───────
122
+ //
123
+ // Object layers stream per-chunk: only chunks whose world AABB intersects the
124
+ // camera frustum have per-cell entities alive in the ECS world. This keeps
125
+ // extractFrame's entity count proportional to visible tile count rather than
126
+ // total map tile count (charter P5: engine-side memoisation / streaming).
127
+ //
128
+ // Design: one WeakMap entry per World owns three bounded maps:
129
+ // layers : layerKey → pre-bucketed specs by chunkIndex (built once
130
+ // from bucketTileLayer, rebuilt on dirty). Avoids re-reading
131
+ // the full tileset and re-materialising every frame.
132
+ // chunkEntities : "${layerKey}:${chunkIdx}" → spawned entity ids
133
+ // for that chunk (purged when chunk leaves frustum).
134
+ // activeChunks : layerKey → Set<chunkIdx> currently spawned.
135
+ //
136
+ // bug-20260703-tilemap-chunk-stale-frustum-and-cull-overhang (D2): each entry
137
+ // stores the ACTUAL world-space bounding box (union of every tile's post-TRS
138
+ // footprint from `computeTileTrs`) rather than a tile-aligned grid box.
139
+ // Multi-cell tiles (`widthCells` / `heightCells > 1`) and non-central pivots
140
+ // let a tile extend beyond its anchor chunk's grid; the grid box would
141
+ // despawn the anchor chunk while overhanging tiles were still on screen,
142
+ // causing edge-of-chunk flicker as the camera pans. Bounds are computed once
143
+ // per (dirty | first-time) rebuild, so the per-frame visibility test stays a
144
+ // single `frustum.intersectsBox` call (plan-strategy §2 D-3).
145
+ interface ChunkStreamEntry {
146
+ readonly specs: readonly DerivedSpawnSpec[];
147
+ readonly bounds: box3.Box3Like;
148
+ }
149
+ interface StreamLayerCache {
150
+ readonly byChunk: ReadonlyMap<number, ChunkStreamEntry>;
151
+ readonly tilemap: {
152
+ readonly cols: number;
153
+ readonly rows: number;
154
+ readonly tileSize: ArrayLike<number>;
155
+ readonly chunkSize: number;
156
+ };
157
+ readonly layerOrder: number;
158
+ readonly sortScope: SortScope;
159
+ }
160
+ interface PerWorldStreamingCache {
161
+ readonly layers: Map<string, StreamLayerCache>;
162
+ readonly chunkEntities: Map<string, number[]>;
163
+ readonly activeChunks: Map<string, Set<number>>;
164
+ }
165
+
166
+ let streamingCacheByWorld = new WeakMap<World, PerWorldStreamingCache>();
167
+
168
+ function streamingCache(world: World): PerWorldStreamingCache {
169
+ let cache = streamingCacheByWorld.get(world);
170
+ if (cache === undefined) {
171
+ cache = {
172
+ layers: new Map(),
173
+ chunkEntities: new Map(),
174
+ activeChunks: new Map(),
175
+ };
176
+ streamingCacheByWorld.set(world, cache);
177
+ }
178
+ return cache;
179
+ }
180
+
181
+ /**
182
+ * Flush both material caches. Useful in test harnesses + after a
183
+ * TilesetAsset reload.
184
+ */
185
+ export function resetTilemapChunkExtractCache(): void {
186
+ atlasMaterialCache = new WeakMap<World, Map<string, number>>();
187
+ atlasOnlyMaterialCache = new WeakMap<World, Map<string, number>>();
188
+ }
189
+
190
+ /**
191
+ * Flush the per-cell streaming caches. Useful in test harnesses + when
192
+ * the World is re-created.
193
+ *
194
+ * Signature preserved (AC-08 hard constraint) after tweak-20260714 M3
195
+ * retired the terrain-side tracker Map + first-frame heuristic Set:
196
+ * terrain layers now derive their "already-built" state from
197
+ * `Children.entities` on the TileLayer (mirror of ChildOf, engine-
198
+ * maintained), so the tracker only needs to clear the 3 streaming
199
+ * caches (plan-strategy §2 D-3 + §2 D-5).
200
+ */
201
+ export function resetTilemapDerivedEntityTracker(): void {
202
+ streamingCacheByWorld = new WeakMap<World, PerWorldStreamingCache>();
203
+ }
204
+
205
+ /**
206
+ * @internal Test-only helper for AC-11 (tweak-20260714 M4). Returns the
207
+ * union of `layerKey`s currently referenced by the three per-cell
208
+ * streaming caches. Post-diff-cleanup, an evicted layerKey MUST NOT
209
+ * appear here; a slot subsequently reused for a fresh TileLayer sees
210
+ * empty caches and rebuilds from zero (plan-strategy §2 D-4).
211
+ *
212
+ * Parsing the per-World `chunkEntities` key format `${entity}:${chunkIdx}`
213
+ * is intentional so callers observe cleanup on all three maps without
214
+ * depending on the invariant `activeSet ↔ chunkEntities
215
+ * keys are paired` — the test then also cross-checks that invariant.
216
+ */
217
+ export function _peekPerCellStreamingLayerKeys(world: World): readonly number[] {
218
+ const cache = streamingCacheByWorld.get(world);
219
+ if (cache === undefined) return [];
220
+ const out = new Set<number>();
221
+ for (const layerKey of cache.layers.keys()) {
222
+ if (Number.isFinite(Number(layerKey))) out.add(Number(layerKey));
223
+ }
224
+ for (const layerKey of cache.activeChunks.keys()) {
225
+ if (Number.isFinite(Number(layerKey))) out.add(Number(layerKey));
226
+ }
227
+ for (const key of cache.chunkEntities.keys()) {
228
+ const parts = key.split(':');
229
+ const entity = parts[0];
230
+ if (entity !== undefined && Number.isFinite(Number(entity))) out.add(Number(entity));
231
+ }
232
+ return Array.from(out);
233
+ }
234
+
235
+ /**
236
+ * Compute the per-layer / per-chunk packed value carried in `Layer.value`
237
+ * on derived entities. `sortScope` is the closed string union from
238
+ * `TileLayer.sortScope`:
239
+ * - `'layer'` (default): `(layerOrder << 20) | (chunkIndex & 0xFFFFF)` —
240
+ * terrain semantics, layerOrder dominates with chunkIndex
241
+ * tiebreak within the layer.
242
+ * - `'per-cell'`: returns `(layerOrder << 20)` (chunkIndex folded to 0)
243
+ * so every derived entity in the layer shares one
244
+ * Layer.value and can Y-interleave with sprite entities
245
+ * carrying the same value (e.g. a player sprite riding
246
+ * `SPRITE_LAYER_VALUE = layerOrder << 20`).
247
+ *
248
+ * Round-2 rename (D-V-3): the third arg was `ySort: boolean` before the
249
+ * sortScope union landed; it is now the closed `SortScope` union with
250
+ * default `'layer'`. The 0x200000 / chunked-bits semantics for the two
251
+ * arms are preserved exactly so existing pixel-parity baselines stay
252
+ * stable (only the AI-user surface widens to a self-documenting literal).
253
+ */
254
+ export function encodeTilemapLayerValue(
255
+ layerOrder: number,
256
+ chunkIndex: number,
257
+ sortScope: SortScope = 'layer',
258
+ ): number {
259
+ if (sortScope === 'per-cell') return (layerOrder << 20) | 0;
260
+ return (layerOrder << 20) | (chunkIndex & 0xfffff) | 0;
261
+ }
262
+
263
+ /**
264
+ * Resolve (regionIndex) into a per-tile sprite material handle by walking
265
+ * the 3-hop chain:
266
+ *
267
+ * tile.regionIndex -> regions[regionIndex] -> region.atlasIndex ?? 0
268
+ * -> atlases[atlasIndex]
269
+ *
270
+ * (plan-strategy §D-7 step 2 + requirements §AC-04 / §AC-11). Caches the
271
+ * resulting MaterialAsset handle so repeated lookups hit a single
272
+ * registered material per atlas-region pair; the cache key stays the
273
+ * binary tuple `(atlasHandle, regionIndex)` -- atlasHandle already
274
+ * encodes the atlasIndex pick so widening to a 3-tuple key would only
275
+ * inflate the SSOT without adding signal (charter P4 + plan-strategy
276
+ * §D-12). atlasIndex out-of-range is caught at register time by
277
+ * `validateTilesetPayload` (m1-t6); the runtime resolver falls through
278
+ * to handle 0 when an atlas slot is unexpectedly empty so the renderer
279
+ * skips the draw rather than silently sampling the wrong texture
280
+ * (charter P3 fail-safe).
281
+ *
282
+ * The shader is `forgeax::sprite`; values are filled with the atlas
283
+ * texture handle + a UV region rectangle covering the supplied
284
+ * TilesetRegion (half-texel inset added in m2-t4).
285
+ */
286
+ function resolveTilesetMaterial(
287
+ world: World,
288
+ tileset: TilesetAsset,
289
+ regionIndex: number,
290
+ lookup: (guid: string) => TilesetAtlasLookup,
291
+ ): number {
292
+ const region = tileset.regions[regionIndex];
293
+ if (region === undefined) return 0;
294
+ // 3-hop walk: regions[i].atlasIndex (default 0) -> atlases[atlasIndex].
295
+ // atlasIndex out-of-range is register-time fail-fast via
296
+ // `validateTilesetPayload` (m1-t6); the runtime resolver still bails
297
+ // when the slot is unexpectedly empty (charter P3 fail-safe).
298
+ const atlasIndex = region.atlasIndex ?? 0;
299
+ const atlasGuid = tileset.atlases[atlasIndex];
300
+ if (atlasGuid === undefined) return 0;
301
+ const atlas = resolveTilesetRuntime(world, atlasGuid, lookup);
302
+ if (!atlas.ok) return 0;
303
+ const atlasHandle = atlas.value.handle;
304
+ const atlasId = unwrapHandle(atlasHandle);
305
+ const cacheKey = `${atlasId}|${regionIndex}`;
306
+ const cache = atlasMaterialCache.get(world) ?? new Map<string, number>();
307
+ atlasMaterialCache.set(world, cache);
308
+ const cached = cache.get(cacheKey);
309
+ if (cached !== undefined) return cached;
310
+
311
+ // Atlas-space rectangle -> normalised UV. M2 adds a half-texel inset
312
+ // on every edge (plan-strategy §D-7 step 3) so GPU bilinear filtering
313
+ // at the region boundary never samples the adjacent atlas tile. Atlas
314
+ // pixel extent comes from atlasSizes[atlasIndex] when present (exact
315
+ // per-atlas pixel dimensions); falls back to columns * tileWidth for
316
+ // single-atlas or legacy callers.
317
+ const atlasSize = tileset.atlasSizes?.[atlasIndex];
318
+ const atlasWidth = Math.max(
319
+ 1,
320
+ atlasSize !== undefined ? atlasSize.pixelWidth : tileset.columns * tileset.tileWidth,
321
+ );
322
+ const atlasHeight = Math.max(
323
+ 1,
324
+ atlasSize !== undefined ? atlasSize.pixelHeight : tileset.rows * tileset.tileHeight,
325
+ );
326
+ const halfTexelU = 0.5 / atlasWidth;
327
+ const halfTexelV = 0.5 / atlasHeight;
328
+ const u = region.x / atlasWidth + halfTexelU;
329
+ const v = region.y / atlasHeight + halfTexelV;
330
+ const w = region.width / atlasWidth - 2 * halfTexelU;
331
+ const h = region.height / atlasHeight - 2 * halfTexelV;
332
+
333
+ const matPayload: MaterialAsset = {
334
+ kind: 'material',
335
+ passes: [
336
+ {
337
+ name: 'Forward',
338
+ program: { module: 'forgeax::sprite' },
339
+ renderState: {
340
+ ...{ blend: SPRITE_PREMULTIPLIED_ALPHA_BLEND },
341
+ tags: { LightMode: 'Forward' },
342
+ queue: 3000,
343
+ },
344
+ },
345
+ ],
346
+ values: {
347
+ colorTint: [1.0, 1.0, 1.0, 1.0],
348
+ baseColorTexture: materialTextureValue(atlasHandle),
349
+ region: [u, v, w, h],
350
+ pivotAndSize: [0.5, 0.5, 1.0, 1.0],
351
+ flipY: 1.0,
352
+ },
353
+ };
354
+ const matHandle = world.allocSharedRef<'MaterialAsset', MaterialAsset>(
355
+ 'MaterialAsset',
356
+ matPayload,
357
+ );
358
+ const id = unwrapHandle(matHandle);
359
+ cache.set(cacheKey, id);
360
+ return id;
361
+ }
362
+
363
+ /**
364
+ * Resolve an atlas-only sprite material (no baked-in UV region). One
365
+ * material per atlas — used by the SpriteInstances batched draw path
366
+ * (sortScope='layer'). The per-instance UV region rectangle lives in the
367
+ * `SpriteInstances.regions` buffer instead of in the material UBO.
368
+ *
369
+ * `values.region` is a `[0, 0, 1, 1]` placeholder. The sprite shader's
370
+ * `PER_INSTANCE_REGION=true` variant reads region from
371
+ * `instances[idx].region` and ignores the material slot; selecting that
372
+ * variant is the record stage's responsibility (see
373
+ * `render-system-record.ts` sprite pass pipeline selection).
374
+ *
375
+ * Returns the registered MaterialAsset slot id (unwrapped Handle u32) or
376
+ * 0 if the atlas slot is empty (charter P3 fail-safe; mirrors
377
+ * `resolveTilesetMaterial`).
378
+ */
379
+ function resolveAtlasOnlyMaterial(
380
+ world: World,
381
+ tileset: TilesetAsset,
382
+ atlasIndex: number,
383
+ lookup: (guid: string) => TilesetAtlasLookup,
384
+ ): number {
385
+ const atlasGuid = tileset.atlases[atlasIndex];
386
+ if (atlasGuid === undefined) return 0;
387
+ const atlas = resolveTilesetRuntime(world, atlasGuid, lookup);
388
+ if (!atlas.ok) return 0;
389
+ const atlasHandle = atlas.value.handle;
390
+ const atlasId = unwrapHandle(atlasHandle);
391
+ const cacheKey = String(atlasId);
392
+ const cache = atlasOnlyMaterialCache.get(world) ?? new Map<string, number>();
393
+ atlasOnlyMaterialCache.set(world, cache);
394
+ const cached = cache.get(cacheKey);
395
+ if (cached !== undefined) return cached;
396
+
397
+ const matPayload: MaterialAsset = {
398
+ kind: 'material',
399
+ passes: [
400
+ {
401
+ name: 'Forward',
402
+ program: { module: 'forgeax::sprite' },
403
+ renderState: {
404
+ ...{ blend: SPRITE_PREMULTIPLIED_ALPHA_BLEND },
405
+ tags: { LightMode: 'Forward' },
406
+ queue: 3000,
407
+ },
408
+ },
409
+ ],
410
+ values: {
411
+ colorTint: [1.0, 1.0, 1.0, 1.0],
412
+ baseColorTexture: materialTextureValue(atlasHandle),
413
+ region: [0.0, 0.0, 1.0, 1.0],
414
+ pivotAndSize: [0.5, 0.5, 1.0, 1.0],
415
+ flipY: 1.0,
416
+ },
417
+ };
418
+ const matHandle = world.allocSharedRef<'MaterialAsset', MaterialAsset>(
419
+ 'MaterialAsset',
420
+ matPayload,
421
+ );
422
+ const id = unwrapHandle(matHandle);
423
+ cache.set(cacheKey, id);
424
+ return id;
425
+ }
426
+
427
+ /**
428
+ * Compute the half-texel-inset normalised UV rectangle for a TilesetRegion
429
+ * (atlas-space pixels → atlas-normalised [u, v, w, h]). Mirrors the inset
430
+ * computation inside `resolveTilesetMaterial` so the per-instance region
431
+ * data fed into `SpriteInstances.regions` produces pixel-identical sampling
432
+ * to the per-region material path (charter P4 consistent abstraction).
433
+ */
434
+ function computeRegionUv(
435
+ tileset: TilesetAsset,
436
+ region: TilesetRegion,
437
+ atlasIndex: number,
438
+ ): [u: number, v: number, w: number, h: number] {
439
+ const atlasSize = tileset.atlasSizes?.[atlasIndex];
440
+ const atlasWidth = Math.max(
441
+ 1,
442
+ atlasSize !== undefined ? atlasSize.pixelWidth : tileset.columns * tileset.tileWidth,
443
+ );
444
+ const atlasHeight = Math.max(
445
+ 1,
446
+ atlasSize !== undefined ? atlasSize.pixelHeight : tileset.rows * tileset.tileHeight,
447
+ );
448
+ const halfTexelU = 0.5 / atlasWidth;
449
+ const halfTexelV = 0.5 / atlasHeight;
450
+ return [
451
+ region.x / atlasWidth + halfTexelU,
452
+ region.y / atlasHeight + halfTexelV,
453
+ region.width / atlasWidth - 2 * halfTexelU,
454
+ region.height / atlasHeight - 2 * halfTexelV,
455
+ ];
456
+ }
457
+
458
+ interface DerivedSpawnSpec {
459
+ readonly cellX: number;
460
+ readonly cellY: number;
461
+ readonly tileId: number;
462
+ readonly packedTile: number;
463
+ readonly materialHandle: number;
464
+ readonly chunkIndex: number;
465
+ // M2: multi-cell footprint + custom pivot land per-tile via TilesetTileEntry.
466
+ readonly widthCells: number;
467
+ readonly heightCells: number;
468
+ readonly pivotX: number;
469
+ readonly pivotY: number;
470
+ // Atlas + region indices retained so the SpriteInstances (sortScope=
471
+ // 'layer') path can group cells by atlas and look up per-instance UV
472
+ // without re-walking the 3-hop chain `tileId → regions → atlasIndex`.
473
+ readonly atlasIndex: number;
474
+ readonly regionIndex: number;
475
+ }
476
+
477
+ const SQRT1_2 = Math.SQRT1_2;
478
+
479
+ /**
480
+ * Compute the post-flip "effective pivot Y" for a tilemap tile entry.
481
+ *
482
+ * Mirrors the D-2 first-line composition used by `spawnDerivedRenderEntities`:
483
+ *
484
+ * basePivotForY = flipDiagonal ? pivotX : pivotY
485
+ * effectivePivotY = flipV ? (1 - basePivotForY) : basePivotForY
486
+ *
487
+ * Exported so the render-system-extract sprite-bucket sort key path can
488
+ * reproduce the *same* per-entity pivot value that landed on
489
+ * `Transform.posY` -- otherwise the foot-Y formula
490
+ * `posY - effectivePivotY * |scaleY|` would drift on flipped tiles
491
+ * (charter P4 single-source pivot SSOT; plan-strategy §D-1 + §D-2 +
492
+ * requirements §AC-12 / §AC-13).
493
+ */
494
+ export function effectivePivotYForTilemapFlip(
495
+ pivotY: number,
496
+ pivotX: number,
497
+ flipV: boolean,
498
+ flipDiagonal: boolean,
499
+ ): number {
500
+ const base = flipDiagonal ? pivotX : pivotY;
501
+ return flipV ? 1 - base : base;
502
+ }
503
+
504
+ /**
505
+ * Compute the per-cell derived spawn spec for one non-zero cell. Carries
506
+ * widthCells / heightCells / pivotX / pivotY from the TilesetTileEntry so
507
+ * `spawnDerivedRenderEntities` can apply the D-2 geometric correction
508
+ * without re-reading the asset registry.
509
+ */
510
+ function specFor(
511
+ layerCols: number,
512
+ chunkSize: number,
513
+ cellIndex: number,
514
+ packedTile: number,
515
+ materialHandle: number,
516
+ entry: TilesetTileEntry,
517
+ atlasIndex: number,
518
+ ): DerivedSpawnSpec {
519
+ const cellX = cellIndex % layerCols;
520
+ const cellY = Math.floor(cellIndex / layerCols);
521
+ const chunkX = Math.floor(cellX / chunkSize);
522
+ const chunkY = Math.floor(cellY / chunkSize);
523
+ const chunksPerRow = Math.max(1, Math.ceil(layerCols / chunkSize));
524
+ const chunkIndex = chunkY * chunksPerRow + chunkX;
525
+ const { tileId } = decodeTileBits(packedTile);
526
+ return {
527
+ cellX,
528
+ cellY,
529
+ tileId,
530
+ packedTile,
531
+ materialHandle,
532
+ chunkIndex,
533
+ widthCells: entry.widthCells ?? 1,
534
+ heightCells: entry.heightCells ?? 1,
535
+ pivotX: entry.pivotX ?? 0.5,
536
+ pivotY: entry.pivotY ?? 0.5,
537
+ atlasIndex,
538
+ regionIndex: entry.regionIndex,
539
+ };
540
+ }
541
+
542
+ /**
543
+ * Compute the post-flip TRS components for one tile cell. The plan-strategy
544
+ * §D-2 multi-cell + flip x pivot composite formula (first-line form):
545
+ *
546
+ * basePivotForX = D ? pivotY : pivotX
547
+ * basePivotForY = D ? pivotX : pivotY
548
+ * effectivePivotX = H ? (1 - basePivotForX) : basePivotForX
549
+ * effectivePivotY = V ? (1 - basePivotForY) : basePivotForY
550
+ * posX = (cellX + effectivePivotX + (0.5 - effectivePivotX) * widthCells)
551
+ * * tileSizeX
552
+ * posY = (cellY + effectivePivotY + (0.5 - effectivePivotY) * heightCells)
553
+ * * tileSizeY
554
+ * scaleX = (H ? -1 : 1) * widthCells * tileSizeX
555
+ * scaleY = (V ? -1 : 1) * heightCells * tileSizeY
556
+ * quatZ = D ? Math.SQRT1_2 : 0
557
+ * quatW = D ? Math.SQRT1_2 : 1
558
+ *
559
+ * Single SSOT consumed by both the per-cell entity spawn path and the
560
+ * SpriteInstances batched path so the two routes produce pixel-identical
561
+ * world transforms (charter P4 consistent abstraction).
562
+ */
563
+ function computeTileTrs(
564
+ tilemap: { tileSize: ArrayLike<number> },
565
+ spec: DerivedSpawnSpec,
566
+ packedTile: number,
567
+ ): {
568
+ posX: number;
569
+ posY: number;
570
+ scaleX: number;
571
+ scaleY: number;
572
+ quatZ: number;
573
+ quatW: number;
574
+ } {
575
+ const { flipH, flipV, flipDiagonal } = decodeTileBits(packedTile);
576
+ const basePivotForX = flipDiagonal ? spec.pivotY : spec.pivotX;
577
+ const effectivePivotX = flipH ? 1 - basePivotForX : basePivotForX;
578
+ const effectivePivotY = effectivePivotYForTilemapFlip(
579
+ spec.pivotY,
580
+ spec.pivotX,
581
+ flipV,
582
+ flipDiagonal,
583
+ );
584
+ const tileSizeX = tilemap.tileSize[0] ?? 1;
585
+ const tileSizeY = tilemap.tileSize[1] ?? 1;
586
+ return {
587
+ posX: (spec.cellX + effectivePivotX + (0.5 - effectivePivotX) * spec.widthCells) * tileSizeX,
588
+ posY: (spec.cellY + effectivePivotY + (0.5 - effectivePivotY) * spec.heightCells) * tileSizeY,
589
+ scaleX: (flipH ? -1 : 1) * spec.widthCells * tileSizeX,
590
+ scaleY: (flipV ? -1 : 1) * spec.heightCells * tileSizeY,
591
+ quatZ: flipDiagonal ? SQRT1_2 : 0,
592
+ quatW: flipDiagonal ? SQRT1_2 : 1,
593
+ };
594
+ }
595
+
596
+ /**
597
+ * Spawn a single derived per-cell render entity. Used by the per-cell
598
+ * sortScope path (`sortScope='per-cell'`, object layers) where each cell
599
+ * needs an independent Y-sort position to interleave with sprite entities
600
+ * (e.g. player) at arbitrary Y positions.
601
+ *
602
+ * `layerEntity` becomes the derived entity's `ChildOf.parent`, so
603
+ * `world.despawn(layerEntity)` cascade-despawns every derived cell entity
604
+ * via the engine's `linkedSpawn: true` default (feat-20260616). Combined
605
+ * with the Tilemap -> TileLayer ChildOf edge, a single
606
+ * `world.despawn(tilemapEntity)` unwinds the entire subtree without a
607
+ * bespoke tilemap-scoped cleanup pass (requirements AC-02 / AC-03).
608
+ */
609
+ function spawnDerivedRenderEntities(
610
+ world: World,
611
+ tilemap: { tileSize: ArrayLike<number> },
612
+ layerEntity: EntityHandle,
613
+ layerOrder: number,
614
+ spec: DerivedSpawnSpec,
615
+ packedTile: number,
616
+ sortScope: SortScope = 'layer',
617
+ ): EntityHandle {
618
+ const { posX, posY, scaleX, scaleY, quatZ, quatW } = computeTileTrs(tilemap, spec, packedTile);
619
+ const layerValue = encodeTilemapLayerValue(layerOrder, spec.chunkIndex, sortScope);
620
+ return world
621
+ .spawn(
622
+ {
623
+ component: Transform,
624
+ data: {
625
+ pos: [posX, posY, 0],
626
+ quat: [0, 0, quatZ, quatW],
627
+ scale: [scaleX, scaleY, 1],
628
+ },
629
+ },
630
+ { component: MeshFilter, data: { assetHandle: unitQuadHandle(world) } },
631
+ {
632
+ component: MeshRenderer,
633
+ data: {
634
+ materials: [spec.materialHandle as unknown as Handle<'MaterialAsset', 'shared'>],
635
+ },
636
+ },
637
+ { component: Layer, data: { value: layerValue } },
638
+ { component: ChildOf, data: { parent: layerEntity } },
639
+ )
640
+ .unwrap();
641
+ }
642
+
643
+ /**
644
+ * Spawn one batched SpriteInstances entity per (TileLayer, chunk, atlas)
645
+ * group. Used by the `sortScope='layer'` path (terrain layers): every
646
+ * cell in the group becomes one instance in the same drawIndexed call.
647
+ *
648
+ * The chunk grid is the unit declared by `Tilemap.chunkSize` (default
649
+ * 16x16). One SpriteInstances entity per (chunk, atlas) pair keeps each
650
+ * GPU instance buffer small (256 cells x 80B = 20KB worst case) and lets
651
+ * downstream frustum culling / incremental rebuild operate at chunk
652
+ * granularity (industry standard: Godot quadrant_size, Bevy ChunkSize,
653
+ * Tiled TMX chunks).
654
+ *
655
+ * Layout:
656
+ * - Transform: identity (the per-instance mat4 carries world-space TRS).
657
+ * - MeshFilter: the World-owned unit-quad shared reference.
658
+ * - MeshRenderer: atlas-only sprite material (region placeholder; shader
659
+ * reads per-instance UV from SpriteInstances.regions under the
660
+ * PER_INSTANCE_REGION=true variant — selected at record stage).
661
+ * - SpriteInstances: { transforms: Float32Array(N*16),
662
+ * regions: Float32Array(N*4) }.
663
+ * - Layer: encodeTilemapLayerValue(layerOrder, chunkIndex, 'layer').
664
+ * chunkIndex distinguishes adjacent chunks of the same layer in the
665
+ * transparent-sort key so the back-to-front ordering between chunks
666
+ * remains stable (chunks within a layer don't visually overlap in
667
+ * normal tilemap scenes; the chunkIndex tiebreaker is byte-exact
668
+ * with the per-cell path's encoding).
669
+ *
670
+ * Returns the spawned EntityHandle. Empty groups (zero cells) short-circuit
671
+ * and return `undefined`.
672
+ */
673
+ function spawnSpriteInstancesGroup(
674
+ world: World,
675
+ tilemap: { cols: number; tileSize: ArrayLike<number>; chunkSize: number },
676
+ tileset: TilesetAsset,
677
+ layerEntity: EntityHandle,
678
+ layerOrder: number,
679
+ chunkIndex: number,
680
+ atlasIndex: number,
681
+ materialHandle: number,
682
+ cellSpecs: readonly DerivedSpawnSpec[],
683
+ ): EntityHandle | undefined {
684
+ if (cellSpecs.length === 0) return undefined;
685
+ const N = cellSpecs.length;
686
+ const transforms = new Float32Array(N * 16);
687
+ const regions = new Float32Array(N * 4);
688
+ const tmpMat = new Float32Array(16) as unknown as Parameters<typeof mat4.compose>[0];
689
+ const tmpF = tmpMat as unknown as Float32Array;
690
+ const tmpT: [number, number, number] = [0, 0, 0];
691
+ const tmpR: [number, number, number, number] = [0, 0, 0, 1];
692
+ const tmpS: [number, number, number] = [1, 1, 1];
693
+
694
+ // Per-chunk frustum cull: entity Transform = chunk world center + chunk
695
+ // extents (scaleX/Y). The frustum culler reads the entity AABB (unit quad
696
+ // local [-0.5, 0.5]² expanded by entity scale/pos) → world AABB exactly
697
+ // covers the chunk footprint.
698
+ //
699
+ // Per-instance transforms become chunk-local: inv_chunk * world_tile.
700
+ // inv_chunk is a pure TRS-inverse (no rotation), so for each column c of
701
+ // the world mat W:
702
+ // local[c*4+0] = (W[c*4+0] - chunkCenterX * W[c*4+3]) * invSX
703
+ // local[c*4+1] = (W[c*4+1] - chunkCenterY * W[c*4+3]) * invSY
704
+ // local[c*4+2] = W[c*4+2]
705
+ // local[c*4+3] = W[c*4+3]
706
+ // The sprite shader then produces:
707
+ // world = chunk_TRS * local_tile * pos_local
708
+ // = chunk_TRS * (inv_chunk * world_tile) * pos_local
709
+ // = world_tile * pos_local ✓ (pixel-identical to identity path)
710
+ //
711
+ // Tiles whose widthCells/heightCells straddle a chunk boundary will have
712
+ // instance transforms that exceed the chunk AABB; the entity is still drawn
713
+ // (conservative cull only skips when the chunk AABB is fully outside).
714
+ const chunksPerRow = Math.max(1, Math.ceil(tilemap.cols / tilemap.chunkSize));
715
+ const chunkX = chunkIndex % chunksPerRow;
716
+ const chunkY = Math.floor(chunkIndex / chunksPerRow);
717
+ const chunkScaleX = tilemap.chunkSize * (tilemap.tileSize[0] ?? 1);
718
+ const chunkScaleY = tilemap.chunkSize * (tilemap.tileSize[1] ?? 1);
719
+ const chunkCenterX = (chunkX + 0.5) * chunkScaleX;
720
+ const chunkCenterY = (chunkY + 0.5) * chunkScaleY;
721
+ const invSX = chunkScaleX > 0 ? 1 / chunkScaleX : 1;
722
+ const invSY = chunkScaleY > 0 ? 1 / chunkScaleY : 1;
723
+
724
+ for (let i = 0; i < N; i++) {
725
+ const spec = cellSpecs[i] as DerivedSpawnSpec;
726
+ const { posX, posY, scaleX, scaleY, quatZ, quatW } = computeTileTrs(
727
+ tilemap,
728
+ spec,
729
+ spec.packedTile,
730
+ );
731
+ tmpT[0] = posX;
732
+ tmpT[1] = posY;
733
+ tmpT[2] = 0;
734
+ tmpR[0] = 0;
735
+ tmpR[1] = 0;
736
+ tmpR[2] = quatZ;
737
+ tmpR[3] = quatW;
738
+ tmpS[0] = scaleX;
739
+ tmpS[1] = scaleY;
740
+ tmpS[2] = 1;
741
+ mat4.compose(tmpMat, tmpT, tmpR, tmpS);
742
+
743
+ // Write chunk-local transform: inv_chunk * world_tile (see formula above).
744
+ const dst = i * 16;
745
+ for (let c = 0; c < 4; c++) {
746
+ const base = c * 4;
747
+ const w3 = tmpF[base + 3] as number;
748
+ transforms[dst + base + 0] = ((tmpF[base + 0] as number) - chunkCenterX * w3) * invSX;
749
+ transforms[dst + base + 1] = ((tmpF[base + 1] as number) - chunkCenterY * w3) * invSY;
750
+ transforms[dst + base + 2] = tmpF[base + 2] as number;
751
+ transforms[dst + base + 3] = w3;
752
+ }
753
+
754
+ const region = tileset.regions[spec.regionIndex];
755
+ if (region !== undefined) {
756
+ const [u, v, w, h] = computeRegionUv(tileset, region, atlasIndex);
757
+ regions[i * 4 + 0] = u;
758
+ regions[i * 4 + 1] = v;
759
+ regions[i * 4 + 2] = w;
760
+ regions[i * 4 + 3] = h;
761
+ }
762
+ }
763
+
764
+ const layerValue = encodeTilemapLayerValue(layerOrder, chunkIndex, 'layer');
765
+ // plan-strategy D-2: the SpriteInstances entity's Transform.posY is set
766
+ // to `chunkCenterY` (becomes `world[13]` in the entity's local-to-world
767
+ // matrix). This is the LAYER_Y fold sort key — terrain rows share a
768
+ // chunk Y, so per-chunk Y resolution is exactly the granularity the
769
+ // fold pass needs. Per-tile world[13] (instance-level) is intentionally
770
+ // _not_ used; instances live in the chunk-local space anchored at
771
+ // chunkCenterY (see line 601-647 above). This is intentional, not a bug.
772
+ return world
773
+ .spawn(
774
+ {
775
+ component: Transform,
776
+ data: {
777
+ pos: [chunkCenterX, chunkCenterY, 0],
778
+ quat: [0, 0, 0, 1],
779
+ scale: [chunkScaleX, chunkScaleY, 1],
780
+ },
781
+ },
782
+ { component: MeshFilter, data: { assetHandle: unitQuadHandle(world) } },
783
+ {
784
+ component: MeshRenderer,
785
+ data: {
786
+ materials: [materialHandle as unknown as Handle<'MaterialAsset', 'shared'>],
787
+ },
788
+ },
789
+ { component: SpriteInstances, data: { transforms, regions } },
790
+ { component: Layer, data: { value: layerValue } },
791
+ { component: ChildOf, data: { parent: layerEntity } },
792
+ )
793
+ .unwrap();
794
+ }
795
+
796
+ /**
797
+ * Bucket a single layer's tiles into per-non-zero-cell spawn specs.
798
+ * Returns the parent Tilemap metadata + the resolved spawn specs.
799
+ */
800
+ function bucketTileLayer(
801
+ world: World,
802
+ layerEntity: EntityHandle,
803
+ parentEntity: EntityHandle,
804
+ lookup: (guid: string) => TilesetAtlasLookup,
805
+ ):
806
+ | {
807
+ readonly tilemap: {
808
+ cols: number;
809
+ rows: number;
810
+ tileSize: ArrayLike<number>;
811
+ chunkSize: number;
812
+ };
813
+ readonly layerOrder: number;
814
+ readonly sortScope: SortScope;
815
+ readonly tileset: TilesetAsset;
816
+ readonly specs: readonly DerivedSpawnSpec[];
817
+ }
818
+ | undefined {
819
+ const tilemapRes = world.get(parentEntity, Tilemap);
820
+ if (!tilemapRes.ok) return undefined;
821
+ const tilemap = tilemapRes.value;
822
+ const tilesetPayload = lookup(tilemap.tileset);
823
+ if (tilesetPayload === undefined || 'code' in tilesetPayload) return undefined;
824
+ if (tilesetPayload.kind !== 'tileset') return undefined;
825
+ const tileset = tilesetPayload;
826
+
827
+ const layerRes = world.get(layerEntity, TileLayer);
828
+ if (!layerRes.ok) return undefined;
829
+ const layer = layerRes.value;
830
+ const tiles = layer.tiles as Uint32Array;
831
+ const sortScope = decodeSortScope(layer.sortScope);
832
+
833
+ // sortScope='layer' (terrain) uses the SpriteInstances batched path which
834
+ // groups cells by atlas under an atlas-only material; sortScope='per-cell'
835
+ // (object) keeps the per-cell entity path with per-region materials so
836
+ // each cell can interleave with sprites by foot-Y. The material resolver
837
+ // selection here keeps the spec's `materialHandle` in the correct
838
+ // granularity for whichever spawn path consumes it downstream.
839
+ const useSpriteInstances = sortScope === 'layer';
840
+
841
+ const specs: DerivedSpawnSpec[] = [];
842
+ for (let i = 0; i < tiles.length; i++) {
843
+ const packed = tiles[i] ?? 0;
844
+ if (packed === 0) continue;
845
+ const { tileId } = decodeTileBits(packed);
846
+ if (tileId === 0) continue;
847
+ const entry = tileset.tiles[tileId - 1];
848
+ if (entry === undefined) continue;
849
+ const region = tileset.regions[entry.regionIndex];
850
+ if (region === undefined) continue;
851
+ const atlasIndex = region.atlasIndex ?? 0;
852
+ const materialHandle = useSpriteInstances
853
+ ? resolveAtlasOnlyMaterial(world, tileset, atlasIndex, lookup)
854
+ : resolveTilesetMaterial(world, tileset, entry.regionIndex, lookup);
855
+ const spec = specFor(
856
+ tilemap.cols,
857
+ tilemap.chunkSize,
858
+ i,
859
+ packed,
860
+ materialHandle,
861
+ entry,
862
+ atlasIndex,
863
+ );
864
+ specs.push(spec);
865
+ }
866
+ return {
867
+ tilemap,
868
+ layerOrder: layer.layerOrder,
869
+ sortScope,
870
+ tileset,
871
+ specs,
872
+ };
873
+ }
874
+
875
+ /**
876
+ * Compute the camera frustum planes for the first Camera + Transform entity
877
+ * found in the world. Returns null when no camera exists or the projection
878
+ * parameters are degenerate (callers treat null as always-visible).
879
+ *
880
+ * Mirrors the frustum-plane computation in render-system-extract so both
881
+ * cull paths use byte-identical planes (charter P4 consistent abstraction).
882
+ */
883
+ function buildCameraFrustumPlanes(world: World): frustum.Frustum | null {
884
+ const query = world.query({ with: [Camera, Transform] }).unwrap();
885
+ let result: frustum.Frustum | null = null;
886
+ for (const row of query) {
887
+ const camEntity = row.entity;
888
+
889
+ const camRes = world.get(camEntity, Camera);
890
+ const trRes = world.get(camEntity, Transform);
891
+ if (!camRes.ok || !trRes.ok) continue;
892
+
893
+ const cam = camRes.value as unknown as {
894
+ near: number;
895
+ far: number;
896
+ projection: number;
897
+ left: number;
898
+ right: number;
899
+ bottom: number;
900
+ top: number;
901
+ fov: number;
902
+ aspect: number;
903
+ };
904
+ const tr = trRes.value as unknown as { world: Float32Array };
905
+
906
+ const { near, far } = cam;
907
+ if (near >= far) continue;
908
+
909
+ const proj = mat4.create();
910
+ if (cam.projection === CAMERA_PROJECTION_ORTHOGRAPHIC) {
911
+ mat4.orthographic(
912
+ proj as Parameters<typeof mat4.orthographic>[0],
913
+ cam.left,
914
+ cam.right,
915
+ cam.bottom,
916
+ cam.top,
917
+ near,
918
+ far,
919
+ );
920
+ } else {
921
+ mat4.perspective(
922
+ proj as Parameters<typeof mat4.perspective>[0],
923
+ cam.fov,
924
+ cam.aspect,
925
+ near,
926
+ far,
927
+ );
928
+ }
929
+ const view = mat4.create();
930
+ mat4.invert(
931
+ view as Parameters<typeof mat4.invert>[0],
932
+ tr.world as Parameters<typeof mat4.invert>[1],
933
+ );
934
+ const vp = mat4.create();
935
+ mat4.multiply(
936
+ vp as Parameters<typeof mat4.multiply>[0],
937
+ proj as Parameters<typeof mat4.multiply>[1],
938
+ view as Parameters<typeof mat4.multiply>[2],
939
+ );
940
+ const f = frustum.create();
941
+ frustum.fromViewProjection(f, vp as Parameters<typeof frustum.fromViewProjection>[1]);
942
+ result = f;
943
+ break;
944
+ }
945
+ return result;
946
+ }
947
+
948
+ /**
949
+ * bug-20260703-tilemap-chunk-stale-frustum-and-cull-overhang (D2): compute
950
+ * the world-space AABB that tightly encloses every tile inside a chunk
951
+ * after post-TRS placement (`computeTileTrs` applied per spec, SSOT shared
952
+ * with the per-cell spawn path and SpriteInstances batched path). This is
953
+ * the SSOT the streaming visibility test uses to decide whether a chunk's
954
+ * tiles could still be visible; using it instead of the tile-aligned grid
955
+ * box makes multi-cell / non-central-pivot overhang round-trip correctly,
956
+ * so an overhanging tile no longer disappears when its anchor chunk goes
957
+ * off-screen (plan-strategy §2 D-2).
958
+ *
959
+ * Empty spec list -> inverted `Number.MAX_VALUE` sentinel (plan-strategy
960
+ * §2 D-4). `bucketTileLayer` filters empty cells before the byChunk map
961
+ * is built, so this branch should never trigger in production; the
962
+ * sentinel is an explicit "no visible pixels" value that
963
+ * `frustum.intersectsBox` rejects immediately, avoiding any silent NaN
964
+ * propagation path (charter P3 explicit failure).
965
+ *
966
+ * Note: `frustum.intersectsBox` picks a p-vertex per plane and computes
967
+ * `dot(normal, p) + d`. When normal has a zero component, multiplying by
968
+ * `Infinity` yields `NaN` (`NaN < 0 === false` -> false positive
969
+ * intersection). The sentinel uses finite `Number.MAX_VALUE` on the axes
970
+ * we want inverted so `0 * MAX_VALUE === 0` and the non-zero-normal axis
971
+ * dominates, driving `dot` to `-Infinity` and correctly rejecting.
972
+ *
973
+ * Z is expanded to +/- 1 so flat sprite geometry (`pos_local.z === 0`) is
974
+ * never degenerate against the frustum's near/far planes.
975
+ *
976
+ * @internal exported for M2 overhang / empty-chunk sentinel unit tests
977
+ * (plan-strategy §5.3); not part of the AI user surface.
978
+ */
979
+ export function computeChunkStreamBounds(
980
+ tilemap: { tileSize: ArrayLike<number> },
981
+ specs: readonly DerivedSpawnSpec[],
982
+ ): box3.Box3Like {
983
+ let minX = Number.MAX_VALUE;
984
+ let minY = Number.MAX_VALUE;
985
+ let maxX = -Number.MAX_VALUE;
986
+ let maxY = -Number.MAX_VALUE;
987
+ for (const spec of specs) {
988
+ const { posX, posY, scaleX, scaleY } = computeTileTrs(tilemap, spec, spec.packedTile);
989
+ const halfW = Math.abs(scaleX) * 0.5;
990
+ const halfH = Math.abs(scaleY) * 0.5;
991
+ const x0 = posX - halfW;
992
+ const x1 = posX + halfW;
993
+ const y0 = posY - halfH;
994
+ const y1 = posY + halfH;
995
+ if (x0 < minX) minX = x0;
996
+ if (y0 < minY) minY = y0;
997
+ if (x1 > maxX) maxX = x1;
998
+ if (y1 > maxY) maxY = y1;
999
+ }
1000
+ return [minX, minY, -1, maxX, maxY, 1] as unknown as box3.Box3Like;
1001
+ }
1002
+
1003
+ /**
1004
+ * Despawn every derived render entity currently attached to `layerEntity`.
1005
+ *
1006
+ * After tweak-20260714 M3 the terrain path locates its previously-spawned
1007
+ * children via `Children.entities` on the TileLayer (mirror of ChildOf,
1008
+ * engine-maintained via the relationship hook) rather than a module-level
1009
+ * tracker Map — SSOT collapse per architecture-principles §2 (Derive,
1010
+ * Don't Duplicate). The snapshot returned by
1011
+ * `world.get(layerEntity, Children).entities` is a fresh read-only
1012
+ * Uint32Array that is stable across the iteration even though each
1013
+ * despawn prunes the mirror in place.
1014
+ *
1015
+ * Empty children (never-built layer, or already-purged) trivially
1016
+ * short-circuits — this is the "empty array no-op" equivalence to the
1017
+ * old first-frame-heuristic guard (plan-strategy §2 D-3 edge case 3).
1018
+ */
1019
+ function purgeDerivedEntities(world: World, layerEntity: EntityHandle): void {
1020
+ const r = world.get(layerEntity, Children);
1021
+ if (!r.ok) return;
1022
+ const snap = r.value.entities;
1023
+ for (let i = 0; i < snap.length; i++) {
1024
+ const e = snap[i];
1025
+ if (e !== undefined) world.despawn(e as EntityHandle);
1026
+ }
1027
+ }
1028
+
1029
+ /**
1030
+ * Snapshot of a single TileLayer's per-frame work item — populated by the
1031
+ * main-loop query and consumed both by the per-cell diff-cleanup preamble
1032
+ * (`evictDeadPerCellStreamingCaches`) and the per-layer processing branches.
1033
+ */
1034
+ interface LayerWork {
1035
+ readonly layerEntity: EntityHandle;
1036
+ readonly parentEntity: EntityHandle;
1037
+ readonly dirty: number;
1038
+ readonly sortScopeRaw: number;
1039
+ }
1040
+
1041
+ /**
1042
+ * tweak-20260714 M4 diff-cleanup preamble (plan-strategy §2 D-4 +
1043
+ * requirements §5 AC-11 + §8 edge case #4).
1044
+ *
1045
+ * The per-cell streaming maps are owned by one weak World entry and outlive
1046
+ * individual TileLayer entities while that World is alive. When a TileLayer
1047
+ * is despawned (or cascade-collected via `world.despawn(tilemapEntity)`) the
1048
+ * ECS mirrors clean up entities and `Children.entities`, but these maps retain
1049
+ * the dead layer's entries until this diff. On slot reuse, the stale entries
1050
+ * would corrupt rebuild: `activeSet` still lists old
1051
+ * chunkIndexes, and the rebuild branch would attempt to despawn stale
1052
+ * entity IDs before repopulating.
1053
+ *
1054
+ * Fix: at the top of each frame, diff this World's cached layer keys against
1055
+ * the fresh query. The set
1056
+ * difference names layers that vanished since the previous call; evict
1057
+ * their entries from all three Maps. `activeSet` is the SSOT for "which
1058
+ * chunkIndexes have entries under this layer" (each insertion / removal
1059
+ * pairs a `chunkEntities` set/delete with an `activeSet` add/delete), so
1060
+ * cleanup iterates `activeSet` rather than scanning `chunkEntities.keys()` —
1061
+ * O(chunks-per-dead-layer) instead
1062
+ * of O(total-cache-keys).
1063
+ *
1064
+ * Cross-world isolation is structural: the WeakMap key is the World object.
1065
+ * No duplicated World id is retained in every layer/chunk key.
1066
+ *
1067
+ * Per-frame cost (OOS-3 invariant): the two `keys()` iterations scan
1068
+ * O(cache_size) ≤ O(all-ever-seen-layers-for-this-worldId). Typical
1069
+ * scenes have ≤ 10 layers so this is a handful of Map lookups; no
1070
+ * matrix arithmetic. Actual eviction work only runs on frames where a
1071
+ * layer vanished — steady-state frames pay only the diff scan.
1072
+ */
1073
+ function evictDeadPerCellStreamingCaches(work: readonly LayerWork[], world: World): void {
1074
+ const cache = streamingCache(world);
1075
+ const aliveLayerKeys = new Set<string>();
1076
+ for (const w of work) {
1077
+ aliveLayerKeys.add(String(unwrapHandle(w.layerEntity as unknown as Handle<string, 'shared'>)));
1078
+ }
1079
+ const deadLayerKeys = new Set<string>();
1080
+ for (const layerKey of cache.activeChunks.keys()) {
1081
+ if (!aliveLayerKeys.has(layerKey)) deadLayerKeys.add(layerKey);
1082
+ }
1083
+ for (const layerKey of cache.layers.keys()) {
1084
+ if (!aliveLayerKeys.has(layerKey)) deadLayerKeys.add(layerKey);
1085
+ }
1086
+ for (const deadKey of deadLayerKeys) {
1087
+ const activeSet = cache.activeChunks.get(deadKey);
1088
+ if (activeSet !== undefined) {
1089
+ for (const chunkIdx of activeSet) {
1090
+ cache.chunkEntities.delete(`${deadKey}:${chunkIdx}`);
1091
+ }
1092
+ cache.activeChunks.delete(deadKey);
1093
+ }
1094
+ cache.layers.delete(deadKey);
1095
+ }
1096
+ }
1097
+
1098
+ /**
1099
+ * Walk every TileLayer ChildOf-ing a Tilemap, extract its non-zero cells
1100
+ * into derived render entities. A Renderer attaches this system to FrameEnd;
1101
+ * World's final publication then resolves newly-created Transform.world values before the
1102
+ * read-only render walk begins.
1103
+ *
1104
+ * Two paths based on `TileLayer.sortScope`:
1105
+ *
1106
+ * `'layer'` (terrain, default):
1107
+ * Batched SpriteInstances path — spawned once, reused until dirty. Each
1108
+ * (chunk, atlas) pair becomes one SpriteInstances entity whose Transform
1109
+ * covers the chunk footprint, so the frustum culler rejects off-screen
1110
+ * chunks at entity granularity.
1111
+ *
1112
+ * `'per-cell'` (object layers):
1113
+ * Chunk-streaming path — specs are bucketed by chunkIndex once (rebuilt
1114
+ * on dirty) and the live entity set is updated every frame to match the
1115
+ * camera frustum. Only chunks whose world AABB intersects the frustum
1116
+ * have ECS entities alive, keeping `extractFrame` iteration proportional
1117
+ * to visible tile count rather than total map tile count.
1118
+ */
1119
+ export function tilemapChunkExtractSystem(
1120
+ world: World,
1121
+ lookup: (guid: string) => TilesetAtlasLookup = () => undefined,
1122
+ ): void {
1123
+ const streaming = streamingCache(world);
1124
+ const work: LayerWork[] = [];
1125
+ const tileLayerQuery = world.query({ read: [TileLayer, ChildOf] }).unwrap();
1126
+ for (const row of tileLayerQuery) {
1127
+ const layer = row.get(TileLayer);
1128
+ const parent = row.get(ChildOf).parent;
1129
+ if (parent === null) continue;
1130
+ work.push({
1131
+ layerEntity: row.entity,
1132
+ parentEntity: parent,
1133
+ dirty: layer.dirty,
1134
+ sortScopeRaw: layer.sortScope,
1135
+ });
1136
+ }
1137
+
1138
+ evictDeadPerCellStreamingCaches(work, world);
1139
+
1140
+ // Compute the camera frustum once per frame — only needed when at least
1141
+ // one streaming (per-cell) layer exists. Null = always-visible fallback.
1142
+ let frustumPlanes: frustum.Frustum | null | undefined;
1143
+
1144
+ for (const w of work) {
1145
+ // The outer WeakMap owns World identity; this generation-bearing packed
1146
+ // entity handle prevents aliasing when an ECS slot is reused.
1147
+ const layerKey = String(unwrapHandle(w.layerEntity as unknown as Handle<string, 'shared'>));
1148
+ // AC-04: decode via the canonical SortScope union; avoid relying on the
1149
+ // raw encoding (0='layer', 1='per-cell') leaking through this call site.
1150
+ // `decodeSortScope` is the SSOT used throughout `bucketTileLayer` (see
1151
+ // line 730); this main-loop branch is the last remaining numeric check.
1152
+ const isStreaming = decodeSortScope(w.sortScopeRaw) === 'per-cell';
1153
+
1154
+ if (!isStreaming) {
1155
+ // ── Terrain batched path (sortScope='layer') ──────────────────────
1156
+ // Spawn once per dirty; entity AABB covers the chunk footprint for
1157
+ // entity-level frustum culling in render-system-extract.
1158
+ //
1159
+ // "already built" is derived from Children.entities (mirror of
1160
+ // ChildOf, engine-maintained). Empty children ⇒ never built (or
1161
+ // just purged) ⇒ don't skip. Populated children + dirty=0 ⇒
1162
+ // steady state ⇒ skip. Populated + dirty=1 ⇒ purge + rebuild.
1163
+ // architecture-principles §2 (Derive, Don't Duplicate): the
1164
+ // former module-level "ever-built" Set was a second copy of
1165
+ // information that Children.entities already carries.
1166
+ const childrenRes = world.get(w.layerEntity, Children);
1167
+ const childCount = childrenRes.ok ? childrenRes.value.entities.length : 0;
1168
+ if (childCount > 0 && w.dirty === 0) continue;
1169
+
1170
+ purgeDerivedEntities(world, w.layerEntity);
1171
+
1172
+ const bucket = bucketTileLayer(world, w.layerEntity, w.parentEntity, lookup);
1173
+ if (bucket === undefined) continue;
1174
+
1175
+ const byChunkAtlas = new Map<number, DerivedSpawnSpec[]>();
1176
+ for (const spec of bucket.specs) {
1177
+ const key = ((spec.chunkIndex & 0xfffff) << 16) | (spec.atlasIndex & 0xffff);
1178
+ const list = byChunkAtlas.get(key);
1179
+ if (list === undefined) {
1180
+ byChunkAtlas.set(key, [spec]);
1181
+ } else {
1182
+ list.push(spec);
1183
+ }
1184
+ }
1185
+ for (const groupSpecs of byChunkAtlas.values()) {
1186
+ const first = groupSpecs[0];
1187
+ if (first === undefined) continue;
1188
+ spawnSpriteInstancesGroup(
1189
+ world,
1190
+ bucket.tilemap,
1191
+ bucket.tileset,
1192
+ w.layerEntity,
1193
+ bucket.layerOrder,
1194
+ first.chunkIndex,
1195
+ first.atlasIndex,
1196
+ first.materialHandle,
1197
+ groupSpecs,
1198
+ );
1199
+ }
1200
+ if (w.dirty !== 0) {
1201
+ world.set(w.layerEntity, TileLayer, { dirty: 0 }).unwrap();
1202
+ }
1203
+ continue;
1204
+ }
1205
+
1206
+ // ── Object streaming path (sortScope='per-cell') ───────────────────
1207
+ // Step 1: rebuild specs cache when dirty or first time.
1208
+ if (w.dirty !== 0 || !streaming.layers.has(layerKey)) {
1209
+ // Despawn all currently-active chunks for this layer.
1210
+ const activeSet = streaming.activeChunks.get(layerKey);
1211
+ if (activeSet !== undefined) {
1212
+ for (const chunkIdx of activeSet) {
1213
+ const key = `${layerKey}:${chunkIdx}`;
1214
+ const entities = streaming.chunkEntities.get(key);
1215
+ if (entities !== undefined) {
1216
+ for (const e of entities) world.despawn(e as EntityHandle);
1217
+ streaming.chunkEntities.delete(key);
1218
+ }
1219
+ }
1220
+ activeSet.clear();
1221
+ }
1222
+ streaming.layers.delete(layerKey);
1223
+
1224
+ const bucket = bucketTileLayer(world, w.layerEntity, w.parentEntity, lookup);
1225
+ if (bucket !== undefined) {
1226
+ const byChunkSpecs = new Map<number, DerivedSpawnSpec[]>();
1227
+ for (const spec of bucket.specs) {
1228
+ const list = byChunkSpecs.get(spec.chunkIndex);
1229
+ if (list === undefined) {
1230
+ byChunkSpecs.set(spec.chunkIndex, [spec]);
1231
+ } else {
1232
+ list.push(spec);
1233
+ }
1234
+ }
1235
+ // bug-20260703 D2: promote each chunk's bucket to a ChunkStreamEntry
1236
+ // carrying the world-space bounds that enclose every tile's actual
1237
+ // footprint (not the tile-aligned grid box). The visibility test
1238
+ // then rejects the chunk only when its TRUE pixels are all
1239
+ // off-screen, so multi-cell / off-pivot tiles no longer flicker at
1240
+ // chunk-boundary crossings (plan-strategy §2 D-2 / D-3).
1241
+ const byChunk = new Map<number, ChunkStreamEntry>();
1242
+ for (const [chunkIdx, specs] of byChunkSpecs) {
1243
+ byChunk.set(chunkIdx, {
1244
+ specs,
1245
+ bounds: computeChunkStreamBounds(bucket.tilemap, specs),
1246
+ });
1247
+ }
1248
+ streaming.layers.set(layerKey, {
1249
+ byChunk,
1250
+ tilemap: bucket.tilemap,
1251
+ layerOrder: bucket.layerOrder,
1252
+ sortScope: bucket.sortScope,
1253
+ });
1254
+ }
1255
+ if (w.dirty !== 0) {
1256
+ world.set(w.layerEntity, TileLayer, { dirty: 0 }).unwrap();
1257
+ }
1258
+ }
1259
+
1260
+ const cache = streaming.layers.get(layerKey);
1261
+ if (cache === undefined) continue;
1262
+
1263
+ // Step 2: lazy-compute camera frustum on first streaming layer.
1264
+ if (frustumPlanes === undefined) {
1265
+ frustumPlanes = buildCameraFrustumPlanes(world);
1266
+ }
1267
+
1268
+ // Step 3: diff visible chunks against active set.
1269
+ const activeSet = streaming.activeChunks.get(layerKey) ?? new Set<number>();
1270
+ streaming.activeChunks.set(layerKey, activeSet);
1271
+ const { tilemap } = cache;
1272
+
1273
+ for (const [chunkIdx, entry] of cache.byChunk) {
1274
+ const { specs, bounds } = entry;
1275
+ const visible = frustumPlanes === null || frustum.intersectsBox(frustumPlanes, bounds);
1276
+ const wasActive = activeSet.has(chunkIdx);
1277
+
1278
+ if (visible && !wasActive) {
1279
+ // Spawn per-cell entities for this newly-visible chunk.
1280
+ const spawned: number[] = [];
1281
+ for (const spec of specs) {
1282
+ const e = spawnDerivedRenderEntities(
1283
+ world,
1284
+ tilemap,
1285
+ w.layerEntity,
1286
+ cache.layerOrder,
1287
+ spec,
1288
+ spec.packedTile,
1289
+ cache.sortScope,
1290
+ );
1291
+ spawned.push(unwrapHandle(e as unknown as Handle<string, 'shared'>));
1292
+ }
1293
+ streaming.chunkEntities.set(`${layerKey}:${chunkIdx}`, spawned);
1294
+ activeSet.add(chunkIdx);
1295
+ } else if (!visible && wasActive) {
1296
+ // Despawn per-cell entities for this newly-invisible chunk.
1297
+ const key = `${layerKey}:${chunkIdx}`;
1298
+ const entities = streaming.chunkEntities.get(key);
1299
+ if (entities !== undefined) {
1300
+ for (const e of entities) world.despawn(e as EntityHandle);
1301
+ streaming.chunkEntities.delete(key);
1302
+ }
1303
+ activeSet.delete(chunkIdx);
1304
+ }
1305
+ }
1306
+ }
1307
+ }