@woosh/meep-engine 3.26.0 → 3.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (449) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +542 -0
  4. package/src/core/binary/lz4/LZ4_LEVEL.d.ts +33 -0
  5. package/src/core/binary/lz4/LZ4_LEVEL.d.ts.map +1 -0
  6. package/src/core/binary/lz4/LZ4_LEVEL.js +32 -0
  7. package/src/core/binary/lz4/lz4_compress_block_at_level.d.ts +33 -0
  8. package/src/core/binary/lz4/lz4_compress_block_at_level.d.ts.map +1 -0
  9. package/src/core/binary/lz4/lz4_compress_block_at_level.js +47 -0
  10. package/src/core/binary/lz4/lz4_compress_block_hc.d.ts +40 -0
  11. package/src/core/binary/lz4/lz4_compress_block_hc.d.ts.map +1 -0
  12. package/src/core/binary/lz4/lz4_compress_block_hc.js +1382 -0
  13. package/src/core/binary/lz4/lz4_decompress_block.d.ts +2 -1
  14. package/src/core/binary/lz4/lz4_decompress_block.d.ts.map +1 -1
  15. package/src/core/binary/lz4/lz4_decompress_block.js +31 -6
  16. package/src/core/geom/3d/mat4/m4_linear_determinant.d.ts +15 -0
  17. package/src/core/geom/3d/mat4/m4_linear_determinant.d.ts.map +1 -0
  18. package/src/core/geom/3d/mat4/m4_linear_determinant.js +24 -0
  19. package/src/core/geom/3d/mat4/m4_normal_matrix3.d.ts +4 -2
  20. package/src/core/geom/3d/mat4/m4_normal_matrix3.d.ts.map +1 -1
  21. package/src/core/geom/3d/mat4/m4_normal_matrix3.js +21 -11
  22. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +14 -7
  23. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
  24. package/src/core/geom/3d/shape/HeightMapShape3D.js +87 -78
  25. package/src/engine/Engine.d.ts.map +1 -1
  26. package/src/engine/Engine.js +38 -1
  27. package/src/engine/graphics3/GraphicsEngine.d.ts +5 -1
  28. package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -1
  29. package/src/engine/graphics3/GraphicsEngine.js +834 -830
  30. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  31. package/src/shade/RENDERER_CONTRACT.md +19 -10
  32. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +1 -1
  33. package/src/shade/playground/vgeo_runtime/README.md +186 -27
  34. package/src/shade/playground/vgeo_runtime/index.html +112 -62
  35. package/src/shade/playground/vgeo_runtime/main.js +678 -88
  36. package/src/shade/playground/vgeo_runtime/sample_asset.d.ts +18 -0
  37. package/src/shade/playground/vgeo_runtime/sample_asset.d.ts.map +1 -0
  38. package/src/shade/playground/{vgeo_viewer → vgeo_runtime}/sample_asset.js +23 -5
  39. package/src/shade/playground/vgeo_scene/README.md +408 -0
  40. package/src/shade/playground/vgeo_scene/index.html +144 -0
  41. package/src/shade/playground/vgeo_scene/main.d.ts +2 -0
  42. package/src/shade/playground/vgeo_scene/main.d.ts.map +1 -0
  43. package/src/shade/playground/vgeo_scene/main.js +1716 -0
  44. package/src/shade/playground/vgeo_viewer/sample_asset.d.ts.map +1 -1
  45. package/src/shade/renderer/Renderer.d.ts +4 -1
  46. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  47. package/src/shade/renderer/Renderer.js +5 -2
  48. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +1 -2
  49. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts +11 -0
  50. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
  51. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +16 -8
  52. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
  53. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +16 -11
  54. package/src/shade/renderer/geometry/meshlet/MeshletBatch.d.ts.map +1 -1
  55. package/src/shade/renderer/geometry/meshlet/MeshletBatch.js +3 -29
  56. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.d.ts +22 -0
  57. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.d.ts.map +1 -0
  58. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.js +49 -0
  59. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +77 -7
  60. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +36 -23
  61. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +17 -14
  62. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +19 -1
  63. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
  64. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +21 -1
  65. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.d.ts.map +1 -1
  66. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.js +5 -9
  67. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevel.d.ts +32 -0
  68. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevel.d.ts.map +1 -0
  69. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts +59 -0
  70. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts.map +1 -0
  71. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelSummary.d.ts +44 -0
  72. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelSummary.d.ts.map +1 -0
  73. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts +36 -0
  74. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts.map +1 -0
  75. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_levels.js → level/vgeo_build_levels.js} +61 -95
  76. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_root_group.d.ts +19 -0
  77. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_root_group.d.ts.map +1 -0
  78. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_root_group.js → level/vgeo_build_root_group.js} +1 -1
  79. package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts +73 -0
  80. package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts.map +1 -0
  81. package/src/shade/renderer/geometry/virtual/build/{VirtualGeometryMesh.js → mesh/VirtualGeometryMesh.js} +7 -7
  82. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +29 -0
  83. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -0
  84. package/src/shade/renderer/geometry/virtual/build/{vgeo_compute_vertex_normals.js → mesh/vgeo_compute_vertex_normals.js} +3 -3
  85. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts +31 -0
  86. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts.map +1 -0
  87. package/src/shade/renderer/geometry/virtual/build/{vgeo_compute_vertex_tangents.js → mesh/vgeo_compute_vertex_tangents.js} +5 -5
  88. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts +46 -0
  89. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -0
  90. package/src/shade/renderer/geometry/virtual/build/{vgeo_normalize_source.js → mesh/vgeo_normalize_source.js} +9 -9
  91. package/src/shade/renderer/geometry/virtual/build/page/vgeo_assemble_pages.d.ts +13 -0
  92. package/src/shade/renderer/geometry/virtual/build/page/vgeo_assemble_pages.d.ts.map +1 -0
  93. package/src/shade/renderer/geometry/virtual/build/{vgeo_assemble_pages.js → page/vgeo_assemble_pages.js} +63 -16
  94. package/src/shade/renderer/geometry/virtual/build/page/vgeo_build_page_nodes.d.ts +13 -0
  95. package/src/shade/renderer/geometry/virtual/build/page/vgeo_build_page_nodes.d.ts.map +1 -0
  96. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_page_nodes.js → page/vgeo_build_page_nodes.js} +4 -4
  97. package/src/shade/renderer/geometry/virtual/build/page/vgeo_page_node_count.d.ts +22 -0
  98. package/src/shade/renderer/geometry/virtual/build/page/vgeo_page_node_count.d.ts.map +1 -0
  99. package/src/shade/renderer/geometry/virtual/build/{vgeo_page_node_count.js → page/vgeo_page_node_count.js} +2 -2
  100. package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts +52 -0
  101. package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts.map +1 -0
  102. package/src/shade/renderer/geometry/virtual/build/{VGeoFaceGraph.js → partition/VGeoFaceGraph.js} +1 -1
  103. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.d.ts +14 -0
  104. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.d.ts.map +1 -0
  105. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_graph.d.ts +29 -0
  106. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_graph.d.ts.map +1 -0
  107. package/src/shade/renderer/geometry/virtual/build/{vgeo_partition_graph.js → partition/vgeo_partition_graph.js} +2 -2
  108. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.d.ts +41 -0
  109. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.d.ts.map +1 -0
  110. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.js +92 -0
  111. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.d.ts +10 -0
  112. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.d.ts.map +1 -0
  113. package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set.js → partition/vgeo_split_face_set.js} +12 -55
  114. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts +15 -0
  115. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts.map +1 -0
  116. package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set_packed.js → partition/vgeo_split_face_set_packed.js} +3 -3
  117. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_bounding_sphere.d.ts +12 -0
  118. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_bounding_sphere.d.ts.map +1 -0
  119. package/src/shade/renderer/geometry/virtual/build/{vgeo_bounding_sphere.js → sphere/vgeo_bounding_sphere.js} +3 -3
  120. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_containing_sphere.d.ts +18 -0
  121. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_containing_sphere.d.ts.map +1 -0
  122. package/src/shade/renderer/geometry/virtual/build/{vgeo_containing_sphere.js → sphere/vgeo_containing_sphere.js} +3 -3
  123. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_sphere_inflate_to_contain.d.ts +9 -0
  124. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_sphere_inflate_to_contain.d.ts.map +1 -0
  125. package/src/shade/renderer/geometry/virtual/build/{vgeo_sphere_inflate_to_contain.js → sphere/vgeo_sphere_inflate_to_contain.js} +1 -1
  126. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts +24 -0
  127. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts.map +1 -0
  128. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts +20 -0
  129. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts.map +1 -0
  130. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts +14 -0
  131. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts.map +1 -0
  132. package/src/shade/renderer/geometry/virtual/build/{vgeo_extract_submesh.js → submesh/vgeo_extract_submesh.js} +5 -5
  133. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts +12 -0
  134. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts.map +1 -0
  135. package/src/shade/renderer/geometry/virtual/build/{vgeo_gather_faces.js → submesh/vgeo_gather_faces.js} +2 -2
  136. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_narrow_indices.d.ts +11 -0
  137. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_narrow_indices.d.ts.map +1 -0
  138. package/src/shade/renderer/geometry/virtual/build/{vgeo_narrow_indices.js → submesh/vgeo_narrow_indices.js} +3 -3
  139. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +1 -1
  140. package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts.map +1 -1
  141. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +10 -11
  142. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +22 -0
  143. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
  144. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +24 -0
  145. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.d.ts +8 -2
  146. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.d.ts.map +1 -1
  147. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js +8 -2
  148. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts +12 -0
  149. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts.map +1 -1
  150. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +13 -0
  151. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts +8 -1
  152. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts.map +1 -1
  153. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +11 -3
  154. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts +9 -0
  155. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts.map +1 -1
  156. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +10 -1
  157. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.d.ts +15 -0
  158. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.d.ts.map +1 -0
  159. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.js +15 -0
  160. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.d.ts +18 -0
  161. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.d.ts.map +1 -0
  162. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.js +17 -0
  163. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.d.ts +8 -0
  164. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.d.ts.map +1 -0
  165. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.js +7 -0
  166. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.d.ts +3 -0
  167. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +16 -0
  168. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.d.ts +14 -0
  169. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.d.ts.map +1 -0
  170. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.js +13 -0
  171. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.d.ts +11 -0
  172. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.d.ts.map +1 -0
  173. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.js +10 -0
  174. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts +28 -0
  175. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts.map +1 -1
  176. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +7 -3
  177. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts +20 -0
  178. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts.map +1 -1
  179. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.js +20 -2
  180. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts +4 -0
  181. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts.map +1 -1
  182. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.js +4 -0
  183. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts +29 -3
  184. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts.map +1 -1
  185. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.js +81 -122
  186. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +40 -0
  187. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
  188. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +44 -0
  189. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts +69 -0
  190. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts.map +1 -0
  191. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.js +236 -0
  192. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts +14 -0
  193. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts.map +1 -1
  194. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.js +15 -0
  195. package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.d.ts +31 -0
  196. package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.d.ts.map +1 -0
  197. package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.js +32 -0
  198. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts +10 -0
  199. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts.map +1 -1
  200. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.js +21 -1
  201. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.d.ts +15 -0
  202. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.d.ts.map +1 -0
  203. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.js +84 -0
  204. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts +22 -0
  205. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts.map +1 -0
  206. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.js +96 -0
  207. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
  208. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +63 -3
  209. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
  210. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +219 -17
  211. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
  212. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +156 -11
  213. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +656 -0
  214. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts +33 -36
  215. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts.map +1 -1
  216. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.js +109 -193
  217. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +109 -29
  218. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
  219. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +173 -105
  220. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +37 -0
  221. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +109 -26
  222. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +548 -0
  223. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts +29 -50
  224. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -1
  225. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +55 -487
  226. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts +84 -0
  227. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts.map +1 -0
  228. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.js +393 -0
  229. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts +234 -0
  230. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -0
  231. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +1388 -0
  232. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.d.ts +81 -0
  233. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.d.ts.map +1 -0
  234. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.js +89 -0
  235. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts +53 -34
  236. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts.map +1 -1
  237. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.js +407 -124
  238. package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.d.ts +100 -0
  239. package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.d.ts.map +1 -0
  240. package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.js +219 -0
  241. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.d.ts +17 -0
  242. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.d.ts.map +1 -0
  243. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.js +47 -0
  244. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.d.ts +20 -0
  245. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.d.ts.map +1 -0
  246. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.js +19 -0
  247. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts.map +1 -1
  248. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.js +30 -12
  249. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts +8 -8
  250. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -1
  251. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +11 -73
  252. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts +19 -0
  253. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts.map +1 -0
  254. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.js +98 -0
  255. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts +31 -0
  256. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts.map +1 -0
  257. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.js +99 -0
  258. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.d.ts +17 -0
  259. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.d.ts.map +1 -0
  260. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.js +39 -0
  261. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts.map +1 -1
  262. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.js +18 -1
  263. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts.map +1 -1
  264. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.js +5 -15
  265. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.d.ts +3 -0
  266. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.d.ts.map +1 -0
  267. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.js +77 -0
  268. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts +3 -0
  269. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts.map +1 -0
  270. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.js +130 -0
  271. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts.map +1 -1
  272. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js +32 -37
  273. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -1
  274. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +576 -230
  275. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts +15 -18
  276. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts.map +1 -1
  277. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.js +19 -28
  278. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts.map +1 -1
  279. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.js +45 -25
  280. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +1 -0
  281. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +11 -0
  282. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +1 -1
  283. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts +0 -1
  284. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.js +12 -10
  285. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts +1 -0
  286. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.js +8 -0
  287. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts +7 -2
  288. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts.map +1 -1
  289. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.js +8 -3
  290. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts +2 -0
  291. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.js +19 -0
  292. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts.map +1 -1
  293. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.js +1 -1
  294. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.d.ts +5 -0
  295. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.d.ts.map +1 -0
  296. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.js +22 -0
  297. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.d.ts +14 -0
  298. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.d.ts.map +1 -0
  299. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.js +13 -0
  300. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.d.ts +17 -0
  301. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.d.ts.map +1 -0
  302. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.js +16 -0
  303. package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.d.ts +15 -0
  304. package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.d.ts.map +1 -0
  305. package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.js +16 -0
  306. package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.d.ts +23 -0
  307. package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.d.ts.map +1 -0
  308. package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.js +28 -0
  309. package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.d.ts +2 -0
  310. package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.d.ts.map +1 -0
  311. package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.js +13 -0
  312. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts +10 -5
  313. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts.map +1 -1
  314. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.js +15 -6
  315. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts +6 -1
  316. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -1
  317. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +38 -1
  318. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.d.ts.map +1 -1
  319. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.js +4 -3
  320. package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
  321. package/src/shade/renderer/loader/gltf/load_gltf.js +0 -19
  322. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  323. package/src/shade/renderer/material/chunk_material_draw_side.d.ts +14 -0
  324. package/src/shade/renderer/material/chunk_material_draw_side.d.ts.map +1 -0
  325. package/src/shade/renderer/material/chunk_material_draw_side.js +34 -0
  326. package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts.map +1 -1
  327. package/src/shade/renderer/material/standard/fragment_gbuffer.js +8 -1
  328. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  329. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +6 -3
  330. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  331. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +6 -3
  332. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
  333. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +13 -1
  334. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  335. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts +19 -5
  336. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts.map +1 -1
  337. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +23 -5
  338. package/src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.d.ts.map +1 -1
  339. package/src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.js +12 -5
  340. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  341. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +12 -4
  342. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.d.ts +22 -0
  343. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.d.ts.map +1 -0
  344. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.js +36 -0
  345. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts +2 -0
  346. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
  347. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +58 -57
  348. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts +15 -0
  349. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts.map +1 -0
  350. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.js +35 -0
  351. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.d.ts.map +1 -1
  352. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.js +3 -5
  353. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.d.ts.map +1 -1
  354. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.js +3 -4
  355. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts +10 -1
  356. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts.map +1 -1
  357. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.js +38 -29
  358. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.d.ts +5 -0
  359. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.d.ts.map +1 -1
  360. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.js +63 -55
  361. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
  362. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +3 -4
  363. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts.map +1 -1
  364. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.js +1 -0
  365. package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.d.ts.map +1 -1
  366. package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.js +7 -7
  367. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts.map +1 -1
  368. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.js +83 -78
  369. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  370. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +44 -53
  371. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.d.ts.map +1 -1
  372. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.js +6 -2
  373. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.d.ts.map +1 -1
  374. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.js +6 -2
  375. package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.d.ts.map +1 -1
  376. package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.js +19 -2
  377. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +7 -4
  378. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  379. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +3 -2
  380. package/src/shade/renderer/rasterize/native/chunk_rasterize_triangle.d.ts.map +1 -1
  381. package/src/shade/renderer/rasterize/native/chunk_rasterize_triangle.js +15 -4
  382. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
  383. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.js +16 -5
  384. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.d.ts.map +1 -1
  385. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.js +16 -5
  386. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts.map +1 -1
  387. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +2 -1
  388. package/src/shade/renderer/rasterize/native/rasterization_shader_resources.d.ts.map +1 -1
  389. package/src/shade/renderer/rasterize/native/rasterization_shader_resources.js +4 -0
  390. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
  391. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.js +16 -7
  392. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  393. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +85 -89
  394. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  395. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +28 -32
  396. package/src/shade/renderer/scene/GPUSceneContext.d.ts +22 -2
  397. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  398. package/src/shade/renderer/scene/GPUSceneContext.js +61 -4
  399. package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts +19 -0
  400. package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts.map +1 -1
  401. package/src/shade/renderer/scene/NODE3D_STRUCT.js +34 -0
  402. package/src/shade/renderer/scene/chunk_node3d_flags.d.ts +9 -0
  403. package/src/shade/renderer/scene/chunk_node3d_flags.d.ts.map +1 -0
  404. package/src/shade/renderer/scene/chunk_node3d_flags.js +23 -0
  405. package/src/shade/renderer/scene/chunk_scene_database_access.d.ts.map +1 -1
  406. package/src/shade/renderer/scene/chunk_scene_database_access.js +15 -1
  407. package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
  408. package/src/shade/renderer/scene/rows/GPUSceneRows.js +9 -1
  409. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.d.ts +7 -0
  410. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.d.ts.map +1 -1
  411. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.js +16 -4
  412. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts +12 -0
  413. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts.map +1 -0
  414. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.js +18 -0
  415. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.d.ts +24 -0
  416. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.d.ts.map +1 -0
  417. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.js +33 -0
  418. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
  419. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +92 -16
  420. package/src/shade/renderer/view/GPUViewContext.d.ts +5 -0
  421. package/src/shade/renderer/view/GPUViewContext.d.ts.map +1 -1
  422. package/src/shade/renderer/view/GPUViewContext.js +21 -0
  423. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  424. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +74 -3
  425. package/src/shade/playground/vgeo_viewer/README.md +0 -150
  426. package/src/shade/playground/vgeo_viewer/cut_geometry.js +0 -460
  427. package/src/shade/playground/vgeo_viewer/index.html +0 -77
  428. package/src/shade/playground/vgeo_viewer/main.js +0 -1343
  429. package/src/shade/playground/vgeo_viewer/select_cut.js +0 -481
  430. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts +0 -258
  431. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts.map +0 -1
  432. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.js +0 -723
  433. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts +0 -49
  434. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts.map +0 -1
  435. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.js +0 -50
  436. package/src/shade/renderer/loader/gltf/fix_up_material_sides.js +0 -109
  437. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.js +0 -78
  438. package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.js +0 -100
  439. package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.js +0 -26
  440. package/src/shade/renderer/shader/chunk/jobs/NOTES.md +0 -47
  441. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.d.ts +0 -3
  442. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.d.ts.map +0 -1
  443. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.js +0 -102
  444. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevel.js → level/VGeoLevel.js} +0 -0
  445. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevelOptions.js → level/VGeoLevelOptions.js} +0 -0
  446. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevelSummary.js → level/VGeoLevelSummary.js} +0 -0
  447. /package/src/shade/renderer/geometry/virtual/build/{vgeo_graph_components.js → partition/vgeo_graph_components.js} +0 -0
  448. /package/src/shade/renderer/geometry/virtual/build/{VGeoGatheredFaces.js → submesh/VGeoGatheredFaces.js} +0 -0
  449. /package/src/shade/renderer/geometry/virtual/build/{VGeoSubmesh.js → submesh/VGeoSubmesh.js} +0 -0
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "description": "Advanced 3D game engine",
7
7
  "type": "module",
