@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
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Gather a face set: unique vertices in first-use order, local indices, coordinates copied out.
3
+ *
4
+ * First-use order is what makes the result depend only on the face list, not on the pool's history.
5
+ *
6
+ * @param {VirtualGeometryMesh} mesh
7
+ * @param {number[]} face_ids allocated triangular faces
8
+ * @returns {VGeoGatheredFaces}
9
+ */
10
+ export function vgeo_gather_faces(mesh: VirtualGeometryMesh, face_ids: number[]): VGeoGatheredFaces;
11
+ import { VGeoGatheredFaces } from "./VGeoGatheredFaces.js";
12
+ //# sourceMappingURL=vgeo_gather_faces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vgeo_gather_faces.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.js"],"names":[],"mappings":"AAcA;;;;;;;;GAQG;AACH,uEAHW,MAAM,EAAE,GACN,iBAAiB,CAiD7B;kCAlEiC,wBAAwB"}
@@ -1,7 +1,7 @@
1
- import { assert } from "../../../../../core/assert.js";
1
+ import { assert } from "../../../../../../core/assert.js";
2
2
  import {
3
3
  bt_face_read_triangle_vertices
4
- } from "../../../../../core/geom/3d/topology/struct/binary/query/bt_face_read_triangle.js";
4
+ } from "../../../../../../core/geom/3d/topology/struct/binary/query/bt_face_read_triangle.js";
5
5
  import { VGeoGatheredFaces } from "./VGeoGatheredFaces.js";
6
6
 
7
7
  /**
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The 8-bit local index list a cluster payload carries.
3
+ *
4
+ * @param {VGeoGatheredFaces} gathered a face set already within the meshlet limits
5
+ * @returns {Uint8Array}
6
+ *
7
+ * @author Alex Goldring
8
+ * @copyright Company Named Limited (c) 2026
9
+ */
10
+ export function vgeo_narrow_indices(gathered: VGeoGatheredFaces): Uint8Array;
11
+ //# sourceMappingURL=vgeo_narrow_indices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vgeo_narrow_indices.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/build/submesh/vgeo_narrow_indices.js"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,kEALa,UAAU,CAiBtB"}
@@ -1,6 +1,6 @@
1
- import { assert } from "../../../../../core/assert.js";
2
- import { VGEO_MAX_CLUSTER_TRIANGLE_COUNT } from "../format/cluster/VGEO_MAX_CLUSTER_TRIANGLE_COUNT.js";
3
- import { VGEO_MAX_CLUSTER_VERTEX_COUNT } from "../format/cluster/VGEO_MAX_CLUSTER_VERTEX_COUNT.js";
1
+ import { assert } from "../../../../../../core/assert.js";
2
+ import { VGEO_MAX_CLUSTER_TRIANGLE_COUNT } from "../../format/cluster/VGEO_MAX_CLUSTER_TRIANGLE_COUNT.js";
3
+ import { VGEO_MAX_CLUSTER_VERTEX_COUNT } from "../../format/cluster/VGEO_MAX_CLUSTER_VERTEX_COUNT.js";
4
4
 
