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