8
8
  "author": "Alexander Goldring",
9
- "version": "3.26.0",
9
+ "version": "3.28.0",
10
10
  "exports": {
11
11
  "./build/*": "./build/*",
12
12
  "./src/*": "./src/*",
@@ -0,0 +1,542 @@
1
+ # LZ4_HIGH_COMPRESSION_PLAN — `src/core/binary/lz4`
2
+
3
+ Date: 2026-09-14 · Adds a compression level to the encoder. The format does not change and
4
+ [lz4_decompress_block.js](lz4_decompress_block.js) is not touched.
5
+
6
+ Every number below was measured on this machine on 2026-09-14, against real data. The measurement
7
+ recipes are in §9 so any of it can be re-run.
8
+
9
+ ---
10
+
11
+ ## 1 · The question, and the answer
12
+
13
+ > Add a configurable compression level that gets us to the current state-of-the-art maximum, without
14
+ > changing the format.
15
+
16
+ **The state of the art for the LZ4 block format is `lz4hc` level 12, and there is nothing
17
+ meaningful past it.** An encoder built with an *uncapped* match search and a shortest-path parse over
18
+ the whole block — strictly more search than level 12 is allowed — was measured against level 12:
19
+
20
+ | corpus | level 12 | uncapped search + whole-block parse | time |
21
+ |---|---|---|---|
22
+ | 60 real `.vgeo` pages (1.77 MB) | 1,531,612 B | **1,531,612 B** — identical | 1.6× slower |
23
+ | 24 text chunks (1.57 MB) | 356,238 B | 356,233 B — **0.0014% smaller** | 7.9× slower |
24
+ | 18 runs and periodic blocks (74 KB) | 4,225 B | **4,225 B** — identical | 110× slower |
25
+
26
+ Five bytes in a third of a megabyte, for eight times the work — and on repetitive data the uncapped
27
+ parse is 110× level 12 and 3,500× level 9, because dropping the reference's `sufficient_len` early
28
+ exit is exactly what lets a run of zeros cost quadratic time. The ceiling is level 12; the plan is to
29
+ reach it and stop.
30
+
31
+ A JS port of `lz4hc` was written and validated before this plan was drafted: **20,976 blocks over
32
+ 1,947 inputs, at every level from 2 to 12, byte for byte identical to liblz4**, first try, including
33
+ runs, periodic data, matches at the far edge of the 64KB window, engine source, glTF JSON and real
34
+ geometry pages. The work is a known quantity, not an experiment.
35
+
36
+ ---
37
+
38
+ ## 2 · What it is worth
39
+
40
+ Corpus: **2,636 decoded pages, 140,268,332 bytes**, taken from 120 of the 2,975 `.vgeo` containers
41
+ the Zorah build wrote on 2026-09-13. These are the exact bytes
42
+ [vgeo_encode_frame_blob.js](../../../shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js)
43
+ is handed in production. Baseline is what [lz4_compress_block.js](lz4_compress_block.js) ships today.
44
+
45
+ | level | ratio | vs. today | JS throughput |
46
+ |---|---|---|---|
47
+ | fast (today) | 0.89470 | — | 412–436 MB/s |
48
+ | 3 | 0.87272 | −2.457% | 51–53 MB/s |
49
+ | 4 | 0.87239 | −2.494% | 51 MB/s |
50
+ | 6 | 0.87214 | −2.522% | 51 MB/s |
51
+ | **9** | **0.87209** | **−2.528%** | **52–55 MB/s** |
52
+ | 10 | 0.87204 | −2.533% | 46 MB/s |
53
+ | 11 | 0.87204 | −2.533% | 45 MB/s |
54
+ | 12 | 0.87202 | −2.535% | 42 MB/s |
55
+
56
+ Two things fall out of that table.
57
+
58
+ **Level 3 already gets 97% of what is available on geometry.** The whole ladder from 3 to 12 is
59
+ 0.078 percentage points — 41 MiB on the 51 GiB Zorah build. Meshlet pages are quantised, densely
60
+ packed bytes; the matches that exist are short and near, and a four-deep chain walk finds them.
61
+
62
+ **Level 9 is both smaller and slightly faster than levels 3–6** — repeatably, three paired runs,
63
+ always in the same direction. Level 9 is the only hash-chain level that turns on pattern analysis
64
+ (the reference enables it at `nbSearches > 128`), and on data with runs in it that pays for itself.
65
+ There is no reason to ship 3–8 as the default when 9 dominates them.
66
+
67
+ ### It is not the same story for every payload
68
+
69
+ The codec is generic core code, so the ladder has to be there even though vgeo barely uses it.
70
+ Measured on 64KB chunks, same baseline:
71
+
72
+ | payload | fast | level 3 | level 9 | level 12 |
73
+ |---|---|---|---|---|
74
+ | engine source + glTF JSON | 0.30153 | 0.23931 (−20.6%) | 0.23164 (−23.2%) | 0.22972 (−23.8%) |
75
+ | runs and periodic data | 0.05763 | 0.05759 (−0.07%) | 0.05731 (−0.57%) | 0.05731 (−0.57%) |
76
+ | real `.vgeo` pages | 0.89470 | −2.46% | −2.53% | −2.54% |
77
+
78
+ On text the top of the ladder is worth 3.2 points over level 3 and costs 8× the time (113 MB/s at
79
+ level 3, 14 MB/s at level 12). That is the payload class that justifies building levels 10–12 at all.
80
+
81
+ ### What it does to a Zorah build
82
+
83
+ From the 2026-09-13 conversion log: pages **51.38 GiB stored, 56.38 GiB decoded**, built in
84
+ **4 h 8 m wall / 17 h 9 m CPU** across ten workers.
85
+
86
+ | level | pages would be | saved | compression CPU | share of build CPU |
87
+ |---|---|---|---|---|
88
+ | fast | 51.38 GiB | — | 2.4 min | 0.2% |
89
+ | 3 | 50.12 GiB | 1.26 GiB | 19.4 min | 1.9% |
90
+ | **9** | **50.08 GiB** | **1.30 GiB** | **18.9 min** | **1.8%** |
91
+ | 12 | 50.08 GiB | 1.30 GiB | 24.0 min | 2.3% |
92
+
93
+ **The whole ladder is free.** Sixteen extra minutes of CPU on a seventeen-hour build, about two
94
+ minutes of wall clock across ten workers, for 1.3 GiB. The interesting decision is not whether to
95
+ pay for high compression — it is which level, and the answer is 9 because 10–12 cost 15–40% more
96
+ time for 0.007 points.
97
+
98
+ ---
99
+
100
+ ## 3 · What it costs the reader
101
+
102
+ The format does not change, so nothing has to be done. But the *shape* of the blocks changes, and
103
+ that is worth saying out loud because it does not go the way one would guess.
104
+
105
+ Measured with [lz4_decompress_block.js](lz4_decompress_block.js) over 250 real pages:
106
+
107
+ | | ratio | sequences | bytes/sequence | decode |
108
+ |---|---|---|---|---|
109
+ | fast | 0.89372 | 208,503 | 61.9 | 1,174 MB/s |
110
+ | level 9 | 0.87087 | 336,176 | 38.4 | 1,059 MB/s |
111
+ | level 12 | 0.87081 | 339,644 | 38.0 | 999 MB/s |
112
+
113
+ On geometry, HC finds **more** matches, not longer ones — 61% more sequences — and the decoder's
114
+ per-sequence cost is a byte loop, so pages decode **10–15% slower**. On text the same measurement
115
+ inverts: 115,657 sequences down to 81,431, and decode goes from 705 to 828 MB/s (**+18%**).
116
+
117
+ This lands in a worker ([vgeo_page_worker_serve.js](../../../shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.js)),
118
+ off the main thread, and it buys 2.5% fewer bytes to fetch on every page of every session. It is a
119
+ fair trade as it stands. It is also **entirely recoverable**, for an unrelated reason — see §8.
120
+
121
+ ---
122
+
123
+ ## 4 · What the reference actually is
124
+
125
+ `lz4hc.c` at 1.10.0. The level table is identical in 1.9.4 and 1.10.0 for levels 3–12, so targeting
126
+ that range is version-stable:
127
+
128
+ | level | strategy | chain attempts | sufficient length |
129
+ |---|---|---|---|
130
+ | 3–9 | hash chain, lazy parse | 4, 8, 16, 32, 64, 128, 256 | 16 |
131
+ | 10 | optimal parse | 96 | 64 |
132
+ | 11 | optimal parse | 512 | 128 |
133
+ | 12 | optimal parse, full update | 16,384 | 4,096 |
134
+
135
+ **Level 2 is deliberately not on that list.** 1.9.4 gives it the hash-chain strategy; 1.10.0 gives it
136
+ `lz4mid`, a different compressor added in that release. "Level 2" does not name one thing across
137
+ reference versions, so this encoder will not have one. Levels 1 (fast) and 3–12 are the ladder.
138
+
139
+ Four pieces of the reference are load-bearing and none of them can be dropped:
140
+
141
+ - **The hash chain.** `hashTable` (32,768 × u32) holds the newest position per 4-byte hash;
142
+ `chainTable` (65,536 × u16) holds, per position, the *delta* back to the previous position with the
143
+ same hash, capped at 65,535. Positions are inserted lazily, from `nextToUpdate` up to the current
144
+ position, so nothing is built that is never searched.
145
+ - **Pattern analysis.** When the chain shows consecutive positions (delta 1) and the 4 bytes are a
146
+ repeat of a single byte, the walk jumps straight to the best position in the run instead of
147
+ stepping through thousands of links. Without it, levels 9–12 go quadratic on a page of zeros.
148
+ This is what makes level 9 *faster* than level 6 on real data.
149
+ - **Chain swap.** On a tie, scan the chain deltas inside the match for the largest step and follow
150
+ that instead. Only the optimal parser uses it.
151
+ - **The three-match lazy parse** (levels 3–9) and the **windowed shortest path** (10–12). The price
152
+ model is exact in bytes — a token, a 2-byte offset, and the continuation bytes each length field
153
+ spills into — which is why the parse is a genuine shortest path and not a heuristic.
154
+
155
+ Everything else in `lz4hc.c` is dictionary and streaming support: `extDict`, `dictCtx`,
156
+ `LZ4HC_protectDictEnd`, the pattern rotation across a dictionary boundary, `favorDecSpeed`,
157
+ `fillOutput`. meep compresses one self-contained block with no history. **All of it drops**, and with
158
+ it about half the file. The port that was validated is ~700 lines against the reference's ~1,400.
159
+
160
+ ---
161
+
162
+ ## 5 · Design
163
+
164
+ ### 5.1 Files
165
+
166
+ Four new files in this directory. **[lz4_compress_block.js](lz4_compress_block.js) is not edited.**
167
+
168
+ - **`LZ4_LEVEL.js`** — `{ FAST: 1, HC_MIN: 3, HC_DEFAULT: 9, HC_MAX: 12 }`, with the note about 2.
169
+ - **`lz4_compress_block_hc.js`** — the high-compression encoder. Same parameter list as the fast one
170
+ with `level` appended:
171
+ `lz4_compress_block_hc(destination, destination_offset, destination_end, source, source_offset, source_end, level)`.
172
+ - **`lz4_compress_block_at_level.js`** — dispatch. `level <= 1` goes to `lz4_compress_block`,
173
+ anything else to `lz4_compress_block_hc`. This is what callers with a knob call; it is the only
174
+ new thing `vgeo_encode_frame_blob` needs to know about.
175
+ - **`lz4_compress_block_hc.spec.js`** — §6.
176
+
177
+ ### 5.2 Why the fast encoder is not touched
178
+
179
+ The obvious tidy — lift `write_token_and_literals` and `write_continuation` out of
180
+ `lz4_compress_block.js` into a module both encoders import — was built and measured. Across five
181
+ paired runs it cost the fast encoder between 0% and 8% of its throughput and never once helped.
182
+ The HC encoder sequences its writes differently anyway (`LZ4HC_encodeSequence` emits token, literals,
183
+ offset and match continuation as one unit, where the fast encoder splits it), so the two are not the
184
+ same function. **The HC module carries its own writers.** The fast encoder's pinned bytes and its
185
+ 435 MB/s cannot regress if nothing edits it.
186
+
187
+ ### 5.3 Memory, and when it is taken
188
+
189
+ | table | type | bytes | needed from |
190
+ |---|---|---|---|
191
+ | hash | `Int32Array(32768)` | 128 KB | level 3 |
192
+ | chain | `Uint16Array(65536)` | 128 KB | level 3 |
193
+ | price / offset / length / literal-length | 4 × `Int32Array(4099)` | 64 KB | level 10 |
194
+
195
+ 320 KB, against the 32 KB the fast encoder holds today. meep ships raw source, so a module-level
196
+ `new Int32Array(1 << 15)` would cost that to every consumer whether or not they ever compress —
197
+ including the runtime, which only ever *decodes*. **Allocate lazily**: the chain tables on the first
198
+ HC call, the parser tables on the first level-10-or-higher call. Module-static after that, like the
199
+ fast encoder's, and safe for the same reason — the encoder is straight-line synchronous code that
200
+ resets its tables on entry.
201
+
202
+ ### 5.4 Table lifetime
203
+
204
+ `hash_table.fill(0)` and `chain_table.fill(0xFFFF)` on entry, per call. This looked like the obvious
205
+ thing to optimise — 256 KB of fill for a page that may be 1.4 KB — so it was measured.
206
+
207
+ *Advancing* that bias from call to call so the tables never need clearing at all — which is what the
208
+ reference does across streamed blocks, and which was verified byte-exact over 6,600 fuzz blocks —
209
+ came out at **0.95–1.01×: no gain**, because turning `IDX_BASE` into a mutable module binding costs
210
+ the inner loop more than the fill saves. Dropping only the chain-table clear (which is provably unnecessary here: every position's
211
+ delta is written by the insert before anything can walk to it) also measured as noise.
212
+
213
+ **Ship the plain per-call fill.** It matches the fast encoder's precedent and it is not the cost.
214
+
215
+ ### 5.5 Two invariants the port must keep
216
+
217
+ - **Self-contained blocks.** No match may reach before `source_offset`. With an index space biased by
218
+ one window, an empty hash slot reads as "before the block" for free and the existing
219
+ "a block compressed after another does not carry the previous one's positions" test passes without
220
+ a special case.
221
+ - **The output bound is checked, not assumed.** The reference has `limitedOutput`; this codebase
222
+ throws, with message shapes the existing spec asserts (`/needs \d+ bytes, and only \d+ are left/`).
223
+ The HC writers must do the same checks at the same two points — before the token-and-literals run,
224
+ and before the offset-and-match-length. The prototype has neither yet; this is the one piece of the
225
+ port that is not already written.
226
+
227
+ ---
228
+
229
+ ## 6 · Tests
230
+
231
+ Following the idiom already in [lz4_compress_block.spec.js](lz4_compress_block.spec.js): pinned
232
+ reference blocks first, then shapes too large to write down driven through and back.
233
+
234
+ **Pinned vectors.** These are what liblz4's own `LZ4_compress_HC` writes, and the prototype
235
+ reproduces them exactly:
236
+
237
+ ```
238
+ // the greedy pass leaves bytes on the table
239
+ // "acddafadcacddaacddafadcacddaacdadcacbddafadcacddaacgdccaafadcacddaacgdadcacdfbeagdda" (84 bytes)
240
+ // fast=47 hc3=42 hc9=42 hc12=42
241
+ // hc3/hc9/hc12: 91 61 63 64 64 61 66 61 64 63 09 00 0d 0e 00 01 0b 00 1a 62 15 00 5a 67 64
242
+ // 63 63 61 11 00 02 0c 00 80 66 62 65 61 67 64 64 61
243
+
244
+ // the optimal parser earns its own row
245
+ // "ffafdfdefafdfdeeahadefehahadefeeahadefehfd" (42 bytes)
246
+ // fast=35 hc3=35 hc9=35 hc12=32
247
+ // hc3/hc9: 83 66 66 61 66 64 66 64 65 07 00 93 65 61 68 61 64 65 66 65 68 08 00 b0 65 61 68
248
+ // 61 64 65 66 65 68 66 64
249
+ // hc12 : 83 66 66 61 66 64 66 64 65 07 00 92 65 61 68 61 64 65 66 65 68 08 00 03 10 00 50
250
+ // 66 65 68 66 64
251
+ ```
252
+
253
+ The first pins that HC finds matches the greedy pass walks past; the second pins that levels 10–12
254
+ are a different parser and not just a longer search.
255
+
256
+ **Properties, over the existing `CORPUS` at every level:**
257
+
258
+ 1. Round trip through `lz4_decompress_block` — bytes back exactly.
259
+ 2. The encoder restrictions, using the spec's existing `read_sequences`: last five bytes literal, no
260
+ match starting within twelve bytes of the end, no offset reaching before the block start, the
261
+ block closes with a literals-only sequence.
262
+ 3. **Aggregate** monotonicity: summed over the corpus, level *n+1* is never larger than level *n*.
263
+ **Not per block** — 54 non-monotone steps were found across 275 inputs, concentrated at the 9→10
264
+ boundary where the parser changes, the worst being a 64KB JSON chunk going 16,741 → 16,993 bytes.
265
+ Asserting per-block monotonicity would be asserting something false.
266
+ 4. A destination one byte short of the block is rejected, at every level, with the same message
267
+ shapes the fast encoder produces.
268
+ 5. `source_offset > 0`: the same payload compressed at offsets 0, 7, 32, 33 and 4096 inside buffers
269
+ filled with different padding bytes produces **identical blocks**. This is the real guard on the
270
+ one latent hazard in the port (§7), and it holds today for both encoders — 72 pages × 5 paddings.
271
+ 6. Compressing a second block after a first does not carry the first's positions (the existing test,
272
+ extended across levels).
273
+ 7. The level argument is validated: 2 and anything outside 1…12 is refused rather than silently
274
+ clamped to something the caller did not ask for.
275
+
276
+ ---
277
+
278
+ ## 7 · What the port has to be careful about
279
+
280
+ Found while building and validating the prototype. None of these are speculative.
281
+
282
+ **The cheap rejection gate can read outside the block.** Before a full compare, the reference tests
283
+ the two bytes at the current best match end: `read16(iLowLimit + longest - 1)` against
284
+ `read16(matchPtr - lookBackLength + longest - 1)`. When the search is allowed to start behind the
285
+ current position — which is exactly what the lazy parser's second and third searches do — the second
286
+ address can fall before `source_offset`, and at `source_offset === 0` before the array. The C reads
287
+ whatever is in the buffer; JS reads `undefined`, which `| 0` turns into a zero. The gate is only a
288
+ heuristic, so either behaviour produces a *valid* block, but they can produce *different* blocks.
289
+ Clamp it explicitly and pin it with test 5 above. Over 72 real pages × 5 paddings, and 1,200 fuzz
290
+ inputs, no divergence was reachable — it is latent, not live, and it should stay that way by
291
+ construction rather than by luck.
292
+
293
+ **The level table, not the levels.** Levels 3–12 map onto `{ attempts, sufficient_length }` pairs and
294
+ a strategy; the strategy boundary at 10 and the pattern-analysis boundary at `attempts > 128` are
295
+ where behaviour changes, not the level numbers. Write the table, not a switch.
296
+
297
+ **`LZ4_MAX_INPUT_SIZE` still applies.** Indices are biased by one window and held in an `Int32Array`,
298
+ which is fine to 2^31, and the existing assert already caps input well below that.
299
+
300
+ ---
301
+
302
+ ## 8 · Adjacent, and out of scope
303
+
304
+ **The decoder's byte loops.** [lz4_decompress_block.js](lz4_decompress_block.js) copies literals and
305
+ matches one byte at a time. Splicing in `destination.set` for literal runs of 16 or more and
306
+ `copyWithin` for non-overlapping matches of 16 or more — nothing else changed, output verified
307
+ identical — measured on 250 real pages:
308
+
309
+ | | shipped decoder | with bulk copies |
310
+ |---|---|---|
311
+ | fast blocks | 1,206 MB/s | **2,034 MB/s** |
312
+ | level 9 blocks | 1,036 MB/s | **1,361 MB/s** |
313
+
314
+ HC blocks under a bulk-copy decoder (1,361 MB/s) decode **faster than fast blocks do today**
315
+ (1,206 MB/s). The decode cost in §3 is a property of the decoder's inner loop, not of high
316
+ compression. This is a separate change to a file this plan deliberately does not touch, and it is
317
+ worth making on its own merits.
318
+
319
+ **Done, 2026-09-14**, in the commit that added this paragraph. The threshold went in at
320
+ thirty-two rather than sixteen: sweeping both crossovers over 2,267 real pages put the top of the
321
+ curve between twenty-four and thirty-two, and thirty-two is where
322
+ [lz4_compress_block.js](lz4_compress_block.js) had already put its own. Measured the way §10 says
323
+ to, twenty-one interleaved rounds alternating which decoder went first:
324
+
325
+ | | shipped decoder | with bulk copies |
326
+ |---|---|---|
327
+ | fast blocks | 1,205 MB/s | **2,237 MB/s** |
328
+ | level 9 blocks | 1,109 MB/s | **1,592 MB/s** |
329
+
330
+ The claim survives with room to spare: level 9 blocks now decode 32% *faster* than fast blocks
331
+ managed before the change, and 44% faster than they manage today. What does **not** close is the
332
+ gap between the two shapes — bulk copies help fast blocks more, because their literal runs are
333
+ 58.8 bytes against level 9's 34.7, so level 9 goes from 8% behind level 1 to 29% behind it. The
334
+ reader ends up far ahead in absolute terms and §3's trade is paid for several times over; the
335
+ *ratio* §3 quoted gets wider, not narrower, and a later reader should not expect otherwise.
336
+
337
+ Output is pinned against the old decoder over **57,938 stored pages of 150 Zorah containers,
338
+ 3,489,392,680 decoded bytes, not one byte different**, and over the same 2,267 pages re-encoded at
339
+ both levels. The overlapping match stays a byte loop and has its own vectors in the spec.
340
+
341
+ What the sweep also showed is that the win is the **literal** path, which §3's text figures
342
+ predict but do not say outright. Four fifths of a decoded page is literal runs averaging 34.7
343
+ bytes at level 9, and its matches average 8.7 — so `set` moves 68% of the output and `copyWithin`
344
+ 4%. On engine source, where literal runs average 1.1 bytes, the change is a wash at any threshold.
345
+
346
+ **python-lz4 is not a usable reference for the fast encoder.** The 4.4.5 wheel (liblz4 1.9.4) fails
347
+ to find matches whose candidate was recorded during the search loop: `"QabcdXXabcdZZZZZZZZ"` codes
348
+ as all literals, while the same input with the repeat at offset 0 codes the match. Its HC path is
349
+ correct. Consequence: **[lz4_compress_block.js](lz4_compress_block.js) is right and that build is
350
+ wrong** — meep's blocks are 1.43% *smaller* than its `mode='default'` output over the corpus, and a
351
+ line-by-line transliteration of `LZ4_compress_generic`'s `byU16` path agrees with meep on all 152
352
+ inputs tried. Validate HC against python-lz4; validate the fast encoder against the lz4 CLI or a
353
+ transliteration.
354
+
355
+ ---
356
+
357
+ ## 9 · The plan
358
+
359
+ Each stage is independently shippable and leaves the tree green.
360
+
361
+ ### Stage A — the hash chain, levels 3–9
362
+
363
+ **Files:** `LZ4_LEVEL.js`, `lz4_compress_block_hc.js`, `lz4_compress_block_at_level.js`,
364
+ `lz4_compress_block_hc.spec.js`
365
+
366
+ Port, from `lz4hc.c`, with no dictionary and no streaming: the lazy insert, the match finder with
367
+ pattern analysis, `LZ4HC_countBack`, and the three-match lazy parser. Add the output-bound checks
368
+ (§5.5) and the clamped gate (§7). Levels 10–12 assert as not yet available.
369
+
370
+ **Proves:** −2.53% on vgeo pages, −23.2% on text, at 52 MB/s. That is 97% of everything this plan can
371
+ deliver.
372
+
373
+ **Test:** all of §6 except the level-12 vector and the 9→10 aggregate step.
374
+
375
+ ### Stage B — the optimal parser, levels 10–12
376
+
377
+ **Files:** `lz4_compress_block_hc.js` (the parser and its lazily-allocated tables), spec additions.
378
+
379
+ Port `LZ4HC_compress_optimal` with `favorDecSpeed` fixed off: the exact byte price model, the
380
+ 4,096-position window, `sufficient_len`, and full update at level 12. Add chain swap to the finder.
381
+
382
+ **Proves:** the ladder reaches the documented maximum, and §1's measurement says there is nothing
383
+ past it.
384
+
385
+ **Test:** the level-12 vector, and aggregate monotonicity across the whole ladder.
386
+
387
+ ### Stage C — the level reaches the build
388
+
389
+ **Files:**
390
+ [VGeoBuildOptions.js](../../../shade/renderer/geometry/virtual/build/VGeoBuildOptions.js) ·
391
+ [VGeoWriteOptions.js](../../../shade/renderer/geometry/virtual/format/VGeoWriteOptions.js) ·
392
+ [vgeo_encode_frame_blob.js](../../../shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js) ·
393
+ [vgeo_write_container.js](../../../shade/renderer/geometry/virtual/format/vgeo_write_container.js) ·
394
+ [gltf_to_vgeo.js](../../../../tools/geometry/virtual/gltf_to_vgeo.js)
395
+
396
+ A `codec_level` beside the existing `codec` at each layer, defaulting to `LZ4_LEVEL.HC_DEFAULT` (9),
397
+ and a `--codec-level` flag on the tool. `vgeo_encode_frame_blob` already declines compression per
398
+ page when it would not shrink the page, so the level cannot make a file larger — nothing about that
399
+ contract changes.
400
+
401
+ **Test:** [vgeo_frame_blob.spec.js](../../../shade/renderer/geometry/virtual/format/frame/vgeo_frame_blob.spec.js)
402
+ gains a level case; the container round trip runs at the default.
403
+
404
+ Nothing in the runtime changes. `codec` on the frame is still `VGEO_CODEC_LZ4`, the reader still
405
+ reads it, and a container built at level 12 is readable by every build of the reader that exists.
406
+
407
+ ### Stage D — confirm on a real conversion
408
+
409
+ Re-run a Zorah subset at the new default and check the two numbers this plan promised: pages about
410
+ 2.5% smaller, build CPU up under 2%. Record it next to the 2026-09-13 log.
411
+
412
+ ---
413
+
414
+ ## 10 · How to re-measure
415
+
416
+ Everything above came from a scratchpad at
417
+ `C:\Users\Alex\AppData\Local\Temp\claude\H--git-CompanyNamed-meep\f41c851a-bc82-447f-be15-80a25e108370\scratchpad`,
418
+ which is session-local and will not survive. The recipes are short:
419
+
420
+ - **Corpus** — walk a `.vgeo` with `vgeo_read_header` + `vgeo_read_directory`, slice each frame at
421
+ `frame_offset`/`fetch_size`, and decode the blob past
422
+ `VGEO_FRAME_HEADER_SIZE + 48 × child_page_count`. Note that `vgeo_read_header` wants more than
423
+ `VGEO_HEADER_SIZE` bytes — the level table follows the header, so hand it 8 KB.
424
+ - **Ground truth** — `python -c "import lz4.block as B; B.compress(data, mode='high_compression', compression=N, store_size=False)"`.
425
+ Python has `lz4` installed here (4.4.5, liblz4 1.9.4). For HC only — see §8.
426
+ - **Timing** — interleave A and B in one process and alternate which runs first; nineteen node
427
+ processes is a normal background load on this machine and a single-shot number means nothing.
428
+ - **Build cost** — `H:\vgeo_scratch\zorah-2026-13-09-console.log` carries the stored/decoded page
429
+ totals and the CPU time the projections in §2 are scaled against.
430
+
431
+ ---
432
+
433
+ ## 11 · What it came to
434
+
435
+ Carried out 2026-09-14, in four commits: the encoder and its spec, the plumbing through the build,
436
+ one profiling pass, and this. Every number below was measured on this machine after the work, the
437
+ same way §10 says to measure it.
438
+
439
+ ### The port
440
+
441
+ `lz4hc.c` at 1.10.0, with the dictionary and streaming half dropped as §4 said. One thing dropped
442
+ that §4 did not name: **`LZ4HC_protectDictEnd` goes too.** It guards against a candidate whose four
443
+ bytes would run off the end of a dictionary, and with no dictionary every candidate the loop reaches
444
+ already satisfies it — `matchCandidateIdx >= lowestMatchIndex` implies it, and so does every index
445
+ the pattern-analysis branch assigns. 733 lines of code against the reference's 2,192 of file.
446
+
447
+ Byte for byte identical to liblz4 1.9.4's `LZ4_compress_HC` over **26,093 blocks**:
448
+
449
+ | corpus | inputs | levels | blocks |
450
+ |---|---|---|---|
451
+ | synthetic: runs, periods, window edges, structured and small fuzz | 1,182 | 3–12 | 11,820 |
452
+ | the same, capped at 20 KB | 961 | 3–12 | 9,610 |
453
+ | the same, over 20 KB | 221 | 10–12 | 663 |
454
+ | real `.vgeo` pages from the 2026-09-13 Zorah build | 400 | 3–12 | 4,000 |
455
+
456
+ First try, as §1 said it would be. §6's two pinned vectors reproduce exactly, level-12 row included.
457
+
458
+ ### The ladder, on 5,802 real pages
459
+
460
+ The pages of a 507-geometry Zorah subset, 190,044,904 bytes decoded — the exact bytes
461
+ `vgeo_encode_frame_blob` was handed:
462
+
463
+ | level | stored | ratio | vs. level 1 | encoder |
464
+ |---|---|---|---|---|
465
+ | 1 | 166,692,697 | 0.87712 | — | 340–390 MB/s |
466
+ | 3 | 162,743,403 | 0.85634 | −2.369% | 44–53 MB/s |
467
+ | **9** | **162,604,364** | **0.85561** | **−2.453%** | **42–50 MB/s** |
468
+ | 12 | 162,586,298 | 0.85552 | −2.463% | 31–38 MB/s |
469
+
470
+ The stored byte counts are exact and repeat to the byte. The throughputs are ranges because that is
471
+ what this machine supports: two best-of-nine runs of the same measurement, minutes apart, came out
472
+ 20% apart, which is wider than most of the differences anyone would want to read off the column.
473
+ What *is* stable is the shape within a run — level 1 is about eight times level 9, and level 9 is
474
+ about 5% slower than level 3 — because those are ratios taken between interleaved passes.
475
+
476
+ §2's shape holds exactly: level 3 is 96.2% of everything the ladder has on this payload, and the
477
+ whole run from 3 to 12 is 0.082 percentage points. On a wider sample — the first 25 pages of each of
478
+ 120 containers, 2,718 pages and 122,283,932 bytes — the same ladder runs 0.90483 to 0.87733, which
479
+ is **−3.040%**, so 2.5% is the conservative end of what a whole build gets rather than the middle.
480
+
481
+ Throughput came out below §2's 52 MB/s at first. Profiling put 16.3% of all ticks in the guarded
482
+ gate read of §7, which V8 was not inlining: it is called once per link of every chain walked. Taking
483
+ the guard only on addresses that need it, and reading the call's context into locals once rather
484
+ than reloading module bindings inside that loop, bought 15% in a paired in-process comparison and
485
+ left nothing outside the search loop above 14% of the profile.
486
+
487
+ ### The two numbers Stage D promised
488
+
489
+ **Pages about 2.5% smaller — confirmed.** The paired conversions, `--max-triangles 30000` on
490
+ `zorah_main_public.v2.gltf`, 507 geometries and 5.25 M triangles, identical in every run:
491
+
492
+ | | pages stored | decoded |
493
+ |---|---|---|
494
+ | level 1 | 159.0 MiB | 181.2 MiB |
495
+ | level 9 | 155.1 MiB | 181.2 MiB |
496
+
497
+ **Build CPU up under 2% — confirmed for the Zorah build, but not for this subset.** Six runs,
498
+ alternating which level went first:
499
+
500
+ | | CPU | wall |
501
+ |---|---|---|
502
+ | level 1 | 139.3 s · 139.7 s · **119.5 s** | 128.8 · 129.4 · **110.8** |
503
+ | level 9 | 143.0 s · 147.6 s · **124.1 s** | 131.6 · 133.8 · **114.9** |
504
+
505
+ The last pair ran back to back on a quiet machine and is the one to read: **+4.6 s of CPU, +3.85%**.
506
+ Measuring the compression alone over the same build's own pages — five interleaved passes, so no
507
+ noise at all — gives 0.51 s against 4.03 s, a delta of **+3.52 s, or +2.95%** of that build.
508
+
509
+ That is over 2%, and it is over 2% *because of what the subset is*. `--max-triangles 30000` selects
510
+ the small geometries, and they are cheap: 190 MB of pages out of 119.5 s of build CPU, which is
511
+ 1.59 MB of page bytes per CPU-second. The 2026-09-13 build produced 60,536 MB of pages from 61,740 s
512
+ (17 h 9 m, validation excluded), which is **0.98** — it spends 62% more CPU per page byte, because
513
+ it is building the large meshes too. Scaling this subset's measured delta by that gives **about 2%**
514
+ of the Zorah build's CPU.
515
+
516
+ **About 2% is as fine as this machine resolves it, and the honest answer is that it straddles the
517
+ line.** The three paired conversions give deltas of +3.7 s, +7.9 s and +4.6 s on a build of 190 MB
518
+ of pages, which scale to 1.9%, 4.1% and 2.4% of the Zorah build; the direct compression measurement,
519
+ taken twice on a quiet machine, gives 1.7% and 2.1%. Every one of those is a difference of a few
520
+ seconds measured against a two-minute build on a box whose own run-to-run spread is 20%, so §2's
521
+ 1.8% is not *confirmed* so much as *not contradicted*. What can be said without a measurement error
522
+ swallowing it: the level costs a couple of percent of a build and a couple of minutes of wall clock
523
+ across ten workers, and it is about 3% on a build made only of small meshes.
524
+
525
+ ### What it costs the reader, re-measured
526
+
527
+ §3 said the blocks change shape and the plan was right about that, on 2,500 real pages:
528
+
529
+ | | ratio | bytes/sequence | decode |
530
+ |---|---|---|---|
531
+ | level 1 | 0.87252 | 55.3 | 1,197 MB/s |
532
+ | level 9 | 0.85228 | 38.4 | 1,080 MB/s |
533
+ | level 12 | 0.85220 | 38.1 | 1,077 MB/s |
534
+
535
+ 38.4 bytes a sequence at level 9, which is §3's figure to the decimal, and decode 9.8% slower rather
536
+ than 10–15%. §8's bulk-copy decoder is still the answer to that, and still out of scope here.
537
+
538
+ ### And it reads
539
+
540
+ 507 Zorah containers built at level 9 pass the tool's whole post-build audit — every build
541
+ invariant, checksum and directory record, in every file — which is the claim that the format does
542
+ not change, made against real geometry rather than argued.
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The compression levels {@link lz4_compress_block_at_level} accepts.
3
+ *
4
+ * Level 1 is the format's fast encoder, {@link lz4_compress_block}. Levels 3 to 12 are the
5
+ * high-compression encoder, {@link lz4_compress_block_hc}: 3 to 9 walk a hash chain with a lazy
6
+ * parse of ever more candidates, and 10 to 12 replace that parse with a shortest path over a window
7
+ * of the block. Twelve is the top of the ladder in the reference and, measured against an encoder
8
+ * with an uncapped search and a whole-block parse, the top of what the block format allows at all.
9
+ *
10
+ * **There is no level 2.** The reference gives the number different algorithms in different
11
+ * releases — 1.9.4 compresses it on the hash chain, 1.10.0 hands it to `lz4mid`, a compressor that
12
+ * release added — so "level 2" does not name one thing, and this encoder will not pretend it does.
13
+ *
14
+ * Nine is the default because the ladder above it is not worth its time: on real geometry pages the
15
+ * whole run from 3 to 12 is under a tenth of a percentage point of ratio, and 10 to 12 cost 15-40%
16
+ * more time for the last thousandth of it. Nine is the smallest of the hash-chain levels and costs
17
+ * about 5% more time than 3 — the reference's own claim that 9 comes out *faster* than the levels
18
+ * below it is a property of its eight-byte pattern scan, which JavaScript has no cheap way to do,
19
+ * and it does not reproduce here. The case for 9 over 3 is ratio, and it is a small one.
20
+ *
21
+ * @see https://github.com/lz4/lz4/blob/dev/lib/lz4hc.h — `LZ4HC_CLEVEL_MIN` and friends
22
+ * @type {{FAST: number, HC_MIN: number, HC_DEFAULT: number, HC_MAX: number}}
23
+ *
24
+ * @author Alex Goldring
25
+ * @copyright Company Named Limited (c) 2026
26
+ */
27
+ export const LZ4_LEVEL: {
28
+ FAST: number;
29
+ HC_MIN: number;
30
+ HC_DEFAULT: number;
31
+ HC_MAX: number;
32
+ };
33
+ //# sourceMappingURL=LZ4_LEVEL.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LZ4_LEVEL.d.ts","sourceRoot":"","sources":["../../../../../src/core/binary/lz4/LZ4_LEVEL.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBALU;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAU1E"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The compression levels {@link lz4_compress_block_at_level} accepts.
3
+ *
4
+ * Level 1 is the format's fast encoder, {@link lz4_compress_block}. Levels 3 to 12 are the
5
+ * high-compression encoder, {@link lz4_compress_block_hc}: 3 to 9 walk a hash chain with a lazy
6
+ * parse of ever more candidates, and 10 to 12 replace that parse with a shortest path over a window
7
+ * of the block. Twelve is the top of the ladder in the reference and, measured against an encoder
8
+ * with an uncapped search and a whole-block parse, the top of what the block format allows at all.
9
+ *
10
+ * **There is no level 2.** The reference gives the number different algorithms in different
11
+ * releases — 1.9.4 compresses it on the hash chain, 1.10.0 hands it to `lz4mid`, a compressor that
12
+ * release added — so "level 2" does not name one thing, and this encoder will not pretend it does.
13
+ *
14
+ * Nine is the default because the ladder above it is not worth its time: on real geometry pages the
15
+ * whole run from 3 to 12 is under a tenth of a percentage point of ratio, and 10 to 12 cost 15-40%
16
+ * more time for the last thousandth of it. Nine is the smallest of the hash-chain levels and costs
17
+ * about 5% more time than 3 — the reference's own claim that 9 comes out *faster* than the levels
18
+ * below it is a property of its eight-byte pattern scan, which JavaScript has no cheap way to do,
19
+ * and it does not reproduce here. The case for 9 over 3 is ratio, and it is a small one.
20
+ *
21
+ * @see https://github.com/lz4/lz4/blob/dev/lib/lz4hc.h — `LZ4HC_CLEVEL_MIN` and friends
22
+ * @type {{FAST: number, HC_MIN: number, HC_DEFAULT: number, HC_MAX: number}}
23
+ *
24
+ * @author Alex Goldring
25
+ * @copyright Company Named Limited (c) 2026
26
+ */
27
+ export const LZ4_LEVEL = {
28
+ FAST: 1,
29
+ HC_MIN: 3,
30
+ HC_DEFAULT: 9,
31
+ HC_MAX: 12
32
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Encode one LZ4 block, at whichever level the caller asks for.
3
+ *
4
+ * This is what a caller with a knob calls. The two encoders behind it are not variants of one
5
+ * another — {@link lz4_compress_block} makes a single greedy pass over a table that holds one
6
+ * position per hash, and {@link lz4_compress_block_hc} walks a chain of every position carrying the
7
+ * same hash and parses the matches against each other — so they are two functions, and this is the
8
+ * one line that chooses between them.
9
+ *
10
+ * **Every level writes the same format.** A block is a block: the level changes how long the encoder
11
+ * spent looking for matches, not what a match is coded as. So the level is a build-time decision
12
+ * with no reader consequence at all, and one that can be changed on an existing pipeline without
13
+ * anything downstream knowing.
14
+ *
15
+ * `destination` and `source` must not overlap.
16
+ *
17
+ * @param {Uint8Array} destination
18
+ * @param {number} destination_offset where this block's output starts
19
+ * @param {number} destination_end one past the last byte the block may write; give it
20
+ * {@link lz4_compress_bound} bytes and it cannot run out
21
+ * @param {Uint8Array} source
22
+ * @param {number} source_offset the first byte of input, and the floor a match may reach back to
23
+ * @param {number} source_end one past the last byte of input
24
+ * @param {number} level {@link LZ4_LEVEL.FAST}, or one of 3 to {@link LZ4_LEVEL.HC_MAX} — there is
25
+ * no level 2, and {@link LZ4_LEVEL} says why
26
+ * @returns {number} the offset just past the last byte written
27
+ * @throws {Error} if the block does not fit the space given for it
28
+ *
29
+ * @author Alex Goldring
30
+ * @copyright Company Named Limited (c) 2026
31
+ */
32
+ export function lz4_compress_block_at_level(destination: Uint8Array, destination_offset: number, destination_end: number, source: Uint8Array, source_offset: number, source_end: number, level: number): number;
33
+ //# sourceMappingURL=lz4_compress_block_at_level.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lz4_compress_block_at_level.d.ts","sourceRoot":"","sources":["../../../../../src/core/binary/lz4/lz4_compress_block_at_level.js"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,yDAfW,UAAU,sBACV,MAAM,mBACN,MAAM,UAEN,UAAU,iBACV,MAAM,cACN,MAAM,SACN,MAAM,GAEJ,MAAM,CAgBlB"}