@woosh/meep-engine 3.25.0 → 3.27.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 (295) hide show
  1. package/package.json +1 -1
  2. package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +542 -0
  3. package/src/core/binary/lz4/LZ4_LEVEL.d.ts +33 -0
  4. package/src/core/binary/lz4/LZ4_LEVEL.d.ts.map +1 -0
  5. package/src/core/binary/lz4/LZ4_LEVEL.js +32 -0
  6. package/src/core/binary/lz4/lz4_compress_block_at_level.d.ts +33 -0
  7. package/src/core/binary/lz4/lz4_compress_block_at_level.d.ts.map +1 -0
  8. package/src/core/binary/lz4/lz4_compress_block_at_level.js +47 -0
  9. package/src/core/binary/lz4/lz4_compress_block_hc.d.ts +40 -0
  10. package/src/core/binary/lz4/lz4_compress_block_hc.d.ts.map +1 -0
  11. package/src/core/binary/lz4/lz4_compress_block_hc.js +1382 -0
  12. package/src/core/binary/lz4/lz4_decompress_block.d.ts +2 -1
  13. package/src/core/binary/lz4/lz4_decompress_block.d.ts.map +1 -1
  14. package/src/core/binary/lz4/lz4_decompress_block.js +31 -6
  15. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +66 -0
  16. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
  17. package/src/core/geom/3d/shape/HeightMapShape3D.js +314 -20
  18. package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
  19. package/src/core/geom/3d/shape/PosedShape3D.js +57 -0
  20. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  21. package/src/core/geom/3d/shape/TransformedShape3D.js +102 -0
  22. package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
  23. package/src/core/geom/3d/shape/UnionShape3D.js +64 -0
  24. package/src/engine/physics/cloth/MEASUREMENTS.md +582 -0
  25. package/src/engine/physics/cloth/PLAN.md +330 -49
  26. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts +84 -17
  27. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts.map +1 -1
  28. package/src/engine/physics/cloth/collider/ClothColliderIndex.js +451 -42
  29. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +65 -7
  30. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
  31. package/src/engine/physics/cloth/ecs/ClothInstance.js +72 -8
  32. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +25 -4
  33. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
  34. package/src/engine/physics/cloth/ecs/ClothSystem.js +75 -8
  35. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts +41 -4
  36. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
  37. package/src/engine/physics/cloth/ecs/ClothWorld.js +207 -63
  38. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts +10 -0
  39. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts.map +1 -1
  40. package/src/engine/physics/cloth/ecs/WorkerClothSystem.js +17 -1
  41. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts +27 -3
  42. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts.map +1 -1
  43. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.js +28 -3
  44. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +75 -10
  45. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -1
  46. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +131 -12
  47. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts +19 -0
  48. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts.map +1 -1
  49. package/src/engine/physics/cloth/ecs/cloth_seed_rig.js +249 -201
  50. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts +21 -0
  51. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts.map +1 -1
  52. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.js +62 -1
  53. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts.map +1 -1
  54. package/src/engine/physics/cloth/ecs/cloth_write_back.js +18 -10
  55. package/src/engine/physics/cloth/playground/collide_main.js +1 -1
  56. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts +29 -8
  57. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts.map +1 -1
  58. package/src/engine/physics/cloth/solver/cloth_contact_find.js +37 -9
  59. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +12 -2
  60. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
  61. package/src/engine/physics/cloth/solver/cloth_solver_constants.js +12 -2
  62. package/src/engine/physics/cloth/solver/cloth_step_instance.js +1 -1
  63. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts +9 -2
  64. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts.map +1 -1
  65. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.js +9 -2
  66. package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts +23 -9
  67. package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts.map +1 -1
  68. package/src/engine/physics/cloth/wind/AbstractClothWind.js +23 -9
  69. package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts +7 -5
  70. package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts.map +1 -1
  71. package/src/engine/physics/cloth/wind/ClothAmbientWind.js +12 -9
  72. package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts.map +1 -1
  73. package/src/engine/physics/cloth/wind/ClothFluidWind.js +5 -2
  74. package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts +7 -2
  75. package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts.map +1 -1
  76. package/src/engine/physics/cloth/wind/cloth_sample_air.js +12 -5
  77. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  78. package/src/shade/playground/vgeo_runtime/README.md +167 -22
  79. package/src/shade/playground/vgeo_runtime/index.html +112 -62
  80. package/src/shade/playground/vgeo_runtime/main.js +660 -83
  81. package/src/shade/playground/vgeo_viewer/sample_asset.d.ts.map +1 -1
  82. package/src/shade/playground/vgeo_viewer/sample_asset.js +19 -2
  83. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts +11 -0
  84. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
  85. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +16 -8
  86. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
  87. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +16 -11
  88. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +73 -5
  89. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +26 -15
  90. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +5 -3
  91. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +18 -0
  92. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
  93. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +20 -0
  94. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +1 -1
  95. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.js +56 -9
  96. package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts.map +1 -1
  97. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +1 -0
  98. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +22 -0
  99. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
  100. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +24 -0
  101. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts +12 -0
  102. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts.map +1 -1
  103. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +13 -0
  104. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts +8 -1
  105. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts.map +1 -1
  106. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +11 -3
  107. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts +9 -0
  108. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts.map +1 -1
  109. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +10 -1
  110. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.d.ts +18 -0
  111. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.d.ts.map +1 -0
  112. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.js +17 -0
  113. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.d.ts +8 -0
  114. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.d.ts.map +1 -0
  115. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.js +7 -0
  116. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.d.ts +3 -0
  117. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +16 -0
  118. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.d.ts +14 -0
  119. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.d.ts.map +1 -0
  120. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.js +13 -0
  121. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.d.ts +11 -0
  122. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.d.ts.map +1 -0
  123. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.js +10 -0
  124. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts +20 -0
  125. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts.map +1 -1
  126. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.js +20 -2
  127. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts +4 -0
  128. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts.map +1 -1
  129. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.js +4 -0
  130. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts +29 -3
  131. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts.map +1 -1
  132. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.js +81 -122
  133. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +40 -0
  134. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
  135. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +44 -0
  136. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts +69 -0
  137. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts.map +1 -0
  138. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.js +236 -0
  139. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts +14 -0
  140. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts.map +1 -1
  141. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.js +15 -0
  142. package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.d.ts +31 -0
  143. package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.d.ts.map +1 -0
  144. package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.js +32 -0
  145. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts +10 -0
  146. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts.map +1 -1
  147. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.js +21 -1
  148. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.d.ts +15 -0
  149. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.d.ts.map +1 -0
  150. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.js +84 -0
  151. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts +22 -0
  152. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts.map +1 -0
  153. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.js +96 -0
  154. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
  155. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +55 -0
  156. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
  157. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +202 -13
  158. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
  159. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +147 -10
  160. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +637 -0
  161. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts +33 -36
  162. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts.map +1 -1
  163. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.js +109 -193
  164. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +58 -28
  165. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
  166. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +77 -105
  167. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +37 -0
  168. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +105 -22
  169. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +548 -0
  170. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts +29 -50
  171. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -1
  172. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +47 -489
  173. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts +84 -0
  174. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts.map +1 -0
  175. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.js +393 -0
  176. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts +234 -0
  177. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -0
  178. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +1363 -0
  179. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.d.ts +81 -0
  180. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.d.ts.map +1 -0
  181. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.js +89 -0
  182. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts +53 -34
  183. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts.map +1 -1
  184. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.js +407 -124
  185. package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.d.ts +100 -0
  186. package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.d.ts.map +1 -0
  187. package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.js +219 -0
  188. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.d.ts +17 -0
  189. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.d.ts.map +1 -0
  190. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.js +47 -0
  191. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.d.ts +20 -0
  192. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.d.ts.map +1 -0
  193. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.js +19 -0
  194. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts.map +1 -1
  195. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.js +30 -12
  196. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -1
  197. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +15 -0
  198. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts.map +1 -1
  199. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.js +18 -1
  200. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts.map +1 -1
  201. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.js +5 -15
  202. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.d.ts +3 -0
  203. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.d.ts.map +1 -0
  204. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.js +77 -0
  205. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts +3 -0
  206. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts.map +1 -0
  207. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.js +186 -0
  208. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts.map +1 -1
  209. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js +20 -8
  210. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -1
  211. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +580 -188
  212. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts +15 -18
  213. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts.map +1 -1
  214. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.js +19 -28
  215. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts.map +1 -1
  216. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.js +45 -25
  217. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +1 -0
  218. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +11 -0
  219. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +1 -1
  220. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts +0 -1
  221. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.js +12 -10
  222. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts +1 -0
  223. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.js +8 -0
  224. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts +7 -2
  225. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts.map +1 -1
  226. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.js +8 -3
  227. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts +2 -0
  228. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.js +19 -0
  229. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts.map +1 -1
  230. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.js +1 -1
  231. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.d.ts +5 -0
  232. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.d.ts.map +1 -0
  233. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.js +22 -0
  234. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.d.ts +14 -0
  235. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.d.ts.map +1 -0
  236. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.js +13 -0
  237. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.d.ts +17 -0
  238. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.d.ts.map +1 -0
  239. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.js +16 -0
  240. package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.d.ts +15 -0
  241. package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.d.ts.map +1 -0
  242. package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.js +16 -0
  243. package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.d.ts +23 -0
  244. package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.d.ts.map +1 -0
  245. package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.js +28 -0
  246. package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.d.ts +2 -0
  247. package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.d.ts.map +1 -0
  248. package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.js +13 -0
  249. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts +10 -5
  250. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts.map +1 -1
  251. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.js +15 -6
  252. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts +6 -1
  253. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -1
  254. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +38 -1
  255. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.d.ts.map +1 -1
  256. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.js +4 -3
  257. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  258. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  259. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +6 -3
  260. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  261. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +6 -3
  262. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  263. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  264. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +12 -4
  265. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts +2 -0
  266. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
  267. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +58 -57
  268. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.d.ts.map +1 -1
  269. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.js +3 -5
  270. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.d.ts.map +1 -1
  271. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.js +3 -4
  272. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
  273. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +3 -4
  274. package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.d.ts.map +1 -1
  275. package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.js +7 -7
  276. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts.map +1 -1
  277. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.js +83 -78
  278. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  279. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +7 -1
  280. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  281. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +7 -1
  282. package/src/shade/renderer/scene/GPUSceneContext.d.ts +22 -2
  283. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  284. package/src/shade/renderer/scene/GPUSceneContext.js +59 -4
  285. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
  286. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +92 -16
  287. package/src/shade/renderer/view/GPUViewContext.d.ts +5 -0
  288. package/src/shade/renderer/view/GPUViewContext.d.ts.map +1 -1
  289. package/src/shade/renderer/view/GPUViewContext.js +21 -0
  290. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  291. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +74 -3
  292. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.js +0 -723
  293. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.js +0 -50
  294. package/src/shade/renderer/shader/chunk/jobs/NOTES.md +0 -47
  295. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.js +0 -102
