Skip to content

Commit

Permalink
Avoid reallocating spans buffer in TextPipeline (#15012)
Browse files Browse the repository at this point in the history
# Objective

- Don't reallocate the spans vector every time TextPipeline updates a
buffer.

## Solution

- Cache the spans buffer in `TextPipeline`. This is possible through
some [rust
magic](https://users.rust-lang.org/t/how-to-cache-a-vectors-capacity/94478/10).
  • Loading branch information
UkoeHB authored Sep 2, 2024
1 parent 3227c3d commit 49a06e9
Showing 1 changed file with 34 additions and 17 deletions.
51 changes: 34 additions & 17 deletions crates/bevy_text/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ pub struct TextPipeline {
///
/// See [`cosmic_text::SwashCache`] for more information.
swash_cache: SwashCache,
/// Buffered vec for collecting spans.
///
/// See [this dark magic](https://users.rust-lang.org/t/how-to-cache-a-vectors-capacity/94478/10).
spans_buffer: Vec<(&'static str, Attrs<'static>)>,
}

impl TextPipeline {
Expand Down Expand Up @@ -95,23 +99,29 @@ impl TextPipeline {
// The section index is stored in the metadata of the spans, and could be used
// to look up the section the span came from and is not used internally
// in cosmic-text.
let spans: Vec<(&str, Attrs)> = sections
.iter()
.enumerate()
.filter(|(_section_index, section)| section.style.font_size > 0.0)
.map(|(section_index, section)| {
(
&section.value[..],
get_attrs(
section,
section_index,
font_system,
&self.map_handle_to_font_id,
scale_factor,
),
)
})
let mut spans: Vec<(&str, Attrs)> = std::mem::take(&mut self.spans_buffer)
.into_iter()
.map(|_| -> (&str, Attrs) { unreachable!() })
.collect();
spans.extend(
sections
.iter()
.enumerate()
.filter(|(_section_index, section)| section.style.font_size > 0.0)
.map(|(section_index, section)| {
(
&section.value[..],
get_attrs(
section,
section_index,
font_system,
&self.map_handle_to_font_id,
scale_factor,
),
)
}),
);
let spans_iter = spans.iter().copied();

buffer.set_metrics(font_system, metrics);
buffer.set_size(font_system, bounds.width, bounds.height);
Expand All @@ -126,7 +136,7 @@ impl TextPipeline {
},
);

buffer.set_rich_text(font_system, spans, Attrs::new(), Shaping::Advanced);
buffer.set_rich_text(font_system, spans_iter, Attrs::new(), Shaping::Advanced);

// PERF: https://github.com/pop-os/cosmic-text/issues/166:
// Setting alignment afterwards appears to invalidate some layouting performed by `set_text` which is presumably not free?
Expand All @@ -135,6 +145,13 @@ impl TextPipeline {
}
buffer.shape_until_scroll(font_system, false);

// Recover the spans buffer.
spans.clear();
self.spans_buffer = spans
.into_iter()
.map(|_| -> (&'static str, Attrs<'static>) { unreachable!() })
.collect();

Ok(())
}

Expand Down

0 comments on commit 49a06e9

Please sign in to comment.