From 39dd4cdd823864487739c5e494c79060ab4d49bd Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Fri, 9 Sep 2022 07:28:49 -0700 Subject: [PATCH] Add config options to control various cache sizes These are more for low level testing than they are intended for users to play with, so they are deliberately undocumented at this time. --- config/src/config.rs | 31 ++++++++++++++++++++++++ wezterm-gui/src/cache.rs | 40 +++++++++++++++++++++++++++++-- wezterm-gui/src/glyphcache.rs | 13 ++++++++-- wezterm-gui/src/renderstate.rs | 17 +++++++++---- wezterm-gui/src/termwindow/mod.rs | 24 +++++++++++++++---- 5 files changed, 112 insertions(+), 13 deletions(-) diff --git a/config/src/config.rs b/config/src/config.rs index de120597b..21a8dc187 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -644,6 +644,17 @@ pub struct Config { #[dynamic(default = "default_max_fps")] pub max_fps: u8, + #[dynamic(default = "default_shape_cache_size")] + pub shape_cache_size: usize, + #[dynamic(default = "default_line_state_cache_size")] + pub line_state_cache_size: usize, + #[dynamic(default = "default_line_quad_cache_size")] + pub line_quad_cache_size: usize, + #[dynamic(default = "default_line_to_ele_shape_cache_size")] + pub line_to_ele_shape_cache_size: usize, + #[dynamic(default = "default_glyph_cache_image_cache_size")] + pub glyph_cache_image_cache_size: usize, + #[dynamic(default)] pub visual_bell: VisualBell, @@ -1598,6 +1609,26 @@ impl DroppedFileQuoting { } } +fn default_glyph_cache_image_cache_size() -> usize { + 256 +} + +fn default_shape_cache_size() -> usize { + 1024 +} + +fn default_line_state_cache_size() -> usize { + 1024 +} + +fn default_line_quad_cache_size() -> usize { + 1024 +} + +fn default_line_to_ele_shape_cache_size() -> usize { + 1024 +} + #[derive(Debug, FromDynamic, ToDynamic, Clone, Copy, PartialEq, Eq)] pub enum ImePreeditRendering { /// IME preedit is rendered by WezTerm itself diff --git a/wezterm-gui/src/cache.rs b/wezterm-gui/src/cache.rs index b84cd0279..50e213f4f 100644 --- a/wezterm-gui/src/cache.rs +++ b/wezterm-gui/src/cache.rs @@ -1,5 +1,6 @@ #![allow(dead_code)] use cache_advisor::CacheAdvisor; +use config::ConfigHandle; use fnv::FnvHashMap; use std::borrow::Borrow; use std::cmp::Eq; @@ -11,6 +12,8 @@ use std::hash::Hash; /// Frequently used items are promoted from temporary to main cache. const ENTRY_PERCENT: u8 = 20; +pub type CapFunc = fn(&ConfigHandle) -> usize; + /// A cache using a Least-Frequently-Used eviction policy. /// If K is u64 you should use LfuCacheU64 instead as it has /// less overhead. @@ -22,10 +25,17 @@ pub struct LfuCache { next_id: u64, advisor: CacheAdvisor, cap: usize, + cap_func: CapFunc, } impl LfuCache { - pub fn new(hit: &'static str, miss: &'static str, cap: usize) -> Self { + pub fn new( + hit: &'static str, + miss: &'static str, + cap_func: CapFunc, + config: &ConfigHandle, + ) -> Self { + let cap = cap_func(config); Self { hit, miss, @@ -34,6 +44,7 @@ impl LfuCache { advisor: CacheAdvisor::new(cap, ENTRY_PERCENT), next_id: 0, cap, + cap_func, } } @@ -41,8 +52,17 @@ impl LfuCache { self.map.len() } + pub fn update_config(&mut self, config: &ConfigHandle) { + let new_cap = (self.cap_func)(config); + if new_cap != self.cap { + self.cap = new_cap; + self.clear(); + } + } + pub fn clear(&mut self) { self.map.clear(); + self.key_to_id.clear(); self.advisor = CacheAdvisor::new(self.cap, ENTRY_PERCENT); } @@ -101,16 +121,24 @@ pub struct LfuCacheU64 { map: FnvHashMap, advisor: CacheAdvisor, cap: usize, + cap_func: CapFunc, } impl LfuCacheU64 { - pub fn new(hit: &'static str, miss: &'static str, cap: usize) -> Self { + pub fn new( + hit: &'static str, + miss: &'static str, + cap_func: CapFunc, + config: &ConfigHandle, + ) -> Self { + let cap = cap_func(config); Self { hit, miss, map: FnvHashMap::default(), advisor: CacheAdvisor::new(cap, ENTRY_PERCENT), cap, + cap_func, } } @@ -118,6 +146,14 @@ impl LfuCacheU64 { self.map.len() } + pub fn update_config(&mut self, config: &ConfigHandle) { + let new_cap = (self.cap_func)(config); + if new_cap != self.cap { + self.cap = new_cap; + self.clear(); + } + } + pub fn clear(&mut self) { self.map.clear(); self.advisor = CacheAdvisor::new(self.cap, ENTRY_PERCENT); diff --git a/wezterm-gui/src/glyphcache.rs b/wezterm-gui/src/glyphcache.rs index 349eadb31..6913667dd 100644 --- a/wezterm-gui/src/glyphcache.rs +++ b/wezterm-gui/src/glyphcache.rs @@ -243,6 +243,8 @@ impl DecodedImage { } } +/// A number of items here are HashMaps rather than LfuCaches; +/// eviction is managed by recreating Self when the Atlas is filled pub struct GlyphCache { glyph_cache: HashMap>>, pub atlas: Atlas, @@ -266,7 +268,8 @@ impl GlyphCache { image_cache: LfuCacheU64::new( "glyph_cache.image_cache.hit.rate", "glyph_cache.image_cache.miss.rate", - 256, + |config| config.glyph_cache_image_cache_size, + &fonts.config(), ), frame_cache: HashMap::new(), atlas, @@ -316,7 +319,8 @@ impl GlyphCache { image_cache: LfuCacheU64::new( "glyph_cache.image_cache.hit.rate", "glyph_cache.image_cache.miss.rate", - 256, // FIXME: make configurable + |config| config.glyph_cache_image_cache_size, + &fonts.config(), ), frame_cache: HashMap::new(), atlas, @@ -395,6 +399,11 @@ impl GlyphCache { Ok(glyph) } + pub fn config_changed(&mut self) { + let config = self.fonts.config(); + self.image_cache.update_config(&config); + } + /// Perform the load and render of a glyph #[allow(clippy::float_cmp)] fn load_glyph( diff --git a/wezterm-gui/src/renderstate.rs b/wezterm-gui/src/renderstate.rs index 5dcf73702..ee6d6e7b6 100644 --- a/wezterm-gui/src/renderstate.rs +++ b/wezterm-gui/src/renderstate.rs @@ -129,9 +129,9 @@ pub struct RenderLayer { impl RenderLayer { pub fn new(context: &Rc, num_quads: usize, zindex: i8) -> anyhow::Result { let vb = [ - Self::compute_vertices(context, 128)?, + Self::compute_vertices(context, 32)?, Self::compute_vertices(context, num_quads)?, - Self::compute_vertices(context, 128)?, + Self::compute_vertices(context, 32)?, ]; Ok(Self { @@ -168,6 +168,11 @@ impl RenderLayer { num_quads: usize, ) -> anyhow::Result { let verts = vec![Vertex::default(); num_quads * VERTICES_PER_CELL]; + log::trace!( + "compute_vertices num_quads={}, allocated {} bytes", + num_quads, + verts.len() * std::mem::size_of::() + ); let mut indices = vec![]; indices.reserve(num_quads * INDICES_PER_CELL); @@ -294,9 +299,9 @@ impl RenderState { for layer in self.layers.borrow().iter() { for vb_idx in 0..3 { if let Some(need_quads) = layer.need_more_quads(vb_idx) { - // Round up to next multiple of 1024 that is >= + // Round up to next multiple of 128 that is >= // the number of needed quads for this frame - let num_quads = (need_quads + 1023) & !1023; + let num_quads = (need_quads + 127) & !127; layer.reallocate_quads(vb_idx, num_quads).with_context(|| { format!( "Failed to allocate {} quads (needed {})", @@ -355,6 +360,10 @@ impl RenderState { ) } + pub fn config_changed(&mut self) { + self.glyph_cache.borrow_mut().config_changed(); + } + pub fn recreate_texture_atlas( &mut self, fonts: &Rc, diff --git a/wezterm-gui/src/termwindow/mod.rs b/wezterm-gui/src/termwindow/mod.rs index 4998ed856..548070fae 100644 --- a/wezterm-gui/src/termwindow/mod.rs +++ b/wezterm-gui/src/termwindow/mod.rs @@ -703,23 +703,27 @@ impl TermWindow { shape_cache: RefCell::new(LfuCache::new( "shape_cache.hit.rate", "shape_cache.miss.rate", - 1024, + |config| config.shape_cache_size, + &config, )), line_state_cache: RefCell::new(LfuCacheU64::new( "line_state_cache.hit.rate", "line_state_cache.miss.rate", - 1024, + |config| config.line_state_cache_size, + &config, )), next_line_state_id: 0, line_quad_cache: RefCell::new(LfuCache::new( "line_quad_cache.hit.rate", "line_quad_cache.miss.rate", - 1024, + |config| config.line_quad_cache_size, + &config, )), line_to_ele_shape_cache: RefCell::new(LfuCache::new( "line_to_ele_shape_cache.hit.rate", "line_to_ele_shape_cache.miss.rate", - 1024, + |config| config.line_to_ele_shape_cache_size, + &config, )), last_status_call: Instant::now(), cursor_blink_state: RefCell::new(ColorEase::new( @@ -1536,12 +1540,22 @@ impl TermWindow { self.show_scroll_bar = config.enable_scroll_bar; self.shape_generation += 1; - self.shape_cache.borrow_mut().clear(); + { + let mut shape_cache = self.shape_cache.borrow_mut(); + shape_cache.update_config(&config); + shape_cache.clear(); + } + self.line_state_cache.borrow_mut().update_config(&config); + self.line_quad_cache.borrow_mut().update_config(&config); + self.line_to_ele_shape_cache + .borrow_mut() + .update_config(&config); self.fancy_tab_bar.take(); self.invalidate_fancy_tab_bar(); self.invalidate_modal(); self.input_map = InputMap::new(&config); self.leader_is_down = None; + self.render_state.as_mut().map(|rs| rs.config_changed()); let dimensions = self.dimensions; if let Err(err) = self.fonts.config_changed(&config) {