Improve performance of layout lines

This commit is contained in:
Jeremy Soller 2022-10-05 11:28:05 -06:00
parent 682407d15a
commit 3227efef89
No known key found for this signature in database
GPG key ID: 87F211AF2BE4C2FE
5 changed files with 85 additions and 32 deletions

View file

@ -1,7 +1,8 @@
use core::marker::PhantomData; use core::marker::PhantomData;
use super::FontLineIndex;
pub struct FontLayoutGlyph<'a, T: 'a> { pub struct FontLayoutGlyph<'a, T: 'a> {
pub line_i: usize,
pub start: usize, pub start: usize,
pub end: usize, pub end: usize,
pub x: f32, pub x: f32,
@ -14,6 +15,7 @@ pub struct FontLayoutGlyph<'a, T: 'a> {
} }
pub struct FontLayoutLine<'a> { pub struct FontLayoutLine<'a> {
pub line_i: FontLineIndex,
pub glyphs: Vec<FontLayoutGlyph<'a, ()>>, pub glyphs: Vec<FontLayoutGlyph<'a, ()>>,
} }

View file

@ -1,11 +1,11 @@
use super::{Font, FontShapeGlyph, FontShapeLine, FontShapeSpan}; use super::{Font, FontLineIndex, FontShapeGlyph, FontShapeLine, FontShapeSpan};
pub struct FontMatches<'a> { pub struct FontMatches<'a> {
pub fonts: Vec<&'a Font<'a>>, pub fonts: Vec<&'a Font<'a>>,
} }
impl<'a> FontMatches<'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 span = &line[start_span..end_span];
let mut spans_by_font = Vec::with_capacity(self.fonts.len()); 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)); let inner = font.rusttype.glyph(rusttype::GlyphId(info.glyph_id as u16));
glyphs.push(FontShapeGlyph { glyphs.push(FontShapeGlyph {
line_i,
start: start_span + info.cluster as usize, start: start_span + info.cluster as usize,
end: end_span, // Set later end: end_span, // Set later
x_advance, x_advance,
@ -114,7 +113,7 @@ impl<'a> FontMatches<'a> {
spans_by_font.remove(least_misses_i).1 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}; use unicode_script::{Script, UnicodeScript};
//TODO: more special handling of characters //TODO: more special handling of characters
@ -130,7 +129,7 @@ impl<'a> FontMatches<'a> {
let cur = c.script(); let cur = c.script();
if prev != cur && prev != Script::Unknown { if prev != cur && prev != Script::Unknown {
// No combination, start new span // 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; start = i;
prev = Script::Unknown; prev = Script::Unknown;
} else { } 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 bidi = unicode_bidi::BidiInfo::new(line, None);
let rtl = if bidi.paragraphs.is_empty() { let rtl = if bidi.paragraphs.is_empty() {
@ -149,6 +148,7 @@ impl<'a> FontMatches<'a> {
}; };
FontShapeLine { FontShapeLine {
line_i,
rtl, rtl,
spans, spans,
} }

View file

@ -10,6 +10,19 @@ mod shape;
pub use self::system::*; pub use self::system::*;
mod 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> { pub struct Font<'a> {
data: &'a [u8], data: &'a [u8],
pub rustybuzz: rustybuzz::Face<'a>, pub rustybuzz: rustybuzz::Face<'a>,

View file

@ -2,10 +2,9 @@
use ab_glyph::Font; use ab_glyph::Font;
use core::marker::PhantomData; use core::marker::PhantomData;
use super::{FontLayoutGlyph, FontLayoutLine}; use super::{FontLayoutGlyph, FontLayoutLine, FontLineIndex};
pub struct FontShapeGlyph<'a> { pub struct FontShapeGlyph<'a> {
pub line_i: usize,
pub start: usize, pub start: usize,
pub end: usize, pub end: usize,
pub x_advance: f32, pub x_advance: f32,
@ -26,12 +25,13 @@ pub struct FontShapeSpan<'a> {
} }
pub struct FontShapeLine<'a> { pub struct FontShapeLine<'a> {
pub line_i: FontLineIndex,
pub rtl: bool, pub rtl: bool,
pub spans: Vec<FontShapeSpan<'a>>, pub spans: Vec<FontShapeSpan<'a>>,
} }
impl<'a> FontShapeLine<'a> { impl<'a> FontShapeLine<'a> {
pub fn layout(&self, font_size: i32, line_width: i32, lines: &mut Vec<FontLayoutLine<'a>>) { pub fn layout(&self, font_size: i32, line_width: i32, layout_lines: &mut Vec<FontLayoutLine<'a>>, mut layout_i: usize) {
let mut push_line = true; let mut push_line = true;
let mut glyphs = Vec::new(); let mut glyphs = Vec::new();
@ -62,7 +62,10 @@ impl<'a> FontShapeLine<'a> {
if x < 0.0 { if x < 0.0 {
let mut glyphs_swap = Vec::new(); let mut glyphs_swap = Vec::new();
std::mem::swap(&mut glyphs, &mut glyphs_swap); 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; x = line_width as f32 - x_advance;
y = 0.0; y = 0.0;
@ -71,8 +74,11 @@ impl<'a> FontShapeLine<'a> {
if x + x_advance > line_width as f32 { if x + x_advance > line_width as f32 {
let mut glyphs_swap = Vec::new(); let mut glyphs_swap = Vec::new();
std::mem::swap(&mut glyphs, &mut glyphs_swap); std::mem::swap(&mut glyphs, &mut glyphs_swap);
lines.push(FontLayoutLine { glyphs: glyphs_swap }); layout_lines.insert(layout_i, FontLayoutLine {
push_line = false; line_i: self.line_i,
glyphs: glyphs_swap
});
layout_i += 1;
x = 0.0; x = 0.0;
y = 0.0; y = 0.0;
@ -99,7 +105,6 @@ impl<'a> FontShapeLine<'a> {
)); ));
glyphs.push(FontLayoutGlyph { glyphs.push(FontLayoutGlyph {
line_i: glyph.line_i,
start: glyph.start, start: glyph.start,
end: glyph.end, end: glyph.end,
x, x,
@ -119,7 +124,11 @@ impl<'a> FontShapeLine<'a> {
} }
if push_line { if push_line {
lines.push(FontLayoutLine { glyphs }); layout_lines.insert(layout_i, FontLayoutLine {
line_i: self.line_i,
glyphs
});
layout_i += 1;
} }
} }
} }

View file

@ -8,6 +8,7 @@ use std::{
}; };
use text::{ use text::{
Font, Font,
FontLineIndex,
FontSystem, FontSystem,
}; };
@ -91,8 +92,9 @@ fn main() {
let mut redraw = true; let mut redraw = true;
let mut rehit = false; let mut rehit = false;
let mut relayout = true; let mut relayout = true;
let mut relayout_lines = Vec::new();
let mut reshape = true; let mut reshape = true;
let mut reshape_lines = Vec::new(); let mut reshape_lines = Vec::<FontLineIndex>::new();
let mut scroll = 0; let mut scroll = 0;
loop { loop {
let (mut font_size, mut line_height) = font_sizes[font_size_i]; 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_x = 8 * display_scale;
let line_width = window.width() as i32 - line_x * 2;
if reshape { if reshape {
let instant = Instant::now(); let instant = Instant::now();
shape_lines.clear(); shape_lines.clear();
for (line_i, text_line) in text_lines.iter().enumerate() { 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; reshape = false;
@ -126,12 +129,11 @@ fn main() {
for line_i in reshape_lines.drain(..) { for line_i in reshape_lines.drain(..) {
let instant = Instant::now(); let instant = Instant::now();
shape_lines[line_i] = font_matches.shape_line(line_i, &text_lines[line_i]); shape_lines[line_i.get()] = font_matches.shape_line(line_i, &text_lines[line_i.get()]);
relayout_lines.push(line_i);
relayout = true;
let duration = instant.elapsed(); let duration = instant.elapsed();
eprintln!("reshape line {}: {:?}", line_i, duration); eprintln!("reshape line {}: {:?}", line_i.get(), duration);
} }
if relayout { if relayout {
@ -139,17 +141,44 @@ fn main() {
layout_lines.clear(); layout_lines.clear();
for line in shape_lines.iter() { for line in shape_lines.iter() {
let line_width = window.width() as i32 - line_x * 2; let layout_i = layout_lines.len();
line.layout(font_size, line_width, &mut layout_lines); line.layout(font_size, line_width, &mut layout_lines, layout_i);
} }
relayout = false; relayout = false;
relayout_lines.clear();
redraw = true; redraw = true;
let duration = instant.elapsed(); let duration = instant.elapsed();
eprintln!("relayout: {:?}", duration); 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 { if rehit {
let instant = Instant::now(); let instant = Instant::now();
@ -222,7 +251,7 @@ fn main() {
Color::rgba(0xFF, 0xFF, 0xFF, 0x20) 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]); eprintln!("{}, {}: '{}'", glyph.start, glyph.end, &text_line[glyph.start..glyph.end]);
} }
} }
@ -288,18 +317,18 @@ fn main() {
if cursor_glyph > 0 { if cursor_glyph > 0 {
cursor_glyph -= 1; cursor_glyph -= 1;
let glyph = &line.glyphs[cursor_glyph]; 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); text_line.remove(glyph.start);
reshape_lines.push(glyph.line_i); reshape_lines.push(line.line_i);
} }
}, },
orbclient::K_DEL => { orbclient::K_DEL => {
let line = &layout_lines[cursor_line]; let line = &layout_lines[cursor_line];
if cursor_glyph < line.glyphs.len() { if cursor_glyph < line.glyphs.len() {
let glyph = &line.glyphs[cursor_glyph]; 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); text_line.remove(glyph.start);
reshape_lines.push(glyph.line_i); reshape_lines.push(line.line_i);
} }
}, },
orbclient::K_0 => { orbclient::K_0 => {
@ -322,19 +351,19 @@ fn main() {
if cursor_glyph >= line.glyphs.len() { if cursor_glyph >= line.glyphs.len() {
match line.glyphs.last() { match line.glyphs.last() {
Some(glyph) => { 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); text_line.insert(glyph.end, event.character);
cursor_glyph += 1; cursor_glyph += 1;
reshape_lines.push(glyph.line_i); reshape_lines.push(line.line_i);
}, },
None => () // TODO None => () // TODO
} }
} else { } else {
let glyph = &line.glyphs[cursor_glyph]; 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); text_line.insert(glyph.start, event.character);
cursor_glyph += 1; cursor_glyph += 1;
reshape_lines.push(glyph.line_i); reshape_lines.push(line.line_i);
} }
}, },
EventOption::Mouse(event) => { EventOption::Mouse(event) => {