@woosh/meep-engine 3.27.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 (255) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/core/geom/3d/mat4/m4_linear_determinant.d.ts +15 -0
  4. package/src/core/geom/3d/mat4/m4_linear_determinant.d.ts.map +1 -0
  5. package/src/core/geom/3d/mat4/m4_linear_determinant.js +24 -0
  6. package/src/core/geom/3d/mat4/m4_normal_matrix3.d.ts +4 -2
  7. package/src/core/geom/3d/mat4/m4_normal_matrix3.d.ts.map +1 -1
  8. package/src/core/geom/3d/mat4/m4_normal_matrix3.js +21 -11
  9. package/src/engine/Engine.d.ts.map +1 -1
  10. package/src/engine/Engine.js +38 -1
  11. package/src/engine/graphics3/GraphicsEngine.d.ts +5 -1
  12. package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -1
  13. package/src/engine/graphics3/GraphicsEngine.js +834 -830
  14. package/src/shade/RENDERER_CONTRACT.md +19 -10
  15. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +1 -1
  16. package/src/shade/playground/vgeo_runtime/README.md +27 -13
  17. package/src/shade/playground/vgeo_runtime/main.js +18 -5
  18. package/src/shade/playground/vgeo_runtime/sample_asset.d.ts +18 -0
  19. package/src/shade/playground/vgeo_runtime/sample_asset.d.ts.map +1 -0
  20. package/src/shade/playground/{vgeo_viewer → vgeo_runtime}/sample_asset.js +9 -8
  21. package/src/shade/playground/vgeo_scene/README.md +408 -0
  22. package/src/shade/playground/vgeo_scene/index.html +144 -0
  23. package/src/shade/playground/vgeo_scene/main.d.ts +2 -0
  24. package/src/shade/playground/vgeo_scene/main.d.ts.map +1 -0
  25. package/src/shade/playground/vgeo_scene/main.js +1716 -0
  26. package/src/shade/renderer/Renderer.d.ts +4 -1
  27. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  28. package/src/shade/renderer/Renderer.js +5 -2
  29. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +1 -2
  30. package/src/shade/renderer/geometry/meshlet/MeshletBatch.d.ts.map +1 -1
  31. package/src/shade/renderer/geometry/meshlet/MeshletBatch.js +3 -29
  32. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.d.ts +22 -0
  33. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.d.ts.map +1 -0
  34. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.js +49 -0
  35. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +4 -2
  36. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +10 -8
  37. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +12 -11
  38. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +1 -1
  39. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
  40. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +1 -1
  41. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.d.ts.map +1 -1
  42. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.js +5 -9
  43. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevel.d.ts +32 -0
  44. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevel.d.ts.map +1 -0
  45. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts +59 -0
  46. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts.map +1 -0
  47. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelSummary.d.ts +44 -0
  48. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelSummary.d.ts.map +1 -0
  49. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts +36 -0
  50. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts.map +1 -0
  51. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_levels.js → level/vgeo_build_levels.js} +61 -95
  52. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_root_group.d.ts +19 -0
  53. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_root_group.d.ts.map +1 -0
  54. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_root_group.js → level/vgeo_build_root_group.js} +1 -1
  55. package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts +73 -0
  56. package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts.map +1 -0
  57. package/src/shade/renderer/geometry/virtual/build/{VirtualGeometryMesh.js → mesh/VirtualGeometryMesh.js} +7 -7
  58. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +29 -0
  59. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -0
  60. package/src/shade/renderer/geometry/virtual/build/{vgeo_compute_vertex_normals.js → mesh/vgeo_compute_vertex_normals.js} +3 -3
  61. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts +31 -0
  62. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts.map +1 -0
  63. package/src/shade/renderer/geometry/virtual/build/{vgeo_compute_vertex_tangents.js → mesh/vgeo_compute_vertex_tangents.js} +5 -5
  64. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts +46 -0
  65. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -0
  66. package/src/shade/renderer/geometry/virtual/build/{vgeo_normalize_source.js → mesh/vgeo_normalize_source.js} +9 -9
  67. package/src/shade/renderer/geometry/virtual/build/page/vgeo_assemble_pages.d.ts +13 -0
  68. package/src/shade/renderer/geometry/virtual/build/page/vgeo_assemble_pages.d.ts.map +1 -0
  69. package/src/shade/renderer/geometry/virtual/build/{vgeo_assemble_pages.js → page/vgeo_assemble_pages.js} +9 -9
  70. package/src/shade/renderer/geometry/virtual/build/page/vgeo_build_page_nodes.d.ts +13 -0
  71. package/src/shade/renderer/geometry/virtual/build/page/vgeo_build_page_nodes.d.ts.map +1 -0
  72. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_page_nodes.js → page/vgeo_build_page_nodes.js} +4 -4
  73. package/src/shade/renderer/geometry/virtual/build/page/vgeo_page_node_count.d.ts +22 -0
  74. package/src/shade/renderer/geometry/virtual/build/page/vgeo_page_node_count.d.ts.map +1 -0
  75. package/src/shade/renderer/geometry/virtual/build/{vgeo_page_node_count.js → page/vgeo_page_node_count.js} +2 -2
  76. package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts +52 -0
  77. package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts.map +1 -0
  78. package/src/shade/renderer/geometry/virtual/build/{VGeoFaceGraph.js → partition/VGeoFaceGraph.js} +1 -1
  79. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.d.ts +14 -0
  80. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.d.ts.map +1 -0
  81. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_graph.d.ts +29 -0
  82. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_graph.d.ts.map +1 -0
  83. package/src/shade/renderer/geometry/virtual/build/{vgeo_partition_graph.js → partition/vgeo_partition_graph.js} +2 -2
  84. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.d.ts +41 -0
  85. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.d.ts.map +1 -0
  86. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.js +92 -0
  87. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.d.ts +10 -0
  88. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.d.ts.map +1 -0
  89. package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set.js → partition/vgeo_split_face_set.js} +12 -55
  90. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts +15 -0
  91. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts.map +1 -0
  92. package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set_packed.js → partition/vgeo_split_face_set_packed.js} +3 -3
  93. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_bounding_sphere.d.ts +12 -0
  94. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_bounding_sphere.d.ts.map +1 -0
  95. package/src/shade/renderer/geometry/virtual/build/{vgeo_bounding_sphere.js → sphere/vgeo_bounding_sphere.js} +3 -3
  96. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_containing_sphere.d.ts +18 -0
  97. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_containing_sphere.d.ts.map +1 -0
  98. package/src/shade/renderer/geometry/virtual/build/{vgeo_containing_sphere.js → sphere/vgeo_containing_sphere.js} +3 -3
  99. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_sphere_inflate_to_contain.d.ts +9 -0
  100. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_sphere_inflate_to_contain.d.ts.map +1 -0
  101. package/src/shade/renderer/geometry/virtual/build/{vgeo_sphere_inflate_to_contain.js → sphere/vgeo_sphere_inflate_to_contain.js} +1 -1
  102. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts +24 -0
  103. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts.map +1 -0
  104. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts +20 -0
  105. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts.map +1 -0
  106. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts +14 -0
  107. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts.map +1 -0
  108. package/src/shade/renderer/geometry/virtual/build/{vgeo_extract_submesh.js → submesh/vgeo_extract_submesh.js} +5 -5
  109. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts +12 -0
  110. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts.map +1 -0
  111. package/src/shade/renderer/geometry/virtual/build/{vgeo_gather_faces.js → submesh/vgeo_gather_faces.js} +2 -2
  112. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_narrow_indices.d.ts +11 -0
  113. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_narrow_indices.d.ts.map +1 -0
  114. package/src/shade/renderer/geometry/virtual/build/{vgeo_narrow_indices.js → submesh/vgeo_narrow_indices.js} +3 -3
  115. package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts.map +1 -1
  116. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +9 -11
  117. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.d.ts +8 -2
  118. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.d.ts.map +1 -1
  119. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js +8 -2
  120. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.d.ts +15 -0
  121. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.d.ts.map +1 -0
  122. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.js +15 -0
  123. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts +28 -0
  124. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts.map +1 -1
  125. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +7 -3
  126. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
  127. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +9 -4
  128. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
  129. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +18 -5
  130. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
  131. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +9 -1
  132. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +21 -2
  133. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +52 -2
  134. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
  135. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +97 -1
  136. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +4 -4
  137. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -1
  138. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +10 -0
  139. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -1
  140. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +59 -34
  141. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts +8 -8
  142. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -1
  143. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +11 -88
  144. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts +19 -0
  145. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts.map +1 -0
  146. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.js +98 -0
  147. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts +31 -0
  148. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts.map +1 -0
  149. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.js +99 -0
  150. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.d.ts +17 -0
  151. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.d.ts.map +1 -0
  152. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.js +39 -0
  153. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts.map +1 -1
  154. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.js +7 -63
  155. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts.map +1 -1
  156. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js +13 -30
  157. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -1
  158. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +6 -52
  159. package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
  160. package/src/shade/renderer/loader/gltf/load_gltf.js +0 -19
  161. package/src/shade/renderer/material/chunk_material_draw_side.d.ts +14 -0
  162. package/src/shade/renderer/material/chunk_material_draw_side.d.ts.map +1 -0
  163. package/src/shade/renderer/material/chunk_material_draw_side.js +34 -0
  164. package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts.map +1 -1
  165. package/src/shade/renderer/material/standard/fragment_gbuffer.js +8 -1
  166. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
  167. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +13 -1
  168. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts +19 -5
  169. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts.map +1 -1
  170. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +23 -5
  171. package/src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.d.ts.map +1 -1
  172. package/src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.js +12 -5
  173. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.d.ts +22 -0
  174. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.d.ts.map +1 -0
  175. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.js +36 -0
  176. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts +15 -0
  177. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts.map +1 -0
  178. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.js +35 -0
  179. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts +10 -1
  180. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts.map +1 -1
  181. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.js +38 -29
  182. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.d.ts +5 -0
  183. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.d.ts.map +1 -1
  184. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.js +63 -55
  185. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts.map +1 -1
  186. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.js +1 -0
  187. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  188. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +44 -53
  189. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.d.ts.map +1 -1
  190. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.js +6 -2
  191. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.d.ts.map +1 -1
  192. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.js +6 -2
  193. package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.d.ts.map +1 -1
  194. package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.js +19 -2
  195. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +7 -4
  196. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  197. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +3 -2
  198. package/src/shade/renderer/rasterize/native/chunk_rasterize_triangle.d.ts.map +1 -1
  199. package/src/shade/renderer/rasterize/native/chunk_rasterize_triangle.js +15 -4
  200. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
  201. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.js +16 -5
  202. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.d.ts.map +1 -1
  203. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.js +16 -5
  204. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts.map +1 -1
  205. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +2 -1
  206. package/src/shade/renderer/rasterize/native/rasterization_shader_resources.d.ts.map +1 -1
  207. package/src/shade/renderer/rasterize/native/rasterization_shader_resources.js +4 -0
  208. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
  209. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.js +16 -7
  210. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  211. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +78 -88
  212. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  213. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +21 -31
  214. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  215. package/src/shade/renderer/scene/GPUSceneContext.js +2 -0
  216. package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts +19 -0
  217. package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts.map +1 -1
  218. package/src/shade/renderer/scene/NODE3D_STRUCT.js +34 -0
  219. package/src/shade/renderer/scene/chunk_node3d_flags.d.ts +9 -0
  220. package/src/shade/renderer/scene/chunk_node3d_flags.d.ts.map +1 -0
  221. package/src/shade/renderer/scene/chunk_node3d_flags.js +23 -0
  222. package/src/shade/renderer/scene/chunk_scene_database_access.d.ts.map +1 -1
  223. package/src/shade/renderer/scene/chunk_scene_database_access.js +15 -1
  224. package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
  225. package/src/shade/renderer/scene/rows/GPUSceneRows.js +9 -1
  226. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.d.ts +7 -0
  227. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.d.ts.map +1 -1
  228. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.js +16 -4
  229. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts +12 -0
  230. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts.map +1 -0
  231. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.js +18 -0
  232. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.d.ts +24 -0
  233. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.d.ts.map +1 -0
  234. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.js +33 -0
  235. package/src/shade/playground/vgeo_viewer/README.md +0 -150
  236. package/src/shade/playground/vgeo_viewer/cut_geometry.js +0 -460
  237. package/src/shade/playground/vgeo_viewer/index.html +0 -77
  238. package/src/shade/playground/vgeo_viewer/main.js +0 -1343
  239. package/src/shade/playground/vgeo_viewer/select_cut.js +0 -481
  240. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts +0 -258
  241. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts.map +0 -1
  242. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts +0 -49
  243. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts.map +0 -1
  244. package/src/shade/renderer/loader/gltf/fix_up_material_sides.js +0 -109
  245. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.js +0 -78
  246. package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.js +0 -100
  247. package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.js +0 -26
  248. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.d.ts +0 -3
  249. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.d.ts.map +0 -1
  250. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevel.js → level/VGeoLevel.js} +0 -0
  251. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevelOptions.js → level/VGeoLevelOptions.js} +0 -0
  252. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevelSummary.js → level/VGeoLevelSummary.js} +0 -0
  253. /package/src/shade/renderer/geometry/virtual/build/{vgeo_graph_components.js → partition/vgeo_graph_components.js} +0 -0
  254. /package/src/shade/renderer/geometry/virtual/build/{VGeoGatheredFaces.js → submesh/VGeoGatheredFaces.js} +0 -0
  255. /package/src/shade/renderer/geometry/virtual/build/{VGeoSubmesh.js → submesh/VGeoSubmesh.js} +0 -0
