Remove mutex for Font::glyph_info_cache

This commit is contained in:
Emil Ernerfeldt 2022-01-22 15:02:31 +01:00
parent b0196527e5
commit 2fe3dd0c58
4 changed files with 50 additions and 51 deletions

View file

@ -700,7 +700,7 @@ impl Context {
self.request_repaint(); self.request_repaint();
} }
self.fonts().lock().end_frame(); self.fonts().end_frame();
{ {
let ctx_impl = &mut *self.write(); let ctx_impl = &mut *self.write();
@ -1015,7 +1015,7 @@ impl Context {
ui.label(format!( ui.label(format!(
"There are {} text galleys in the layout cache", "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"); .on_hover_text("This is approximately the number of text strings on screen");
ui.add_space(16.0); ui.add_space(16.0);

View file

@ -209,7 +209,7 @@ pub struct Font {
replacement_glyph: (FontIndex, GlyphInfo), replacement_glyph: (FontIndex, GlyphInfo),
pixels_per_point: f32, pixels_per_point: f32,
row_height: f32, row_height: f32,
glyph_info_cache: RwLock<AHashMap<char, (FontIndex, GlyphInfo)>>, glyph_info_cache: AHashMap<char, (FontIndex, GlyphInfo)>,
} }
impl Font { impl Font {
@ -295,35 +295,30 @@ impl Font {
pub fn uv_rect(&self, c: char) -> UvRect { pub fn uv_rect(&self, c: char) -> UvRect {
self.glyph_info_cache self.glyph_info_cache
.read()
.get(&c) .get(&c)
.map(|gi| gi.1.uv_rect) .map(|gi| gi.1.uv_rect)
.unwrap_or_default() .unwrap_or_default()
} }
/// Width of this character in points. /// 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 self.glyph_info(c).1.advance_width
} }
/// `\n` will (intentionally) show up as the replacement character. /// `\n` will (intentionally) show up as the replacement character.
fn glyph_info(&self, c: char) -> (FontIndex, GlyphInfo) { fn glyph_info(&mut self, c: char) -> (FontIndex, GlyphInfo) {
{ if let Some(font_index_glyph_info) = self.glyph_info_cache.get(&c) {
if let Some(font_index_glyph_info) = self.glyph_info_cache.read().get(&c) {
return *font_index_glyph_info; return *font_index_glyph_info;
} }
}
let font_index_glyph_info = self.glyph_info_no_cache_or_fallback(c); 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); let font_index_glyph_info = font_index_glyph_info.unwrap_or(self.replacement_glyph);
self.glyph_info_cache self.glyph_info_cache.insert(c, font_index_glyph_info);
.write()
.insert(c, font_index_glyph_info);
font_index_glyph_info font_index_glyph_info
} }
#[inline] #[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() { if self.fonts.is_empty() {
return (None, self.replacement_glyph.1); return (None, self.replacement_glyph.1);
} }
@ -332,12 +327,10 @@ impl Font {
(Some(font_impl), glyph_info) (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() { for (font_index, font_impl) in self.fonts.iter().enumerate() {
if let Some(glyph_info) = font_impl.glyph_info(c) { if let Some(glyph_info) = font_impl.glyph_info(c) {
self.glyph_info_cache self.glyph_info_cache.insert(c, (font_index, glyph_info));
.write()
.insert(c, (font_index, glyph_info));
return Some((font_index, glyph_info)); return Some((font_index, glyph_info));
} }
} }

View file

@ -233,23 +233,27 @@ impl Default for FontDefinitions {
/// Create one and reuse. Cheap to clone. /// Create one and reuse. Cheap to clone.
/// ///
/// Wrapper for `Arc<Mutex<FontsImpl>>`. /// Wrapper for `Arc<Mutex<FontsImpl>>`.
pub struct Fonts(Arc<Mutex<FontsImpl>>); pub struct Fonts {
fonts: Arc<Mutex<FontsImpl>>,
galley_cache: Arc<Mutex<GalleyCache>>,
}
impl Fonts { impl Fonts {
/// Create a new [`Fonts`] for text layout. /// Create a new [`Fonts`] for text layout.
/// This call is expensive, so only create one [`Fonts`] and then reuse it. /// This call is expensive, so only create one [`Fonts`] and then reuse it.
pub fn new(pixels_per_point: f32, definitions: FontDefinitions) -> Self { pub fn new(pixels_per_point: f32, definitions: FontDefinitions) -> Self {
Self(Arc::new(Mutex::new(FontsImpl::new( let fonts = Arc::new(Mutex::new(FontsImpl::new(pixels_per_point, definitions)));
pixels_per_point, Self {
definitions, fonts,
)))) galley_cache: Default::default(),
}
} }
/// Access the underlying [`FontsImpl`]. /// Access the underlying [`FontsImpl`].
#[doc(hidden)] #[doc(hidden)]
#[inline] #[inline]
pub fn lock(&self) -> crate::mutex::MutexGuard<'_, FontsImpl> { pub fn lock(&self) -> crate::mutex::MutexGuard<'_, FontsImpl> {
self.0.lock() self.fonts.lock()
} }
#[inline] #[inline]
@ -277,7 +281,16 @@ impl Fonts {
/// The implementation uses memoization so repeated calls are cheap. /// The implementation uses memoization so repeated calls are cheap.
/// #[inline] /// #[inline]
pub fn layout_job(&self, job: LayoutJob) -> Arc<Galley> { pub fn layout_job(&self, job: LayoutJob) -> Arc<Galley> {
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`. /// Will wrap text at the given width and line break at `\n`.
@ -324,6 +337,14 @@ impl Fonts {
)) ))
} }
} }
// ----------------------------------------------------------------------------
// pub struct CachedFonts {
// fonts: Arc<Mutex<FontsImpl>>,
// galley_cache: Arc<Mutex<GalleyCache>>,
// }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/// The collection of fonts used by `epaint`. /// The collection of fonts used by `epaint`.
@ -334,7 +355,6 @@ pub struct FontsImpl {
definitions: FontDefinitions, definitions: FontDefinitions,
fonts: BTreeMap<TextStyle, Font>, fonts: BTreeMap<TextStyle, Font>,
atlas: Arc<Mutex<TextureAtlas>>, atlas: Arc<Mutex<TextureAtlas>>,
galley_cache: Mutex<GalleyCache>,
} }
impl FontsImpl { impl FontsImpl {
@ -384,7 +404,6 @@ impl FontsImpl {
definitions, definitions,
fonts, fonts,
atlas, atlas,
galley_cache: Default::default(),
} }
} }
@ -402,6 +421,11 @@ impl FontsImpl {
&self.fonts[&text_style] &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. /// Call each frame to get the change to the font texture since last call.
pub fn font_image_delta(&self) -> Option<crate::ImageDelta> { pub fn font_image_delta(&self) -> Option<crate::ImageDelta> {
self.atlas.lock().take_delta() self.atlas.lock().take_delta()
@ -413,32 +437,14 @@ impl FontsImpl {
} }
/// Width of this character in points. /// Width of this character in points.
pub fn glyph_width(&self, text_style: TextStyle, c: char) -> f32 { pub fn glyph_width(&mut self, text_style: TextStyle, c: char) -> f32 {
self.fonts[&text_style].glyph_width(c) self.font_mut(text_style).glyph_width(c)
} }
/// Height of one row of text. In points /// Height of one row of text. In points
pub fn row_height(&self, text_style: TextStyle) -> f32 { pub fn row_height(&self, text_style: TextStyle) -> f32 {
self.fonts[&text_style].row_height() 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<Galley> {
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 { impl GalleyCache {
fn layout(&mut self, fonts: &FontsImpl, job: LayoutJob) -> Arc<Galley> { fn layout(&mut self, fonts: &mut FontsImpl, job: LayoutJob) -> Arc<Galley> {
let hash = crate::util::hash(&job); // TODO: even faster hasher? let hash = crate::util::hash(&job); // TODO: even faster hasher?
match self.cache.entry(hash) { match self.cache.entry(hash) {

View file

@ -50,7 +50,7 @@ struct Paragraph {
/// ///
/// In most cases you should use [`Fonts::layout_job`] instead /// In most cases you should use [`Fonts::layout_job`] instead
/// since that memoizes the input, making subsequent layouting of the same text much faster. /// since that memoizes the input, making subsequent layouting of the same text much faster.
pub fn layout(fonts: &FontsImpl, job: Arc<LayoutJob>) -> Galley { pub fn layout(fonts: &mut FontsImpl, job: Arc<LayoutJob>) -> Galley {
let mut paragraphs = vec![Paragraph::default()]; let mut paragraphs = vec![Paragraph::default()];
for (section_index, section) in job.sections.iter().enumerate() { for (section_index, section) in job.sections.iter().enumerate() {
layout_section(fonts, &job, section_index as u32, section, &mut paragraphs); layout_section(fonts, &job, section_index as u32, section, &mut paragraphs);
@ -75,7 +75,7 @@ pub fn layout(fonts: &FontsImpl, job: Arc<LayoutJob>) -> Galley {
} }
fn layout_section( fn layout_section(
fonts: &FontsImpl, fonts: &mut FontsImpl,
job: &LayoutJob, job: &LayoutJob,
section_index: u32, section_index: u32,
section: &LayoutSection, section: &LayoutSection,
@ -86,7 +86,7 @@ fn layout_section(
byte_range, byte_range,
format, format,
} = section; } = section;
let font = fonts.font(format.style); let font = fonts.font_mut(format.style);
let font_height = font.row_height(); let font_height = font.row_height();
let mut paragraph = out_paragraphs.last_mut().unwrap(); let mut paragraph = out_paragraphs.last_mut().unwrap();