5
5
  /**
6
6
  * The 8-bit local index list a cluster payload carries.
@@ -1 +1 @@
1
- {"version":3,"file":"vgeo_build.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/build/vgeo_build.js"],"names":[],"mappings":"AA8GA;;;;;;;;;;;;;;;;;;GAkBG;AACH,2EAFa,eAAe,CAyE3B;gCA9L+B,sBAAsB"}
1
+ {"version":3,"file":"vgeo_build.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/build/vgeo_build.js"],"names":[],"mappings":"AA4GA;;;;;;;;;;;;;;;;;;GAkBG;AACH,2EAFa,eAAe,CAyE3B;gCA3L+B,sBAAsB"}
@@ -1,16 +1,17 @@
1
1
  import { assert } from "../../../../../core/assert.js";
2
+ import { aabb3_array_combine } from "../../../../../core/geom/3d/aabb/aabb3_array_combine.js";
2
3
  import { vgeo_attribute_mask } from "../format/attribute/vgeo_attribute_mask.js";
3
4
  import { vgeo_write_container } from "../format/vgeo_write_container.js";
4
5
  import { VGeoLevelRecord } from "../format/header/VGeoLevelRecord.js";
5
6
  import { VGeoWriteOptions } from "../format/VGeoWriteOptions.js";
6
- import { vgeo_assemble_pages } from "./vgeo_assemble_pages.js";
7
- import { vgeo_build_levels } from "./vgeo_build_levels.js";
8
- import { vgeo_build_root_group } from "./vgeo_build_root_group.js";
9
- import { vgeo_containing_sphere } from "./vgeo_containing_sphere.js";
10
- import { vgeo_normalize_source } from "./vgeo_normalize_source.js";
7
+ import { vgeo_assemble_pages } from "./page/vgeo_assemble_pages.js";
8
+ import { vgeo_build_levels } from "./level/vgeo_build_levels.js";
9
+ import { vgeo_build_root_group } from "./level/vgeo_build_root_group.js";
10
+ import { vgeo_containing_sphere } from "./sphere/vgeo_containing_sphere.js";
11
+ import { vgeo_normalize_source } from "./mesh/vgeo_normalize_source.js";
11
12
  import { VGeoBuildResult } from "./VGeoBuildResult.js";
12
- import { VGeoLevelSummary } from "./VGeoLevelSummary.js";
13
- import { VirtualGeometryMesh } from "./VirtualGeometryMesh.js";
13
+ import { VGeoLevelSummary } from "./level/VGeoLevelSummary.js";
14
+ import { VirtualGeometryMesh } from "./mesh/VirtualGeometryMesh.js";
14
15
 
15
16
  /**
16
17
  * The whole builder pipeline, from a triangle soup to `.vgeo` bytes.
@@ -46,10 +47,7 @@ function compute_object_bounds(levels) {
46
47
  for (const cluster of level.clusters) {
47
48
  spheres.push(cluster.bounds_sphere);
48
49
 
49
- for (let axis = 0; axis < 3; axis++) {
50
- bounds[axis] = Math.min(bounds[axis], cluster.aabb[axis]);
51
- bounds[axis + 3] = Math.max(bounds[axis + 3], cluster.aabb[axis + 3]);
52
- }
50
+ aabb3_array_combine(bounds, 0, bounds, 0, cluster.aabb, 0);
53
51
  }
54
52
  }
55
53
 
@@ -1,7 +1,13 @@
1
1
  /**
2
2
  * Read one vertex's value for one channel, falling back to the format's default for a channel this
3
- * geometry does not carry — which is exactly what the container does with it, since the payload
4
- * never omits a section and constant-compresses the missing ones to the same values.
3
+ * geometry does not carry.
4
+ *
5
+ * These are the **unencoded** defaults, for a caller that is going to encode what it reads.
6
+ * `vgeo_encode_cluster_payload` never reaches them: a channel the geometry lacks takes its
7
+ * `ENCODED_DEFAULTS` path instead, which emits Shade's encoded constant directly. The two are not
8
+ * everywhere the same value — colour's encoded default carries an alpha byte that
9
+ * `encode_colors_to_u32` does not write — so which of the two a caller wants is a real question,
10
+ * and the answer is: this one only when the value is about to be encoded from scratch.
5
11
  *
6
12
  * @param {Float32Array|number[]} target
7
13
  * @param {number} target_offset
@@ -1 +1 @@
1
- {"version":3,"file":"vgeo_attributes_read.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,6CATW,YAAY,GAAC,MAAM,EAAE,iBACrB,MAAM,UACN,CAAC,kBAAgB,IAAI,CAAC,EAAE,aACxB,MAAM,UACN,MAAM,QAmBhB"}
1
+ {"version":3,"file":"vgeo_attributes_read.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,6CATW,YAAY,GAAC,MAAM,EAAE,iBACrB,MAAM,UACN,CAAC,kBAAgB,IAAI,CAAC,EAAE,aACxB,MAAM,UACN,MAAM,QAmBhB"}
@@ -2,8 +2,14 @@ import { VGEO_ATTRIBUTES } from "./VGEO_ATTRIBUTES.js";
2
2
 
3
3
  /**
4
4
  * Read one vertex's value for one channel, falling back to the format's default for a channel this
5
- * geometry does not carry — which is exactly what the container does with it, since the payload
6
- * never omits a section and constant-compresses the missing ones to the same values.
5
+ * geometry does not carry.
6
+ *
7
+ * These are the **unencoded** defaults, for a caller that is going to encode what it reads.
8
+ * `vgeo_encode_cluster_payload` never reaches them: a channel the geometry lacks takes its
9
+ * `ENCODED_DEFAULTS` path instead, which emits Shade's encoded constant directly. The two are not
10
+ * everywhere the same value — colour's encoded default carries an alpha byte that
11
+ * `encode_colors_to_u32` does not write — so which of the two a caller wants is a real question,
12
+ * and the answer is: this one only when the value is about to be encoded from scratch.
7
13
  *
8
14
  * @param {Float32Array|number[]} target
9
15
  * @param {number} target_offset
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Group record field offsets, relative to the record's own start.
3
+ *
4
+ * The record is `VGEO_GROUP_RECORD_SIZE` bytes and `VGEO_FORMAT.md` §6.2 is where the layout is
5
+ * stated; this is that statement in code, so the writer, the reader and the audit address the same
6
+ * bytes by the same names rather than each counting them out again.
7
+ */
8
+ export type VGEO_GROUP_OFFSET = number;
9
+ export namespace VGEO_GROUP_OFFSET {
10
+ let CLUSTER_FIRST: number;
11
+ let CLUSTER_COUNT: number;
12
+ let PARENT_CLUSTER_COUNT: number;
13
+ let PARENT_FIRST: number;
14
+ }
15
+ //# sourceMappingURL=VGEO_GROUP_OFFSET.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VGEO_GROUP_OFFSET.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.js"],"names":[],"mappings":";;;;;;;gCAOU,MAAM"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Group record field offsets, relative to the record's own start.
3
+ *
4
+ * The record is `VGEO_GROUP_RECORD_SIZE` bytes and `VGEO_FORMAT.md` §6.2 is where the layout is
5
+ * stated; this is that statement in code, so the writer, the reader and the audit address the same
6
+ * bytes by the same names rather than each counting them out again.
7
+ *
8
+ * @enum {number}
9
+ */
10
+ export const VGEO_GROUP_OFFSET = {
11
+ CLUSTER_FIRST: 0,
12
+ CLUSTER_COUNT: 2,
13
+ PARENT_CLUSTER_COUNT: 4,
14
+ PARENT_FIRST: 6,
15
+ };
@@ -7,5 +7,33 @@
7
7
  * @returns {VGeoEncodedPayload}
