@woosh/meep-engine 3.60.4 → 3.60.6

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 (419) hide show
  1. package/CHANGELOG.md +2621 -0
  2. package/CONTRIBUTING.md +7 -0
  3. package/MIGRATION_2_TO_3.md +136 -0
  4. package/SECURITY.md +4 -0
  5. package/editor/EditorCameraNavigation.d.ts +2 -1
  6. package/editor/EditorCameraNavigation.d.ts.map +1 -1
  7. package/editor/EditorCameraNavigation.js +51 -57
  8. package/package.json +6 -2
  9. package/src/engine/EngineHarness.d.ts +3 -9
  10. package/src/engine/EngineHarness.d.ts.map +1 -1
  11. package/src/engine/EngineHarness.js +6 -8
  12. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts +9 -6
  13. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts.map +1 -1
  14. package/src/engine/graphics/camera/makeOrbitalCameraController.js +44 -71
  15. package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts +64 -0
  16. package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts.map +1 -0
  17. package/src/engine/graphics/camera/orbital/MomentumAxis.js +187 -0
  18. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts +86 -0
  19. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts.map +1 -0
  20. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.js +319 -0
  21. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts +34 -10
  22. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts.map +1 -1
  23. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +116 -30
  24. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts +1 -7
  25. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
  26. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +1 -37
  27. package/src/engine/graphics/ecs/water/Water.d.ts +8 -0
  28. package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
  29. package/src/engine/graphics/ecs/water/Water.js +8 -0
  30. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  31. package/src/engine/graphics3/WaterSystem.js +731 -734
  32. package/src/engine/graphics3/decal/DecalTileShader.d.ts +61 -0
  33. package/src/engine/graphics3/decal/DecalTileShader.d.ts.map +1 -0
  34. package/src/engine/graphics3/decal/DecalTileShader.js +138 -0
  35. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts +10 -4
  36. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts.map +1 -1
  37. package/src/engine/graphics3/decal/GPUDecalRenderer.js +149 -44
  38. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts +25 -0
  39. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts.map +1 -0
  40. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.js +55 -0
  41. package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts +22 -0
  42. package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts.map +1 -0
  43. package/src/engine/graphics3/decal/graph_gather_decal_tiles.js +70 -0
  44. package/src/engine/graphics3/decal/shader_decal_composite.d.ts +7 -5
  45. package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
  46. package/src/engine/graphics3/decal/shader_decal_composite.js +48 -44
  47. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts +12 -0
  48. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts.map +1 -0
  49. package/src/engine/graphics3/decal/shader_decal_tile_copy.js +78 -0
  50. package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts +11 -0
  51. package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts.map +1 -0
  52. package/src/engine/graphics3/decal/shader_gather_decal_tiles.js +80 -0
  53. package/src/engine/input/devices/mouse/MouseEventButtons.d.ts +13 -0
  54. package/src/engine/input/devices/mouse/MouseEventButtons.d.ts.map +1 -0
  55. package/src/engine/input/devices/mouse/MouseEventButtons.js +23 -0
  56. package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts +4 -0
  57. package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts.map +1 -1
  58. package/src/engine/input/ecs/controllers/KeyboardCameraController.js +9 -45
  59. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  60. package/src/shade/playground/water_refraction/README.md +5 -6
  61. package/src/shade/playground/water_refraction/index.html +1 -1
  62. package/src/shade/renderer/Renderer.d.ts +11 -0
  63. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  64. package/src/shade/renderer/Renderer.js +50 -6
  65. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +40 -80
  66. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
  67. package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +197 -147
  68. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  69. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  70. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  71. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  72. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  73. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +28 -45
  74. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  75. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +111 -119
  76. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +22 -3
  77. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
  78. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +84 -3
  79. package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts +19 -0
  80. package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts.map +1 -0
  81. package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.js +25 -0
  82. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts +31 -0
  83. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts.map +1 -0
  84. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.js +343 -0
  85. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts +15 -0
  86. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts.map +1 -1
  87. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.js +38 -0
  88. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts +17 -0
  89. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts.map +1 -0
  90. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.js +29 -0
  91. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -1
  92. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +7 -13
  93. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts +28 -59
  94. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts.map +1 -1
  95. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.js +84 -789
  96. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  97. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +1 -8
  98. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +7 -6
  99. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
  100. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +12 -11
  101. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.d.ts +2 -2
  102. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.js +2 -2
  103. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +1 -4
  104. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  105. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +1 -10
  106. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts +5 -15
  107. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts.map +1 -1
  108. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.js +6 -34
  109. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
  110. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -7
  111. package/src/shade/renderer/hiz/HZBFootprint.d.ts +14 -0
  112. package/src/shade/renderer/hiz/HZBFootprint.d.ts.map +1 -0
  113. package/src/shade/renderer/hiz/HZBFootprint.js +16 -0
  114. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts +10 -0
  115. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts.map +1 -0
  116. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.js +11 -0
  117. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts +10 -0
  118. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts.map +1 -0
  119. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.js +11 -0
  120. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +0 -7
  121. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  122. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +405 -350
  123. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts +12 -0
  124. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts.map +1 -0
  125. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.js +27 -0
  126. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts +12 -0
  127. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts.map +1 -0
  128. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.js +27 -0
  129. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts +14 -0
  130. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts.map +1 -0
  131. package/src/shade/renderer/hiz/hzb_footprint_texels.js +34 -0
  132. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +0 -5
  133. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
  134. package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +37 -57
  135. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts +11 -0
  136. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts.map +1 -0
  137. package/src/shade/renderer/hiz/{shader_hzb_reduce_mip0_clip.js → shader_hzb_reduce_mip0.js} +120 -111
  138. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  139. package/src/shade/renderer/material/GPUMaterialManager.d.ts +11 -1
  140. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  141. package/src/shade/renderer/material/GPUMaterialManager.js +16 -8
  142. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +12 -12
  143. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  144. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +17 -118
  145. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +7 -3
  146. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
  147. package/src/shade/renderer/postprocess/gtao/GTAO.js +26 -13
  148. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts +14 -0
  149. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
  150. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +196 -68
  151. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
  152. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +42 -28
  153. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.d.ts.map +1 -1
  154. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +429 -414
  155. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts +16 -0
  156. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts.map +1 -0
  157. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.js +156 -0
  158. package/src/shade/renderer/scene/GPUSceneContext.d.ts +2 -2
  159. package/src/shade/renderer/scene/GPUSceneContext.js +5 -5
  160. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  161. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +7 -2
  162. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +0 -6
  163. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  164. package/src/shade/renderer/water/GPUWaterRenderer.js +12 -17
  165. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts +35 -0
  166. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts.map +1 -0
  167. package/src/shade/renderer/water/medium/graph_water_meshlets.js +89 -0
  168. package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
  169. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  170. package/src/shade/renderer/water/shader_water.js +22 -13
  171. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
  172. package/src/shade/renderer/water/shader_water_avboit.js +7 -7
  173. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +1 -1
  174. package/src/shade/renderer/water/surface/graph_water_tessellation.js +1 -1
  175. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +9 -1
  176. package/editor/REWORK_PLAN_2026_08.md +0 -692
  177. package/src/CODEC_LAYOUT_VERIFICATION.md +0 -419
  178. package/src/CORRECTNESS_HUNT_2026_07_24.md +0 -142
  179. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +0 -738
  180. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +0 -79
  181. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +0 -93
  182. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +0 -80
  183. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +0 -164
  184. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +0 -69
  185. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +0 -110
  186. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +0 -129
  187. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +0 -72
  188. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +0 -74
  189. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +0 -169
  190. package/src/DUPLICATION_AUDIT_2026_08_07.md +0 -254
  191. package/src/DUPLICATION_AUDIT_REMEDIATION.md +0 -250
  192. package/src/DUPLICATION_REVIEW_2026_10_24.md +0 -1072
  193. package/src/REVIEW_2026_08_06/adv-core.md +0 -511
  194. package/src/REVIEW_2026_08_06/adv-gfx.md +0 -480
  195. package/src/REVIEW_2026_08_06/adv-view.md +0 -446
  196. package/src/REVIEW_2026_08_06/api-core.md +0 -296
  197. package/src/REVIEW_2026_08_06/api-engine.md +0 -195
  198. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +0 -507
  199. package/src/REVIEW_2026_08_06/core-binary.md +0 -348
  200. package/src/REVIEW_2026_08_06/core-collection.md +0 -376
  201. package/src/REVIEW_2026_08_06/core-math.md +0 -221
  202. package/src/REVIEW_2026_08_06/core-process.md +0 -266
  203. package/src/REVIEW_2026_08_06/core-science.md +0 -223
  204. package/src/REVIEW_2026_08_06/engine-ai-nav.md +0 -279
  205. package/src/REVIEW_2026_08_06/engine-animation.md +0 -167
  206. package/src/REVIEW_2026_08_06/engine-asset.md +0 -166
  207. package/src/REVIEW_2026_08_06/engine-ecs.md +0 -920
  208. package/src/REVIEW_2026_08_06/engine-input.md +0 -197
  209. package/src/REVIEW_2026_08_06/engine-misc.md +0 -248
  210. package/src/REVIEW_2026_08_06/engine-terrain.md +0 -52
  211. package/src/REVIEW_2026_08_06/generation.md +0 -176
  212. package/src/REVIEW_2026_08_06/geom-2d.md +0 -434
  213. package/src/REVIEW_2026_08_06/geom-3d.md +0 -117
  214. package/src/REVIEW_2026_08_06/gfx-gi.md +0 -143
  215. package/src/REVIEW_2026_08_06/gfx-render.md +0 -740
  216. package/src/REVIEW_2026_08_06/gfx-texture.md +0 -690
  217. package/src/REVIEW_2026_08_06/prior-criticals.md +0 -49
  218. package/src/REVIEW_2026_08_06/prior-highs.md +0 -138
  219. package/src/REVIEW_2026_08_06/test-gaps.md +0 -364
  220. package/src/REVIEW_2026_08_06/view.md +0 -667
  221. package/src/REVIEW_2026_08_06.md +0 -610
  222. package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +0 -542
  223. package/src/core/bvh2/bvh3/NOTES.md +0 -2
  224. package/src/core/bvh8/NOTES.md +0 -63
  225. package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +0 -243
  226. package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +0 -417
  227. package/src/core/color/COLOR_REVIEW_2026_08_28.md +0 -779
  228. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +0 -433
  229. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +0 -486
  230. package/src/core/geom/3d/atlas/BFF_PLAN.md +0 -174
  231. package/src/core/geom/3d/atlas/REVIEW_02.md +0 -392
  232. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +0 -146
  233. package/src/core/geom/3d/gjk/NOTES.md +0 -8
  234. package/src/core/geom/3d/topology/TOPO_MESH_RETIREMENT_PLAN.md +0 -135
  235. package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +0 -381
  236. package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +0 -684
  237. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +0 -554
  238. package/src/core/model/reactive/evaluation/PREDICATE_APPLICABILITY_PROPOSAL.md +0 -446
  239. package/src/engine/REVIEW_2026_07_03.md +0 -211
  240. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +0 -815
  241. package/src/engine/control/first-person/DESIGN.md +0 -658
  242. package/src/engine/control/first-person/DESIGN_COLLISION.md +0 -382
  243. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +0 -563
  244. package/src/engine/control/first-person/TODO.md +0 -160
  245. package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +0 -531
  246. package/src/engine/ecs/HIERARCHY_REVIEW.md +0 -717
  247. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +0 -262
  248. package/src/engine/ecs/ik/PLAN.md +0 -179
  249. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +0 -272
  250. package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +0 -199
  251. package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +0 -521
  252. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +0 -463
  253. package/src/engine/graphics/ecs/camera/pp/NOTES.md +0 -48
  254. package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +0 -659
  255. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +0 -235
  256. package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +0 -490
  257. package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +0 -466
  258. package/src/engine/input/GAMEPAD_API_PROPOSAL.md +0 -717
  259. package/src/engine/input/ecs/ism/ISM_DESIGN.md +0 -1523
  260. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +0 -627
  261. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +0 -661
  262. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +0 -229
  263. package/src/engine/network/CONGESTION_CONTROL.md +0 -87
  264. package/src/engine/network/MESSAGE_TRANSPORT.md +0 -364
  265. package/src/engine/network/SMOOTHNESS_DESIGN.md +0 -400
  266. package/src/engine/physics/cloth/MEASUREMENTS.md +0 -5304
  267. package/src/engine/physics/cloth/PLAN.md +0 -3056
  268. package/src/engine/physics/fluid/GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md +0 -1382
  269. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +0 -243
  270. package/src/engine/physics/fluid/solver/NOTES.md +0 -46
  271. package/src/engine/sound/SOUND_EMITTER_RETIREMENT_PLAN.md +0 -536
  272. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +0 -432
  273. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +0 -250
  274. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +0 -322
  275. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +0 -261
  276. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +0 -121
  277. package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +0 -999
  278. package/src/format/image/avif/DECISIONS.md +0 -545
  279. package/src/format/image/jpeg/NOTES.md +0 -3
  280. package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +0 -814
  281. package/src/generation/COORDINATES.md +0 -54
  282. package/src/generation/REVIEW_01_ACTION_PLAN.md +0 -628
  283. package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +0 -220
  284. package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +0 -172
  285. package/src/generation/foliage/CROWN_FULLNESS_BRIEF_2026_10_26.md +0 -86
  286. package/src/generation/foliage/FOLIAGE_DESIGN.md +0 -547
  287. package/src/generation/foliage/FOLIAGE_PLAN.md +0 -198
  288. package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +0 -158
  289. package/src/generation/foliage/LOD_REWORK_BRIEF_2026_10_27.md +0 -521
  290. package/src/generation/foliage/REAL_TIME_TREES_BRIEF_2026_10_27.md +0 -86
  291. package/src/generation/foliage/SELF_ORGANIZING_FRAMEWORK_BRIEF_2026_10_26.md +0 -347
  292. package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +0 -248
  293. package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +0 -694
  294. package/src/generation/foliage/TWO_FRONT_ENDS_BRIEF_2026_10_28.md +0 -552
  295. package/src/shade/RENDERER_CONTRACT.md +0 -697
  296. package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +0 -263
  297. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +0 -569
  298. package/src/shade/device/PASS_ENCODER_PROPOSAL_2026_08_31.md +0 -755
  299. package/src/shade/device/mock/EXTENSION_PROPOSAL_2026_08_22.md +0 -458
  300. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +0 -255
  301. package/src/shade/device/mock/REVIEW_2026_08_22.md +0 -527
  302. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +0 -894
  303. package/src/shade/device/timing/profile/SGPT_FORMAT.md +0 -345
  304. package/src/shade/renderer/NOTES.md +0 -78
  305. package/src/shade/renderer/PLAN.md +0 -8
  306. package/src/shade/renderer/animation/ADDITIVE_BLENDING_PLAN.md +0 -145
  307. package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +0 -357
  308. package/src/shade/renderer/atmosphere/NOTES.md +0 -6
  309. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts +0 -76
  310. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts.map +0 -1
  311. package/src/shade/renderer/camera/orbital/MomentumAxis.js +0 -164
  312. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts +0 -45
  313. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts.map +0 -1
  314. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.js +0 -66
  315. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +0 -98
  316. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +0 -1
  317. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +0 -184
  318. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +0 -175
  319. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +0 -1
  320. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +0 -411
  321. package/src/shade/renderer/camera/orbital/README.md +0 -81
  322. package/src/shade/renderer/camera/orbital/index.d.ts +0 -6
  323. package/src/shade/renderer/camera/orbital/index.d.ts.map +0 -1
  324. package/src/shade/renderer/camera/orbital/index.js +0 -9
  325. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +0 -964
  326. package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +0 -361
  327. package/src/shade/renderer/geometry/bvh/BLAS_REFIT_BATCH_PLAN.md +0 -619
  328. package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +0 -674
  329. package/src/shade/renderer/geometry/sdf/NOTES.md +0 -6
  330. package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +0 -540
  331. package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +0 -305
  332. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +0 -913
  333. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +0 -787
  334. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +0 -481
  335. package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +0 -301
  336. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +0 -448
  337. package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +0 -385
  338. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +0 -656
  339. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +0 -308
  340. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +0 -657
  341. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +0 -551
  342. package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +0 -529
  343. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts +0 -47
  344. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts.map +0 -1
  345. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.js +0 -80
  346. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts +0 -16
  347. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts.map +0 -1
  348. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.js +0 -67
  349. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts +0 -17
  350. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts.map +0 -1
  351. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.js +0 -145
  352. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts +0 -12
  353. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts.map +0 -1
  354. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.js +0 -105
  355. package/src/shade/renderer/global_illumination/lpv/NOTES.md +0 -3
  356. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +0 -4306
  357. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +0 -410
  358. package/src/shade/renderer/global_illumination/sharc/NOTES.md +0 -8
  359. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +0 -1369
  360. package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +0 -202
  361. package/src/shade/renderer/gpu_primitive/indirect/NOTES.md +0 -94
  362. package/src/shade/renderer/hiz/NOTES.md +0 -3
  363. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +0 -8
  364. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +0 -1
  365. package/src/shade/renderer/light/cluster/NOTES.md +0 -11
  366. package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +0 -524
  367. package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +0 -1101
  368. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts +0 -24
  369. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts.map +0 -1
  370. package/src/shade/renderer/material/resident/shader_resident_texture_means.js +0 -120
  371. package/src/shade/renderer/particles/DESIGN.md +0 -1011
  372. package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +0 -280
  373. package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +0 -453
  374. package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +0 -520
  375. package/src/shade/renderer/postprocess/bloom/NOTES.md +0 -6
  376. package/src/shade/renderer/postprocess/fsr/NOTES.md +0 -4
  377. package/src/shade/renderer/postprocess/gtao/NOTES.md +0 -6
  378. package/src/shade/renderer/postprocess/nss/NOTES.md +0 -332
  379. package/src/shade/renderer/postprocess/perspective/NOTES.md +0 -3
  380. package/src/shade/renderer/postprocess/sscs/NOTES.md +0 -5
  381. package/src/shade/renderer/postprocess/ssr/NOTES.md +0 -14
  382. package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +0 -286
  383. package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -39
  384. package/src/shade/renderer/postprocess/taa/NOTES.md +0 -11
  385. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +0 -78
  386. package/src/shade/renderer/postprocess/vrs/NOTES.md +0 -4
  387. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +0 -1218
  388. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +0 -600
  389. package/src/shade/renderer/restir/reservoir/NOTES.md +0 -6
  390. package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +0 -6
  391. package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +0 -391
  392. package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +0 -153
  393. package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +0 -322
  394. package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +0 -247
  395. package/src/shade/renderer/shader/chunk/fsr/NOTES.md +0 -3
  396. package/src/shade/renderer/shader/chunk/fsr/spd/NOTES.md +0 -4
  397. package/src/shade/renderer/shader/chunk/geometry/ray/NOTES.md +0 -4
  398. package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +0 -159
  399. package/src/shade/renderer/shader/chunk/mboit/NOTES.md +0 -38
  400. package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/NOTES.md +0 -3
  401. package/src/shade/renderer/shader/chunk/spherical_harmonics/sampling/NOTES.md +0 -8
  402. package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +0 -927
  403. package/src/shade/renderer/shadow/contact/FINDINGS.md +0 -413
  404. package/src/shade/renderer/shadow/map/csm/NOTES.md +0 -5
  405. package/src/shade/renderer/shadow/ray/NOTES.md +0 -31
  406. package/src/shade/renderer/shadow/sdf/NOTES.md +0 -9
  407. package/src/shade/renderer/shadow/sdf/brixelizer/NOTES.md +0 -5
  408. package/src/shade/renderer/shadow/sdf/cascade/NOTES.md +0 -9
  409. package/src/shade/renderer/shadow/vsm/NOTES.md +0 -12
  410. package/src/shade/renderer/shadow/vsm/PLAN.md +0 -442
  411. package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +0 -43
  412. package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +0 -2444
  413. package/src/shade/renderer/texture/mipmaps/NOTES.md +0 -3
  414. package/src/shade/renderer/volumetrics/NOTES.md +0 -1329
  415. package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +0 -336
  416. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +0 -23
  417. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +0 -1
  418. package/src/shade/renderer/water/medium/GPUWaterBodies.js +0 -75
  419. package/src/shade/wgsl/NOTES.md +0 -4
