From 3227efef89cb4f492f7783eb1f86137710588af8 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 5 Oct 2022 11:28:05 -0600 Subject: [PATCH] Improve performance of layout lines --- examples/text/src/font/layout.rs | 4 +- examples/text/src/font/matches.rs | 12 +++--- examples/text/src/font/mod.rs | 13 +++++++ examples/text/src/font/shape.rs | 25 ++++++++---- examples/text/src/main.rs | 63 ++++++++++++++++++++++--------- 5 files changed, 85 insertions(+), 32 deletions(-) diff --git a/examples/text/src/font/layout.rs b/examples/text/src/font/layout.rs index 992ad3c6..c7661540 100644 --- a/examples/text/src/font/layout.rs +++ b/examples/text/src/font/layout.rs @@ -1,7 +1,8 @@ use core::marker::PhantomData; +use super::FontLineIndex; + pub struct FontLayoutGlyph<'a, T: 'a> { - pub line_i: usize, pub start: usize, pub end: usize, pub x: f32, @@ -14,6 +15,7 @@ pub struct FontLayoutGlyph<'a, T: 'a> { } pub struct FontLayoutLine<'a> { + pub line_i: FontLineIndex, pub glyphs: Vec>, } diff --git a/examples/text/src/font/matches.rs b/examples/text/src/font/matches.rs index ac7b42d4..56fb1a36 100644 --- a/examples/text/src/font/matches.rs +++ b/examples/text/src/font/matches.rs @@ -1,11 +1,11 @@ -use super::{Font, FontShapeGlyph, FontShapeLine, FontShapeSpan}; +use super::{Font, FontLineIndex, FontShapeGlyph, FontShapeLine, FontShapeSpan}; pub struct FontMatches<'a> { pub fonts: Vec<&'a Font<'a>>, } impl<'a> FontMatches<'a> { - fn shape_span(&self, line_i: usize, line: &str, start_span: usize, end_span: usize) -> FontShapeSpan { + fn shape_span(&self, line: &str, start_span: usize, end_span: usize) -> FontShapeSpan { let span = &line[start_span..end_span]; let mut spans_by_font = Vec::with_capacity(self.fonts.len()); @@ -44,7 +44,6 @@ impl<'a> FontMatches<'a> { let inner = font.rusttype.glyph(rusttype::GlyphId(info.glyph_id as u16)); glyphs.push(FontShapeGlyph { - line_i, start: start_span + info.cluster as usize, end: end_span, // Set later x_advance, @@ -114,7 +113,7 @@ impl<'a> FontMatches<'a> { spans_by_font.remove(least_misses_i).1 } - pub fn shape_line(&self, line_i: usize, line: &str) -> FontShapeLine { + pub fn shape_line(&self, line_i: FontLineIndex, line: &str) -> FontShapeLine { use unicode_script::{Script, UnicodeScript}; //TODO: more special handling of characters @@ -130,7 +129,7 @@ impl<'a> FontMatches<'a> { let cur = c.script(); if prev != cur && prev != Script::Unknown { // No combination, start new span - spans.push(self.shape_span(line_i, line, start, i)); + spans.push(self.shape_span(line, start, i)); start = i; prev = Script::Unknown; } else { @@ -138,7 +137,7 @@ impl<'a> FontMatches<'a> { } } - spans.push(self.shape_span(line_i, line, start, line.len())); + spans.push(self.shape_span(line, start, line.len())); let bidi = unicode_bidi::BidiInfo::new(line, None); let rtl = if bidi.paragraphs.is_empty() { @@ -149,6 +148,7 @@ impl<'a> FontMatches<'a> { }; FontShapeLine { + line_i, rtl, spans, } diff --git a/examples/text/src/font/mod.rs b/examples/text/src/font/mod.rs index a596347c..eeafb27f 100644 --- a/examples/text/src/font/mod.rs +++ b/examples/text/src/font/mod.rs @@ -10,6 +10,19 @@ mod shape; pub use self::system::*; mod system; +#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] +pub struct FontLineIndex(usize); + +impl FontLineIndex { + pub fn new(index: usize) -> Self { + Self(index) + } + + pub fn get(&self) -> usize { + self.0 + } +} + pub struct Font<'a> { data: &'a [u8], pub rustybuzz: rustybuzz::Face<'a>, diff --git a/examples/text/src/font/shape.rs b/examples/text/src/font/shape.rs index 060fd1c9..2aae5f6e 100644 --- a/examples/text/src/font/shape.rs +++ b/examples/text/src/font/shape.rs @@ -2,10 +2,9 @@ use ab_glyph::Font; use core::marker::PhantomData; -use super::{FontLayoutGlyph, FontLayoutLine}; +use super::{FontLayoutGlyph, FontLayoutLine, FontLineIndex}; pub struct FontShapeGlyph<'a> { - pub line_i: usize, pub start: usize, pub end: usize, pub x_advance: f32, @@ -26,12 +25,13 @@ pub struct FontShapeSpan<'a> { } pub struct FontShapeLine<'a> { + pub line_i: FontLineIndex, pub rtl: bool, pub spans: Vec>, } impl<'a> FontShapeLine<'a> { - pub fn layout(&self, font_size: i32, line_width: i32, lines: &mut Vec>) { + pub fn layout(&self, font_size: i32, line_width: i32, layout_lines: &mut Vec>, mut layout_i: usize) { let mut push_line = true; let mut glyphs = Vec::new(); @@ -62,7 +62,10 @@ impl<'a> FontShapeLine<'a> { if x < 0.0 { let mut glyphs_swap = Vec::new(); std::mem::swap(&mut glyphs, &mut glyphs_swap); - lines.push(FontLayoutLine { glyphs: glyphs_swap }); + layout_lines.push(FontLayoutLine { + line_i: self.line_i, + glyphs: glyphs_swap + }); x = line_width as f32 - x_advance; y = 0.0; @@ -71,8 +74,11 @@ impl<'a> FontShapeLine<'a> { if x + x_advance > line_width as f32 { let mut glyphs_swap = Vec::new(); std::mem::swap(&mut glyphs, &mut glyphs_swap); - lines.push(FontLayoutLine { glyphs: glyphs_swap }); - push_line = false; + layout_lines.insert(layout_i, FontLayoutLine { + line_i: self.line_i, + glyphs: glyphs_swap + }); + layout_i += 1; x = 0.0; y = 0.0; @@ -99,7 +105,6 @@ impl<'a> FontShapeLine<'a> { )); glyphs.push(FontLayoutGlyph { - line_i: glyph.line_i, start: glyph.start, end: glyph.end, x, @@ -119,7 +124,11 @@ impl<'a> FontShapeLine<'a> { } if push_line { - lines.push(FontLayoutLine { glyphs }); + layout_lines.insert(layout_i, FontLayoutLine { + line_i: self.line_i, + glyphs + }); + layout_i += 1; } } } diff --git a/examples/text/src/main.rs b/examples/text/src/main.rs index a0648210..ab3dd564 100644 --- a/examples/text/src/main.rs +++ b/examples/text/src/main.rs @@ -8,6 +8,7 @@ use std::{ }; use text::{ Font, + FontLineIndex, FontSystem, }; @@ -91,8 +92,9 @@ fn main() { let mut redraw = true; let mut rehit = false; let mut relayout = true; + let mut relayout_lines = Vec::new(); let mut reshape = true; - let mut reshape_lines = Vec::new(); + let mut reshape_lines = Vec::::new(); let mut scroll = 0; loop { let (mut font_size, mut line_height) = font_sizes[font_size_i]; @@ -106,13 +108,14 @@ fn main() { )); let line_x = 8 * display_scale; + let line_width = window.width() as i32 - line_x * 2; if reshape { let instant = Instant::now(); shape_lines.clear(); for (line_i, text_line) in text_lines.iter().enumerate() { - shape_lines.push(font_matches.shape_line(line_i, text_line)); + shape_lines.push(font_matches.shape_line(FontLineIndex::new(line_i), text_line)); } reshape = false; @@ -126,12 +129,11 @@ fn main() { for line_i in reshape_lines.drain(..) { let instant = Instant::now(); - shape_lines[line_i] = font_matches.shape_line(line_i, &text_lines[line_i]); - - relayout = true; + shape_lines[line_i.get()] = font_matches.shape_line(line_i, &text_lines[line_i.get()]); + relayout_lines.push(line_i); let duration = instant.elapsed(); - eprintln!("reshape line {}: {:?}", line_i, duration); + eprintln!("reshape line {}: {:?}", line_i.get(), duration); } if relayout { @@ -139,17 +141,44 @@ fn main() { layout_lines.clear(); for line in shape_lines.iter() { - let line_width = window.width() as i32 - line_x * 2; - line.layout(font_size, line_width, &mut layout_lines); + let layout_i = layout_lines.len(); + line.layout(font_size, line_width, &mut layout_lines, layout_i); } relayout = false; + relayout_lines.clear(); redraw = true; let duration = instant.elapsed(); eprintln!("relayout: {:?}", duration); } + for line_i in relayout_lines.drain(..) { + let instant = Instant::now(); + + let mut insert_opt = None; + let mut layout_i = 0; + while layout_i < layout_lines.len() { + let layout_line = &layout_lines[layout_i]; + if layout_line.line_i == line_i { + if insert_opt.is_none() { + insert_opt = Some(layout_i); + } + layout_lines.remove(layout_i); + } else { + layout_i += 1; + } + } + + let shape_line = &shape_lines[line_i.get()]; + shape_line.layout(font_size, line_width, &mut layout_lines, insert_opt.unwrap()); + + redraw = true; + + let duration = instant.elapsed(); + eprintln!("relayout line {}: {:?}", line_i.get(), duration); + } + if rehit { let instant = Instant::now(); @@ -222,7 +251,7 @@ fn main() { Color::rgba(0xFF, 0xFF, 0xFF, 0x20) ); - let text_line = &text_lines[glyph.line_i]; + let text_line = &text_lines[line.line_i.get()]; eprintln!("{}, {}: '{}'", glyph.start, glyph.end, &text_line[glyph.start..glyph.end]); } } @@ -288,18 +317,18 @@ fn main() { if cursor_glyph > 0 { cursor_glyph -= 1; let glyph = &line.glyphs[cursor_glyph]; - let text_line = &mut text_lines[glyph.line_i]; + let text_line = &mut text_lines[line.line_i.get()]; text_line.remove(glyph.start); - reshape_lines.push(glyph.line_i); + reshape_lines.push(line.line_i); } }, orbclient::K_DEL => { let line = &layout_lines[cursor_line]; if cursor_glyph < line.glyphs.len() { let glyph = &line.glyphs[cursor_glyph]; - let text_line = &mut text_lines[glyph.line_i]; + let text_line = &mut text_lines[line.line_i.get()]; text_line.remove(glyph.start); - reshape_lines.push(glyph.line_i); + reshape_lines.push(line.line_i); } }, orbclient::K_0 => { @@ -322,19 +351,19 @@ fn main() { if cursor_glyph >= line.glyphs.len() { match line.glyphs.last() { Some(glyph) => { - let text_line = &mut text_lines[glyph.line_i]; + let text_line = &mut text_lines[line.line_i.get()]; text_line.insert(glyph.end, event.character); cursor_glyph += 1; - reshape_lines.push(glyph.line_i); + reshape_lines.push(line.line_i); }, None => () // TODO } } else { let glyph = &line.glyphs[cursor_glyph]; - let text_line = &mut text_lines[glyph.line_i]; + let text_line = &mut text_lines[line.line_i.get()]; text_line.insert(glyph.start, event.character); cursor_glyph += 1; - reshape_lines.push(glyph.line_i); + reshape_lines.push(line.line_i); } }, EventOption::Mouse(event) => {