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.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);

View file

@ -209,7 +209,7 @@ pub struct Font {
replacement_glyph: (FontIndex, GlyphInfo),
pixels_per_point: f32,
row_height: f32,
glyph_info_cache: RwLock<AHashMap<char, (FontIndex, GlyphInfo)>>,
glyph_info_cache: AHashMap<char, (FontIndex, GlyphInfo)>,
}
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));
}
}

View file

@ -233,23 +233,27 @@ impl Default for FontDefinitions {
/// Create one and reuse. Cheap to clone.
///
/// 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 {
/// 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<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`.
@ -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`.
@ -334,7 +355,6 @@ pub struct FontsImpl {
definitions: FontDefinitions,
fonts: BTreeMap<TextStyle, Font>,
atlas: Arc<Mutex<TextureAtlas>>,
galley_cache: Mutex<GalleyCache>,
}
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<crate::ImageDelta> {
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<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 {
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?
match self.cache.entry(hash) {

View file

@ -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<LayoutJob>) -> Galley {
pub fn layout(fonts: &mut FontsImpl, job: Arc<LayoutJob>) -> 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<LayoutJob>) -> 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();