@@ -1,542 +0,0 @@
1
- # LZ4_HIGH_COMPRESSION_PLAN — `src/core/binary/lz4`
2
-
3
- Date: 2026-09-14 · Adds a compression level to the encoder. The format does not change and
4
- [lz4_decompress_block.js](lz4_decompress_block.js) is not touched.
5
-
6
- Every number below was measured on this machine on 2026-09-14, against real data. The measurement
7
- recipes are in §9 so any of it can be re-run.
8
-
9
- ---
10
-
11
- ## 1 · The question, and the answer
12
-
13
- > Add a configurable compression level that gets us to the current state-of-the-art maximum, without
14
- > changing the format.
15
-
16
- **The state of the art for the LZ4 block format is `lz4hc` level 12, and there is nothing
17
- meaningful past it.** An encoder built with an *uncapped* match search and a shortest-path parse over
18
- the whole block — strictly more search than level 12 is allowed — was measured against level 12:
19
-
20
- | corpus | level 12 | uncapped search + whole-block parse | time |
21
- |---|---|---|---|
22
- | 60 real `.vgeo` pages (1.77 MB) | 1,531,612 B | **1,531,612 B** — identical | 1.6× slower |
23
- | 24 text chunks (1.57 MB) | 356,238 B | 356,233 B — **0.0014% smaller** | 7.9× slower |
24
- | 18 runs and periodic blocks (74 KB) | 4,225 B | **4,225 B** — identical | 110× slower |
25
-
26
- Five bytes in a third of a megabyte, for eight times the work — and on repetitive data the uncapped
27
- parse is 110× level 12 and 3,500× level 9, because dropping the reference's `sufficient_len` early
28
- exit is exactly what lets a run of zeros cost quadratic time. The ceiling is level 12; the plan is to
29
- reach it and stop.
30
-
31
- A JS port of `lz4hc` was written and validated before this plan was drafted: **20,976 blocks over
32
- 1,947 inputs, at every level from 2 to 12, byte for byte identical to liblz4**, first try, including
33
- runs, periodic data, matches at the far edge of the 64KB window, engine source, glTF JSON and real
34
- geometry pages. The work is a known quantity, not an experiment.
35
-
36
- ---
37
-
38
- ## 2 · What it is worth
39
-
40
- Corpus: **2,636 decoded pages, 140,268,332 bytes**, taken from 120 of the 2,975 `.vgeo` containers
41
- the Zorah build wrote on 2026-09-13. These are the exact bytes
42
- [vgeo_encode_frame_blob.js](../../../shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js)
43
- is handed in production. Baseline is what [lz4_compress_block.js](lz4_compress_block.js) ships today.
44
-
45
- | level | ratio | vs. today | JS throughput |
46
- |---|---|---|---|
47
- | fast (today) | 0.89470 | — | 412–436 MB/s |
48
- | 3 | 0.87272 | −2.457% | 51–53 MB/s |
49
- | 4 | 0.87239 | −2.494% | 51 MB/s |
50
- | 6 | 0.87214 | −2.522% | 51 MB/s |
51
- | **9** | **0.87209** | **−2.528%** | **52–55 MB/s** |
52
- | 10 | 0.87204 | −2.533% | 46 MB/s |
53
- | 11 | 0.87204 | −2.533% | 45 MB/s |
54
- | 12 | 0.87202 | −2.535% | 42 MB/s |
55
-
56
- Two things fall out of that table.
57
-
58
- **Level 3 already gets 97% of what is available on geometry.** The whole ladder from 3 to 12 is
59
- 0.078 percentage points — 41 MiB on the 51 GiB Zorah build. Meshlet pages are quantised, densely
60
- packed bytes; the matches that exist are short and near, and a four-deep chain walk finds them.
61
-
62
- **Level 9 is both smaller and slightly faster than levels 3–6** — repeatably, three paired runs,
63
- always in the same direction. Level 9 is the only hash-chain level that turns on pattern analysis
64
- (the reference enables it at `nbSearches > 128`), and on data with runs in it that pays for itself.
65
- There is no reason to ship 3–8 as the default when 9 dominates them.
66
-
67
- ### It is not the same story for every payload
68
-
69
- The codec is generic core code, so the ladder has to be there even though vgeo barely uses it.
70
- Measured on 64KB chunks, same baseline:
71
-
72
- | payload | fast | level 3 | level 9 | level 12 |
73
- |---|---|---|---|---|
74
- | engine source + glTF JSON | 0.30153 | 0.23931 (−20.6%) | 0.23164 (−23.2%) | 0.22972 (−23.8%) |
75
- | runs and periodic data | 0.05763 | 0.05759 (−0.07%) | 0.05731 (−0.57%) | 0.05731 (−0.57%) |
76
- | real `.vgeo` pages | 0.89470 | −2.46% | −2.53% | −2.54% |
77
-
78
- On text the top of the ladder is worth 3.2 points over level 3 and costs 8× the time (113 MB/s at
79
- level 3, 14 MB/s at level 12). That is the payload class that justifies building levels 10–12 at all.
80
-
81
- ### What it does to a Zorah build
82
-
83
- From the 2026-09-13 conversion log: pages **51.38 GiB stored, 56.38 GiB decoded**, built in
84
- **4 h 8 m wall / 17 h 9 m CPU** across ten workers.
85
-
86
- | level | pages would be | saved | compression CPU | share of build CPU |
87
- |---|---|---|---|---|
88
- | fast | 51.38 GiB | — | 2.4 min | 0.2% |
89
- | 3 | 50.12 GiB | 1.26 GiB | 19.4 min | 1.9% |
90
- | **9** | **50.08 GiB** | **1.30 GiB** | **18.9 min** | **1.8%** |
91
- | 12 | 50.08 GiB | 1.30 GiB | 24.0 min | 2.3% |
92
-
93
- **The whole ladder is free.** Sixteen extra minutes of CPU on a seventeen-hour build, about two
94
- minutes of wall clock across ten workers, for 1.3 GiB. The interesting decision is not whether to
95
- pay for high compression — it is which level, and the answer is 9 because 10–12 cost 15–40% more
96
- time for 0.007 points.
97
-
98
- ---
99
-
100
- ## 3 · What it costs the reader
101
-
102
- The format does not change, so nothing has to be done. But the *shape* of the blocks changes, and
103
- that is worth saying out loud because it does not go the way one would guess.
104
-
105
- Measured with [lz4_decompress_block.js](lz4_decompress_block.js) over 250 real pages:
106
-
107
- | | ratio | sequences | bytes/sequence | decode |
108
- |---|---|---|---|---|
109
- | fast | 0.89372 | 208,503 | 61.9 | 1,174 MB/s |
110
- | level 9 | 0.87087 | 336,176 | 38.4 | 1,059 MB/s |
111
- | level 12 | 0.87081 | 339,644 | 38.0 | 999 MB/s |
112
-
113
- On geometry, HC finds **more** matches, not longer ones — 61% more sequences — and the decoder's
114
- per-sequence cost is a byte loop, so pages decode **10–15% slower**. On text the same measurement
115
- inverts: 115,657 sequences down to 81,431, and decode goes from 705 to 828 MB/s (**+18%**).
116
-
117
- This lands in a worker ([vgeo_page_worker_serve.js](../../../shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.js)),
118
- off the main thread, and it buys 2.5% fewer bytes to fetch on every page of every session. It is a
119
- fair trade as it stands. It is also **entirely recoverable**, for an unrelated reason — see §8.
120
-
121
- ---
122
-
123
- ## 4 · What the reference actually is
124
-
125
- `lz4hc.c` at 1.10.0. The level table is identical in 1.9.4 and 1.10.0 for levels 3–12, so targeting
126
- that range is version-stable:
127
-
128
- | level | strategy | chain attempts | sufficient length |
129
- |---|---|---|---|
130
- | 3–9 | hash chain, lazy parse | 4, 8, 16, 32, 64, 128, 256 | 16 |
131
- | 10 | optimal parse | 96 | 64 |
132
- | 11 | optimal parse | 512 | 128 |
133
- | 12 | optimal parse, full update | 16,384 | 4,096 |
134
-
135
- **Level 2 is deliberately not on that list.** 1.9.4 gives it the hash-chain strategy; 1.10.0 gives it
136
- `lz4mid`, a different compressor added in that release. "Level 2" does not name one thing across
137
- reference versions, so this encoder will not have one. Levels 1 (fast) and 3–12 are the ladder.
138
-
139
- Four pieces of the reference are load-bearing and none of them can be dropped:
140
-
141
- - **The hash chain.** `hashTable` (32,768 × u32) holds the newest position per 4-byte hash;
142
- `chainTable` (65,536 × u16) holds, per position, the *delta* back to the previous position with the
143
- same hash, capped at 65,535. Positions are inserted lazily, from `nextToUpdate` up to the current
144
- position, so nothing is built that is never searched.
145
- - **Pattern analysis.** When the chain shows consecutive positions (delta 1) and the 4 bytes are a
146
- repeat of a single byte, the walk jumps straight to the best position in the run instead of
147
- stepping through thousands of links. Without it, levels 9–12 go quadratic on a page of zeros.
148
- This is what makes level 9 *faster* than level 6 on real data.
149
- - **Chain swap.** On a tie, scan the chain deltas inside the match for the largest step and follow
150
- that instead. Only the optimal parser uses it.
151
- - **The three-match lazy parse** (levels 3–9) and the **windowed shortest path** (10–12). The price
152
- model is exact in bytes — a token, a 2-byte offset, and the continuation bytes each length field
153
- spills into — which is why the parse is a genuine shortest path and not a heuristic.
154
-
155
- Everything else in `lz4hc.c` is dictionary and streaming support: `extDict`, `dictCtx`,
156
- `LZ4HC_protectDictEnd`, the pattern rotation across a dictionary boundary, `favorDecSpeed`,
157
- `fillOutput`. meep compresses one self-contained block with no history. **All of it drops**, and with
158
- it about half the file. The port that was validated is ~700 lines against the reference's ~1,400.
159
-
160
- ---
161
-
162
- ## 5 · Design
163
-
164
- ### 5.1 Files
165
-
166
- Four new files in this directory. **[lz4_compress_block.js](lz4_compress_block.js) is not edited.**
167
-
168
- - **`LZ4_LEVEL.js`** — `{ FAST: 1, HC_MIN: 3, HC_DEFAULT: 9, HC_MAX: 12 }`, with the note about 2.
169
- - **`lz4_compress_block_hc.js`** — the high-compression encoder. Same parameter list as the fast one
170
- with `level` appended:
171
- `lz4_compress_block_hc(destination, destination_offset, destination_end, source, source_offset, source_end, level)`.
172
- - **`lz4_compress_block_at_level.js`** — dispatch. `level <= 1` goes to `lz4_compress_block`,
173
- anything else to `lz4_compress_block_hc`. This is what callers with a knob call; it is the only
174
- new thing `vgeo_encode_frame_blob` needs to know about.
175
- - **`lz4_compress_block_hc.spec.js`** — §6.
176
-
177
- ### 5.2 Why the fast encoder is not touched
178
-
179
- The obvious tidy — lift `write_token_and_literals` and `write_continuation` out of
180
- `lz4_compress_block.js` into a module both encoders import — was built and measured. Across five
181
- paired runs it cost the fast encoder between 0% and 8% of its throughput and never once helped.
182
- The HC encoder sequences its writes differently anyway (`LZ4HC_encodeSequence` emits token, literals,
183
- offset and match continuation as one unit, where the fast encoder splits it), so the two are not the
184
- same function. **The HC module carries its own writers.** The fast encoder's pinned bytes and its
185
- 435 MB/s cannot regress if nothing edits it.
186
-
187
- ### 5.3 Memory, and when it is taken
188
-
189
- | table | type | bytes | needed from |
190
- |---|---|---|---|
191
- | hash | `Int32Array(32768)` | 128 KB | level 3 |
192
- | chain | `Uint16Array(65536)` | 128 KB | level 3 |
193
- | price / offset / length / literal-length | 4 × `Int32Array(4099)` | 64 KB | level 10 |
194
-
195
- 320 KB, against the 32 KB the fast encoder holds today. meep ships raw source, so a module-level
196
- `new Int32Array(1 << 15)` would cost that to every consumer whether or not they ever compress —
197
- including the runtime, which only ever *decodes*. **Allocate lazily**: the chain tables on the first
198
- HC call, the parser tables on the first level-10-or-higher call. Module-static after that, like the
199
- fast encoder's, and safe for the same reason — the encoder is straight-line synchronous code that
200
- resets its tables on entry.
201
-
202
- ### 5.4 Table lifetime
203
-
204
- `hash_table.fill(0)` and `chain_table.fill(0xFFFF)` on entry, per call. This looked like the obvious
205
- thing to optimise — 256 KB of fill for a page that may be 1.4 KB — so it was measured.
206
-
207
- *Advancing* that bias from call to call so the tables never need clearing at all — which is what the
208
- reference does across streamed blocks, and which was verified byte-exact over 6,600 fuzz blocks —
209
- came out at **0.95–1.01×: no gain**, because turning `IDX_BASE` into a mutable module binding costs
210
- the inner loop more than the fill saves. Dropping only the chain-table clear (which is provably unnecessary here: every position's
211
- delta is written by the insert before anything can walk to it) also measured as noise.
212
-
213
- **Ship the plain per-call fill.** It matches the fast encoder's precedent and it is not the cost.
214
-
215
- ### 5.5 Two invariants the port must keep
216
-
217
- - **Self-contained blocks.** No match may reach before `source_offset`. With an index space biased by
218
- one window, an empty hash slot reads as "before the block" for free and the existing
219
- "a block compressed after another does not carry the previous one's positions" test passes without
220
- a special case.
221
- - **The output bound is checked, not assumed.** The reference has `limitedOutput`; this codebase
222
- throws, with message shapes the existing spec asserts (`/needs \d+ bytes, and only \d+ are left/`).
223
- The HC writers must do the same checks at the same two points — before the token-and-literals run,
224
- and before the offset-and-match-length. The prototype has neither yet; this is the one piece of the
225
- port that is not already written.
226
-
227
- ---
228
-
229
- ## 6 · Tests
230
-
231
- Following the idiom already in [lz4_compress_block.spec.js](lz4_compress_block.spec.js): pinned
232
- reference blocks first, then shapes too large to write down driven through and back.
233
-
234
- **Pinned vectors.** These are what liblz4's own `LZ4_compress_HC` writes, and the prototype
235
- reproduces them exactly:
236
-
237
- ```
238
- // the greedy pass leaves bytes on the table
239
- // "acddafadcacddaacddafadcacddaacdadcacbddafadcacddaacgdccaafadcacddaacgdadcacdfbeagdda" (84 bytes)
240
- // fast=47 hc3=42 hc9=42 hc12=42
241
- // hc3/hc9/hc12: 91 61 63 64 64 61 66 61 64 63 09 00 0d 0e 00 01 0b 00 1a 62 15 00 5a 67 64
242
- // 63 63 61 11 00 02 0c 00 80 66 62 65 61 67 64 64 61
243
-
244
- // the optimal parser earns its own row
245
- // "ffafdfdefafdfdeeahadefehahadefeeahadefehfd" (42 bytes)
246
- // fast=35 hc3=35 hc9=35 hc12=32
247
- // hc3/hc9: 83 66 66 61 66 64 66 64 65 07 00 93 65 61 68 61 64 65 66 65 68 08 00 b0 65 61 68
248
- // 61 64 65 66 65 68 66 64
249
- // hc12 : 83 66 66 61 66 64 66 64 65 07 00 92 65 61 68 61 64 65 66 65 68 08 00 03 10 00 50
250
- // 66 65 68 66 64
251
- ```
252
-
253
- The first pins that HC finds matches the greedy pass walks past; the second pins that levels 10–12
254
- are a different parser and not just a longer search.
255
-
256
- **Properties, over the existing `CORPUS` at every level:**
257
-
258
- 1. Round trip through `lz4_decompress_block` — bytes back exactly.
259
- 2. The encoder restrictions, using the spec's existing `read_sequences`: last five bytes literal, no
260
- match starting within twelve bytes of the end, no offset reaching before the block start, the
261
- block closes with a literals-only sequence.
262
- 3. **Aggregate** monotonicity: summed over the corpus, level *n+1* is never larger than level *n*.
263
- **Not per block** — 54 non-monotone steps were found across 275 inputs, concentrated at the 9→10
264
- boundary where the parser changes, the worst being a 64KB JSON chunk going 16,741 → 16,993 bytes.
265
- Asserting per-block monotonicity would be asserting something false.
266
- 4. A destination one byte short of the block is rejected, at every level, with the same message
267
- shapes the fast encoder produces.
268
- 5. `source_offset > 0`: the same payload compressed at offsets 0, 7, 32, 33 and 4096 inside buffers
269
- filled with different padding bytes produces **identical blocks**. This is the real guard on the
270
- one latent hazard in the port (§7), and it holds today for both encoders — 72 pages × 5 paddings.
271
- 6. Compressing a second block after a first does not carry the first's positions (the existing test,
272
- extended across levels).
273
- 7. The level argument is validated: 2 and anything outside 1…12 is refused rather than silently
274
- clamped to something the caller did not ask for.
275
-
276
- ---
277
-
278
- ## 7 · What the port has to be careful about
279
-
280
- Found while building and validating the prototype. None of these are speculative.
281
-
282
- **The cheap rejection gate can read outside the block.** Before a full compare, the reference tests
283
- the two bytes at the current best match end: `read16(iLowLimit + longest - 1)` against
284
- `read16(matchPtr - lookBackLength + longest - 1)`. When the search is allowed to start behind the
285
- current position — which is exactly what the lazy parser's second and third searches do — the second
286
- address can fall before `source_offset`, and at `source_offset === 0` before the array. The C reads
287
- whatever is in the buffer; JS reads `undefined`, which `| 0` turns into a zero. The gate is only a
288
- heuristic, so either behaviour produces a *valid* block, but they can produce *different* blocks.
289
- Clamp it explicitly and pin it with test 5 above. Over 72 real pages × 5 paddings, and 1,200 fuzz
290
- inputs, no divergence was reachable — it is latent, not live, and it should stay that way by
291
- construction rather than by luck.
292
-
293
- **The level table, not the levels.** Levels 3–12 map onto `{ attempts, sufficient_length }` pairs and
294
- a strategy; the strategy boundary at 10 and the pattern-analysis boundary at `attempts > 128` are
295
- where behaviour changes, not the level numbers. Write the table, not a switch.
296
-
297
- **`LZ4_MAX_INPUT_SIZE` still applies.** Indices are biased by one window and held in an `Int32Array`,
298
- which is fine to 2^31, and the existing assert already caps input well below that.
299
-
300
- ---
301
-
302
- ## 8 · Adjacent, and out of scope
303
-
304
- **The decoder's byte loops.** [lz4_decompress_block.js](lz4_decompress_block.js) copies literals and
305
- matches one byte at a time. Splicing in `destination.set` for literal runs of 16 or more and
306
- `copyWithin` for non-overlapping matches of 16 or more — nothing else changed, output verified
307
- identical — measured on 250 real pages:
308
-
309
- | | shipped decoder | with bulk copies |
310
- |---|---|---|
311
- | fast blocks | 1,206 MB/s | **2,034 MB/s** |
312
- | level 9 blocks | 1,036 MB/s | **1,361 MB/s** |
313
-
314
- HC blocks under a bulk-copy decoder (1,361 MB/s) decode **faster than fast blocks do today**
315
- (1,206 MB/s). The decode cost in §3 is a property of the decoder's inner loop, not of high
316
- compression. This is a separate change to a file this plan deliberately does not touch, and it is
317
- worth making on its own merits.
318
-
319
- **Done, 2026-09-14**, in the commit that added this paragraph. The threshold went in at
320
- thirty-two rather than sixteen: sweeping both crossovers over 2,267 real pages put the top of the
321
- curve between twenty-four and thirty-two, and thirty-two is where
322
- [lz4_compress_block.js](lz4_compress_block.js) had already put its own. Measured the way §10 says
323
- to, twenty-one interleaved rounds alternating which decoder went first:
324
-
325
- | | shipped decoder | with bulk copies |
326
- |---|---|---|
327
- | fast blocks | 1,205 MB/s | **2,237 MB/s** |
328
- | level 9 blocks | 1,109 MB/s | **1,592 MB/s** |
329
-
330
- The claim survives with room to spare: level 9 blocks now decode 32% *faster* than fast blocks
331
- managed before the change, and 44% faster than they manage today. What does **not** close is the
332
- gap between the two shapes — bulk copies help fast blocks more, because their literal runs are
333
- 58.8 bytes against level 9's 34.7, so level 9 goes from 8% behind level 1 to 29% behind it. The
334
- reader ends up far ahead in absolute terms and §3's trade is paid for several times over; the
335
- *ratio* §3 quoted gets wider, not narrower, and a later reader should not expect otherwise.
336
-
337
- Output is pinned against the old decoder over **57,938 stored pages of 150 Zorah containers,
338
- 3,489,392,680 decoded bytes, not one byte different**, and over the same 2,267 pages re-encoded at
339
- both levels. The overlapping match stays a byte loop and has its own vectors in the spec.
340
-
341
- What the sweep also showed is that the win is the **literal** path, which §3's text figures
342
- predict but do not say outright. Four fifths of a decoded page is literal runs averaging 34.7
343
- bytes at level 9, and its matches average 8.7 — so `set` moves 68% of the output and `copyWithin`
344
- 4%. On engine source, where literal runs average 1.1 bytes, the change is a wash at any threshold.
345
-
346
- **python-lz4 is not a usable reference for the fast encoder.** The 4.4.5 wheel (liblz4 1.9.4) fails
347
- to find matches whose candidate was recorded during the search loop: `"QabcdXXabcdZZZZZZZZ"` codes
348
- as all literals, while the same input with the repeat at offset 0 codes the match. Its HC path is
349
- correct. Consequence: **[lz4_compress_block.js](lz4_compress_block.js) is right and that build is
350
- wrong** — meep's blocks are 1.43% *smaller* than its `mode='default'` output over the corpus, and a
351
- line-by-line transliteration of `LZ4_compress_generic`'s `byU16` path agrees with meep on all 152
352
- inputs tried. Validate HC against python-lz4; validate the fast encoder against the lz4 CLI or a
353
- transliteration.
354
-
355
- ---
356
-
357
- ## 9 · The plan
358
-
359
- Each stage is independently shippable and leaves the tree green.
360
-
361
- ### Stage A — the hash chain, levels 3–9
362
-
363
- **Files:** `LZ4_LEVEL.js`, `lz4_compress_block_hc.js`, `lz4_compress_block_at_level.js`,
364
- `lz4_compress_block_hc.spec.js`
365
-
366
- Port, from `lz4hc.c`, with no dictionary and no streaming: the lazy insert, the match finder with
367
- pattern analysis, `LZ4HC_countBack`, and the three-match lazy parser. Add the output-bound checks
368
- (§5.5) and the clamped gate (§7). Levels 10–12 assert as not yet available.
369
-
370
- **Proves:** −2.53% on vgeo pages, −23.2% on text, at 52 MB/s. That is 97% of everything this plan can
371
- deliver.
372
-
373
- **Test:** all of §6 except the level-12 vector and the 9→10 aggregate step.
374
-
375
- ### Stage B — the optimal parser, levels 10–12
376
-
377
- **Files:** `lz4_compress_block_hc.js` (the parser and its lazily-allocated tables), spec additions.
378
-
379
- Port `LZ4HC_compress_optimal` with `favorDecSpeed` fixed off: the exact byte price model, the
380
- 4,096-position window, `sufficient_len`, and full update at level 12. Add chain swap to the finder.
381
-
382
- **Proves:** the ladder reaches the documented maximum, and §1's measurement says there is nothing
383
- past it.
384
-
385
- **Test:** the level-12 vector, and aggregate monotonicity across the whole ladder.
386
-
387
- ### Stage C — the level reaches the build
388
-
389
- **Files:**
390
- [VGeoBuildOptions.js](../../../shade/renderer/geometry/virtual/build/VGeoBuildOptions.js) ·
391
- [VGeoWriteOptions.js](../../../shade/renderer/geometry/virtual/format/VGeoWriteOptions.js) ·
392
- [vgeo_encode_frame_blob.js](../../../shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js) ·
393
- [vgeo_write_container.js](../../../shade/renderer/geometry/virtual/format/vgeo_write_container.js) ·
394
- [gltf_to_vgeo.js](../../../../tools/geometry/virtual/gltf_to_vgeo.js)
395
-
396
- A `codec_level` beside the existing `codec` at each layer, defaulting to `LZ4_LEVEL.HC_DEFAULT` (9),
397
- and a `--codec-level` flag on the tool. `vgeo_encode_frame_blob` already declines compression per
398
- page when it would not shrink the page, so the level cannot make a file larger — nothing about that
399
- contract changes.
400
-
401
- **Test:** [vgeo_frame_blob.spec.js](../../../shade/renderer/geometry/virtual/format/frame/vgeo_frame_blob.spec.js)
402
- gains a level case; the container round trip runs at the default.
403
-
404
- Nothing in the runtime changes. `codec` on the frame is still `VGEO_CODEC_LZ4`, the reader still
405
- reads it, and a container built at level 12 is readable by every build of the reader that exists.
406
-
407
- ### Stage D — confirm on a real conversion
408
-
409
- Re-run a Zorah subset at the new default and check the two numbers this plan promised: pages about
410
- 2.5% smaller, build CPU up under 2%. Record it next to the 2026-09-13 log.
411
-
412
- ---
413
-
414
- ## 10 · How to re-measure
415
-
416
- Everything above came from a scratchpad at
417
- `C:\Users\Alex\AppData\Local\Temp\claude\H--git-CompanyNamed-meep\f41c851a-bc82-447f-be15-80a25e108370\scratchpad`,
418
- which is session-local and will not survive. The recipes are short:
419
-
420
- - **Corpus** — walk a `.vgeo` with `vgeo_read_header` + `vgeo_read_directory`, slice each frame at
421
- `frame_offset`/`fetch_size`, and decode the blob past
422
- `VGEO_FRAME_HEADER_SIZE + 48 × child_page_count`. Note that `vgeo_read_header` wants more than
423
- `VGEO_HEADER_SIZE` bytes — the level table follows the header, so hand it 8 KB.
424
- - **Ground truth** — `python -c "import lz4.block as B; B.compress(data, mode='high_compression', compression=N, store_size=False)"`.
425
- Python has `lz4` installed here (4.4.5, liblz4 1.9.4). For HC only — see §8.
426
- - **Timing** — interleave A and B in one process and alternate which runs first; nineteen node
427
- processes is a normal background load on this machine and a single-shot number means nothing.
428
- - **Build cost** — `H:\vgeo_scratch\zorah-2026-13-09-console.log` carries the stored/decoded page
429
- totals and the CPU time the projections in §2 are scaled against.
430
-
431
- ---
432
-
433
- ## 11 · What it came to
434
-
435
- Carried out 2026-09-14, in four commits: the encoder and its spec, the plumbing through the build,
436
- one profiling pass, and this. Every number below was measured on this machine after the work, the
437
- same way §10 says to measure it.
438
-
439
- ### The port
440
-
441
- `lz4hc.c` at 1.10.0, with the dictionary and streaming half dropped as §4 said. One thing dropped
442
- that §4 did not name: **`LZ4HC_protectDictEnd` goes too.** It guards against a candidate whose four
443
- bytes would run off the end of a dictionary, and with no dictionary every candidate the loop reaches
444
- already satisfies it — `matchCandidateIdx >= lowestMatchIndex` implies it, and so does every index
445
- the pattern-analysis branch assigns. 733 lines of code against the reference's 2,192 of file.
446
-
447
- Byte for byte identical to liblz4 1.9.4's `LZ4_compress_HC` over **26,093 blocks**:
448
-
449
- | corpus | inputs | levels | blocks |
450
- |---|---|---|---|
451
- | synthetic: runs, periods, window edges, structured and small fuzz | 1,182 | 3–12 | 11,820 |
452
- | the same, capped at 20 KB | 961 | 3–12 | 9,610 |
453
- | the same, over 20 KB | 221 | 10–12 | 663 |
454
- | real `.vgeo` pages from the 2026-09-13 Zorah build | 400 | 3–12 | 4,000 |
455
-
456
- First try, as §1 said it would be. §6's two pinned vectors reproduce exactly, level-12 row included.
457
-
458
- ### The ladder, on 5,802 real pages
459
-
460
- The pages of a 507-geometry Zorah subset, 190,044,904 bytes decoded — the exact bytes
461
- `vgeo_encode_frame_blob` was handed:
462
-
463
- | level | stored | ratio | vs. level 1 | encoder |
464
- |---|---|---|---|---|
465
- | 1 | 166,692,697 | 0.87712 | — | 340–390 MB/s |
466
- | 3 | 162,743,403 | 0.85634 | −2.369% | 44–53 MB/s |
467
- | **9** | **162,604,364** | **0.85561** | **−2.453%** | **42–50 MB/s** |
468
- | 12 | 162,586,298 | 0.85552 | −2.463% | 31–38 MB/s |
469
-
470
- The stored byte counts are exact and repeat to the byte. The throughputs are ranges because that is
471
- what this machine supports: two best-of-nine runs of the same measurement, minutes apart, came out
472
- 20% apart, which is wider than most of the differences anyone would want to read off the column.
473
- What *is* stable is the shape within a run — level 1 is about eight times level 9, and level 9 is
474
- about 5% slower than level 3 — because those are ratios taken between interleaved passes.
475
-
476
- §2's shape holds exactly: level 3 is 96.2% of everything the ladder has on this payload, and the
477
- whole run from 3 to 12 is 0.082 percentage points. On a wider sample — the first 25 pages of each of
478
- 120 containers, 2,718 pages and 122,283,932 bytes — the same ladder runs 0.90483 to 0.87733, which
479
- is **−3.040%**, so 2.5% is the conservative end of what a whole build gets rather than the middle.
480
-
481
- Throughput came out below §2's 52 MB/s at first. Profiling put 16.3% of all ticks in the guarded
482
- gate read of §7, which V8 was not inlining: it is called once per link of every chain walked. Taking
483
- the guard only on addresses that need it, and reading the call's context into locals once rather
484
- than reloading module bindings inside that loop, bought 15% in a paired in-process comparison and
485
- left nothing outside the search loop above 14% of the profile.
486
-
487
- ### The two numbers Stage D promised
488
-
489
- **Pages about 2.5% smaller — confirmed.** The paired conversions, `--max-triangles 30000` on
490
- `zorah_main_public.v2.gltf`, 507 geometries and 5.25 M triangles, identical in every run:
491
-
492
- | | pages stored | decoded |
493
- |---|---|---|
494
- | level 1 | 159.0 MiB | 181.2 MiB |
495
- | level 9 | 155.1 MiB | 181.2 MiB |
496
-
497
- **Build CPU up under 2% — confirmed for the Zorah build, but not for this subset.** Six runs,
498
- alternating which level went first:
499
-
500
- | | CPU | wall |
501
- |---|---|---|
502
- | level 1 | 139.3 s · 139.7 s · **119.5 s** | 128.8 · 129.4 · **110.8** |
503
- | level 9 | 143.0 s · 147.6 s · **124.1 s** | 131.6 · 133.8 · **114.9** |
504
-
505
- The last pair ran back to back on a quiet machine and is the one to read: **+4.6 s of CPU, +3.85%**.
506
- Measuring the compression alone over the same build's own pages — five interleaved passes, so no
507
- noise at all — gives 0.51 s against 4.03 s, a delta of **+3.52 s, or +2.95%** of that build.
508
-
509
- That is over 2%, and it is over 2% *because of what the subset is*. `--max-triangles 30000` selects
510
- the small geometries, and they are cheap: 190 MB of pages out of 119.5 s of build CPU, which is
511
- 1.59 MB of page bytes per CPU-second. The 2026-09-13 build produced 60,536 MB of pages from 61,740 s
512
- (17 h 9 m, validation excluded), which is **0.98** — it spends 62% more CPU per page byte, because
513
- it is building the large meshes too. Scaling this subset's measured delta by that gives **about 2%**
514
- of the Zorah build's CPU.
515
-
516
- **About 2% is as fine as this machine resolves it, and the honest answer is that it straddles the
517
- line.** The three paired conversions give deltas of +3.7 s, +7.9 s and +4.6 s on a build of 190 MB
518
- of pages, which scale to 1.9%, 4.1% and 2.4% of the Zorah build; the direct compression measurement,
519
- taken twice on a quiet machine, gives 1.7% and 2.1%. Every one of those is a difference of a few
520
- seconds measured against a two-minute build on a box whose own run-to-run spread is 20%, so §2's
521
- 1.8% is not *confirmed* so much as *not contradicted*. What can be said without a measurement error
522
- swallowing it: the level costs a couple of percent of a build and a couple of minutes of wall clock
523
- across ten workers, and it is about 3% on a build made only of small meshes.
524
-
525
- ### What it costs the reader, re-measured
526
-
527
- §3 said the blocks change shape and the plan was right about that, on 2,500 real pages:
528
-
529
- | | ratio | bytes/sequence | decode |
530
- |---|---|---|---|
531
- | level 1 | 0.87252 | 55.3 | 1,197 MB/s |
532
- | level 9 | 0.85228 | 38.4 | 1,080 MB/s |
533
- | level 12 | 0.85220 | 38.1 | 1,077 MB/s |
534
-
535
- 38.4 bytes a sequence at level 9, which is §3's figure to the decimal, and decode 9.8% slower rather
536
- than 10–15%. §8's bulk-copy decoder is still the answer to that, and still out of scope here.
537
-
538
- ### And it reads
539
-
540
- 507 Zorah containers built at level 9 pass the tool's whole post-build audit — every build
541
- invariant, checksum and directory record, in every file — which is the claim that the format does
542
- not change, made against real geometry rather than argued.
@@ -1,2 +0,0 @@
1
- # References:
2
- * "Fast and Simple Agglomerative LBVH Construction" by Ciprian Apetrei, 2014
@@ -1,63 +0,0 @@
1
- References:
2
-
3
- * "Efficient Incoherent Ray Traversal on GPUs Through Compressed Wide BVHs" 2017, NVIDIA
4
- * https://github.com/jan-van-bergen/GPU-Raytracer
5
- * https://github.com/WeiPhil/nbvh/blob/0a7f8e0e09898ba6e161cd7433325368355e2d2a/include/cuda/accel/bvh8.cuh#L35
6
- * https://github.com/DGriffin91/obvhs/tree/main/src/cwbvh
7
- *
8
-
9
- Build
10
-
11
- * Consider triangles in groups of up-to 32 (bitmask)
12
- * Find "lowest" index
13
- * Walk up, and mark bitmask
14
-
15
- Explore additional metrics such as:
16
-
17
- * % of empty nodes
18
- * % of utilized triangle slots (up to 3 per child)
19
-
20
- Runtime
21
-
22
- * Debug shader for raycast, visualize:
23
- * traversal depth (number of node intersection tests)
24
- * number of triangle tests
25
- * amount of memory bandwidth (estimate based on size of what we have to load)
26
-
27
- # Findings [2024/12/27]:
28
-
29
- > [2026-06-11] The numbers below predate the review-01 fixes (see REVIEW_01_ACTION_PLAN.md) —
30
- > notably the morton sort keyed on box *size* instead of box *center* (F4) and the converter's
31
- > corrupted SAH cost table (F3), both of which directly degrade build quality and time.
32
- > Re-measure before drawing conclusions from this section.
33
-
34
- Conclusion for now - not worth it. Too slow to build, and runtime performance inadequate.
35
-
36
- // RUNGHOLT, ORIGINAL build mechanism
37
- BVH8 triangles = 24, size = 480 bytes, quality = 88.16 (total=53.92515886274739)
38
- Geometry build: 27701.5078125 ms
39
- BVH2 size total: 465,232,160
40
- BLAS size: 134,670,480 bytes
41
-
42
- // RUNGHOLT, NEW (by A.G.)
43
- BVH8 triangles = 24, size = 240 bytes, quality = 145.27999999999997 (total=594.418961181131)
44
- Geometry build: 315166.4069824219 ms
45
- BVH2 size total: 465,232,160
46
- BLAS size: 302,576,640 bytes
47
-
48
- ## Findings
49
-
50
- * Build times are far too long for runtime.
51
- * Binary sizes are considerably smaller than BVH2
52
- * Render times are much worse, in some cases 10x on shadows alone
53
- * Sorting children during traversal does help, as paper claims, by as much as 10%
54
- * packing a lot of triangles is tricky, due to restriction of only being able to address 24 contiunuous primitives per
55
- node
56
- * you have to try and pack at least 1 triangle per node, otherwise you're wasting the 4 bytes reserved for base triangle
57
- offset
58
- * original paper requires an extra triangle index array, which is hugely wasteful and creates an extra indirection
59
- table, which is bad for perf
60
-
61
- # microsoft's DXR fallback implementation
62
-
63
- https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Libraries/D3D12RaytracingFallback