Skip to content

Commit

Permalink
Cache rustybuzz shape plans.
Browse files Browse the repository at this point in the history
  • Loading branch information
vorporeal committed Dec 18, 2023
1 parent 94e6cde commit 73acfb0
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
6 changes: 5 additions & 1 deletion src/font/fallback/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand Down Expand Up @@ -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)
Expand Down
22 changes: 12 additions & 10 deletions src/font/system.rs
Original file line number Diff line number Diff line change
@@ -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<rustc_hash::FxHasher>;

#[cfg(feature = "std")]
type HashMap<K, V> = std::collections::HashMap<K, V, BuildHasher>;
#[cfg(not(feature = "std"))]
type HashMap<K, V> = hashbrown::HashMap<K, V, BuildHasher>;

// re-export fontdb and rustybuzz
pub use fontdb;
pub use rustybuzz;
Expand All @@ -29,6 +22,9 @@ pub struct FontSystem {

/// Cache for font matches.
font_matches_cache: HashMap<AttrsOwned, Arc<Vec<fontdb::ID>>>,

/// Cache for rustybuzz shape plans.
shape_plan_cache: ShapePlanCache,
}

impl fmt::Debug for FontSystem {
Expand Down Expand Up @@ -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(),
}
}

Expand All @@ -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();
Expand Down
File renamed without changes.
14 changes: 12 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<rustc_hash::FxHasher>;

#[cfg(feature = "std")]
type HashMap<K, V> = std::collections::HashMap<K, V, BuildHasher>;
#[cfg(not(feature = "std"))]
type HashMap<K, V> = hashbrown::HashMap<K, V, BuildHasher>;
19 changes: 16 additions & 3 deletions src/shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -85,6 +87,7 @@ impl fmt::Debug for ShapeBuffer {
fn shape_fallback(
scratch: &mut ShapeBuffer,
glyphs: &mut Vec<ShapeGlyph>,
shape_plan_cache: &mut ShapePlanCache,
font: &Font,
line: &str,
attrs_list: &AttrsList,
Expand All @@ -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();

Expand Down Expand Up @@ -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!
Expand All @@ -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,
Expand Down
52 changes: 52 additions & 0 deletions src/shape_plan_cache.rs
Original file line number Diff line number Diff line change
@@ -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<rustybuzz::Language>,
}

/// A helper structure for caching rustybuzz shape plans.
#[derive(Default)]
pub struct ShapePlanCache(HashMap<ShapePlanKey, rustybuzz::ShapePlan>);

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()
}
}

0 comments on commit 73acfb0

Please sign in to comment.