@@ -18,7 +18,7 @@
18
18
  * is on screen, which is the fallback-chain bug `VTPhysicalCache` already keys on touch to avoid.
19
19
  *
20
20
  * **Two budgets, and both of them bind.** Slots are fixed and finite, so a full table is as real a
21
- * reason to evict as a full byte budget; `#make_room` loops on whichever of the two is short.
21
+ * reason to evict as a full byte budget; `VGeoResidency` makes room on whichever of the two is short.
22
22
  *
23
23
  * @author Alex Goldring
24
24
  * @copyright Company Named Limited (c) 2026
@@ -29,19 +29,13 @@ export class GPUVirtualGeometryPool {
29
29
  * @param {GPUDevice} options.device
30
30
  * @param {GPUMeshletManager} options.meshlets the pool VG shares with static geometry, so a VG
31
31
  * draw record is read downstream by the same shader that reads a static one
32
- * @param {number} [options.budget_bytes] decoded page bytes this tier may hold; defaults to
33
- * what its slots hold at the page size it was built for, so the two agree by construction
34
- * @param {number} [options.protect_window] frames a touched page is safe for; no shorter than
35
- * feedback latency, which is at least one frame of `mapAsync` and in practice several
36
32
  * @param {Object} [options.table_options] passed to {@link VGeoRuntimeTables}
37
33
  * @param {number} [options.page_size] the page size every container registered here must have
38
34
  * been built at, and the one this tier's slots are sized from
39
35
  */
40
- constructor({ device, meshlets, budget_bytes, protect_window, table_options, page_size, }: {
36
+ constructor({ device, meshlets, table_options, page_size, }: {
41
37
  device: GPUDevice;
42
38
  meshlets: GPUMeshletManager;
43
- budget_bytes?: number;
44
- protect_window?: number;
45
39
  table_options?: any;
46
40
  page_size?: number;
47
41
  });
@@ -58,6 +52,11 @@ export class GPUVirtualGeometryPool {
58
52
  * @returns {GPUBuffer}
59
53
  */
60
54
  get buffer_geometries(): GPUBuffer;
55
+ /**
56
+ * The persistent, frame-tagged want stamps, one per lookup word.
57
+ * @returns {GPUBuffer}
58
+ */
59
+ get buffer_want_stamps(): GPUBuffer;
61
60
  /**
62
61
  * @returns {VGeoRuntimeTables}
63
62
  */
@@ -66,10 +65,6 @@ export class GPUVirtualGeometryPool {
66
65
  * @returns {number}
67
66
  */
68
67
  get resident_bytes(): number;
69
- /**
70
- * @returns {number}
71
- */
72
- get budget_bytes(): number;
73
68
  /**
74
69
  * The page size every container registered here is required to have been built at.
75
70
  * @returns {number}
@@ -88,14 +83,28 @@ export class GPUVirtualGeometryPool {
88
83
  */
89
84
  get evicted_page_count(): number;
90
85
  /**
91
- * Installs that could not be made — no slot could be freed, or the page did not fit one.
86
+ * The decoded bytes a resident row was charged for, out of its own slot.
87
+ *
88
+ * @param {number} page_row
92
89
  * @returns {number}
93
90
  */
94
- get refused_install_count(): number;
91
+ bytes_of(page_row: number): number;
92
+ /**
93
+ * @param {number} page_row
94
+ * @returns {number} the container page index a resident row holds
95
+ */
96
+ page_index_of(page_row: number): number;
95
97
  /**
96
98
  * @returns {number}
97
99
  */
98
100
  get frame(): number;
101
+ /**
102
+ * The frame whose flush publishes this row's current occupant, or `0xFFFFFFFF` for none.
103
+ *
104
+ * @param {number} page_row
105
+ * @returns {number}
106
+ */
107
+ visible_from(page_row: number): number;
99
108
  /**
100
109
  * One past the highest page row handed out — what sizes the feedback buffer's touch stamps.
101
110
  * @returns {number}
@@ -106,11 +115,13 @@ export class GPUVirtualGeometryPool {
106
115
  */
107
116
  get lookup_word_count(): number;
108
117
  /**
109
- * Give a container a geometry row, and make its root page resident and pinned.
118
+ * Give a container a geometry row and a block of the lookup, with every page absent.
110
119
  *
111
- * The root is not an optimization: every traversal is seeded with it, invariant §11.7 puts
112
- * exactly one root cluster in the file, and a measured root page is one cluster of under two
113
- * kilobytes. A geometry whose root is not here draws nothing.
120
+ * Nothing resident, not even the root: which pages are here is `VGeoResidency`'s decision and it
121
+ * installs the root through the same door as every other page, pinned in its own tables. A
122
+ * geometry whose root is not here draws nothing — every traversal is seeded with it, invariant
123
+ * §11.7 puts exactly one root cluster in the file — and the residency throws on a registration
124
+ * that cannot make it resident, rolling this back.
114
125
  *
115
126
  * This is also the door the page-size guarantee is checked at, because it is the last moment a
116
127
  * refusal is still cheap: past it, this container's pages are laid into the tier's storage on
@@ -188,28 +199,12 @@ export class GPUVirtualGeometryPool {
188
199
  row: number;
189
200
  } | undefined;
190
201
  /**
191
- * A page the cut reported drawing from, this frame.
202
+ * The runtime calls this at `begin_frame`, before the flush; see {@link visible_from} for why
203
+ * the order matters.
192
204
  *
193
- * @param {number} page_row
194
- * @returns {void}
195
- */
196
- touch(page_row: number): void;
197
- /**
198
205
  * @returns {void}
199
206
  */
200
207
  advance_frame(): void;
201
- /**
202
- * Whether a page may go: not pinned, not touched inside the protect window, and with no VRAM
203
- * child.
204
- *
205
- * The child rule is this tier's own, out of this tier's tables. Dropping a parent while its
206
- * children stay leaves those children unreachable — nothing refines into them any more — so
207
- * they would sit in VRAM, selectable, and never drawn.
208
- *
209
- * @param {number} page_row
210
- * @returns {boolean}
211
- */
212
- evictable(page_row: number): boolean;
213
208
  /**
214
209
  * Push everything that changed since the last call to the device.
215
210
  *
@@ -224,5 +219,7 @@ export class GPUVirtualGeometryPool {
224
219
  destroy(): void;
225
220
  #private;
226
221
  }
222
+ export type GPUMeshletManager = import("../../meshlet/GPUMeshletManager.js").GPUMeshletManager;
223
+ export type VGeoContainerReader = import("../format/read/VGeoContainerReader.js").VGeoContainerReader;
227
224
  import { VGeoRuntimeTables } from "./VGeoRuntimeTables.js";
228
225
  //# sourceMappingURL=GPUVirtualGeometryPool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GPUVirtualGeometryPool.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.js"],"names":[],"mappings":"AA8CA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH;IA4FI;;;;;;;;;;;;OAYG;IACH;QAX8B,MAAM;QACE,QAAQ;QAElB,YAAY,GAA7B,MAAM;QAEW,cAAc,GAA/B,MAAM;QAEW,aAAa;QACb,SAAS,GAA1B,MAAM;OAsChB;IAkCD;;OAEG;IACH,+BAEC;IAED;;;OAGG;IACH,8BAEC;IAED;;OAEG;IACH,mCAEC;IAED;;OAEG;IACH,gCAEC;IAED;;OAEG;IACH,6BAEC;IAED;;OAEG;IACH,2BAEC;IAED;;;OAGG;IACH,wBAEC;IAED;;OAEG;IACH,8BAEC;IAED;;OAEG;IACH,kCAEC;IAED;;OAEG;IACH,iCAEC;IAED;;;OAGG;IACH,oCAEC;IAED;;OAEG;IACH,oBAEC;IAED;;;OAGG;IACH,gCAEC;IAED;;OAEG;IACH,gCAEC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,uCAJa,MAAM,CAkDlB;IAED;;;;;;;;;OASG;IACH,yCAFa,OAAO,CAuCnB;IAqCD;;;OAGG;IACH,8CAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,qDAHW,MAAM,GACJ,OAAO,CAInB;IAED;;;;;;;OAOG;IACH,yDAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;OAMG;IACH,iDAHW,MAAM,GACJ,MAAM,CAsElB;IAED;;;OAGG;IACH,gBAHW,MAAM,GACJ,OAAO,CA8CnB;IAED;;;;;;;;;;;;;;OAcG;IACH,mBAHW,MAAM,GACJ;QAAC,MAAM,sBAAsB;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,GAAC,SAAS,CAIhE;IAED;;;;;OAKG;IACH,gBAHW,MAAM,GACJ,IAAI,CAMhB;IAED;;OAEG;IACH,iBAFa,IAAI,CAIhB;IAED;;;;;;;;;;OAUG;IACH,oBAHW,MAAM,GACJ,OAAO,CAgBnB;IAkID;;;;;;;;;OASG;IACH,SAFa,IAAI,CAMhB;IAwDD,gBAcC;;CACJ;kCA55BiC,wBAAwB"}
1
+ {"version":3,"file":"GPUVirtualGeometryPool.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.js"],"names":[],"mappings":"AAmDA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH;IAoFI;;;;;;;;OAQG;IACH;QAP8B,MAAM;QACE,QAAQ,EAAnC,iBAAiB;QAEA,aAAa;QACb,SAAS,GAA1B,MAAM;OAyChB;IAkCD;;OAEG;IACH,+BAEC;IAED;;;OAGG;IACH,8BAEC;IAED;;OAEG;IACH,mCAEC;IAED;;;OAGG;IACH,oCAEC;IAED;;OAEG;IACH,gCAEC;IAED;;OAEG;IACH,6BAEC;IAGD;;;OAGG;IACH,wBAEC;IAED;;OAEG;IACH,8BAEC;IAED;;OAEG;IACH,kCAEC;IAED;;OAEG;IACH,iCAEC;IAED;;;;;OAKG;IACH,mBAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;OAGG;IACH,wBAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;OAEG;IACH,oBAEC;IAED;;;;;OAKG;IACH,uBAHW,MAAM,GACJ,MAAM,CAQlB;IAED;;;OAGG;IACH,gCAEC;IAED;;OAEG;IACH,gCAEC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,iBALW,mBAAmB,GACjB,MAAM,CAmClB;IAED;;;;;;;;;OASG;IACH,mBAHW,mBAAmB,GACjB,OAAO,CA+CnB;IAqCD;;;OAGG;IACH,wBAHW,mBAAmB,GACjB,MAAM,CAIlB;IAED;;;;OAIG;IACH,oBAJW,mBAAmB,cACnB,MAAM,GACJ,OAAO,CAInB;IAED;;;;;;;OAOG;IACH,wBAJW,mBAAmB,cACnB,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;OAMG;IACH,gBAJW,mBAAmB,cACnB,MAAM,GACJ,MAAM,CAgElB;IAED;;;OAGG;IACH,gBAHW,MAAM,GACJ,OAAO,CA0CnB;IAED;;;;;;;;;;;;;;OAcG;IACH,mBAHW,MAAM,GACJ;QAAC,MAAM,EAAE,mBAAmB,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,GAAC,SAAS,CAIhE;IAED;;;;;OAKG;IACH,iBAFa,IAAI,CAIhB;IA2ED;;;;;;;;;OASG;IACH,SAFa,IAAI,CAMhB;IAwDD,gBAcC;;CACJ;gCAp0BY,OAAO,oCAAoC,EAAE,iBAAiB;kCAC9D,OAAO,uCAAuC,EAAE,mBAAmB;kCAL9C,wBAAwB"}
@@ -8,6 +8,11 @@ import { VGEO_PAGE_ROW_WORDS } from "./layout/VGEO_PAGE_ROW_WORDS.js";
8
8
  import { VGeoRuntimeTables } from "./VGeoRuntimeTables.js";
9
9
  import { vgeo_runtime_meshlet_batch } from "./vgeo_runtime_meshlet_batch.js";
10
10
 
11
+ /**
12
+ * @typedef {import("../../meshlet/GPUMeshletManager.js").GPUMeshletManager} GPUMeshletManager
13
+ * @typedef {import("../format/read/VGeoContainerReader.js").VGeoContainerReader} VGeoContainerReader
14
+ */
15
+
11
16
  /**
12
17
  * One resident geometry's bookkeeping above the tables.
13
18
  */
@@ -64,7 +69,7 @@ class VGeoPoolGeometry {
64
69
  * is on screen, which is the fallback-chain bug `VTPhysicalCache` already keys on touch to avoid.
65
70
  *
66
71
  * **Two budgets, and both of them bind.** Slots are fixed and finite, so a full table is as real a
67
- * reason to evict as a full byte budget; `#make_room` loops on whichever of the two is short.
72
+ * reason to evict as a full byte budget; `VGeoResidency` makes room on whichever of the two is short.
68
73
  *
69
74
  * @author Alex Goldring
70
75
  * @copyright Company Named Limited (c) 2026
@@ -96,18 +101,6 @@ export class GPUVirtualGeometryPool {
96
101
  */
97
102
  #owner = new Map();
98
103
 
99
- /**
100
- * Page row to the frame it was last selected from.
101
- * @type {Map<number, number>}
102
- */
103
- #touched = new Map();
104
-
105
- /**
106
- * Page rows that may never go: the root page of every registered geometry, which is where every
107
- * traversal starts and without which a geometry draws nothing at all.
108
- * @type {Set<number>}
109
- */
110
- #pinned = new Set();
111
104
 
112
105
  /**
113
106
  * Per page row, the unsubscribe for that page's meshlet batch relocation signal.
@@ -116,24 +109,32 @@ export class GPUVirtualGeometryPool {
116
109
  #batch_listeners = new Map();
117
110
 
118
111
  /**
112
+ * Advanced by the runtime at `begin_frame`, before the flush — so it is the number of the
113
+ * flush that publishes whatever is installed before it, and the flush after that publishes
114
+ * what lands later in the frame.
119
115
  * @type {number}
120
116
  */
121
117
  #frame = 0;
122
118
 
123
119
  /**
124
- * @type {number}
120
+ * Per page row, the frame whose flush publishes the row's current occupant, or
121
+ * `0xFFFFFFFF` for a row with none.
122
+ *
123
+ * What a reading compares against, per record, instead of a snapshot of every row's generation
124
+ * per readback: a record from the cut of frame F naming row r is about r's occupant iff
125
+ * `visible_from[r] <= F`. An install stamps `frame + 1` — this frame's flush has already run
126
+ * — so a row refilled after the cut, in the same frame or later, is rejected by that cut's
127
+ * reading, and a freed row is rejected by every reading (RUNTIME_PLAN_2026_09_14 §2.4).
128
+ *
129
+ * @type {Uint32Array}
125
130
  */
126
- #resident_bytes = 0;
131
+ #visible_from;
127
132
 
128
133
  /**
129
134
  * @type {number}
130
135
  */
131
- #budget_bytes;
136
+ #resident_bytes = 0;
132
137
 
133
- /**
134
- * @type {number}
135
- */
136
- #protect_window;
137
138
 
138
139
  /**
139
140
  * @type {number}
@@ -156,20 +157,12 @@ export class GPUVirtualGeometryPool {
156
157
  */
157
158
  #evicted_page_count = 0;
158
159
 
159
- /**
160
- * @type {number}
161
- */
162
- #refused_install_count = 0;
163
160
 
164
161
  /**
165
162
  * @param {Object} options
166
163
  * @param {GPUDevice} options.device
167
164
  * @param {GPUMeshletManager} options.meshlets the pool VG shares with static geometry, so a VG
168
165
  * draw record is read downstream by the same shader that reads a static one
169
- * @param {number} [options.budget_bytes] decoded page bytes this tier may hold; defaults to
170
- * what its slots hold at the page size it was built for, so the two agree by construction
171
- * @param {number} [options.protect_window] frames a touched page is safe for; no shorter than
172
- * feedback latency, which is at least one frame of `mapAsync` and in practice several
173
166
  * @param {Object} [options.table_options] passed to {@link VGeoRuntimeTables}
174
167
  * @param {number} [options.page_size] the page size every container registered here must have
175
168
  * been built at, and the one this tier's slots are sized from
@@ -177,33 +170,36 @@ export class GPUVirtualGeometryPool {
177
170
  constructor({
178
171
  device,
179
172
  meshlets,
180
- budget_bytes = undefined,
181
- protect_window = 4,
182
173
  table_options = undefined,
183
174
  page_size = VGEO_DEFAULT_PAGE_SIZE,
184
175
  }) {
185
176
  assert.defined(device, 'device');
186
177
  assert.defined(meshlets, 'meshlets');
187
- assert.isNonNegativeInteger(protect_window, 'protect_window');
188
178
  assert.isPositiveInteger(page_size, 'page_size');
189
179
 
190
180
  this.#device = device;
191
181
  this.#meshlets = meshlets;
192
- this.#protect_window = protect_window;
193
182
  this.#page_size = page_size;
194
183
 
195
184
  this.#tables = new VGeoRuntimeTables({ page_size, ...table_options });
196
185
 
197
- this.#budget_bytes = budget_bytes ?? this.#tables.page_capacity * page_size;
198
-
199
- assert.isNonNegativeInteger(this.#budget_bytes, 'budget_bytes');
200
186
 
201
187
  this.#buffers = {
202
188
  lookup: this.#create('lookup', this.#tables.lookup_words.byteLength),
203
189
  arena: this.#create('arena', this.#tables.arena_words.byteLength),
204
190
  geometries: this.#create('geometries', this.#tables.geometry_words.byteLength),
191
+ /*
192
+ One want stamp per lookup word, persistent. The cut dedups a want by exchanging the
193
+ frame's tag into it, so this is never cleared and never read back — where it used to
194
+ be a region of the per-frame feedback transient, cleared and mapped every frame at the
195
+ size of the lookup, which is one word per page of every registered geometry
196
+ (RUNTIME_PLAN_2026_09_14 §2.3).
197
+ */
198
+ want_stamps: this.#create('want stamps', this.#tables.lookup_words.byteLength),
205
199
  };
206
200
 
201
+ this.#visible_from = new Uint32Array(this.#tables.page_capacity).fill(0xFFFFFFFF);
202
+
207
203
  // everything starts absent, and "absent" is a sentinel rather than a zero — the lookup has
208
204
  // to reach the device before any cut reads it
209
205
  this.#dirty.lookup = [0, this.#tables.lookup_words.length];
@@ -265,6 +261,14 @@ export class GPUVirtualGeometryPool {
265
261
  return this.#buffers.geometries;
266
262
  }
267
263
 
264
+ /**
265
+ * The persistent, frame-tagged want stamps, one per lookup word.
266
+ * @returns {GPUBuffer}
267
+ */
268
+ get buffer_want_stamps() {
269
+ return this.#buffers.want_stamps;
270
+ }
271
+
268
272
  /**
269
273
  * @returns {VGeoRuntimeTables}
270
274
  */
@@ -279,12 +283,6 @@ export class GPUVirtualGeometryPool {
279
283
  return this.#resident_bytes;
280
284
  }
281
285
 
282
- /**
283
- * @returns {number}
284
- */
285
- get budget_bytes() {
286
- return this.#budget_bytes;
287
- }
288
286
 
289
287
  /**
290
288
  * The page size every container registered here is required to have been built at.
@@ -316,11 +314,21 @@ export class GPUVirtualGeometryPool {
316
314
  }
317
315
 
318
316
  /**
319
- * Installs that could not be made — no slot could be freed, or the page did not fit one.
317
+ * The decoded bytes a resident row was charged for, out of its own slot.
318
+ *
319
+ * @param {number} page_row
320
320
  * @returns {number}
321
321
  */
322
- get refused_install_count() {
323
- return this.#refused_install_count;
322
+ bytes_of(page_row) {
323
+ return this.#decoded_bytes_of(page_row);
324
+ }
325
+
326
+ /**
327
+ * @param {number} page_row
328
+ * @returns {number} the container page index a resident row holds
329
+ */
330
+ page_index_of(page_row) {
331
+ return this.#tables.page_field(page_row, VGEO_PAGE_ROW_OFFSET.PAGE_INDEX);
324
332
  }
325
333
 
326
334
  /**
@@ -330,6 +338,20 @@ export class GPUVirtualGeometryPool {
330
338
  return this.#frame;
331
339
  }
332
340
 
341
+ /**
342
+ * The frame whose flush publishes this row's current occupant, or `0xFFFFFFFF` for none.
343
+ *
344
+ * @param {number} page_row
345
+ * @returns {number}
346
+ */
347
+ visible_from(page_row) {
348
+ if (page_row >= this.#visible_from.length) {
349
+ return 0xFFFFFFFF;
350
+ }
351
+
352
+ return this.#visible_from[page_row];
353
+ }
354
+
333
355
  /**
334
356
  * One past the highest page row handed out — what sizes the feedback buffer's touch stamps.
335
357
  * @returns {number}
@@ -346,11 +368,13 @@ export class GPUVirtualGeometryPool {
346
368
  }
347
369
 
348
370
  /**
349
- * Give a container a geometry row, and make its root page resident and pinned.
371
+ * Give a container a geometry row and a block of the lookup, with every page absent.
350
372
  *
351
- * The root is not an optimization: every traversal is seeded with it, invariant §11.7 puts
352
- * exactly one root cluster in the file, and a measured root page is one cluster of under two
353
- * kilobytes. A geometry whose root is not here draws nothing.
373
+ * Nothing resident, not even the root: which pages are here is `VGeoResidency`'s decision and it
374
+ * installs the root through the same door as every other page, pinned in its own tables. A
375
+ * geometry whose root is not here draws nothing — every traversal is seeded with it, invariant
376
+ * §11.7 puts exactly one root cluster in the file — and the residency throws on a registration
377
+ * that cannot make it resident, rolling this back.
354
378
  *
355
379
  * This is also the door the page-size guarantee is checked at, because it is the last moment a
356
380
  * refusal is still cheap: past it, this container's pages are laid into the tier's storage on
@@ -381,32 +405,17 @@ export class GPUVirtualGeometryPool {
381
405
 
382
406
  this.#mark('geometries', row * VGEO_GEOMETRY_ROW_WORDS, VGEO_GEOMETRY_ROW_WORDS);
383
407
 
384
- const page_row = this.install(reader, 0);
385
-
386
- if (page_row === -1) {
387
- /*
388
- A thrown error rather than a stripped assert. `assert.*` is removed from the shipped
389
- build by the consumer, so the check that used to stand here was a development-only
390
- one — and what it was guarding is the seed kernel reading VGEO_PAGE_ROW_NONE out of the
391
- geometry row as a u32 index and multiplying it into a live stat word (REVIEW_LEDGER
392
- C7). The geometry is rolled back rather than left half-registered.
393
- */
394
- this.#geometries.delete(reader);
395
-
396
- this.#tables.unregister_geometry(row);
397
-
398
- this.#mark('geometries', row * VGEO_GEOMETRY_ROW_WORDS, VGEO_GEOMETRY_ROW_WORDS);
399
-
400
- throw new Error(
401
- `virtual geometry: the root page of this container could not be made resident, so`
402
- + ` it would draw nothing and every cut seeded from it would read an absent row.`
403
- + ` The tier holds ${this.#tables.page_capacity} slots and`
404
- + ` ${this.#tables.resident_page_count} are taken, of a ${this.#budget_bytes}-byte`
405
- + ` budget with ${this.#resident_bytes} spent.`
406
- );
407
- }
408
-
409
- this.#pinned.add(page_row);
408
+ /*
409
+ The block of NONE the tables just wrote has to reach the device too. The first flush
410
+ uploads the lookup whole, so the first container's block rode along; a container
411
+ registered after that flush was leaving zeros behind, and zero is row 0 — every one of
412
+ its pages resolved to another geometry's root and the cut never wanted anything.
413
+ */
414
+ this.#mark(
415
+ 'lookup',
416
+ this.#tables.geometry_field(row, VGEO_GEOMETRY_ROW_OFFSET.LOOKUP_WORD_OFFSET),
417
+ reader.header.total_pages
418
+ );
410
419
 
411
420
  return row;
412
421
  }
@@ -428,8 +437,15 @@ export class GPUVirtualGeometryPool {
428
437
  return false;
429
438
  }
430
439
 
440
+ // read before the tables forget the row
441
+ const lookup_offset = this.#tables.geometry_field(
442
+ geometry.row, VGEO_GEOMETRY_ROW_OFFSET.LOOKUP_WORD_OFFSET
443
+ );
444
+ const page_count = this.#tables.geometry_field(
445
+ geometry.row, VGEO_GEOMETRY_ROW_OFFSET.PAGE_COUNT
446
+ );
447
+
431
448
  for (const page_row of geometry.rows.values()) {
432
- this.#pinned.delete(page_row);
433
449
  this.#unwatch_batch(page_row);
434
450
 
435
451
  // before the eviction, which zeroes the slot header this number lives in
@@ -445,14 +461,15 @@ export class GPUVirtualGeometryPool {
445
461
 
446
462
  for (const page_row of this.#tables.unregister_geometry(geometry.row)) {
447
463
  this.#owner.delete(page_row);
448
- this.#touched.delete(page_row);
464
+ this.#visible_from[page_row] = 0xFFFFFFFF;
449
465
 
450
466
  this.#mark('arena', page_row * this.#tables.slot_words, VGEO_PAGE_ROW_WORDS);
451
467
  }
452
468
 
453
469
  this.#geometries.delete(reader);
454
470
 
455
- this.#mark('lookup', 0, this.#tables.lookup_words.length);
471
+ // this geometry's words and not the whole lookup, which is every registered geometry's
472
+ this.#mark('lookup', lookup_offset, page_count);
456
473
  this.#mark(
457
474
  'geometries', geometry.row * VGEO_GEOMETRY_ROW_WORDS, VGEO_GEOMETRY_ROW_WORDS
458
475
  );
@@ -551,14 +568,11 @@ export class GPUVirtualGeometryPool {
551
568
 
552
569
  if (this.#tables.slot_words_for(page) > this.#tables.slot_words) {
553
570
  // nothing to evict for: no slot in this tier is ever bigger than another
554
- this.#refused_install_count++;
555
-
556
571
  return -1;
557
572
  }
558
573
 
559
- if (!this.#make_room(page.decoded_size)) {
560
- this.#refused_install_count++;
561
-
574
+ if (!this.#tables.has_free_page_row) {
575
+ // room is the residency's business; a full table here is a refusal, not a cue
562
576
  return -1;
563
577
  }
564
578
 
@@ -571,8 +585,6 @@ export class GPUVirtualGeometryPool {
571
585
  } catch (error) {
572
586
  this.#meshlets.remove_batch(batch);
573
587
 
574
- this.#refused_install_count++;
575
-
576
588
  return -1;
577
589
  }
578
590
 
@@ -580,7 +592,8 @@ export class GPUVirtualGeometryPool {
580
592
  geometry.rows.set(page_index, page_row);
581
593
 
582
594
  this.#owner.set(page_row, geometry);
583
- this.#touched.set(page_row, this.#frame);
595
+ // this frame's flush has run; the next one is what publishes this row
596
+ this.#visible_from[page_row] = this.#frame + 1;
584
597
  this.#watch_batch(page_row, batch);
585
598
 
586
599
  this.#resident_bytes += page.decoded_size;
@@ -591,12 +604,10 @@ export class GPUVirtualGeometryPool {
591
604
  ) + page_index, 1);
592
605
  this.#mark('geometries', geometry.row * VGEO_GEOMETRY_ROW_WORDS, VGEO_GEOMETRY_ROW_WORDS);
593
606
 
594
- /*
595
- Activation reaches further than the page that arrived: a group becomes selectable when the
596
- last of its parents' pages installs, and those parents can be anywhere already resident. So
597
- the whole geometry's answer is recomputed rather than just this page's.
598
- */
599
- this.#refresh_selectable(geometry);
607
+ // activation reaches further than the page that arrived — a group becomes selectable when
608
+ // the last of its parents' pages installs, and the tables cascade that — so the rows whose
609
+ // flags flipped are whatever they say, not this page's alone
610
+ this.#mark_activated();
600
611
 
601
612
  return page_row;
602
613
  }
@@ -606,10 +617,6 @@ export class GPUVirtualGeometryPool {
606
617
  * @returns {boolean}
607
618
  */
608
619
  evict(page_row) {
609
- if (this.#pinned.has(page_row)) {
610
- return false;
611
- }
612
-
613
620
  const geometry = this.#owner.get(page_row);
614
621
 
615
622
  if (geometry === undefined) {
@@ -639,14 +646,14 @@ export class GPUVirtualGeometryPool {
639
646
  geometry.rows.delete(page_index);
640
647
 
641
648
  this.#owner.delete(page_row);
642
- this.#touched.delete(page_row);
649
+ this.#visible_from[page_row] = 0xFFFFFFFF;
643
650
 
644
651
  this.#evicted_page_count++;
645
652
 
646
653
  this.#mark_slot(page_row);
647
654
  this.#mark('lookup', lookup_word, 1);
648
655
 
649
- this.#refresh_selectable(geometry);
656
+ this.#mark_activated();
650
657
 
651
658
  return true;
652
659
  }
@@ -671,106 +678,15 @@ export class GPUVirtualGeometryPool {
671
678
  }
672
679
 
673
680
  /**
674
- * A page the cut reported drawing from, this frame.
681
+ * The runtime calls this at `begin_frame`, before the flush; see {@link visible_from} for why
682
+ * the order matters.
675
683
  *
676
- * @param {number} page_row
677
- * @returns {void}
678
- */
679
- touch(page_row) {
680
- if (this.#owner.has(page_row)) {
681
- this.#touched.set(page_row, this.#frame);
682
- }
683
- }
684
-
685
- /**
686
684
  * @returns {void}
687
685
  */
688
686
  advance_frame() {
689
687
  this.#frame++;
690
688
  }
691
689
 
692
- /**
693
- * Whether a page may go: not pinned, not touched inside the protect window, and with no VRAM
694
- * child.
695
- *
696
- * The child rule is this tier's own, out of this tier's tables. Dropping a parent while its
697
- * children stay leaves those children unreachable — nothing refines into them any more — so
698
- * they would sit in VRAM, selectable, and never drawn.
699
- *
700
- * @param {number} page_row
701
- * @returns {boolean}
702
- */
703
- evictable(page_row) {
704
- if (this.#pinned.has(page_row)) {
705
- return false;
706
- }
707
-
708
- if (!this.#owner.has(page_row)) {
709
- return false;
710
- }
711
-
712
- if (this.#frame - (this.#touched.get(page_row) ?? 0) < this.#protect_window) {
713
- return false;
714
- }
715
-
716
- return !this.#tables.has_resident_child(page_row);
717
- }
718
-
719
- /**
720
- * Drop coldest-first until a slot is free and `bytes` fit.
721
- *
722
- * Both budgets bind. Slots are fixed and finite now, so a full table is a reason to evict in its
723
- * own right — where it used to be a permanent cliff independent of the byte budget
724
- * (REVIEW_LEDGER M17).
725
- *
726
- * @param {number} bytes
727
- * @returns {boolean}
728
- */
729
- #make_room(bytes) {
730
- let guard = this.#tables.resident_page_count + 1;
731
-
732
- while (guard-- > 0) {
733
- const short = this.#resident_bytes + bytes > this.#budget_bytes
734
- || !this.#tables.has_free_page_row;
735
-
736
- if (!short) {
737
- return true;
738
- }
739
-
740
- const coldest = this.#coldest();
741
-
742
- if (coldest === -1) {
743
- // nothing may go — every page is pinned, hot, or a parent. Refusing the install is
744
- // the right answer: the cut clamps and draws coarser, which is what it is for.
745
- return false;
746
- }
747
-
748
- this.evict(coldest);
749
- }
750
-
751
- return this.#resident_bytes + bytes <= this.#budget_bytes
752
- && this.#tables.has_free_page_row;
753
- }
754
-
755
- /**
756
- * @returns {number} page row, or -1
757
- */
758
- #coldest() {
759
- let best = -1;
760
- let best_stamp = Number.POSITIVE_INFINITY;
761
-
762
- for (const [page_row, stamp] of this.#touched) {
763
- if (stamp >= best_stamp || !this.evictable(page_row)) {
764
- continue;
765
- }
766
-
767
- best = page_row;
768
- best_stamp = stamp;
769
- }
770
-
771
- return best;
772
- }
773
-
774
690
  /**
775
691
  * @param {number} page_row
776
692
  * @returns {number}
@@ -816,10 +732,10 @@ export class GPUVirtualGeometryPool {
816
732
  }
817
733
 
818
734
  /**
819
- * @param {VGeoPoolGeometry} geometry
735
+ * Upload the group rows whose flags the tables flipped.
820
736
  */
821
- #refresh_selectable(geometry) {
822
- for (const page_row of this.#tables.refresh_selectable(geometry.row)) {
737
+ #mark_activated() {
738
+ for (const page_row of this.#tables.take_activated_rows()) {
823
739
  const [start, count] = this.#tables.group_range(page_row);
824
740
 
825
741
  this.#mark('arena', start, count);