@@ -39,7 +39,7 @@ held to `vgeo_select_cut` exactly, and `playground/vgeo_runtime/` runs it on a d
39
39
 
40
40
  Everything below the runtime was already built when this was written. The builder produces a DAG,
41
41
  the container holds it, the reader streams it a page at a time with residency and activation,
42
- `VGeoResidencyManager` owns the RAM budget, and `playground/vgeo_viewer/select_cut.js` selects a
42
+ `VGeoResidencyManager` owns the RAM budget, and `runtime/cut/__fixtures__/vgeo_select_cut.js` selects a
43
43
  correct cut on the CPU against a partially resident container. What had never run was any of it on
44
44
  the GPU, and the one piece of M0 that is engine-facing — the expansion seam — had never been built
45
45
  at all.
@@ -76,7 +76,7 @@ no cluster the flat rule would draw is missed.
76
76
  | `GPUMeshletManager` | data + metadata pools, `add_batch`/`remove_batch` | the VRAM tier's storage; format §8 makes a page payload a **copy** into the data pool |
77
77
  | `MESHLET_DEFINITION_STRUCT` | `(meshlet index, mesh index)` | the cut's output record type — unchanged, which is what makes the seam one call |
78
78
  | `graph_expand_meshes_to_meshlets` | four call sites, three of them opaque | the seam: a VG expansion runs beside each opaque one and appends into the same collection (§5) |
