@woosh/meep-engine 3.26.0 → 3.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (449) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +542 -0
  4. package/src/core/binary/lz4/LZ4_LEVEL.d.ts +33 -0
  5. package/src/core/binary/lz4/LZ4_LEVEL.d.ts.map +1 -0
  6. package/src/core/binary/lz4/LZ4_LEVEL.js +32 -0
  7. package/src/core/binary/lz4/lz4_compress_block_at_level.d.ts +33 -0
  8. package/src/core/binary/lz4/lz4_compress_block_at_level.d.ts.map +1 -0
  9. package/src/core/binary/lz4/lz4_compress_block_at_level.js +47 -0
  10. package/src/core/binary/lz4/lz4_compress_block_hc.d.ts +40 -0
  11. package/src/core/binary/lz4/lz4_compress_block_hc.d.ts.map +1 -0
  12. package/src/core/binary/lz4/lz4_compress_block_hc.js +1382 -0
  13. package/src/core/binary/lz4/lz4_decompress_block.d.ts +2 -1
  14. package/src/core/binary/lz4/lz4_decompress_block.d.ts.map +1 -1
  15. package/src/core/binary/lz4/lz4_decompress_block.js +31 -6
  16. package/src/core/geom/3d/mat4/m4_linear_determinant.d.ts +15 -0
  17. package/src/core/geom/3d/mat4/m4_linear_determinant.d.ts.map +1 -0
  18. package/src/core/geom/3d/mat4/m4_linear_determinant.js +24 -0
  19. package/src/core/geom/3d/mat4/m4_normal_matrix3.d.ts +4 -2
  20. package/src/core/geom/3d/mat4/m4_normal_matrix3.d.ts.map +1 -1
  21. package/src/core/geom/3d/mat4/m4_normal_matrix3.js +21 -11
  22. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +14 -7
  23. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
  24. package/src/core/geom/3d/shape/HeightMapShape3D.js +87 -78
  25. package/src/engine/Engine.d.ts.map +1 -1
  26. package/src/engine/Engine.js +38 -1
  27. package/src/engine/graphics3/GraphicsEngine.d.ts +5 -1
  28. package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -1
  29. package/src/engine/graphics3/GraphicsEngine.js +834 -830
  30. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  31. package/src/shade/RENDERER_CONTRACT.md +19 -10
  32. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +1 -1
  33. package/src/shade/playground/vgeo_runtime/README.md +186 -27
  34. package/src/shade/playground/vgeo_runtime/index.html +112 -62
  35. package/src/shade/playground/vgeo_runtime/main.js +678 -88
  36. package/src/shade/playground/vgeo_runtime/sample_asset.d.ts +18 -0
  37. package/src/shade/playground/vgeo_runtime/sample_asset.d.ts.map +1 -0
  38. package/src/shade/playground/{vgeo_viewer → vgeo_runtime}/sample_asset.js +23 -5
  39. package/src/shade/playground/vgeo_scene/README.md +408 -0
  40. package/src/shade/playground/vgeo_scene/index.html +144 -0
  41. package/src/shade/playground/vgeo_scene/main.d.ts +2 -0
  42. package/src/shade/playground/vgeo_scene/main.d.ts.map +1 -0
  43. package/src/shade/playground/vgeo_scene/main.js +1716 -0
  44. package/src/shade/playground/vgeo_viewer/sample_asset.d.ts.map +1 -1
  45. package/src/shade/renderer/Renderer.d.ts +4 -1
  46. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  47. package/src/shade/renderer/Renderer.js +5 -2
  48. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +1 -2
  49. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts +11 -0
  50. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
  51. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +16 -8
  52. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
  53. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +16 -11
  54. package/src/shade/renderer/geometry/meshlet/MeshletBatch.d.ts.map +1 -1
  55. package/src/shade/renderer/geometry/meshlet/MeshletBatch.js +3 -29
  56. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.d.ts +22 -0
  57. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.d.ts.map +1 -0
  58. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.js +49 -0
  59. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +77 -7
  60. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +36 -23
  61. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +17 -14
  62. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +19 -1
  63. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
  64. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +21 -1
  65. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.d.ts.map +1 -1
  66. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.js +5 -9
  67. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevel.d.ts +32 -0
  68. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevel.d.ts.map +1 -0
  69. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts +59 -0
  70. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts.map +1 -0
  71. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelSummary.d.ts +44 -0
  72. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelSummary.d.ts.map +1 -0
  73. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts +36 -0
  74. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts.map +1 -0
  75. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_levels.js → level/vgeo_build_levels.js} +61 -95
  76. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_root_group.d.ts +19 -0
  77. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_root_group.d.ts.map +1 -0
  78. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_root_group.js → level/vgeo_build_root_group.js} +1 -1
  79. package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts +73 -0
  80. package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts.map +1 -0
  81. package/src/shade/renderer/geometry/virtual/build/{VirtualGeometryMesh.js → mesh/VirtualGeometryMesh.js} +7 -7
  82. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +29 -0
  83. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -0
  84. package/src/shade/renderer/geometry/virtual/build/{vgeo_compute_vertex_normals.js → mesh/vgeo_compute_vertex_normals.js} +3 -3
  85. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts +31 -0
  86. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts.map +1 -0
  87. package/src/shade/renderer/geometry/virtual/build/{vgeo_compute_vertex_tangents.js → mesh/vgeo_compute_vertex_tangents.js} +5 -5
  88. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts +46 -0
  89. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -0
  90. package/src/shade/renderer/geometry/virtual/build/{vgeo_normalize_source.js → mesh/vgeo_normalize_source.js} +9 -9
  91. package/src/shade/renderer/geometry/virtual/build/page/vgeo_assemble_pages.d.ts +13 -0
  92. package/src/shade/renderer/geometry/virtual/build/page/vgeo_assemble_pages.d.ts.map +1 -0
  93. package/src/shade/renderer/geometry/virtual/build/{vgeo_assemble_pages.js → page/vgeo_assemble_pages.js} +63 -16
  94. package/src/shade/renderer/geometry/virtual/build/page/vgeo_build_page_nodes.d.ts +13 -0
  95. package/src/shade/renderer/geometry/virtual/build/page/vgeo_build_page_nodes.d.ts.map +1 -0
  96. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_page_nodes.js → page/vgeo_build_page_nodes.js} +4 -4
  97. package/src/shade/renderer/geometry/virtual/build/page/vgeo_page_node_count.d.ts +22 -0
  98. package/src/shade/renderer/geometry/virtual/build/page/vgeo_page_node_count.d.ts.map +1 -0
  99. package/src/shade/renderer/geometry/virtual/build/{vgeo_page_node_count.js → page/vgeo_page_node_count.js} +2 -2
  100. package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts +52 -0
  101. package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts.map +1 -0
  102. package/src/shade/renderer/geometry/virtual/build/{VGeoFaceGraph.js → partition/VGeoFaceGraph.js} +1 -1
  103. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.d.ts +14 -0
  104. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.d.ts.map +1 -0
  105. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_graph.d.ts +29 -0
  106. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_graph.d.ts.map +1 -0
  107. package/src/shade/renderer/geometry/virtual/build/{vgeo_partition_graph.js → partition/vgeo_partition_graph.js} +2 -2
  108. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.d.ts +41 -0
  109. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.d.ts.map +1 -0
  110. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.js +92 -0
  111. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.d.ts +10 -0
  112. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.d.ts.map +1 -0
  113. package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set.js → partition/vgeo_split_face_set.js} +12 -55
  114. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts +15 -0
  115. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts.map +1 -0
  116. package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set_packed.js → partition/vgeo_split_face_set_packed.js} +3 -3
  117. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_bounding_sphere.d.ts +12 -0
  118. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_bounding_sphere.d.ts.map +1 -0
  119. package/src/shade/renderer/geometry/virtual/build/{vgeo_bounding_sphere.js → sphere/vgeo_bounding_sphere.js} +3 -3
  120. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_containing_sphere.d.ts +18 -0
  121. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_containing_sphere.d.ts.map +1 -0
  122. package/src/shade/renderer/geometry/virtual/build/{vgeo_containing_sphere.js → sphere/vgeo_containing_sphere.js} +3 -3
  123. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_sphere_inflate_to_contain.d.ts +9 -0
  124. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_sphere_inflate_to_contain.d.ts.map +1 -0
  125. package/src/shade/renderer/geometry/virtual/build/{vgeo_sphere_inflate_to_contain.js → sphere/vgeo_sphere_inflate_to_contain.js} +1 -1
  126. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts +24 -0
  127. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts.map +1 -0
  128. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts +20 -0
  129. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts.map +1 -0
  130. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts +14 -0
  131. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts.map +1 -0
  132. package/src/shade/renderer/geometry/virtual/build/{vgeo_extract_submesh.js → submesh/vgeo_extract_submesh.js} +5 -5
  133. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts +12 -0
  134. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts.map +1 -0
  135. package/src/shade/renderer/geometry/virtual/build/{vgeo_gather_faces.js → submesh/vgeo_gather_faces.js} +2 -2
  136. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_narrow_indices.d.ts +11 -0
  137. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_narrow_indices.d.ts.map +1 -0
  138. package/src/shade/renderer/geometry/virtual/build/{vgeo_narrow_indices.js → submesh/vgeo_narrow_indices.js} +3 -3
  139. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +1 -1
  140. package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts.map +1 -1
  141. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +10 -11
  142. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +22 -0
  143. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
  144. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +24 -0
  145. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.d.ts +8 -2
  146. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.d.ts.map +1 -1
  147. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js +8 -2
  148. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts +12 -0
  149. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts.map +1 -1
  150. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +13 -0
  151. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts +8 -1
  152. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts.map +1 -1
  153. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +11 -3
  154. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts +9 -0
  155. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts.map +1 -1
  156. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +10 -1
  157. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.d.ts +15 -0
  158. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.d.ts.map +1 -0
  159. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.js +15 -0
  160. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.d.ts +18 -0
  161. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.d.ts.map +1 -0
  162. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_ENTRY_COUNT.js +17 -0
  163. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.d.ts +8 -0
  164. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.d.ts.map +1 -0
  165. package/src/shade/renderer/geometry/virtual/format/page/VGEO_MAX_PARENT_REF.js +7 -0
  166. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.d.ts +3 -0
  167. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +16 -0
  168. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.d.ts +14 -0
  169. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.d.ts.map +1 -0
  170. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_ENTRY_SIZE.js +13 -0
  171. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.d.ts +11 -0
  172. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.d.ts.map +1 -0
  173. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PARENT_PAGE_ENTRY_SIZE.js +10 -0
  174. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts +28 -0
  175. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts.map +1 -1
  176. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +7 -3
  177. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts +20 -0
  178. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts.map +1 -1
  179. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.js +20 -2
  180. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts +4 -0
  181. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts.map +1 -1
  182. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.js +4 -0
  183. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts +29 -3
  184. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts.map +1 -1
  185. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.js +81 -122
  186. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +40 -0
  187. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
  188. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +44 -0
  189. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts +69 -0
  190. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts.map +1 -0
  191. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.js +236 -0
  192. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts +14 -0
  193. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts.map +1 -1
  194. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.js +15 -0
  195. package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.d.ts +31 -0
  196. package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.d.ts.map +1 -0
  197. package/src/shade/renderer/geometry/virtual/format/read/vgeo_byte_source_from_descriptor.js +32 -0
  198. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts +10 -0
  199. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts.map +1 -1
  200. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.js +21 -1
  201. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.d.ts +15 -0
  202. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.d.ts.map +1 -0
  203. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_transfer.js +84 -0
  204. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts +22 -0
  205. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts.map +1 -0
  206. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.js +96 -0
  207. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
  208. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +63 -3
  209. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
  210. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +219 -17
  211. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
  212. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +156 -11
  213. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +656 -0
  214. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts +33 -36
  215. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts.map +1 -1
  216. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.js +109 -193
  217. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +109 -29
  218. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
  219. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +173 -105
  220. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +37 -0
  221. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +109 -26
  222. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +548 -0
  223. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts +29 -50
  224. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -1
  225. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +55 -487
  226. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts +84 -0
  227. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts.map +1 -0
  228. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.js +393 -0
  229. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts +234 -0
  230. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -0
  231. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +1388 -0
  232. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.d.ts +81 -0
  233. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.d.ts.map +1 -0
  234. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidencyOptions.js +89 -0
  235. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts +53 -34
  236. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts.map +1 -1
  237. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.js +407 -124
  238. package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.d.ts +100 -0
  239. package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.d.ts.map +1 -0
  240. package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.js +219 -0
  241. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.d.ts +17 -0
  242. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.d.ts.map +1 -0
  243. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_EXPAND.js +47 -0
  244. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.d.ts +20 -0
  245. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.d.ts.map +1 -0
  246. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_COARSEN.js +19 -0
  247. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts.map +1 -1
  248. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.js +30 -12
  249. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts +8 -8
  250. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -1
  251. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +11 -73
  252. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts +19 -0
  253. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts.map +1 -0
  254. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.js +98 -0
  255. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts +31 -0
  256. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts.map +1 -0
  257. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.js +99 -0
  258. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.d.ts +17 -0
  259. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.d.ts.map +1 -0
  260. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.js +39 -0
  261. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts.map +1 -1
  262. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.js +18 -1
  263. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts.map +1 -1
  264. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.js +5 -15
  265. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.d.ts +3 -0
  266. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.d.ts.map +1 -0
  267. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_remainder.js +77 -0
  268. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts +3 -0
  269. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts.map +1 -0
  270. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.js +130 -0
  271. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts.map +1 -1
  272. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js +32 -37
  273. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -1
  274. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +576 -230
  275. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts +15 -18
  276. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts.map +1 -1
  277. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.js +19 -28
  278. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts.map +1 -1
  279. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.js +45 -25
  280. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +1 -0
  281. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +11 -0
  282. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +1 -1
  283. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts +0 -1
  284. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.js +12 -10
  285. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts +1 -0
  286. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.js +8 -0
  287. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts +7 -2
  288. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts.map +1 -1
  289. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.js +8 -3
  290. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts +2 -0
  291. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.js +19 -0
  292. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts.map +1 -1
  293. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.js +1 -1
  294. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.d.ts +5 -0
  295. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.d.ts.map +1 -0
  296. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_OFFSET.js +22 -0
  297. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.d.ts +14 -0
  298. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.d.ts.map +1 -0
  299. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PARENT_ROW_WORDS.js +13 -0
  300. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.d.ts +17 -0
  301. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.d.ts.map +1 -0
  302. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_PARENT_BIT.js +16 -0
  303. package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.d.ts +15 -0
  304. package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.d.ts.map +1 -0
  305. package/src/shade/renderer/geometry/virtual/runtime/make_vgeo_page_worker.js +16 -0
  306. package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.d.ts +23 -0
  307. package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.d.ts.map +1 -0
  308. package/src/shade/renderer/geometry/virtual/runtime/vgeo_frontier_words.js +28 -0
  309. package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.d.ts +2 -0
  310. package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.d.ts.map +1 -0
  311. package/src/shade/renderer/geometry/virtual/runtime/vgeo_page.worker.js +13 -0
  312. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts +10 -5
  313. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts.map +1 -1
  314. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.js +15 -6
  315. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts +6 -1
  316. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -1
  317. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +38 -1
  318. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.d.ts.map +1 -1
  319. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.js +4 -3
  320. package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
  321. package/src/shade/renderer/loader/gltf/load_gltf.js +0 -19
  322. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  323. package/src/shade/renderer/material/chunk_material_draw_side.d.ts +14 -0
  324. package/src/shade/renderer/material/chunk_material_draw_side.d.ts.map +1 -0
  325. package/src/shade/renderer/material/chunk_material_draw_side.js +34 -0
  326. package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts.map +1 -1
  327. package/src/shade/renderer/material/standard/fragment_gbuffer.js +8 -1
  328. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  329. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +6 -3
  330. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  331. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +6 -3
  332. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
  333. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +13 -1
  334. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  335. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts +19 -5
  336. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts.map +1 -1
  337. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +23 -5
  338. package/src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.d.ts.map +1 -1
  339. package/src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.js +12 -5
  340. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  341. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +12 -4
  342. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.d.ts +22 -0
  343. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.d.ts.map +1 -0
  344. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.js +36 -0
  345. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts +2 -0
  346. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
  347. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +58 -57
  348. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts +15 -0
  349. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts.map +1 -0
  350. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.js +35 -0
  351. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.d.ts.map +1 -1
  352. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.js +3 -5
  353. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.d.ts.map +1 -1
  354. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.js +3 -4
  355. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts +10 -1
  356. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts.map +1 -1
  357. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.js +38 -29
  358. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.d.ts +5 -0
  359. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.d.ts.map +1 -1
  360. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.js +63 -55
  361. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
  362. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +3 -4
  363. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts.map +1 -1
  364. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.js +1 -0
  365. package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.d.ts.map +1 -1
  366. package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.js +7 -7
  367. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts.map +1 -1
  368. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.js +83 -78
  369. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  370. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +44 -53
  371. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.d.ts.map +1 -1
  372. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.js +6 -2
  373. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.d.ts.map +1 -1
  374. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.js +6 -2
  375. package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.d.ts.map +1 -1
  376. package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.js +19 -2
  377. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +7 -4
  378. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  379. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +3 -2
  380. package/src/shade/renderer/rasterize/native/chunk_rasterize_triangle.d.ts.map +1 -1
  381. package/src/shade/renderer/rasterize/native/chunk_rasterize_triangle.js +15 -4
  382. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
  383. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.js +16 -5
  384. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.d.ts.map +1 -1
  385. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.js +16 -5
  386. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts.map +1 -1
  387. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +2 -1
  388. package/src/shade/renderer/rasterize/native/rasterization_shader_resources.d.ts.map +1 -1
  389. package/src/shade/renderer/rasterize/native/rasterization_shader_resources.js +4 -0
  390. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
  391. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.js +16 -7
  392. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  393. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +85 -89
  394. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  395. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +28 -32
  396. package/src/shade/renderer/scene/GPUSceneContext.d.ts +22 -2
  397. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  398. package/src/shade/renderer/scene/GPUSceneContext.js +61 -4
  399. package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts +19 -0
  400. package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts.map +1 -1
  401. package/src/shade/renderer/scene/NODE3D_STRUCT.js +34 -0
  402. package/src/shade/renderer/scene/chunk_node3d_flags.d.ts +9 -0
  403. package/src/shade/renderer/scene/chunk_node3d_flags.d.ts.map +1 -0
  404. package/src/shade/renderer/scene/chunk_node3d_flags.js +23 -0
  405. package/src/shade/renderer/scene/chunk_scene_database_access.d.ts.map +1 -1
  406. package/src/shade/renderer/scene/chunk_scene_database_access.js +15 -1
  407. package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
  408. package/src/shade/renderer/scene/rows/GPUSceneRows.js +9 -1
  409. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.d.ts +7 -0
  410. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.d.ts.map +1 -1
  411. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.js +16 -4
  412. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts +12 -0
  413. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts.map +1 -0
  414. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.js +18 -0
  415. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.d.ts +24 -0
  416. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.d.ts.map +1 -0
  417. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.js +33 -0
  418. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
  419. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +92 -16
  420. package/src/shade/renderer/view/GPUViewContext.d.ts +5 -0
  421. package/src/shade/renderer/view/GPUViewContext.d.ts.map +1 -1
  422. package/src/shade/renderer/view/GPUViewContext.js +21 -0
  423. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  424. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +74 -3
  425. package/src/shade/playground/vgeo_viewer/README.md +0 -150
  426. package/src/shade/playground/vgeo_viewer/cut_geometry.js +0 -460
  427. package/src/shade/playground/vgeo_viewer/index.html +0 -77
  428. package/src/shade/playground/vgeo_viewer/main.js +0 -1343
  429. package/src/shade/playground/vgeo_viewer/select_cut.js +0 -481
  430. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts +0 -258
  431. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts.map +0 -1
  432. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.js +0 -723
  433. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts +0 -49
  434. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts.map +0 -1
  435. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.js +0 -50
  436. package/src/shade/renderer/loader/gltf/fix_up_material_sides.js +0 -109
  437. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.js +0 -78
  438. package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.js +0 -100
  439. package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.js +0 -26
  440. package/src/shade/renderer/shader/chunk/jobs/NOTES.md +0 -47
  441. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.d.ts +0 -3
  442. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.d.ts.map +0 -1
  443. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.js +0 -102
  444. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevel.js → level/VGeoLevel.js} +0 -0
  445. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevelOptions.js → level/VGeoLevelOptions.js} +0 -0
  446. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevelSummary.js → level/VGeoLevelSummary.js} +0 -0
  447. /package/src/shade/renderer/geometry/virtual/build/{vgeo_graph_components.js → partition/vgeo_graph_components.js} +0 -0
  448. /package/src/shade/renderer/geometry/virtual/build/{VGeoGatheredFaces.js → submesh/VGeoGatheredFaces.js} +0 -0
  449. /package/src/shade/renderer/geometry/virtual/build/{VGeoSubmesh.js → submesh/VGeoSubmesh.js} +0 -0
