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
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add `shape-run-cache` feature, that can significantly improve shaping performance
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Remove editor-libcosmic, see cosmic-edit instead
|
- Remove editor-libcosmic, see cosmic-edit instead
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@ features = ["hardcoded-data"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std", "swash", "fontconfig"]
|
default = ["std", "swash", "fontconfig"]
|
||||||
|
fontconfig = ["fontdb/fontconfig", "std"]
|
||||||
no_std = ["rustybuzz/libm", "hashbrown"]
|
no_std = ["rustybuzz/libm", "hashbrown"]
|
||||||
|
shape-run-cache = []
|
||||||
std = [
|
std = [
|
||||||
"fontdb/memmap",
|
"fontdb/memmap",
|
||||||
"fontdb/std",
|
"fontdb/std",
|
||||||
|
|
@ -47,7 +49,6 @@ std = [
|
||||||
vi = ["modit", "syntect", "cosmic_undo_2"]
|
vi = ["modit", "syntect", "cosmic_undo_2"]
|
||||||
wasm-web = ["sys-locale?/js"]
|
wasm-web = ["sys-locale?/js"]
|
||||||
warn_on_missing_glyphs = []
|
warn_on_missing_glyphs = []
|
||||||
fontconfig = ["fontdb/fontconfig", "std"]
|
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "layout"
|
name = "layout"
|
||||||
|
|
|
||||||
|
|
@ -252,7 +252,7 @@ impl AttrsOwned {
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct AttrsList {
|
pub struct AttrsList {
|
||||||
defaults: AttrsOwned,
|
defaults: AttrsOwned,
|
||||||
spans: RangeMap<usize, AttrsOwned>,
|
pub(crate) spans: RangeMap<usize, AttrsOwned>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttrsList {
|
impl AttrsList {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ pub struct FontSystem {
|
||||||
|
|
||||||
/// Cache for rustybuzz shape plans.
|
/// Cache for rustybuzz shape plans.
|
||||||
shape_plan_cache: ShapePlanCache,
|
shape_plan_cache: ShapePlanCache,
|
||||||
|
|
||||||
|
/// Cache for shaped runs
|
||||||
|
#[cfg(feature = "shape-run-cache")]
|
||||||
|
pub shape_run_cache: crate::ShapeRunCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for FontSystem {
|
impl fmt::Debug for FontSystem {
|
||||||
|
|
@ -81,6 +85,8 @@ impl FontSystem {
|
||||||
font_cache: Default::default(),
|
font_cache: Default::default(),
|
||||||
font_matches_cache: Default::default(),
|
font_matches_cache: Default::default(),
|
||||||
shape_plan_cache: ShapePlanCache::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::*;
|
use self::shape_plan_cache::*;
|
||||||
mod shape_plan_cache;
|
mod shape_plan_cache;
|
||||||
|
|
||||||
|
pub use self::shape_run_cache::*;
|
||||||
|
mod shape_run_cache;
|
||||||
|
|
||||||
#[cfg(feature = "swash")]
|
#[cfg(feature = "swash")]
|
||||||
pub use self::swash::*;
|
pub use self::swash::*;
|
||||||
#[cfg(feature = "swash")]
|
#[cfg(feature = "swash")]
|
||||||
|
|
|
||||||
69
src/shape.rs
69
src/shape.rs
|
|
@ -52,6 +52,7 @@ impl Shaping {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "swash")]
|
#[cfg(feature = "swash")]
|
||||||
Self::Basic => shape_skip(font_system, glyphs, line, attrs_list, start_run, end_run),
|
Self::Basic => shape_skip(font_system, glyphs, line, attrs_list, start_run, end_run),
|
||||||
|
#[cfg(not(feature = "shape-run-cache"))]
|
||||||
Self::Advanced => shape_run(
|
Self::Advanced => shape_run(
|
||||||
scratch,
|
scratch,
|
||||||
glyphs,
|
glyphs,
|
||||||
|
|
@ -62,6 +63,17 @@ impl Shaping {
|
||||||
end_run,
|
end_run,
|
||||||
span_rtl,
|
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;
|
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")]
|
#[cfg(feature = "swash")]
|
||||||
fn shape_skip(
|
fn shape_skip(
|
||||||
font_system: &mut FontSystem,
|
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