@woosh/meep-engine 3.26.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 (236) 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 +14 -7
  16. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
  17. package/src/core/geom/3d/shape/HeightMapShape3D.js +87 -78
  18. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  19. package/src/shade/playground/vgeo_runtime/README.md +167 -22
  20. package/src/shade/playground/vgeo_runtime/index.html +112 -62
  21. package/src/shade/playground/vgeo_runtime/main.js +660 -83
  22. package/src/shade/playground/vgeo_viewer/sample_asset.d.ts.map +1 -1
  23. package/src/shade/playground/vgeo_viewer/sample_asset.js +19 -2
  24. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts +11 -0
  25. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
  26. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +16 -8
  27. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
  28. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +16 -11
  29. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +73 -5
  30. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +26 -15
  31. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +5 -3
  32. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +18 -0
  33. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
  34. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +20 -0
  35. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +1 -1
  36. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.js +56 -9
  37. package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts.map +1 -1
  38. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +1 -0
  39. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +22 -0
  40. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
  41. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +24 -0
  42. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts +12 -0
  43. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts.map +1 -1
  44. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +13 -0
  45. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts +8 -1
  46. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts.map +1 -1
  47. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +11 -3
  48. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts +9 -0
  49. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts.map +1 -1
  50. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +10 -1
  51. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.d.ts +18 -0
  52. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.d.ts.map +1 -0
  53. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.js +17 -0
  54. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.d.ts +8 -0
  55. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.d.ts.map +1 -0
  56. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.js +7 -0
  57. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.d.ts +3 -0
  58. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +16 -0
  59. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.d.ts +14 -0
  60. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.d.ts.map +1 -0
  61. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.js +13 -0
  62. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.d.ts +11 -0
  63. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.d.ts.map +1 -0
  64. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.js +10 -0
  65. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts +20 -0
  66. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts.map +1 -1
  67. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.js +20 -2
  68. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts +4 -0
  69. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts.map +1 -1
  70. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.js +4 -0
  71. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts +29 -3
  72. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts.map +1 -1
  73. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.js +81 -122
  74. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +40 -0
  75. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
  76. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +44 -0
  77. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts +69 -0
  78. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts.map +1 -0
  79. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.js +236 -0
  80. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts +14 -0
  81. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts.map +1 -1
  82. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.js +15 -0
  83. package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.d.ts +31 -0
  84. package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.d.ts.map +1 -0
  85. package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.js +32 -0
  86. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts +10 -0
  87. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts.map +1 -1
  88. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.js +21 -1
  89. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.d.ts +15 -0
  90. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.d.ts.map +1 -0
  91. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.js +84 -0
  92. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts +22 -0
  93. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts.map +1 -0
  94. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.js +96 -0
  95. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
  96. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +55 -0
  97. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
  98. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +202 -13
  99. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
  100. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +147 -10
  101. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +637 -0
  102. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts +33 -36
  103. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts.map +1 -1
  104. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.js +109 -193
  105. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +58 -28
  106. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
  107. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +77 -105
  108. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +37 -0
  109. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +105 -22
  110. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +548 -0
  111. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts +29 -50
  112. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -1
  113. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +47 -489
  114. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts +84 -0
  115. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts.map +1 -0
  116. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.js +393 -0
  117. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts +234 -0
  118. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -0
  119. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +1363 -0
  120. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.d.ts +81 -0
  121. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.d.ts.map +1 -0
  122. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.js +89 -0
  123. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts +53 -34
  124. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts.map +1 -1
  125. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.js +407 -124
  126. package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.d.ts +100 -0
  127. package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.d.ts.map +1 -0
  128. package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.js +219 -0
  129. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.d.ts +17 -0
  130. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.d.ts.map +1 -0
  131. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.js +47 -0
  132. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.d.ts +20 -0
  133. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.d.ts.map +1 -0
  134. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.js +19 -0
  135. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts.map +1 -1
  136. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.js +30 -12
  137. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -1
  138. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +15 -0
  139. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts.map +1 -1
  140. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.js +18 -1
  141. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts.map +1 -1
  142. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.js +5 -15
  143. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.d.ts +3 -0
  144. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.d.ts.map +1 -0
  145. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.js +77 -0
  146. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts +3 -0
  147. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts.map +1 -0
  148. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.js +186 -0
  149. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts.map +1 -1
  150. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js +20 -8
  151. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -1
  152. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +580 -188
  153. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts +15 -18
  154. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts.map +1 -1
  155. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.js +19 -28
  156. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts.map +1 -1
  157. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.js +45 -25
  158. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +1 -0
  159. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +11 -0
  160. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +1 -1
  161. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts +0 -1
  162. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.js +12 -10
  163. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts +1 -0
  164. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.js +8 -0
  165. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts +7 -2
  166. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts.map +1 -1
  167. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.js +8 -3
  168. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts +2 -0
  169. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.js +19 -0
  170. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts.map +1 -1
  171. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.js +1 -1
  172. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.d.ts +5 -0
  173. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.d.ts.map +1 -0
  174. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.js +22 -0
  175. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.d.ts +14 -0
  176. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.d.ts.map +1 -0
  177. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.js +13 -0
  178. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.d.ts +17 -0
  179. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.d.ts.map +1 -0
  180. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.js +16 -0
  181. package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.d.ts +15 -0
  182. package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.d.ts.map +1 -0
  183. package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.js +16 -0
  184. package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.d.ts +23 -0
  185. package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.d.ts.map +1 -0
  186. package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.js +28 -0
  187. package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.d.ts +2 -0
  188. package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.d.ts.map +1 -0
  189. package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.js +13 -0
  190. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts +10 -5
  191. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts.map +1 -1
  192. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.js +15 -6
  193. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts +6 -1
  194. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -1
  195. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +38 -1
  196. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.d.ts.map +1 -1
  197. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.js +4 -3
  198. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  199. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  200. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +6 -3
  201. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  202. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +6 -3
  203. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  204. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  205. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +12 -4
  206. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts +2 -0
  207. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
  208. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +58 -57
  209. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.d.ts.map +1 -1
  210. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.js +3 -5
  211. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.d.ts.map +1 -1
  212. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.js +3 -4
  213. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
  214. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +3 -4
  215. package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.d.ts.map +1 -1
  216. package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.js +7 -7
  217. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts.map +1 -1
  218. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.js +83 -78
  219. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  220. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +7 -1
  221. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  222. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +7 -1
  223. package/src/shade/renderer/scene/GPUSceneContext.d.ts +22 -2
  224. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  225. package/src/shade/renderer/scene/GPUSceneContext.js +59 -4
  226. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
  227. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +92 -16
  228. package/src/shade/renderer/view/GPUViewContext.d.ts +5 -0
  229. package/src/shade/renderer/view/GPUViewContext.d.ts.map +1 -1
  230. package/src/shade/renderer/view/GPUViewContext.js +21 -0
  231. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  232. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +74 -3
  233. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.js +0 -723
  234. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.js +0 -50
  235. package/src/shade/renderer/shader/chunk/jobs/NOTES.md +0 -47
  236. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.js +0 -102
@@ -0,0 +1,1363 @@
1
+ import { assert } from "../../../../../core/assert.js";
2
+ import { ConcurrencyGate } from "../../../../../core/process/ConcurrencyGate.js";
3
+ import { VGEO_FEEDBACK_OFFSET } from "./layout/VGEO_FEEDBACK_OFFSET.js";
4
+ import { VGEO_PAGE_ROW_OFFSET } from "./layout/VGEO_PAGE_ROW_OFFSET.js";
5
+ import { VGEO_WANT_WORDS } from "./layout/VGEO_WANT_WORDS.js";
6
+ import { VGeoPageFetcher } from "./VGeoPageFetcher.js";
7
+ import { VGeoResidencyOptions } from "./VGeoResidencyOptions.js";
8
+
9
+ /**
10
+ * @typedef {import("../format/read/VGeoContainerReader.js").VGeoContainerReader} VGeoContainerReader
11
+ * @typedef {import("../format/read/VGeoPage.js").VGeoPage} VGeoPage
12
+ * @typedef {import("./GPUVirtualGeometryPool.js").GPUVirtualGeometryPool} GPUVirtualGeometryPool
13
+ */
14
+
15
+ /**
16
+ * Where a page of a registered container is, as one word per page beside the lookup: the top two
17
+ * bits are the tag, the rest is the RAM slot or the VRAM row.
18
+ */
19
+ const TAG_SHIFT = 30;
20
+ const INDEX_MASK = (1 << TAG_SHIFT) - 1;
21
+
22
+ const TAG_ABSENT = 0;
23
+ const TAG_PENDING = 1;
24
+ const TAG_RAM = 2;
25
+ const TAG_VRAM = 3;
26
+
27
+ const STATE_ABSENT = 0;
28
+ const STATE_FETCHING = (TAG_PENDING << TAG_SHIFT) | 1;
29
+ const STATE_QUARANTINED = (TAG_PENDING << TAG_SHIFT) | 2;
30
+
31
+ const NO_SLOT = 0xFFFFFFFF;
32
+
33
+ /**
34
+ * @param {number} state
35
+ * @returns {number}
36
+ */
37
+ function tag_of(state) {
38
+ return state >>> TAG_SHIFT;
39
+ }
40
+
41
+ /**
42
+ * @param {number} state
43
+ * @returns {number}
44
+ */
45
+ function index_of(state) {
46
+ return state & INDEX_MASK;
47
+ }
48
+
49
+ /**
50
+ * One registered container: its reader, its geometry row in the pool, and one word per page.
51
+ */
52
+ class VGeoResidencyGeometry {
53
+ /**
54
+ * @type {VGeoContainerReader}
55
+ */
56
+ reader;
57
+
58
+ /**
59
+ * @type {number}
60
+ */
61
+ row;
62
+
63
+ /**
64
+ * Per container page index, where the page is — see the tags above.
65
+ * @type {Uint32Array}
66
+ */
67
+ state;
68
+
69
+ /**
70
+ * Per container page index, the RAM slot the page holds while it is in RAM or VRAM (VRAM is a
71
+ * subset of RAM), or {@link NO_SLOT}.
72
+ * @type {Uint32Array}
73
+ */
74
+ slot;
75
+
76
+ /**
77
+ * Fetches this reading dispatched for this container and how they ended, so that a container
78
+ * whose every fetch of a reading was quarantined is reported once.
79
+ * @type {{size: number, settled: number, quarantined: number}|undefined}
80
+ */
81
+ batch;
82
+
83
+ /**
84
+ * @param {VGeoContainerReader} reader
85
+ * @param {number} row
86
+ * @param {number} page_count
87
+ */
88
+ constructor(reader, row, page_count) {
89
+ this.reader = reader;
90
+ this.row = row;
91
+ this.state = new Uint32Array(page_count);
92
+ this.slot = new Uint32Array(page_count).fill(NO_SLOT);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Both tiers of residency for every registered container, flat, and nothing per frame that scales
98
+ * with what is resident.
99
+ *
100
+ * `VIRTUAL_GEOMETRY_DESIGN.md` §6 names the tiers and the rules — RAM ⊇ VRAM, a byte budget each,
101
+ * LRU on last-touched under a protect window, never a page whose child is resident in the same
102
+ * tier, the root pinned, admission before the fetch. What this class changes is not a rule but the
103
+ * shape everything is kept in (RUNTIME_PLAN_2026_09_14 §4). Two owners kept the rules before it:
104
+ * `GPUVirtualGeometryPool` for VRAM, with a `Map` of touch stamps it walked once per install to
105
+ * find the coldest row, and `VGeoResidencyManager` for RAM, which built and sorted an array of
106
+ * every resident page of every reader whenever its budget bound. On the Zorah bdf6f961 container at
107
+ * 4,096 slots the first was 0.55 ms per refused install and the second the same defect in the tier
108
+ * above, and neither scaled with anything the host had chosen.
109
+ *
110
+ * So:
111
+ *
112
+ * - **One word per page**, beside the lookup and the same length, says where a page is — absent,
113
+ * fetching, quarantined, in RAM at a slot, in VRAM at a row. A want is dropped, promoted or
114
+ * fetched by one read of it, which is the whole of the cross-frame dedup.
115
+ * - **Struct-of-arrays per tier**, sized to a capacity the host stated: last use, bytes, the page,
116
+ * the key. No `Map` is walked per frame.
117
+ * - **Touches arrive as bits** and are folded into `last_used` per set bit; a page in RAM and not in
118
+ * VRAM cannot be touched, so its last use is the frame it left VRAM, written once.
119
+ * - **Eviction is CLOCK under a test budget.** A hand over each tier's array tests candidates in
120
+ * order and evicts the first that may go; a reading may run at most
121
+ * `max_eviction_tests_per_reading` tests across both tiers, after which it refuses what is left
122
+ * and the hand carries on next reading. A full tier therefore turns over at a rate the options
123
+ * chose, never at one the resident count chose. Not exact LRU, deliberately: the protect window
124
+ * already coarsens the order to "not recently", and an exact order costs an update per touch.
125
+ * - **Admission before the fetch**, against the RAM budget and a slot, out of the `decoded_size`
126
+ * the parent's child table carries (FORMAT §10) — a page that will not fit is never fetched.
127
+ * - **VRAM ⊆ RAM is enforced**: the RAM hand skips a page that is in VRAM, so the page a want was
128
+ * discovered through is in RAM whenever the want is valid, and its child table resolves the
129
+ * range. Before this nothing enforced it, and a want whose source the RAM tier had dropped was
130
+ * silently skipped.
131
+ *
132
+ * A registered reader is this class's: its page map is driven through `install` and `evict` from
133
+ * here, and a tool or a viewer driving the same reader alongside it is unsupported rather than
134
+ * reconciled per frame.
135
+ *
136
+ * @author Alex Goldring
137
+ * @copyright Company Named Limited (c) 2026
138
+ */
139
+ export class VGeoResidency {
140
+ /**
141
+ * @type {GPUVirtualGeometryPool}
142
+ */
143
+ #pool;
144
+
145
+ /**
146
+ * @type {VGeoResidencyOptions}
147
+ */
148
+ #options;
149
+
150
+ /**
151
+ * @type {ConcurrencyGate}
152
+ */
153
+ #gate;
154
+
155
+ /**
156
+ * @type {VGeoPageFetcher}
157
+ */
158
+ #fetcher;
159
+
160
+ /**
161
+ * Whether {@link destroy} terminates the fetcher's workers — it does when this made it.
162
+ * @type {boolean}
163
+ */
164
+ #owns_fetcher;
165
+
166
+ /**
167
+ * @type {Map<VGeoContainerReader, VGeoResidencyGeometry>}
168
+ */
169
+ #geometries = new Map();
170
+
171
+ /**
172
+ * Geometry row to its record, which is how a page row found through the pool reaches its
173
+ * container.
174
+ * @type {Map<number, VGeoResidencyGeometry>}
175
+ */
176
+ #by_row = new Map();
177
+
178
+ // ------------------------------------------------------------------ the VRAM tier
179
+
180
+ /**
181
+ * Per page row, the frame it was last drawn from — or installed at, so a page promoted in this
182
+ * reading is inside the protect window and not this reading's next victim.
183
+ * @type {Uint32Array}
184
+ */
185
+ #vram_last_used;
186
+
187
+ /**
188
+ * @type {Uint8Array}
189
+ */
190
+ #vram_pinned;
191
+
192
+ /**
193
+ * @type {number}
194
+ */
195
+ #vram_hand = 0;
196
+
197
+ // ------------------------------------------------------------------ the RAM tier
198
+
199
+ /**
200
+ * @type {(VGeoPage|undefined)[]}
201
+ */
202
+ #ram_page;
203
+
204
+ /**
205
+ * Geometry row per slot, or -1 for a free one.
206
+ * @type {Int32Array}
207
+ */
208
+ #ram_geometry;
209
+
210
+ /**
211
+ * @type {Uint32Array}
212
+ */
213
+ #ram_page_index;
214
+
215
+ /**
216
+ * Per slot, the frame the page last left VRAM, or entered RAM.
217
+ * @type {Uint32Array}
218
+ */
219
+ #ram_last;
220
+
221
+ /**
222
+ * Decoded bytes per slot; what the tier is charged and refunded.
223
+ * @type {Uint32Array}
224
+ */
225
+ #ram_bytes;
226
+
227
+ /**
228
+ * @type {number[]}
229
+ */
230
+ #ram_free = [];
231
+
232
+ /**
233
+ * @type {number}
234
+ */
235
+ #ram_hand = 0;
236
+
237
+ /**
238
+ * @type {number}
239
+ */
240
+ #ram_resident_bytes = 0;
241
+
242
+ /**
243
+ * Bytes admitted for fetches that have not landed, charged against the budget from the moment
244
+ * of admission so that a reading cannot admit more than the tier will have room for.
245
+ * @type {number}
246
+ */
247
+ #ram_reserved_bytes = 0;
248
+
249
+ /**
250
+ * @type {number}
251
+ */
252
+ #ram_count = 0;
253
+
254
+ // ------------------------------------------------------------------ per reading
255
+
256
+ /**
257
+ * @type {number}
258
+ */
259
+ #tests_left = 0;
260
+
261
+ /**
262
+ * @type {number}
263
+ */
264
+ #last_touch_count = 0;
265
+
266
+ /**
267
+ * @type {number}
268
+ */
269
+ #last_want_count = 0;
270
+
271
+ /**
272
+ * Scratch for ranking a reading's wants: record index by priority.
273
+ * @type {number[]}
274
+ */
275
+ #order = [];
276
+
277
+ // ------------------------------------------------------------------ counters
278
+
279
+ #evicted_vram_count = 0;
280
+ #evicted_ram_count = 0;
281
+ #refused_install_count = 0;
282
+ #refused_fetch_count = 0;
283
+ #deferred_want_count = 0;
284
+ #deduped_want_count = 0;
285
+ #stale_record_count = 0;
286
+ #lost_source_count = 0;
287
+ #moot_record_count = 0;
288
+ #failed_batch_count = 0;
289
+ #fetches_in_flight = 0;
290
+
291
+ /**
292
+ * @param {Object} options
293
+ * @param {GPUVirtualGeometryPool} options.pool
294
+ * @param {VGeoResidencyOptions} [options.options]
295
+ * @param {ConcurrencyGate} [options.gate] a fetch gate shared with other residencies
296
+ * @param {VGeoPageFetcher} [options.fetcher] where pages are fetched and decoded; by default
297
+ * an in-process one, because only a host that can spawn a worker knows how
298
+ */
299
+ constructor({ pool, options = new VGeoResidencyOptions(), gate = undefined, fetcher = undefined }) {
300
+ assert.defined(pool, 'pool');
301
+
302
+ this.#pool = pool;
303
+ this.#options = options;
304
+ this.#gate = gate ?? new ConcurrencyGate(options.max_concurrent_fetches);
305
+ this.#fetcher = fetcher ?? new VGeoPageFetcher();
306
+ this.#owns_fetcher = fetcher === undefined;
307
+
308
+ const rows = pool.tables.page_capacity;
309
+
310
+ this.#vram_last_used = new Uint32Array(rows);
311
+ this.#vram_pinned = new Uint8Array(rows);
312
+
313
+ const slots = options.ram_slot_capacity;
314
+
315
+ assert.isPositiveInteger(slots, 'ram_slot_capacity');
316
+
317
+ this.#ram_page = new Array(slots).fill(undefined);
318
+ this.#ram_geometry = new Int32Array(slots).fill(-1);
319
+ this.#ram_page_index = new Uint32Array(slots);
320
+ this.#ram_last = new Uint32Array(slots);
321
+ this.#ram_bytes = new Uint32Array(slots);
322
+
323
+ for (let slot = slots - 1; slot >= 0; slot--) {
324
+ this.#ram_free.push(slot);
325
+ }
326
+ }
327
+
328
+ // ------------------------------------------------------------------ readings
329
+
330
+ /**
331
+ * @returns {VGeoResidencyOptions}
332
+ */
333
+ get options() {
334
+ return this.#options;
335
+ }
336
+
337
+ /**
338
+ * @returns {ConcurrencyGate}
339
+ */
340
+ get gate() {
341
+ return this.#gate;
342
+ }
343
+
344
+ /**
345
+ * @returns {VGeoPageFetcher}
346
+ */
347
+ get fetcher() {
348
+ return this.#fetcher;
349
+ }
350
+
351
+ /**
352
+ * @returns {GPUVirtualGeometryPool}
353
+ */
354
+ get pool() {
355
+ return this.#pool;
356
+ }
357
+
358
+ /**
359
+ * The clock both tiers read: the pool's frame, which the runtime advances at `begin_frame`
360
+ * whether or not a reading is scheduled that frame.
361
+ * @returns {number}
362
+ */
363
+ get frame() {
364
+ return this.#pool.frame;
365
+ }
366
+
367
+ /**
368
+ * Decoded bytes the VRAM tier may hold: the option, or what the pool's slots hold at its page
369
+ * size. Read each time, so a host may move it.
370
+ * @returns {number}
371
+ */
372
+ get vram_budget_bytes() {
373
+ return this.#options.vram_budget_bytes
374
+ ?? this.#pool.tables.page_capacity * this.#pool.page_size;
375
+ }
376
+
377
+ get ram_budget_bytes() {
378
+ return this.#options.ram_budget_bytes;
379
+ }
380
+
381
+ get ram_resident_bytes() {
382
+ return this.#ram_resident_bytes;
383
+ }
384
+
385
+ get ram_page_count() {
386
+ return this.#ram_count;
387
+ }
388
+
389
+ get reader_count() {
390
+ return this.#geometries.size;
391
+ }
392
+
393
+ get evicted_vram_count() {
394
+ return this.#evicted_vram_count;
395
+ }
396
+
397
+ get evicted_ram_count() {
398
+ return this.#evicted_ram_count;
399
+ }
400
+
401
+ /**
402
+ * Promotions the VRAM tier had no room for — no slot could be freed under the reading's test
403
+ * budget, or the page did not fit one.
404
+ * @returns {number}
405
+ */
406
+ get refused_install_count() {
407
+ return this.#refused_install_count;
408
+ }
409
+
410
+ /**
411
+ * Fetches the RAM tier had no room for.
412
+ * @returns {number}
413
+ */
414
+ get refused_fetch_count() {
415
+ return this.#refused_fetch_count;
416
+ }
417
+
418
+ /**
419
+ * Wants past a reading's promotion or fetch budget. Not a failure: the next cut asks again.
420
+ * @returns {number}
421
+ */
422
+ get deferred_want_count() {
423
+ return this.#deferred_want_count;
424
+ }
425
+
426
+ /**
427
+ * Wants for a page already in flight, or already in VRAM by the time the reading landed.
428
+ * @returns {number}
429
+ */
430
+ get deduped_want_count() {
431
+ return this.#deduped_want_count;
432
+ }
433
+
434
+ /**
435
+ * Records naming a page row that was refilled between the cut and the reading.
436
+ * @returns {number}
437
+ */
438
+ get stale_record_count() {
439
+ return this.#stale_record_count;
440
+ }
441
+
442
+ /**
443
+ * Wants whose source page was not in RAM, which the RAM tier's hand is meant to make impossible.
444
+ * @returns {number}
445
+ */
446
+ get lost_source_count() {
447
+ return this.#lost_source_count;
448
+ }
449
+
450
+ /**
451
+ * Records that named an empty row, an unregistered container, or a child reference the source
452
+ * page's table does not carry. The first is ordinary — a page evicted between the cut and the
453
+ * reading; the others a cut over a validated container does not produce.
454
+ * @returns {number}
455
+ */
456
+ get moot_record_count() {
457
+ return this.#moot_record_count;
458
+ }
459
+
460
+ /**
461
+ * Readings in which every fetch dispatched for one container was quarantined.
462
+ * @returns {number}
463
+ */
464
+ get failed_batch_count() {
465
+ return this.#failed_batch_count;
466
+ }
467
+
468
+ get fetches_in_flight() {
469
+ return this.#fetches_in_flight;
470
+ }
471
+
472
+ get last_touch_count() {
473
+ return this.#last_touch_count;
474
+ }
475
+
476
+ get last_want_count() {
477
+ return this.#last_want_count;
478
+ }
479
+
480
+ /**
481
+ * @returns {number}
482
+ */
483
+ get quarantined_page_count() {
484
+ let count = 0;
485
+
486
+ for (const geometry of this.#geometries.values()) {
487
+ count += geometry.reader.quarantined_page_count;
488
+ }
489
+
490
+ return count;
491
+ }
492
+
493
+ // ------------------------------------------------------------------ containers
494
+
495
+ /**
496
+ * Take a container under both tiers: a geometry row and a lookup in the pool, its root page in
497
+ * RAM and VRAM, pinned in both.
498
+ *
499
+ * A refusal leaves nothing behind. Both ways this fails — a container built at another page
500
+ * size, and a tier with no room for another root — throw, and the pool's row is given back
501
+ * before they do; the message says what the tier holds.
502
+ *
503
+ * @param {VGeoContainerReader} reader open, with its root page resident
504
+ * @returns {number} the geometry row
505
+ */
506
+ register(reader) {
507
+ assert.defined(reader, 'reader');
508
+
509
+ const existing = this.#geometries.get(reader);
510
+
511
+ if (existing !== undefined) {
512
+ return existing.row;
513
+ }
514
+
515
+ assert.defined(reader.root, 'reader.root — the container must be open');
516
+
517
+ const row = this.#pool.register(reader);
518
+
519
+ const geometry = new VGeoResidencyGeometry(reader, row, reader.header.total_pages);
520
+
521
+ this.#geometries.set(reader, geometry);
522
+ this.#by_row.set(row, geometry);
523
+
524
+ const root = reader.root;
525
+
526
+ // a registration is a reading of its own as far as the eviction budget is concerned
527
+ this.#tests_left = this.#options.max_eviction_tests_per_reading;
528
+
529
+ const page_row = this.#make_ram_room(root.decoded_size)
530
+ ? (this.#take_ram_slot(geometry, root), this.#promote(geometry, root))
531
+ : -1;
532
+
533
+ if (page_row === -1) {
534
+ this.#drop_geometry(geometry);
535
+
536
+ throw new Error(
537
+ `virtual geometry: the root page of this container could not be made resident, so`
538
+ + ` it would draw nothing and every cut seeded from it would read an absent row.`
539
+ + ` The VRAM tier holds ${this.#pool.tables.page_capacity} slots and`
540
+ + ` ${this.#pool.resident_page_count} are taken, of a ${this.vram_budget_bytes}-byte`
541
+ + ` budget with ${this.#pool.resident_bytes} spent; the RAM tier holds`
542
+ + ` ${this.#ram_count} pages of a ${this.#options.ram_budget_bytes}-byte budget`
543
+ + ` with ${this.#ram_resident_bytes} spent.`
544
+ );
545
+ }
546
+
547
+ this.#vram_pinned[page_row] = 1;
548
+
549
+ return row;
550
+ }
551
+
552
+ /**
553
+ * Give a container back: its rows, its lookup, its RAM slots, and the fetches it had on the
554
+ * wire. The decoded pages stay in the reader's map for the host to drop or keep.
555
+ *
556
+ * @param {VGeoContainerReader} reader
557
+ * @returns {boolean} whether it was registered
558
+ */
559
+ unregister(reader) {
560
+ const geometry = this.#geometries.get(reader);
561
+
562
+ if (geometry === undefined) {
563
+ return false;
564
+ }
565
+
566
+ // a reply that lands after this finds no geometry and is dropped
567
+ this.#fetcher.cancel(reader);
568
+ reader.cancel_all();
569
+
570
+ this.#drop_geometry(geometry);
571
+
572
+ return true;
573
+ }
574
+
575
+ /**
576
+ * @param {VGeoContainerReader} reader
577
+ * @returns {boolean}
578
+ */
579
+ is_registered(reader) {
580
+ return this.#geometries.has(reader);
581
+ }
582
+
583
+ /**
584
+ * Where one page of a registered container is, by name — for a panel or a spec.
585
+ *
586
+ * @param {VGeoContainerReader} reader
587
+ * @param {number} page_index
588
+ * @returns {'absent'|'fetching'|'quarantined'|'ram'|'vram'|'unregistered'}
589
+ */
590
+ where(reader, page_index) {
591
+ const geometry = this.#geometries.get(reader);
592
+
593
+ if (geometry === undefined || page_index >= geometry.state.length) {
594
+ return 'unregistered';
595
+ }
596
+
597
+ const state = geometry.state[page_index];
598
+
599
+ switch (tag_of(state)) {
600
+ case TAG_RAM:
601
+ return 'ram';
602
+ case TAG_VRAM:
603
+ return 'vram';
604
+ case TAG_PENDING:
605
+ return state === STATE_FETCHING ? 'fetching' : 'quarantined';
606
+ default:
607
+ return 'absent';
608
+ }
609
+ }
610
+
611
+ /**
612
+ * @param {VGeoResidencyGeometry} geometry
613
+ */
614
+ #drop_geometry(geometry) {
615
+ const state = geometry.state;
616
+
617
+ for (let page_index = 0; page_index < state.length; page_index++) {
618
+ const tag = tag_of(state[page_index]);
619
+
620
+ if (tag === TAG_VRAM) {
621
+ const row = index_of(state[page_index]);
622
+
623
+ this.#vram_pinned[row] = 0;
624
+ this.#vram_last_used[row] = 0;
625
+ }
626
+
627
+ if (tag === TAG_VRAM || tag === TAG_RAM) {
628
+ this.#free_ram_slot(geometry.slot[page_index]);
629
+ }
630
+
631
+ state[page_index] = STATE_ABSENT;
632
+ geometry.slot[page_index] = NO_SLOT;
633
+ }
634
+
635
+ this.#pool.unregister(geometry.reader);
636
+
637
+ this.#geometries.delete(geometry.reader);
638
+ this.#by_row.delete(geometry.row);
639
+ }
640
+
641
+ /**
642
+ * Stop every fetch every registered container has on the wire.
643
+ *
644
+ * @returns {void}
645
+ */
646
+ abort() {
647
+ for (const geometry of this.#geometries.values()) {
648
+ this.#fetcher.cancel(geometry.reader);
649
+ geometry.reader.cancel_all();
650
+ }
651
+ }
652
+
653
+ /**
654
+ * Give up on everything: every fetch, every container.
655
+ *
656
+ * @returns {void}
657
+ */
658
+ destroy() {
659
+ for (const reader of Array.from(this.#geometries.keys())) {
660
+ this.unregister(reader);
661
+ }
662
+
663
+ if (this.#owns_fetcher) {
664
+ this.#fetcher.destroy();
665
+ }
666
+ }
667
+
668
+ /**
669
+ * Let every quarantined page be asked for again.
670
+ *
671
+ * @returns {number} pages released
672
+ */
673
+ clear_quarantines() {
674
+ let released = 0;
675
+
676
+ for (const geometry of this.#geometries.values()) {
677
+ released += geometry.reader.clear_quarantine();
678
+
679
+ const state = geometry.state;
680
+
681
+ for (let page_index = 0; page_index < state.length; page_index++) {
682
+ if (state[page_index] === STATE_QUARANTINED) {
683
+ state[page_index] = STATE_ABSENT;
684
+ }
685
+ }
686
+ }
687
+
688
+ return released;
689
+ }
690
+
691
+ // ------------------------------------------------------------------ a reading
692
+
693
+ /**
694
+ * Apply one frame's feedback: the touches to both tiers' clocks, and the wants to promotions and
695
+ * fetches, under the reading's budgets.
696
+ *
697
+ * @param {Uint32Array} words the feedback buffer, decoded with its own frame's layout
698
+ * @param {{touch_bits_base: number, touch_word_count: number, want_base: number,
699
+ * word_count: number}} layout
700
+ * @param {number} reading_frame the frame whose cut wrote these words
701
+ * @returns {void}
702
+ */
703
+ apply(words, layout, reading_frame) {
704
+ const options = this.#options;
705
+
706
+ this.#tests_left = options.max_eviction_tests_per_reading;
707
+
708
+ for (const geometry of this.#geometries.values()) {
709
+ geometry.batch = undefined;
710
+ }
711
+
712
+ this.#fold_touches(words, layout, reading_frame);
713
+
714
+ const count = this.#rank_wants(words, layout);
715
+
716
+ let promotions = 0;
717
+ let fetches = 0;
718
+
719
+ for (let i = 0; i < count; i++) {
720
+ const base = layout.want_base + this.#order[i] * VGEO_WANT_WORDS;
721
+
722
+ const source_row = words[base];
723
+ const child_ref = words[base + 1];
724
+ const child_page_index = words[base + 2];
725
+ const priority = decode_float(words[base + 3]);
726
+
727
+ const entry = this.#pool.entry_of(source_row);
728
+
729
+ if (entry === undefined) {
730
+ // evicted between the cut and this reading, and the row is empty: moot
731
+ this.#moot_record_count++;
732
+
733
+ continue;
734
+ }
735
+
736
+ if (!this.#still_the_same_page(source_row, reading_frame)) {
737
+ // the row was refilled between the cut and this reading, so the page index and the
738
+ // byte range the child table would give come from two different pages
739
+ this.#stale_record_count++;
740
+
741
+ continue;
742
+ }
743
+
744
+ const geometry = this.#geometries.get(entry.reader);
745
+
746
+ if (geometry === undefined || child_page_index >= geometry.state.length) {
747
+ this.#moot_record_count++;
748
+
749
+ continue;
750
+ }
751
+
752
+ const state = geometry.state[child_page_index];
753
+ const tag = tag_of(state);
754
+
755
+ if (tag === TAG_VRAM || tag === TAG_PENDING) {
756
+ // promoted a reading or two ago and the cut had not seen it yet; or on the wire, or
757
+ // not coming back until somebody clears the quarantine
758
+ this.#deduped_want_count++;
759
+
760
+ continue;
761
+ }
762
+
763
+ if (tag === TAG_RAM) {
764
+ if (promotions >= options.max_promotions_per_reading) {
765
+ this.#deferred_want_count++;
766
+
767
+ continue;
768
+ }
769
+
770
+ const page = this.#ram_page[geometry.slot[child_page_index]];
771
+
772
+ if (this.#promote(geometry, page) !== -1) {
773
+ promotions++;
774
+ }
775
+
776
+ continue;
777
+ }
778
+
779
+ // absent: a fetch, through the page the want was discovered through
780
+ if (fetches >= options.max_fetches_per_reading
781
+ || this.#fetches_in_flight >= options.max_concurrent_fetches * 2) {
782
+ this.#deferred_want_count++;
783
+
784
+ continue;
785
+ }
786
+
787
+ const source_index = this.#pool.page_index_of(source_row);
788
+
789
+ const source = entry.reader.page(source_index);
790
+
791
+ if (source === undefined) {
792
+ this.#lost_source_count++;
793
+
794
+ continue;
795
+ }
796
+
797
+ if (child_ref >= source.child_page_index.length
798
+ || source.child_page_index[child_ref] !== child_page_index) {
799
+ // a record the child table cannot resolve, which a cut over a validated container
800
+ // does not produce
801
+ this.#moot_record_count++;
802
+
803
+ continue;
804
+ }
805
+
806
+ if (this.#fetch(
807
+ geometry,
808
+ child_page_index,
809
+ source.child_frame_offset[child_ref],
810
+ source.child_fetch_size[child_ref],
811
+ source.child_decoded_size[child_ref],
812
+ priority
813
+ )) {
814
+ fetches++;
815
+ }
816
+ }
817
+
818
+ /*
819
+ And the budgets themselves, once a reading, under whatever is left of the test budget. A
820
+ host that lowers a budget while the tiers are full gets them back under it over the
821
+ following readings rather than only when the next admission happens to ask; within budget
822
+ this is two comparisons.
823
+ */
824
+ this.#make_vram_room(0, false);
825
+ this.#make_ram_room(0, false);
826
+ }
827
+
828
+ /**
829
+ * Whether a page row still holds the page it held when the cut of `reading_frame` ran.
830
+ *
831
+ * @param {number} page_row
832
+ * @param {number} reading_frame
833
+ * @returns {boolean}
834
+ */
835
+ #still_the_same_page(page_row, reading_frame) {
836
+ return this.#pool.visible_from(page_row) <= reading_frame;
837
+ }
838
+
839
+ /**
840
+ * @param {Uint32Array} words
841
+ * @param {object} layout
842
+ * @param {number} reading_frame
843
+ */
844
+ #fold_touches(words, layout, reading_frame) {
845
+ const frame = this.frame;
846
+
847
+ let count = 0;
848
+
849
+ for (let word = 0; word < layout.touch_word_count; word++) {
850
+ let bits = words[layout.touch_bits_base + word];
851
+
852
+ while (bits !== 0) {
853
+ const bit = 31 - Math.clz32(bits & -bits);
854
+
855
+ bits &= bits - 1;
856
+
857
+ const page_row = word * 32 + bit;
858
+
859
+ count++;
860
+
861
+ if (this.#pool.entry_of(page_row) === undefined) {
862
+ // evicted between the cut and this reading; the stamp it would take is moot
863
+ continue;
864
+ }
865
+
866
+ if (!this.#still_the_same_page(page_row, reading_frame)) {
867
+ // evicted and refilled in between; stamping now would make a page nobody has
868
+ // drawn the hottest thing in the tier
869
+ this.#stale_record_count++;
870
+
871
+ continue;
872
+ }
873
+
874
+ this.#vram_last_used[page_row] = frame;
875
+ }
876
+ }
877
+
878
+ this.#last_touch_count = count;
879
+ }
880
+
881
+ /**
882
+ * Order this reading's want records by priority, highest first.
883
+ *
884
+ * A sort of at most `want_capacity` indices. It is not on the GPU, deliberately: the list is
885
+ * capped, a sort of two thousand numbers is microseconds, and a device sort is a dozen dispatches
886
+ * to shave a few kilobytes off a readback (RUNTIME_PLAN_2026_09_14 §4.5).
887
+ *
888
+ * @param {Uint32Array} words
889
+ * @param {object} layout
890
+ * @returns {number} records
891
+ */
892
+ #rank_wants(words, layout) {
893
+ const capacity = Math.floor((layout.word_count - layout.want_base) / VGEO_WANT_WORDS);
894
+
895
+ // clamped: the count is an atomicAdd that ran past the capacity the kernel refused to write at
896
+ const count = Math.min(words[VGEO_FEEDBACK_OFFSET.WANT_COUNT], capacity);
897
+
898
+ this.#last_want_count = count;
899
+
900
+ const order = this.#order;
901
+
902
+ order.length = count;
903
+
904
+ for (let i = 0; i < count; i++) {
905
+ order[i] = i;
906
+ }
907
+
908
+ const priority = i => decode_float(words[layout.want_base + i * VGEO_WANT_WORDS + 3]);
909
+
910
+ order.sort((a, b) => priority(b) - priority(a));
911
+
912
+ return count;
913
+ }
914
+
915
+ // ------------------------------------------------------------------ VRAM
916
+
917
+ /**
918
+ * Whether a page row may be evicted from VRAM right now: resident, not pinned, outside the
919
+ * protect window, and with no child page resident in VRAM.
920
+ *
921
+ * @param {number} page_row
922
+ * @returns {boolean}
923
+ */
924
+ evictable_vram(page_row) {
925
+ if (this.#pool.entry_of(page_row) === undefined) {
926
+ return false;
927
+ }
928
+
929
+ if (this.#vram_pinned[page_row] !== 0) {
930
+ return false;
931
+ }
932
+
933
+ if (this.frame - this.#vram_last_used[page_row] < this.#options.protect_frames) {
934
+ return false;
935
+ }
936
+
937
+ return this.#pool.tables.resident_children(page_row) === 0;
938
+ }
939
+
940
+ /**
941
+ * Whether a RAM slot's page may be evicted from RAM right now: occupied, not the root, not in
942
+ * VRAM, outside the protect window, and with no child page resident in either tier.
943
+ *
944
+ * @param {number} slot
945
+ * @returns {boolean}
946
+ */
947
+ evictable_ram(slot) {
948
+ const geometry_row = this.#ram_geometry[slot];
949
+
950
+ if (geometry_row === -1) {
951
+ return false;
952
+ }
953
+
954
+ const page = this.#ram_page[slot];
955
+
956
+ if (page === undefined || page.index === 0) {
957
+ return false;
958
+ }
959
+
960
+ const geometry = this.#by_row.get(geometry_row);
961
+
962
+ if (tag_of(geometry.state[page.index]) === TAG_VRAM) {
963
+ return false;
964
+ }
965
+
966
+ if (this.frame - this.#ram_last[slot] < this.#options.protect_frames) {
967
+ return false;
968
+ }
969
+
970
+ const state = geometry.state;
971
+
972
+ for (const child of page.child_page_index) {
973
+ const tag = tag_of(state[child]);
974
+
975
+ if (tag === TAG_RAM || tag === TAG_VRAM) {
976
+ return false;
977
+ }
978
+ }
979
+
980
+ return true;
981
+ }
982
+
983
+ /**
984
+ * Make a page resident in VRAM out of RAM, making room under the reading's test budget.
985
+ *
986
+ * @param {VGeoResidencyGeometry} geometry
987
+ * @param {VGeoPage} page
988
+ * @returns {number} page row, or -1
989
+ */
990
+ #promote(geometry, page) {
991
+ if (!this.#make_vram_room(page.decoded_size)) {
992
+ this.#refused_install_count++;
993
+
994
+ return -1;
995
+ }
996
+
997
+ const page_row = this.#pool.install(geometry.reader, page.index);
998
+
999
+ if (page_row === -1) {
1000
+ this.#refused_install_count++;
1001
+
1002
+ return -1;
1003
+ }
1004
+
1005
+ geometry.state[page.index] = (TAG_VRAM << TAG_SHIFT) | page_row;
1006
+
1007
+ this.#vram_last_used[page_row] = this.frame;
1008
+ this.#vram_pinned[page_row] = 0;
1009
+
1010
+ return page_row;
1011
+ }
1012
+
1013
+ /**
1014
+ * @param {number} bytes
1015
+ * @param {boolean} [slot] whether a free page row is needed as well — it is for an install,
1016
+ * and not for bringing a tier back under a budget that moved
1017
+ * @returns {boolean}
1018
+ */
1019
+ #make_vram_room(bytes, slot = true) {
1020
+ const pool = this.#pool;
1021
+
1022
+ for (;;) {
1023
+ if (pool.resident_bytes + bytes <= this.vram_budget_bytes
1024
+ && (!slot || pool.tables.has_free_page_row)) {
1025
+ return true;
1026
+ }
1027
+
1028
+ const victim = this.#next_vram_victim();
1029
+
1030
+ if (victim === -1) {
1031
+ return false;
1032
+ }
1033
+
1034
+ this.#evict_vram(victim);
1035
+ }
1036
+ }
1037
+
1038
+ /**
1039
+ * The CLOCK hand over the page rows, under the reading's test budget.
1040
+ *
1041
+ * @returns {number} page row, or -1 when the budget is spent without finding one
1042
+ */
1043
+ #next_vram_victim() {
1044
+ const capacity = this.#vram_last_used.length;
1045
+
1046
+ while (this.#tests_left > 0) {
1047
+ this.#tests_left--;
1048
+
1049
+ const page_row = this.#vram_hand;
1050
+
1051
+ this.#vram_hand = (page_row + 1) % capacity;
1052
+
1053
+ if (this.evictable_vram(page_row)) {
1054
+ return page_row;
1055
+ }
1056
+ }
1057
+
1058
+ return -1;
1059
+ }
1060
+
1061
+ /**
1062
+ * @param {number} page_row
1063
+ */
1064
+ #evict_vram(page_row) {
1065
+ const entry = this.#pool.entry_of(page_row);
1066
+
1067
+ const geometry = this.#geometries.get(entry.reader);
1068
+
1069
+ const page_index = this.#pool.page_index_of(page_row);
1070
+
1071
+ this.#pool.evict(page_row);
1072
+
1073
+ const slot = geometry.slot[page_index];
1074
+
1075
+ geometry.state[page_index] = (TAG_RAM << TAG_SHIFT) | slot;
1076
+
1077
+ // the last time it was drawn from is the last time it was in VRAM
1078
+ this.#ram_last[slot] = this.frame;
1079
+
1080
+ this.#vram_pinned[page_row] = 0;
1081
+ this.#vram_last_used[page_row] = 0;
1082
+
1083
+ this.#evicted_vram_count++;
1084
+ }
1085
+
1086
+ // ------------------------------------------------------------------ RAM
1087
+
1088
+ /**
1089
+ * @param {number} bytes
1090
+ * @param {boolean} [slot] whether a free slot is needed as well
1091
+ * @returns {boolean}
1092
+ */
1093
+ #make_ram_room(bytes, slot = true) {
1094
+ const budget = this.#options.ram_budget_bytes;
1095
+
1096
+ for (;;) {
1097
+ if (this.#ram_resident_bytes + this.#ram_reserved_bytes + bytes <= budget
1098
+ && (!slot || this.#ram_free.length > 0)) {
1099
+ return true;
1100
+ }
1101
+
1102
+ const victim = this.#next_ram_victim();
1103
+
1104
+ if (victim === -1) {
1105
+ return false;
1106
+ }
1107
+
1108
+ this.#evict_ram(victim);
1109
+ }
1110
+ }
1111
+
1112
+ /**
1113
+ * @returns {number} slot, or -1
1114
+ */
1115
+ #next_ram_victim() {
1116
+ const capacity = this.#ram_geometry.length;
1117
+
1118
+ while (this.#tests_left > 0) {
1119
+ this.#tests_left--;
1120
+
1121
+ const slot = this.#ram_hand;
1122
+
1123
+ this.#ram_hand = (slot + 1) % capacity;
1124
+
1125
+ if (this.evictable_ram(slot)) {
1126
+ return slot;
1127
+ }
1128
+ }
1129
+
1130
+ return -1;
1131
+ }
1132
+
1133
+ /**
1134
+ * @param {number} slot
1135
+ */
1136
+ #evict_ram(slot) {
1137
+ const geometry = this.#by_row.get(this.#ram_geometry[slot]);
1138
+
1139
+ const page_index = this.#ram_page_index[slot];
1140
+
1141
+ geometry.reader.evict(page_index);
1142
+
1143
+ geometry.state[page_index] = STATE_ABSENT;
1144
+ geometry.slot[page_index] = NO_SLOT;
1145
+
1146
+ this.#free_ram_slot(slot);
1147
+
1148
+ this.#evicted_ram_count++;
1149
+ }
1150
+
1151
+ /**
1152
+ * Put a page the reader already holds into a slot. Room is the caller's business.
1153
+ *
1154
+ * @param {VGeoResidencyGeometry} geometry
1155
+ * @param {VGeoPage} page
1156
+ * @returns {number} slot
1157
+ */
1158
+ #take_ram_slot(geometry, page) {
1159
+ const slot = this.#reserve_ram_slot(geometry, page.index, page.decoded_size);
1160
+
1161
+ this.#land(slot, page);
1162
+
1163
+ return slot;
1164
+ }
1165
+
1166
+ /**
1167
+ * Take a slot and charge the bytes, for a page that is here or on its way.
1168
+ *
1169
+ * @param {VGeoResidencyGeometry} geometry
1170
+ * @param {number} page_index
1171
+ * @param {number} bytes
1172
+ * @returns {number} slot
1173
+ */
1174
+ #reserve_ram_slot(geometry, page_index, bytes) {
1175
+ const slot = this.#ram_free.pop();
1176
+
1177
+ this.#ram_geometry[slot] = geometry.row;
1178
+ this.#ram_page_index[slot] = page_index;
1179
+ this.#ram_page[slot] = undefined;
1180
+ this.#ram_bytes[slot] = bytes;
1181
+ this.#ram_last[slot] = this.frame;
1182
+
1183
+ this.#ram_reserved_bytes += bytes;
1184
+
1185
+ geometry.slot[page_index] = slot;
1186
+
1187
+ return slot;
1188
+ }
1189
+
1190
+ /**
1191
+ * A reserved slot's page has arrived.
1192
+ *
1193
+ * @param {number} slot
1194
+ * @param {VGeoPage} page
1195
+ */
1196
+ #land(slot, page) {
1197
+ const geometry = this.#by_row.get(this.#ram_geometry[slot]);
1198
+
1199
+ this.#ram_reserved_bytes -= this.#ram_bytes[slot];
1200
+
1201
+ // what it actually decoded to, which is what the tier holds and refunds
1202
+ this.#ram_bytes[slot] = page.decoded_size;
1203
+ this.#ram_page[slot] = page;
1204
+ this.#ram_last[slot] = this.frame;
1205
+
1206
+ this.#ram_resident_bytes += page.decoded_size;
1207
+ this.#ram_count++;
1208
+
1209
+ geometry.state[page.index] = (TAG_RAM << TAG_SHIFT) | slot;
1210
+ }
1211
+
1212
+ /**
1213
+ * @param {number} slot
1214
+ */
1215
+ #free_ram_slot(slot) {
1216
+ if (slot === NO_SLOT || this.#ram_geometry[slot] === -1) {
1217
+ return;
1218
+ }
1219
+
1220
+ if (this.#ram_page[slot] !== undefined) {
1221
+ this.#ram_resident_bytes -= this.#ram_bytes[slot];
1222
+ this.#ram_count--;
1223
+ } else {
1224
+ this.#ram_reserved_bytes -= this.#ram_bytes[slot];
1225
+ }
1226
+
1227
+ this.#ram_page[slot] = undefined;
1228
+ this.#ram_geometry[slot] = -1;
1229
+ this.#ram_bytes[slot] = 0;
1230
+
1231
+ this.#ram_free.push(slot);
1232
+ }
1233
+
1234
+ // ------------------------------------------------------------------ fetching
1235
+
1236
+ /**
1237
+ * Admit a page against the RAM tier and start its fetch.
1238
+ *
1239
+ * @param {VGeoResidencyGeometry} geometry
1240
+ * @param {number} page_index
1241
+ * @param {number} frame_offset
1242
+ * @param {number} fetch_size
1243
+ * @param {number} decoded_size
1244
+ * @param {number} priority
1245
+ * @returns {boolean} whether a fetch was started
1246
+ */
1247
+ #fetch(geometry, page_index, frame_offset, fetch_size, decoded_size, priority) {
1248
+ if (!this.#make_ram_room(decoded_size)) {
1249
+ this.#refused_fetch_count++;
1250
+
1251
+ return false;
1252
+ }
1253
+
1254
+ const reader = geometry.reader;
1255
+
1256
+ const slot = this.#reserve_ram_slot(geometry, page_index, decoded_size);
1257
+
1258
+ geometry.state[page_index] = STATE_FETCHING;
1259
+
1260
+ if (geometry.batch === undefined) {
1261
+ geometry.batch = { size: 0, settled: 0, quarantined: 0 };
1262
+ }
1263
+
1264
+ const batch = geometry.batch;
1265
+
1266
+ batch.size++;
1267
+
1268
+ this.#fetches_in_flight++;
1269
+
1270
+ this.#gate.submit(
1271
+ priority,
1272
+ () => this.#fetcher.fetch(reader, page_index, frame_offset, fetch_size)
1273
+ )
1274
+ .then(
1275
+ page => {
1276
+ this.#fetches_in_flight--;
1277
+
1278
+ if (this.#geometries.get(reader) !== geometry) {
1279
+ // unregistered while on the wire; the slot went with the geometry
1280
+ return;
1281
+ }
1282
+
1283
+ if (geometry.state[page_index] !== STATE_FETCHING) {
1284
+ return;
1285
+ }
1286
+
1287
+ // the reader's page map is the RAM tier's store, and this is its one door
1288
+ this.#land(slot, reader.adopt(page));
1289
+
1290
+ this.#settle(reader, geometry, batch, false);
1291
+ },
1292
+ error => {
1293
+ this.#fetches_in_flight--;
1294
+
1295
+ if (this.#geometries.get(reader) !== geometry) {
1296
+ return;
1297
+ }
1298
+
1299
+ if (geometry.state[page_index] !== STATE_FETCHING) {
1300
+ return;
1301
+ }
1302
+
1303
+ // a quarantine is the fetch's verdict and the reader's fact; a cancellation is
1304
+ // nobody's fault, and the next cut asks again
1305
+ const quarantined = error?.quarantine === true;
1306
+
1307
+ if (quarantined) {
1308
+ reader.quarantine_page(page_index, error.reason ?? error.message);
1309
+ }
1310
+
1311
+ geometry.state[page_index] = quarantined ? STATE_QUARANTINED : STATE_ABSENT;
1312
+ geometry.slot[page_index] = NO_SLOT;
1313
+
1314
+ this.#free_ram_slot(slot);
1315
+
1316
+ this.#settle(reader, geometry, batch, quarantined);
1317
+ }
1318
+ );
1319
+
1320
+ return true;
1321
+ }
1322
+
1323
+ /**
1324
+ * One fetch of a reading's batch has ended; a batch every page of which was quarantined is a
1325
+ * container that cannot be streamed, which is worth one line and a counter.
1326
+ *
1327
+ * @param {VGeoContainerReader} reader
1328
+ * @param {VGeoResidencyGeometry} geometry
1329
+ * @param {{size: number, settled: number, quarantined: number}} batch
1330
+ * @param {boolean} quarantined
1331
+ */
1332
+ #settle(reader, geometry, batch, quarantined) {
1333
+ batch.settled++;
1334
+
1335
+ if (quarantined) {
1336
+ batch.quarantined++;
1337
+ }
1338
+
1339
+ if (batch.settled < batch.size || batch.quarantined < batch.size) {
1340
+ return;
1341
+ }
1342
+
1343
+ this.#failed_batch_count++;
1344
+
1345
+ console.warn(
1346
+ `vgeo: every one of ${batch.size} page fetches was quarantined`
1347
+ + ` by ${reader.name ?? 'a reader'}`
1348
+ );
1349
+ }
1350
+ }
1351
+
1352
+ const FLOAT_SCRATCH = new Float32Array(1);
1353
+ const WORD_SCRATCH = new Uint32Array(FLOAT_SCRATCH.buffer);
1354
+
1355
+ /**
1356
+ * @param {number} word
1357
+ * @returns {number}
1358
+ */
1359
+ function decode_float(word) {
1360
+ WORD_SCRATCH[0] = word;
1361
+
1362
+ return FLOAT_SCRATCH[0];
1363
+ }