79
- | `playground/vgeo_viewer/select_cut.js` | the rule on the CPU | **the oracle**. The GPU cut is held to agreeing with it exactly |
79
+ | `cut/__fixtures__/vgeo_select_cut.js` | the rule on the CPU | **the oracle**. The GPU cut is held to agreeing with it exactly |
80
80
 
81
81
  ## 2. Phase 1 — the page decode has holes, and they are load-bearing
82
82
 
@@ -287,7 +287,7 @@ is resident but not selectable — its clusters' parents are split across pages
287
287
  pages has not arrived. The clamp draws `C`'s parent `P`, correctly, standing in over that surface.
288
288
  But `C` itself satisfies §4's form — `project(C.parent) > τ` and `project(C.self) ≤ τ` — so a
289
289
  flat evaluation draws it as well, underneath `P`, and the depth buffer picks between two surfaces
290
- a few microns apart. This is exactly the failure `select_cut.spec.js` was written to catch, and
290
+ a few microns apart. This is exactly the failure `vgeo_select_cut.spec.js` was written to catch, and
291
291
  the CPU traversal never meets it because *not entering a non-selectable group* is how it descends.
292
292
  A predicate has to say so out loud:
293
293
 
@@ -535,7 +535,7 @@ Four tiers, in the order they catch things.
535
535
  pages withheld. Multiset, not set: a duplicate is the failure mode that reads as corrupt
536
536
  geometry rather than as a missing page, and a set comparison cannot see it.
537
537
 
538
- **At 64 KB pages, not the 8 KB `select_cut.spec.js` uses** (REVIEW_LEDGER D2). The 8 KB fixture
538
+ **At 64 KB pages, not the 8 KB `vgeo_select_cut.spec.js` uses** (REVIEW_LEDGER D2). The 8 KB fixture
539
539
  is the one that puts a group's parents on either side of a page boundary thirteen times over,
540
540
  which is what *that* spec is for — and it is the one fixture in which this spec's headline case
541
541
  cannot occur: measured, 8 KB puts exactly one group in every page of this torus, so a blocked
@@ -1 +1 @@
1
- {"version":3,"file":"VGeoFeedbackLoop.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js"],"names":[],"mappings":"AAKA;;;;;GAKG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH;IAqCI;;;;;;OAMG;IACH;QAL8C,OAAO,EAA1C,yBAAyB;QACF,SAAS,EAAhC,aAAa;QACI,aAAa,GAA9B,MAAM;OAUhB;IAED;;OAEG;IACH,4DAEC;IAED;;OAEG;IACH,yBAEC;IAED;;;;;;;OAOG;IACH,6BAEC;IAED;;OAEG;IACH,yBAEC;IAED;;OAEG;IACH,8BAEC;IAED;;OAEG;IACH,+BAEC;IAED;;;OAGG;IACH,iCAEC;IAED;;;;;OAKG;IACH,YAHW,UAAU,GACR,OAAO,CAuDnB;IAeD;;;;OAIG;IACH,SAFa,IAAI,CAIhB;IAED;;;;;;;OAOG;IACH,WAFa,IAAI,CAIhB;;CACJ;yBAjOY,OAAO,iEAAiE,EAAE,UAAU;wCACpF,OAAO,gCAAgC,EAAE,yBAAyB;4BAClE,OAAO,oBAAoB,EAAE,aAAa"}
1
+ {"version":3,"file":"VGeoFeedbackLoop.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js"],"names":[],"mappings":"AAMA;;;;;GAKG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH;IAqCI;;;;;;OAMG;IACH;QAL8C,OAAO,EAA1C,yBAAyB;QACF,SAAS,EAAhC,aAAa;QACI,aAAa,GAA9B,MAAM;OAUhB;IAED;;OAEG;IACH,4DAEC;IAED;;OAEG;IACH,yBAEC;IAED;;;;;;;OAOG;IACH,6BAEC;IAED;;OAEG;IACH,yBAEC;IAED;;OAEG;IACH,8BAEC;IAED;;OAEG;IACH,+BAEC;IAED;;;OAGG;IACH,iCAEC;IAED;;;;;OAKG;IACH,YAHW,UAAU,GACR,OAAO,CAuDnB;IAwBD;;;;OAIG;IACH,SAFa,IAAI,CAIhB;IAED;;;;;;;OAOG;IACH,WAFa,IAAI,CAIhB;;CACJ;yBA1OY,OAAO,iEAAiE,EAAE,UAAU;wCACpF,OAAO,gCAAgC,EAAE,yBAAyB;4BAClE,OAAO,oBAAoB,EAAE,aAAa"}
@@ -1,5 +1,6 @@
1
1
  import { assert } from "../../../../../core/assert.js";
2
2
  import { graph_read_buffer } from "../../../buffer/graph_read_buffer.js";
3
+ import { VGEO_CUT_STAT } from "./layout/VGEO_CUT_STAT.js";
3
4
  import { VGEO_CUT_STAT_COUNT } from "./layout/VGEO_CUT_STAT_COUNT.js";
4
5
  import { VGEO_FEEDBACK_OFFSET } from "./layout/VGEO_FEEDBACK_OFFSET.js";
5
6
 
@@ -206,6 +207,15 @@ export class VGeoFeedbackLoop {
206
207
  this.#stats[i] = words[VGEO_FEEDBACK_OFFSET.STATS + i];
207
208
  }
208
209
 
210
+ /*
211
+ One counter is not a statistic, it is an instruction. `QUEUE_REMAINING` is non-zero exactly
212
+ when the descent ran out of wavefronts and abandoned subtrees it had not expanded — a hole
213
+ in the picture rather than a coarser surface — and this is the only moment anything on the
214
+ host learns it. The runtime owns what to do about it; the loop's job here is the same as
215
+ with a want, which is to carry the reading to whoever it is about.
216
+ */
217
+ this.#runtime.note_incomplete_descent(this.#stats[VGEO_CUT_STAT.QUEUE_REMAINING]);
218
+
209
219
  this.#residency.apply(words, layout, reading_frame);
