From 73acfb0962e5cbc14b5bfd9357e993cae0486343 Mon Sep 17 00:00:00 2001 From: David Stern Date: Mon, 18 Dec 2023 18:10:09 -0500 Subject: [PATCH] Cache rustybuzz shape plans. --- Cargo.toml | 2 +- src/font/fallback/mod.rs | 6 +++- src/font/system.rs | 22 ++++++++------ src/{cache.rs => glyph_cache.rs} | 0 src/lib.rs | 14 +++++++-- src/shape.rs | 19 ++++++++++-- src/shape_plan_cache.rs | 52 ++++++++++++++++++++++++++++++++ 7 files changed, 98 insertions(+), 17 deletions(-) rename src/{cache.rs => glyph_cache.rs} (100%) create mode 100644 src/shape_plan_cache.rs diff --git a/Cargo.toml b/Cargo.toml index 12c9d7c..2a7d870 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ log = "0.4.20" modit = { version = "0.1.3", optional = true } rangemap = "1.4.0" rustc-hash = { version = "1.1.0", default-features = false } -rustybuzz = { version = "0.11.0", default-features = false, features = ["libm"] } +rustybuzz = { version = "0.12.0", default-features = false, features = ["libm"] } self_cell = "1.0.1" swash = { version = "0.1.8", optional = true } syntect = { version = "5.1.0", optional = true } diff --git a/src/font/fallback/mod.rs b/src/font/fallback/mod.rs index 845cf5d..b5e1838 100644 --- a/src/font/fallback/mod.rs +++ b/src/font/fallback/mod.rs @@ -6,7 +6,7 @@ use alloc::vec::Vec; use fontdb::Family; use unicode_script::Script; -use crate::{Font, FontSystem}; +use crate::{Font, FontSystem, ShapePlanCache}; use self::platform::*; @@ -103,6 +103,10 @@ impl<'a> FontFallbackIter<'a> { } } + pub fn shape_plan_cache(&mut self) -> &mut ShapePlanCache { + self.font_system.shape_plan_cache() + } + fn face_contains_family(&self, id: fontdb::ID, family_name: &str) -> bool { if let Some(face) = self.font_system.db().face(id) { face.families.iter().any(|(name, _)| name == family_name) diff --git a/src/font/system.rs b/src/font/system.rs index 7739aad..eaefe2b 100644 --- a/src/font/system.rs +++ b/src/font/system.rs @@ -1,17 +1,10 @@ -use crate::{Attrs, AttrsOwned, Font}; +use crate::{Attrs, AttrsOwned, Font, HashMap, ShapePlanCache}; use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; use core::fmt; use core::ops::{Deref, DerefMut}; -type BuildHasher = core::hash::BuildHasherDefault; - -#[cfg(feature = "std")] -type HashMap = std::collections::HashMap; -#[cfg(not(feature = "std"))] -type HashMap = hashbrown::HashMap; - // re-export fontdb and rustybuzz pub use fontdb; pub use rustybuzz; @@ -29,6 +22,9 @@ pub struct FontSystem { /// Cache for font matches. font_matches_cache: HashMap>>, + + /// Cache for rustybuzz shape plans. + shape_plan_cache: ShapePlanCache, } impl fmt::Debug for FontSystem { @@ -74,8 +70,9 @@ impl FontSystem { Self { locale, db, - font_cache: HashMap::default(), - font_matches_cache: HashMap::default(), + font_cache: Default::default(), + font_matches_cache: Default::default(), + shape_plan_cache: ShapePlanCache::default(), } } @@ -89,6 +86,11 @@ impl FontSystem { &self.db } + /// Get the shape plan cache. + pub(crate) fn shape_plan_cache(&mut self) -> &mut ShapePlanCache { + &mut self.shape_plan_cache + } + /// Get a mutable reference to the database. pub fn db_mut(&mut self) -> &mut fontdb::Database { self.font_matches_cache.clear(); diff --git a/src/cache.rs b/src/glyph_cache.rs similarity index 100% rename from src/cache.rs rename to src/glyph_cache.rs diff --git a/src/lib.rs b/src/lib.rs index 52b48f6..2345697 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,8 +108,8 @@ mod buffer; pub use self::buffer_line::*; mod buffer_line; -pub use self::cache::*; -mod cache; +pub use self::glyph_cache::*; +mod glyph_cache; pub use self::edit::*; mod edit; @@ -123,7 +123,17 @@ mod layout; pub use self::shape::*; mod shape; +use self::shape_plan_cache::*; +mod shape_plan_cache; + #[cfg(feature = "swash")] pub use self::swash::*; #[cfg(feature = "swash")] mod swash; + +type BuildHasher = core::hash::BuildHasherDefault; + +#[cfg(feature = "std")] +type HashMap = std::collections::HashMap; +#[cfg(not(feature = "std"))] +type HashMap = hashbrown::HashMap; diff --git a/src/shape.rs b/src/shape.rs index cfc9da0..88c1439 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -12,7 +12,9 @@ use unicode_script::{Script, UnicodeScript}; use unicode_segmentation::UnicodeSegmentation; use crate::fallback::FontFallbackIter; -use crate::{Align, AttrsList, Color, Font, FontSystem, LayoutGlyph, LayoutLine, Wrap}; +use crate::{ + Align, AttrsList, Color, Font, FontSystem, LayoutGlyph, LayoutLine, ShapePlanCache, Wrap, +}; /// The shaping strategy of some text. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -85,6 +87,7 @@ impl fmt::Debug for ShapeBuffer { fn shape_fallback( scratch: &mut ShapeBuffer, glyphs: &mut Vec, + shape_plan_cache: &mut ShapePlanCache, font: &Font, line: &str, attrs_list: &AttrsList, @@ -110,7 +113,8 @@ fn shape_fallback( let rtl = matches!(buffer.direction(), rustybuzz::Direction::RightToLeft); assert_eq!(rtl, span_rtl); - let glyph_buffer = rustybuzz::shape(font.rustybuzz(), &[], buffer); + let shape_plan = shape_plan_cache.get(font, &buffer); + let glyph_buffer = rustybuzz::shape_with_plan(font.rustybuzz(), shape_plan, buffer); let glyph_infos = glyph_buffer.glyph_infos(); let glyph_positions = glyph_buffer.glyph_positions(); @@ -218,7 +222,15 @@ fn shape_run( let glyph_start = glyphs.len(); let mut missing = shape_fallback( - scratch, glyphs, &font, line, attrs_list, start_run, end_run, span_rtl, + scratch, + glyphs, + font_iter.shape_plan_cache(), + &font, + line, + attrs_list, + start_run, + end_run, + span_rtl, ); //TODO: improve performance! @@ -236,6 +248,7 @@ fn shape_run( let fb_missing = shape_fallback( scratch, &mut fb_glyphs, + font_iter.shape_plan_cache(), &font, line, attrs_list, diff --git a/src/shape_plan_cache.rs b/src/shape_plan_cache.rs new file mode 100644 index 0000000..19ce69b --- /dev/null +++ b/src/shape_plan_cache.rs @@ -0,0 +1,52 @@ +use std::collections::hash_map::Entry; + +use crate::{Font, HashMap}; + +/// Key for caching shape plans. +#[derive(Debug, Hash, PartialEq, Eq)] +struct ShapePlanKey { + font_id: fontdb::ID, + direction: rustybuzz::Direction, + script: rustybuzz::Script, + language: Option, +} + +/// A helper structure for caching rustybuzz shape plans. +#[derive(Default)] +pub struct ShapePlanCache(HashMap); + +impl ShapePlanCache { + pub fn get(&mut self, font: &Font, buffer: &rustybuzz::UnicodeBuffer) -> &rustybuzz::ShapePlan { + let key = ShapePlanKey { + font_id: font.id(), + direction: buffer.direction(), + script: buffer.script(), + language: buffer.language(), + }; + match self.0.entry(key) { + Entry::Occupied(occ) => occ.into_mut(), + Entry::Vacant(vac) => { + let ShapePlanKey { + direction, + script, + language, + .. + } = vac.key(); + let plan = rustybuzz::ShapePlan::new( + font.rustybuzz(), + *direction, + Some(*script), + language.as_ref(), + &[], + ); + vac.insert(plan) + } + } + } +} + +impl std::fmt::Debug for ShapePlanCache { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("ShapePlanCache").finish() + } +}