8
8
  */
9
9
  export function vgeo_encode_cluster_payload(positions: Float32Array, indices: Uint8Array, vertex_ids: Uint32Array, layers: (BinaryDataLayer | null)[]): VGeoEncodedPayload;
10
+ /**
11
+ * Encode one cluster's geometry into a `SHADE_MESHLET_V1` payload: the local index section, then
12
+ * every attribute section in the frozen order, each either one value per vertex or — when every
13
+ * vertex agrees, or the source simply lacks the attribute — a single constant-compressed value.
14
+ *
15
+ * The encoders themselves are Shade's, unmodified. That is the whole point of the encoding's
16
+ * "byte-compatible with the data pool" claim: uploading a payload is a copy, so it has to be the
17
+ * same bits Shade's own encoder would have produced.
18
+ *
19
+ * @author Alex Goldring
20
+ * @copyright Company Named Limited (c) 2026
21
+ */
22
+ /**
23
+ * The encoded word a section carries when the source lacks the attribute — Shade's
24
+ * `ENCODED_ATTRIBUTE_DEFAULT_VALUES`, in this format's own attribute order, so that a `.vgeo`
25
+ * payload and a `MeshletBatch` payload of the same geometry differ nowhere.
26
+ *
27
+ * Colour's default is opaque white with its alpha byte set, while `encode_colors_to_u32` writes only
28
+ * three channels and leaves alpha zero. That asymmetry is Shade's, and matching it is the point.
29
+ *
30
+ * Exported so that `vgeo_encode_cluster_payload.spec.js` can hold it against Shade's own table
31
+ * rather than against nothing — it is the fourth of the four tables the byte-compatibility claim
32
+ * rests on, and was for a long time the only one no test compared.
33
+ *
34
+ * @readonly
35
+ * @type {number[][]}
36
+ */
37
+ export const ENCODED_DEFAULTS: number[][];
10
38
  import { VGeoEncodedPayload } from "./VGeoEncodedPayload.js";
11
39
  //# sourceMappingURL=vgeo_encode_cluster_payload.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vgeo_encode_cluster_payload.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js"],"names":[],"mappings":"AAgHA;;;;;;;GAOG;AACH,uDAPW,YAAY,WACZ,UAAU,cACV,WAAW,UACX,CAAC,kBAAgB,IAAI,CAAC,EAAE,GAEtB,kBAAkB,CAiF9B;mCA1LkC,yBAAyB"}
1
+ {"version":3,"file":"vgeo_encode_cluster_payload.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js"],"names":[],"mappings":"AAoHA;;;;;;;GAOG;AACH,uDAPW,YAAY,WACZ,UAAU,cACV,WAAW,UACX,CAAC,kBAAgB,IAAI,CAAC,EAAE,GAEtB,kBAAkB,CAiF9B;AA5LD;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;GAcG;AACH,+BAFU,MAAM,EAAE,EAAE,CAEe;mCA9BA,yBAAyB"}
@@ -28,16 +28,20 @@ import { VGeoEncodedPayload } from "./VGeoEncodedPayload.js";
28
28
 
29
29
  /**
30
30
  * The encoded word a section carries when the source lacks the attribute — Shade's
31
- * `ENCODED_ATTRIBUTE_DEFAULT_VALUES`, reproduced so that a `.vgeo` payload and a `MeshletBatch`
32
- * payload of the same geometry differ nowhere.
31
+ * `ENCODED_ATTRIBUTE_DEFAULT_VALUES`, in this format's own attribute order, so that a `.vgeo`
32
+ * payload and a `MeshletBatch` payload of the same geometry differ nowhere.
33
33
  *
34
34
  * Colour's default is opaque white with its alpha byte set, while `encode_colors_to_u32` writes only
35
35
  * three channels and leaves alpha zero. That asymmetry is Shade's, and matching it is the point.
36
36
  *
37
+ * Exported so that `vgeo_encode_cluster_payload.spec.js` can hold it against Shade's own table
38
+ * rather than against nothing — it is the fourth of the four tables the byte-compatibility claim
39
+ * rests on, and was for a long time the only one no test compared.
40
+ *
37
41
  * @readonly
38
42
  * @type {number[][]}
39
43
  */
40
- const ENCODED_DEFAULTS = [];
44
+ export const ENCODED_DEFAULTS = [];
41
45
 
42
46
  {
43
47
  const normal = new Uint32Array(1);
@@ -1 +1 @@
1
- {"version":3,"file":"vgeo_read_page.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js"],"names":[],"mappings":"AAqCA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,4CAVW,UAAU,cACV,MAAM,GAEJ,QAAQ,CAiFpB;yBAhHwB,eAAe"}
1
+ {"version":3,"file":"vgeo_read_page.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js"],"names":[],"mappings":"AAsCA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,4CAVW,UAAU,cACV,MAAM,GAEJ,QAAQ,CAiFpB;yBAhHwB,eAAe"}
@@ -10,6 +10,7 @@ import { vgeo_decode_frame_blob } from "../frame/vgeo_decode_frame_blob.js";
10
10
  import { VGEO_FRAME_HEADER_SIZE } from "../frame/VGEO_FRAME_HEADER_SIZE.js";
11
11
  import { VGEO_FRAME_OFFSET } from "../frame/VGEO_FRAME_OFFSET.js";