210
220
  }
211
221
 
@@ -1 +1 @@
1
- {"version":3,"file":"VGeoResidency.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js"],"names":[],"mappings":"AA+FA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH;IAwJI;;;;;;;OAOG;IACH;QAN2C,IAAI,EAApC,sBAAsB;QACS,OAAO,GAAtC,oBAAoB;QACM,IAAI,GAA9B,eAAe;QACW,OAAO,GAAjC,eAAe;OA8BzB;IAID;;OAEG;IACH,oCAEC;IAED;;OAEG;IACH,4BAEC;IAED;;OAEG;IACH,+BAEC;IAED;;OAEG;IACH,yEAEC;IAED;;;;OAIG;IACH,oBAEC;IAED;;;;OAIG;IACH,gCAGC;IAED,+BAEC;IAED,iCAEC;IAED,6BAEC;IAED,2BAEC;IAED,iCAEC;IAED,gCAEC;IAED;;;;OAIG;IACH,oCAEC;IAED;;;OAGG;IACH,kCAEC;IAED;;;OAGG;IACH,kCAEC;IAED;;;OAGG;IACH,iCAEC;IAED;;;OAGG;IACH,iCAEC;IAED;;;OAGG;IACH,gCAEC;IAED;;;;;OAKG;IACH,gCAEC;IAED;;;OAGG;IACH,iCAEC;IAED,gCAEC;IAED,+BAEC;IAED,8BAEC;IAED;;OAEG;IACH,qCAQC;IAID;;;;;;;;;;OAUG;IACH,iBAHW,mBAAmB,GACjB,MAAM,CA8ClB;IAED;;;;;;OAMG;IACH,mBAHW,mBAAmB,GACjB,OAAO,CAgBnB;IAED;;;OAGG;IACH,sBAHW,mBAAmB,GACjB,OAAO,CAInB;IAED;;;;;;OAMG;IACH,cAJW,mBAAmB,cACnB,MAAM,GACJ,QAAQ,GAAC,UAAU,GAAC,aAAa,GAAC,KAAK,GAAC,MAAM,GAAC,cAAc,CAqBzE;IAgCD;;;;OAIG;IACH,SAFa,IAAI,CAOhB;IAED;;;;OAIG;IACH,WAFa,IAAI,CAUhB;IAED;;;;OAIG;IACH,qBAFa,MAAM,CAkBlB;IAID;;;;;;;;;OASG;IACH,aANW,WAAW,UACX;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAChF,UAAgB,EAAE,MAAM,CAAA;KAAC,iBACf,MAAM,GACJ,IAAI,CA6HhB;IA2FD;;;;;;OAMG;IACH,yBAHW,MAAM,GACJ,OAAO,CAgBnB;IAED;;;;;;OAMG;IACH,oBAHW,MAAM,GACJ,OAAO,CAoCnB;;CAiXJ;kCA5zCY,OAAO,uCAAuC,EAAE,mBAAmB;uBACnE,OAAO,4BAA4B,EAAE,QAAQ;qCAC7C,OAAO,6BAA6B,EAAE,sBAAsB;qCALpC,2BAA2B;gCALhC,gDAAgD;gCAIhD,sBAAsB"}
1
+ {"version":3,"file":"VGeoResidency.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js"],"names":[],"mappings":"AA2GA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH;IAiKI;;;;;;;OAOG;IACH;QAN2C,IAAI,EAApC,sBAAsB;QACS,OAAO,GAAtC,oBAAoB;QACM,IAAI,GAA9B,eAAe;QACW,OAAO,GAAjC,eAAe;OA8BzB;IAID;;OAEG;IACH,oCAEC;IAED;;OAEG;IACH,4BAEC;IAED;;OAEG;IACH,+BAEC;IAED;;OAEG;IACH,yEAEC;IAED;;;;OAIG;IACH,oBAEC;IAED;;;;OAIG;IACH,gCAGC;IAED,+BAEC;IAED,iCAEC;IAED,6BAEC;IAED,2BAEC;IAED,iCAEC;IAED,gCAEC;IAED;;;;OAIG;IACH,oCAEC;IAED;;;OAGG;IACH,kCAEC;IAED;;;OAGG;IACH,kCAEC;IAED;;;OAGG;IACH,iCAEC;IAED;;;OAGG;IACH,iCAEC;IAED;;;OAGG;IACH,gCAEC;IAED;;;;;OAKG;IACH,gCAEC;IAED;;;OAGG;IACH,iCAEC;IAED,gCAEC;IAED,+BAEC;IAED,8BAEC;IAED;;OAEG;IACH,qCAQC;IAID;;;;;;;;;;OAUG;IACH,iBAHW,mBAAmB,GACjB,MAAM,CA8ClB;IAED;;;;;;OAMG;IACH,mBAHW,mBAAmB,GACjB,OAAO,CAgBnB;IAED;;;OAGG;IACH,sBAHW,mBAAmB,GACjB,OAAO,CAInB;IAED;;;;;;OAMG;IACH,cAJW,mBAAmB,cACnB,MAAM,GACJ,QAAQ,GAAC,UAAU,GAAC,aAAa,GAAC,KAAK,GAAC,MAAM,GAAC,cAAc,CAqBzE;IAgCD;;;;OAIG;IACH,SAFa,IAAI,CAOhB;IAED;;;;OAIG;IACH,WAFa,IAAI,CAUhB;IAED;;;;OAIG;IACH,qBAFa,MAAM,CAkBlB;IAID;;;;;;;;;OASG;IACH,aANW,WAAW,UACX;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAChF,UAAgB,EAAE,MAAM,CAAA;KAAC,iBACf,MAAM,GACJ,IAAI,CA6HhB;IA2FD;;;;;;OAMG;IACH,yBAHW,MAAM,GACJ,OAAO,CAgBnB;IAED;;;;;;OAMG;IACH,oBAHW,MAAM,GACJ,OAAO,CAoCnB;;CAqXJ;kCAr1CY,OAAO,uCAAuC,EAAE,mBAAmB;uBACnE,OAAO,4BAA4B,EAAE,QAAQ;qCAC7C,OAAO,6BAA6B,EAAE,sBAAsB;qCALpC,2BAA2B;gCALhC,gDAAgD;gCAIhD,sBAAsB"}
@@ -34,6 +34,18 @@ const NO_SLOT = 0xFFFFFFFF;
34
34
  * @param {number} state
35
35
  * @returns {number}
36
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
+
37
49
  function tag_of(state) {
38
50
  return state >>> TAG_SHIFT;
39
51
  }
