Add shape-run-cache feature, that can significantly improve shaping performance
This commit is contained in:
parent
990d66ed41
commit
1eb3233373
7 changed files with 134 additions and 2 deletions
|
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- Add `shape-run-cache` feature, that can significantly improve shaping performance
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove editor-libcosmic, see cosmic-edit instead
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@ features = ["hardcoded-data"]
|
|||
|
||||
[features]
|
||||
default = ["std", "swash", "fontconfig"]
|
||||
fontconfig = ["fontdb/fontconfig", "std"]
|
||||
no_std = ["rustybuzz/libm", "hashbrown"]
|
||||
shape-run-cache = []
|
||||
std = [
|
||||
"fontdb/memmap",
|
||||
"fontdb/std",
|
||||
|
|
@ -47,7 +49,6 @@ std = [
|
|||
vi = ["modit", "syntect", "cosmic_undo_2"]
|
||||
wasm-web = ["sys-locale?/js"]
|
||||
warn_on_missing_glyphs = []
|
||||
fontconfig = ["fontdb/fontconfig", "std"]
|
||||
|
||||
[[bench]]
|
||||
name = "layout"
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ impl AttrsOwned {
|
|||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct AttrsList {
|
||||
defaults: AttrsOwned,
|
||||
spans: RangeMap<usize, AttrsOwned>,
|
||||
pub(crate) spans: RangeMap<usize, AttrsOwned>,
|
||||
}
|
||||
|
||||
impl AttrsList {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ pub struct FontSystem {
|
|||
|
||||
/// Cache for rustybuzz shape plans.
|
||||
shape_plan_cache: ShapePlanCache,
|
||||
|
||||
/// Cache for shaped runs
|
||||
#[cfg(feature = "shape-run-cache")]
|
||||
pub shape_run_cache: crate::ShapeRunCache,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FontSystem {
|
||||
|
|
@ -81,6 +85,8 @@ impl FontSystem {
|
|||
font_cache: Default::default(),
|
||||
font_matches_cache: Default::default(),
|
||||
shape_plan_cache: ShapePlanCache::default(),
|
||||
#[cfg(feature = "shape-run-cache")]
|
||||
shape_run_cache: crate::ShapeRunCache::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,9 @@ mod shape;
|
|||
use self::shape_plan_cache::*;
|
||||
mod shape_plan_cache;
|
||||
|
||||
pub use self::shape_run_cache::*;
|
||||
mod shape_run_cache;
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
pub use self::swash::*;
|
||||
#[cfg(feature = "swash")]
|
||||
|
|
|
|||
69
src/shape.rs
69
src/shape.rs
|
|
@ -52,6 +52,7 @@ impl Shaping {
|
|||
match self {
|
||||
#[cfg(feature = "swash")]
|
||||
Self::Basic => shape_skip(font_system, glyphs, line, attrs_list, start_run, end_run),
|
||||
#[cfg(not(feature = "shape-run-cache"))]
|
||||
Self::Advanced => shape_run(
|
||||
scratch,
|
||||
glyphs,
|
||||
|
|
@ -62,6 +63,17 @@ impl Shaping {
|
|||
end_run,
|
||||
span_rtl,
|
||||
),
|
||||
#[cfg(feature = "shape-run-cache")]
|
||||
Self::Advanced => shape_run_cached(
|
||||
scratch,
|
||||
glyphs,
|
||||
font_system,
|
||||
line,
|
||||
attrs_list,
|
||||
start_run,
|
||||
end_run,
|
||||
span_rtl,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -334,6 +346,63 @@ fn shape_run(
|
|||
scratch.scripts = scripts;
|
||||
}
|
||||
|
||||
#[cfg(feature = "shape-run-cache")]
|
||||
fn shape_run_cached(
|
||||
scratch: &mut ShapeBuffer,
|
||||
glyphs: &mut Vec<ShapeGlyph>,
|
||||
font_system: &mut FontSystem,
|
||||
line: &str,
|
||||
attrs_list: &AttrsList,
|
||||
start_run: usize,
|
||||
end_run: usize,
|
||||
span_rtl: bool,
|
||||
) {
|
||||
use crate::{AttrsOwned, ShapeRunKey};
|
||||
|
||||
let run_range = start_run..end_run;
|
||||
let mut key = ShapeRunKey {
|
||||
text: line[run_range.clone()].to_string(),
|
||||
default_attrs: AttrsOwned::new(attrs_list.defaults()),
|
||||
attrs_spans: Vec::new(),
|
||||
};
|
||||
for (attrs_range, attrs) in attrs_list.spans.overlapping(&run_range) {
|
||||
if attrs == &key.default_attrs {
|
||||
// Skip if attrs matches default attrs
|
||||
continue;
|
||||
}
|
||||
let start = max(attrs_range.start, start_run)
|
||||
.checked_sub(start_run)
|
||||
.unwrap_or(0);
|
||||
let end = min(attrs_range.end, end_run)
|
||||
.checked_sub(start_run)
|
||||
.unwrap_or(0);
|
||||
if end > start {
|
||||
let range = start..end;
|
||||
key.attrs_spans.push((range, attrs.clone()));
|
||||
}
|
||||
}
|
||||
if let Some(cache_glyphs) = font_system.shape_run_cache.get(&key) {
|
||||
// Use cached glyphs
|
||||
glyphs.extend_from_slice(&cache_glyphs);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill in cache if not already set
|
||||
let mut cache_glyphs = Vec::new();
|
||||
shape_run(
|
||||
scratch,
|
||||
&mut cache_glyphs,
|
||||
font_system,
|
||||
line,
|
||||
attrs_list,
|
||||
start_run,
|
||||
end_run,
|
||||
span_rtl,
|
||||
);
|
||||
glyphs.extend_from_slice(&cache_glyphs);
|
||||
font_system.shape_run_cache.insert(key, cache_glyphs);
|
||||
}
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
fn shape_skip(
|
||||
font_system: &mut FontSystem,
|
||||
|
|
|
|||
49
src/shape_run_cache.rs
Normal file
49
src/shape_run_cache.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#[cfg(not(feature = "std"))]
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use core::ops::Range;
|
||||
|
||||
use crate::{AttrsOwned, HashMap, ShapeGlyph};
|
||||
|
||||
/// Key for caching shape runs.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct ShapeRunKey {
|
||||
pub text: String,
|
||||
pub default_attrs: AttrsOwned,
|
||||
pub attrs_spans: Vec<(Range<usize>, AttrsOwned)>,
|
||||
}
|
||||
|
||||
/// A helper structure for caching shape runs.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ShapeRunCache {
|
||||
age: u64,
|
||||
cache: HashMap<ShapeRunKey, (u64, Vec<ShapeGlyph>)>,
|
||||
}
|
||||
|
||||
impl ShapeRunCache {
|
||||
/// Get cache item, updating age if found
|
||||
pub fn get(&mut self, key: &ShapeRunKey) -> Option<&Vec<ShapeGlyph>> {
|
||||
self.cache.get_mut(key).map(|(age, glyphs)| {
|
||||
*age = self.age;
|
||||
&*glyphs
|
||||
})
|
||||
}
|
||||
|
||||
/// Insert cache item with current age
|
||||
pub fn insert(&mut self, key: ShapeRunKey, glyphs: Vec<ShapeGlyph>) {
|
||||
self.cache.insert(key, (self.age, glyphs));
|
||||
}
|
||||
|
||||
/// Remove anything in the cache with an age older than keep_ages
|
||||
pub fn trim(&mut self, keep_ages: u64) {
|
||||
self.cache
|
||||
.retain(|_key, (age, _glyphs)| *age + keep_ages >= self.age);
|
||||
// Increase age
|
||||
self.age += 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for ShapeRunCache {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_tuple("ShapeRunCache").finish()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue