From 64e469718bdcf72b4a2566190726ab3b711c0722 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Tue, 18 Jul 2023 19:18:56 -0700 Subject: [PATCH] Use a scratch buffer to reduce allocations This commit adds a new ShapeBuffer type, which contains some buffers tha are commonly re-created during the shaping process. New APIs are added that take this structure, and old APIs are turned into wrappers around the new API. The goal is to reduce the number of per-layout allocations that happen in a typical layout call. --- src/buffer.rs | 22 +++- src/buffer_line.rs | 34 ++++- src/font/fallback/mod.rs | 4 +- src/shape.rs | 271 ++++++++++++++++++++++++++++++--------- 4 files changed, 265 insertions(+), 66 deletions(-) diff --git a/src/buffer.rs b/src/buffer.rs index fd85c74..227b228 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -10,7 +10,7 @@ use unicode_segmentation::UnicodeSegmentation; use crate::{ Attrs, AttrsList, BidiParagraphs, BorrowedWithFontSystem, BufferLine, Color, FontSystem, - LayoutGlyph, LayoutLine, ShapeLine, Shaping, Wrap, + LayoutGlyph, LayoutLine, ShapeBuffer, ShapeLine, Shaping, Wrap, }; /// Current cursor location @@ -330,6 +330,9 @@ pub struct Buffer { /// True if a redraw is requires. Set to false after processing redraw: bool, wrap: Wrap, + + /// Scratch buffer for shaping and laying out. + scratch: ShapeBuffer, } impl Buffer { @@ -354,6 +357,7 @@ impl Buffer { scroll: 0, redraw: false, wrap: Wrap::Word, + scratch: ShapeBuffer::default(), } } @@ -411,7 +415,13 @@ impl Buffer { if line.shape_opt().is_none() { reshaped += 1; } - let layout = line.layout(font_system, self.metrics.font_size, self.width, self.wrap); + let layout = line.layout_in_buffer( + &mut self.scratch, + font_system, + self.metrics.font_size, + self.width, + self.wrap, + ); total_layout += layout.len() as i32; } @@ -439,7 +449,13 @@ impl Buffer { if line.shape_opt().is_none() { reshaped += 1; } - let layout = line.layout(font_system, self.metrics.font_size, self.width, self.wrap); + let layout = line.layout_in_buffer( + &mut self.scratch, + font_system, + self.metrics.font_size, + self.width, + self.wrap, + ); if line_i == cursor.line { let layout_cursor = self.layout_cursor(&cursor); layout_i += layout_cursor.layout as i32; diff --git a/src/buffer_line.rs b/src/buffer_line.rs index ae8d149..667cc80 100644 --- a/src/buffer_line.rs +++ b/src/buffer_line.rs @@ -1,7 +1,7 @@ #[cfg(not(feature = "std"))] use alloc::{string::String, vec::Vec}; -use crate::{Align, AttrsList, FontSystem, LayoutLine, ShapeLine, Shaping, Wrap}; +use crate::{Align, AttrsList, FontSystem, LayoutLine, ShapeBuffer, ShapeLine, Shaping, Wrap}; /// A line (or paragraph) of text that is shaped and laid out #[derive(Debug)] @@ -169,8 +169,18 @@ impl BufferLine { /// Shape line, will cache results pub fn shape(&mut self, font_system: &mut FontSystem) -> &ShapeLine { + self.shape_in_buffer(&mut ShapeBuffer::default(), font_system) + } + + /// Shape a line using a pre-existing shape buffer. + pub fn shape_in_buffer( + &mut self, + scratch: &mut ShapeBuffer, + font_system: &mut FontSystem, + ) -> &ShapeLine { if self.shape_opt.is_none() { - self.shape_opt = Some(ShapeLine::new( + self.shape_opt = Some(ShapeLine::new_in_buffer( + scratch, font_system, &self.text, &self.attrs_list, @@ -204,6 +214,26 @@ impl BufferLine { self.layout_opt.as_ref().expect("layout not found") } + /// Layout a line using a pre-existing shape buffer. + pub fn layout_in_buffer( + &mut self, + scratch: &mut ShapeBuffer, + font_system: &mut FontSystem, + font_size: f32, + width: f32, + wrap: Wrap, + ) -> &[LayoutLine] { + if self.layout_opt.is_none() { + self.wrap = wrap; + let align = self.align; + let shape = self.shape_in_buffer(scratch, font_system); + let mut layout = Vec::with_capacity(1); + shape.layout_to_buffer(scratch, font_size, width, wrap, align, &mut layout); + self.layout_opt = Some(layout); + } + self.layout_opt.as_ref().expect("layout not found") + } + /// Get line layout cache pub fn layout_opt(&self) -> &Option> { &self.layout_opt diff --git a/src/font/fallback/mod.rs b/src/font/fallback/mod.rs index dbabcee..4cf4382 100644 --- a/src/font/fallback/mod.rs +++ b/src/font/fallback/mod.rs @@ -36,7 +36,7 @@ pub struct FontFallbackIter<'a> { font_ids: &'a [fontdb::ID], default_families: &'a [&'a Family<'a>], default_i: usize, - scripts: Vec