From a38ef04585199f96d035acbcacffd97fdd32bf3b Mon Sep 17 00:00:00 2001 From: ElyTheCat <78264512+elythepreciouscat@users.noreply.github.com> Date: Tue, 3 Nov 2026 13:02:30 +0330 Subject: [PATCH] Make entity precheck threshold configurable Co-Authored-By: OpenAI Codex --- crates/horizon-client-render/src/graphics.rs | 10 +++++++++- .../horizon-client-render/src/graphics_ui.rs | 19 ++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/crates/horizon-client-render/src/graphics.rs b/crates/horizon-client-render/src/graphics.rs index 2280863ea0..642992a29d 100644 --- a/crates/horizon-client-render/src/graphics.rs +++ b/crates/horizon-client-render/src/graphics.rs @@ -551,6 +551,8 @@ pub struct Graphics { pub cave_culling: bool, /// Skip drawing entities hidden behind opaque terrain, synchronously or asynchronously. pub entity_culling: EntityCullingMode, + /// Minimum entity population for coarse AABB culling; zero disables it. + pub entity_culling_precheck: u32, /// Terrain leaf faces inside a canopy (see [`LeafCullingMode`]); `Off` draws /// vanilla's fancy leaves. Ignored with a resource pack that may replace /// the vanilla leaf models ([`Graphics::leaf_culling_effective`]). @@ -648,6 +650,7 @@ impl Default for Graphics { blue_map: crate::bluemap::Settings::default(), cave_culling: true, entity_culling: EntityCullingMode::Off, + entity_culling_precheck: 64, leaf_culling: LeafCullingMode::Off, cache_shulker_boxes: false, // Sign text has used its renderer cache independently of the new model caches. @@ -741,6 +744,9 @@ impl Graphics { "blueMapAutoDetect" => g.blue_map.auto_detect = flag(g.blue_map.auto_detect), "caveCulling" => g.cave_culling = flag(g.cave_culling), "entityCulling" => g.entity_culling = EntityCullingMode::parse(value), + "entityCullingPrecheck" => { + g.entity_culling_precheck = number(g.entity_culling_precheck).min(512) + } "leafCulling" => g.leaf_culling = LeafCullingMode::parse(value), "blockEntityCache" => { let enabled = flag(false); @@ -831,12 +837,13 @@ impl Graphics { } pub fn serialize(&self) -> String { let mut text = format!( - "preset:{}\nlodDistance:{}\nfarDetail:{}\ncaveCulling:{}\nentityCulling:{}\nleafCulling:{}\nblockEntityCacheShulkerBoxes:{}\nblockEntityCacheSigns:{}\nblockEntityCacheDecoratedPots:{}\nblockEntityCacheBells:{}\nblockEntityCacheChests:{}\nblockEntityCacheBanners:{}\nblockEntityCacheCopperGolemStatues:{}\nchunkUpdates:{}\nuploadBudgetKiB:{}\ntextureBudgetMiB:{}\nsky:{}\nfog:{}\nweather:{}\nanimatedTextures:{}\nditherCutoutMips:{}\npresentMode:{}\nframePacing:{}\nworkers:{}\noverlay:{}\nresolutionScale:{}\nfxaa:{}\ntaa:{}\nupscaling:{}\nscreenshotQuality:{}\nframeGeneration:{}\nhdr:{}\nhdrPaperWhite:{}\nhdrPeak:{}\nhdrBlackLevel:{}\nhdrUiBrightness:{}\nhdrKey:{}\nbatteryMode:{}\nshadowCache:{}\ngraphicsApi:{}\npickedShaderPack:{}\nbatchTranslucent:{}\n", + "preset:{}\nlodDistance:{}\nfarDetail:{}\ncaveCulling:{}\nentityCulling:{}\nentityCullingPrecheck:{}\nleafCulling:{}\nblockEntityCacheShulkerBoxes:{}\nblockEntityCacheSigns:{}\nblockEntityCacheDecoratedPots:{}\nblockEntityCacheBells:{}\nblockEntityCacheChests:{}\nblockEntityCacheBanners:{}\nblockEntityCacheCopperGolemStatues:{}\nchunkUpdates:{}\nuploadBudgetKiB:{}\ntextureBudgetMiB:{}\nsky:{}\nfog:{}\nweather:{}\nanimatedTextures:{}\nditherCutoutMips:{}\npresentMode:{}\nframePacing:{}\nworkers:{}\noverlay:{}\nresolutionScale:{}\nfxaa:{}\ntaa:{}\nupscaling:{}\nscreenshotQuality:{}\nframeGeneration:{}\nhdr:{}\nhdrPaperWhite:{}\nhdrPeak:{}\nhdrBlackLevel:{}\nhdrUiBrightness:{}\nhdrKey:{}\nbatteryMode:{}\nshadowCache:{}\ngraphicsApi:{}\npickedShaderPack:{}\nbatchTranslucent:{}\n", self.preset.name(), self.lod_distance, self.far_detail.name(), bool_text(self.cave_culling), self.entity_culling.name(), + self.entity_culling_precheck, self.leaf_culling.name(), bool_text(self.cache_shulker_boxes), bool_text(self.cache_signs), @@ -1015,6 +1022,7 @@ mod tests { }, cave_culling: false, entity_culling: EntityCullingMode::Synchronous, + entity_culling_precheck: 128, leaf_culling: LeafCullingMode::SolidAggressive, cache_shulker_boxes: true, cache_signs: true, diff --git a/crates/horizon-client-render/src/graphics_ui.rs b/crates/horizon-client-render/src/graphics_ui.rs index 903b7ba372..522078640d 100644 --- a/crates/horizon-client-render/src/graphics_ui.rs +++ b/crates/horizon-client-render/src/graphics_ui.rs @@ -66,6 +66,7 @@ pub enum H { BlueMapRadius, CaveCulling, EntityCulling, + EntityCullingPrecheck, LeafCulling, CacheShulkerBoxes, CacheSigns, @@ -379,6 +380,11 @@ pub fn rows() -> Vec<(Page, Vec<(&'static str, Vec)>)> { None, "Hides entities behind opaque blocks.", ), + h( + H::EntityCullingPrecheck, + Saves, + "Runs coarse entity culling after this many entities are visible. Zero disables it.", + ), h( H::LeafCulling, Saves, @@ -646,6 +652,7 @@ impl H { let g = &s.horizon; match self { H::ChunkUpdates => Some(g.chunk_updates as f32 / 64.0), + H::EntityCullingPrecheck => Some(g.entity_culling_precheck as f32 / 512.0), H::UploadBudget => { let i = UPLOAD_STEPS .iter() @@ -676,7 +683,8 @@ impl H { } pub fn slider_steps(self) -> Option { match self { - H::ChunkUpdates => Some(64), + H::ChunkUpdates => Some(32), + H::EntityCullingPrecheck => Some(32), H::UploadBudget => Some((UPLOAD_STEPS.len() - 1) as u32), H::Workers => Some(max_workers()), H::ResolutionScale => Some(30), @@ -703,6 +711,14 @@ impl H { ), H::CaveCulling => labelled("Cave Culling", on_off(g.cave_culling)), H::EntityCulling => labelled("Entity Culling", g.entity_culling.label()), + H::EntityCullingPrecheck => labelled( + "Entity Culling Precheck", + if g.entity_culling_precheck == 0 { + "Off".to_string() + } else { + format!("{} entities", g.entity_culling_precheck) + }, + ), H::LeafCulling => labelled("Leaf Culling", g.leaf_culling.label()), H::CacheShulkerBoxes => labelled("Shulker Boxes", on_off(g.cache_shulker_boxes)), H::CacheSigns => labelled("Signs", on_off(g.cache_signs)), @@ -809,6 +825,7 @@ impl H { let g = &mut s.horizon; match self { H::ChunkUpdates => g.chunk_updates = (v * 64.0).round() as u32, + H::EntityCullingPrecheck => g.entity_culling_precheck = (v * 512.0).round() as u32, H::UploadBudget => { g.upload_budget_kib = UPLOAD_STEPS[(v * (UPLOAD_STEPS.len() - 1) as f32).round() as usize]