12
12
  import { vgeo_frame_decode_bound } from "../frame/vgeo_frame_decode_bound.js";
13
+ import { VGEO_GROUP_OFFSET } from "../page/VGEO_GROUP_OFFSET.js";
13
14
  import { VGEO_GROUP_RECORD_SIZE } from "../page/VGEO_GROUP_RECORD_SIZE.js";
14
15
  import { VGEO_METRIC_OFFSET } from "../page/VGEO_METRIC_OFFSET.js";
15
16
  import { VGEO_NODE_OFFSET } from "../page/VGEO_NODE_OFFSET.js";
@@ -298,10 +299,14 @@ function read_group_table(page) {
298
299
  for (let i = 0; i < count; i++) {
299
300
  const record = VGEO_PAGE_HEADER_SIZE + i * VGEO_GROUP_RECORD_SIZE;
300
301
 
301
- page.group_cluster_first[i] = data.getUint16(record, LE);
302
- page.group_cluster_count[i] = data.getUint16(record + 2, LE);
303
- page.group_parent_count[i] = data.getUint16(record + 4, LE);
304
- page.group_parent_first[i] = data.getUint16(record + 6, LE);
302
+ page.group_cluster_first[i] =
303
+ data.getUint16(record + VGEO_GROUP_OFFSET.CLUSTER_FIRST, LE);
304
+ page.group_cluster_count[i] =
305
+ data.getUint16(record + VGEO_GROUP_OFFSET.CLUSTER_COUNT, LE);
306
+ page.group_parent_count[i] =
307
+ data.getUint16(record + VGEO_GROUP_OFFSET.PARENT_CLUSTER_COUNT, LE);
308
+ page.group_parent_first[i] =
309
+ data.getUint16(record + VGEO_GROUP_OFFSET.PARENT_FIRST, LE);
305
310
  }
306
311
  }
307
312
 
@@ -1 +1 @@
1
- {"version":3,"file":"vgeo_validate_container.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js"],"names":[],"mappings":"AAgtDA;;;;;GAKG;AACH,gDAHW,YAAY,GACV,UAAU,EAAE,CAqFxB;6BAzyD4B,4CAA4C;2BAkD9C,iBAAiB"}
1
+ {"version":3,"file":"vgeo_validate_container.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js"],"names":[],"mappings":"AA6tDA;;;;;GAKG;AACH,gDAHW,YAAY,GACV,UAAU,EAAE,CAqFxB;6BAtzD4B,4CAA4C;2BAmD9C,iBAAiB"}
@@ -20,6 +20,7 @@ import { VGEO_FRAME_ALIGNMENT } from "./frame/VGEO_FRAME_ALIGNMENT.js";
20
20
  import { vgeo_frame_decode_bound } from "./frame/vgeo_frame_decode_bound.js";
21
21
  import { VGEO_FRAME_HEADER_SIZE } from "./frame/VGEO_FRAME_HEADER_SIZE.js";
22
22
  import { VGEO_FRAME_OFFSET } from "./frame/VGEO_FRAME_OFFSET.js";
23
+ import { VGEO_GROUP_OFFSET } from "./page/VGEO_GROUP_OFFSET.js";
23
24
  import { VGEO_GROUP_RECORD_SIZE } from "./page/VGEO_GROUP_RECORD_SIZE.js";
24
25
  import { VGEO_HASH_ALGO_XXH3 } from "./header/VGEO_HASH_ALGO_XXH3.js";
25
26
  import { VGEO_HEADER_OFFSET } from "./header/VGEO_HEADER_OFFSET.js";
@@ -1071,13 +1072,25 @@ function read_groups(page, out) {
1071
1072
  let expected_parent_first = 0;
1072
1073
 
1073
1074
  for (let g = 0; g < page.group_count; g++) {
1074
- page.seek(VGEO_PAGE_HEADER_SIZE + g * VGEO_GROUP_RECORD_SIZE);
1075
+ const record = VGEO_PAGE_HEADER_SIZE + g * VGEO_GROUP_RECORD_SIZE;
1076
+
1077
+ /**
1078
+ * Each field is sought to by name rather than read in a run, so that this audit and
1079
+ * `vgeo_read_page` address the record through the same table — a validator that agreed
1080
+ * with the reader only by having been written in the same order is a validator that stops
1081
+ * agreeing the first time a field moves.
1082
+ */
1083
+ const read = (field) => {
1084
+ page.seek(record + field);
1085
+
1086
+ return buffer.readUint16();
1087
+ };
1075
1088
 
1076
1089
  const group = {
1077
- cluster_first: buffer.readUint16(),
1078
- cluster_count: buffer.readUint16(),
1079
- parent_cluster_count: buffer.readUint16(),
1080
- parent_first: buffer.readUint16(),
1090
+ cluster_first: read(VGEO_GROUP_OFFSET.CLUSTER_FIRST),
1091
+ cluster_count: read(VGEO_GROUP_OFFSET.CLUSTER_COUNT),
1092
+ parent_cluster_count: read(VGEO_GROUP_OFFSET.PARENT_CLUSTER_COUNT),
1093
+ parent_first: read(VGEO_GROUP_OFFSET.PARENT_FIRST),
1081
1094
  };
1082
1095
 
1083
1096
  if (group.cluster_count < 1 || group.cluster_count > VGEO_MAX_GROUP_CLUSTER_COUNT) {
@@ -1 +1 @@
1
- {"version":3,"file":"vgeo_write_container.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/format/vgeo_write_container.js"],"names":[],"mappings":"AA4bA;;;;;;;;GAQG;AACH,4CANW,qBAAqB,8BAGnB,YAAY,CAmNxB;6BAppB4B,4CAA4C"}
1
+ {"version":3,"file":"vgeo_write_container.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/format/vgeo_write_container.js"],"names":[],"mappings":"AAocA;;;;;;;;GAQG;AACH,4CANW,qBAAqB,8BAGnB,YAAY,CAmNxB;6BA5pB4B,4CAA4C"}
@@ -17,6 +17,7 @@ import { VGEO_FORMAT_VERSION } from "./header/VGEO_FORMAT_VERSION.js";
17
17
  import { VGEO_FRAME_ALIGNMENT } from "./frame/VGEO_FRAME_ALIGNMENT.js";
18
18
  import { VGEO_FRAME_HEADER_SIZE } from "./frame/VGEO_FRAME_HEADER_SIZE.js";
19
19
  import { VGEO_FRAME_OFFSET } from "./frame/VGEO_FRAME_OFFSET.js";
20
+ import { VGEO_GROUP_OFFSET } from "./page/VGEO_GROUP_OFFSET.js";
20
21
  import { VGEO_GROUP_RECORD_SIZE } from "./page/VGEO_GROUP_RECORD_SIZE.js";
21
22
  import { VGEO_HASH_ALGO_XXH3 } from "./header/VGEO_HASH_ALGO_XXH3.js";
22
23
  import { VGEO_HEADER_OFFSET } from "./header/VGEO_HEADER_OFFSET.js";
@@ -355,11 +356,18 @@ function build_page_blob(page, child_pages, parents) {
355
356
  buffer.writeUint16(parent.cluster);
356
357
  });