@@ -0,0 +1,1382 @@
1
+ import { assert } from "../../assert.js";
2
+ import { lsb_32 } from "../lsb_32.js";
3
+ import { LZ4_LEVEL } from "./LZ4_LEVEL.js";
4
+ import { LZ4_MAX_INPUT_SIZE } from "./LZ4_MAX_INPUT_SIZE.js";
5
+
6
+ /** the shortest match the format can code — the match length field is biased by this */
7
+ const MIN_MATCH = 4;
8
+
9
+ /** no match starts within this many bytes of the end of a block */
10
+ const MATCH_FIND_LIMIT = 12;
11
+
12
+ /** the last bytes of a block are literals, and no match may cover them */
13
+ const LAST_LITERALS = 5;
14
+
15
+ /** below this there is no room for a match at all, so the block is one literal run */
16
+ const MIN_LENGTH = MATCH_FIND_LIMIT + 1;
17
+
18
+ /** the offset field is sixteen bits, so a match reaches back no further than this */
19
+ const MAX_DISTANCE = 0xFFFF;
20
+
21
+ /** both length nibbles saturate here, and continue in whole bytes */
22
+ const NIBBLE_MAX = 15;
23
+
24
+ /** a length continuation codes this much per byte, and a byte below it ends the run */
25
+ const CONTINUATION_MAX = 255;
26
+
27
+ /**
28
+ * Literal runs shorter than this are cheaper to copy a byte at a time than to hand to `set`, whose
29
+ * call overhead dominates until there is a memmove's worth of work behind it. The high-compression
30
+ * encoder finds more matches than the fast one and so writes *shorter* literal runs on geometry —
31
+ * about 38 bytes a sequence against 62 — but on text it is the other way round, and the runs there
32
+ * are long enough that the call pays.
33
+ */
34
+ const BULK_COPY_LENGTH = 32;
35
+
36
+ /**
37
+ * The match length past which the lazy parse stops trying to squeeze a second match in front of
38
+ * the one it has: the longest match whose length still fits the token's nibble.
39
+ */
40
+ const OPTIMAL_ML = (NIBBLE_MAX - 1) + MIN_MATCH;
41
+
42
+ /** Knuth's multiplicative constant, which is what the reference encoder hashes with */
43
+ const HASH_MULTIPLIER = 2654435761;
44
+
45
+ /**
46
+ * The high-compression encoder hashes into a table twice the width of the fast one's, because it
47
+ * keeps a chain per slot rather than a single position and a collision costs a walk rather than a
48
+ * miss.
49
+ */
50
+ const HASH_LOG = 15;
51
+ const HASH_SHIFT = 32 - HASH_LOG;
52
+
53
+ /**
54
+ * Positions are held in an index space biased by one window, so that a zeroed hash slot reads as a
55
+ * position a whole window before the block — which is to say, as one no match may reach. That is
56
+ * what keeps blocks self-contained without a special case in the search: the "is this candidate
57
+ * inside the block" test the reference already needs is the same test that rejects an empty slot.
58
+ */
59
+ const IDX_BASE = 0x10000;
60
+
61
+ /** how many attempts a search makes before pattern analysis is worth its own cost */
62
+ const PATTERN_ANALYSIS_THRESHOLD = 128;
63
+
64
+ /** positions the shortest path is solved over at once */
65
+ const OPT_NUM = 1 << 12;
66
+
67
+ /** positions past the last match a solved window also prices, as plain literals */
68
+ const TRAILING_LITERALS = 3;
69
+
70
+ /** the parse tables carry this many entries, which is the furthest either index above reaches */
71
+ const OPT_TABLE_SIZE = OPT_NUM + TRAILING_LITERALS;
72
+
73
+ /**
74
+ * Whether the four bytes under the search are a repeat of a single byte, which is what makes a
75
+ * chain of consecutive positions worth jumping over rather than walking.
76
+ */
77
+ const REPEAT_UNTESTED = 0;
78
+ const REPEAT_NOT = 1;
79
+ const REPEAT_CONFIRMED = 2;
80
+
81
+ /**
82
+ * What each level searches with, indexed by the level itself. The boundaries that matter are not
83
+ * the level numbers: the strategy changes at 10, and pattern analysis turns on above
84
+ * {@link PATTERN_ANALYSIS_THRESHOLD} attempts, which is what makes 9 the first level that has it.
85
+ *
86
+ * Entries for 0 and 2 are `null` — the reference gives level 2 different algorithms in different
87
+ * releases, so this encoder does not offer it. See {@link LZ4_LEVEL}.
88
+ *
89
+ * `attempts` is how many chain links a single search walks. `sufficient_length` is the match length
90
+ * at which a search stops looking for better and the parse takes what it has; it is also what keeps
91
+ * the optimal parse out of quadratic time on a run of one byte.
92
+ *
93
+ * @see https://github.com/lz4/lz4/blob/dev/lib/lz4hc.c — `k_clTable`
94
+ * @type {({attempts: number, sufficient_length: number, optimal: boolean, full_update: boolean}|null)[]}
95
+ */
96
+ const LEVEL_TABLE = [
97
+ null,
98
+ null,
99
+ null,
100
+ { attempts: 4, sufficient_length: 16, optimal: false, full_update: false },
101
+ { attempts: 8, sufficient_length: 16, optimal: false, full_update: false },
102
+ { attempts: 16, sufficient_length: 16, optimal: false, full_update: false },
103
+ { attempts: 32, sufficient_length: 16, optimal: false, full_update: false },
104
+ { attempts: 64, sufficient_length: 16, optimal: false, full_update: false },
105
+ { attempts: 128, sufficient_length: 16, optimal: false, full_update: false },
106
+ { attempts: 256, sufficient_length: 16, optimal: false, full_update: false },
107
+ { attempts: 96, sufficient_length: 64, optimal: true, full_update: false },
108
+ { attempts: 512, sufficient_length: 128, optimal: true, full_update: false },
109
+ { attempts: 16384, sufficient_length: OPT_NUM, optimal: true, full_update: true }
110
+ ];
111
+
112
+ /**
113
+ * The newest position carrying each four-byte hash, and, per position, the distance back to the
114
+ * previous position carrying the same one. Together they are the chain a search walks.
115
+ *
116
+ * Both are allocated on the first high-compression call rather than at module scope, and the parse
117
+ * tables below on the first call above level 9. meep ships raw source: a consumer that only ever
118
+ * *decodes* — which is what the runtime does — would otherwise hold a third of a megabyte of
119
+ * encoder tables for nothing.
120
+ *
121
+ * Module-static once taken, on the same terms as the fast encoder's table: this is straight-line
122
+ * synchronous code that clears what it needs on entry, so no second call can be part-way through.
123
+ *
124
+ * @type {Int32Array|null}
125
+ */
126
+ let hash_table = null;
127
+
128
+ /** @type {Uint16Array|null} */
129
+ let chain_table = null;
130
+
131
+ /** the cheapest coding of the first `n` bytes found so far, and what the last step of it was */
132
+ /** @type {Int32Array|null} */
133
+ let opt_price = null;
134
+ /** @type {Int32Array|null} */
135
+ let opt_offset = null;
136
+ /** @type {Int32Array|null} */
137
+ let opt_match_length = null;
138
+ /** @type {Int32Array|null} */
139
+ let opt_literal_length = null;
140
+
141
+ /*
142
+ The call in progress. The reference carries these as a context struct and a pile of pointers; here
143
+ they are module bindings for the same reason the tables are, and with the same argument for why it
144
+ is safe. Positions below are *block* positions — zero is the first byte of the input — and the
145
+ index space the hash and chain tables are keyed in is that plus IDX_BASE.
146
+ */
147
+
148
+ /** @type {Uint8Array} */
149
+ let source = null;
150
+ /** @type {DataView} */
151
+ let view = null;
152
+ /** @type {Uint8Array} */
153
+ let destination = null;
154
+
155
+ /** where block position zero sits in {@link source} */
156
+ let base = 0;
157
+
158
+ /** bytes of input */
159
+ let input_size = 0;
160
+
161
+ /** one past the last position a search may start at */
162
+ let match_find_limit = 0;
163
+
164
+ /** one past the last byte a match may cover */
165
+ let match_limit = 0;
166
+
167
+ /** positions below this are already on their chain */
168
+ let next_to_update = 0;
169
+
170
+ /** where the next output byte goes, and one past the last the block may write */
171
+ let out = 0;
172
+ let out_end = 0;
173
+
174
+ /*
175
+ What the last search found. The searches run thousands of times over a page and the reference
176
+ returns a three-field struct from each one; returning an object here would be that many allocations
177
+ for a value read immediately and never kept.
178
+ */
179
+
180
+ /** the length of the match found, less than {@link MIN_MATCH} when there is none */
181
+ let found_length = 0;
182
+
183
+ /** how far back it reaches */
184
+ let found_offset = 0;
185
+
186
+ /** how far *before* the position searched from it starts, as a negative number */
187
+ let found_back = 0;
188
+
189
+ /** where {@link encode_window} left the parse, for the same reason */
190
+ let window_ip = 0;
191
+ let window_anchor = 0;
192
+
193
+ /**
194
+ * @param {number} sequence four bytes of input, read little-endian
195
+ * @returns {number} a slot in {@link hash_table}
196
+ */
197
+ function hash_of(sequence) {
198
+ return Math.imul(sequence, HASH_MULTIPLIER) >>> HASH_SHIFT;
199
+ }
200
+
201
+ /**
202
+ * Bytes a length continuation takes to carry `remainder`.
203
+ *
204
+ * @param {number} remainder what is left of a length once its nibble saturated
205
+ * @returns {number}
206
+ */
207
+ function continuation_size(remainder) {
208
+ return 1 + ((remainder / CONTINUATION_MAX) | 0);
209
+ }
210
+
211
+ /**
212
+ * Write a length continuation: whole 255s, then a byte below 255 to end it.
213
+ *
214
+ * @param {number} d
215
+ * @param {number} remainder
216
+ * @returns {number} the offset just past the last byte written
217
+ */
218
+ function write_continuation(d, remainder) {
219
+ let r = remainder;
220
+ let p = d;
221
+
222
+ while (r >= CONTINUATION_MAX) {
223
+ destination[p++] = CONTINUATION_MAX;
224
+ r -= CONTINUATION_MAX;
225
+ }
226
+
227
+ destination[p++] = r;
228
+
229
+ return p;
230
+ }
231
+
232
+ /**
233
+ * Two bytes of the block, with anything outside it reading as zero.
234
+ *
235
+ * This exists for one read: the candidate's half of the cheap test the search makes before a full
236
+ * compare, which looks at the two bytes where the *current best* match would end on either side.
237
+ * When a search is allowed to start behind the position it was called at — which is what the lazy
238
+ * parse's second and third searches do — the candidate's half is at `match - look_back + longest - 1`,
239
+ * and nothing in the arithmetic there stops it landing before the block. The reference reads
240
+ * whatever the buffer holds; a block that is a window into a larger buffer would then compress
241
+ * differently depending on what its caller happened to put in front of it, which is not a property
242
+ * an encoder is allowed to have. Reachable or not, it is cheap to make impossible, and the specs
243
+ * pin it by compressing the same payload at five offsets inside five different paddings.
244
+ *
245
+ * The *searched* side of that test needs no such guard: it sits at `low_limit + longest - 1`, and
246
+ * `longest` is never more than what a match starting at `low_limit` could cover, so it is inside
247
+ * the block by construction.
248
+ *
249
+ * @param {number} position a block position, which may be outside the block
250
+ * @returns {number}
251
+ */
252
+ function read16_in_block(position) {
253
+ const low = (position >= 0 && position < input_size) ? source[base + position] : 0;
254
+ const high = (position + 1 >= 0 && position + 1 < input_size) ? source[base + position + 1] : 0;
255
+
256
+ return low | (high << 8);
257
+ }
258
+
259
+ /**
260
+ * How many bytes beyond the four already known to agree two positions agree on.
261
+ *
262
+ * @param {number} a the later position, past its first four bytes
263
+ * @param {number} b the earlier position, past its first four bytes
264
+ * @param {number} limit one past the last byte a match may cover
265
+ * @returns {number}
266
+ */
267
+ function count_match(a, b, limit) {
268
+ let p = a;
269
+ let q = b;
270
+
271
+ while (p + 4 <= limit && view.getUint32(base + p, true) === view.getUint32(base + q, true)) {
272
+ p += 4;
273
+ q += 4;
274
+ }
275
+
276
+ while (p < limit && source[base + p] === source[base + q]) {
277
+ p++;
278
+ q++;
279
+ }
280
+
281
+ return p - a;
282
+ }
283
+
284
+ /**
285
+ * How many bytes *before* two positions agree, as a negative number, bounded on both sides.
286
+ *
287
+ * @param {number} ip the later position
288
+ * @param {number} match the earlier position
289
+ * @param {number} i_min the furthest back `ip` may walk
290
+ * @param {number} m_min the furthest back `match` may walk
291
+ * @returns {number} zero or less
292
+ */
293
+ function count_back(ip, match, i_min, m_min) {
294
+ const a = i_min - ip;
295
+ const b = m_min - match;
296
+ const min = a > b ? a : b;
297
+
298
+ let back = 0;
299
+
300
+ while ((back - min) > 3) {
301
+ const v = view.getUint32(base + ip + back - 4, true) ^ view.getUint32(base + match + back - 4, true);
302
+
303
+ if (v !== 0) {
304
+ // the bytes nearest `back` are the high ones of a little-endian word, so the leading
305
+ // zero bytes of the difference are the ones that agreed
306
+ return back - (Math.clz32(v) >> 3);
307
+ }
308
+
309
+ back -= 4;
310
+ }
311
+
312
+ while (back > min && source[base + ip + back - 1] === source[base + match + back - 1]) {
313
+ back--;
314
+ }
315
+
316
+ return back;
317
+ }
318
+
319
+ /**
320
+ * How far a repeat of `pattern` runs forward from `position`.
321
+ *
322
+ * `pattern` is four bytes of a run whose period divides four — one byte, two bytes, or four — which
323
+ * is the only shape the caller ever offers it, and what makes reading it four bytes at a time
324
+ * rather than eight, as the reference does on a 64-bit word, give the same answer.
325
+ *
326
+ * @param {number} position
327
+ * @param {number} end one past the last byte it may look at
328
+ * @param {number} pattern
329
+ * @returns {number}
330
+ */
331
+ function count_pattern(position, end, pattern) {
332
+ let p = position;
333
+
334
+ while (p < end - 3) {
335
+ const diff = view.getUint32(base + p, true) ^ pattern;
336
+
337
+ if (diff === 0) {
338
+ p += 4;
339
+ continue;
340
+ }
341
+
342
+ // the first byte that differs is the lowest one set, and the bytes below it agreed
343
+ return (p - position) + (lsb_32(diff) >> 3);
344
+ }
345
+
346
+ let shift = 0;
347
+
348
+ while (p < end && source[base + p] === ((pattern >>> shift) & 0xFF)) {
349
+ p++;
350
+ shift = (shift + 8) & 31;
351
+ }
352
+
353
+ return p - position;
354
+ }
355
+
356
+ /**
357
+ * How far a repeat of `pattern` runs *backwards* from `position`.
358
+ *
359
+ * @param {number} position
360
+ * @param {number} low the furthest back it may look
361
+ * @param {number} pattern
362
+ * @returns {number} zero or more
363
+ */
364
+ function reverse_count_pattern(position, low, pattern) {
365
+ let p = position;
366
+
367
+ while (p >= low + 4) {
368
+ if (view.getUint32(base + p - 4, true) !== pattern) {
369
+ break;
370
+ }
371
+
372
+ p -= 4;
373
+ }
374
+
375
+ // the byte just before `p` is the pattern's last one, and walking back walks the pattern back
376
+ let shift = 24;
377
+
378
+ while (p > low && source[base + p - 1] === ((pattern >>> shift) & 0xFF)) {
379
+ p--;
380
+ shift -= 8;
381
+ }
382
+
383
+ return position - p;
384
+ }
385
+
386
+ /**
387
+ * Put every position below `position` on its chain.
388
+ *
389
+ * Lazily, from wherever the last search left off: a position is only ever wanted by a search that
390
+ * starts after it, so nothing is built that is never walked. The chain holds, per position, the
391
+ * *distance* back to the previous position with the same hash, which is what lets it be sixteen
392
+ * bits wide — a distance further than the window is one no match may use anyway, and saturating it
393
+ * there ends the walk exactly where the window does.
394
+ *
395
+ * @param {number} position a block position
396
+ */
397
+ function insert(position) {
398
+ const target = position + IDX_BASE;
399
+
400
+ let idx = next_to_update;
401
+
402
+ while (idx < target) {
403
+ const h = hash_of(view.getUint32(base + idx - IDX_BASE, true));
404
+
405
+ let delta = idx - hash_table[h];
406
+
407
+ if (delta > MAX_DISTANCE) {
408
+ delta = MAX_DISTANCE;
409
+ }
410
+
411
+ chain_table[idx & 0xFFFF] = delta;
412
+ hash_table[h] = idx;
413
+
414
+ idx++;
415
+ }
416
+
417
+ next_to_update = target;
418
+ }
419
+
420
+ /**
421
+ * Walk the chain from `position` and leave the best match it holds in {@link found_length},
422
+ * {@link found_offset} and {@link found_back}.
423
+ *
424
+ * `low_limit` is where the match is allowed to *start*, which the caller sets behind `position`
425
+ * when it wants a match that reaches back over bytes an earlier one already covers; `found_back`
426
+ * comes back as how far behind `position` the match actually starts, and is zero or less. `longest`
427
+ * is what the caller already has, so a search that cannot beat it reports nothing.
428
+ *
429
+ * @param {number} position where the four bytes being matched are
430
+ * @param {number} low_limit the earliest position the match may start at
431
+ * @param {number} high_limit one past the last byte the match may cover
432
+ * @param {number} longest the length to beat
433
+ * @param {number} max_attempts how many links of the chain to walk
434
+ * @param {boolean} pattern_analysis whether to jump over runs rather than walk them
435
+ * @param {boolean} chain_swap whether to change chains on a tie
436
+ */
437
+ function insert_and_get_wider_match(position, low_limit, high_limit, longest, max_attempts, pattern_analysis, chain_swap) {
438
+ // the call's context, read once: the loop below runs hundreds of millions of times over a
439
+ // conversion, and a module binding is not a local
440
+ const bytes = source;
441
+ const data = view;
442
+ const origin = base;
443
+ const length = input_size;
444
+ const chain = chain_table;
445
+
446
+ const ip_index = position + IDX_BASE;
447
+
448
+ // A match reaches back a window, and never before the block. Below one window in, those are the
449
+ // same bound, and the block's own start is what holds — which is also what makes an empty hash
450
+ // slot, reading as a position a window before the block, fail the test for free.
451
+ const lowest_match_index = (IDX_BASE + MAX_DISTANCE + 1) > ip_index ? IDX_BASE : ip_index - MAX_DISTANCE;
452
+
453
+ const look_back_length = position - low_limit;
454
+ const pattern = data.getUint32(origin + position, true);
455
+
456
+ let attempts = max_attempts;
457
+ let match_chain_pos = 0;
458
+ let repeat = REPEAT_UNTESTED;
459
+ let source_pattern_length = 0;
460
+ let offset = 0;
461
+ let s_back = 0;
462
+
463
+ insert(position);
464
+
465
+ let match_index = hash_table[hash_of(pattern)];
466
+
467
+ while (match_index >= lowest_match_index && attempts > 0) {
468
+ const match_position = match_index - IDX_BASE;
469
+
470
+ let match_length = 0;
471
+
472
+ attempts--;
473
+
474
+ // The cheap rejection: unless the two bytes where the current best match *ends* agree, no
475
+ // longer match can start here, and the full compare is not worth making. This is the
476
+ // hottest read in the encoder — once per link of every chain walked — so the candidate's
477
+ // side takes the guard only on the addresses that need it.
478
+ const gate = origin + low_limit + longest - 1;
479
+ const candidate_gate = match_position - look_back_length + longest - 1;
480
+
481
+ const candidate_bytes = (candidate_gate >= 0 && candidate_gate + 1 < length)
482
+ ? (bytes[origin + candidate_gate] | (bytes[origin + candidate_gate + 1] << 8))
483
+ : read16_in_block(candidate_gate);
484
+
485
+ if ((bytes[gate] | (bytes[gate + 1] << 8)) === candidate_bytes) {
486
+ if (data.getUint32(origin + match_position, true) === pattern) {
487
+ const back = look_back_length !== 0 ? count_back(position, match_position, low_limit, 0) : 0;
488
+
489
+ match_length = MIN_MATCH + count_match(position + MIN_MATCH, match_position + MIN_MATCH, high_limit);
490
+ match_length -= back;
491
+
492
+ if (match_length > longest) {
493
+ longest = match_length;
494
+ offset = ip_index - match_index;
495
+ s_back = back;
496
+ }
497
+ }
498
+ }
499
+
500
+ // A tie means this chain has nothing more to offer. The positions inside the match have
501
+ // chains of their own, and the one that steps furthest back is the one worth following.
502
+ if (chain_swap && match_length === longest && match_index + longest <= ip_index) {
503
+ const end = longest - MIN_MATCH + 1;
504
+
505
+ let distance_to_next_match = 1;
506
+ let step = 1;
507
+ let accel = 1 << 4;
508
+
509
+ for (let p = 0; p < end; p += step) {
510
+ const candidate_distance = chain[(match_index + p) & 0xFFFF];
511
+
512
+ step = accel >> 4;
513
+ accel++;
514
+
515
+ if (candidate_distance > distance_to_next_match) {
516
+ distance_to_next_match = candidate_distance;
517
+ match_chain_pos = p;
518
+ accel = 1 << 4;
519
+ }
520
+ }
521
+
522
+ if (distance_to_next_match > 1) {
523
+ if (distance_to_next_match > match_index) {
524
+ break;
525
+ }
526
+
527
+ match_index -= distance_to_next_match;
528
+ continue;
529
+ }
530
+ }
531
+
532
+ // Consecutive positions on one chain are a run of the same byte. Walking it link by link
533
+ // is what takes the wider levels quadratic on a page of zeros; measuring the run instead
534
+ // and jumping to the best place in it is what makes them finish at all. It does not make
535
+ // them *fast*: the reference measures pattern analysis as paying for its own cost by level
536
+ // 9, on an eight-byte scan this port does at four, and here level 9 still comes out about
537
+ // 5% slower than level 3 on real pages.
538
+ if (pattern_analysis && chain[match_index & 0xFFFF] === 1 && match_chain_pos === 0) {
539
+ const candidate_index = match_index - 1;
540
+
541
+ if (repeat === REPEAT_UNTESTED) {
542
+ if (((pattern & 0xFFFF) === (pattern >>> 16)) && ((pattern & 0xFF) === (pattern >>> 24))) {
543
+ repeat = REPEAT_CONFIRMED;
544
+ source_pattern_length = count_pattern(position + 4, high_limit, pattern) + 4;
545
+ } else {
546
+ repeat = REPEAT_NOT;
547
+ }
548
+ }
549
+
550
+ if (repeat === REPEAT_CONFIRMED && candidate_index >= lowest_match_index) {
551
+ const candidate_position = candidate_index - IDX_BASE;
552
+
553
+ if (data.getUint32(origin + candidate_position, true) === pattern) {
554
+ const forward_length = count_pattern(candidate_position + 4, high_limit, pattern) + 4;
555
+
556
+ const raw_back = reverse_count_pattern(candidate_position, 0, pattern);
557
+ const floor = candidate_index - raw_back > lowest_match_index
558
+ ? candidate_index - raw_back
559
+ : lowest_match_index;
560
+ const back_length = candidate_index - floor;
561
+
562
+ const segment_length = back_length + forward_length;
563
+
564
+ if (segment_length >= source_pattern_length && forward_length <= source_pattern_length) {
565
+ // the run here is long enough to hold the whole of the run at `position`,
566
+ // and this is where the two line up
567
+ match_index = candidate_index + forward_length - source_pattern_length;
568
+ } else {
569
+ // otherwise take the far end of the run, which is the longest match in it
570
+ match_index = floor;
571
+
572
+ if (look_back_length === 0) {
573
+ const max_length = segment_length < source_pattern_length
574
+ ? segment_length
575
+ : source_pattern_length;
576
+
577
+ if (longest < max_length) {
578
+ if (ip_index - match_index > MAX_DISTANCE) {
579
+ break;
580
+ }
581
+
582
+ longest = max_length;
583
+ offset = ip_index - match_index;
584
+ }
585
+
586
+ const distance_to_next_pattern = chain[match_index & 0xFFFF];
587
+
588
+ if (distance_to_next_pattern > match_index) {
589
+ break;
590
+ }
591
+
592
+ match_index -= distance_to_next_pattern;
593
+ }
594
+ }
595
+
596
+ continue;
597
+ }
598
+ }
599
+ }
600
+
601
+ match_index -= chain[(match_index + match_chain_pos) & 0xFFFF];
602
+ }
603
+
604
+ found_length = longest;
605
+ found_offset = offset;
606
+ found_back = s_back;
607
+ }
608
+
609
+ /**
610
+ * The same search, made from the position the match must start at — so it cannot reach behind, and
611
+ * {@link found_back} comes back zero.
612
+ *
613
+ * @param {number} position
614
+ * @param {number} high_limit
615
+ * @param {number} max_attempts
616
+ * @param {boolean} pattern_analysis
617
+ */
618
+ function insert_and_find_best_match(position, high_limit, max_attempts, pattern_analysis) {
619
+ insert_and_get_wider_match(position, position, high_limit, MIN_MATCH - 1, max_attempts, pattern_analysis, false);
620
+ }
621
+
622
+ /**
623
+ * Write one sequence: the token, the literal run in front of the match, the match's offset, and
624
+ * whatever its length spilled into.
625
+ *
626
+ * Where the fast encoder splits this — it knows a match's length only after it has written the
627
+ * token the length shares with the literal count — the high-compression parses hand a whole
628
+ * sequence over at once, so it is written as one unit and bounds-checked at the two points the
629
+ * output can actually run out.
630
+ *
631
+ * @param {number} anchor the first literal
632
+ * @param {number} position one past the last literal, and the first byte of the match
633
+ * @param {number} match_length
634
+ * @param {number} offset how far back the match reaches
635
+ * @throws {Error} if the sequence does not fit the space given
636
+ */
637
+ function encode_sequence(anchor, position, match_length, offset) {
638
+ const literal_length = position - anchor;
639
+ const literal_saturated = literal_length >= NIBBLE_MAX;
640
+ const coded_match_length = match_length - MIN_MATCH;
641
+ const match_saturated = coded_match_length >= NIBBLE_MAX;
642
+
643
+ let required = 1 + literal_length;
644
+
645
+ if (literal_saturated) {
646
+ required += continuation_size(literal_length - NIBBLE_MAX);
647
+ }
648
+
649
+ if (out + required > out_end) {
650
+ throw new Error(
651
+ `lz4 sequence of ${literal_length} literals needs ${required} bytes, and only ${out_end - out} are left`
652
+ );
653
+ }
654
+
655
+ const token = out++;
656
+
657
+ destination[token] = ((literal_saturated ? NIBBLE_MAX : literal_length) << 4)
658
+ | (match_saturated ? NIBBLE_MAX : coded_match_length);
659
+
660
+ if (literal_saturated) {
661
+ out = write_continuation(out, literal_length - NIBBLE_MAX);
662
+ }
663
+
664
+ if (literal_length >= BULK_COPY_LENGTH) {
665
+ destination.set(source.subarray(base + anchor, base + anchor + literal_length), out);
666
+ } else {
667
+ for (let i = 0; i < literal_length; i++) {
668
+ destination[out + i] = source[base + anchor + i];
669
+ }
670
+ }
671
+
672
+ out += literal_length;
673
+
674
+ let match_required = 2;
675
+
676
+ if (match_saturated) {
677
+ match_required += continuation_size(coded_match_length - NIBBLE_MAX);
678
+ }
679
+
680
+ if (out + match_required > out_end) {
681
+ throw new Error(
682
+ `lz4 match of ${match_length} needs ${match_required} bytes, and only ${out_end - out} are left`
683
+ );
684
+ }
685
+
686
+ destination[out++] = offset & 0xFF;
687
+ destination[out++] = (offset >> 8) & 0xFF;
688
+
689
+ if (match_saturated) {
690
+ out = write_continuation(out, coded_match_length - NIBBLE_MAX);
691
+ }
692
+ }
693
+
694
+ /**
695
+ * Close the block with its trailing literals, and no match behind them.
696
+ *
697
+ * @param {number} anchor
698
+ * @param {number} end
699
+ * @throws {Error} if they do not fit the space given
700
+ */
701
+ function write_last_literals(anchor, end) {
702
+ const literal_length = end - anchor;
703
+ const saturated = literal_length >= NIBBLE_MAX;
704
+
705
+ let required = 1 + literal_length;
706
+
707
+ if (saturated) {
708
+ required += continuation_size(literal_length - NIBBLE_MAX);
709
+ }
710
+
711
+ if (out + required > out_end) {
712
+ throw new Error(
713
+ `lz4 sequence of ${literal_length} literals needs ${required} bytes, and only ${out_end - out} are left`
714
+ );
715
+ }
716
+
717
+ destination[out++] = (saturated ? NIBBLE_MAX : literal_length) << 4;
718
+
719
+ if (saturated) {
720
+ out = write_continuation(out, literal_length - NIBBLE_MAX);
721
+ }
722
+
723
+ if (literal_length >= BULK_COPY_LENGTH) {
724
+ destination.set(source.subarray(base + anchor, base + anchor + literal_length), out);
725
+ } else {
726
+ for (let i = 0; i < literal_length; i++) {
727
+ destination[out + i] = source[base + anchor + i];
728
+ }
729
+ }
730
+
731
+ out += literal_length;
732
+ }
733
+
734
+ /**
735
+ * Levels 3 to 9: walk the chain, and parse lazily.
736
+ *
737
+ * The parse holds up to three matches at once — the one in hand, one starting two bytes before it
738
+ * ends, and one starting three bytes before *that* ends — and trims the earlier ones against the
739
+ * later so that a long match is never cut short by a short one that happened to be found first.
740
+ * Where they overlap it is the *bytes* that decide, not the order: a match is shortened only as far
741
+ * as the one behind it can pay for.
742
+ *
743
+ * @param {number} max_attempts
744
+ * @param {boolean} pattern_analysis
745
+ */
746
+ function compress_hash_chain(max_attempts, pattern_analysis) {
747
+ let ip = 0;
748
+ let anchor = 0;
749
+
750
+ let start0 = 0;
751
+ let start2 = 0;
752
+ let start3 = 0;
753
+
754
+ let m0_length = 0;
755
+ let m0_offset = 0;
756
+ let m1_length = 0;
757
+ let m1_offset = 0;
758
+ let m2_length = 0;
759
+ let m2_offset = 0;
760
+ let m3_length = 0;
761
+ let m3_offset = 0;
762
+
763
+ main: while (ip <= match_find_limit) {
764
+ insert_and_find_best_match(ip, match_limit, max_attempts, pattern_analysis);
765
+
766
+ m1_length = found_length;
767
+ m1_offset = found_offset;
768
+
769
+ if (m1_length < MIN_MATCH) {
770
+ ip++;
771
+ continue;
772
+ }
773
+
774
+ // kept in case the search below walks so far forward that the first match is worth going
775
+ // back for
776
+ start0 = ip;
777
+ m0_length = m1_length;
778
+ m0_offset = m1_offset;
779
+
780
+ search2: for (;;) {
781
+ if (ip + m1_length <= match_find_limit) {
782
+ start2 = ip + m1_length - 2;
783
+
784
+ insert_and_get_wider_match(start2, ip, match_limit, m1_length, max_attempts, pattern_analysis, false);
785
+
786
+ m2_length = found_length;
787
+ m2_offset = found_offset;
788
+ start2 += found_back;
789
+ } else {
790
+ m2_length = 0;
791
+ m2_offset = 0;
792
+ }
793
+
794
+ if (m2_length <= m1_length) {
795
+ encode_sequence(anchor, ip, m1_length, m1_offset);
796
+
797
+ ip += m1_length;
798
+ anchor = ip;
799
+
800
+ continue main;
801
+ }
802
+
803
+ if (start0 < ip && start2 < ip + m0_length) {
804
+ // squeezing this match between the one it replaced and the better one found past
805
+ // it costs more than going back and taking the first one
806
+ ip = start0;
807
+ m1_length = m0_length;
808
+ m1_offset = m0_offset;
809
+ }
810
+
811
+ if ((start2 - ip) < 3) {
812
+ // nothing would be left of the first match, so it goes
813
+ ip = start2;
814
+ m1_length = m2_length;
815
+ m1_offset = m2_offset;
816
+
817
+ continue search2;
818
+ }
819
+
820
+ search3: for (;;) {
821
+ if ((start2 - ip) < OPTIMAL_ML) {
822
+ let new_length = m1_length;
823
+
824
+ if (new_length > OPTIMAL_ML) {
825
+ new_length = OPTIMAL_ML;
826
+ }
827
+
828
+ if (ip + new_length > start2 + m2_length - MIN_MATCH) {
829
+ new_length = (start2 - ip) + m2_length - MIN_MATCH;
830
+ }
831
+
832
+ const correction = new_length - (start2 - ip);
833
+
834
+ if (correction > 0) {
835
+ start2 += correction;
836
+ m2_length -= correction;
837
+ }
838
+ }
839
+
840
+ if (start2 + m2_length <= match_find_limit) {
841
+ start3 = start2 + m2_length - 3;
842
+
843
+ insert_and_get_wider_match(start3, start2, match_limit, m2_length, max_attempts, pattern_analysis, false);
844
+
845
+ m3_length = found_length;
846
+ m3_offset = found_offset;
847
+ start3 += found_back;
848
+ } else {
849
+ m3_length = 0;
850
+ m3_offset = 0;
851
+ }
852
+
853
+ if (m3_length <= m2_length) {
854
+ // two matches, and nothing better behind them
855
+ if (start2 < ip + m1_length) {
856
+ m1_length = start2 - ip;
857
+ }
858
+
859
+ encode_sequence(anchor, ip, m1_length, m1_offset);
860
+
861
+ ip += m1_length;
862
+ anchor = ip;
863
+
864
+ ip = start2;
865
+
866
+ encode_sequence(anchor, ip, m2_length, m2_offset);
867
+
868
+ ip += m2_length;
869
+ anchor = ip;
870
+
871
+ continue main;
872
+ }
873
+
874
+ if (start3 < ip + m1_length + 3) {
875
+ // no room for the middle match
876
+ if (start3 >= ip + m1_length) {
877
+ if (start2 < ip + m1_length) {
878
+ const correction = (ip + m1_length) - start2;
879
+
880
+ start2 += correction;
881
+ m2_length -= correction;
882
+
883
+ if (m2_length < MIN_MATCH) {
884
+ start2 = start3;
885
+ m2_length = m3_length;
886
+ m2_offset = m3_offset;
887
+ }
888
+ }
889
+
890
+ encode_sequence(anchor, ip, m1_length, m1_offset);
891
+
892
+ ip += m1_length;
893
+ anchor = ip;
894
+
895
+ ip = start3;
896
+ m1_length = m3_length;
897
+ m1_offset = m3_offset;
898
+
899
+ start0 = start2;
900
+ m0_length = m2_length;
901
+ m0_offset = m2_offset;
902
+
903
+ continue search2;
904
+ }
905
+
906
+ start2 = start3;
907
+ m2_length = m3_length;
908
+ m2_offset = m3_offset;
909
+
910
+ continue search3;
911
+ }
912
+
913
+ // three matches in a row: write the first, and slide the other two down
914
+ if (start2 < ip + m1_length) {
915
+ if ((start2 - ip) < OPTIMAL_ML) {
916
+ if (m1_length > OPTIMAL_ML) {
917
+ m1_length = OPTIMAL_ML;
918
+ }
919
+
920
+ if (ip + m1_length > start2 + m2_length - MIN_MATCH) {
921
+ m1_length = (start2 - ip) + m2_length - MIN_MATCH;
922
+ }
923
+
924
+ const correction = m1_length - (start2 - ip);
925
+
926
+ if (correction > 0) {
927
+ start2 += correction;
928
+ m2_length -= correction;
929
+ }
930
+ } else {
931
+ m1_length = start2 - ip;
932
+ }
933
+ }
934
+
935
+ encode_sequence(anchor, ip, m1_length, m1_offset);
936
+
937
+ ip += m1_length;
938
+ anchor = ip;
939
+
940
+ ip = start2;
941
+ m1_length = m2_length;
942
+ m1_offset = m2_offset;
943
+
944
+ start2 = start3;
945
+ m2_length = m3_length;
946
+ m2_offset = m3_offset;
947
+
948
+ continue search3;
949
+ }
950
+ }
951
+ }
952
+
953
+ write_last_literals(anchor, input_size);
954
+ }
955
+
956
+ /**
957
+ * What a literal run costs in bytes: the literals, and the continuation its count spills into.
958
+ *
959
+ * @param {number} length
960
+ * @returns {number}
961
+ */
962
+ function literals_price(length) {
963
+ let price = length;
964
+
965
+ if (length >= NIBBLE_MAX) {
966
+ price += 1 + (((length - NIBBLE_MAX) / CONTINUATION_MAX) | 0);
967
+ }
968
+
969
+ return price;
970
+ }
971
+
972
+ /**
973
+ * What a whole sequence costs in bytes: a token, a sixteen-bit offset, the literals in front of it,
974
+ * and the continuation the match length spills into.
975
+ *
976
+ * @param {number} literal_length
977
+ * @param {number} match_length at least {@link MIN_MATCH}
978
+ * @returns {number}
979
+ */
980
+ function sequence_price(literal_length, match_length) {
981
+ let price = 1 + 2 + literals_price(literal_length);
982
+
983
+ if (match_length >= NIBBLE_MAX + MIN_MATCH) {
984
+ price += 1 + (((match_length - (NIBBLE_MAX + MIN_MATCH)) / CONTINUATION_MAX) | 0);
985
+ }
986
+
987
+ return price;
988
+ }
989
+
990
+ /**
991
+ * The search the optimal parse makes: from the position itself, so the match cannot reach behind,
992
+ * and reporting nothing at all unless it beats `min_length` outright.
993
+ *
994
+ * @param {number} position
995
+ * @param {number} high_limit
996
+ * @param {number} min_length the length to beat, which a caller that has priced everything up to
997
+ * some point sets to what is already covered rather than to the format's
998
+ * minimum — a shorter match there cannot change any price
999
+ * @param {number} max_attempts
1000
+ */
1001
+ function find_longer_match(position, high_limit, min_length, max_attempts) {
1002
+ insert_and_get_wider_match(position, position, high_limit, min_length, max_attempts, true, true);
1003
+
1004
+ if (found_length <= min_length) {
1005
+ found_length = 0;
1006
+ found_offset = 0;
1007
+ }
1008
+ }
1009
+
1010
+ /**
1011
+ * Recover the path through a solved window, write the sequences on it, and leave the parse just
1012
+ * past the last of them in {@link window_ip} and {@link window_anchor}.
1013
+ *
1014
+ * The table holds, at each position, the step that arrives there most cheaply — which is a step
1015
+ * read *backwards*. Walking those steps from the end of the path rewrites each entry as the step
1016
+ * that *leaves* it, and the same table is then the path read forwards.
1017
+ *
1018
+ * @param {number} ip where the window starts
1019
+ * @param {number} anchor the first literal not yet written
1020
+ * @param {number} from the position the last sequence on the path starts at
1021
+ * @param {number} last_match_pos one past the last position the path covers
1022
+ * @param {number} best_length the last sequence's match length
1023
+ * @param {number} best_offset the last sequence's offset
1024
+ */
1025
+ function encode_window(ip, anchor, from, last_match_pos, best_length, best_offset) {
1026
+ let candidate = from;
1027
+ let length = best_length;
1028
+ let offset = best_offset;
1029
+
1030
+ for (;;) {
1031
+ const next_length = opt_match_length[candidate];
1032
+ const next_offset = opt_offset[candidate];
1033
+
1034
+ opt_match_length[candidate] = length;
1035
+ opt_offset[candidate] = offset;
1036
+
1037
+ length = next_length;
1038
+ offset = next_offset;
1039
+
1040
+ if (next_length > candidate) {
1041
+ // the step that arrives here reaches back past the start of the window, so it is the
1042
+ // first sequence of the path and there is nothing behind it
1043
+ break;
1044
+ }
1045
+
1046
+ candidate -= next_length;
1047
+ }
1048
+
1049
+ let p = ip;
1050
+ let a = anchor;
1051
+ let r = 0;
1052
+
1053
+ while (r < last_match_pos) {
1054
+ const match_length = opt_match_length[r];
1055
+
1056
+ // a literal, and possibly several: the path steps over them one at a time
1057
+ if (match_length === 1) {
1058
+ p++;
1059
+ r++;
1060
+ continue;
1061
+ }
1062
+
1063
+ encode_sequence(a, p, match_length, opt_offset[r]);
1064
+
1065
+ p += match_length;
1066
+ a = p;
1067
+ r += match_length;
1068
+ }
1069
+
1070
+ window_ip = p;
1071
+ window_anchor = a;
1072
+ }
1073
+
1074
+ /**
1075
+ * Levels 10 to 12: solve the parse rather than guess it.
1076
+ *
1077
+ * Over a window of the block, the cheapest coding of the first `n` bytes is built from the cheapest
1078
+ * coding of every shorter prefix, taking a match found at each position as one edge and a literal
1079
+ * as another. The price is in *bytes*, exactly — a token, a two-byte offset, and the continuation
1080
+ * bytes each saturated length field spills into — so this is a genuine shortest path and not a
1081
+ * heuristic with a cost model bolted on. The window is walked back from its end to recover the
1082
+ * path, and the sequences on it are written in order.
1083
+ *
1084
+ * What stops it going quadratic is `sufficient_length`: a match at least that long is taken and
1085
+ * encoded on the spot, with no path solved through it at all. Level 12 raises that to the window
1086
+ * itself and searches every position rather than only those whose price rises, which is what the
1087
+ * last two ten-thousandths of a percent cost — and why an encoder that drops the early exit
1088
+ * entirely, which was measured, spends a hundred times level 12's budget to find nothing.
1089
+ *
1090
+ * @param {number} max_attempts
1091
+ * @param {number} sufficient_length
1092
+ * @param {boolean} full_update
1093
+ */
1094
+ function compress_optimal(max_attempts, sufficient_length, full_update) {
1095
+ // the window is what the tables hold, so nothing may be priced past it
1096
+ const enough = sufficient_length >= OPT_NUM ? OPT_NUM - 1 : sufficient_length;
1097
+
1098
+ let ip = 0;
1099
+ let anchor = 0;
1100
+
1101
+ while (ip <= match_find_limit) {
1102
+ const literal_run = ip - anchor;
1103
+
1104
+ find_longer_match(ip, match_limit, MIN_MATCH - 1, max_attempts);
1105
+
1106
+ if (found_length === 0) {
1107
+ ip++;
1108
+ continue;
1109
+ }
1110
+
1111
+ const first_length = found_length;
1112
+ const first_offset = found_offset;
1113
+
1114
+ if (first_length > enough) {
1115
+ // good enough to take on sight
1116
+ encode_sequence(anchor, ip, first_length, first_offset);
1117
+
1118
+ ip += first_length;
1119
+ anchor = ip;
1120
+
1121
+ continue;
1122
+ }
1123
+
1124
+ // the first few positions, as literals carrying on from the run already open
1125
+ for (let r = 0; r < MIN_MATCH; r++) {
1126
+ opt_match_length[r] = 1;
1127
+ opt_offset[r] = 0;
1128
+ opt_literal_length[r] = literal_run + r;
1129
+ opt_price[r] = literals_price(literal_run + r);
1130
+ }
1131
+
1132
+ // and every length the first match can be cut to
1133
+ for (let length = MIN_MATCH; length <= first_length; length++) {
1134
+ opt_match_length[length] = length;
1135
+ opt_offset[length] = first_offset;
1136
+ opt_literal_length[length] = literal_run;
1137
+ opt_price[length] = sequence_price(literal_run, length);
1138
+ }
1139
+
1140
+ let last_match_pos = first_length;
1141
+ let best_length = 0;
1142
+ let best_offset = 0;
1143
+ let from = 0;
1144
+ let solved = false;
1145
+
1146
+ for (let add = 1; add <= TRAILING_LITERALS; add++) {
1147
+ opt_match_length[last_match_pos + add] = 1;
1148
+ opt_offset[last_match_pos + add] = 0;
1149
+ opt_literal_length[last_match_pos + add] = add;
1150
+ opt_price[last_match_pos + add] = opt_price[last_match_pos] + literals_price(add);
1151
+ }
1152
+
1153
+ for (let cur = 1; cur < last_match_pos; cur++) {
1154
+ const position = ip + cur;
1155
+
1156
+ if (position > match_find_limit) {
1157
+ break;
1158
+ }
1159
+
1160
+ if (full_update) {
1161
+ // A position whose successor already codes at least as cheaply is one nothing can
1162
+ // be gained at — unless the price climbs sharply just past it, where even the
1163
+ // shortest sequence there is would pay for itself.
1164
+ if (
1165
+ opt_price[cur + 1] <= opt_price[cur]
1166
+ && opt_price[cur + MIN_MATCH] < opt_price[cur] + 3
1167
+ ) {
1168
+ continue;
1169
+ }
1170
+ } else if (opt_price[cur + 1] <= opt_price[cur]) {
1171
+ continue;
1172
+ }
1173
+
1174
+ // Below the top level, only a match reaching past everything already priced is compared
1175
+ // at all. It misses a few bytes, and it is most of what levels 10 and 11 cost less than
1176
+ // 12 for.
1177
+ find_longer_match(
1178
+ position,
1179
+ match_limit,
1180
+ full_update ? MIN_MATCH - 1 : last_match_pos - cur,
1181
+ max_attempts
1182
+ );
1183
+
1184
+ if (found_length === 0) {
1185
+ continue;
1186
+ }
1187
+
1188
+ const new_length = found_length;
1189
+ const new_offset = found_offset;
1190
+
1191
+ if (new_length > enough || new_length + cur >= OPT_NUM) {
1192
+ // good enough, or past what the tables hold: stop solving and take it
1193
+ best_length = new_length;
1194
+ best_offset = new_offset;
1195
+ from = cur;
1196
+ last_match_pos = cur + 1;
1197
+ solved = true;
1198
+
1199
+ break;
1200
+ }
1201
+
1202
+ // literals in front of a match here, which the prices past it may not have counted
1203
+ const base_literals = opt_literal_length[cur];
1204
+
1205
+ for (let extra = 1; extra < MIN_MATCH; extra++) {
1206
+ const price = opt_price[cur] - literals_price(base_literals) + literals_price(base_literals + extra);
1207
+ const p = cur + extra;
1208
+
1209
+ if (price < opt_price[p]) {
1210
+ opt_match_length[p] = 1;
1211
+ opt_offset[p] = 0;
1212
+ opt_literal_length[p] = base_literals + extra;
1213
+ opt_price[p] = price;
1214
+ }
1215
+ }
1216
+
1217
+ // and the match itself, at every length it can be cut to
1218
+ for (let length = MIN_MATCH; length <= new_length; length++) {
1219
+ const p = cur + length;
1220
+
1221
+ let literals;
1222
+ let price;
1223
+
1224
+ if (opt_match_length[cur] === 1) {
1225
+ // arriving here on literals, so they join the run in front of this match rather
1226
+ // than being paid for twice
1227
+ literals = opt_literal_length[cur];
1228
+ price = (cur > literals ? opt_price[cur - literals] : 0) + sequence_price(literals, length);
1229
+ } else {
1230
+ literals = 0;
1231
+ price = opt_price[cur] + sequence_price(0, length);
1232
+ }
1233
+
1234
+ if (p > last_match_pos + TRAILING_LITERALS || price <= opt_price[p]) {
1235
+ if (length === new_length && last_match_pos < p) {
1236
+ last_match_pos = p;
1237
+ }
1238
+
1239
+ opt_match_length[p] = length;
1240
+ opt_offset[p] = new_offset;
1241
+ opt_literal_length[p] = literals;
1242
+ opt_price[p] = price;
1243
+ }
1244
+ }
1245
+
1246
+ for (let add = 1; add <= TRAILING_LITERALS; add++) {
1247
+ opt_match_length[last_match_pos + add] = 1;
1248
+ opt_offset[last_match_pos + add] = 0;
1249
+ opt_literal_length[last_match_pos + add] = add;
1250
+ opt_price[last_match_pos + add] = opt_price[last_match_pos] + literals_price(add);
1251
+ }
1252
+ }
1253
+
1254
+ if (solved === false) {
1255
+ best_length = opt_match_length[last_match_pos];
1256
+ best_offset = opt_offset[last_match_pos];
1257
+ from = last_match_pos - best_length;
1258
+ }
1259
+
1260
+ encode_window(ip, anchor, from, last_match_pos, best_length, best_offset);
1261
+
1262
+ ip = window_ip;
1263
+ anchor = window_anchor;
1264
+ }
1265
+
1266
+ write_last_literals(anchor, input_size);
1267
+ }
1268
+
1269
+ /**
1270
+ * Encode one LZ4 block at a compression level.
1271
+ *
1272
+ * A port of the reference implementation's `lz4hc`, without its dictionary and streaming support:
1273
+ * meep compresses one self-contained block with no history, so the external dictionary, the
1274
+ * dictionary context, the pattern rotation across a dictionary boundary, `favorDecSpeed` and
1275
+ * `fillOutput` all drop, and with them about half the reference file. What is left is byte for byte
1276
+ * what `LZ4_compress_HC` writes at the same level.
1277
+ *
1278
+ * **The format does not change.** This emits ordinary LZ4 blocks — the same tokens, the same
1279
+ * sixteen-bit offsets, the same restrictions on where a match may start — so a block written here
1280
+ * at any level decodes under {@link lz4_decompress_block} and under every other reader that exists.
1281
+ * What changes is only how hard the encoder looked: where {@link lz4_compress_block} takes the
1282
+ * first match a hash slot offers, this walks a chain of every position carrying the same hash and
1283
+ * parses the matches it finds against each other.
1284
+ *
1285
+ * What that is worth depends entirely on the payload. On text it is 20% or more; on quantised,
1286
+ * densely packed bytes — geometry pages, say — it is about 2.5%, and almost all of that is already
1287
+ * there at level 3. What it costs is an order of magnitude of throughput: 400 MB/s becomes 50.
1288
+ *
1289
+ * `destination` and `source` must not overlap.
1290
+ *
1291
+ * @see https://github.com/lz4/lz4/blob/dev/lib/lz4hc.c — `LZ4HC_compress_hashChain` and `LZ4HC_compress_optimal`
1292
+ *
1293
+ * @param {Uint8Array} p_destination
1294
+ * @param {number} destination_offset where this block's output starts
1295
+ * @param {number} destination_end one past the last byte the block may write; give it
1296
+ * {@link lz4_compress_bound} bytes and it cannot run out
1297
+ * @param {Uint8Array} p_source
1298
+ * @param {number} source_offset the first byte of input, and the floor a match may reach back to
1299
+ * @param {number} source_end one past the last byte of input
1300
+ * @param {number} level one of {@link LZ4_LEVEL}'s high-compression levels
1301
+ * @returns {number} the offset just past the last byte written
1302
+ * @throws {Error} if the block does not fit the space given for it
1303
+ *
1304
+ * @author Alex Goldring
1305
+ * @copyright Company Named Limited (c) 2026
1306
+ */
1307
+ export function lz4_compress_block_hc(p_destination, destination_offset, destination_end, p_source, source_offset, source_end, level) {
1308
+ assert.isNonNegativeInteger(destination_offset, 'destination_offset');
1309
+ assert.lessThanOrEqual(destination_end, p_destination.length, 'destination_end');
1310
+ assert.lessThanOrEqual(destination_offset, destination_end, 'destination_offset');
1311
+ assert.isNonNegativeInteger(source_offset, 'source_offset');
1312
+ assert.lessThanOrEqual(source_end, p_source.length, 'source_end');
1313
+ assert.lessThanOrEqual(source_offset, source_end, 'source_offset');
1314
+ // positions are held in an Int32Array, and a conforming block carries no more than this anyway
1315
+ assert.lessThanOrEqual(source_end, LZ4_MAX_INPUT_SIZE, 'source_end');
1316
+ assert.isInteger(level, 'level');
1317
+ // a level outside the ladder is a caller asking for something this encoder does not have, and
1318
+ // clamping it would silently give them a different encoder than the one they named
1319
+ assert.ok(
1320
+ level >= LZ4_LEVEL.HC_MIN && level <= LZ4_LEVEL.HC_MAX,
1321
+ `level must be between ${LZ4_LEVEL.HC_MIN} and ${LZ4_LEVEL.HC_MAX}, was ${level}`
1322
+ );
1323
+
1324
+ // Read *here*, not inside the length check below, so that a level this encoder does not have
1325
+ // fails the same way whatever the input size. The assert above is what refuses one, and a
1326
+ // consumer that strips asserts — which is how meep ships — has only this: without it, level 2
1327
+ // would throw on a page and quietly produce a valid block on anything under thirteen bytes.
1328
+ const parameters = LEVEL_TABLE[level];
1329
+ const attempts = parameters.attempts;
1330
+ const optimal = parameters.optimal;
1331
+
1332
+ destination = p_destination;
1333
+ source = p_source;
1334
+ base = source_offset;
1335
+ input_size = source_end - source_offset;
1336
+
1337
+ out = destination_offset;
1338
+ out_end = destination_end;
1339
+
1340
+ try {
1341
+ if (input_size >= MIN_LENGTH) {
1342
+ if (hash_table === null) {
1343
+ hash_table = new Int32Array(1 << HASH_LOG);
1344
+ chain_table = new Uint16Array(1 << 16);
1345
+ }
1346
+
1347
+ view = new DataView(source.buffer, source.byteOffset, source.byteLength);
1348
+
1349
+ hash_table.fill(0);
1350
+ chain_table.fill(0xFFFF);
1351
+
1352
+ next_to_update = IDX_BASE;
1353
+ match_find_limit = input_size - MATCH_FIND_LIMIT;
1354
+ match_limit = input_size - LAST_LITERALS;
1355
+
1356
+ if (optimal) {
1357
+ if (opt_price === null) {
1358
+ opt_price = new Int32Array(OPT_TABLE_SIZE);
1359
+ opt_offset = new Int32Array(OPT_TABLE_SIZE);
1360
+ opt_match_length = new Int32Array(OPT_TABLE_SIZE);
1361
+ opt_literal_length = new Int32Array(OPT_TABLE_SIZE);
1362
+ }
1363
+
1364
+ compress_optimal(attempts, parameters.sufficient_length, parameters.full_update);
1365
+ } else {
1366
+ compress_hash_chain(attempts, attempts > PATTERN_ANALYSIS_THRESHOLD);
1367
+ }
1368
+ } else {
1369
+ write_last_literals(0, input_size);
1370
+ }
1371
+ } finally {
1372
+ // However the call ends. A destination too small throws, and without this the encoder would
1373
+ // go on holding that call's buffers — the caller's whole input — until the next successful
1374
+ // one. A caller that catches the throw and carries on is exactly the caller that would
1375
+ // notice: it is the one still running.
1376
+ source = null;
1377
+ view = null;
1378
+ destination = null;
1379
+ }
1380
+
1381
+ return out;
1382
+ }