From 2fe3dd0c585b811a9e80b1a7b631f23bbe64ba9a Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sat, 22 Jan 2022 15:02:31 +0100 Subject: [PATCH] Remove mutex for Font::glyph_info_cache --- egui/src/context.rs | 4 +-- epaint/src/text/font.rs | 25 +++++-------- epaint/src/text/fonts.rs | 66 ++++++++++++++++++---------------- epaint/src/text/text_layout.rs | 6 ++-- 4 files changed, 50 insertions(+), 51 deletions(-) diff --git a/egui/src/context.rs b/egui/src/context.rs index 88794251..6c4087d6 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -700,7 +700,7 @@ impl Context { self.request_repaint(); } - self.fonts().lock().end_frame(); + self.fonts().end_frame(); { let ctx_impl = &mut *self.write(); @@ -1015,7 +1015,7 @@ impl Context { ui.label(format!( "There are {} text galleys in the layout cache", - self.fonts().lock().num_galleys_in_cache() + self.fonts().num_galleys_in_cache() )) .on_hover_text("This is approximately the number of text strings on screen"); ui.add_space(16.0); diff --git a/epaint/src/text/font.rs b/epaint/src/text/font.rs index 2ac9838e..1594f2fc 100644 --- a/epaint/src/text/font.rs +++ b/epaint/src/text/font.rs @@ -209,7 +209,7 @@ pub struct Font { replacement_glyph: (FontIndex, GlyphInfo), pixels_per_point: f32, row_height: f32, - glyph_info_cache: RwLock>, + glyph_info_cache: AHashMap, } impl Font { @@ -295,35 +295,30 @@ impl Font { pub fn uv_rect(&self, c: char) -> UvRect { self.glyph_info_cache - .read() .get(&c) .map(|gi| gi.1.uv_rect) .unwrap_or_default() } /// Width of this character in points. - pub fn glyph_width(&self, c: char) -> f32 { + pub fn glyph_width(&mut self, c: char) -> f32 { self.glyph_info(c).1.advance_width } /// `\n` will (intentionally) show up as the replacement character. - fn glyph_info(&self, c: char) -> (FontIndex, GlyphInfo) { - { - if let Some(font_index_glyph_info) = self.glyph_info_cache.read().get(&c) { - return *font_index_glyph_info; - } + fn glyph_info(&mut self, c: char) -> (FontIndex, GlyphInfo) { + if let Some(font_index_glyph_info) = self.glyph_info_cache.get(&c) { + return *font_index_glyph_info; } let font_index_glyph_info = self.glyph_info_no_cache_or_fallback(c); let font_index_glyph_info = font_index_glyph_info.unwrap_or(self.replacement_glyph); - self.glyph_info_cache - .write() - .insert(c, font_index_glyph_info); + self.glyph_info_cache.insert(c, font_index_glyph_info); font_index_glyph_info } #[inline] - pub(crate) fn glyph_info_and_font_impl(&self, c: char) -> (Option<&FontImpl>, GlyphInfo) { + pub(crate) fn glyph_info_and_font_impl(&mut self, c: char) -> (Option<&FontImpl>, GlyphInfo) { if self.fonts.is_empty() { return (None, self.replacement_glyph.1); } @@ -332,12 +327,10 @@ impl Font { (Some(font_impl), glyph_info) } - fn glyph_info_no_cache_or_fallback(&self, c: char) -> Option<(FontIndex, GlyphInfo)> { + fn glyph_info_no_cache_or_fallback(&mut self, c: char) -> Option<(FontIndex, GlyphInfo)> { for (font_index, font_impl) in self.fonts.iter().enumerate() { if let Some(glyph_info) = font_impl.glyph_info(c) { - self.glyph_info_cache - .write() - .insert(c, (font_index, glyph_info)); + self.glyph_info_cache.insert(c, (font_index, glyph_info)); return Some((font_index, glyph_info)); } } diff --git a/epaint/src/text/fonts.rs b/epaint/src/text/fonts.rs index 9583b54b..059e37ac 100644 --- a/epaint/src/text/fonts.rs +++ b/epaint/src/text/fonts.rs @@ -233,23 +233,27 @@ impl Default for FontDefinitions { /// Create one and reuse. Cheap to clone. /// /// Wrapper for `Arc>`. -pub struct Fonts(Arc>); +pub struct Fonts { + fonts: Arc>, + galley_cache: Arc>, +} impl Fonts { /// Create a new [`Fonts`] for text layout. /// This call is expensive, so only create one [`Fonts`] and then reuse it. pub fn new(pixels_per_point: f32, definitions: FontDefinitions) -> Self { - Self(Arc::new(Mutex::new(FontsImpl::new( - pixels_per_point, - definitions, - )))) + let fonts = Arc::new(Mutex::new(FontsImpl::new(pixels_per_point, definitions))); + Self { + fonts, + galley_cache: Default::default(), + } } /// Access the underlying [`FontsImpl`]. #[doc(hidden)] #[inline] pub fn lock(&self) -> crate::mutex::MutexGuard<'_, FontsImpl> { - self.0.lock() + self.fonts.lock() } #[inline] @@ -277,7 +281,16 @@ impl Fonts { /// The implementation uses memoization so repeated calls are cheap. /// #[inline] pub fn layout_job(&self, job: LayoutJob) -> Arc { - self.lock().layout_job(job) + self.galley_cache.lock().layout(&mut self.fonts.lock(), job) + } + + pub fn num_galleys_in_cache(&self) -> usize { + self.galley_cache.lock().num_galleys_in_cache() + } + + /// Must be called once per frame to clear the [`Galley`] cache. + pub fn end_frame(&self) { + self.galley_cache.lock().end_frame(); } /// Will wrap text at the given width and line break at `\n`. @@ -324,6 +337,14 @@ impl Fonts { )) } } + +// ---------------------------------------------------------------------------- + +// pub struct CachedFonts { +// fonts: Arc>, +// galley_cache: Arc>, +// } + // ---------------------------------------------------------------------------- /// The collection of fonts used by `epaint`. @@ -334,7 +355,6 @@ pub struct FontsImpl { definitions: FontDefinitions, fonts: BTreeMap, atlas: Arc>, - galley_cache: Mutex, } impl FontsImpl { @@ -384,7 +404,6 @@ impl FontsImpl { definitions, fonts, atlas, - galley_cache: Default::default(), } } @@ -402,6 +421,11 @@ impl FontsImpl { &self.fonts[&text_style] } + #[inline] + pub fn font_mut(&mut self, text_style: TextStyle) -> &mut Font { + self.fonts.get_mut(&text_style).unwrap() + } + /// Call each frame to get the change to the font texture since last call. pub fn font_image_delta(&self) -> Option { self.atlas.lock().take_delta() @@ -413,32 +437,14 @@ impl FontsImpl { } /// Width of this character in points. - pub fn glyph_width(&self, text_style: TextStyle, c: char) -> f32 { - self.fonts[&text_style].glyph_width(c) + pub fn glyph_width(&mut self, text_style: TextStyle, c: char) -> f32 { + self.font_mut(text_style).glyph_width(c) } /// Height of one row of text. In points pub fn row_height(&self, text_style: TextStyle) -> f32 { self.fonts[&text_style].row_height() } - - /// Layout some text. - /// This is the most advanced layout function. - /// See also [`Self::layout`], [`Self::layout_no_wrap`] and - /// [`Self::layout_delayed_color`]. - /// - /// The implementation uses memoization so repeated calls are cheap. - pub fn layout_job(&self, job: LayoutJob) -> Arc { - self.galley_cache.lock().layout(self, job) - } - pub fn num_galleys_in_cache(&self) -> usize { - self.galley_cache.lock().num_galleys_in_cache() - } - - /// Must be called once per frame to clear the [`Galley`] cache. - pub fn end_frame(&self) { - self.galley_cache.lock().end_frame(); - } } // ---------------------------------------------------------------------------- @@ -457,7 +463,7 @@ struct GalleyCache { } impl GalleyCache { - fn layout(&mut self, fonts: &FontsImpl, job: LayoutJob) -> Arc { + fn layout(&mut self, fonts: &mut FontsImpl, job: LayoutJob) -> Arc { let hash = crate::util::hash(&job); // TODO: even faster hasher? match self.cache.entry(hash) { diff --git a/epaint/src/text/text_layout.rs b/epaint/src/text/text_layout.rs index a63170c0..a009d1cf 100644 --- a/epaint/src/text/text_layout.rs +++ b/epaint/src/text/text_layout.rs @@ -50,7 +50,7 @@ struct Paragraph { /// /// In most cases you should use [`Fonts::layout_job`] instead /// since that memoizes the input, making subsequent layouting of the same text much faster. -pub fn layout(fonts: &FontsImpl, job: Arc) -> Galley { +pub fn layout(fonts: &mut FontsImpl, job: Arc) -> Galley { let mut paragraphs = vec![Paragraph::default()]; for (section_index, section) in job.sections.iter().enumerate() { layout_section(fonts, &job, section_index as u32, section, &mut paragraphs); @@ -75,7 +75,7 @@ pub fn layout(fonts: &FontsImpl, job: Arc) -> Galley { } fn layout_section( - fonts: &FontsImpl, + fonts: &mut FontsImpl, job: &LayoutJob, section_index: u32, section: &LayoutSection, @@ -86,7 +86,7 @@ fn layout_section( byte_range, format, } = section; - let font = fonts.font(format.style); + let font = fonts.font_mut(format.style); let font_height = font.row_height(); let mut paragraph = out_paragraphs.last_mut().unwrap();