357
358
 
358
- buffer.position = VGEO_PAGE_HEADER_SIZE + group_index * VGEO_GROUP_RECORD_SIZE;
359
+ const group_record = VGEO_PAGE_HEADER_SIZE + group_index * VGEO_GROUP_RECORD_SIZE;
359
360
 
361
+ buffer.position = group_record + VGEO_GROUP_OFFSET.CLUSTER_FIRST;
360
362
  buffer.writeUint16(cluster_index);
363
+
364
+ buffer.position = group_record + VGEO_GROUP_OFFSET.CLUSTER_COUNT;
361
365
  buffer.writeUint16(group.clusters.length);
366
+
367
+ buffer.position = group_record + VGEO_GROUP_OFFSET.PARENT_CLUSTER_COUNT;
362
368
  buffer.writeUint16(group.parent_cluster_count);
369
+
370
+ buffer.position = group_record + VGEO_GROUP_OFFSET.PARENT_FIRST;
363
371
  buffer.writeUint16(parent_first);
364
372
 
365
373
  parent_first += group_parents.length;
@@ -435,7 +435,7 @@ the view-dependent half, with the same eviction hooks.
435
435
  another site already moved.
436
436
 
437
437
  **There are not two sites — and that is a defect, not a shape to design around.** The
438
- phase-one seam call sits *inside* the `ShadeDrawMode × ShadeDrawSide` loop of
438
+ phase-one seam call sits *inside* the per-opaque-bucket loop of
439
439
  `graph_rasterize_partial_opaque`, so a standard view records one cut per **active opaque bucket**
440
440
  plus one for phase two. The engine already says so where rev 2 did not read it —
441
441
  `GPUVirtualGeometryRuntime`: "once per bucket per HZB pass per view".
@@ -518,7 +518,26 @@ the view-dependent half, with the same eviction hooks.
518
518
  checks instead of asking an asset its version — and `GENERATION`. `slot_range` learned the
519
519
  section, or the parent rows would never have been uploaded. Still nothing consumes them.
520
520
 
521
- **A verification surface went away mid-session, and §7.5 needs it.** The `vgeo_runtime`
521
+ **A verification surface went away mid-session and came back — steps 1–6 are confirmed in a
522
+ real browser.** Chrome's regression was worked around on master (`b3919f9c3`, `34c836d4d`:
523
+ the four indirectly dispatched culls, and then particles and SHARC, off immediates and onto a
524
+ uniform), and with that in place the `vgeo_runtime` playground draws again. Re-measured
525
+ against it afterwards:
526
+
527
+ - the in-page container, now written at `format_version` 4 with parent tables, builds, loads
528
+ and draws, and the cut shaders — the coarsen path and the tenth binding included — compile
529
+ under Tint with no console error;
530
+ - the deepest converted Zorah container, which is **`format_version` 3 and carries no parent
531
+ tables at all**, loads and draws under the format-4 reader at every camera tried. That is
532
+ §2.1's back-compatibility claim tested rather than argued, on 27.9M faces;
533
+ - §7.1's table reproduces exactly: `descent_steps` 32 takes 5 dispatches and 16 takes 6, both
534
+ with `QUEUE_REMAINING` 0, while 8 takes all 8 and leaves 12–124 items queued. The default
535
+ of 24 sits inside the clean band.
536
+
537
+ What follows is the note written while it was broken, kept because the diagnosis is what
538
+ makes the gap in the record legible:
539
+
540
+ **A verification surface went away mid-session, and §7.5 needed it.** The `vgeo_runtime`
522
541
  playground draws nothing in headless Chrome: `begin_frame` runs, the feedback resource is open,