@@ -189,11 +201,6 @@ export class VGeoResidency {
189
201
  */
190
202
  #vram_pinned;
191
203
 
192
- /**
193
- * @type {number}
194
- */
195
- #vram_hand = 0;
196
-
197
204
  // ------------------------------------------------------------------ the RAM tier
198
205
 
199
206
  /**
@@ -229,11 +236,6 @@ export class VGeoResidency {
229
236
  */
230
237
  #ram_free = [];
231
238
 
232
- /**
233
- * @type {number}
234
- */
235
- #ram_hand = 0;
236
-
237
239
  /**
238
240
  * @type {number}
239
241
  */
@@ -253,6 +255,25 @@ export class VGeoResidency {
253
255
 
254
256
  // ------------------------------------------------------------------ per reading
255
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
+
256
277
  /**
257
278
  * @type {number}
258
279
  */
@@ -1036,28 +1057,44 @@ export class VGeoResidency {
1036
1057
  }
1037
1058
 
1038
1059
  /**
1039
- * The CLOCK hand over the page rows, under the reading's test budget.
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.
1040
1067
  *
1041
- * @returns {number} page row, or -1 when the budget is spent without finding one
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
1042
1072
  */
1043
- #next_vram_victim() {
1044
- const capacity = this.#vram_last_used.length;
1045
-
1073
+ #next_victim(hand, capacity, evictable) {
1046
1074
  while (this.#tests_left > 0) {
1047
1075
  this.#tests_left--;
1048
1076
 
1049
- const page_row = this.#vram_hand;
1077
+ const candidate = this.#hands[hand];
1050
1078
 
1051
- this.#vram_hand = (page_row + 1) % capacity;
1079
+ this.#hands[hand] = (candidate + 1) % capacity;
1052
1080
 
1053
- if (this.evictable_vram(page_row)) {
1054
- return page_row;
1081
+ if (evictable(candidate)) {
1082
+ return candidate;
1055
1083
  }
1056
1084
  }
1057
1085
 
1058
1086
  return -1;
1059
1087
  }
1060
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
+
1061
1098
  /**
1062
1099
  * @param {number} page_row
1063
1100
  */
@@ -1113,21 +1150,9 @@ export class VGeoResidency {
1113
1150
  * @returns {number} slot, or -1
1114
1151
  */
1115
1152
  #next_ram_victim() {
1116
- const capacity = this.#ram_geometry.length;
1117
-
1118
- while (this.#tests_left > 0) {
1119
- this.#tests_left--;
1120
-
1121
- const slot = this.#ram_hand;
1122
-
1123
- this.#ram_hand = (slot + 1) % capacity;
1124
-
1125
- if (this.evictable_ram(slot)) {
1126
- return slot;
1127
- }
1128
- }
1129
-
1130
- return -1;
1153
+ return this.#next_victim(
1154
+ HAND_RAM, this.#ram_geometry.length, this.#is_evictable_ram
1155
+ );
1131
1156
  }
1132
1157
 
1133
1158
  /**
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Addressing and arithmetic shared by every pass of the cut — how to find a row, and how to project
3
- * an error.
2
+ * The arithmetic the cut rule is written in: where the camera is in an instance's own space, what
3
+ * an object-space error projects to in pixels, and whether a sphere is worth descending into at all.
4
4
  *
5
- * The arena is `array<u32>` with hand-written accessors rather than a marshalled struct array,
6
- * because it holds three different row types in one allocation per page and no struct array can say
7
- * that. The offsets it indexes with are the `layout/` constants, so the CPU writer and the kernel
8
- * are reading the same numbers rather than two copies of them.
5
+ * Addressing moved to {@link chunk_vgeo_cut_arena}, which this pulls in — a pass that only wants to
6
+ * find a row should not have to take the frustum test with it, and the passes that did not want to
7
+ * wrote their own accessor instead.
9
8
  *
10
- * Every buffer named here has to be declared by the importing shader. That is the price of sharing
11
- * addressing across passes, and it is the same bargain `chunk_scene_database_access` makes.
9
+ * Every buffer named here has to be declared by the importing shader — `instances`, plus whatever
10
+ * the arena chunk needs. That is the price of sharing addressing across passes, and it is the same
11
+ * bargain `chunk_scene_database_access` makes.
12
12
  *
13
13
  * @author Alex Goldring
14
14
  * @copyright Company Named Limited (c) 2026
@@ -1 +1 @@
1
- {"version":3,"file":"chunk_vgeo_cut_access.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js"],"names":[],"mappings":"AAaA;;;;;;;;;;;;;;;GAeG;AACH,oCAFU,SAAS,CAmJZ;0BA9KmB,0CAA0C"}
1
+ {"version":3,"file":"chunk_vgeo_cut_access.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AACH,oCAFU,SAAS,CA+EQ;0BAjGD,0CAA0C"}
@@ -1,27 +1,18 @@
1
1
  import { CodeChunk } from "../../../../shader/compiler/CodeChunk.js";
2
- import { VGEO_CLUSTER_ROW_WORDS } from "../layout/VGEO_CLUSTER_ROW_WORDS.js";
3
- import { VGEO_GEOMETRY_ROW_WORDS } from "../layout/VGEO_GEOMETRY_ROW_WORDS.js";
4
- import { VGEO_GROUP_ROW_WORDS } from "../layout/VGEO_GROUP_ROW_WORDS.js";
5
2
  import { VGEO_INSTANCE_OFFSET } from "../layout/VGEO_INSTANCE_OFFSET.js";
6
- import { VGEO_INSTANCE_WORDS } from "../layout/VGEO_INSTANCE_WORDS.js";
7
- import { VGEO_NODE_ROW_LEAF_BIT } from "../layout/VGEO_NODE_ROW_LEAF_BIT.js";
8
- import { VGEO_NODE_ROW_OFFSET } from "../layout/VGEO_NODE_ROW_OFFSET.js";
9
- import { VGEO_NODE_ROW_WORDS } from "../layout/VGEO_NODE_ROW_WORDS.js";
10
- import { VGEO_PAGE_ROW_OFFSET } from "../layout/VGEO_PAGE_ROW_OFFSET.js";
11
- import { VGEO_PAGE_ROW_WORDS } from "../layout/VGEO_PAGE_ROW_WORDS.js";
12
- import { VGEO_PARENT_ROW_WORDS } from "../layout/VGEO_PARENT_ROW_WORDS.js";
3
+ import { chunk_vgeo_cut_arena } from "./chunk_vgeo_cut_arena.js";
13
4
 
14
5
  /**
15
- * Addressing and arithmetic shared by every pass of the cut — how to find a row, and how to project
16
- * an error.
6
+ * The arithmetic the cut rule is written in: where the camera is in an instance's own space, what
7
+ * an object-space error projects to in pixels, and whether a sphere is worth descending into at all.
17
8
  *
18
- * The arena is `array<u32>` with hand-written accessors rather than a marshalled struct array,
19
- * because it holds three different row types in one allocation per page and no struct array can say
20
- * that. The offsets it indexes with are the `layout/` constants, so the CPU writer and the kernel
21
- * are reading the same numbers rather than two copies of them.
9
+ * Addressing moved to {@link chunk_vgeo_cut_arena}, which this pulls in — a pass that only wants to
10
+ * find a row should not have to take the frustum test with it, and the passes that did not want to
11
+ * wrote their own accessor instead.
22
12
  *
23
- * Every buffer named here has to be declared by the importing shader. That is the price of sharing
24
- * addressing across passes, and it is the same bargain `chunk_scene_database_access` makes.
13
+ * Every buffer named here has to be declared by the importing shader — `instances`, plus whatever
14
+ * the arena chunk needs. That is the price of sharing addressing across passes, and it is the same
15
+ * bargain `chunk_scene_database_access` makes.
25
16
  *
26
17
  * @author Alex Goldring
27
18
  * @copyright Company Named Limited (c) 2026
@@ -30,17 +21,6 @@ import { VGEO_PARENT_ROW_WORDS } from "../layout/VGEO_PARENT_ROW_WORDS.js";
30
21
  export const chunk_vgeo_cut_access = CodeChunk.from(
31
22
  //language=WGSL
32
23
  `
33
- const VG_PAGE_ROW_WORDS = ${VGEO_PAGE_ROW_WORDS}u;
34
- const VG_NODE_ROW_WORDS = ${VGEO_NODE_ROW_WORDS}u;
35
- const VG_GROUP_ROW_WORDS = ${VGEO_GROUP_ROW_WORDS}u;
36
- const VG_CLUSTER_ROW_WORDS = ${VGEO_CLUSTER_ROW_WORDS}u;
37
- const VG_PARENT_ROW_WORDS = ${VGEO_PARENT_ROW_WORDS}u;
38
- const VG_INSTANCE_WORDS = ${VGEO_INSTANCE_WORDS}u;
39
- const VG_GEOMETRY_ROW_WORDS = ${VGEO_GEOMETRY_ROW_WORDS}u;
40
-
41
- const VG_NODE_LEAF_BIT = ${VGEO_NODE_ROW_LEAF_BIT}u;
42
- const VG_NODE_CHILD_COUNT_MASK = 0xffu;
43
-
44
24
  /**
45
25
  * Any error at or above this is the root's +infinity, however the file spelled it.
46
26
  *
@@ -51,63 +31,6 @@ const VG_NODE_CHILD_COUNT_MASK = 0xffu;
51
31
  const VG_ERROR_INFINITE = 3.0e38;
52
32
  const VG_PROJECTION_MAX = 3.4e38;
53
33
 
54
- /**
55
- * A page's slot: its header, then its node, group and cluster rows.
56
- *
57
- * The stride is a uniform rather than a constant because the tier derives it from the page size its
58
- * containers are bounded at, and it is a **multiply** rather than a table read because format
59
- * §11.14 made that legal — design §6 asked for fixed-size page slots from the start and the bound
60
- * is what it was waiting for. There is no page table, no allocator, and no second storage binding
61
- * to keep coherent with this one.
62
- */
63
- fn vg_slot_base(page_row: u32) -> u32 {
64
- return page_row * params.page_slot_words;
65
- }
66
-
67
- fn vg_page_field(page_row: u32, field: u32) -> u32 {
68
- return vg_arena[vg_slot_base(page_row) + field];
69
- }
70
-
71
- fn vg_node_base(page_row: u32) -> u32 {
72
- return vg_slot_base(page_row) + VG_PAGE_ROW_WORDS;
73
- }
74
-
75
- fn vg_group_base(page_row: u32) -> u32 {
76
- return vg_node_base(page_row)
77
- + vg_page_field(page_row, ${VGEO_PAGE_ROW_OFFSET.NODE_COUNT}u) * VG_NODE_ROW_WORDS;
78
- }
79
-
80
- fn vg_cluster_base(page_row: u32) -> u32 {
81
- return vg_group_base(page_row)
82
- + vg_page_field(page_row, ${VGEO_PAGE_ROW_OFFSET.GROUP_COUNT}u) * VG_GROUP_ROW_WORDS;
83
- }
84
-
85
- /**
86
- * The fourth section: this page's parent rows, which is where a walk *up* reads.
87
- *
88
- * It may be empty, and a reader has to check rather than assume: the page row's parent
89
- * entry count is zero for
90
- * every page of a container written before format 4, which has no upward edge at all. Zero is not a
91
- * malformed page; it is an asset that can only be re-descended.
92
- */
93
- fn vg_parent_base(page_row: u32) -> u32 {
94
- return vg_cluster_base(page_row)
95
- + vg_page_field(page_row, ${VGEO_PAGE_ROW_OFFSET.CLUSTER_COUNT}u) * VG_CLUSTER_ROW_WORDS;
96
- }
97
-
98
- fn vg_arena_f32(word: u32) -> f32 {
99
- return bitcast<f32>(vg_arena[word]);
100
- }
101
-
102
- fn vg_sphere_at(word: u32) -> vec4<f32> {
103
- return vec4<f32>(
104
- bitcast<f32>(vg_arena[word]),
105
- bitcast<f32>(vg_arena[word + 1u]),
106
- bitcast<f32>(vg_arena[word + 2u]),
107
- bitcast<f32>(vg_arena[word + 3u])
108
- );
109
- }
110
-
111
34
  fn vg_instance_f32(instance_base: u32, field: u32) -> f32 {
112
35
  return bitcast<f32>(instances[instance_base + field]);
113
36
  }
@@ -123,7 +46,7 @@ fn vg_instance_eye(instance_base: u32) -> vec3<f32> {
123
46
  /**
124
47
  * \`err_px = r_e * screen_height / (2 * tan(fov_y / 2) * d)\`, with \`d\` the distance to the
125
48
  * **closest point** of the sphere — design §4, and written in that order so that it agrees with
126
- * \`select_cut.js\` term for term rather than merely in the limit.
49
+ * \`vgeo_select_cut\` term for term rather than merely in the limit.
127
50
  *
128
51
  * Two conventions carry real weight. A zero error projects to zero **unconditionally**, so a leaf
129
52
  * — whose error is exactly zero — does not become undrawable the moment the camera enters its
@@ -172,4 +95,4 @@ fn vg_sphere_outside_frustum(instance_base: u32, sphere: vec4<f32>) -> bool {
172
95
 
173
96
  return false;
174
97
  }
175
- `, []);
98
+ `, [chunk_vgeo_cut_arena]);
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Where a row is, in the arena — and nothing else.
3
+ *
4
+ * Split out of {@link chunk_vgeo_cut_access} because three passes want an address and only one of
5
+ * them wants a projection. Before the split, a pass that needed one field of a page header wrote
6
+ * its own accessor rather than take a chunk that also declares the frustum test, the instance eye
7
+ * and the error projection; `shader_vgeo_cut_reseed` said so where its copy was
8
+ * (`vg_reseed_page_field`). The answer to "the chunk declares more than this pass binds" is a
9
+ * smaller chunk, not a second accessor.
10
+ *
11
+ * Requires the importing shader to declare `params` and `vg_arena`.
12
+ *
13
+ * @author Alex Goldring
14
+ * @copyright Company Named Limited (c) 2026
15
+ * @type {CodeChunk}
16
+ */
17
+ export const chunk_vgeo_cut_arena: CodeChunk;
18
+ import { CodeChunk } from "../../../../shader/compiler/CodeChunk.js";
19
+ //# sourceMappingURL=chunk_vgeo_cut_arena.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk_vgeo_cut_arena.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.js"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;GAeG;AACH,mCAFU,SAAS,CAwEZ;0BAjGmB,0CAA0C"}
@@ -0,0 +1,98 @@
1
+ import { CodeChunk } from "../../../../shader/compiler/CodeChunk.js";
2
+ import { VGEO_CLUSTER_ROW_WORDS } from "../layout/VGEO_CLUSTER_ROW_WORDS.js";
3
+ import { VGEO_GEOMETRY_ROW_WORDS } from "../layout/VGEO_GEOMETRY_ROW_WORDS.js";
4
+ import { VGEO_GROUP_ROW_WORDS } from "../layout/VGEO_GROUP_ROW_WORDS.js";
5
+ import { VGEO_INSTANCE_WORDS } from "../layout/VGEO_INSTANCE_WORDS.js";
6
+ import { VGEO_NODE_ROW_LEAF_BIT } from "../layout/VGEO_NODE_ROW_LEAF_BIT.js";
7
+ import { VGEO_NODE_ROW_WORDS } from "../layout/VGEO_NODE_ROW_WORDS.js";
8
+ import { VGEO_PAGE_ROW_OFFSET } from "../layout/VGEO_PAGE_ROW_OFFSET.js";
9
+ import { VGEO_PAGE_ROW_WORDS } from "../layout/VGEO_PAGE_ROW_WORDS.js";
10
+ import { VGEO_PARENT_ROW_WORDS } from "../layout/VGEO_PARENT_ROW_WORDS.js";
11
+
12
+ /**
13
+ * Where a row is, in the arena — and nothing else.
14
+ *
15
+ * Split out of {@link chunk_vgeo_cut_access} because three passes want an address and only one of
16
+ * them wants a projection. Before the split, a pass that needed one field of a page header wrote
17
+ * its own accessor rather than take a chunk that also declares the frustum test, the instance eye
18
+ * and the error projection; `shader_vgeo_cut_reseed` said so where its copy was
19
+ * (`vg_reseed_page_field`). The answer to "the chunk declares more than this pass binds" is a
20
+ * smaller chunk, not a second accessor.
21
+ *
22
+ * Requires the importing shader to declare `params` and `vg_arena`.
23
+ *
24
+ * @author Alex Goldring
25
+ * @copyright Company Named Limited (c) 2026
26
+ * @type {CodeChunk}
27
+ */
28
+ export const chunk_vgeo_cut_arena = CodeChunk.from(
29
+ //language=WGSL
30
+ `
31
+ const VG_PAGE_ROW_WORDS = ${VGEO_PAGE_ROW_WORDS}u;
32
+ const VG_NODE_ROW_WORDS = ${VGEO_NODE_ROW_WORDS}u;
33
+ const VG_GROUP_ROW_WORDS = ${VGEO_GROUP_ROW_WORDS}u;
34
+ const VG_CLUSTER_ROW_WORDS = ${VGEO_CLUSTER_ROW_WORDS}u;
35
+ const VG_PARENT_ROW_WORDS = ${VGEO_PARENT_ROW_WORDS}u;
36
+ const VG_INSTANCE_WORDS = ${VGEO_INSTANCE_WORDS}u;
37
+ const VG_GEOMETRY_ROW_WORDS = ${VGEO_GEOMETRY_ROW_WORDS}u;
38
+
39
+ const VG_NODE_LEAF_BIT = ${VGEO_NODE_ROW_LEAF_BIT}u;
40
+ const VG_NODE_CHILD_COUNT_MASK = 0xffu;
41
+
42
+ /**
43
+ * A page's slot: its header, then its node, group and cluster rows.
44
+ *
45
+ * The stride is a uniform rather than a constant because the tier derives it from the page size its
46
+ * containers are bounded at, and it is a **multiply** rather than a table read because format
47
+ * §11.14 made that legal — design §6 asked for fixed-size page slots from the start and the bound
48
+ * is what it was waiting for. There is no page table, no allocator, and no second storage binding
49
+ * to keep coherent with this one.
50
+ */
51
+ fn vg_slot_base(page_row: u32) -> u32 {
52
+ return page_row * params.page_slot_words;
53
+ }
54
+
55
+ fn vg_page_field(page_row: u32, field: u32) -> u32 {
56
+ return vg_arena[vg_slot_base(page_row) + field];
57
+ }
58
+
59
+ fn vg_node_base(page_row: u32) -> u32 {
60
+ return vg_slot_base(page_row) + VG_PAGE_ROW_WORDS;
61
+ }
62
+
63
+ fn vg_group_base(page_row: u32) -> u32 {
64
+ return vg_node_base(page_row)
65
+ + vg_page_field(page_row, ${VGEO_PAGE_ROW_OFFSET.NODE_COUNT}u) * VG_NODE_ROW_WORDS;
66
+ }
67
+
68
+ fn vg_cluster_base(page_row: u32) -> u32 {
69
+ return vg_group_base(page_row)
70
+ + vg_page_field(page_row, ${VGEO_PAGE_ROW_OFFSET.GROUP_COUNT}u) * VG_GROUP_ROW_WORDS;
71
+ }
72
+
73
+ /**
74
+ * The fourth section: this page's parent rows, which is where a walk *up* reads.
75
+ *
76
+ * It may be empty, and a reader has to check rather than assume: the page row's parent
77
+ * entry count is zero for
78
+ * every page of a container written before format 4, which has no upward edge at all. Zero is not a
79
+ * malformed page; it is an asset that can only be re-descended.
80
+ */
81
+ fn vg_parent_base(page_row: u32) -> u32 {
82
+ return vg_cluster_base(page_row)
83
+ + vg_page_field(page_row, ${VGEO_PAGE_ROW_OFFSET.CLUSTER_COUNT}u) * VG_CLUSTER_ROW_WORDS;
84
+ }
85
+
86
+ fn vg_arena_f32(word: u32) -> f32 {
87
+ return bitcast<f32>(vg_arena[word]);
88
+ }
89
+
90
+ fn vg_sphere_at(word: u32) -> vec4<f32> {
91
+ return vec4<f32>(
92
+ bitcast<f32>(vg_arena[word]),
93
+ bitcast<f32>(vg_arena[word + 1u]),
94
+ bitcast<f32>(vg_arena[word + 2u]),
95
+ bitcast<f32>(vg_arena[word + 3u])
96
+ );
97
+ }
98
+ `, []);
@@ -0,0 +1,31 @@
1
+ /**
2
+ * One page, claimed once per instance, and touched when the claim is won.
3
+ *
4
+ * This is the invariant the whole cut rests on — "at most one draw record per (instance, cluster)" —
5
+ * and it was written out three times: here's-the-original in `shader_vgeo_cut_traverse`, a
6
+ * line-for-line copy in `shader_vgeo_cut_reseed`, and inline in `shader_vgeo_cut_seed`. The
7
+ * copies had already begun to differ: the seed pass omitted the `claim_word_count` bound the other
8
+ * two take. It was correct — the seed pass establishes that bound a few lines earlier — but a
9
+ * correctness invariant whose three spellings disagree about which checks it needs is one nobody
10
+ * can read off the source any more.
11
+ *
12
+ * The touch is the other half and travels with it, because a claim that does not stamp the page is
13
+ * a page that ages out of the LRU while it is on screen. LRU on last-**touched**, never on
14
+ * last-wanted: stamping at fetch time freezes the stamp of exactly the pages that are being drawn
15
+ * and evicts them, which is the fallback-chain bug the virtual texture cache already keys on touch
16
+ * to avoid.
17
+ *
18
+ * One bit per page row, because the feedback buffer is per frame rather than per invocation: the
19
+ * bit dedups across every view and bucket of the frame and is the record the host reads, so there
20
+ * is no list to fill and no touch a full list could lose.
21
+ *
22
+ * Requires the importing shader to declare `params`, `vg_arena`, `instances`, `claim` and
23
+ * `feedback`.
24
+ *
25
+ * @author Alex Goldring
26
+ * @copyright Company Named Limited (c) 2026
27
+ * @type {CodeChunk}
28
+ */
29
+ export const chunk_vgeo_cut_claim: CodeChunk;
30
+ import { CodeChunk } from "../../../../shader/compiler/CodeChunk.js";
31
+ //# sourceMappingURL=chunk_vgeo_cut_claim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk_vgeo_cut_claim.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.js"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,mCAFU,SAAS,CAiEQ;0BAlGD,0CAA0C"}
@@ -0,0 +1,99 @@
1
+ import { CodeChunk } from "../../../../shader/compiler/CodeChunk.js";
2
+ import { VGEO_CUT_STAT } from "../layout/VGEO_CUT_STAT.js";
3
+ import { VGEO_FEEDBACK_OFFSET } from "../layout/VGEO_FEEDBACK_OFFSET.js";
4
+ import { VGEO_INSTANCE_OFFSET } from "../layout/VGEO_INSTANCE_OFFSET.js";
5
+ import { VGEO_PAGE_ROW_OFFSET } from "../layout/VGEO_PAGE_ROW_OFFSET.js";
6
+ import { chunk_vgeo_cut_arena } from "./chunk_vgeo_cut_arena.js";
7
+
8
+ /**
9
+ * One page, claimed once per instance, and touched when the claim is won.
10
+ *
11
+ * This is the invariant the whole cut rests on — "at most one draw record per (instance, cluster)" —
12
+ * and it was written out three times: here's-the-original in `shader_vgeo_cut_traverse`, a
13
+ * line-for-line copy in `shader_vgeo_cut_reseed`, and inline in `shader_vgeo_cut_seed`. The
14
+ * copies had already begun to differ: the seed pass omitted the `claim_word_count` bound the other
15
+ * two take. It was correct — the seed pass establishes that bound a few lines earlier — but a
16
+ * correctness invariant whose three spellings disagree about which checks it needs is one nobody
17
+ * can read off the source any more.
18
+ *
19
+ * The touch is the other half and travels with it, because a claim that does not stamp the page is
20
+ * a page that ages out of the LRU while it is on screen. LRU on last-**touched**, never on
21
+ * last-wanted: stamping at fetch time freezes the stamp of exactly the pages that are being drawn
22
+ * and evicts them, which is the fallback-chain bug the virtual texture cache already keys on touch
23
+ * to avoid.
24
+ *
25
+ * One bit per page row, because the feedback buffer is per frame rather than per invocation: the
26
+ * bit dedups across every view and bucket of the frame and is the record the host reads, so there
27
+ * is no list to fill and no touch a full list could lose.
28
+ *
29
+ * Requires the importing shader to declare `params`, `vg_arena`, `instances`, `claim` and
30
+ * `feedback`.
31
+ *
32
+ * @author Alex Goldring
33
+ * @copyright Company Named Limited (c) 2026
34
+ * @type {CodeChunk}
35
+ */
36
+ export const chunk_vgeo_cut_claim = CodeChunk.from(
37
+ //language=WGSL
38
+ `
39
+ fn vg_stat(slot: u32, amount: u32) {
40
+ atomicAdd(&feedback[${VGEO_FEEDBACK_OFFSET.STATS}u + slot], amount);
41
+ }
42
+
43
+ /**
44
+ * Record that this page was selected from this frame — design §6's eviction key.
45
+ */
46
+ fn vg_touch_page(page_row: u32) {
47
+ atomicOr(&feedback[params.touch_bits_base + (page_row >> 5u)], 1u << (page_row & 31u));
48
+ }
49
+
50
+ /**
51
+ * The claim itself, against a slice of the bitmask the caller already has in hand.
52
+ *
53
+ * Taken this way rather than always through the instance record because the seed pass computes the
54
+ * slice base in the same invocation that claims with it — it has just written it to the instance
55
+ * record, and reading it straight back would be a storage round trip for a value already in a
56
+ * register.
57
+ *
58
+ * @returns whether this invocation is the one that claimed the page
59
+ */
60
+ fn vg_claim_page_at(claim_base: u32, ordinal_capacity: u32, page_row: u32) -> bool {
61
+ let ordinal = vg_page_field(page_row, ${VGEO_PAGE_ROW_OFFSET.ORDINAL}u);
62
+
63
+ if (ordinal >= ordinal_capacity) {
64
+ return false;
65
+ }
66
+
67
+ let bit = claim_base + ordinal;
68
+
69
+ let word = bit >> 5u;
70
+
71
+ if (word >= params.claim_word_count) {
72
+ return false;
73
+ }
74
+
75
+ let mask = 1u << (bit & 31u);
76
+
77
+ if ((atomicOr(&claim[word], mask) & mask) != 0u) {
78
+ return false;
79
+ }
80
+
81
+ vg_stat(${VGEO_CUT_STAT.PAGES}u, 1u);
82
+
83
+ vg_touch_page(page_row);
84
+
85
+ return true;
86
+ }
87
+
88
+ /**
89
+ * The same claim, for a caller holding an instance rather than its slice. An instance whose slice
90
+ * of the bitmask does not fit is refused upstream, in the seed pass, rather than run unclaimed.
91
+ */
92
+ fn vg_claim_page(instance_base: u32, page_row: u32) -> bool {
93
+ return vg_claim_page_at(
94
+ instances[instance_base + ${VGEO_INSTANCE_OFFSET.CLAIM_BASE}u],
95
+ instances[instance_base + ${VGEO_INSTANCE_OFFSET.ORDINAL_CAPACITY}u],
96
+ page_row
97
+ );
98
+ }
99
+ `, [chunk_vgeo_cut_arena]);