@woosh/meep-engine 3.60.3 → 3.60.5
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.
- package/CHANGELOG.md +2602 -0
- package/CONTRIBUTING.md +7 -0
- package/MIGRATION_2_TO_3.md +136 -0
- package/SECURITY.md +4 -0
- package/editor/EditorCameraNavigation.d.ts +2 -1
- package/editor/EditorCameraNavigation.d.ts.map +1 -1
- package/editor/EditorCameraNavigation.js +51 -57
- package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts +3 -0
- package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts.map +1 -1
- package/editor/ecs/component/editors/ecs/Trail3DEditor.js +6 -0
- package/package.json +6 -2
- package/src/engine/EngineHarness.d.ts +3 -9
- package/src/engine/EngineHarness.d.ts.map +1 -1
- package/src/engine/EngineHarness.js +6 -8
- package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts +9 -6
- package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts.map +1 -1
- package/src/engine/graphics/camera/makeOrbitalCameraController.js +44 -71
- package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts +64 -0
- package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts.map +1 -0
- package/src/engine/graphics/camera/orbital/MomentumAxis.js +187 -0
- package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts +86 -0
- package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts.map +1 -0
- package/src/engine/graphics/camera/orbital/OrbitalCameraRig.js +319 -0
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts +34 -10
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +116 -30
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts +1 -7
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +1 -37
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +1 -2
- package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.d.ts +14 -0
- package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.d.ts.map +1 -0
- package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.js +22 -0
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +19 -3
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3D.js +31 -4
- package/src/engine/graphics/ecs/trail3d/advance_trail.js +1 -1
- package/src/engine/graphics/ecs/water/Water.d.ts +8 -0
- package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/Water.js +8 -0
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts +5 -5
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts.map +1 -1
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +6 -6
- package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/WaterSystem.js +731 -734
- package/src/engine/graphics3/decal/DecalTileShader.d.ts +61 -0
- package/src/engine/graphics3/decal/DecalTileShader.d.ts.map +1 -0
- package/src/engine/graphics3/decal/DecalTileShader.js +138 -0
- package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts +10 -4
- package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts.map +1 -1
- package/src/engine/graphics3/decal/GPUDecalRenderer.js +149 -44
- package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts +25 -0
- package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts.map +1 -0
- package/src/engine/graphics3/decal/chunk_decal_tile_vertex.js +55 -0
- package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts +22 -0
- package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts.map +1 -0
- package/src/engine/graphics3/decal/graph_gather_decal_tiles.js +70 -0
- package/src/engine/graphics3/decal/shader_decal_composite.d.ts +7 -5
- package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
- package/src/engine/graphics3/decal/shader_decal_composite.js +48 -44
- package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts +12 -0
- package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts.map +1 -0
- package/src/engine/graphics3/decal/shader_decal_tile_copy.js +78 -0
- package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts +11 -0
- package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts.map +1 -0
- package/src/engine/graphics3/decal/shader_gather_decal_tiles.js +80 -0
- package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts +10 -0
- package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts.map +1 -1
- package/src/engine/graphics3/trail/trail_tube_write_geometry.js +13 -1
- package/src/engine/input/devices/mouse/MouseEventButtons.d.ts +13 -0
- package/src/engine/input/devices/mouse/MouseEventButtons.d.ts.map +1 -0
- package/src/engine/input/devices/mouse/MouseEventButtons.js +23 -0
- package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts +4 -0
- package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts.map +1 -1
- package/src/engine/input/ecs/controllers/KeyboardCameraController.js +9 -45
- package/src/engine/physics/cloth/ecs/cloth_geodesic.d.ts +2 -2
- package/src/engine/physics/cloth/ecs/cloth_geodesic.js +2 -2
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/playground/probe_clipmap/README.md +1 -0
- package/src/shade/playground/probe_clipmap/index.html +5 -0
- package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
- package/src/shade/playground/probe_clipmap/main.js +16 -2
- package/src/shade/playground/vgeo_scene/README.md +1 -1
- package/src/shade/playground/water_refraction/README.md +5 -6
- package/src/shade/playground/water_refraction/index.html +1 -1
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +7 -5
- package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts +4 -6
- package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts.map +1 -1
- package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.js +34 -36
- package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts +1 -1
- package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts.map +1 -1
- package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.js +162 -163
- package/src/shade/renderer/atmosphere/compute_mie_absorption.d.ts +1 -1
- package/src/shade/renderer/atmosphere/compute_mie_absorption.js +1 -1
- package/src/shade/renderer/atmosphere/hillaire/ATMOSPHERE_EARTH.js +1 -1
- package/src/shade/renderer/atmosphere/hillaire/ATMOSPHERE_PARAMETERS_STRUCT.js +1 -1
- package/src/shade/renderer/atmosphere/shader_multiscatter_lut.js +164 -164
- package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.js +1 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +40 -80
- package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +197 -147
- package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
- package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
- package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
- package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
- package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +4 -4
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +4 -4
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +2 -1
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +18 -1
- package/src/shade/renderer/geometry/sdf/cards/mesh_cards_raster_pass_descriptor.js +2 -2
- package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_histogram.d.ts +1 -1
- package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_histogram.js +1 -1
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts +4 -5
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.js +6 -7
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts +31 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.js +343 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts +17 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.js +29 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +7 -13
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +2 -8
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +5 -5
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +10 -10
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts +12 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.js +22 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +4 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +50 -13
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +6 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +7 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts +13 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.js +13 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +17 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +20 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts +5 -15
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.js +6 -34
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.d.ts +18 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.js +42 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +5 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +4 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +13 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts +52 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.js +250 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -7
- package/src/shade/renderer/hiz/HZBFootprint.d.ts +14 -0
- package/src/shade/renderer/hiz/HZBFootprint.d.ts.map +1 -0
- package/src/shade/renderer/hiz/HZBFootprint.js +16 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts +10 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts.map +1 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.js +11 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts +10 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts.map +1 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.js +11 -0
- package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +0 -7
- package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
- package/src/shade/renderer/hiz/HierarchicalZBuffer.js +405 -350
- package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts +12 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts.map +1 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_x.js +27 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts +12 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts.map +1 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_y.js +27 -0
- package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts +14 -0
- package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts.map +1 -0
- package/src/shade/renderer/hiz/hzb_footprint_texels.js +34 -0
- package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +0 -5
- package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
- package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +37 -57
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts +11 -0
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts.map +1 -0
- package/src/shade/renderer/hiz/{shader_hzb_reduce_mip0_clip.js → shader_hzb_reduce_mip0.js} +120 -111
- package/src/shade/renderer/light/environment/environment_level_1_blur_plan.d.ts +39 -0
- package/src/shade/renderer/light/environment/environment_level_1_blur_plan.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/environment_level_1_blur_plan.js +119 -0
- package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts +15 -7
- package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/execute_filter_environment_map.js +66 -83
- package/src/shade/renderer/light/environment/graph_filter_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/graph_filter_environment_map.js +15 -6
- package/src/shade/renderer/light/environment/shader_blur_environment_map.d.ts +3 -0
- package/src/shade/renderer/light/environment/shader_blur_environment_map.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/shader_blur_environment_map.js +111 -0
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.d.ts +3 -0
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.js +128 -0
- package/src/shade/renderer/light/environment/shader_downsample_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/shader_downsample_environment_map.js +0 -1
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/material/GPUMaterialManager.d.ts +7 -0
- package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
- package/src/shade/renderer/material/GPUMaterialManager.js +11 -0
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +12 -12
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +17 -118
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -3
- package/src/shade/renderer/postprocess/nss/NSS.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.js +1 -3
- package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.d.ts +5 -6
- package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.js +9 -10
- package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.js +48 -49
- package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.js +12 -16
- package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.js +246 -246
- package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts +1 -2
- package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.js +34 -35
- package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.d.ts +2 -2
- package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.js +2 -2
- package/src/shade/renderer/shader/chunk/atmosphere/chunk_compute_extinction.js +1 -1
- package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.d.ts +1 -1
- package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.js +1 -1
- package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_Schlick.d.ts +3 -3
- package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_Schlick.js +3 -3
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.d.ts +9 -10
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.js +9 -10
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_tonemap_ue_filmic.d.ts +4 -4
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_tonemap_ue_filmic.js +5 -5
- package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts +1 -2
- package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.js +1 -2
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.d.ts +14 -1
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.js +19 -6
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.d.ts +10 -1
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.js +14 -6
- package/src/shade/renderer/shader/chunk/random/murmur/chunk_murmur3_vec3u.d.ts +1 -1
- package/src/shade/renderer/shader/chunk/random/murmur/chunk_murmur3_vec3u.js +1 -1
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +7 -2
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts +2 -3
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.js +7 -8
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_LOAD_FACTOR.d.ts +1 -1
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_LOAD_FACTOR.js +12 -12
- package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts +2 -2
- package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/VSMPageManager.js +3 -4
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts +0 -6
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
- package/src/shade/renderer/water/GPUWaterRenderer.js +12 -17
- package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts +35 -0
- package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts.map +1 -0
- package/src/shade/renderer/water/medium/graph_water_meshlets.js +89 -0
- package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
- package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
- package/src/shade/renderer/water/shader_water.js +22 -13
- package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
- package/src/shade/renderer/water/shader_water_avboit.js +7 -7
- package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +1 -1
- package/src/shade/renderer/water/surface/graph_water_tessellation.js +1 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +9 -1
- package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts +0 -19
- package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts.map +1 -1
- package/src/view/elements/progress/segmented/SegmentedResourceBarView.js +2 -52
- package/editor/REWORK_PLAN_2026_08.md +0 -692
- package/src/CODEC_LAYOUT_VERIFICATION.md +0 -419
- package/src/CORRECTNESS_HUNT_2026_07_24.md +0 -142
- package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +0 -738
- package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +0 -79
- package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +0 -93
- package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +0 -80
- package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +0 -164
- package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +0 -69
- package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +0 -110
- package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +0 -129
- package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +0 -72
- package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +0 -74
- package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +0 -169
- package/src/DUPLICATION_AUDIT_2026_08_07.md +0 -254
- package/src/DUPLICATION_AUDIT_REMEDIATION.md +0 -250
- package/src/DUPLICATION_REVIEW_2026_10_24.md +0 -1072
- package/src/REVIEW_2026_08_06/adv-core.md +0 -511
- package/src/REVIEW_2026_08_06/adv-gfx.md +0 -480
- package/src/REVIEW_2026_08_06/adv-view.md +0 -446
- package/src/REVIEW_2026_08_06/api-core.md +0 -296
- package/src/REVIEW_2026_08_06/api-engine.md +0 -195
- package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +0 -507
- package/src/REVIEW_2026_08_06/core-binary.md +0 -348
- package/src/REVIEW_2026_08_06/core-collection.md +0 -376
- package/src/REVIEW_2026_08_06/core-math.md +0 -221
- package/src/REVIEW_2026_08_06/core-process.md +0 -266
- package/src/REVIEW_2026_08_06/core-science.md +0 -223
- package/src/REVIEW_2026_08_06/engine-ai-nav.md +0 -279
- package/src/REVIEW_2026_08_06/engine-animation.md +0 -167
- package/src/REVIEW_2026_08_06/engine-asset.md +0 -166
- package/src/REVIEW_2026_08_06/engine-ecs.md +0 -920
- package/src/REVIEW_2026_08_06/engine-input.md +0 -197
- package/src/REVIEW_2026_08_06/engine-misc.md +0 -248
- package/src/REVIEW_2026_08_06/engine-terrain.md +0 -52
- package/src/REVIEW_2026_08_06/generation.md +0 -176
- package/src/REVIEW_2026_08_06/geom-2d.md +0 -434
- package/src/REVIEW_2026_08_06/geom-3d.md +0 -117
- package/src/REVIEW_2026_08_06/gfx-gi.md +0 -143
- package/src/REVIEW_2026_08_06/gfx-render.md +0 -740
- package/src/REVIEW_2026_08_06/gfx-texture.md +0 -690
- package/src/REVIEW_2026_08_06/prior-criticals.md +0 -49
- package/src/REVIEW_2026_08_06/prior-highs.md +0 -138
- package/src/REVIEW_2026_08_06/test-gaps.md +0 -364
- package/src/REVIEW_2026_08_06/view.md +0 -667
- package/src/REVIEW_2026_08_06.md +0 -610
- package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +0 -542
- package/src/core/bvh2/bvh3/NOTES.md +0 -2
- package/src/core/bvh8/NOTES.md +0 -63
- package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +0 -243
- package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +0 -417
- package/src/core/color/COLOR_REVIEW_2026_08_28.md +0 -779
- package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +0 -433
- package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +0 -486
- package/src/core/geom/3d/atlas/BFF_PLAN.md +0 -174
- package/src/core/geom/3d/atlas/REVIEW_02.md +0 -392
- package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +0 -146
- package/src/core/geom/3d/gjk/NOTES.md +0 -8
- package/src/core/geom/3d/topology/TOPO_MESH_RETIREMENT_PLAN.md +0 -135
- package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +0 -381
- package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +0 -684
- package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +0 -554
- package/src/core/model/reactive/evaluation/PREDICATE_APPLICABILITY_PROPOSAL.md +0 -446
- package/src/engine/REVIEW_2026_07_03.md +0 -211
- package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +0 -815
- package/src/engine/control/first-person/DESIGN.md +0 -658
- package/src/engine/control/first-person/DESIGN_COLLISION.md +0 -382
- package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +0 -563
- package/src/engine/control/first-person/TODO.md +0 -160
- package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +0 -531
- package/src/engine/ecs/HIERARCHY_REVIEW.md +0 -717
- package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +0 -262
- package/src/engine/ecs/ik/PLAN.md +0 -179
- package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +0 -272
- package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +0 -199
- package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +0 -521
- package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +0 -463
- package/src/engine/graphics/ecs/camera/pp/NOTES.md +0 -48
- package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +0 -659
- package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +0 -235
- package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +0 -490
- package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +0 -466
- package/src/engine/input/GAMEPAD_API_PROPOSAL.md +0 -717
- package/src/engine/input/ecs/ism/ISM_DESIGN.md +0 -1523
- package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +0 -627
- package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +0 -661
- package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +0 -229
- package/src/engine/network/CONGESTION_CONTROL.md +0 -87
- package/src/engine/network/MESSAGE_TRANSPORT.md +0 -364
- package/src/engine/network/SMOOTHNESS_DESIGN.md +0 -400
- package/src/engine/physics/cloth/MEASUREMENTS.md +0 -5304
- package/src/engine/physics/cloth/PLAN.md +0 -3056
- package/src/engine/physics/fluid/GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md +0 -1382
- package/src/engine/physics/fluid/REVIEW_02_PLAN.md +0 -243
- package/src/engine/physics/fluid/solver/NOTES.md +0 -46
- package/src/engine/sound/SOUND_EMITTER_RETIREMENT_PLAN.md +0 -536
- package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +0 -432
- package/src/engine/sound/simulation/PROBE_PLACEMENT.md +0 -250
- package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +0 -322
- package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +0 -261
- package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +0 -121
- package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +0 -999
- package/src/format/image/avif/DECISIONS.md +0 -545
- package/src/format/image/jpeg/NOTES.md +0 -3
- package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +0 -814
- package/src/generation/COORDINATES.md +0 -54
- package/src/generation/REVIEW_01_ACTION_PLAN.md +0 -628
- package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +0 -220
- package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +0 -172
- package/src/generation/foliage/CROWN_FULLNESS_BRIEF_2026_10_26.md +0 -86
- package/src/generation/foliage/FOLIAGE_DESIGN.md +0 -547
- package/src/generation/foliage/FOLIAGE_PLAN.md +0 -198
- package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +0 -158
- package/src/generation/foliage/LOD_REWORK_BRIEF_2026_10_27.md +0 -521
- package/src/generation/foliage/REAL_TIME_TREES_BRIEF_2026_10_27.md +0 -86
- package/src/generation/foliage/SELF_ORGANIZING_FRAMEWORK_BRIEF_2026_10_26.md +0 -347
- package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +0 -248
- package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +0 -694
- package/src/generation/foliage/TWO_FRONT_ENDS_BRIEF_2026_10_28.md +0 -552
- package/src/shade/RENDERER_CONTRACT.md +0 -697
- package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +0 -263
- package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +0 -569
- package/src/shade/device/PASS_ENCODER_PROPOSAL_2026_08_31.md +0 -755
- package/src/shade/device/mock/EXTENSION_PROPOSAL_2026_08_22.md +0 -458
- package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +0 -255
- package/src/shade/device/mock/REVIEW_2026_08_22.md +0 -527
- package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +0 -894
- package/src/shade/device/timing/profile/SGPT_FORMAT.md +0 -345
- package/src/shade/renderer/NOTES.md +0 -78
- package/src/shade/renderer/PLAN.md +0 -8
- package/src/shade/renderer/animation/ADDITIVE_BLENDING_PLAN.md +0 -145
- package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +0 -357
- package/src/shade/renderer/atmosphere/NOTES.md +0 -6
- package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts +0 -76
- package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/MomentumAxis.js +0 -164
- package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts +0 -45
- package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.js +0 -66
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +0 -98
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +0 -184
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +0 -175
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +0 -411
- package/src/shade/renderer/camera/orbital/README.md +0 -81
- package/src/shade/renderer/camera/orbital/index.d.ts +0 -6
- package/src/shade/renderer/camera/orbital/index.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/index.js +0 -9
- package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +0 -964
- package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +0 -361
- package/src/shade/renderer/geometry/bvh/BLAS_REFIT_BATCH_PLAN.md +0 -619
- package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +0 -674
- package/src/shade/renderer/geometry/sdf/NOTES.md +0 -6
- package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +0 -540
- package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +0 -305
- package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +0 -913
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +0 -787
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +0 -483
- package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +0 -301
- package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +0 -448
- package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +0 -385
- package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +0 -656
- package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +0 -308
- package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +0 -657
- package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +0 -551
- package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +0 -536
- package/src/shade/renderer/global_illumination/lpv/NOTES.md +0 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +0 -4225
- package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +0 -410
- package/src/shade/renderer/global_illumination/sharc/NOTES.md +0 -8
- package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +0 -1369
- package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +0 -202
- package/src/shade/renderer/gpu_primitive/indirect/NOTES.md +0 -94
- package/src/shade/renderer/hiz/NOTES.md +0 -3
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +0 -8
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +0 -1
- package/src/shade/renderer/light/cluster/NOTES.md +0 -11
- package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +0 -524
- package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +0 -1101
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts +0 -13
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts.map +0 -1
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.js +0 -198
- package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts +0 -24
- package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts.map +0 -1
- package/src/shade/renderer/material/resident/shader_resident_texture_means.js +0 -120
- package/src/shade/renderer/particles/DESIGN.md +0 -1011
- package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +0 -283
- package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +0 -453
- package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +0 -520
- package/src/shade/renderer/postprocess/bloom/NOTES.md +0 -6
- package/src/shade/renderer/postprocess/fsr/NOTES.md +0 -4
- package/src/shade/renderer/postprocess/gtao/NOTES.md +0 -6
- package/src/shade/renderer/postprocess/nss/NOTES.md +0 -339
- package/src/shade/renderer/postprocess/perspective/NOTES.md +0 -3
- package/src/shade/renderer/postprocess/sscs/NOTES.md +0 -5
- package/src/shade/renderer/postprocess/ssr/NOTES.md +0 -14
- package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +0 -286
- package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -39
- package/src/shade/renderer/postprocess/taa/NOTES.md +0 -11
- package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +0 -78
- package/src/shade/renderer/postprocess/vrs/NOTES.md +0 -4
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +0 -1218
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +0 -600
- package/src/shade/renderer/restir/reservoir/NOTES.md +0 -6
- package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +0 -6
- package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +0 -394
- package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +0 -160
- package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +0 -322
- package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +0 -247
- package/src/shade/renderer/shader/chunk/fsr/NOTES.md +0 -3
- package/src/shade/renderer/shader/chunk/fsr/spd/NOTES.md +0 -4
- package/src/shade/renderer/shader/chunk/geometry/ray/NOTES.md +0 -4
- package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +0 -159
- package/src/shade/renderer/shader/chunk/mboit/NOTES.md +0 -38
- package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/NOTES.md +0 -3
- package/src/shade/renderer/shader/chunk/spherical_harmonics/sampling/NOTES.md +0 -8
- package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +0 -932
- package/src/shade/renderer/shadow/contact/FINDINGS.md +0 -413
- package/src/shade/renderer/shadow/map/csm/NOTES.md +0 -5
- package/src/shade/renderer/shadow/ray/NOTES.md +0 -31
- package/src/shade/renderer/shadow/sdf/NOTES.md +0 -9
- package/src/shade/renderer/shadow/sdf/brixelizer/NOTES.md +0 -5
- package/src/shade/renderer/shadow/sdf/cascade/NOTES.md +0 -9
- package/src/shade/renderer/shadow/vsm/NOTES.md +0 -12
- package/src/shade/renderer/shadow/vsm/PLAN.md +0 -442
- package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +0 -43
- package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +0 -2444
- package/src/shade/renderer/texture/mipmaps/NOTES.md +0 -4
- package/src/shade/renderer/volumetrics/NOTES.md +0 -1329
- package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +0 -336
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +0 -23
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +0 -1
- package/src/shade/renderer/water/medium/GPUWaterBodies.js +0 -75
- package/src/shade/wgsl/NOTES.md +0 -4
- package/src/view/elements/progress/segmented/segment_frame_transform.d.ts +0 -15
- package/src/view/elements/progress/segmented/segment_frame_transform.d.ts.map +0 -1
- package/src/view/elements/progress/segmented/segment_frame_transform.js +0 -69
|
@@ -1,913 +0,0 @@
|
|
|
1
|
-
# `.vgeo` — Virtual Geometry Container Format
|
|
2
|
-
|
|
3
|
-
Status: **writer, validator and reader implemented; no GPU runtime.** Companion to
|
|
4
|
-
[VIRTUAL_GEOMETRY_DESIGN.md](./VIRTUAL_GEOMETRY_DESIGN.md); every requirement pinned in
|
|
5
|
-
its §5/§6 is realized here as a concrete field or invariant. Pinned structure below;
|
|
6
|
-
open details are listed at the end. One `.vgeo` file = one unique geometry's complete
|
|
7
|
-
cluster DAG; instances share the file.
|
|
8
|
-
|
|
9
|
-
The implementation lives beside this document: `format/` is the container — what a file is,
|
|
10
|
-
how one is written, and `vgeo_validate_container.js`, which audits every §11 invariant in
|
|
11
|
-
one; `format/read/` reads one a page at a time; and `build/` is the pipeline that produces
|
|
12
|
-
it from a `Geometry`, glTF front end included. The node command line over the writer and the
|
|
13
|
-
validator is at `meep-engine/tools/geometry/virtual/`. One glTF yields **one file per
|
|
14
|
-
primitive**, in object space, with the scene's placements written to a sidecar manifest
|
|
15
|
-
rather than baked in.
|
|
16
|
-
|
|
17
|
-
**Where the reader stops**, because it is the first thing a runtime needs to know. `format/read/`
|
|
18
|
-
implements the *mechanics* of §10 — cold start, one fetch per page from the byte range its parent
|
|
19
|
-
wrote down, want dedup, activation, eviction, and the mandatory reject rules of §4 and §5. It
|
|
20
|
-
implements none of the *policy*: no fetch scheduler, no budgets, no touch-LRU, no quarantine, and
|
|
21
|
-
the pinning rules are left to whoever manages residency.
|
|
22
|
-
|
|
23
|
-
It decodes the **node table** (§6.1) — the GPU cut pass is the consumer that was predicted here and
|
|
24
|
-
it arrived; the node metric's `max_parent_error` is what lets a whole subtree be skipped without
|
|
25
|
-
touching a cluster. It decodes the **child-table traversal metric** (§5, §6.2) as well, because a
|
|
26
|
-
reader that decodes two thirds of a structure is worse than one that decodes all of it — but no
|
|
27
|
-
consumer reads it, and `runtime/RUNTIME_PLAN.md` §4.8 says why: at the moment a want is emitted, the
|
|
28
|
-
per-cluster test the cut has already passed is strictly tighter than any page-level test, so the
|
|
29
|
-
page-level one can never reject anything. Its consumer is prefetch, which is a later campaign.
|
|
30
|
-
|
|
31
|
-
`shade/playground/vgeo_runtime/` and `shade/playground/vgeo_scene/` drive the reader end to end
|
|
32
|
-
against a device, and `runtime/cut/__fixtures__/vgeo_select_cut.js` is a CPU reference traversal of the
|
|
33
|
-
cut rule this format was shaped around — the oracle the GPU cut is held to, and the only traversal
|
|
34
|
-
of it that is not a kernel.
|
|
35
|
-
|
|
36
|
-
## 1. Design constraints (inherited, binding)
|
|
37
|
-
|
|
38
|
-
- **Geometry only.** `.vgeo` carries geometry and its vertex attributes — nothing else.
|
|
39
|
-
Materials are orthogonal, engine-side concerns; a multi-material source splits into
|
|
40
|
-
one geometry per material before it reaches the DAG builder — for glTF that split is
|
|
41
|
-
free, since a primitive already carries exactly one material, and the converter's
|
|
42
|
-
front-end emits one file per primitive. The format has no material fields.
|
|
43
|
-
- Billions of source triangles per asset: 64-bit file offsets, 32-bit page indices.
|
|
44
|
-
- Immutable, content-addressed; perfect HTTP/CDN caching; fetched by single-range
|
|
45
|
-
requests.
|
|
46
|
-
- Fetch unit = size-bounded **page**, independently decodable; a group never spans pages.
|
|
47
|
-
- No separate global hierarchy: cluster metadata travels **in** pages, so the hierarchy
|
|
48
|
-
streams with the geometry it describes.
|
|
49
|
-
- **Traversal-first**: a page carries everything needed to fetch the pages it refines
|
|
50
|
-
into. Standard runtime operation is header → root page → page-to-page traversal; the
|
|
51
|
-
directory is auxiliary (tooling, validation, arbitrary-page access), never a hot-path
|
|
52
|
-
dependency.
|
|
53
|
-
- Time-to-first-pixel = header + root page, O(1) in asset size.
|
|
54
|
-
- Prefix-readable: a plain byte-prefix of the file is a complete coarse asset.
|
|
55
|
-
- Per-page compression inside the container only; transparent HTTP compression is
|
|
56
|
-
assumed absent (it does not compose with range requests).
|
|
57
|
-
- The page DAG is acyclic by construction: child references point strictly forward in
|
|
58
|
-
the file.
|
|
59
|
-
|
|
60
|
-
## 2. Conventions
|
|
61
|
-
|
|
62
|
-
- Little-endian throughout. Types: `u8 u16 u32 u64 f32`; `[T; N]` = N-element array.
|
|
63
|
-
- All file offsets are absolute byte offsets from file start. All in-page offsets are
|
|
64
|
-
byte offsets from the start of the **decoded** page.
|
|
65
|
-
- **Frames** (stored pages) start at 16-byte-aligned file offsets; `directory_offset`
|
|
66
|
-
is 16-aligned; alignment padding is real bytes in the file. Inside a decoded page,
|
|
67
|
-
all tables and payload sections are 4-byte aligned (Shade's pools are u32-word
|
|
68
|
-
addressed).
|
|
69
|
-
- Levels are numbered `0 = leaves`, increasing toward the root. "Coarser" = higher
|
|
70
|
-
level. A page's **band** is a contiguous level range `min_level..max_level`.
|
|
71
|
-
A level is a **group** ordinal, not a per-cluster depth: it says which band a group
|
|
72
|
-
sits in, and a group's clusters may sit at several different depths in the DAG,
|
|
73
|
-
because a cluster the builder had no use for is carried forward untouched rather than
|
|
74
|
-
coarsened for the sake of symmetry. Cluster records carry no level, and nothing needs
|
|
75
|
-
one. What the ordinal guarantees is the property file order depends on: **every group
|
|
76
|
-
is in a strictly lower band than the group holding its parents**, so pages laid out by
|
|
77
|
-
descending band make every child reference point forward.
|
|
78
|
-
- Sentinels for cluster `child_ref`: `NO_CHILD = 0xFFFF` (leaves),
|
|
79
|
-
`SAME_PAGE = 0xFFFE` (child group lives in this page — legal only in multi-level
|
|
80
|
-
pages, the root page included); valid child-table indices are `≤ 0xFFFD`. Cluster
|
|
81
|
-
`child_group`: `NO_GROUP = 0xFFFF`, exactly for leaves. All `f32` fields are NaN-free.
|
|
82
|
-
Error values are finite `≥ 0`, except the root's parent error which is `+∞` (f32
|
|
83
|
-
infinity).
|
|
84
|
-
- Reserved fields: writers MUST write 0; readers MUST ignore their contents.
|
|
85
|
-
- Hashing is xxHash3; a `hash_algo` id is reserved for future change. **Integrity
|
|
86
|
-
model**: identity-seeded per-frame checksums are the *operative client-side*
|
|
87
|
-
mechanism — and because all DAG edges (child tables, cluster records) live inside
|
|
88
|
-
frames, the traversal path is covered by them end to end; the header and the
|
|
89
|
-
directory carry their own checksums; `asset_id` is whole-file and
|
|
90
|
-
*publisher/pipeline/cache-side* — a range-fetching client never reads the whole file
|
|
91
|
-
and can never verify it. None of these are a security boundary — they defend against
|
|
92
|
-
truncation, corruption, version skew, and cache mixups, not attackers.
|
|
93
|
-
|
|
94
|
-
## 3. Top-level layout
|
|
95
|
-
|
|
96
|
-
```
|
|
97
|
-
[ Header (256 B) ]
|
|
98
|
-
[ Level table ] ← level_count × 32 B, §4.1
|
|
99
|
-
[ Root page frame ] ← always-resident set = header + table + this
|
|
100
|
-
[ Page frames, coarse-first band order ] ← prefix of the file = coarse asset
|
|
101
|
-
[ Page directory ] ← flat 32 B records, range-fetchable
|
|
102
|
-
[ Directory block checksums ] ← u64 per 128 records (4 KB block)
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
The frame region is **exactly** `[root_page_offset, directory_offset)` — that is the
|
|
106
|
-
normative end-of-frames rule (see §5). Its start is a header field rather than a constant
|
|
107
|
-
because the level table (§4.1) precedes it and its size depends on the build. The directory is **derived data**: every field in it is
|
|
108
|
-
reconstructible by scanning frames and decoding page headers. It exists for special
|
|
109
|
-
tasks — tooling, validation, RAM budgeting before fetch, resuming from a disk cache,
|
|
110
|
-
jumping to an arbitrary page without walking the DAG — and standard runtime traversal
|
|
111
|
-
never touches it. Random-access readers fetch directory slices on demand: record `i`
|
|
112
|
-
lives at exactly `directory_offset + 32·i`.
|
|
113
|
-
|
|
114
|
-
## 4. Header (256 bytes, fixed)
|
|
115
|
-
|
|
116
|
-
All u64/u128 fields are naturally 8-byte aligned.
|
|
117
|
-
|
|
118
|
-
| offset | type | field | notes |
|
|
119
|
-
|-------:|-----------|--------------------------|-------|
|
|
120
|
-
| 0 | u32 | magic | `'VGEO'` = 0x4F454756 |
|
|
121
|
-
| 4 | u16 | format_version | 6 today; breaking layout changes only |
|
|
122
|
-
| 6 | u16 | min_reader_version | 6 today; reader MUST reject if its version is lower |
|
|
123
|
-
| 8 | u16 | header_size | = 256 |
|
|
124
|
-
| 10 | u8 | hash_algo | 0 = xxHash3; readers MUST reject unknown values |
|
|
125
|
-
| 11 | u8 | reserved | |
|
|
126
|
-
| 12 | u32 | feature_flags_required | readers MUST reject any unknown set bit |
|
|
127
|
-
| 16 | u32 | feature_flags_ignorable | readers MUST ignore unknown set bits; bit 0 = every page is within `nominal_page_size` (§11.14) |
|
|
128
|
-
| 20 | u32 | payload_encoding | 1 = `SHADE_MESHLET_V1` (§8); readers MUST reject unknown values |
|
|
129
|
-
| 24 | [u8; 16] | asset_id | xxh3-128 of the whole file with every **derived checksum field** zeroed (see below); stored low64 LE at bytes 0–7, high64 LE at bytes 8–15 |
|
|
130
|
-
| 40 | u32 | vertex_attribute_mask | advisory attribute presence (§8 bit table) |
|
|
131
|
-
| 44 | u32 | total_pages | = directory record count |
|
|
132
|
-
| 48 | u64 | total_clusters | ≥ 1 |
|
|
133
|
-
| 56 | u64 | total_groups | ≥ 1 |
|
|
134
|
-
| 64 | u64 | root_page_offset | = `align16(256 + 32 · level_count)` — the level table (§4.1) sits between |
|
|
135
|
-
| 72 | u32 | root_page_fetch_size | same definition as child-table/directory `fetch_size`, for page 0 |
|
|
136
|
-
| 76 | u32 | root_page_decoded_size | RAM admission for the always-resident set, known from the header alone |
|
|
137
|
-
| 80 | u64 | directory_offset | also the exact end of the frame region |
|
|
138
|
-
| 88 | u64 | dir_block_checksums_offset | `ceil(total_pages / 128)` × u64 |
|
|
139
|
-
| 96 | u32 | nominal_page_size | bytes a decoded page may come to; a **hard bound** when bit 0 of `feature_flags_ignorable` is set (§11.14), advisory otherwise |
|
|
140
|
-
| 100 | u16 | reserved | |
|
|
141
|
-
| 102 | u16 | level_count | levels `0..level_count-1`; ≤ 65,535. A `u8` through version 5, and the byte above it was reserved and written as zero — so little-endian makes this field read identically through either width for any container of 255 levels or fewer |
|
|
142
|
-
| 104 | u64 | source_vertex_count | vertices of the normalized source mesh (post-weld) the leaves represent |
|
|
143
|
-
| 112 | u64 | source_face_count | triangles of the normalized source mesh |
|
|
144
|
-
| 120 | [f32; 4] | object_sphere | object-space minimal bounding sphere (center xyz, radius) of the whole geometry |
|
|
145
|
-
| 136 | [f32; 6] | object_bounds | object-space AABB (min xyz, max xyz); instances transform at runtime |
|
|
146
|
-
| 160 | [u8; 88] | reserved | |
|
|
147
|
-
| 248 | u64 | header_checksum | xxh3-64 of bytes 0–255 with this field zeroed (`asset_id` already final) |
|
|
148
|
-
|
|
149
|
-
**`format_version` 4 moved and `min_reader_version` did not**, which is the pair working as
|
|
150
|
-
intended rather than an oversight. 4 added the parent tables (§6.3, §6.4): they occupy bytes that
|
|
151
|
-
were reserved and written as zero, and they sit between the cluster records and the payload, so
|
|
152
|
-
every offset a version-3 reader computes lands exactly where it did and such a reader reads a
|
|
153
|
-
version-4 file correctly by ignoring them. In the other direction a version-4 reader meets
|
|
154
|
-
`parent_table_offset = 0` on an older file and takes it as "this container has no upward edge",
|
|
155
|
-
which is true — so neither direction needs a version test, and nothing has to be rebuilt to stay
|
|
156
|
-
readable.
|
|
157
|
-
|
|
158
|
-
**5 moved both**, which is the pair working the other way. It put `subtree_aabb` in the cluster
|
|
159
|
-
record (§7) over the bytes `bounds_sphere` and `cone_reserved` held, so every field from offset 40
|
|
160
|
-
on shifted: a version-4 reader would take a version-5 `child_ref` out of the middle of a bounding
|
|
161
|
-
box and descend into whatever group that named. There is no ignoring it and no partial reading of
|
|
162
|
-
it, so `min_reader_version` moved with the version, which is what that field is for. A version-5
|
|
163
|
-
reader still reads version-4 records — it branches on the version it is holding — so nothing has to
|
|
164
|
-
be rebuilt to stay readable, only to be culled tightly.
|
|
165
|
-
|
|
166
|
-
**6 widened the level ordinal to `u16`** in all three places it is written down: `level_count`
|
|
167
|
-
here, the page band in the page header (§6.1), and the page band in the directory record (§9). 255
|
|
168
|
-
was a ceiling a real asset reached — the 2026-09-14 Zorah conversion refused a three-million-triangle
|
|
169
|
-
bush at exactly 255 levels with 39 clusters still unmerged, while the other 2,974 geometries of that
|
|
170
|
-
scene, one of them 27 million triangles, converged by 149 — and a ceiling an asset can touch is a
|
|
171
|
-
ceiling in the wrong place. Two of the three cost nothing: this header and the directory record both
|
|
172
|
-
had reserved bytes lying beside the field. The page header had none, and rather than grow the page
|
|
173
|
-
this spends `node_table_offset`, which only ever restated `56 + 8·group_count`. So **a page header
|
|
174
|
-
is still 56 bytes and every other field of every page ever written is still at its old offset** —
|
|
175
|
-
but bytes 4..8 and 20..24 of it now mean different things, and there is no partial reading of that,
|
|
176
|
-
so `min_reader_version` moved. A version-6 reader keeps the older page layout as it keeps the older
|
|
177
|
-
cluster layout, so again nothing has to be rebuilt to stay readable.
|
|
178
|
-
|
|
179
|
-
### 4.1 Level table (`level_count` × 32 B, immediately after the header)
|
|
180
|
-
|
|
181
|
-
Statistics per level, at `256 + 32·i`, ending where the root page frame begins. Small —
|
|
182
|
-
a hundred levels is 3.2 KB, and the deepest container yet converted is 149 — so a single
|
|
183
|
-
fetch of the file's head brings the header, the whole table, and the always-resident root
|
|
184
|
-
page together, and the table is available before any page has been decoded. That is a
|
|
185
|
-
statement about what builds produce and not a bound: `level_count` is a `u16`, and a
|
|
186
|
-
reader sizing a head fetch must take the number it finds rather than assume this one.
|
|
187
|
-
|
|
188
|
-
| offset | type | field | notes |
|
|
189
|
-
|-------:|------|-------------------|-------|
|
|
190
|
-
| 0 | f32 | min_error | smallest object-space `r_e` any cluster of this level carries |
|
|
191
|
-
| 4 | f32 | max_error | largest |
|
|
192
|
-
| 8 | f32 | max_sphere_radius | largest LOD-metric sphere radius at this level |
|
|
193
|
-
| 12 | u32 | cluster_count | ≥ 1 |
|
|
194
|
-
| 16 | u32 | group_count | groups sitting in this band; ≥ 1 |
|
|
195
|
-
| 20 | u32 | triangle_count | |
|
|
196
|
-
| 24 | u64 | reserved | |
|
|
197
|
-
|
|
198
|
-
What it is for: an instance's level range can be **bracketed** from the errors and radii
|
|
199
|
-
alone — the coarsest level that still passes the threshold at the instance's near point,
|
|
200
|
-
the finest that could be wanted at its far one — without reading a cluster record or
|
|
201
|
-
fetching a page. That is what lets a scene of ten thousand identical parts resolve to a
|
|
202
|
-
handful of distinct cuts instead of ten thousand of them, and what lets a prefetch name
|
|
203
|
-
exactly the bands a bracket spans.
|
|
204
|
-
|
|
205
|
-
These are facts about the build, not a policy. In particular **`max_error` is not monotonic
|
|
206
|
-
in the level**: a round merges an error band, and a later round can produce a cluster finer
|
|
207
|
-
than an earlier one did, by finally reaching geometry that had been waiting for a partner.
|
|
208
|
-
A reader that wants a monotonic bound takes a running maximum from the coarsest end; that
|
|
209
|
-
is the reader's choice and not the file's.
|
|
210
|
-
|
|
211
|
-
`cluster_count` and `group_count` sum to the header's `total_clusters` and `total_groups`,
|
|
212
|
-
which is the cross-check that keeps the table from drifting from the pages.
|
|
213
|
-
|
|
214
|
-
**What `asset_id` covers.** The whole file, with these fields treated as zero: `asset_id` itself,
|
|
215
|
-
`header_checksum`, every frame's `checksum` (§5), and the entire `dir_block_checksums` region (§9).
|
|
216
|
-
Excluding the last two is what breaks the circle — the frame checksums are seeded from
|
|
217
|
-
`asset_id_low64` and the directory block checksums from `asset_id_high64`, so covering them would
|
|
218
|
-
make the identity a function of itself. Nothing is lost by the exclusion: every one of those fields
|
|
219
|
-
is a function of content the hash still covers, so any change to the asset still moves `asset_id`.
|
|
220
|
-
|
|
221
|
-
Build order, and the order a verifier repeats: zero those fields → hash → write `asset_id` →
|
|
222
|
-
compute and write the frame and directory-block checksums it seeds → compute and write
|
|
223
|
-
`header_checksum` over bytes 0–255 with only that field zeroed.
|
|
224
|
-
|
|
225
|
-
The asset filename MUST be derived from `asset_id` in streamed deployments (content
|
|
226
|
-
addressing is what makes same-URL version skew impossible; every client-side integrity
|
|
227
|
-
property leans on it). The field is authoritative, the name is the enforcement.
|
|
228
|
-
|
|
229
|
-
A new `payload_encoding` (e.g. quantized positions) is a **per-asset capability gate,
|
|
230
|
-
not a format break**: old readers cleanly reject the asset instead of misreading it;
|
|
231
|
-
`format_version` moves only when the container layout itself changes.
|
|
232
|
-
|
|
233
|
-
## 5. Page frame (stored page)
|
|
234
|
-
|
|
235
|
-
Every page — root included — is stored as a self-delimiting frame:
|
|
236
|
-
|
|
237
|
-
```
|
|
238
|
-
[ FrameHeader (24 B) ] [ Child table (plaintext) ] [ blob (codec-encoded) ]
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
| offset | type | field | notes |
|
|
242
|
-
|-------:|------|------------------|-------|
|
|
243
|
-
| 0 | u32 | magic | `'VGPG'` = 0x47504756 |
|
|
244
|
-
| 4 | u8 | codec | 0 = raw, 1 = one LZ4 block, 2 = one raw DEFLATE stream; applies to the blob only; readers MUST reject unknown values |
|
|
245
|
-
| 5 | u8 | flags | 0 |
|
|
246
|
-
| 6 | u16 | child_page_count | entries in the child table; 0 for all-leaf pages |
|
|
247
|
-
| 8 | u32 | stored_size | bytes of blob (after the child table) |
|
|
248
|
-
| 12 | u32 | decoded_size | decoded-page bytes (== stored_size when raw); MUST equal the referencing child-table entry's (and the directory record's), else reject |
|
|
249
|
-
| 16 | u64 | checksum | xxh3-64, **seed = asset_id_low64 XOR page_index**, over frame bytes `[0, 16)` followed by `[24, 24 + 48·child_page_count + stored_size)` — everything but this field |
|
|
250
|
-
|
|
251
|
-
**Child table** — `child_page_count` × 48 B, plaintext (never compressed), deduplicated:
|
|
252
|
-
one entry per distinct child page this page's clusters refine into. This is what makes
|
|
253
|
-
traversal self-sufficient — an installed page needs nothing but itself to fetch every
|
|
254
|
-
page one level down — and it is plaintext for two load-bearing reasons: fetch ranges
|
|
255
|
-
are readable before (or without) blob decode, and stored sizes stay independent of
|
|
256
|
-
offset values, which keeps the build's layout computation non-circular (§10).
|
|
257
|
-
|
|
258
|
-
| offset | type | field | notes |
|
|
259
|
-
|-------:|------|---------------|-------|
|
|
260
|
-
| 0 | u64 | frame_offset | absolute; strictly greater than this page's own frame offset (§11.3) |
|
|
261
|
-
| 8 | u32 | page_index | the child page's global index (frame ordinal) — checksum seed, want dedup |
|
|
262
|
-
| 12 | u32 | fetch_size | `24 + 48·child.child_page_count + child.stored_size`, rounded up to 16 — the exact range to fetch |
|
|
263
|
-
| 16 | u32 | decoded_size | RAM budgeting before fetch; MUST equal the child frame's |
|
|
264
|
-
| 20 | u32 | reserved | |
|
|
265
|
-
| 24 | metric | metric | the child page's own metric (§6.2), copied — this is what makes a page testable *before* it is fetched; MUST equal that page's header metric (§11.11) |
|
|
266
|
-
|
|
267
|
-
The identity-seeded checksum binds the frame to *this page of this asset* and covers
|
|
268
|
-
the frame header fields, the child table, and the blob — a well-formed frame of the
|
|
269
|
-
wrong page, wrong asset, or wrong version fails verification instead of installing
|
|
270
|
-
silently. It also guards the traversal path itself: a corrupt child-table entry fetches
|
|
271
|
-
a range whose frame cannot checksum as the expected page index.
|
|
272
|
-
|
|
273
|
-
The next frame starts at `round_up(frame_offset + 24 + 48·child_page_count +
|
|
274
|
-
stored_size, 16)`. **Prefix scanning**: page indices are frame ordinals (file order ==
|
|
275
|
-
index order); scan sequentially and stop when the next aligned offset + 24 exceeds
|
|
276
|
-
`min(bytes_available, directory_offset)` — never rely on magic mismatch as a
|
|
277
|
-
terminator.
|
|
278
|
-
|
|
279
|
-
**Codecs.** `codec` is a property of one frame, not of the file: a writer picks per page
|
|
280
|
-
and an asset may carry a mixture, so a reader that handles the defined codecs handles
|
|
281
|
-
any file without asking what it was written with. That is what makes "compress unless it
|
|
282
|
-
would not shrink" expressible — the LZ4 worst case is about one byte in 255 plus a
|
|
283
|
-
token, deflate's is a stored block that is never smaller than its input, and paying
|
|
284
|
-
either to store bytes verbatim is a straight loss, so a page that does not compress is
|
|
285
|
-
written raw and says so.
|
|
286
|
-
|
|
287
|
-
A new codec id is a **per-asset capability gate, not a format break**, exactly as a new
|
|
288
|
-
`payload_encoding` is: an older reader meets a codec it has no decoder for and refuses
|
|
289
|
-
the asset instead of misreading it. `format_version` moves only when the container
|
|
290
|
-
layout itself changes, and adding a codec does not change it — `2` went in without
|
|
291
|
-
touching `format_version` or `min_reader_version`.
|
|
292
|
-
|
|
293
|
-
`1` is a **bare LZ4 block**, not an LZ4 frame, and `2` is a **bare DEFLATE stream**
|
|
294
|
-
(RFC 1951), not a zlib or gzip member. Both wrappers carry a magic, a checksum and a
|
|
295
|
-
content size that this frame already carries better, and `decoded_size` is what the blob
|
|
296
|
-
is decoded against. Both codecs are independent per blob — no match reaches outside the
|
|
297
|
-
page — so a page decodes from its own bytes with no other page involved, which is the
|
|
298
|
-
same property residency already needs.
|
|
299
|
-
|
|
300
|
-
`decoded_size` is untrusted input, and readers allocate against it before anything can
|
|
301
|
-
contradict it. It MUST therefore be checked against what `stored_size` bytes could
|
|
302
|
-
physically produce under `codec` — exactly `stored_size` for raw, at most
|
|
303
|
-
`255·stored_size + 19` for LZ4, at most `1032·stored_size` for deflate — *before* the
|
|
304
|
-
allocation. A blob that then decodes to anything other than `decoded_size` MUST be
|
|
305
|
-
rejected: a page truncated to a plausible prefix is the one corruption that reads as a
|
|
306
|
-
valid page with geometry missing.
|
|
307
|
-
|
|
308
|
-
Those ceilings are what the *format* permits, and neither compressing one is anything
|
|
309
|
-
like what a page looks like, so a length check alone is a weak guard — deflate's 1032:1 admits a 256 KB blob claiming 264
|
|
310
|
-
MB. A decoder MUST therefore bound its own output as it produces it rather than
|
|
311
|
-
comparing lengths once it is done: the LZ4 decoder is given the end of the page as its
|
|
312
|
-
write limit, and the deflate decoder stops at the first chunk that would pass it. A blob
|
|
313
|
-
that expands further than its header admits then costs one chunk rather than the whole
|
|
314
|
-
expansion.
|
|
315
|
-
|
|
316
|
-
**Which codec, and why deflate is the default.** Measured over the 44 pages of a
|
|
317
|
-
240×120 torus: LZ4 stores them at **1.295x** and deflate at **1.369x**, and decoding
|
|
318
|
-
every page costs **3.8 ms** under LZ4 against **16.9 ms** under deflate — 562 MB/s of
|
|
319
|
-
decoded output against 126. Deflate buys about seven points of ratio for four and a half
|
|
320
|
-
times the decode.
|
|
321
|
-
|
|
322
|
-
**The builder and the converter write deflate**, because the two sides of that trade are
|
|
323
|
-
not paid the same number of times: an asset is built once and fetched forever, and the
|
|
324
|
-
bytes deflate saves are saved on every fetch of every page by every client. LZ4 is the
|
|
325
|
-
deliberate setting for a container whose pages arrive while someone is looking at it —
|
|
326
|
-
there the decode is a page's *arrival* cost on the residency path, and 126 MB/s is what
|
|
327
|
-
that costs. Neither is a property of the file format: `codec` is a frame field and a
|
|
328
|
-
reader that handles the defined codecs reads either without being told.
|
|
329
|
-
|
|
330
|
-
A *default* codec is a statement about readers rather than about bytes. Under the gate
|
|
331
|
-
above, a container written with deflate opens only in a reader that carries the deflate
|
|
332
|
-
decoder, so the default is what decides which builds of this engine can open a freshly
|
|
333
|
-
converted asset. `format_version` still does not move, and an older reader still refuses
|
|
334
|
-
rather than misreads.
|
|
335
|
-
|
|
336
|
-
Deflate is also the *hosted* codec: `CompressionStream`/`DecompressionStream` are the
|
|
337
|
-
platform's own zlib, so it costs no bytes of library — and the platform offers it only
|
|
338
|
-
as a stream, which is why everything in this reader from the blob decode upwards is
|
|
339
|
-
asynchronous. Nothing in the format requires that; a synchronous inflate would read the
|
|
340
|
-
same files.
|
|
341
|
-
|
|
342
|
-
**What it is worth, measured.** LZ4 over the pages of the Stanford models comes to
|
|
343
|
-
**1.12x on the bunny, 1.18x on the dragon and 1.14x on the 27M-triangle lucy** — pages
|
|
344
|
-
are >99% of a file, so that is very nearly the file ratio. That is modest, and the reason
|
|
345
|
-
is worth writing down because it says where the next win is rather than here:
|
|
346
|
-
|
|
347
|
-
| section | share of page bytes | lz4 |
|
|
348
|
-
|---|---:|---:|
|
|
349
|
-
| page header, group, node and cluster tables | 7% | 1.6x |
|
|
350
|
-
| payload | 93% | 1.09–1.15x |
|
|
351
|
-
|
|
352
|
-
The tables compress, and there are not enough of them to matter. The payload does not,
|
|
353
|
-
because under `SHADE_MESHLET_V1` it is float32 positions and packed attribute words —
|
|
354
|
-
high-entropy at the byte level, with no repetition for an LZ matcher to find. For
|
|
355
|
-
reference, on the same bytes `gzip -9` reaches 1.22x — codec `2`'s algorithm, at a level
|
|
356
|
-
the platform API does not expose — and `xz -9` 1.63x, so this is the data resisting
|
|
357
|
-
rather than the codec failing; an entropy coder buys a little more for a lot more time. **The lever is `payload_encoding`, not
|
|
358
|
-
`codec`** — quantized positions (§12) shrink those bytes directly and leave the residue
|
|
359
|
-
far more compressible.
|
|
360
|
-
|
|
361
|
-
What the reader pays is one block decode per page installed, into the buffer the page was
|
|
362
|
-
going to be copied into anyway: ~350–500 MB/s of decoded output from the JS decoder, so
|
|
363
|
-
about 0.6 ms for a 256 KB page.
|
|
364
|
-
|
|
365
|
-
## 6. Decoded page
|
|
366
|
-
|
|
367
|
-
```
|
|
368
|
-
[ PageHeader (56 B) ] [ Group table ] [ Node table ] [ Cluster records ]
|
|
369
|
-
[ Parent-page table ] [ Group-parent table ] [ Payload ]
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
The two parent tables sit between the cluster records and the payload, and that placement is the
|
|
373
|
-
reason `min_reader_version` did not move when version 4 added them (§4): the node table's `= 56 +
|
|
374
|
-
8·group_count` stays true, every earlier offset is where it was, and both tables hold 4-byte entries
|
|
375
|
-
over sections that are already multiples of four, so §11.8's word-aligned payload survives.
|
|
376
|
-
|
|
377
|
-
PageHeader:
|
|
378
|
-
|
|
379
|
-
| offset | type | field | notes |
|
|
380
|
-
|-------:|------|-----------------------|-------|
|
|
381
|
-
| 0 | u16 | group_count | ≥ 1; group table immediately follows the header |
|
|
382
|
-
| 2 | u16 | cluster_count | ≥ 1 |
|
|
383
|
-
| 4 | u16 | min_level | page band; non-root pages span one contiguous band. A `u8` at 4 and another at 5 through version 5 |
|
|
384
|
-
| 6 | u16 | max_level | |
|
|
385
|
-
| 8 | u32 | cluster_table_offset | |
|
|
386
|
-
| 12 | u32 | payload_offset | |
|
|
387
|
-
| 16 | u32 | payload_size | |
|
|
388
|
-
| 20 | u16 | node_count | ≥ 1; ≤ 2·`group_count` (§6.1). At 6 through version 5 |
|
|
389
|
-
| 22 | u16 | reserved | what version 5 spent on `node_table_offset`, which said only `56 + 8·group_count` — the group table follows the header and the node table follows the group table, so a reader derives it. MUST be zero |
|
|
390
|
-
| 24 | u32 | parent_table_offset | start of the parent section — the parent-page table, then the group-parent table. **0 means the page carries neither**, which is what every container written before `format_version` 4 says about itself |
|
|
391
|
-
| 28 | u16 | parent_entry_count | entries in the group-parent table (§6.3) = Σ `parent_cluster_count` over this page's groups |
|
|
392
|
-
| 30 | u16 | parent_page_count | entries in the parent-page table (§6.4) |
|
|
393
|
-
| 32 | metric | metric | this page's own traversal metric (§6.2) — the root node's, repeated here so an installed page can be tested from itself |
|
|
394
|
-
|
|
395
|
-
Group table — `group_count` × 8 B. Groups are the atomic LOD/residency unit; because a
|
|
396
|
-
group never spans pages, installing/evicting a page carries whole groups by
|
|
397
|
-
construction — no global group index exists or is needed:
|
|
398
|
-
|
|
399
|
-
| offset | type | field | notes |
|
|
400
|
-
|-------:|------|----------------------|-------|
|
|
401
|
-
| 0 | u16 | cluster_first | index into this page's cluster records |
|
|
402
|
-
| 2 | u16 | cluster_count | ≥ 1; ≤ 4096 (format cap, §11.5) |
|
|
403
|
-
| 4 | u16 | parent_cluster_count | number of parent clusters referencing this group, `SAME_PAGE` references included; 0 exactly for the root page's topmost-level groups — drives activation (§10). Also the length of this group's run in the group-parent table |
|
|
404
|
-
| 6 | u16 | parent_first | index of that run's first entry (§6.3). Groups tile the table in order, exactly as `cluster_first` tiles the cluster records |
|
|
405
|
-
|
|
406
|
-
### 6.3 Group-parent table (`parent_entry_count` × 4 B)
|
|
407
|
-
|
|
408
|
-
The format's **only upward edge**, and the mirror of the child table with everything a parent does
|
|
409
|
-
not need taken out. Every entry here is the reverse of exactly one cluster's
|
|
410
|
-
`child_ref`/`child_group` pair, and §11.16 holds the two to each other.
|
|
411
|
-
|
|
412
|
-
| offset | type | field | notes |
|
|
413
|
-
|-------:|------|----------------|-------|
|
|
414
|
-
| 0 | u16 | parent_ref | index into the parent-page table (§6.4), or `SAME_PAGE` |
|
|
415
|
-
| 2 | u16 | parent_cluster | cluster index within that page |
|
|
416
|
-
|
|
417
|
-
**Four bytes an entry, against the child table's 48**, because the two directions are not
|
|
418
|
-
symmetric. A child reference carries a fetch range and a metric copy because the child may be
|
|
419
|
-
absent and the decision to fetch it is taken before it arrives. A parent of a group in the cut is
|
|
420
|
-
resident already — `VGEO_GROUP_FLAG_SELECTABLE` is exactly "every parent reference is live" — so a
|
|
421
|
-
parent reference has nothing to decide and only has to say where.
|
|
422
|
-
|
|
423
|
-
Measured across 8,899 pages of 30 converted containers: 17.4 entries a page on average and 26 at
|
|
424
|
-
the worst, so the table runs to about 70 bytes against pages of 56–256 KB, and compresses with the
|
|
425
|
-
rest of them (§5: tables are 7% of page bytes at 1.6×).
|
|
426
|
-
|
|
427
|
-
What it is *for* is a cut that is kept between frames. A cut that has descended to a group and then
|
|
428
|
-
sees the camera pull back has to draw the clusters that consumed that group instead, and without
|
|
429
|
-
this it could only find them by descending from the root again — which is the cost keeping a cut is
|
|
430
|
-
meant to remove, paid on the common case.
|
|
431
|
-
|
|
432
|
-
### 6.4 Parent-page table (`parent_page_count` × 4 B)
|
|
433
|
-
|
|
434
|
-
One `page_index u32` per **distinct other** page holding a parent of one of this page's groups,
|
|
435
|
-
deduplicated and ascending. A parent inside this page spends `SAME_PAGE` in §6.3 instead and has no
|
|
436
|
-
entry here, so a page never appears in its own table.
|
|
437
|
-
|
|
438
|
-
It lives in the decoded page rather than beside the plaintext child table because nothing needs it
|
|
439
|
-
before decode: no fetch range is computed from it, and in the blob it compresses with the other
|
|
440
|
-
tables. Measured: 4.9 entries a page on average, 18 at the worst.
|
|
441
|
-
|
|
442
|
-
### 6.1 Node table (`node_count` × 32 B)
|
|
443
|
-
|
|
444
|
-
A tree over **this page's groups**, and nothing else. It is the part of the hierarchy below page
|
|
445
|
-
granularity, and it has no paging question of its own: it arrives with the page it describes,
|
|
446
|
-
leaves with it, and can never name anything that is not there.
|
|
447
|
-
|
|
448
|
-
| offset | type | field | notes |
|
|
449
|
-
|-------:|--------|-------------|-------|
|
|
450
|
-
| 0 | metric | metric | bounds everything under this node (§6.2) |
|
|
451
|
-
| 24 | u16 | child_first | first child — a node index when interior, a group index when a leaf |
|
|
452
|
-
| 26 | u16 | child_count | 1..4 |
|
|
453
|
-
| 28 | u16 | flags | bit 0: leaf (children are groups) |
|
|
454
|
-
| 30 | u16 | reserved | |
|
|
455
|
-
|
|
456
|
-
Node 0 is the root, always. Children occupy a **contiguous run**, and the tree is laid out
|
|
457
|
-
breadth-first, so an interior node's `child_first` is strictly greater than its own index — which
|
|
458
|
-
bounds any walk without a reader having to track one. Leaves **partition** the group table: every
|
|
459
|
-
group is reached through exactly one leaf, no group twice and none not at all. A page with a single
|
|
460
|
-
group still has a tree: one leaf node covering it, so a reader always starts at node 0 and never
|
|
461
|
-
special-cases.
|
|
462
|
-
|
|
463
|
-
Cost, measured across sphere assets from 3k to 98k triangles at page sizes from 8 KB to 256 KB: the
|
|
464
|
-
node table plus every metric copy runs **0.3%–1.6% of asset size**, and trees come out one to three
|
|
465
|
-
deep.
|
|
466
|
-
|
|
467
|
-
### 6.2 Traversal metric (24 B)
|
|
468
|
-
|
|
469
|
-
The same three fields wherever they appear — a child-table entry (§5), a page header (§6), a node
|
|
470
|
-
(§6.1):
|
|
471
|
-
|
|
472
|
-
| offset | type | field | notes |
|
|
473
|
-
|-------:|----------|------------------|-------|
|
|
474
|
-
| 0 | [f32; 4] | sphere | contains every bound anything covered carries |
|
|
475
|
-
| 16 | f32 | min_self_error | finest error covered |
|
|
476
|
-
| 20 | f32 | max_parent_error | coarsest parent error covered; `+∞` where the root is covered |
|
|
477
|
-
|
|
478
|
-
One sphere serves both the spatial test and the projection. It is the union of the covered clusters'
|
|
479
|
-
`parent_sphere`s, and monotonicity does the rest: `parent_sphere` contains `self_sphere`, which
|
|
480
|
-
contains the cluster's own vertices, so nothing under the node escapes it.
|
|
481
|
-
|
|
482
|
-
`max_parent_error` is the load-bearing number. A cluster is drawn only while `project(parent) > τ`,
|
|
483
|
-
so if the *largest* parent error under a node still projects to no more than τ, nothing under it can
|
|
484
|
-
be drawn and the entire subtree is skippable. `min_self_error` answers the other question — whether
|
|
485
|
-
anything here is drawable at all, or whether this is only a waypoint on the way to something finer —
|
|
486
|
-
which is what a prefetch ranks its wants by.
|
|
487
|
-
|
|
488
|
-
A group is the natural unit for all of this and needs no new geometry: every cluster of a group is
|
|
489
|
-
consumed by one step and therefore shares that step's `parent_sphere` and `parent_error`, which is
|
|
490
|
-
the same fact that makes the group the atomic LOD decision.
|
|
491
|
-
|
|
492
|
-
**The copy in a child-table entry is the point of the whole thing.** It is what a traversal reads
|
|
493
|
-
*before* fetching a page, and without it the only way to learn a page was not wanted is to fetch and
|
|
494
|
-
decode it. Being a copy, it can drift; §11.11 requires it to match the page's own.
|
|
495
|
-
|
|
496
|
-
## 7. Cluster record (112 bytes)
|
|
497
|
-
|
|
498
|
-
Everything the runtime cut rule, culling, wants, and upload need — denormalized per
|
|
499
|
-
cluster so selection reads one record. Three kinds of bounds, deliberately distinct: the
|
|
500
|
-
**LOD-metric pair** (`self_sphere`/`parent_sphere` — shared per group step so all
|
|
501
|
-
siblings project identical values, monotonically inflated, never a tight fit), the
|
|
502
|
-
**cluster's own box** (`aabb` — its tight fit, the meshlet metadata's `bounds_box`, culled
|
|
503
|
-
downstream by the HZB), and the **subtree box** (`subtree_aabb` — this cluster's geometry and
|
|
504
|
-
everything reachable below it, and the only one of the three the cut's per-cluster frustum test
|
|
505
|
-
can use, because culling a cluster takes its whole child group out of the descent):
|
|
506
|
-
|
|
507
|
-
| offset | type | field | notes |
|
|
508
|
-
|-------:|----------|-----------------|-------|
|
|
509
|
-
| 0 | [f32; 4] | self_sphere | LOD-metric sphere (center xyz + radius) of the group step that created this cluster; leaves: equals `bounds_sphere` |
|
|
510
|
-
| 16 | f32 | self_error | object-space `r_e`; leaves: 0 |
|
|
511
|
-
| 20 | f32 | parent_error | error of the step that consumed it; roots: +∞ |
|
|
512
|
-
| 24 | [f32; 4] | parent_sphere | LOD-metric sphere of the consuming step; roots: duplicate of self_sphere |
|
|
513
|
-
| 40 | [f32; 6] | subtree_aabb | object-space AABB of this cluster's vertices **and of every cluster reachable below it** through child groups (§11.17); leaves: equals `aabb`. What the runtime's per-cluster frustum cull reads |
|
|
514
|
-
| 64 | [f32; 6] | aabb | this cluster's own object-space AABB — GPU metadata `bounds_box`, the downstream culls, and the (future) dequantization grid |
|
|
515
|
-
| 88 | u16 | child_ref | index into this frame's child table; `SAME_PAGE` when the child group is in this page; `NO_CHILD` for leaves |
|
|
516
|
-
| 90 | u16 | child_group | group index within the child page (or this page, for `SAME_PAGE`); `NO_GROUP` for leaves |
|
|
517
|
-
| 92 | u32 | encoding_flags | frozen bit table in §8 |
|
|
518
|
-
| 96 | u32 | payload_offset | into this page's payload section; ≡ 0 (mod 4) |
|
|
519
|
-
| 100 | u32 | payload_size | bytes; ≡ 0 (mod 4) |
|
|
520
|
-
| 104 | u8 | vertex_count | 3 ≤ n ≤ 128 |
|
|
521
|
-
| 105 | u8 | triangle_count | 1 ≤ n ≤ 128 |
|
|
522
|
-
| 106 | [u8; 6] | reserved | **write 0** |
|
|
523
|
-
|
|
524
|
-
**What version 5 spent to fit `subtree_aabb` in the same 112 bytes.** `bounds_sphere`, the cluster's
|
|
525
|
-
own miniball, which no runtime ever read — the cut tests the LOD sphere, the HZB and the small-primitive
|
|
526
|
-
cull test `bounds_box`, and the builder keeps its own copy of the sphere for the object bounds and the
|
|
527
|
-
group centroids. And `cone_reserved`, four bytes held for a backface-cone cull that is **declined
|
|
528
|
-
outright** (design §9, plan §5): the earlier note kept it because reclaiming four bytes was not worth
|
|
529
|
-
moving `format_version`, and moving it for twenty-four was.
|
|
530
|
-
|
|
531
|
-
Monotonicity (`parent_error ≥ self_error`, `parent_sphere ⊇ self_sphere`) is a build
|
|
532
|
-
invariant, not a reader concern. A single-cluster asset is valid: one root page, one
|
|
533
|
-
group with `parent_cluster_count = 0`, one cluster with `parent_error = +∞`,
|
|
534
|
-
`child_ref = NO_CHILD`, `child_page_count = 0`.
|
|
535
|
-
|
|
536
|
-
## 8. Payload encoding
|
|
537
|
-
|
|
538
|
-
`payload_encoding = 1` (`SHADE_MESHLET_V1`). **Byte-compat scope**: the payload block is
|
|
539
|
-
byte-compatible with `GPUMeshletManager`'s **data pool** — uploading it is a copy. The
|
|
540
|
-
40-byte GPU metadata rows (`MESHLET_METADATA_STRUCT`: bounds_box, address,
|
|
541
|
-
primitive_count, vertex_count, flags) are **synthesized at install** from the cluster
|
|
542
|
-
record (aabb → bounds_box, counts widened to u32, `address = slot_base_word +
|
|
543
|
-
payload_offset/4`, flags = encoding_flags) — a small pack step that is part of the
|
|
544
|
-
design's "VRAM paging mechanism is new work", not an existing patch path.
|
|
545
|
-
|
|
546
|
-
Payload block layout:
|
|
547
|
-
|
|
548
|
-
1. Index section: `triangle_count × 3` local u8 indices, zero-padded to a 4-byte word
|
|
549
|
-
boundary.
|
|
550
|
-
2. Attribute sections, in the frozen order below, each `words × vertex_count` u32
|
|
551
|
-
words — unless its `encoding_flags` bit marks it constant-compressed, in which case
|
|
552
|
-
exactly `words` u32s are stored once.
|
|
553
|
-
|
|
554
|
-
Frozen tables (byte-identical to Shade's `ENCODED_ORDERED_ATTRIBUTES`,
|
|
555
|
-
`ENCODED_ATTRIBUTE_SIZES`, `MESHLET_METADATA_FLAGS` as of this writing — the spec, not
|
|
556
|
-
the source, is now the authority for `.vgeo` files):
|
|
557
|
-
|
|
558
|
-
| # | attribute | words/vertex | encoding | flags bit (constant-compressed) | mask bit |
|
|
559
|
-
|---|-----------|-------------:|----------------------------|-------------------------------:|---------:|
|
|
560
|
-
| 0 | position | 3 | raw f32×3 | — (never compressed) | 0 |
|
|
561
|
-
| 1 | normal | 1 | octahedral u32 | 1 (0x01) | 1 |
|
|
562
|
-
| 2 | tangent | 1 | packed u32 | 2 (0x02) | 2 |
|
|
563
|
-
| 3 | color | 1 | RGBA8 u32 | 4 (0x04) | 3 |
|
|
564
|
-
| 4 | uv0 | 2 | raw f32×2 | 8 (0x08) | 4 |
|
|
565
|
-
| 5 | uv1 | 1 | 2×unorm16 u32 | 16 (0x10) | 5 |
|
|
566
|
-
|
|
567
|
-
- `encoding_flags` bits 5 (0x20, skinning joints) and 6 (0x40, skinning weights) MUST
|
|
568
|
-
be clear, and **no skinning sections are stored** — `SHADE_MESHLET_V1` payloads end
|
|
569
|
-
at uv1. (Load-bearing note: skinning attributes are last in Shade's attribute order
|
|
570
|
-
and the static decode path never addresses them, which is exactly what makes their
|
|
571
|
-
omission safe. VG is static-only, design §9.)
|
|
572
|
-
- All other `encoding_flags` bits are reserved, MUST be 0.
|
|
573
|
-
- An attribute the source lacks still ships its section, constant-compressed to the
|
|
574
|
-
engine default — **sections are never omitted**; `vertex_attribute_mask` is advisory
|
|
575
|
-
(which attributes carry real data). True section omission would shift every following
|
|
576
|
-
offset and is only expressible as a new `payload_encoding` id.
|
|
577
|
-
|
|
578
|
-
Quantized positions land as a **new `payload_encoding` id** (plus a page codec if
|
|
579
|
-
entropy coding pays). The cluster record already carries the dequantization grid
|
|
580
|
-
(`aabb`).
|
|
581
|
-
|
|
582
|
-
## 9. Page directory (auxiliary)
|
|
583
|
-
|
|
584
|
-
Flat array of `total_pages` 32 B records at `directory_offset`; record `i` describes
|
|
585
|
-
page index `i` (page index order == file order == coarse-first). Auxiliary and fully
|
|
586
|
-
derivable from the frames (§3) — standard runtime traversal never reads it.
|
|
587
|
-
|
|
588
|
-
| offset | type | field | notes |
|
|
589
|
-
|-------:|------|----------------|-------|
|
|
590
|
-
| 0 | u64 | frame_offset | absolute; frame per §5 |
|
|
591
|
-
| 8 | u32 | fetch_size | as in the child table: the exact byte range to fetch is `[frame_offset, frame_offset + fetch_size)` |
|
|
592
|
-
| 12 | u32 | decoded_size | MUST equal the frame's |
|
|
593
|
-
| 16 | u16 | min_level | a `u8` here and another at 17 through version 5, which is what moved the three fields below by two — into bytes this record has always held in reserve |
|
|
594
|
-
| 18 | u16 | max_level | |
|
|
595
|
-
| 20 | u16 | flags | bit 0: root page |
|
|
596
|
-
| 22 | u16 | group_count | |
|
|
597
|
-
| 24 | u16 | cluster_count | |
|
|
598
|
-
| 26 | [u8; 6] | reserved | |
|
|
599
|
-
|
|
600
|
-
**Directory integrity**: `dir_block_checksums` holds one xxh3-64 per 128 directory
|
|
601
|
-
records (one 4 KB block), seed = `asset_id_high64 XOR block_index`. Readers that use
|
|
602
|
-
the directory fetch it at block granularity and MUST verify.
|
|
603
|
-
|
|
604
|
-
## 10. Reader requirements
|
|
605
|
-
|
|
606
|
-
Standard runtime operation is **pure traversal** — the directory is not involved. Every item
|
|
607
|
-
below is marked with what `format/read/` does about it: *(shipped)*, *(open)*, or a split where
|
|
608
|
-
the mechanics are there and the policy is the caller's. Where a requirement is *policy*, the
|
|
609
|
-
implementation is `renderer/geometry/virtual/residency/` rather than `format/read/` — a sibling of
|
|
610
|
-
the format, not part of it, because what may be resident and what may be in flight are questions
|
|
611
|
-
about a scene and not about a container.
|
|
612
|
-
|
|
613
|
-
- **Cold start** *(shipped)*: fetch `[0, 256)`, verify `header_checksum`, magic, versions (§4 reject
|
|
614
|
-
rules), then fetch `[root_page_offset, root_page_offset + root_page_fetch_size)` — the
|
|
615
|
-
level table (§4.1) sits between the two, so a head prefix covering both brings the whole
|
|
616
|
-
always-resident set in one request. The root frame's `decoded_size` MUST equal
|
|
617
|
-
`root_page_decoded_size` and its frame extent MUST match `root_page_fetch_size`, else
|
|
618
|
-
reject. Or speculatively fetch one larger head prefix — coarse-first layout makes every
|
|
619
|
-
prefix byte useful.
|
|
620
|
-
|
|
621
|
-
**How much of that prefix to keep, and for how long, is not a free choice at scale.** The
|
|
622
|
-
always-resident set is small: invariant §11.7 puts exactly one root cluster in the file and the
|
|
623
|
-
assembler gives the root page the complete topmost level, so a measured root page is one cluster —
|
|
624
|
-
1,732 B and 1,472 B on two torus builds, header and level table included coming to about 2.4 KB.
|
|
625
|
-
A prefix past that is speculation about the *next* band, and at the 64 KiB default page size it
|
|
626
|
-
buys about one page. `VGeoReadOptions.head_prefix_bytes` therefore defaults to 64 KiB rather
|
|
627
|
-
than the 256 KiB it did, and the reader **releases the prefix** the first time a read falls
|
|
628
|
-
outside it (`VGeoContainerReader.release_head`, and `head_bytes` to see it). Measured, at the old
|
|
629
|
-
default a 256 KiB prefix over a 22 MB container covered 105 of its 4,000 pages and served nothing
|
|
630
|
-
after those installed — while sitting outside `resident_bytes` and outside anything `evict` could
|
|
631
|
-
reach, which across a thousand open assets was a quarter of a gigabyte no budget could see.
|
|
632
|
-
- **Refinement** *(shipped)*: a want is a `child_ref` in an installed page; its child-table entry
|
|
633
|
-
carries the exact byte range — one round-trip per cold page, no metadata fetch ever.
|
|
634
|
-
Wants dedup on `page_index` across clusters, groups, and instances. A want whose
|
|
635
|
-
target page is **already installed** resolves as an activation recheck of the
|
|
636
|
-
referenced group (below), never a fetch.
|
|
637
|
-
|
|
638
|
-
A want that goes stale mid-flight is cancellable: `VGeoByteSource.read` takes a per-read
|
|
639
|
-
`AbortSignal`, `vgeo_fetch_byte_source` passes it to `fetch` alongside the source's own, and
|
|
640
|
-
`VGeoContainerReader.cancel` abandons one page's request without touching the others.
|
|
641
|
-
**What a stale want does, decided:** it is aborted while it can still be aborted, and installed
|
|
642
|
-
once it cannot — the bytes are already paid for, and refusing to keep them costs the fetch *and*
|
|
643
|
-
the page. An arrival nobody wanted enters residency untouched, which under LRU on last-touched
|
|
644
|
-
makes it the first eviction candidate and free if the camera comes back.
|
|
645
|
-
- **Activation (closure without dependency lists)** *(shipped)* — the selectable invariant:
|
|
646
|
-
|
|
647
|
-
*A group `g` is selectable iff every one of its `parent_cluster_count` references is
|
|
648
|
-
live — the referencing cluster's page is installed and that cluster's own group is
|
|
649
|
-
selectable. Groups with `parent_cluster_count = 0` (exactly the root page's topmost
|
|
650
|
-
level) are selectable at install.*
|
|
651
|
-
|
|
652
|
-
By induction, a selectable cluster's entire ancestor cone is installed and
|
|
653
|
-
selectable — the closure the cut rule needs, restored by a per-group counter instead
|
|
654
|
-
of dependency lists. Runtime bookkeeping: each resident group keeps a live-reference
|
|
655
|
-
counter; when a group becomes selectable, its clusters' references (child-table and
|
|
656
|
-
`SAME_PAGE` alike) increment the referenced groups' counters — cascading
|
|
657
|
-
transitively; installing a page counts the existing live references into it (the
|
|
658
|
-
runtime maintains a reverse-reference index for pending pages — the same structure
|
|
659
|
-
that carries wants). Counters only grow while the pages involved stay resident (the
|
|
660
|
-
eviction rule below guarantees referencing parents outlive children). Activation
|
|
661
|
-
flips whole groups; the cut rule's clamp reads *selectable*, not merely resident
|
|
662
|
-
(design §4/§6): resident-but-inactive groups draw nothing, their coarser selectable
|
|
663
|
-
ancestors keep covering that surface — **no holes and no double-draw, in any arrival
|
|
664
|
-
order**, because a group never draws before all pages holding its parents are
|
|
665
|
-
installed and selectable.
|
|
666
|
-
- **In-flight protection** *(open)*: a fetch discovered through parent page P pins P until the
|
|
667
|
-
child installs — known at queue time, purely local. Arrivals verify the
|
|
668
|
-
identity-seeded frame checksum; activation makes any arrival order safe beyond that.
|
|
669
|
-
The reader dedups in-flight fetches by page index but pins nothing.
|
|
670
|
-
Still open, and the blocker named here is gone. It was that a `VGeoPageWant` carried the page it
|
|
671
|
-
named and not the page it was *discovered through*, so neither the reader nor the manager could
|
|
672
|
-
identify P — the format stores parent counts rather than parent references, deliberately, so
|
|
673
|
-
nothing below the cut can name one. The cut says it: a want record is
|
|
674
|
-
`(source_page_row, child_ref, child_page_index, priority)` and the source row is P. Nothing pins
|
|
675
|
-
anything yet, and that is the whole of what remains. It is an efficiency item and not a
|
|
676
|
-
correctness one — evicting P while its child is on the wire wastes the fetch, because the child
|
|
677
|
-
installs non-selectable and activation is safe at every arrival order.
|
|
678
|
-
- **Eviction** *(mechanics shipped in the reader, policy shipped above it)*: within each residency tier, a page is
|
|
679
|
-
evictable only while it has no resident child pages in that tier — checked against its own
|
|
680
|
-
child table (`SAME_PAGE` references never pin). Leaves-first order emerges from the rule;
|
|
681
|
-
the root page is pinned. `VGeoContainerReader.evict` does the dropping and the activation
|
|
682
|
-
retraction that goes with it — deliberately including the case where a caller evicts against
|
|
683
|
-
this rule, where the cascade is what stops an orphaned child page from staying drawable — but
|
|
684
|
-
it chooses nothing: which page to drop, and when, is the budget owner's.
|
|
685
|
-
`VGeoContainerReader.resident_child_count` is the lookup the rule is written in terms of, and it is
|
|
686
|
-
the whole of what the reader contributes.
|
|
687
|
-
- **The budget owner** *(shipped, above the reader)*: `VGeoResidencyManager`. Readers register with
|
|
688
|
-
one; it holds the RAM tier's byte budget across every registered reader, admits a want against it
|
|
689
|
-
*before* the fetch out of the `decoded_size` the child table already carries, and evicts on the
|
|
690
|
-
rule above ordered by design §6's LRU on **last-touched** with a protect window no shorter than
|
|
691
|
-
feedback latency. It reaches nothing private: it is written entirely against the reader's public
|
|
692
|
-
surface, which is the test that the split is real rather than nominal.
|
|
693
|
-
|
|
694
|
-
A shared **fetch scheduler** comes with it, and has to: `VGeoReadOptions.max_concurrent_fetches`
|
|
695
|
-
bounds one reader and cannot do better, and four readers at that cap were measured issuing sixteen
|
|
696
|
-
concurrent range requests, eight readers at the default 6 issuing 48. `VGeoResidencyManager`
|
|
697
|
-
holds one total across every reader in a single `ConcurrencyGate` (`core/process/`), as one
|
|
698
|
-
priority queue — so a want's priority, which is pixels of projected error and therefore
|
|
699
|
-
comparable across assets by construction, is compared globally rather than within whichever
|
|
700
|
-
reader happened to ask first.
|
|
701
|
-
|
|
702
|
-
**Most of that shape already exists in this engine twice, and the bullet is more useful read as
|
|
703
|
-
the third one than as new work.** `renderer/texture/virtual/VTPhysicalCache` keeps a
|
|
704
|
-
`#last_touch` stamp per slot, a lock flag for slots that may never go, and
|
|
705
|
-
`find_eviction_candidate(current_frame, protect_window)` — LRU on last-touch under a protect
|
|
706
|
-
window justified by feedback latency, which is the rule above. Design §6 said so when it pinned
|
|
707
|
-
that rule ("the VT system already keys on touch for this reason"), so the resemblance is
|
|
708
|
-
convergent by instruction rather than accidental. `engine/asset/AssetManager` caps concurrent
|
|
709
|
-
top-level loads on `load_concurrency` for the reason the gate holds a total, and inverts
|
|
710
|
-
priority into a `BinaryHeap` the same way in `get_pending_asset_priority_score` — being the
|
|
711
|
-
third place to want that, and the second to write that inversion, is why the gate is now
|
|
712
|
-
`core/process/ConcurrencyGate` rather than a file in this directory, and `AssetManager` keeps
|
|
713
|
-
its own queue anyway for the reasons its `load_concurrency` doc records.
|
|
714
|
-
`VirtualTextureManager` caps `max_concurrent_loads` at 32 and drops queued requests untouched
|
|
715
|
-
for `queue_stale_window` frames, which is a stale-want policy of the kind `cancel` answers here.
|
|
716
|
-
|
|
717
|
-
**The narrow claim that survives that** is a *byte* budget across *several* readers, admitted
|
|
718
|
-
against before the fetch: `VTPhysicalCache` budgets a count of fixed-size slots and
|
|
719
|
-
`AssetManager` budgets nothing and evicts nothing. The retry-into-quarantine and the per-fetch
|
|
720
|
-
`AbortSignal` above are likewise new here — `AssetManager` records a failure and lets a later
|
|
721
|
-
`get` retry, and neither precedent can abort one request. Granularity is why the pages are not
|
|
722
|
-
simply `AssetManager` entries: an `AssetDescription` is a path and a type, where a page is a
|
|
723
|
-
byte range in a container the format gives no global id to (a cluster is a page plus an index),
|
|
724
|
-
at the 4,000 and 20,000 pages per container measured above and a thousand assets open at once.
|
|
725
|
-
- *(shipped)* Verify every ranged response is `206` with the requested `Content-Range`; a `200`
|
|
726
|
-
means the server ignored the range — abort the transfer immediately (design §6).
|
|
727
|
-
`vgeo_fetch_byte_source` checks the status *and* the returned range: a proxy can answer 206
|
|
728
|
-
with a range other than the one asked for.
|
|
729
|
-
- *(shipped)* Verify frame checksums (identity-seeded, §5) before decode, and `decoded_size`
|
|
730
|
-
against the child-table entry used to fetch and against the codec's decode bound
|
|
731
|
-
(§5) before allocating for it. On mismatch: bounded retries with
|
|
732
|
-
backoff, then **quarantine** the page — the clamped cut keeps a coarse stand-in
|
|
733
|
-
correct indefinitely — and surface telemetry. Never retry-loop: corruption at a
|
|
734
|
-
content-addressed URL is deterministic.
|
|
735
|
-
The decode bound and the exact-length decode are unconditional. The frame checksum is
|
|
736
|
-
`VGeoReadOptions.verify_frames`, off by default because a tool reading a file it just built is not
|
|
737
|
-
defending against a network — a runtime registering a reader with `VGeoResidencyManager` should
|
|
738
|
-
turn it on, and it is what lets the retries see corruption rather than only outages.
|
|
739
|
-
Retry and backoff are `fetch_retries` (2) and `fetch_retry_backoff_ms` (50, doubling); after them
|
|
740
|
-
the page enters `VGeoContainerReader.quarantine()` with its reason and is never fetched again, and
|
|
741
|
-
`install_wants` skips it rather than failing the batch — one bad page out of a thousand assets'
|
|
742
|
-
worth costs exactly that page. *When* to lift a quarantine is policy and is not the reader's:
|
|
743
|
-
a checksum that failed at a content-addressed URL will fail again, while a run of 503s from one
|
|
744
|
-
edge node is a different thing that a manager watching other assets arrive can tell apart, so
|
|
745
|
-
`clear_quarantine` exists and `VGeoResidencyManager.clear_quarantines` is who calls it.
|
|
746
|
-
- *(open)* After install, stamp each cluster's selection row with its own page index and its
|
|
747
|
-
child page index/group (touch and want emission, design §6/§7). There is no selection buffer
|
|
748
|
-
yet; `VGeoPage` keeps a page's tables and the traversal reads them directly.
|
|
749
|
-
- *(open)* Fetch batching: coalesce adjacent wanted pages into contiguous single ranges; ranges
|
|
750
|
-
never cross a band boundary by construction (coarse-first ordering), so coalescing
|
|
751
|
-
lives within bands — the within-band spatial curve (§12) is what carries fetch
|
|
752
|
-
locality and must be chosen with that as a criterion. Fetching through small unwanted
|
|
753
|
-
gaps is permitted under an implementation waste threshold.
|
|
754
|
-
- *(shipped)* Take a page's *actual* size from child tables (traversal), frames (prefix scan), or
|
|
755
|
-
the directory (special tasks) — never from `nominal_page_size`, which is an upper bound and not a
|
|
756
|
-
size. The bound itself may be relied on, and only when bit 0 of `feature_flags_ignorable` is set:
|
|
757
|
-
then no page exceeds it (§11.14) and a reader may size fixed-stride page slots against it. A
|
|
758
|
-
reader that depends on that MUST check the bit and refuse a container without it — containers
|
|
759
|
-
written before the bound existed carry a `nominal_page_size` that pages ran over.
|
|
760
|
-
- *(shipped)* **Nothing a runtime holds may be able to read the whole file.** The whole-file audit
|
|
761
|
-
(§11, every checksum, the directory against a frame scan) and the install-every-page sweep are in
|
|
762
|
-
`format/read/tool/` as `vgeo_audit_container` and `vgeo_install_all_pages`, not on the reader —
|
|
763
|
-
`VGeoContainerReader.validate` and `.install_all_pages` remain as deprecated shims for the
|
|
764
|
-
playground and the specs, and they reach the tools through a dynamic import so that a bundle that
|
|
765
|
-
only opens containers does not carry the validator at all. The audit takes the container as one
|
|
766
|
-
`ArrayBuffer`, because every §11 invariant is a cross-check between two places in the file; that is
|
|
767
|
-
a ceiling and not a slope, so it refuses above `VGEO_MAX_AUDIT_BYTES` (1 GiB) naming the size,
|
|
768
|
-
rather than failing in the allocator. Measured: a 514 MiB container audited in 19.4 s, and
|
|
769
|
-
installing every page of a 2,000-page container of 544 KB pages made 514 MiB resident in 210 ms.
|
|
770
|
-
- *(shipped)* The reverse-reference index of the activation rule is bounded by the **residency
|
|
771
|
-
frontier**, not by how many pages a session has visited, and it is worth knowing which because it
|
|
772
|
-
has the shape of an index that leaks. An entry exists only while a resident, selectable cluster
|
|
773
|
-
refers into an absent page, and install/activation and evict/deactivation are symmetric. Measured
|
|
774
|
-
on a 20,000-page container with the 3,280 pages of its eight coarse bands pinned resident:
|
|
775
|
-
`pending_reference_page_count` was 6,561 after pinning and still 6,561 after touring all 6,561
|
|
776
|
-
pages of the band below them a window at a time and evicting each window. Dropping the pinned
|
|
777
|
-
pages too leaves 3.
|
|
778
|
-
|
|
779
|
-
Build consequence (accepted): child tables hold downstream offsets — the format is
|
|
780
|
-
optimized for reading, and the builder pays for it. Preferred strategy: emit frames
|
|
781
|
-
with placeholder child tables, then **backpatch** them once all offsets are known, and
|
|
782
|
-
finalize frame checksums last (the checksum covers the child table). Computing the full
|
|
783
|
-
layout up front (compress all blobs → prefix-sum offsets → one forward emit) is equally
|
|
784
|
-
valid. Both are cheap and non-circular **because child tables are plaintext in the
|
|
785
|
-
frame**: a frame's extent (`24 + 48·children + stored_size`, with `stored_size` the
|
|
786
|
-
blob's compressed size) never depends on the offset values, and patching plaintext
|
|
787
|
-
bytes needs no re-compression. Build times are non-interactive by expectation —
|
|
788
|
-
clustering and simplification dominate regardless.
|
|
789
|
-
|
|
790
|
-
## 11. Build invariants (validated at build, assumed by readers)
|
|
791
|
-
|
|
792
|
-
1. A group's clusters are contiguous in one page (`cluster_first/cluster_count`); a
|
|
793
|
-
group never spans pages. Non-root pages span a single contiguous level band.
|
|
794
|
-
2. Child tables are exact: deduplicated, one entry per distinct **other** page
|
|
795
|
-
referenced by this page's clusters (`SAME_PAGE` references appear in no child
|
|
796
|
-
table); every `child_ref`/`child_group` resolves to a valid entry and group;
|
|
797
|
-
`frame_offset`/`fetch_size`/`decoded_size` agree with the referenced frame and the
|
|
798
|
-
directory (all referencing pages agree by construction).
|
|
799
|
-
3. Acyclicity and reachability: every child-table `frame_offset` is strictly greater
|
|
800
|
-
than the owning page's frame offset (file order is topological), and every page is
|
|
801
|
-
reachable from the root page through child tables.
|
|
802
|
-
4. File order (== page index order) is coarse-first: the root page first, bands by
|
|
803
|
-
descending level, spatial locality within a band. The root page contains the
|
|
804
|
-
complete topmost level(s) — at minimum a complete drawable cut of the DAG. A
|
|
805
|
-
cluster's child group is always in a page whose band tops out strictly below its own
|
|
806
|
-
page's, unless the reference is `SAME_PAGE`.
|
|
807
|
-
5. Group size: `1 ≤ cluster_count ≤ 4096` (format cap; builders target far less —
|
|
808
|
-
design §3 grouping is adaptive from ~4). `parent_cluster_count` equals the exact
|
|
809
|
-
number of clusters (across all pages, `SAME_PAGE` included) whose
|
|
810
|
-
`child_ref/child_group` name this group; it is 0 exactly for the root page's
|
|
811
|
-
topmost-level groups.
|
|
812
|
-
6. `1 ≤ triangle_count ≤ 128`, `3 ≤ vertex_count ≤ 128` (Shade meshlet limits);
|
|
813
|
-
`total_clusters ≥ 1`; `source_vertex_count ≥ 3`, `source_face_count ≥ 1`.
|
|
814
|
-
7. Error/sphere monotonicity per design §4; `self_error = 0` and `child_ref = NO_CHILD`
|
|
815
|
-
exactly for leaves; `parent_error = +∞` exactly for **the** root — there is exactly
|
|
816
|
-
one, and it is in page 0. Several parentless clusters would be several entry points,
|
|
817
|
-
and the cut only ever enters at one; it is also what an unconverged build leaves
|
|
818
|
-
behind, which is the likelier cause. All floats NaN-free.
|
|
819
|
-
`self_sphere`, `aabb` and `subtree_aabb` contain the cluster's decoded vertices; the header's
|
|
820
|
-
`object_sphere` and `object_bounds` contain the normalized source mesh. The `self_sphere` half is
|
|
821
|
-
what makes the LOD sphere usable as a conservative bound at all — the runtime's first frustum test
|
|
822
|
-
is on it, and a reader meeting a pre-5 container stands its cube in for `subtree_aabb`.
|
|
823
|
-
8. Payload offsets and sizes are 4-byte multiples; every payload decodes/uploads
|
|
824
|
-
against `payload_encoding` with no external state beyond its own cluster record.
|
|
825
|
-
A leaf's `self_error` of 0 is only as honest as the simplifier's metric — see design
|
|
826
|
-
§2, boundary constraint quadrics — and no reader-side check can distinguish an
|
|
827
|
-
exact coarse cluster from one whose error went unrecorded.
|
|
828
|
-
9. `asset_id` and `header_checksum` verify per §4 — including its zeroing rule, which excludes
|
|
829
|
-
every derived checksum field; every frame verifies per §5; the directory verifies per §9 and
|
|
830
|
-
byte-for-byte matches what a frame scan reconstructs. `header_checksum` covers bytes 0–255
|
|
831
|
-
only, so the level table's integrity rests on `asset_id`, which covers the whole file.
|
|
832
|
-
10. Every page's node table is a tree over exactly that page's groups: node 0 is the root, an
|
|
833
|
-
interior node's children are a contiguous run at a strictly greater index, `1 ≤ child_count ≤ 4`,
|
|
834
|
-
and the leaves partition the group table — each group reached through exactly one leaf. Each
|
|
835
|
-
node's metric bounds its whole subtree: its sphere contains every child's, its `min_self_error`
|
|
836
|
-
is no greater than any child's, its `max_parent_error` no less. The page header's metric equals
|
|
837
|
-
the root node's. The table begins at `56 + 8·group_count` — derived, because the page header
|
|
838
|
-
carries no offset for it since version 6 — and the `u16` at 22 that its offset used to occupy is
|
|
839
|
-
zero.
|
|
840
|
-
11. Every child-table entry's metric equals the header metric of the page it names. It is a copy
|
|
841
|
-
read before that page is fetched, so nothing else can catch it having drifted — and a traversal
|
|
842
|
-
that trusts a stale one skips a page it needed and leaves a hole.
|
|
843
|
-
12. Every frame names a defined `codec` (§5); a raw frame's `stored_size` equals its
|
|
844
|
-
`decoded_size`; and no frame's `decoded_size` exceeds what its codec could produce from
|
|
845
|
-
`stored_size` bytes. Every blob decodes to exactly `decoded_size` — neither short nor long. A
|
|
846
|
-
short decode is a truncated page, and a page truncated to a plausible prefix is the one
|
|
847
|
-
corruption that reads as valid geometry with pieces missing; a long one is a blob that would
|
|
848
|
-
write past what the reader allocated from `decoded_size`, and is stopped while it is producing
|
|
849
|
-
rather than measured afterwards.
|
|
850
|
-
13. `root_page_offset = align16(256 + 32 · level_count)`; the level table's `cluster_count` and
|
|
851
|
-
`group_count` columns sum to `total_clusters` and `total_groups`; every level holds at least
|
|
852
|
-
one cluster and one group; `min_error ≤ max_error`, both finite and ≥ 0. `level_count ≥ 1`,
|
|
853
|
-
and no page's `max_level` reaches it.
|
|
854
|
-
14. **Page size is a bound.** When bit 0 of `feature_flags_ignorable` is set, every page's
|
|
855
|
-
`decoded_size` is ≤ `nominal_page_size`, and `nominal_page_size` is non-zero. The bit is what a
|
|
856
|
-
reader checks; unset, `nominal_page_size` is the advisory build parameter it used to be and
|
|
857
|
-
nothing here applies.
|
|
858
|
-
|
|
859
|
-
The bound is what makes a page slot a fixed stride in VRAM — a slot address is a multiply, with
|
|
860
|
-
no allocator, no growth path and no address migration behind it (design §6). It costs the
|
|
861
|
-
internal fragmentation of each band's last page, measured at about 9% of a page across a
|
|
862
|
-
254-page build, and it is kept by splitting a band into more pages. What it cannot be kept by is
|
|
863
|
-
splitting a **group**: §11.1 makes a group atomic to its page. So a build whose clusters group
|
|
864
|
-
into more bytes than a page holds has no layout, and the builder refuses it naming the page size
|
|
865
|
-
that would work, rather than writing a page over the size the header states.
|
|
866
|
-
|
|
867
|
-
15. **Parent-table bounds.** `parent_entry_count ≤ 65,535`, so that a group record's u16
|
|
868
|
-
`parent_first` can index the whole table, and `parent_page_count ≤ parent_entry_count`, since
|
|
869
|
-
no entry can name more than one page. The first is a cap rather than a wider field because the
|
|
870
|
-
distance is not close: the worst page of 8,899 measured carries 26 entries, which leaves the
|
|
871
|
-
bound standing 2,521× above it, and widening `parent_first` would cost the group record its
|
|
872
|
-
8-byte size. A builder that ever approached the cap would split the page.
|
|
873
|
-
|
|
874
|
-
16. **The two edges agree.** Every entry of a page's group-parent table (§6.3) resolves to a
|
|
875
|
-
cluster whose `child_group` is the group whose run the entry is in; each group's run holds
|
|
876
|
-
exactly the clusters that name it, with no repeats; and the run is sorted by
|
|
877
|
-
(page index, cluster index). The first is what makes a coarsen legal at all. The third is what
|
|
878
|
-
lets a runtime elect one appender out of a group's several parents by taking the first entry of
|
|
879
|
-
the run, rather than carrying a dedup bitmask sized by instances × groups — so the order is
|
|
880
|
-
load-bearing and not presentation.
|
|
881
|
-
|
|
882
|
-
17. **`subtree_aabb` bounds the subtree.** It contains the cluster's own `aabb` and, for every
|
|
883
|
-
cluster of the group the cluster refines into, that cluster's `subtree_aabb` — so by induction
|
|
884
|
-
it contains every vertex anywhere below the cluster in the DAG. A leaf's equals its `aabb`.
|
|
885
|
-
|
|
886
|
-
**Exact, on the stored values, with no tolerance.** A union of float32 extremes is a float32
|
|
887
|
-
extreme, so unlike the spheres there is nothing here for rounding to eat and nothing to inflate
|
|
888
|
-
against it. That is half of why the runtime cull is a box: the other half is that a subtree of a
|
|
889
|
-
surface is a flat patch, and its AABB is a fraction of the volume of any sphere around it.
|
|
890
|
-
|
|
891
|
-
**Why the subtree and not the cluster.** The runtime's per-cluster frustum test decides whether
|
|
892
|
-
a cluster refines into its child group, so culling it removes that whole group — and everything
|
|
893
|
-
under it — from the cut. A step consumes one group and produces several parents, each naming the
|
|
894
|
-
*whole* consumed group, so what is reachable below a cluster carries its siblings' surface too
|
|
895
|
-
and grows with depth. A bound that covered only the cluster would cull a parent with on-screen
|
|
896
|
-
descendants, and a culled subtree leaves a hole with nothing standing in over it.
|
|
897
|
-
|
|
898
|
-
## 12. Open
|
|
899
|
-
|
|
900
|
-
- ~~Page codec ids beyond raw and LZ4~~ — `2` is raw DEFLATE through the platform's own zlib (§5)
|
|
901
|
-
and is what the builder writes by default: 1.369x against LZ4's 1.295x on the same pages, for four
|
|
902
|
-
and a half times the decode. Still open is a codec that knows what it is compressing —
|
|
903
|
-
meshopt-style over the payload rather than an LZ matcher over its bytes. The quantized-position `payload_encoding` is the other half of the same
|
|
904
|
-
question, and would take most of what either general-purpose codec currently finds.
|
|
905
|
-
- ~~Default `nominal_page_size`~~ — 64 KiB, `VGEO_DEFAULT_PAGE_SIZE`: one page size rather than
|
|
906
|
-
Nanite's two, set between hiding fetch latency and paying for it in response time and in VRAM per
|
|
907
|
-
resident page on a low-end device, with a floor at the largest group a build produces. Provisional
|
|
908
|
-
— reopen it on a device measurement, and when quantization lands, since that moves what a cluster
|
|
909
|
-
costs.
|
|
910
|
-
- Spatial ordering curve within a level band (Morton over group centroids is the
|
|
911
|
-
default candidate; chosen with fetch-coalescing locality as an explicit criterion,
|
|
912
|
-
§10).
|
|
913
|
-
- Multi-geometry containers (v1: one geometry per file; scenes reference many files).
|