523
542
  the adapter is an NVIDIA Lovelace with subgroups, the container builds and its root page installs
524
543
  with a selectable group — and `VGEO_CUT_STAT.PAGES` stays 0, so the seed never reaches its first
@@ -29,18 +29,21 @@ export class GPUVirtualGeometryRuntime {
29
29
  * @param {number} [options.claim_word_capacity] words of the per-invocation claim bitmask,
30
30
  * overriding the derivation below
31
31
  * @param {number} [options.queue_capacity] traversal items one wavefront may hold
32
- * @param {number} [options.max_iterations] wavefronts before the descent is abandoned
32
+ * @param {number} [options.max_iterations] wavefronts before the descent is abandoned, to start
33
+ * with; the runtime raises it from what the cut reports
34
+ * @param {number} [options.max_iteration_ceiling] as far as that raising may go
33
35
  * @param {number} [options.descent_steps] steps of the descent one traversal dispatch runs
34
36
  * before handing back what it still holds; see the traversal kernel for why it cannot
35
37
  * simply stop when the wave runs dry
36
38
  */
37
- constructor({ pool, want_capacity, visible_instance_hint, claim_word_capacity, queue_capacity, max_iterations, descent_steps, }: {
39
+ constructor({ pool, want_capacity, visible_instance_hint, claim_word_capacity, queue_capacity, max_iterations, max_iteration_ceiling, descent_steps, }: {
38
40
  pool: GPUVirtualGeometryPool;
39
41
  want_capacity?: number;
40
42
  visible_instance_hint?: number;
41
43
  claim_word_capacity?: number;
42
44
  queue_capacity?: number;
43
45
  max_iterations?: number;
46
+ max_iteration_ceiling?: number;
44
47
  descent_steps?: number;
45
48
  });
46
49
  /**
@@ -71,9 +74,56 @@ export class GPUVirtualGeometryRuntime {
71
74
  */
72
75
  get claim_word_capacity(): number;
73
76
  /**
77
+ * Wavefronts the descent may run, as the runtime currently understands this scene.
78
+ *
79
+ * A **high-water mark**, not a setting: it starts where the host put it and is raised by
80
+ * {@link note_incomplete_descent} from what the cut reports. `graph_vgeo_expand_instances_to_meshlets`
81
+ * reads it while it is recording passes, so a rise lands on the next frame.
82
+ *
74
83
  * @returns {number}
75
84
  */
76
85
  get max_iterations(): number;
86
+ /**
87
+ * Times the bound has been raised. Zero for the whole life of almost every scene; a small
88
+ * number, once, for one that needed more than it was given.
89
+ *
90
+ * @returns {number}
91
+ */
92
+ get iteration_escalations(): number;
93
+ /**
94
+ * @returns {number}
95
+ */
96
+ get iteration_ceiling(): number;
97
+ /**
98
+ * A reading saying the descent stopped with work still queued — raise the bound and keep it
99
+ * raised.
100
+ *
101
+ * **This is the only place the engine can learn it.** How many wavefronts a cut needs is a
102
+ * function of the frontier's *breadth*, which is a function of the scene, the viewpoint and the
103
+ * render target together; the kernel cannot know it before it runs and the host cannot know it
104
+ * at all. So the bound is guessed low, and the one reading that says the guess was wrong —
105
+ * `VGEO_CUT_STAT.QUEUE_REMAINING`, non-zero exactly when items were abandoned — pays for
106
+ * correcting it. It is the same shape as the rasterizer's meshlet counts, which cannot be known
107
+ * before the expansion runs either and are grown from what the frame reported.
108
+ *
109
+ * **Doubling, because the reading does not say how much more was needed.** A remainder is a
110
+ * count of *items*, and what they cost in wavefronts depends on how they spill; there is no
111
+ * ratio to convert one into the other. So it goes up by a factor until it drains, which is at
112
+ * most three steps from 8 to the ceiling.
113
+ *
114
+ * **Never lowered.** A frame that drained is evidence about that frame, not that the scene got
115
+ * smaller — and a bound that fell back would re-learn the same lesson every time the camera
116
+ * turned away and back, at a hole a time. An unused wavefront is an empty indirect dispatch,
117
+ * which is the cheap side of that trade.
118
+ *
119
+ * What it costs when it is wrong is bounded by the readback: the cut is capped at two readings
120
+ * in flight and skips frames when saturated, so the scene draws with a hole for the few frames
121
+ * between the first truncated descent and the reading that reports it — and then never again.
122
+ *
123
+ * @param {number} remaining items the descent still held when it was abandoned
124
+ * @returns {boolean} whether the bound moved
125
+ */
126
+ note_incomplete_descent(remaining: number): boolean;
77
127
  /**
78
128
  * Settable, like tau, and for the same reason: it is a quality-of-schedule dial whose right
79
129
  * value is a property of the asset's depth and the scene's breadth together rather than of the
@@ -1 +1 @@
1
- {"version":3,"file":"GPUVirtualGeometryRuntime.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js"],"names":[],"mappings":"AAKA;;;GAGG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH;IAwEI;;;;;;;;;;;;;OAaG;IACH;QAZ2C,IAAI,EAApC,sBAAsB;QACL,aAAa,GAA9B,MAAM;QACW,qBAAqB,GAAtC,MAAM;QAEW,mBAAmB,GAApC,MAAM;QAEW,cAAc,GAA/B,MAAM;QACW,cAAc,GAA/B,MAAM;QACW,aAAa,GAA9B,MAAM;OA+DhB;IA/GD;;;OAGG;IACH,KAFU,MAAM,CAER;IAER;;OAEG;IACH,cAFU,OAAO,CAEG;IAwGpB;;OAEG;IACH,yEAEC;IAED;;;;OAIG;IACH,4BAEC;IAED;;OAEG;IACH,6BAEC;IAED;;OAEG;IACH,kCAEC;IAED;;OAEG;IACH,6BAEC;IAWD;;;;;;OAMG;IACH,iCAKC;IArBD;;;;OAIG;IACH,4BAEC;IAgBD;;;OAGG;IACH,uBAEC;IAED;;;;;;;;;;;;;;OAcG;IACH,yBAHW,MAAM,GACJ,IAAI,CAMhB;IAED;;OAEG;IACH;;QAvOA;;WAEG;;;4BAIH;;WAEG;MAiOF;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH;QAH+B,KAAK,EAAzB,UAAU;QACR,MAAM,CA+BlB;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH;QAJ2B,iBAAiB,EAAjC,MAAM;QACU,gBAAgB,EAAhC,MAAM;QACJ,IAAI,CAahB;IAED;;OAEG;IACH,gCAEC;IAED;;OAEG;IACH,+BAEC;IAED;;;;;;;;;;OAUG;IACH,wGAEC;IAGD;;;;;OAKG;IACH,aAFa,IAAI,CAKhB;;CACJ;qCAtYY,OAAO,6BAA6B,EAAE,sBAAsB;yBAC5D,OAAO,iEAAiE,EAAE,UAAU"}
1
+ {"version":3,"file":"GPUVirtualGeometryRuntime.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js"],"names":[],"mappings":"AAKA;;;GAGG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH;IAkFI;;;;;;;;;;;;;;;OAeG;IACH;QAd2C,IAAI,EAApC,sBAAsB;QACL,aAAa,GAA9B,MAAM;QACW,qBAAqB,GAAtC,MAAM;QAEW,mBAAmB,GAApC,MAAM;QAEW,cAAc,GAA/B,MAAM;QACW,cAAc,GAA/B,MAAM;QAEW,qBAAqB,GAAtC,MAAM;QACW,aAAa,GAA9B,MAAM;OAkFhB;IApID;;;OAGG;IACH,KAFU,MAAM,CAER;IAER;;OAEG;IACH,cAFU,OAAO,CAEG;IA6HpB;;OAEG;IACH,yEAEC;IAED;;;;OAIG;IACH,4BAEC;IAED;;OAEG;IACH,6BAEC;IAED;;OAEG;IACH,kCAEC;IAED;;;;;;;;OAQG;IACH,6BAEC;IAED;;;;;OAKG;IACH,oCAEC;IAED;;OAEG;IACH,gCAEC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,mCAHW,MAAM,GACJ,OAAO,CAanB;IAWD;;;;;;OAMG;IACH,iCAKC;IArBD;;;;OAIG;IACH,4BAEC;IAgBD;;;OAGG;IACH,uBAEC;IAED;;;;;;;;;;;;;;OAcG;IACH,yBAHW,MAAM,GACJ,IAAI,CAMhB;IAED;;OAEG;IACH;;QAvUA;;WAEG;;;4BAIH;;WAEG;MAiUF;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH;QAH+B,KAAK,EAAzB,UAAU;QACR,MAAM,CA+BlB;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH;QAJ2B,iBAAiB,EAAjC,MAAM;QACU,gBAAgB,EAAhC,MAAM;QACJ,IAAI,CAahB;IAED;;OAEG;IACH,gCAEC;IAED;;OAEG;IACH,+BAEC;IAED;;;;;;;;;;OAUG;IACH,wGAEC;IAGD;;;;;OAKG;IACH,aAFa,IAAI,CAKhB;;CACJ;qCAteY,OAAO,6BAA6B,EAAE,sBAAsB;yBAC5D,OAAO,iEAAiE,EAAE,UAAU"}
@@ -54,6 +54,16 @@ export class GPUVirtualGeometryRuntime {
54
54
  */
55
55
  #max_iterations;
56
56
 
57
+ /**
58
+ * @type {number}
59
+ */
60
+ #iteration_ceiling;
61
+
62
+ /**
63
+ * @type {number}
64
+ */
65
+ #iteration_escalations = 0;
66
+
57
67
  /**
58
68
  * @type {number}
59
69
  */
@@ -106,7 +116,9 @@ export class GPUVirtualGeometryRuntime {
106
116
  * @param {number} [options.claim_word_capacity] words of the per-invocation claim bitmask,
107
117
  * overriding the derivation below
108
118
  * @param {number} [options.queue_capacity] traversal items one wavefront may hold
109
- * @param {number} [options.max_iterations] wavefronts before the descent is abandoned
119
+ * @param {number} [options.max_iterations] wavefronts before the descent is abandoned, to start
120
+ * with; the runtime raises it from what the cut reports
121
+ * @param {number} [options.max_iteration_ceiling] as far as that raising may go
110
122
  * @param {number} [options.descent_steps] steps of the descent one traversal dispatch runs
111
123
  * before handing back what it still holds; see the traversal kernel for why it cannot
112
124
  * simply stop when the wave runs dry
@@ -140,7 +152,25 @@ export class GPUVirtualGeometryRuntime {
140
152
  with work outstanding, which is the one degradation that costs a hole.
141
153
  */
142
154
  queue_capacity = 1 << 16,
155
+ /*
156
+ Where the wavefront bound *starts*. It is raised by the runtime itself when a
157
+ reading says the descent could not finish — see {@link note_incomplete_descent}
158
+ — so this is the cost a scene pays until it has been told otherwise rather than
159
+ a limit anyone has to get right in advance.
160
+
161
+ Eight is what almost every cut needs: one container at a modest viewport drains
162
+ in six. A converted scene at 3441×1440 does not, and nothing on the host can
163
+ know that before the kernel has run.
164
+ */
143
165
  max_iterations = 8,
166
+ /*
167
+ And where it stops. A frontier that genuinely cannot be drained inside any
168
+ budget would otherwise climb until the frame graph is nothing but empty
169
+ dispatches; a hole is the better failure, because it is visible and
170
+ `QUEUE_REMAINING` goes on reporting it. Measured peak need on the scene that
171
+ found this was 12 to 13.
172
+ */
173
+ max_iteration_ceiling = 64,
144
174
  /*
145
175
  Steps of the in-kernel descent one dispatch runs, and the knob that is *not*
146
176
  the safety bound — `max_iterations` is, and lowering this spends it. A wave
@@ -167,6 +197,7 @@ export class GPUVirtualGeometryRuntime {
167
197
  this.#claim_word_capacity = claim_word_capacity;
168
198
  this.#queue_capacity = queue_capacity;
169
199
  this.#max_iterations = max_iterations;
200
+ this.#iteration_ceiling = Math.max(max_iterations, max_iteration_ceiling);
170
201
  this.#descent_steps = descent_steps;
171
202
 
172
203
 
@@ -203,12 +234,77 @@ export class GPUVirtualGeometryRuntime {
203
234
  }
204
235
 
205
236
  /**
237
+ * Wavefronts the descent may run, as the runtime currently understands this scene.
238
+ *
239
+ * A **high-water mark**, not a setting: it starts where the host put it and is raised by
240
+ * {@link note_incomplete_descent} from what the cut reports. `graph_vgeo_expand_instances_to_meshlets`
241
+ * reads it while it is recording passes, so a rise lands on the next frame.
242
+ *
206
243
  * @returns {number}
207
244
  */
208
245
  get max_iterations() {
209
246
  return this.#max_iterations;
210
247
  }
211
248
 
249
+ /**
250
+ * Times the bound has been raised. Zero for the whole life of almost every scene; a small
251
+ * number, once, for one that needed more than it was given.
252
+ *
253
+ * @returns {number}
254
+ */
255
+ get iteration_escalations() {
256
+ return this.#iteration_escalations;
257
+ }
258
+
259
+ /**
260
+ * @returns {number}
261
+ */
262
+ get iteration_ceiling() {
263
+ return this.#iteration_ceiling;
264
+ }
265
+
266
+ /**
267
+ * A reading saying the descent stopped with work still queued — raise the bound and keep it
268
+ * raised.
269
+ *
270
+ * **This is the only place the engine can learn it.** How many wavefronts a cut needs is a
271
+ * function of the frontier's *breadth*, which is a function of the scene, the viewpoint and the
272
+ * render target together; the kernel cannot know it before it runs and the host cannot know it
273
+ * at all. So the bound is guessed low, and the one reading that says the guess was wrong —
274
+ * `VGEO_CUT_STAT.QUEUE_REMAINING`, non-zero exactly when items were abandoned — pays for
275
+ * correcting it. It is the same shape as the rasterizer's meshlet counts, which cannot be known
276
+ * before the expansion runs either and are grown from what the frame reported.
277
+ *
278
+ * **Doubling, because the reading does not say how much more was needed.** A remainder is a
279
+ * count of *items*, and what they cost in wavefronts depends on how they spill; there is no
280
+ * ratio to convert one into the other. So it goes up by a factor until it drains, which is at
281
+ * most three steps from 8 to the ceiling.
282
+ *
283
+ * **Never lowered.** A frame that drained is evidence about that frame, not that the scene got
284
+ * smaller — and a bound that fell back would re-learn the same lesson every time the camera
285
+ * turned away and back, at a hole a time. An unused wavefront is an empty indirect dispatch,
286
+ * which is the cheap side of that trade.
287
+ *
288
+ * What it costs when it is wrong is bounded by the readback: the cut is capped at two readings
289
+ * in flight and skips frames when saturated, so the scene draws with a hole for the few frames
290
+ * between the first truncated descent and the reading that reports it — and then never again.
291
+ *
292
+ * @param {number} remaining items the descent still held when it was abandoned
293
+ * @returns {boolean} whether the bound moved
294
+ */
295
+ note_incomplete_descent(remaining) {
296
+ assert.isNonNegativeInteger(remaining, 'remaining');
297
+
298
+ if (remaining === 0 || this.#max_iterations >= this.#iteration_ceiling) {
299
+ return false;
300
+ }
301
+
302
+ this.#max_iterations = Math.min(this.#max_iterations * 2, this.#iteration_ceiling);
303
+ this.#iteration_escalations++;
304
+
305
+ return true;
306
+ }
307
+
212
308
  /**
213
309
  * Steps of the descent one traversal dispatch runs.
214
310
  *