@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
@@ -1,723 +0,0 @@
1
- import { assert } from "../../../../../core/assert.js";
2
- import { ConcurrencyGate } from "../../../../../core/process/ConcurrencyGate.js";
3
- import { VGeoResidencyOptions } from "./VGeoResidencyOptions.js";
4
-
5
- /**
6
- * One reader's place in the manager's books.
7
- */
8
- class VGeoResidencyEntry {
9
- /**
10
- * @type {VGeoContainerReader}
11
- */
12
- reader = undefined;
13
-
14
- /**
15
- * The frame each resident page was last touched on. A page installs touched, because installing
16
- * it was somebody wanting it.
17
- * @type {Map<number, number>}
18
- */
19
- touched = new Map();
20
-
21
- /**
22
- * What this reader's `resident_bytes` was the last time the total was reconciled with it. Kept
23
- * per reader so that the total stays exact under interleaved installs: the delta is taken
24
- * against the reader's own authoritative number rather than accumulated from page sizes, so two
25
- * installs completing in either order come to the same total.
26
- * @type {number}
27
- */
28
- bytes = 0;
29
-
30
- /**
31
- * @param {VGeoContainerReader} reader
32
- */
33
- constructor(reader) {
34
- this.reader = reader;
35
- }
36
- }
37
-
38
- /**
39
- * The budget owner: what may be resident, what may be in flight, and what gets dropped.
40
- *
41
- * `VGeoContainerReader` is mechanics — it fetches a page, decodes it, keeps the activation counters
42
- * straight, and reports what it holds. It decides none of the questions that only make sense across
43
- * readers, and `VGEO_FORMAT.md` §10 says so deliberately, requirement by requirement: the eviction
44
- * *rule* is spelled out there and the *choice* of which page to drop is left to "whatever owns the
45
- * budget". This is that. Nothing here reaches inside a reader; it is written entirely against the
46
- * reader's public surface, which is the test that the split is real.
47
- *
48
- * Three things live here because none of them can live below:
49
- *
50
- * - **A byte budget across every reader.** A reader reports `resident_bytes` and enforces nothing;
51
- * at a thousand assets of several gigabytes each, the resident set has to be a small fraction of
52
- * *one* asset and no reader can know that. {@link install_wants} admits a want against the budget
53
- * before it is fetched, using the `decoded_size` the child table already told the traversal, and
54
- * {@link evict_to_budget} is what makes room.
55
- * - **A shared fetch cap and a global priority order.** Per-reader caps do not compose: eight
56
- * readers at `max_concurrent_fetches = 6` were measured issuing 48 concurrent range requests.
57
- * One {@link ConcurrencyGate} holds the total, and holds it as one priority queue so that the
58
- * most visibly coarse page wins wherever it came from. That is meaningful only because a want's
59
- * priority is pixels of projected error past the threshold, which is comparable across assets by
60
- * construction; a per-reader queue could not say it however truthfully the numbers compared.
61
- * - **Eviction, on §10's rule and design §6's order.** A page is evictable only while it has no
62
- * resident child pages (leaves-first falls out of that), the root page is never evictable, and
63
- * among the rest the oldest **last-touched** stamp goes first with a protect window no shorter
64
- * than feedback latency. LRU on last-*wanted* would freeze stamps at fetch time and evict what is
65
- * on screen.
66
- *
67
- * **Two of those three are the third implementation in this engine, and that is the honest way to
68
- * read them.** `VTPhysicalCache` is the near-identical precedent for eviction: a `#last_touch`
69
- * `Uint32Array` per slot, `touch(slot, frame)` and `last_touch(slot)`, a per-slot lock flag for
70
- * slots that must never be evicted — the analogue of pinning the root page here — and
71
- * `find_eviction_candidate(current_frame, protect_window)`, whose own doc justifies the window the
72
- * same way this class does: feedback readback runs a couple of frames behind, so recently touched
73
- * really means probably on screen. That convergence is neither coincidence nor independent
74
- * invention — design §6 pinned the rule and wrote "the VT system already keys on touch for this
75
- * reason" while doing it. The one thing that cache does differently is worth copying if this ever
76
- * gets hot: it picks by linear scan, on the argument that at ≤ ~1k slots a scan beats maintaining
77
- * a heap. For the fetch cap, `AssetManager.load_concurrency` and
78
- * `VirtualTextureManager.max_concurrent_loads` both predate it — being the third place to want it
79
- * is why the gate is now {@link ConcurrencyGate}, in `core/process/`, whose own doc says where it
80
- * repeats those two and why neither of them adopted it.
81
- *
82
- * **The first bullet is what has no precedent here.** Neither of those budgets *bytes* across
83
- * *several* readers. `VTPhysicalCache` budgets a count of fixed-size slots, which is the whole
84
- * reason its eviction can be a scan over a flat array; `AssetManager` budgets nothing at all —
85
- * `remove` and `clear` are manual and there is no eviction policy to compare against. Admitting a
86
- * page against a byte budget *before* the fetch, out of the `decoded_size` its parent's child
87
- * table already carries, is the part of this that had nothing to borrow from. So are the two
88
- * failure behaviours below: bounded retry with backoff into per-page quarantine, where
89
- * `AssetManager` only records a failure and lets a *new* `get` retry it, and per-fetch
90
- * `AbortSignal` cancellation, which neither precedent has.
91
- *
92
- * **Why these pages are not simply `AssetManager`'s problem**, given how much of this it already
93
- * does: an `AssetDescription` is a path and a type, two strings, one entry per thing loaded. A VG
94
- * page is a byte range inside a container that the format gives no global identifier at all — a
95
- * cluster is a page plus an index within it — and containers here run to 4,000 and 20,000 pages
96
- * apiece against a thousand open assets. The unit is wrong by orders of magnitude before anything
97
- * about the cache itself is considered.
98
- *
99
- * **What it does not do.** It does not decide what to want — that is the cut's job, and the wants
100
- * arrive here already ranked. It does not know about VRAM: design §6's third tier is a separate
101
- * budget over a separate pool, and this is the RAM tier. And it does not make a dropped page
102
- * incorrect: everything it evicts, the clamped cut covers with a coarser ancestor that is still
103
- * selectable, which is why a budget can be enforced at all.
104
- *
105
- * A runtime registering a reader should also set `VGeoReadOptions.verify_frames` on it. §10 requires
106
- * frame verification of a runtime reader and the reader defaults it off, because a tool reading a
107
- * file it just built is not defending against a network — but a manager holding a thousand streamed
108
- * assets is exactly the case the requirement is about, and verification is what lets the retries and
109
- * the quarantine below see corruption rather than only outages.
110
- *
111
- * @author Alex Goldring
112
- * @copyright Company Named Limited (c) 2026
113
- */
114
- export class VGeoResidencyManager {
115
- /**
116
- * @type {VGeoResidencyOptions}
117
- */
118
- #options;
119
-
120
- /**
121
- * @type {ConcurrencyGate}
122
- */
123
- #scheduler;
124
-
125
- /**
126
- * @type {Map<VGeoContainerReader, VGeoResidencyEntry>}
127
- */
128
- #readers = new Map();
129
-
130
- /**
131
- * @type {number}
132
- */
133
- #frame = 0;
134
-
135
- /**
136
- * Decoded bytes across every registered reader. Kept incrementally — see
137
- * {@link VGeoResidencyEntry.bytes} — and reconciled outright by {@link resync}.
138
- * @type {number}
139
- */
140
- #resident_bytes = 0;
141
-
142
- /**
143
- * @type {number}
144
- */
145
- #evicted_page_count = 0;
146
-
147
- /**
148
- * @type {number}
149
- */
150
- #refused_want_count = 0;
151
-
152
- /**
153
- * @param {VGeoResidencyOptions} [options]
154
- * @param {ConcurrencyGate} [scheduler] one is built from {@link options} when it is left out;
155
- * pass one to share a cap with something else that fetches
156
- */
157
- constructor(options = new VGeoResidencyOptions(), scheduler = undefined) {
158
- this.#options = options;
159
- this.#scheduler = scheduler ?? new ConcurrencyGate(options.max_concurrent_fetches);
160
- }
161
-
162
- /**
163
- * @returns {VGeoResidencyOptions}
164
- */
165
- get options() {
166
- return this.#options;
167
- }
168
-
169
- /**
170
- * @returns {ConcurrencyGate}
171
- */
172
- get scheduler() {
173
- return this.#scheduler;
174
- }
175
-
176
- /**
177
- * @returns {number}
178
- */
179
- get reader_count() {
180
- return this.#readers.size;
181
- }
182
-
183
- /**
184
- * @returns {number}
185
- */
186
- get frame() {
187
- return this.#frame;
188
- }
189
-
190
- /**
191
- * Decoded page bytes held by every registered reader together — the number the budget is spent
192
- * against.
193
- * @returns {number}
194
- */
195
- get resident_bytes() {
196
- return this.#resident_bytes;
197
- }
198
-
199
- /**
200
- * @returns {number}
201
- */
202
- get budget_bytes() {
203
- return this.#options.budget_bytes;
204
- }
205
-
206
- /**
207
- * @returns {number}
208
- */
209
- get resident_page_count() {
210
- let count = 0;
211
-
212
- for (const entry of this.#readers.values()) {
213
- count += entry.reader.resident_page_count;
214
- }
215
-
216
- return count;
217
- }
218
-
219
- /**
220
- * How many pages this manager has dropped. Telemetry, and the thing to watch when a scene is
221
- * thrashing: a page count that climbs faster than the frame count is a budget too small for the
222
- * cut being asked for.
223
- * @returns {number}
224
- */
225
- get evicted_page_count() {
226
- return this.#evicted_page_count;
227
- }
228
-
229
- /**
230
- * How many wants were refused because nothing could be evicted to make room for them.
231
- *
232
- * Not an error: a refused want is a page the next traversal will want again, from a cut that is
233
- * correct without it. A number that stays high is the budget saying it cannot hold the cut being
234
- * asked for, which is a τ or a budget decision and not a streaming one.
235
- *
236
- * @returns {number}
237
- */
238
- get refused_want_count() {
239
- return this.#refused_want_count;
240
- }
241
-
242
- /**
243
- * Take a reader under the budget.
244
- *
245
- * @param {VGeoContainerReader} reader
246
- * @returns {VGeoContainerReader} the same reader, so this can wrap an `open`
247
- */
248
- register(reader) {
249
- assert.defined(reader, 'reader');
250
-
251
- if (this.#readers.has(reader)) {
252
- return reader;
253
- }
254
-
255
- const entry = new VGeoResidencyEntry(reader);
256
-
257
- entry.bytes = reader.resident_bytes;
258
-
259
- for (const page of reader.resident_pages()) {
260
- entry.touched.set(page.index, this.#frame);
261
- }
262
-
263
- this.#readers.set(reader, entry);
264
- this.#resident_bytes += entry.bytes;
265
-
266
- return reader;
267
- }
268
-
269
- /**
270
- * Stop counting a reader's pages against the budget.
271
- *
272
- * The reader keeps whatever it holds — dropping it is the caller's business, and a reader being
273
- * unregistered is usually one about to be discarded whole.
274
- *
275
- * @param {VGeoContainerReader} reader
276
- * @returns {boolean} whether it was registered
277
- */
278
- unregister(reader) {
279
- const entry = this.#readers.get(reader);
280
-
281
- if (entry === undefined) {
282
- return false;
283
- }
284
-
285
- this.#resident_bytes -= entry.bytes;
286
-
287
- this.#readers.delete(reader);
288
-
289
- return true;
290
- }
291
-
292
- /**
293
- * @param {VGeoContainerReader} reader
294
- * @returns {boolean}
295
- */
296
- is_registered(reader) {
297
- return this.#readers.has(reader);
298
- }
299
-
300
- /**
301
- * Advance the frame counter, and reconcile the byte total with what the readers actually hold.
302
- *
303
- * Call it once a frame. The reconciliation is what keeps the budget honest when something
304
- * installed or evicted without going through here — which a tool, a test, or a playground
305
- * button all legitimately do.
306
- *
307
- * @returns {number} the new frame number
308
- */
309
- advance_frame() {
310
- this.#frame++;
311
-
312
- this.resync();
313
-
314
- return this.#frame;
315
- }
316
-
317
- /**
318
- * Re-read every registered reader's `resident_bytes`, and forget touch stamps for pages that are
319
- * no longer resident.
320
- *
321
- * @returns {number} the reconciled total
322
- */
323
- resync() {
324
- let total = 0;
325
-
326
- for (const entry of this.#readers.values()) {
327
- entry.bytes = entry.reader.resident_bytes;
328
-
329
- total += entry.bytes;
330
-
331
- if (entry.touched.size > entry.reader.resident_page_count) {
332
- for (const page_index of Array.from(entry.touched.keys())) {
333
- if (!entry.reader.is_resident(page_index)) {
334
- entry.touched.delete(page_index);
335
- }
336
- }
337
- }
338
- }
339
-
340
- this.#resident_bytes = total;
341
-
342
- return total;
343
- }
344
-
345
- /**
346
- * Record that a page was selected from this frame — design §6's touch, which is what eviction
347
- * orders on.
348
- *
349
- * @param {VGeoContainerReader} reader
350
- * @param {number} page_index
351
- */
352
- touch(reader, page_index) {
353
- const entry = this.#readers.get(reader);
354
-
355
- if (entry === undefined) {
356
- return;
357
- }
358
-
359
- entry.touched.set(page_index, this.#frame);
360
- }
361
-
362
- /**
363
- * Record every page of a cut as touched.
364
- *
365
- * @param {VGeoContainerReader} reader
366
- * @param {Iterable<number>} page_indices
367
- */
368
- touch_all(reader, page_indices) {
369
- const entry = this.#readers.get(reader);
370
-
371
- if (entry === undefined) {
372
- return;
373
- }
374
-
375
- for (const page_index of page_indices) {
376
- entry.touched.set(page_index, this.#frame);
377
- }
378
- }
379
-
380
- /**
381
- * The frame a page was last touched on, or `-1` for one nothing has reported.
382
- *
383
- * @param {VGeoContainerReader} reader
384
- * @param {number} page_index
385
- * @returns {number}
386
- */
387
- last_touched(reader, page_index) {
388
- return this.#readers.get(reader)?.touched.get(page_index) ?? -1;
389
- }
390
-
391
- /**
392
- * Whether a page may be dropped right now — §10's eviction rule, plus design §6's protect
393
- * window.
394
- *
395
- * @param {VGeoContainerReader} reader
396
- * @param {number} page_index
397
- * @returns {boolean}
398
- */
399
- evictable(reader, page_index) {
400
- const entry = this.#readers.get(reader);
401
-
402
- if (entry === undefined || !reader.is_resident(page_index)) {
403
- return false;
404
- }
405
-
406
- // §10: the root page is pinned, because every traversal starts by walking its parentless
407
- // groups and a cut with no entry point is not a coarser cut, it is no cut
408
- if (page_index === 0) {
409
- return false;
410
- }
411
-
412
- // §10: within a tier, a page is evictable only while it has no resident child pages —
413
- // checked against its own child table. Leaves-first order is a consequence, not a rule.
414
- if (reader.resident_child_count(page_index) > 0) {
415
- return false;
416
- }
417
-
418
- const touched = entry.touched.get(page_index);
419
-
420
- if (touched !== undefined && this.#frame - touched < this.#options.protect_frames) {
421
- return false;
422
- }
423
-
424
- return true;
425
- }
426
-
427
- /**
428
- * Drop pages until the budget has room for `headroom` more bytes.
429
- *
430
- * Oldest touch first, and only pages the rule allows — so the first pass finds leaves, dropping
431
- * them makes their parents evictable, and the next pass finds those. Dropping a page can only
432
- * ever make another page *more* evictable, never less, which is what lets each pass reuse the
433
- * ordering it built.
434
- *
435
- * @param {number} [headroom] bytes to leave room for on top of the budget's own limit
436
- * @returns {number} pages dropped
437
- */
438
- evict_to_budget(headroom = 0) {
439
- const budget = this.#options.budget_bytes;
440
-
441
- let dropped = 0;
442
-
443
- for (;;) {
444
- if (this.#resident_bytes + headroom <= budget) {
445
- return dropped;
446
- }
447
-
448
- const candidates = this.#candidates();
449
-
450
- if (candidates.length === 0) {
451
- return dropped;
452
- }
453
-
454
- candidates.sort((a, b) => a.touched - b.touched);
455
-
456
- let this_pass = 0;
457
-
458
- for (const candidate of candidates) {
459
- if (this.#resident_bytes + headroom <= budget) {
460
- break;
461
- }
462
-
463
- if (!this.evictable(candidate.entry.reader, candidate.page_index)) {
464
- continue;
465
- }
466
-
467
- this.#drop(candidate.entry, candidate.page_index);
468
-
469
- dropped++;
470
- this_pass++;
471
- }
472
-
473
- if (this_pass === 0) {
474
- return dropped;
475
- }
476
- }
477
- }
478
-
479
- /**
480
- * Every page that could be dropped right now, with its touch stamp.
481
- *
482
- * @returns {{entry: VGeoResidencyEntry, page_index: number, touched: number}[]}
483
- */
484
- #candidates() {
485
- const out = [];
486
-
487
- for (const entry of this.#readers.values()) {
488
- const reader = entry.reader;
489
-
490
- for (const page of reader.resident_pages()) {
491
- if (!this.evictable(reader, page.index)) {
492
- continue;
493
- }
494
-
495
- out.push({
496
- entry,
497
- page_index: page.index,
498
- // a page nothing has reported is as cold as it gets, and is the right first
499
- // victim: it arrived, and no cut has selected from it since
500
- touched: entry.touched.get(page.index) ?? -1,
501
- });
502
- }
503
- }
504
-
505
- return out;
506
- }
507
-
508
- /**
509
- * @param {VGeoResidencyEntry} entry
510
- * @param {number} page_index
511
- */
512
- #drop(entry, page_index) {
513
- if (!entry.reader.evict(page_index)) {
514
- return;
515
- }
516
-
517
- entry.touched.delete(page_index);
518
-
519
- this.#reconcile(entry);
520
-
521
- this.#evicted_page_count++;
522
- }
523
-
524
- /**
525
- * Pull the total back onto what one reader now says it holds.
526
- *
527
- * @param {VGeoResidencyEntry} entry
528
- */
529
- #reconcile(entry) {
530
- const now = entry.reader.resident_bytes;
531
-
532
- this.#resident_bytes += now - entry.bytes;
533
-
534
- entry.bytes = now;
535
- }
536
-
537
- /**
538
- * Install a reader's wants, under the shared fetch cap and the shared byte budget.
539
- *
540
- * This is the runtime's entry point, and what it does that `VGeoContainerReader.install_wants`
541
- * cannot: every fetch queues on one scheduler shared with every other reader, so priority is
542
- * compared across assets and the in-flight total is bounded; and every page is admitted against
543
- * the budget *before* it is fetched, out of the `decoded_size` its parent's child table already
544
- * carries — so a page that will not fit is never paid for.
545
- *
546
- * A want that cannot be admitted is dropped, not queued. It is not a failure: the cut that
547
- * produced it is correct without the page, and the next traversal will want it again — by which
548
- * time something may have gone cold enough to evict. {@link refused_want_count} counts them.
549
- *
550
- * A want with no `decoded_size` is admitted blind and the budget is enforced after the fact, at
551
- * the end of this call. Wants that came from a cut always carry it.
552
- *
553
- * @param {VGeoContainerReader} reader must be registered
554
- * @param {VGeoPageWant[]} wants
555
- * @param {number} [limit] most pages to install this call
556
- * @param {AbortSignal} [signal] stops admitting, and abandons what this reader has in flight
557
- * @returns {Promise<VGeoPage[]>} the pages installed, in completion order
558
- */
559
- async install_wants(reader, wants, limit = wants.length, signal = undefined) {
560
- const entry = this.#readers.get(reader);
561
-
562
- assert.defined(entry, 'the reader must be registered before its wants can be installed');
563
-
564
- // Ranked before it is cut, so a limit takes the most visibly coarse pages rather than an
565
- // arbitrary prefix — and deduplicated on page index, because two clusters wanting one page
566
- // is one fetch (§10) and two admissions would double-charge the budget for it.
567
- const queue = [];
568
- const seen = new Set();
569
-
570
- for (const want of wants.slice().sort((a, b) => b.priority - a.priority)) {
571
- if (seen.has(want.page_index)) {
572
- continue;
573
- }
574
-
575
- seen.add(want.page_index);
576
-
577
- queue.push(want);
578
-
579
- if (queue.length >= limit) {
580
- break;
581
- }
582
- }
583
-
584
- const installed = [];
585
-
586
- /**
587
- * Pages this call has on the wire, so that aborting it abandons those and not whatever
588
- * another view of another asset started through the same reader.
589
- * @type {Set<number>}
590
- */
591
- const mine = new Set();
592
-
593
- const abandon = () => {
594
- for (const page_index of mine) {
595
- reader.cancel(page_index);
596
- }
597
- };
598
-
599
- signal?.addEventListener('abort', abandon, { once: true });
600
-
601
- try {
602
- await Promise.all(queue.map((want) => this.#scheduler.submit(
603
- want.priority, () => this.#install_one(entry, want, signal, installed, mine)
604
- )));
605
- } finally {
606
- signal?.removeEventListener('abort', abandon);
607
- }
608
-
609
- // The backstop for wants that carried no `decoded_size`, and for anything that installed
610
- // outside this call while it was running.
611
- this.evict_to_budget();
612
-
613
- return installed;
614
- }
615
-
616
- /**
617
- * @param {VGeoResidencyEntry} entry
618
- * @param {VGeoPageWant} want
619
- * @param {AbortSignal|undefined} signal
620
- * @param {VGeoPage[]} installed
621
- * @param {Set<number>} in_flight the calling batch's own, for a scoped abort
622
- * @returns {Promise<void>}
623
- */
624
- async #install_one(entry, want, signal, installed, in_flight) {
625
- if (signal !== undefined && signal.aborted) {
626
- return;
627
- }
628
-
629
- const reader = entry.reader;
630
-
631
- const resident = reader.page(want.page_index);
632
-
633
- if (resident !== undefined) {
634
- // §10: a want whose target page is already installed resolves as an activation recheck,
635
- // never a fetch — and the reader has already done that recheck. What is left is the
636
- // touch, which is what keeps the page from being the next thing evicted.
637
- entry.touched.set(want.page_index, this.#frame);
638
-
639
- return;
640
- }
641
-
642
- if (reader.is_quarantined(want.page_index)) {
643
- return;
644
- }
645
-
646
- if (!this.#admit(want.decoded_size)) {
647
- this.#refused_want_count++;
648
-
649
- return;
650
- }
651
-
652
- in_flight.add(want.page_index);
653
-
654
- try {
655
- const page = await reader.install_want(want);
656
-
657
- entry.touched.set(page.index, this.#frame);
658
-
659
- this.#reconcile(entry);
660
-
661
- installed.push(page);
662
- } catch {
663
- // quarantined, or cancelled. Either way the cut stands without it, and the reason is on
664
- // the reader's quarantine for whoever is watching telemetry.
665
- this.#reconcile(entry);
666
- } finally {
667
- in_flight.delete(want.page_index);
668
- }
669
- }
670
-
671
- /**
672
- * Whether the budget can take `bytes` more, after evicting what it can.
673
- *
674
- * @param {number} bytes
675
- * @returns {boolean}
676
- */
677
- #admit(bytes) {
678
- const budget = this.#options.budget_bytes;
679
-
680
- if (this.#resident_bytes + bytes <= budget) {
681
- return true;
682
- }
683
-
684
- this.evict_to_budget(bytes);
685
-
686
- return this.#resident_bytes + bytes <= budget;
687
- }
688
-
689
- /**
690
- * Let every quarantined page of every registered reader be asked for again.
691
- *
692
- * The policy half of §10's quarantine, and the reason it is here rather than on the reader: a
693
- * reader that failed three fetches of one page cannot tell an unreachable CDN from a corrupt
694
- * frame, and a manager that has watched other pages of other assets arrive since can. A caller
695
- * that has just come back online, or that is retrying a whole scene, calls this; nothing calls it
696
- * on a timer, because a checksum that failed once will fail again and the retry costs a fetch.
697
- *
698
- * @returns {number} how many pages were released, across every reader
699
- */
700
- clear_quarantines() {
701
- let released = 0;
702
-
703
- for (const entry of this.#readers.values()) {
704
- released += entry.reader.clear_quarantine();
705
- }
706
-
707
- return released;
708
- }
709
-
710
- /**
711
- * How many pages are quarantined across every registered reader.
712
- * @returns {number}
713
- */
714
- get quarantined_page_count() {
715
- let count = 0;
716
-
717
- for (const entry of this.#readers.values()) {
718
- count += entry.reader.quarantined_page_count;
719
- }
720
-
721
- return count;
722
- }
723
- }