From 9dc024616b6999c85dd57d4f1cf89a50340bf09c Mon Sep 17 00:00:00 2001 From: koe Date: Sun, 1 Sep 2024 23:07:18 -0500 Subject: [PATCH] move ShapeBuffer to FontSystem --- src/buffer.rs | 17 +--- src/buffer_line.rs | 44 +--------- src/font/fallback/mod.rs | 9 +- src/font/system.rs | 13 ++- src/shape.rs | 172 +++++++++++---------------------------- 5 files changed, 69 insertions(+), 186 deletions(-) diff --git a/src/buffer.rs b/src/buffer.rs index b81e9d7..46c8215 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,14 +1,12 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -#[cfg(not(feature = "std"))] -use alloc::{string::String, vec::Vec}; use core::{cmp, fmt}; use unicode_segmentation::UnicodeSegmentation; use crate::{ Affinity, Align, Attrs, AttrsList, BidiParagraphs, BorrowedWithFontSystem, BufferLine, Color, Cursor, FontSystem, LayoutCursor, LayoutGlyph, LayoutLine, LineEnding, LineIter, Motion, - Scroll, ShapeBuffer, ShapeLine, Shaping, Wrap, + Scroll, ShapeLine, Shaping, Wrap, }; /// A line of visible text for rendering @@ -213,9 +211,6 @@ pub struct Buffer { wrap: Wrap, monospace_width: Option, tab_width: u16, - - /// Scratch buffer for shaping and laying out. - scratch: ShapeBuffer, } impl Clone for Buffer { @@ -230,7 +225,6 @@ impl Clone for Buffer { wrap: self.wrap, monospace_width: self.monospace_width, tab_width: self.tab_width, - scratch: ShapeBuffer::default(), } } } @@ -257,7 +251,6 @@ impl Buffer { scroll: Scroll::default(), redraw: false, wrap: Wrap::WordOrGlyph, - scratch: ShapeBuffer::default(), monospace_width: None, tab_width: 8, } @@ -292,8 +285,7 @@ impl Buffer { for line in &mut self.lines { if line.shape_opt().is_some() { line.reset_layout(); - line.layout_in_buffer( - &mut self.scratch, + line.layout( font_system, self.metrics.font_size, self.width_opt, @@ -521,7 +513,7 @@ impl Buffer { line_i: usize, ) -> Option<&ShapeLine> { let line = self.lines.get_mut(line_i)?; - Some(line.shape_in_buffer(&mut self.scratch, font_system, self.tab_width)) + Some(line.shape(font_system, self.tab_width)) } /// Lay out the provided line index and return the result @@ -531,8 +523,7 @@ impl Buffer { line_i: usize, ) -> Option<&[LayoutLine]> { let line = self.lines.get_mut(line_i)?; - Some(line.layout_in_buffer( - &mut self.scratch, + Some(line.layout( font_system, self.metrics.font_size, self.width_opt, diff --git a/src/buffer_line.rs b/src/buffer_line.rs index 44b9e0a..ee10acc 100644 --- a/src/buffer_line.rs +++ b/src/buffer_line.rs @@ -1,10 +1,7 @@ -#[cfg(not(feature = "std"))] -use alloc::{string::String, vec::Vec}; use core::mem; use crate::{ - Align, Attrs, AttrsList, Cached, FontSystem, LayoutLine, LineEnding, ShapeBuffer, ShapeLine, - Shaping, Wrap, + Align, Attrs, AttrsList, Cached, FontSystem, LayoutLine, LineEnding, ShapeLine, Shaping, Wrap, }; /// A line (or paragraph) of text that is shaped and laid out @@ -205,23 +202,12 @@ impl BufferLine { /// Shape line, will cache results pub fn shape(&mut self, font_system: &mut FontSystem, tab_width: u16) -> &ShapeLine { - self.shape_in_buffer(&mut ShapeBuffer::default(), font_system, tab_width) - } - - /// Shape a line using a pre-existing shape buffer, will cache results - pub fn shape_in_buffer( - &mut self, - scratch: &mut ShapeBuffer, - font_system: &mut FontSystem, - tab_width: u16, - ) -> &ShapeLine { if self.shape_opt.is_unused() { let mut line = self .shape_opt .take_unused() .unwrap_or_else(ShapeLine::empty); - line.build_in_buffer( - scratch, + line.build( font_system, &self.text, &self.attrs_list, @@ -248,28 +234,6 @@ impl BufferLine { wrap: Wrap, match_mono_width: Option, tab_width: u16, - ) -> &[LayoutLine] { - self.layout_in_buffer( - &mut ShapeBuffer::default(), - font_system, - font_size, - width_opt, - wrap, - match_mono_width, - tab_width, - ) - } - - /// Layout a line using a pre-existing shape buffer, will cache results - pub fn layout_in_buffer( - &mut self, - scratch: &mut ShapeBuffer, - font_system: &mut FontSystem, - font_size: f32, - width_opt: Option, - wrap: Wrap, - match_mono_width: Option, - tab_width: u16, ) -> &[LayoutLine] { if self.layout_opt.is_unused() { let align = self.align; @@ -277,9 +241,9 @@ impl BufferLine { .layout_opt .take_unused() .unwrap_or_else(|| Vec::with_capacity(1)); - let shape = self.shape_in_buffer(scratch, font_system, tab_width); + let shape = self.shape(font_system, tab_width); shape.layout_to_buffer( - scratch, + &mut font_system.shape_buffer, font_size, width_opt, wrap, diff --git a/src/font/fallback/mod.rs b/src/font/fallback/mod.rs index c30dfc1..293249b 100644 --- a/src/font/fallback/mod.rs +++ b/src/font/fallback/mod.rs @@ -6,7 +6,7 @@ use alloc::vec::Vec; use fontdb::Family; use unicode_script::Script; -use crate::{Font, FontMatchKey, FontSystem, ShapePlanCache}; +use crate::{Font, FontMatchKey, FontSystem, ShapeBuffer, ShapePlanCache}; use self::platform::*; @@ -119,8 +119,11 @@ impl<'a> FontFallbackIter<'a> { } } - pub fn shape_plan_cache(&mut self) -> &mut ShapePlanCache { - self.font_system.shape_plan_cache() + pub fn shape_caches(&mut self) -> (&mut ShapeBuffer, &mut ShapePlanCache) { + ( + &mut self.font_system.shape_buffer, + &mut self.font_system.shape_plan_cache, + ) } fn face_contains_family(&self, id: fontdb::ID, family_name: &str) -> bool { diff --git a/src/font/system.rs b/src/font/system.rs index 5e8b65b..aac3042 100644 --- a/src/font/system.rs +++ b/src/font/system.rs @@ -1,4 +1,4 @@ -use crate::{Attrs, Font, FontMatchAttrs, HashMap, ShapePlanCache}; +use crate::{Attrs, Font, FontMatchAttrs, HashMap, ShapeBuffer, ShapePlanCache}; use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; @@ -101,7 +101,10 @@ pub struct FontSystem { font_matches_cache: HashMap>>, /// Cache for rustybuzz shape plans. - shape_plan_cache: ShapePlanCache, + pub(crate) shape_plan_cache: ShapePlanCache, + + /// Scratch buffer for shaping and laying out. + pub(crate) shape_buffer: ShapeBuffer, /// Cache for shaped runs #[cfg(feature = "shape-run-cache")] @@ -171,6 +174,7 @@ impl FontSystem { shape_plan_cache: ShapePlanCache::default(), #[cfg(feature = "shape-run-cache")] shape_run_cache: crate::ShapeRunCache::default(), + shape_buffer: ShapeBuffer::default(), }; ret.cache_fonts(cloned_monospace_font_ids.clone()); cloned_monospace_font_ids.into_iter().for_each(|id| { @@ -196,11 +200,6 @@ impl FontSystem { &self.db } - /// Get the shape plan cache. - pub(crate) fn shape_plan_cache(&mut self) -> &mut ShapePlanCache { - &mut self.shape_plan_cache - } - /// Get a mutable reference to the database. pub fn db_mut(&mut self) -> &mut fontdb::Database { self.font_matches_cache.clear(); diff --git a/src/shape.rs b/src/shape.rs index 0afb5ba..b52f073 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -40,7 +40,6 @@ pub enum Shaping { impl Shaping { fn run( self, - scratch: &mut ShapeBuffer, glyphs: &mut Vec, font_system: &mut FontSystem, line: &str, @@ -54,7 +53,6 @@ impl Shaping { Self::Basic => shape_skip(font_system, glyphs, line, attrs_list, start_run, end_run), #[cfg(not(feature = "shape-run-cache"))] Self::Advanced => shape_run( - scratch, glyphs, font_system, line, @@ -65,7 +63,6 @@ impl Shaping { ), #[cfg(feature = "shape-run-cache")] Self::Advanced => shape_run_cached( - scratch, glyphs, font_system, line, @@ -109,8 +106,8 @@ impl fmt::Debug for ShapeBuffer { fn shape_fallback( scratch: &mut ShapeBuffer, - glyphs: &mut Vec, shape_plan_cache: &mut ShapePlanCache, + glyphs: &mut Vec, font: &Font, line: &str, attrs_list: &AttrsList, @@ -217,7 +214,6 @@ fn shape_fallback( } fn shape_run( - scratch: &mut ShapeBuffer, glyphs: &mut Vec, font_system: &mut FontSystem, line: &str, @@ -228,7 +224,7 @@ fn shape_run( ) { // Re-use the previous script buffer if possible. let mut scripts = { - let mut scripts = mem::take(&mut scratch.scripts); + let mut scripts = mem::take(&mut font_system.shape_buffer.scripts); scripts.clear(); scripts }; @@ -261,17 +257,20 @@ fn shape_run( let font = font_iter.next().expect("no default font found"); let glyph_start = glyphs.len(); - let mut missing = shape_fallback( - scratch, - glyphs, - font_iter.shape_plan_cache(), - &font, - line, - attrs_list, - start_run, - end_run, - span_rtl, - ); + let mut missing = { + let (scratch, shape_plan_cache) = font_iter.shape_caches(); + shape_fallback( + scratch, + shape_plan_cache, + glyphs, + &font, + line, + attrs_list, + start_run, + end_run, + span_rtl, + ) + }; //TODO: improve performance! while !missing.is_empty() { @@ -285,10 +284,11 @@ fn shape_run( font_iter.face_name(font.id()) ); let mut fb_glyphs = Vec::new(); + let (scratch, shape_plan_cache) = font_iter.shape_caches(); let fb_missing = shape_fallback( scratch, + shape_plan_cache, &mut fb_glyphs, - font_iter.shape_plan_cache(), &font, line, attrs_list, @@ -362,12 +362,11 @@ fn shape_run( */ // Restore the scripts buffer. - scratch.scripts = scripts; + font_system.shape_buffer.scripts = scripts; } #[cfg(feature = "shape-run-cache")] fn shape_run_cached( - scratch: &mut ShapeBuffer, glyphs: &mut Vec, font_system: &mut FontSystem, line: &str, @@ -413,7 +412,6 @@ fn shape_run_cached( // Fill in cache if not already set let mut cache_glyphs = Vec::new(); shape_run( - scratch, &mut cache_glyphs, font_system, line, @@ -562,6 +560,8 @@ impl ShapeWord { } } + /// Shape a word into a set of glyphs. + #[allow(clippy::too_many_arguments)] pub fn new( font_system: &mut FontSystem, line: &str, @@ -570,34 +570,9 @@ impl ShapeWord { level: unicode_bidi::Level, blank: bool, shaping: Shaping, - ) -> Self { - Self::new_in_buffer( - &mut ShapeBuffer::default(), - font_system, - line, - attrs_list, - word_range, - level, - blank, - shaping, - ) - } - - /// Shape a word into a set of glyphs, using a scratch buffer. - #[allow(clippy::too_many_arguments)] - pub fn new_in_buffer( - scratch: &mut ShapeBuffer, - font_system: &mut FontSystem, - line: &str, - attrs_list: &AttrsList, - word_range: Range, - level: unicode_bidi::Level, - blank: bool, - shaping: Shaping, ) -> Self { let mut empty = Self::empty(); - empty.build_in_buffer( - scratch, + empty.build( font_system, line, attrs_list, @@ -609,13 +584,12 @@ impl ShapeWord { empty } - /// See [`Self::new_in_buffer`]. + /// See [`Self::new`]. /// /// Reuses as much of the pre-existing internal allocations as possible. #[allow(clippy::too_many_arguments)] - pub fn build_in_buffer( + pub fn build( &mut self, - scratch: &mut ShapeBuffer, font_system: &mut FontSystem, line: &str, attrs_list: &AttrsList, @@ -644,7 +618,6 @@ impl ShapeWord { let attrs_egc = attrs_list.get_span(start_egc); if !attrs.compatible(&attrs_egc) { shaping.run( - scratch, &mut glyphs, font_system, line, @@ -660,7 +633,6 @@ impl ShapeWord { } if start_run < word_range.end { shaping.run( - scratch, &mut glyphs, font_system, line, @@ -703,6 +675,7 @@ impl ShapeSpan { } } + /// Shape a span into a set of words. pub fn new( font_system: &mut FontSystem, line: &str, @@ -711,33 +684,9 @@ impl ShapeSpan { line_rtl: bool, level: unicode_bidi::Level, shaping: Shaping, - ) -> Self { - Self::new_in_buffer( - &mut ShapeBuffer::default(), - font_system, - line, - attrs_list, - span_range, - line_rtl, - level, - shaping, - ) - } - - /// Shape a span into a set of words, using a scratch buffer. - pub fn new_in_buffer( - scratch: &mut ShapeBuffer, - font_system: &mut FontSystem, - line: &str, - attrs_list: &AttrsList, - span_range: Range, - line_rtl: bool, - level: unicode_bidi::Level, - shaping: Shaping, ) -> Self { let mut empty = Self::empty(); - empty.build_in_buffer( - scratch, + empty.build( font_system, line, attrs_list, @@ -749,12 +698,11 @@ impl ShapeSpan { empty } - /// See [`Self::new_in_buffer`]. + /// See [`Self::new`]. /// /// Reuses as much of the pre-existing internal allocations as possible. - pub fn build_in_buffer( + pub fn build( &mut self, - scratch: &mut ShapeBuffer, font_system: &mut FontSystem, line: &str, attrs_list: &AttrsList, @@ -774,11 +722,11 @@ impl ShapeSpan { let mut words = mem::take(&mut self.words); // Cache the shape words in reverse order so they can be popped for reuse in the same order. - let mut cached_words = mem::take(&mut scratch.words); + let mut cached_words = mem::take(&mut font_system.shape_buffer.words); cached_words.clear(); if line_rtl != level.is_rtl() { // Un-reverse previous words so the internal glyph counts match accurately when rewriting memory. - cached_words.extend(words.drain(..)); + cached_words.append(&mut words); } else { cached_words.extend(words.drain(..).rev()); } @@ -799,8 +747,7 @@ impl ShapeSpan { } if start_word < start_lb { let mut word = cached_words.pop().unwrap_or_else(ShapeWord::empty); - word.build_in_buffer( - scratch, + word.build( font_system, line, attrs_list, @@ -815,8 +762,7 @@ impl ShapeSpan { for (i, c) in span[start_lb..end_lb].char_indices() { // assert!(c.is_whitespace()); let mut word = cached_words.pop().unwrap_or_else(ShapeWord::empty); - word.build_in_buffer( - scratch, + word.build( font_system, line, attrs_list, @@ -848,7 +794,7 @@ impl ShapeSpan { self.words = words; // Cache buffer for future reuse. - scratch.words = cached_words; + font_system.shape_buffer.words = cached_words; } } @@ -890,9 +836,12 @@ impl ShapeLine { } } + /// Shape a line into a set of spans, using a scratch buffer. If [`unicode_bidi::BidiInfo`] + /// detects multiple paragraphs, they will be joined. + /// /// # Panics /// - /// Will panic if `line` contains more than one paragraph. + /// Will panic if `line` contains multiple paragraphs that do not have matching direction pub fn new( font_system: &mut FontSystem, line: &str, @@ -900,41 +849,20 @@ impl ShapeLine { shaping: Shaping, tab_width: u16, ) -> Self { - Self::new_in_buffer( - &mut ShapeBuffer::default(), - font_system, - line, - attrs_list, - shaping, - tab_width, - ) + let mut empty = Self::empty(); + empty.build(font_system, line, attrs_list, shaping, tab_width); + empty } - /// Shape a line into a set of spans, using a scratch buffer. If [`unicode_bidi::BidiInfo`] - /// detects multiple paragraphs, they will be joined. + /// See [`Self::new`]. + /// + /// Reuses as much of the pre-existing internal allocations as possible. /// /// # Panics /// /// Will panic if `line` contains multiple paragraphs that do not have matching direction - pub fn new_in_buffer( - scratch: &mut ShapeBuffer, - font_system: &mut FontSystem, - line: &str, - attrs_list: &AttrsList, - shaping: Shaping, - tab_width: u16, - ) -> Self { - let mut empty = Self::empty(); - empty.build_in_buffer(scratch, font_system, line, attrs_list, shaping, tab_width); - empty - } - - /// See [`Self::new_in_buffer`]. - /// - /// Reuses as much of the pre-existing internal allocations as possible. - pub fn build_in_buffer( + pub fn build( &mut self, - scratch: &mut ShapeBuffer, font_system: &mut FontSystem, line: &str, attrs_list: &AttrsList, @@ -944,7 +872,7 @@ impl ShapeLine { let mut spans = mem::take(&mut self.spans); // Cache the shape spans in reverse order so they can be popped for reuse in the same order. - let mut cached_spans = mem::take(&mut scratch.spans); + let mut cached_spans = mem::take(&mut font_system.shape_buffer.spans); cached_spans.clear(); cached_spans.extend(spans.drain(..).rev()); @@ -979,8 +907,7 @@ impl ShapeLine { if new_level != run_level { // End of the previous run, start of a new one. let mut span = cached_spans.pop().unwrap_or_else(ShapeSpan::empty); - span.build_in_buffer( - scratch, + span.build( font_system, line, attrs_list, @@ -995,8 +922,7 @@ impl ShapeLine { } } let mut span = cached_spans.pop().unwrap_or_else(ShapeSpan::empty); - span.build_in_buffer( - scratch, + span.build( font_system, line, attrs_list, @@ -1029,7 +955,7 @@ impl ShapeLine { self.metrics_opt = attrs_list.defaults().metrics_opt.map(|x| x.into()); // Return the buffer for later reuse. - scratch.spans = cached_spans; + font_system.shape_buffer.spans = cached_spans; } // A modified version of first part of unicode_bidi::bidi_info::visual_run @@ -1705,7 +1631,7 @@ impl ShapeLine { // Restore the buffer to the scratch set to prevent reallocations. scratch.visual_lines = visual_lines; - scratch.visual_lines.extend(cached_visual_lines.drain(..)); + scratch.visual_lines.append(&mut cached_visual_lines); scratch.cached_visual_lines = cached_visual_lines; scratch.glyph_sets = cached_glyph_sets; }