Skip to content

Commit

Permalink
cache by sugarline hash
Browse files Browse the repository at this point in the history
  • Loading branch information
raphamorim committed Apr 25, 2024
1 parent d1c37d9 commit fff46e6
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 159 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sugarloaf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ xi-unicode = "0.3.0"
approx = "0.5.1"
fnv = "1.0.7"
swash = "0.1.15"
lru = "0.12.3"
# smallvec = "1.13.1"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
Expand Down
104 changes: 39 additions & 65 deletions sugarloaf/src/layout/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,34 @@ use super::span_style::*;
use super::MAX_ID;
use crate::font::{FontContext, FontLibrary, FontLibraryData};
use crate::layout::render_data::{RenderData, RunCacheEntry};
use std::collections::HashMap;
use lru::LruCache;
use swash::shape::{self, ShapeContext};
use swash::text::cluster::{CharCluster, CharInfo, Parser, Token};
use swash::text::{analyze, Language, Script};
use swash::{Setting, Synthesis};

#[derive(Default)]
pub struct RunCache {
inner: HashMap<usize, RunCacheEntry>,
inner: LruCache<u64, RunCacheEntry>,
}

impl RunCache {
fn clear(&mut self) {
self.inner.clear();
#[inline]
fn new() -> Self {
Self {
inner: LruCache::new(std::num::NonZeroUsize::new(4000).unwrap()),
}
}

#[inline]
fn insert(&mut self, line_number: usize, data: RunCacheEntry) {
fn insert(&mut self, line_hash: u64, data: RunCacheEntry) {
if data.runs.is_empty() {
return;
}

if let Some(line) = self.inner.get_mut(&line_number) {
if let Some(line) = self.inner.get_mut(&line_hash) {
*line = data;
} else {
self.inner.insert(line_number, data);
self.inner.put(line_hash, data);
}
}
}
Expand All @@ -63,7 +65,7 @@ impl LayoutContext {
bidi: BidiResolver::new(),
scx: ShapeContext::new(),
state: BuilderState::new(),
cache: RunCache::default(),
cache: RunCache::new(),
}
}

Expand All @@ -80,29 +82,6 @@ impl LayoutContext {
direction: Direction,
_language: Option<Language>,
scale: f32,
) -> ParagraphBuilder {
self.cache.clear();
self.state.clear();
self.state.begin();
self.state.scale = scale;
ParagraphBuilder {
fcx: &mut self.fcx,
bidi: &mut self.bidi,
needs_bidi: false,
dir: direction,
fonts: &self.fonts,
scx: &mut self.scx,
s: &mut self.state,
last_offset: 0,
cache: &mut self.cache,
}
}

#[inline]
pub fn cached_builder(
&mut self,
direction: Direction,
scale: f32,
) -> ParagraphBuilder {
self.state.clear();
self.state.begin();
Expand Down Expand Up @@ -168,6 +147,14 @@ impl<'a> ParagraphBuilder<'a> {
// }
// }

#[inline]
pub fn set_hash(&mut self, hash: u64) {
if hash > 0 {
let current_line = self.s.current_line();
self.s.lines[current_line].hash = Some(hash);
}
}

#[inline]
pub fn new_line(&mut self) {
self.s.new_line();
Expand Down Expand Up @@ -368,17 +355,7 @@ impl<'a> ParagraphBuilder<'a> {

/// Consumes the builder and fills the specified paragraph with the result.
pub fn build_into(mut self, render_data: &mut RenderData) {
self.resolve(render_data, None);
render_data.finish();
}

/// Consumes the builder and fills the specified paragraph with the result.
pub fn build_into_specific_lines(
mut self,
render_data: &mut RenderData,
lines: &[usize],
) {
self.resolve(render_data, Some(lines));
self.resolve(render_data);
render_data.finish();
}

Expand All @@ -391,25 +368,24 @@ impl<'a> ParagraphBuilder<'a> {
}

impl<'a> ParagraphBuilder<'a> {
#[inline]
fn process_from_cache(
&mut self,
render_data: &mut RenderData,
line_number: usize,
current_line: usize,
) -> bool {
if let Some(data) = self.cache.inner.get(&line_number) {
render_data.push_run_from_cached_line(data);
if let Some(line_hash) = self.s.lines[current_line].hash {
if let Some(data) = self.cache.inner.get(&line_hash) {
render_data.push_run_from_cached_line(data, current_line as u32);

true
} else {
false
return true;
}
}

false
}

fn resolve(
&mut self,
render_data: &mut RenderData,
lines_to_render: Option<&[usize]>,
) {
fn resolve(&mut self, render_data: &mut RenderData) {
// Bit of a hack: add a single trailing space fragment to account for
// empty paragraphs and to force an extra break if the paragraph ends
// in a newline.
Expand All @@ -420,18 +396,12 @@ impl<'a> ParagraphBuilder<'a> {
// self.push_char(PDI);
// }

let lines_to_render = lines_to_render.unwrap_or_default();
let render_specific_lines = !lines_to_render.is_empty();

for line_number in 0..self.s.lines.len() {
// In case should render only requested lines
// and the line number isn't part of the requested then process from cache
if render_specific_lines && !lines_to_render.contains(&line_number) {
if self.process_from_cache(render_data, line_number) {
continue;
}
} else {
self.cache.inner.remove(&line_number);
// if render_specific_lines && !lines_to_render.contains(&line_number) {
if self.process_from_cache(render_data, line_number) {
continue;
}

let line = &mut self.s.lines[line_number];
Expand Down Expand Up @@ -691,7 +661,9 @@ fn shape_item(
current_line,
) {}

cache.insert(current_line, render_data.last_cached_run.to_owned());
if let Some(line_hash) = state.lines[current_line].hash {
cache.insert(line_hash, render_data.last_cached_run.to_owned());
}
} else {
let chars = state.lines[current_line].text.content[range.clone()]
.iter()
Expand Down Expand Up @@ -729,7 +701,9 @@ fn shape_item(
current_line,
) {}

cache.insert(current_line, render_data.last_cached_run.to_owned());
if let Some(line_hash) = state.lines[current_line].hash {
cache.insert(line_hash, render_data.last_cached_run.to_owned());
}
}
Some(())
}
Expand Down
2 changes: 2 additions & 0 deletions sugarloaf/src/layout/builder_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ pub struct BuilderLine {
pub items: Vec<ItemData>,
/// Span index per character.
pub styles: Vec<FragmentStyle>,
/// Line Hash
pub hash: Option<u64>,
}

/// Builder state.
Expand Down
55 changes: 36 additions & 19 deletions sugarloaf/src/layout/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,28 @@ pub struct Fragment {
style: FragmentStyle,
}

#[derive(PartialEq, Debug, Clone)]
pub struct LineFragments {
data: Vec<Fragment>,
hash: u64,
}

#[derive(Clone)]
pub struct Content {
pub fragments: Vec<Vec<Fragment>>,
pub fragments: Vec<LineFragments>,
pub text: String,
pub current_line: usize,
}

impl Default for Content {
fn default() -> Self {
Self {
fragments: vec![vec![]],
fragments: vec![LineFragments {
data: vec![],
// 0 means uninitialized hash
// that will reflect in uncached
hash: 0,
}],
text: String::default(),
current_line: 0,
}
Expand All @@ -50,7 +61,9 @@ impl Content {
#[inline]
pub fn layout(&self, lcx: &mut ParagraphBuilder) {
for line in 0..self.current_line + 1 {
for e in &self.fragments[line] {
lcx.set_hash(self.fragments[line].hash);

for e in &self.fragments[line].data {
if e.start < e.end {
if let Some(s) = self.text.get(e.start as usize..e.end as usize) {
lcx.add_text(s, Some(e.style));
Expand Down Expand Up @@ -81,8 +94,8 @@ impl Content {
self.text.insert_str(offset, text);
let len = text.len() as u32;
let frag_index = self.fragment_from_offset(offset).unwrap_or(0);
self.fragments[self.current_line][frag_index].end += len;
for frag in &mut self.fragments[self.current_line][frag_index + 1..] {
self.fragments[self.current_line].data[frag_index].end += len;
for frag in &mut self.fragments[self.current_line].data[frag_index + 1..] {
frag.start += len;
frag.end += len;
}
Expand All @@ -97,8 +110,8 @@ impl Content {
self.text.insert(offset, ch);
let len = ch.len_utf8() as u32;
let frag_index = self.fragment_from_offset(offset).unwrap_or(0);
self.fragments[self.current_line][frag_index].end += len;
for frag in &mut self.fragments[self.current_line][frag_index + 1..] {
self.fragments[self.current_line].data[frag_index].end += len;
for frag in &mut self.fragments[self.current_line].data[frag_index + 1..] {
frag.start += len;
frag.end += len;
}
Expand All @@ -117,7 +130,7 @@ impl Content {
}

fn fragment_from_offset(&self, offset: usize) -> Option<usize> {
for (i, frag) in self.fragments[self.current_line].iter().enumerate() {
for (i, frag) in self.fragments[self.current_line].data.iter().enumerate() {
if offset >= frag.start as usize && offset < frag.end as usize {
return Some(i);
}
Expand All @@ -137,23 +150,24 @@ impl ContentBuilder {
let start = self.content.text.len() as u32;
self.content.text.push_str(text);
let end = self.content.text.len() as u32;
self.content.fragments[self.content.current_line].push(Fragment {
start,
end,
style,
});
self.content.fragments[self.content.current_line]
.data
.push(Fragment { start, end, style });
}

#[inline]
pub fn add_char(&mut self, text: char, style: FragmentStyle) {
let start = self.content.text.len() as u32;
self.content.text.push(text);
let end = self.content.text.len() as u32;
self.content.fragments[self.content.current_line].push(Fragment {
start,
end,
style,
});
self.content.fragments[self.content.current_line]
.data
.push(Fragment { start, end, style });
}

#[inline]
pub fn set_current_line_hash(&mut self, hash: u64) {
self.content.fragments[self.content.current_line].hash = hash;
}

#[inline]
Expand All @@ -165,7 +179,10 @@ impl ContentBuilder {
self.add_char('\n', FragmentStyle::default());

self.content.current_line += 1;
self.content.fragments.push(vec![]);
self.content.fragments.push(LineFragments {
data: vec![],
hash: 0,
});
}

#[inline]
Expand Down
8 changes: 6 additions & 2 deletions sugarloaf/src/layout/render_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@ pub struct RunCacheEntry {
}

impl RenderData {
pub(super) fn push_run_from_cached_line(&mut self, cached_entry: &RunCacheEntry) {
pub(super) fn push_run_from_cached_line(
&mut self,
cached_entry: &RunCacheEntry,
line: u32,
) {
// Every time a line is cached we need to rebuild the indexes
// so RunData, Clusters, DetailedClusterData and Glyphs need to be
// pointed correctly across each other otherwise will lead to panic
Expand Down Expand Up @@ -160,7 +164,7 @@ impl RenderData {
coords: (coords_start, coords_end),
clusters: (clusters_start, clusters_end),
span: cached_run.span,
line: cached_run.line,
line,
font: cached_run.font,
color: cached_run.color,
background_color: cached_run.background_color,
Expand Down
Loading

0 comments on commit fff46e6

Please sign in to comment.