@woosh/meep-engine 3.22.0 → 3.23.0

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 (267) hide show
  1. package/package.json +1 -1
  2. package/src/core/geom/3d/hash-grid/PointHashGrid3.d.ts +118 -0
  3. package/src/core/geom/3d/hash-grid/PointHashGrid3.d.ts.map +1 -0
  4. package/src/core/geom/3d/hash-grid/PointHashGrid3.js +283 -0
  5. package/src/core/geom/3d/shape/BoxShape3D.d.ts.map +1 -1
  6. package/src/core/geom/3d/shape/BoxShape3D.js +6 -17
  7. package/src/core/geom/3d/shape/CapsuleShape3D.d.ts.map +1 -1
  8. package/src/core/geom/3d/shape/CapsuleShape3D.js +3 -14
  9. package/src/core/geom/3d/shape/CylinderShape3D.d.ts.map +1 -1
  10. package/src/core/geom/3d/shape/CylinderShape3D.js +3 -21
  11. package/src/core/geom/3d/shape/PlaneShape3D.d.ts +105 -0
  12. package/src/core/geom/3d/shape/PlaneShape3D.d.ts.map +1 -0
  13. package/src/core/geom/3d/shape/PlaneShape3D.js +251 -0
  14. package/src/core/geom/3d/shape/SphereShape3D.js +3 -3
  15. package/src/core/geom/3d/shape/json/shape_to_type.d.ts.map +1 -1
  16. package/src/core/geom/3d/shape/json/shape_to_type.js +3 -0
  17. package/src/core/geom/3d/shape/json/type_adapters.d.ts +14 -0
  18. package/src/core/geom/3d/shape/json/type_adapters.d.ts.map +1 -1
  19. package/src/core/geom/3d/shape/json/type_adapters.js +15 -0
  20. package/src/core/geom/3d/shape/sdf/sdf3_box.d.ts +51 -0
  21. package/src/core/geom/3d/shape/sdf/sdf3_box.d.ts.map +1 -0
  22. package/src/core/geom/3d/shape/sdf/sdf3_box.js +113 -0
  23. package/src/core/geom/3d/shape/sdf/sdf3_capsule.d.ts +46 -0
  24. package/src/core/geom/3d/shape/sdf/sdf3_capsule.d.ts.map +1 -0
  25. package/src/core/geom/3d/shape/sdf/sdf3_capsule.js +82 -0
  26. package/src/core/geom/3d/shape/sdf/sdf3_cylinder.d.ts +60 -0
  27. package/src/core/geom/3d/shape/sdf/sdf3_cylinder.d.ts.map +1 -0
  28. package/src/core/geom/3d/shape/sdf/sdf3_cylinder.js +126 -0
  29. package/src/core/geom/3d/shape/sdf/sdf3_degenerate_normal.d.ts +50 -0
  30. package/src/core/geom/3d/shape/sdf/sdf3_degenerate_normal.d.ts.map +1 -0
  31. package/src/core/geom/3d/shape/sdf/sdf3_degenerate_normal.js +53 -0
  32. package/src/core/geom/3d/shape/sdf/sdf3_plane.d.ts +52 -0
  33. package/src/core/geom/3d/shape/sdf/sdf3_plane.d.ts.map +1 -0
  34. package/src/core/geom/3d/shape/sdf/sdf3_plane.js +61 -0
  35. package/src/core/geom/3d/shape/sdf/sdf3_sphere.d.ts +41 -0
  36. package/src/core/geom/3d/shape/sdf/sdf3_sphere.d.ts.map +1 -0
  37. package/src/core/geom/3d/shape/sdf/sdf3_sphere.js +71 -0
  38. package/src/core/process/ConcurrencyGate.d.ts +103 -0
  39. package/src/core/process/ConcurrencyGate.d.ts.map +1 -0
  40. package/src/core/process/ConcurrencyGate.js +207 -0
  41. package/src/engine/asset/AssetManager.d.ts +10 -0
  42. package/src/engine/asset/AssetManager.d.ts.map +1 -1
  43. package/src/engine/asset/AssetManager.js +10 -0
  44. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +30 -13
  45. package/src/engine/graphics3/pose/collect_entity_playbacks.d.ts +26 -0
  46. package/src/engine/graphics3/pose/collect_entity_playbacks.d.ts.map +1 -0
  47. package/src/engine/graphics3/pose/collect_entity_playbacks.js +40 -0
  48. package/src/engine/graphics3/pose/query_entity_node_world_pose.d.ts.map +1 -1
  49. package/src/engine/graphics3/pose/query_entity_node_world_pose.js +74 -101
  50. package/src/engine/physics/cloth/MEASUREMENTS.md +1287 -4
  51. package/src/engine/physics/cloth/PLAN.md +1967 -1581
  52. package/src/engine/physics/cloth/build/ClothProxy.d.ts +147 -0
  53. package/src/engine/physics/cloth/build/ClothProxy.d.ts.map +1 -0
  54. package/src/engine/physics/cloth/build/ClothProxy.js +207 -0
  55. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts +70 -0
  56. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts.map +1 -0
  57. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.js +256 -0
  58. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts +176 -0
  59. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts.map +1 -0
  60. package/src/engine/physics/cloth/collider/ClothColliderIndex.js +435 -0
  61. package/src/engine/physics/cloth/collider/ClothColliderKind.d.ts +12 -0
  62. package/src/engine/physics/cloth/collider/ClothColliderKind.d.ts.map +1 -0
  63. package/src/engine/physics/cloth/collider/ClothColliderKind.js +38 -0
  64. package/src/engine/physics/cloth/collider/ClothColliderRecord.d.ts +120 -0
  65. package/src/engine/physics/cloth/collider/ClothColliderRecord.d.ts.map +1 -0
  66. package/src/engine/physics/cloth/collider/ClothColliderRecord.js +137 -0
  67. package/src/engine/physics/cloth/collider/cloth_collider_bounds.d.ts +25 -0
  68. package/src/engine/physics/cloth/collider/cloth_collider_bounds.d.ts.map +1 -0
  69. package/src/engine/physics/cloth/collider/cloth_collider_bounds.js +190 -0
  70. package/src/engine/physics/cloth/collider/cloth_collider_pack.d.ts +44 -0
  71. package/src/engine/physics/cloth/collider/cloth_collider_pack.d.ts.map +1 -0
  72. package/src/engine/physics/cloth/collider/cloth_collider_pack.js +173 -0
  73. package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts +94 -0
  74. package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts.map +1 -0
  75. package/src/engine/physics/cloth/collider/cloth_collider_sdf.js +275 -0
  76. package/src/engine/physics/cloth/ecs/ClothCollider.d.ts +126 -0
  77. package/src/engine/physics/cloth/ecs/ClothCollider.d.ts.map +1 -0
  78. package/src/engine/physics/cloth/ecs/ClothCollider.js +199 -0
  79. package/src/engine/physics/cloth/ecs/ClothColliderFlags.d.ts +9 -0
  80. package/src/engine/physics/cloth/ecs/ClothColliderFlags.d.ts.map +1 -0
  81. package/src/engine/physics/cloth/ecs/ClothColliderFlags.js +32 -0
  82. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts +114 -0
  83. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts.map +1 -0
  84. package/src/engine/physics/cloth/ecs/ClothColliderSystem.js +243 -0
  85. package/src/engine/physics/cloth/ecs/ClothDynamicsFlags.js +66 -57
  86. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +109 -1
  87. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
  88. package/src/engine/physics/cloth/ecs/ClothInstance.js +479 -363
  89. package/src/engine/physics/cloth/ecs/ClothRig.d.ts +95 -0
  90. package/src/engine/physics/cloth/ecs/ClothRig.d.ts.map +1 -0
  91. package/src/engine/physics/cloth/ecs/ClothRig.js +140 -0
  92. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +30 -1
  93. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
  94. package/src/engine/physics/cloth/ecs/ClothSystem.js +1021 -773
  95. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts +90 -0
  96. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -0
  97. package/src/engine/physics/cloth/ecs/cloth_build_rows.js +374 -0
  98. package/src/engine/physics/cloth/ecs/cloth_dynamics_library.d.ts +32 -3
  99. package/src/engine/physics/cloth/ecs/cloth_dynamics_library.d.ts.map +1 -1
  100. package/src/engine/physics/cloth/ecs/cloth_dynamics_library.js +283 -254
  101. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +59 -0
  102. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -0
  103. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +328 -0
  104. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts +13 -0
  105. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts.map +1 -0
  106. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.js +288 -0
  107. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts +58 -0
  108. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts.map +1 -0
  109. package/src/engine/physics/cloth/ecs/cloth_seed_rig.js +199 -0
  110. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts +8 -63
  111. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts.map +1 -1
  112. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.js +317 -645
  113. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts +61 -0
  114. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts.map +1 -1
  115. package/src/engine/physics/cloth/ecs/cloth_write_back.js +212 -449
  116. package/src/engine/physics/cloth/ecs/cloth_write_back_rig.d.ts +49 -0
  117. package/src/engine/physics/cloth/ecs/cloth_write_back_rig.d.ts.map +1 -0
  118. package/src/engine/physics/cloth/ecs/cloth_write_back_rig.js +115 -0
  119. package/src/engine/physics/cloth/playground/README.md +155 -1
  120. package/src/engine/physics/cloth/playground/collide.html +178 -0
  121. package/src/engine/physics/cloth/playground/collide_build.d.ts +95 -0
  122. package/src/engine/physics/cloth/playground/collide_build.d.ts.map +1 -0
  123. package/src/engine/physics/cloth/playground/collide_build.js +315 -0
  124. package/src/engine/physics/cloth/playground/collide_main.d.ts +2 -0
  125. package/src/engine/physics/cloth/playground/collide_main.d.ts.map +1 -0
  126. package/src/engine/physics/cloth/playground/collide_main.js +720 -0
  127. package/src/engine/physics/cloth/playground/garment.html +182 -0
  128. package/src/engine/physics/cloth/playground/garment_build.d.ts +78 -0
  129. package/src/engine/physics/cloth/playground/garment_build.d.ts.map +1 -0
  130. package/src/engine/physics/cloth/playground/garment_build.js +359 -0
  131. package/src/engine/physics/cloth/playground/garment_main.d.ts +2 -0
  132. package/src/engine/physics/cloth/playground/garment_main.d.ts.map +1 -0
  133. package/src/engine/physics/cloth/playground/garment_main.js +633 -0
  134. package/src/engine/physics/cloth/solver/ClothState.d.ts +208 -5
  135. package/src/engine/physics/cloth/solver/ClothState.d.ts.map +1 -1
  136. package/src/engine/physics/cloth/solver/ClothState.js +302 -8
  137. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts +87 -0
  138. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts.map +1 -0
  139. package/src/engine/physics/cloth/solver/cloth_contact_find.js +343 -0
  140. package/src/engine/physics/cloth/solver/cloth_self_find.d.ts +112 -0
  141. package/src/engine/physics/cloth/solver/cloth_self_find.d.ts.map +1 -0
  142. package/src/engine/physics/cloth/solver/cloth_self_find.js +448 -0
  143. package/src/engine/physics/cloth/solver/cloth_solve_vertex.d.ts +4 -1
  144. package/src/engine/physics/cloth/solver/cloth_solve_vertex.d.ts.map +1 -1
  145. package/src/engine/physics/cloth/solver/cloth_solve_vertex.js +16 -1
  146. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +112 -5
  147. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
  148. package/src/engine/physics/cloth/solver/cloth_solver_constants.js +117 -6
  149. package/src/engine/physics/cloth/solver/cloth_step.d.ts +40 -9
  150. package/src/engine/physics/cloth/solver/cloth_step.d.ts.map +1 -1
  151. package/src/engine/physics/cloth/solver/cloth_step.js +122 -10
  152. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts +95 -0
  153. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts.map +1 -0
  154. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.js +285 -0
  155. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts +129 -0
  156. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts.map +1 -0
  157. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.js +501 -0
  158. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.d.ts +75 -0
  159. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.d.ts.map +1 -0
  160. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.js +202 -0
  161. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  162. package/src/shade/playground/vgeo_viewer/README.md +18 -0
  163. package/src/shade/playground/vgeo_viewer/main.js +87 -50
  164. package/src/shade/renderer/buffer/table/GPUDatabase.d.ts.map +1 -1
  165. package/src/shade/renderer/buffer/table/GPUDatabase.js +191 -62
  166. package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts +94 -2
  167. package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
  168. package/src/shade/renderer/buffer/table/GPUTypedTable.js +414 -2
  169. package/src/shade/renderer/buffer/table/GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS.d.ts +14 -0
  170. package/src/shade/renderer/buffer/table/GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS.d.ts.map +1 -0
  171. package/src/shade/renderer/buffer/table/GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS.js +13 -0
  172. package/src/shade/renderer/buffer/table/gpu_table_partial_upload_shader.d.ts +17 -0
  173. package/src/shade/renderer/buffer/table/gpu_table_partial_upload_shader.d.ts.map +1 -0
  174. package/src/shade/renderer/buffer/table/gpu_table_partial_upload_shader.js +116 -0
  175. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts +9 -4
  176. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
  177. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +843 -734
  178. package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.d.ts +12 -0
  179. package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.d.ts.map +1 -1
  180. package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.js +13 -0
  181. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts +17 -0
  182. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
  183. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +185 -30
  184. package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +17 -0
  185. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +141 -24
  186. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +204 -64
  187. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +110 -10
  188. package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +19 -0
  189. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts +19 -6
  190. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts.map +1 -1
  191. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.js +26 -7
  192. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts +173 -14
  193. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts.map +1 -1
  194. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.js +458 -51
  195. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts +49 -13
  196. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts.map +1 -1
  197. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.js +52 -14
  198. package/src/shade/renderer/geometry/virtual/format/read/tool/VGEO_MAX_AUDIT_BYTES.d.ts +20 -0
  199. package/src/shade/renderer/geometry/virtual/format/read/tool/VGEO_MAX_AUDIT_BYTES.d.ts.map +1 -0
  200. package/src/shade/renderer/geometry/virtual/format/read/tool/VGEO_MAX_AUDIT_BYTES.js +19 -0
  201. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_audit_container.d.ts +27 -0
  202. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_audit_container.d.ts.map +1 -0
  203. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_audit_container.js +48 -0
  204. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_install_all_pages.d.ts +29 -0
  205. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_install_all_pages.d.ts.map +1 -0
  206. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_install_all_pages.js +60 -0
  207. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts +8 -1
  208. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts.map +1 -1
  209. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.js +32 -2
  210. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts +258 -0
  211. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts.map +1 -0
  212. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.js +723 -0
  213. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts +49 -0
  214. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts.map +1 -0
  215. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.js +50 -0
  216. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  217. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  218. package/src/shade/renderer/texture/bindless/GPUBindlessTextureManager.d.ts +3 -0
  219. package/src/shade/renderer/texture/bindless/GPUBindlessTextureManager.d.ts.map +1 -1
  220. package/src/shade/renderer/texture/bindless/GPUBindlessTextureManager.js +121 -21
  221. package/src/shade/renderer/texture/virtual/README.md +6 -4
  222. package/src/shade/renderer/texture/virtual/VirtualTextureManager.d.ts.map +1 -1
  223. package/src/shade/renderer/texture/virtual/VirtualTextureManager.js +7 -0
  224. package/src/shade/renderer/texture/virtual/source/VTSourceTiled.d.ts +3 -3
  225. package/src/shade/renderer/texture/virtual/source/VTSourceTiled.js +3 -3
  226. package/src/engine/ecs/system/SystemExecutionPhase.d.ts +0 -9
  227. package/src/engine/ecs/system/SystemExecutionPhase.d.ts.map +0 -1
  228. package/src/engine/graphics/texture/virtual/NOTES.md +0 -128
  229. package/src/engine/graphics/texture/virtual/VT_DEFAULT_PAGE_RESOLUTION.d.ts +0 -6
  230. package/src/engine/graphics/texture/virtual/VT_DEFAULT_PAGE_RESOLUTION.d.ts.map +0 -1
  231. package/src/engine/graphics/texture/virtual/VT_DEFAULT_PAGE_RESOLUTION.js +0 -5
  232. package/src/engine/graphics/texture/virtual/VirtualTextureTileLoader.d.ts +0 -49
  233. package/src/engine/graphics/texture/virtual/VirtualTextureTileLoader.d.ts.map +0 -1
  234. package/src/engine/graphics/texture/virtual/VirtualTextureTileLoader.js +0 -264
  235. package/src/engine/graphics/texture/virtual/VirtualTextureUsage.d.ts +0 -44
  236. package/src/engine/graphics/texture/virtual/VirtualTextureUsage.d.ts.map +0 -1
  237. package/src/engine/graphics/texture/virtual/VirtualTextureUsage.js +0 -228
  238. package/src/engine/graphics/texture/virtual/VirtualTextureUsageShader.d.ts +0 -3
  239. package/src/engine/graphics/texture/virtual/VirtualTextureUsageShader.d.ts.map +0 -1
  240. package/src/engine/graphics/texture/virtual/VirtualTextureUsageShader.js +0 -73
  241. package/src/engine/graphics/texture/virtual/debug/ResidencyDebugView.d.ts +0 -14
  242. package/src/engine/graphics/texture/virtual/debug/ResidencyDebugView.d.ts.map +0 -1
  243. package/src/engine/graphics/texture/virtual/debug/ResidencyDebugView.js +0 -85
  244. package/src/engine/graphics/texture/virtual/debug/UsageDebugView.d.ts +0 -12
  245. package/src/engine/graphics/texture/virtual/debug/UsageDebugView.d.ts.map +0 -1
  246. package/src/engine/graphics/texture/virtual/debug/UsageDebugView.js +0 -63
  247. package/src/engine/graphics/texture/virtual/debug/UsagePyramidDebugView.d.ts +0 -12
  248. package/src/engine/graphics/texture/virtual/debug/UsagePyramidDebugView.d.ts.map +0 -1
  249. package/src/engine/graphics/texture/virtual/debug/UsagePyramidDebugView.js +0 -243
  250. package/src/engine/graphics/texture/virtual/tile/VirtualTextureTile.d.ts +0 -30
  251. package/src/engine/graphics/texture/virtual/tile/VirtualTextureTile.d.ts.map +0 -1
  252. package/src/engine/graphics/texture/virtual/tile/VirtualTextureTile.js +0 -39
  253. package/src/engine/graphics/texture/virtual/tile/compose_finger_print.d.ts +0 -9
  254. package/src/engine/graphics/texture/virtual/tile/compose_finger_print.d.ts.map +0 -1
  255. package/src/engine/graphics/texture/virtual/tile/compose_finger_print.js +0 -24
  256. package/src/engine/graphics/texture/virtual/tile/compose_tile_address.d.ts +0 -9
  257. package/src/engine/graphics/texture/virtual/tile/compose_tile_address.d.ts.map +0 -1
  258. package/src/engine/graphics/texture/virtual/tile/compose_tile_address.js +0 -26
  259. package/src/engine/graphics/texture/virtual/tile/decompose_finger_print.d.ts +0 -11
  260. package/src/engine/graphics/texture/virtual/tile/decompose_finger_print.d.ts.map +0 -1
  261. package/src/engine/graphics/texture/virtual/tile/decompose_finger_print.js +0 -12
  262. package/src/engine/graphics/texture/virtual/tile/finger_print_to_tile_address.d.ts +0 -7
  263. package/src/engine/graphics/texture/virtual/tile/finger_print_to_tile_address.d.ts.map +0 -1
  264. package/src/engine/graphics/texture/virtual/tile/finger_print_to_tile_address.js +0 -16
  265. package/src/engine/graphics/texture/virtual/tile/tile_address_to_finger_print.d.ts +0 -7
  266. package/src/engine/graphics/texture/virtual/tile/tile_address_to_finger_print.d.ts.map +0 -1
  267. package/src/engine/graphics/texture/virtual/tile/tile_address_to_finger_print.js +0 -35
@@ -0,0 +1,207 @@
1
+ import { assert } from "../assert.js";
2
+ import BinaryHeap from "../collection/heap/BinaryHeap.js";
3
+
4
+ /**
5
+ * Runs at most {@link max_concurrent} tasks at a time, highest priority first across everything
6
+ * submitted to it.
7
+ *
8
+ * The point of putting one of these between a caller and its work is almost never the cap on its
9
+ * own — a caller can count its own in-flight requests. It is that **one** gate shared by several
10
+ * callers orders their work against each other. Per-caller caps do not compose: the number a
11
+ * browser, a CDN or a disk cares about is the total, and a caller that knows about one file cannot
12
+ * see it. Neither can a per-caller queue say that the second caller's request matters more than the
13
+ * first caller's, however truthfully their priorities compare.
14
+ *
15
+ * Ties are broken arbitrarily. Two tasks submitted at the same priority are, by the only measure the
16
+ * gate has, equally worth running.
17
+ *
18
+ * Nothing here is a new idea in this engine, which is why it ended up here rather than inside any
19
+ * one of its callers. `AssetManager` has capped concurrent top-level loads on `load_concurrency` for the
20
+ * same stated reason — keeping network slots free for higher-priority requests — and orders its
21
+ * pending queue through `get_pending_asset_priority_score`, which negates the priority for exactly
22
+ * the reason this heap does: `BinaryHeap` pops the lowest score first. `VirtualTextureManager` caps
23
+ * `max_concurrent_loads` against `#loads_in_flight`. When the virtual-geometry residency layer
24
+ * wanted the cap a third time and wrote that same negation a second time, it stopped being a detail
25
+ * of any one caller. The other two keep their own queues anyway, deliberately, and each says why
26
+ * where its cap is declared; the three paragraphs below are those reasons.
27
+ *
28
+ * **Priority is read once, at {@link submit}.** A gate keyed on a number captured at submission is
29
+ * the wrong shape for work whose importance moves while it waits, or whose priorities were never on
30
+ * a common scale: `AssetRequestScope.final_priority` is a richer model than a bare number, but it
31
+ * propagates hierarchically from an asset's requesting scopes and so compares only inside one
32
+ * hierarchy. For either, keep a queue over live records and re-score it (see below).
33
+ *
34
+ * **A task must not wait on another submission to the same gate.** The parent holds a slot while the
35
+ * child waits for one, so at a full gate that is a deadlock, and the gate cannot detect it: it sees
36
+ * a task that has not settled. Work that nests needs the parent's slot to cover the child, which
37
+ * means dispatching the child over the cap — `AssetManager` does exactly that for the requests its
38
+ * loaders make while servicing a load, and keeps its own queue for it.
39
+ *
40
+ * **What it deliberately is not.** There is no cancellation, and no way to drop or re-order a
41
+ * submission once it is made: a queued entry holds a closure and the gate will eventually run it.
42
+ * A caller that wants to *discard* pending work — because the page stopped being on screen, or a
43
+ * coarser mip arrived instead — wants a queue of live records it can filter, drained on its own
44
+ * clock, not a gate that owns closures. `VirtualTextureManager` drops queued requests untouched for
45
+ * `queue_stale_window` frames; it is that second thing, and no worse for it.
46
+ *
47
+ * Nothing bounds how long a low-priority submission waits. A caller that keeps feeding the gate
48
+ * higher-priority work will starve it indefinitely, which is what a priority queue means and is
49
+ * fine for the admission gates named above — where a dropped want is re-derived next frame — and not
50
+ * fine for work that has to finish eventually.
51
+ *
52
+ * @author Alex Goldring
53
+ * @copyright Company Named Limited (c) 2026
54
+ */
55
+ export class ConcurrencyGate {
56
+ /**
57
+ * @type {number}
58
+ */
59
+ #max_concurrent;
60
+
61
+ /**
62
+ * @type {number}
63
+ */
64
+ #in_flight = 0;
65
+
66
+ /**
67
+ * Waiting tasks, highest priority first — a min-heap on the negated priority.
68
+ * @type {BinaryHeap<{priority: number, task: function(): Promise<*>, resolve: function(*): void, reject: function(*): void}>}
69
+ */
70
+ #queue = new BinaryHeap((entry) => -entry.priority);
71
+
72
+ /**
73
+ * @type {number}
74
+ */
75
+ #admitted = 0;
76
+
77
+ /**
78
+ * @type {number}
79
+ */
80
+ #peak_in_flight = 0;
81
+
82
+ /**
83
+ * @param {number} max_concurrent how many tasks may be running at once
84
+ */
85
+ constructor(max_concurrent) {
86
+ assert.isPositiveInteger(max_concurrent, 'max_concurrent');
87
+
88
+ this.#max_concurrent = max_concurrent;
89
+ }
90
+
91
+ /**
92
+ * @returns {number}
93
+ */
94
+ get max_concurrent() {
95
+ return this.#max_concurrent;
96
+ }
97
+
98
+ /**
99
+ * Raising this starts whatever was already waiting. Lowering it starts nothing and stops
100
+ * nothing — tasks already running are left to finish, and the gate admits again once they have
101
+ * drained below the new value.
102
+ *
103
+ * @param {number} value
104
+ */
105
+ set max_concurrent(value) {
106
+ assert.isPositiveInteger(value, 'max_concurrent');
107
+
108
+ this.#max_concurrent = value;
109
+
110
+ this.#pump();
111
+ }
112
+
113
+ /**
114
+ * @returns {number}
115
+ */
116
+ get in_flight() {
117
+ return this.#in_flight;
118
+ }
119
+
120
+ /**
121
+ * @returns {number}
122
+ */
123
+ get queued() {
124
+ return this.#queue.size();
125
+ }
126
+
127
+ /**
128
+ * How many tasks this gate has started. Telemetry.
129
+ * @returns {number}
130
+ */
131
+ get admitted() {
132
+ return this.#admitted;
133
+ }
134
+
135
+ /**
136
+ * The most that were ever in flight at once — which should never exceed the cap that was in
137
+ * force when they started, and is here so that a test can say so rather than assume it.
138
+ * @returns {number}
139
+ */
140
+ get peak_in_flight() {
141
+ return this.#peak_in_flight;
142
+ }
143
+
144
+ /**
145
+ * Run a task when there is room, highest priority first.
146
+ *
147
+ * The task is never invoked synchronously, even when the gate is empty: it runs from a
148
+ * microtask, so a task that throws on its first line is a rejected promise here rather than an
149
+ * exception thrown back at whoever called {@link submit}.
150
+ *
151
+ * @template T
152
+ * @param {number} priority higher runs sooner
153
+ * @param {function(): Promise<T>} task
154
+ * @returns {Promise<T>} whatever the task settled to, once it ran
155
+ */
156
+ submit(priority, task) {
157
+ assert.isNumber(priority, 'priority');
158
+ assert.isFunction(task, 'task');
159
+
160
+ return new Promise((resolve, reject) => {
161
+ this.#queue.push({ priority, task, resolve, reject });
162
+
163
+ this.#pump();
164
+ });
165
+ }
166
+
167
+ /**
168
+ * Start whatever fits.
169
+ */
170
+ #pump() {
171
+ while (this.#in_flight < this.#max_concurrent && !this.#queue.isEmpty()) {
172
+ const entry = this.#queue.pop();
173
+
174
+ this.#in_flight++;
175
+ this.#admitted++;
176
+
177
+ if (this.#in_flight > this.#peak_in_flight) {
178
+ this.#peak_in_flight = this.#in_flight;
179
+ }
180
+
181
+ /*
182
+ Settled through two explicit branches rather than `.finally`, because `.finally` returns
183
+ a promise of its own and a rejection travelling through it with nothing attached is an
184
+ unhandled rejection — which at the scale a gate is reached for is not a warning in a
185
+ log, it is a process that stops.
186
+ */
187
+ const settle = () => {
188
+ this.#in_flight--;
189
+
190
+ this.#pump();
191
+ };
192
+
193
+ Promise.resolve().then(() => entry.task()).then(
194
+ (value) => {
195
+ settle();
196
+
197
+ entry.resolve(value);
198
+ },
199
+ (error) => {
200
+ settle();
201
+
202
+ entry.reject(error);
203
+ }
204
+ );
205
+ }
206
+ }
207
+ }
@@ -32,6 +32,16 @@ export class AssetManager<CTX> {
32
32
  * this can be useful to keep some network slots available for high-priority requests such as UI related ones.
33
33
  * Nested requests (made by a loader while servicing another load) are dispatched immediately and never wait
34
34
  * on this limit: their parent occupies a slot until they resolve, so queueing them would deadlock.
35
+ *
36
+ * Not a `core/process/ConcurrencyGate`, and the three reasons are each on their own decisive.
37
+ * That gate admits closures it owns and resolves one promise per task; a load here is dispatched
38
+ * by callback and settles exactly once, reached from several places. Its ordering key is read
39
+ * once, at submission; the key here is re-read live on every heap sift, because
40
+ * {@link AssetRequestScope#final_priority} walks the scope chain and a later request joining an
41
+ * already-queued asset re-scores it in place. And the nested-request bypass dispatches *over*
42
+ * this limit on purpose, which is the one thing a gate whose whole contract is "never more than
43
+ * N in flight" cannot express.
44
+ *
35
45
  * @type {number}
36
46
  */
37
47
  load_concurrency: number;
@@ -1 +1 @@
1
- {"version":3,"file":"AssetManager.d.ts","sourceRoot":"","sources":["../../../../src/engine/asset/AssetManager.js"],"names":[],"mappings":"AAuDA;;;;GAIG;AACH;IA4MI;;;;;;OAMG;IACH;QAJwB,OAAO,EAApB,GAAG;QACyB,QAAQ,EAApC,kBAAkB;OAW5B;IA1ND;;;;OAIG;IACH,eAEG;IAEH;;;OAGG;IACH,aAFU,YAAY,gBAAgB,EAAE,YAAY,CAAC,CAER;IAuB7C;;;;;;;OAOG;IACH,kBAFU,MAAM,CAEY;IA4B5B;;;;;;OAMG;IACH,iBAAc;IAEd;;;OAGG;IACH,mBAFU,iBAAiB,CAEiB;IAuI5C,gBAWC;IAED;;;;;;OAMG;IACH,qBALW,OAAO,GAGN,QAAQ,IAAI,CAAC,CAgBxB;IA4ED;;;;;;;OAOG;IACH,yBAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;OAKG;IACH,oCAFa,OAAO,CAOnB;IAED;;OAEG;IACH,cAEC;IAED,8BAEC;IAED;;;;;;;;OAQG;IACH;QALuC,KAAK,GAAjC,iBAAiB;QACC,UAAU,GAA5B,OAAO;QAC2C,QAAQ,GAA1D,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI;QACrC,wCAAc,CAc1B;IAED;;;;;;;;;;OAUG;IACH;;;mCANgC,IAAI;kBACzB,CAAC,MAAM,KAAG,KAAK,IAAI;mBACnB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI;qBACvC,OAAO;gBACP,iBAAiB;aA8D3B;IAED;;;;;;;OAOG;IACH,gBAJW,MAAM,QACN,MAAM,GACL,OAAO,CAKlB;IAGD;;;;;;;OAOG;IACH,eALW,MAAM,QACN,MAAM,GACJ,OAAO,CAOnB;IAED;;;;;;;OAOG;IACH,eAJW,MAAM,QACN,MAAM,GACL,OAAO,CAMlB;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,uBAZW,MAAM,QACN,MAAM,sBACM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,iCAwE9D;IAED;;;;;;;;;;;OAWG;IACH,kBAJW,MAAM,QACN,MAAM,oBAoChB;IAgbD;;;;;;OAMG;IACH,sBAkCC;IAGD;;;;OAIG;IACH,uBAHW,MAAM,GACL,OAAO,CAIlB;IAED;;;;OAIG;IACH,sBAHW,MAAM,GACJ,wBAAY,SAAS,CAMjC;IAaD;;;;;;;;;;OAUG;IACH,+BALW,MAAM,qBAEJ,IAAI,CAqBhB;IAED;;;;;;OAMG;IACH,iCALW,MAAM,qBAEJ,OAAO,CAyBnB;IAED;;;;;;OAMG;IACH,6BAJW,MAAM,kCAEJ,QAAQ,OAAO,CAAC,CAU5B;IAED;;;;;OAKG;IACH,0BAJW,MAAM,iEAwChB;IAED;;;OAGG;IACH,uBAFW,MAAM,iBAkBhB;IAED;;;;;;;OAOG;IACH,6CAUC;IAED;;;;OAIG;IACH,2BAHW,MAAM,gBAchB;IAED;;;;OAIG;IACH,oBAHW,MAAM,GACL,gBAAgB,GAAC,SAAS,CAQrC;IAED;;;;;OAKG;IACH,mBAJW,MAAM,QACN,MAAM,QACN,MAAM,QAUhB;IAGL;;;OAGG;IACH,yBAFU,OAAO,CAEoB;;CANpC;;;4BA/3C2B,0CAA0C;iCAOrC,uBAAuB;6BAM3B,mBAAmB;kCAFd,6BAA6B;kCAD7B,wBAAwB;4BAE9B,0BAA0B;+BARvB,mDAAmD"}
1
+ {"version":3,"file":"AssetManager.d.ts","sourceRoot":"","sources":["../../../../src/engine/asset/AssetManager.js"],"names":[],"mappings":"AAuDA;;;;GAIG;AACH;IAsNI;;;;;;OAMG;IACH;QAJwB,OAAO,EAApB,GAAG;QACyB,QAAQ,EAApC,kBAAkB;OAW5B;IApOD;;;;OAIG;IACH,eAEG;IAEH;;;OAGG;IACH,aAFU,YAAY,gBAAgB,EAAE,YAAY,CAAC,CAER;IAuB7C;;;;;;;;;;;;;;;;;OAiBG;IACH,kBAFU,MAAM,CAEY;IA4B5B;;;;;;OAMG;IACH,iBAAc;IAEd;;;OAGG;IACH,mBAFU,iBAAiB,CAEiB;IAuI5C,gBAWC;IAED;;;;;;OAMG;IACH,qBALW,OAAO,GAGN,QAAQ,IAAI,CAAC,CAgBxB;IA4ED;;;;;;;OAOG;IACH,yBAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;OAKG;IACH,oCAFa,OAAO,CAOnB;IAED;;OAEG;IACH,cAEC;IAED,8BAEC;IAED;;;;;;;;OAQG;IACH;QALuC,KAAK,GAAjC,iBAAiB;QACC,UAAU,GAA5B,OAAO;QAC2C,QAAQ,GAA1D,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI;QACrC,wCAAc,CAc1B;IAED;;;;;;;;;;OAUG;IACH;;;mCANgC,IAAI;kBACzB,CAAC,MAAM,KAAG,KAAK,IAAI;mBACnB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI;qBACvC,OAAO;gBACP,iBAAiB;aA8D3B;IAED;;;;;;;OAOG;IACH,gBAJW,MAAM,QACN,MAAM,GACL,OAAO,CAKlB;IAGD;;;;;;;OAOG;IACH,eALW,MAAM,QACN,MAAM,GACJ,OAAO,CAOnB;IAED;;;;;;;OAOG;IACH,eAJW,MAAM,QACN,MAAM,GACL,OAAO,CAMlB;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,uBAZW,MAAM,QACN,MAAM,sBACM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,iCAwE9D;IAED;;;;;;;;;;;OAWG;IACH,kBAJW,MAAM,QACN,MAAM,oBAoChB;IAgbD;;;;;;OAMG;IACH,sBAkCC;IAGD;;;;OAIG;IACH,uBAHW,MAAM,GACL,OAAO,CAIlB;IAED;;;;OAIG;IACH,sBAHW,MAAM,GACJ,wBAAY,SAAS,CAMjC;IAaD;;;;;;;;;;OAUG;IACH,+BALW,MAAM,qBAEJ,IAAI,CAqBhB;IAED;;;;;;OAMG;IACH,iCALW,MAAM,qBAEJ,OAAO,CAyBnB;IAED;;;;;;OAMG;IACH,6BAJW,MAAM,kCAEJ,QAAQ,OAAO,CAAC,CAU5B;IAED;;;;;OAKG;IACH,0BAJW,MAAM,iEAwChB;IAED;;;OAGG;IACH,uBAFW,MAAM,iBAkBhB;IAED;;;;;;;OAOG;IACH,6CAUC;IAED;;;;OAIG;IACH,2BAHW,MAAM,gBAchB;IAED;;;;OAIG;IACH,oBAHW,MAAM,GACL,gBAAgB,GAAC,SAAS,CAQrC;IAED;;;;;OAKG;IACH,mBAJW,MAAM,QACN,MAAM,QACN,MAAM,QAUhB;IAGL;;;OAGG;IACH,yBAFU,OAAO,CAEoB;;CANpC;;;4BAz4C2B,0CAA0C;iCAOrC,uBAAuB;6BAM3B,mBAAmB;kCAFd,6BAA6B;kCAD7B,wBAAwB;4BAE9B,0BAA0B;+BARvB,mDAAmD"}
@@ -101,6 +101,16 @@ export class AssetManager {
101
101
  * this can be useful to keep some network slots available for high-priority requests such as UI related ones.
102
102
  * Nested requests (made by a loader while servicing another load) are dispatched immediately and never wait
103
103
  * on this limit: their parent occupies a slot until they resolve, so queueing them would deadlock.
104
+ *
105
+ * Not a `core/process/ConcurrencyGate`, and the three reasons are each on their own decisive.
106
+ * That gate admits closures it owns and resolves one promise per task; a load here is dispatched
107
+ * by callback and settles exactly once, reached from several places. Its ordering key is read
108
+ * once, at submission; the key here is re-read live on every heap sift, because
109
+ * {@link AssetRequestScope#final_priority} walks the scope chain and a later request joining an
110
+ * already-queued asset re-scores it in place. And the nested-request bypass dispatches *over*
111
+ * this limit on purpose, which is the one thing a gate whose whole contract is "never more than
112
+ * N in flight" cannot express.
113
+ *
104
114
  * @type {number}
105
115
  */
106
116
  load_concurrency = Infinity;
@@ -6,8 +6,14 @@ Date: 2026-07-30. Scope: `meep` graphics stack (three.js r136, WebGL2) as consum
6
6
  Goal: when the browser drops the WebGL context, detect it, recover in place, and let the player continue —
7
7
  no application restart, no reload, no lost session state.
8
8
 
9
- **Status: phases 0–3 implemented and verified.** Phases 4–6 remain (§11). Claims below are tagged
10
- **[src]** (read from three.js / meep source) or **[measured]** (observed in the browser harness, §10).
9
+ **Status: superseded, kept as the record.** Phases 0–3 were implemented and verified against the
10
+ renderer described here; that renderer — three.js r136 on WebGL2 — has since been removed from the
11
+ repo, and Shade (WebGPU) replaced it. Phases 4–6 (§11) will not be done as written: Shade does not
12
+ recover a lost device at all — `Renderer.on.contextRestored` is documented as never firing, and the
13
+ response to a loss is a message and a reload (`ShadeDeviceFailure.device_lost`) — so the in-place
14
+ recovery this document is about has no successor to port it to. Most links below point into a tree
15
+ that no longer holds these files. Claims are tagged **[src]** (read from three.js / meep source) or
16
+ **[measured]** (observed in the browser harness, §10).
11
17
 
12
18
  ---
13
19
 
@@ -110,18 +116,27 @@ produces the prefiltered radiance; `set_environment_texture` adds the skybox on
110
116
  re-bakes and disposes the previous target. `load_and_set_cubemap_v0` now loads the source and hands it
111
117
  over instead of baking behind the engine's back.
112
118
 
113
- ### 4.3 Raw GL handles and residency bookkeeping — library tier, not in the shipped game
119
+ ### 4.3 Raw GL handles and residency bookkeeping — library tier, since deleted
114
120
 
115
- These are `meep` public surface but are not reachable from `makeMirEngineConfig`, so they are correctness
116
- debt rather than shipping blockers (Phase 6):
121
+ These were `meep` public surface but were not reachable from `makeMirEngineConfig`, so they were
122
+ correctness debt rather than shipping blockers (Phase 6). Every one of them went with the WebGL
123
+ renderer, so the paths are recorded as they stood rather than linked:
117
124
 
118
- | Subsystem | What breaks | File |
125
+ | Subsystem | What breaks | File, as of 2026-07-30 |
119
126
  | --- | --- | --- |
120
- | Virtual texture | page atlas written with `copyTextureToTexture`; CPU residency map still claims pages are valid → garbage tiles forever | [VirtualTexturePage.js:389](src/engine/graphics/texture/virtual/VirtualTexturePage.js:389) |
121
- | `WebGLTextureAtlas` | same pattern | [WebGLTextureAtlas.js:178](src/engine/graphics/texture/atlas/gpu/WebGLTextureAtlas.js:178) |
122
- | Node-based particles | raw `gl.createBuffer()` handles + cached `gl`, no invalidation | [GLDataBuffer.js:48](src/engine/graphics/particles/node-based/simulation/GLDataBuffer.js:48), [TransformFeedback.js:414](src/engine/graphics/particles/node-based/simulation/TransformFeedback.js:414) |
123
- | Hi-Z occlusion | readback of a lost target yields zeroes → whole-frame visibility misclassification | [BatchOcclusionQuery.js:149](src/engine/graphics/render/visibility/hiz/query/BatchOcclusionQuery.js:149) |
124
- | Impostors | baked atlases are GPU-only | [ImpostorBaker.js](src/engine/graphics/impostors/octahedral/ImpostorBaker.js) |
127
+ | Virtual texture | page atlas written with `copyTextureToTexture`; CPU residency map still claims pages are valid → garbage tiles forever | `src/engine/graphics/texture/virtual/VirtualTexturePage.js:389` |
128
+ | `WebGLTextureAtlas` | same pattern | `src/engine/graphics/texture/atlas/gpu/WebGLTextureAtlas.js:178` |
129
+ | Node-based particles | raw `gl.createBuffer()` handles + cached `gl`, no invalidation | `src/engine/graphics/particles/node-based/simulation/GLDataBuffer.js:48`, `TransformFeedback.js:414` |
130
+ | Hi-Z occlusion | readback of a lost target yields zeroes → whole-frame visibility misclassification | `src/engine/graphics/render/visibility/hiz/query/BatchOcclusionQuery.js:149` |
131
+ | Impostors | baked atlases are GPU-only | `src/engine/graphics/impostors/octahedral/ImpostorBaker.js` |
132
+
133
+ Virtual texturing is the one row with a live successor — `shade/renderer/texture/virtual/`
134
+ (`VirtualTextureManager` / `VTPageTable` / `VTPhysicalCache`), which keeps a CPU mirror of residency
135
+ just as the old stack did. The concern still does not carry over, because it is a *restore* hazard:
136
+ it needs a frame drawn after the device comes back, reading a residency map that outlived the loss.
137
+ Shade never draws that frame — `Renderer.on.contextRestored` is documented as never firing — and the
138
+ VT stack accordingly has no loss, restore or generation hook anywhere in it. There is nothing to
139
+ re-point this row at.
125
140
 
126
141
  ### 4.4 Secondary contexts — Phase 4
127
142
 
@@ -442,5 +457,7 @@ re-attach `viewport.el` / `graphics.domElement` / styles / size, and re-run fram
442
457
  disposal of the old targets. The monitor already supports it — `attach()` resets the loss budget and returns
443
458
  to `Live` while keeping the generation counter monotonic. Only worth building on evidence.
444
459
 
445
- **Phase 6 (deferred) — library tier.** VT and GPU-atlas residency invalidation, node-based particle GL
446
- buffers, Hi-Z, impostor atlases (§4.3). Each is self-contained; do them when the subsystem is next touched.
460
+ **Phase 6 (closed) — library tier.** Not done, and will not be: every subsystem in §4.3 was deleted
461
+ along with the WebGL renderer. The only one with a successor is virtual texturing, and residency
462
+ invalidation does not carry over to it — Shade does not restore a lost device, so there is no frame in
463
+ which a stale residency map could be read (§4.3).
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Collect what every animation driver is playing on an entity, in the form the
3
+ * CPU pose evaluator takes.
4
+ *
5
+ * Both drivers are asked because either may be the one driving it — a combat
6
+ * unit runs a state machine, a building runs a clip list — and an entity has
7
+ * one of them, so the two sets do not overlap in practice. What they hand back
8
+ * is one entry per clip of the model, silent clips included, because that is
9
+ * what the GPU's accumulator is summing.
10
+ *
11
+ * A free function rather than a private helper of
12
+ * {@link query_entity_node_world_pose} because M3 gave it a second caller:
13
+ * cloth's rig path needs the anchor joint's world pose on the CPU for exactly
14
+ * the reason gameplay needs a socket's, and computing it from a different set
15
+ * of playbacks would put the garment somewhere the socket is not.
16
+ *
17
+ * @param {PosePlayback[]} target appended to; the caller owns its length
18
+ * @param {EntityManager} entity_manager
19
+ * @param {number} entity
20
+ * @returns {void}
21
+ *
22
+ * @author Alex Goldring
23
+ * @copyright Company Named Limited (c) 2026
24
+ */
25
+ export function collect_entity_playbacks(target: PosePlayback[], entity_manager: EntityManager, entity: number): void;
26
+ //# sourceMappingURL=collect_entity_playbacks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect_entity_playbacks.d.ts","sourceRoot":"","sources":["../../../../../src/engine/graphics3/pose/collect_entity_playbacks.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,iDARW,cAAc,yCAEd,MAAM,GACJ,IAAI,CAiBhB"}
@@ -0,0 +1,40 @@
1
+ import { AnimationGraphSystem } from "../AnimationGraphSystem.js";
2
+ import { AnimationSystem } from "../AnimationSystem.js";
3
+
4
+ /**
5
+ * Collect what every animation driver is playing on an entity, in the form the
6
+ * CPU pose evaluator takes.
7
+ *
8
+ * Both drivers are asked because either may be the one driving it — a combat
9
+ * unit runs a state machine, a building runs a clip list — and an entity has
10
+ * one of them, so the two sets do not overlap in practice. What they hand back
11
+ * is one entry per clip of the model, silent clips included, because that is
12
+ * what the GPU's accumulator is summing.
13
+ *
14
+ * A free function rather than a private helper of
15
+ * {@link query_entity_node_world_pose} because M3 gave it a second caller:
16
+ * cloth's rig path needs the anchor joint's world pose on the CPU for exactly
17
+ * the reason gameplay needs a socket's, and computing it from a different set
18
+ * of playbacks would put the garment somewhere the socket is not.
19
+ *
20
+ * @param {PosePlayback[]} target appended to; the caller owns its length
21
+ * @param {EntityManager} entity_manager
22
+ * @param {number} entity
23
+ * @returns {void}
24
+ *
25
+ * @author Alex Goldring
26
+ * @copyright Company Named Limited (c) 2026
27
+ */
28
+ export function collect_entity_playbacks(target, entity_manager, entity) {
29
+ const graphs = entity_manager.getSystem(AnimationGraphSystem);
30
+
31
+ if (graphs !== null) {
32
+ graphs.write_pose_playbacks(target, entity);
33
+ }
34
+
35
+ const clips = entity_manager.getSystem(AnimationSystem);
36
+
37
+ if (clips !== null) {
38
+ clips.write_pose_playbacks(target, entity);
39
+ }
40
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"query_entity_node_world_pose.d.ts","sourceRoot":"","sources":["../../../../../src/engine/graphics3/pose/query_entity_node_world_pose.js"],"names":[],"mappings":"AAeA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,yGANW,MAAM,aACN,MAAM,GACJ,OAAO,CAwCnB"}
1
+ {"version":3,"file":"query_entity_node_world_pose.d.ts","sourceRoot":"","sources":["../../../../../src/engine/graphics3/pose/query_entity_node_world_pose.js"],"names":[],"mappings":"AAcA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,yGANW,MAAM,aACN,MAAM,GACJ,OAAO,CAwCnB"}
@@ -1,101 +1,74 @@
1
- import { assert } from "../../../core/assert.js";
2
- import { PosePlayback } from "../../../shade/renderer/animation/pose/PosePlayback.js";
3
- import { pose_evaluate_world } from "../../../shade/renderer/animation/pose/pose_evaluate_world.js";
4
- import { AnimationGraphSystem } from "../AnimationGraphSystem.js";
5
- import { AnimationSystem } from "../AnimationSystem.js";
6
- import { MeshSystem } from "../MeshSystem.js";
7
-
8
- /**
9
- * The clip set handed to the evaluator, rebuilt per call from the systems' own entries. Nothing in
10
- * here outlives the call — the query is on a per-frame path and allocates nothing.
11
- *
12
- * @type {PosePlayback[]}
13
- */
14
- const PLAYBACKS = [];
15
-
16
- /**
17
- * Where a named part of an entity's model is, in world space.
18
- *
19
- * This is the question gameplay asks of an animated model and the only sanctioned way to ask it:
20
- * where does this projectile leave from, where does that effect anchor, where does the floating UI
21
- * hang. **The answer is computed, never read back** — GPU readback is a frame late and asynchronous,
22
- * so an outcome that depended on it would depend on frame pacing, and determinism and replay would
23
- * go with it: no GPU readback on a gameplay-affecting path.
24
- *
25
- * The pose is {@link pose_evaluate_world} over the clips the animation systems are driving: a pure
26
- * function of (clip set, time, weights), off the same clock the renderer is written from, which is
27
- * what keeps a socket on the mesh that is drawn.
28
- *
29
- * The result carries both its matrix and its components.
30
- *
31
- * @param {Transform64} result
32
- * @param {EntityManager} entity_manager
33
- * @param {number} entity
34
- * @param {string} node_name name the asset gave the node — a bone, a socket's bone, an effector
35
- * @returns {boolean} `false` when there is no such node to answer about: no mesh system is
36
- * registered, the model has not loaded yet, the entity has none, or the name is not in it. None of
37
- * the four is an error
38
- */
39
- export function query_entity_node_world_pose(result, entity_manager, entity, node_name) {
40
- assert.defined(result, 'result');
41
- assert.defined(entity_manager, 'entity_manager');
42
- assert.isNonNegativeInteger(entity, 'entity');
43
- assert.isString(node_name, 'node_name');
44
-
45
- const meshes = entity_manager.getSystem(MeshSystem);
46
-
47
- if (meshes === null) {
48
- // nothing draws models here, so nothing holds one to answer about
49
- return false;
50
- }
51
-
52
- const instance = meshes.instance_of(entity);
53
-
54
- if (instance === null) {
55
- return false;
56
- }
57
-
58
- const node = instance.nodes.get(node_name);
59
-
60
- if (node === undefined) {
61
- return false;
62
- }
63
-
64
- const playbacks = PLAYBACKS;
65
-
66
- playbacks.length = 0;
67
-
68
- write_playbacks(playbacks, entity_manager, entity);
69
-
70
- pose_evaluate_world(result, node, playbacks);
71
-
72
- playbacks.length = 0;
73
-
74
- return true;
75
- }
76
-
77
- /**
78
- * Collect what every animation driver is playing on the entity.
79
- *
80
- * Both are asked because either may be the one driving it — a combat unit runs a state machine, a
81
- * building runs a clip list — and an entity has one of them, so the two sets do not overlap in
82
- * practice. What they hand back is one entry per clip of the model, silent clips included, because
83
- * that is what the GPU's accumulator is summing.
84
- *
85
- * @param {PosePlayback[]} target
86
- * @param {EntityManager} entity_manager
87
- * @param {number} entity
88
- */
89
- function write_playbacks(target, entity_manager, entity) {
90
- const graphs = entity_manager.getSystem(AnimationGraphSystem);
91
-
92
- if (graphs !== null) {
93
- graphs.write_pose_playbacks(target, entity);
94
- }
95
-
96
- const clips = entity_manager.getSystem(AnimationSystem);
97
-
98
- if (clips !== null) {
99
- clips.write_pose_playbacks(target, entity);
100
- }
101
- }
1
+ import { assert } from "../../../core/assert.js";
2
+ import { PosePlayback } from "../../../shade/renderer/animation/pose/PosePlayback.js";
3
+ import { pose_evaluate_world } from "../../../shade/renderer/animation/pose/pose_evaluate_world.js";
4
+ import { MeshSystem } from "../MeshSystem.js";
5
+ import { collect_entity_playbacks } from "./collect_entity_playbacks.js";
6
+
7
+ /**
8
+ * The clip set handed to the evaluator, rebuilt per call from the systems' own entries. Nothing in
9
+ * here outlives the call — the query is on a per-frame path and allocates nothing.
10
+ *
11
+ * @type {PosePlayback[]}
12
+ */
13
+ const PLAYBACKS = [];
14
+
15
+ /**
16
+ * Where a named part of an entity's model is, in world space.
17
+ *
18
+ * This is the question gameplay asks of an animated model and the only sanctioned way to ask it:
19
+ * where does this projectile leave from, where does that effect anchor, where does the floating UI
20
+ * hang. **The answer is computed, never read back** — GPU readback is a frame late and asynchronous,
21
+ * so an outcome that depended on it would depend on frame pacing, and determinism and replay would
22
+ * go with it: no GPU readback on a gameplay-affecting path.
23
+ *
24
+ * The pose is {@link pose_evaluate_world} over the clips the animation systems are driving: a pure
25
+ * function of (clip set, time, weights), off the same clock the renderer is written from, which is
26
+ * what keeps a socket on the mesh that is drawn.
27
+ *
28
+ * The result carries both its matrix and its components.
29
+ *
30
+ * @param {Transform64} result
31
+ * @param {EntityManager} entity_manager
32
+ * @param {number} entity
33
+ * @param {string} node_name name the asset gave the node — a bone, a socket's bone, an effector
34
+ * @returns {boolean} `false` when there is no such node to answer about: no mesh system is
35
+ * registered, the model has not loaded yet, the entity has none, or the name is not in it. None of
36
+ * the four is an error
37
+ */
38
+ export function query_entity_node_world_pose(result, entity_manager, entity, node_name) {
39
+ assert.defined(result, 'result');
40
+ assert.defined(entity_manager, 'entity_manager');
41
+ assert.isNonNegativeInteger(entity, 'entity');
42
+ assert.isString(node_name, 'node_name');
43
+
44
+ const meshes = entity_manager.getSystem(MeshSystem);
45
+
46
+ if (meshes === null) {
47
+ // nothing draws models here, so nothing holds one to answer about
48
+ return false;
49
+ }
50
+
51
+ const instance = meshes.instance_of(entity);
52
+
53
+ if (instance === null) {
54
+ return false;
55
+ }
56
+
57
+ const node = instance.nodes.get(node_name);
58
+
59
+ if (node === undefined) {
60
+ return false;
61
+ }
62
+
63
+ const playbacks = PLAYBACKS;
64
+
65
+ playbacks.length = 0;
66
+
67
+ collect_entity_playbacks(playbacks, entity_manager, entity);
68
+
69
+ pose_evaluate_world(result, node, playbacks);
70
+
71
+ playbacks.length = 0;
72
+
73
+ return true;
74
+ }