Pass around Arc<Galley> to avoid copying a lot of data

This commit is contained in:
Emil Ernerfeldt 2021-03-29 22:30:18 +02:00
parent 94baf98eab
commit 1c60dc8d66
6 changed files with 33 additions and 20 deletions

View file

@ -673,7 +673,7 @@ fn paint_frame_interaction(
struct TitleBar { struct TitleBar {
id: Id, id: Id,
title_label: Label, title_label: Label,
title_galley: Galley, title_galley: std::sync::Arc<Galley>,
min_rect: Rect, min_rect: Rect,
rect: Rect, rect: Rect,
} }

View file

@ -302,14 +302,14 @@ impl Painter {
} }
/// Paint text that has already been layed out in a `Galley`. /// Paint text that has already been layed out in a `Galley`.
pub fn galley(&self, pos: Pos2, galley: Galley, color: Color32) { pub fn galley(&self, pos: Pos2, galley: std::sync::Arc<Galley>, color: Color32) {
self.galley_with_italics(pos, galley, color, false) self.galley_with_italics(pos, galley, color, false)
} }
pub fn galley_with_italics( pub fn galley_with_italics(
&self, &self,
pos: Pos2, pos: Pos2,
galley: Galley, galley: std::sync::Arc<Galley>,
color: Color32, color: Color32,
fake_italics: bool, fake_italics: bool,
) { ) {

View file

@ -1,5 +1,6 @@
use crate::*; use crate::*;
use epaint::Galley; use epaint::Galley;
use std::sync::Arc;
/// Static text. /// Static text.
/// ///
@ -143,12 +144,12 @@ impl Label {
} }
impl Label { impl Label {
pub fn layout(&self, ui: &Ui) -> Galley { pub fn layout(&self, ui: &Ui) -> Arc<Galley> {
let max_width = ui.available_width(); let max_width = ui.available_width();
self.layout_width(ui, max_width) self.layout_width(ui, max_width)
} }
pub fn layout_width(&self, ui: &Ui, max_width: f32) -> Galley { pub fn layout_width(&self, ui: &Ui, max_width: f32) -> Arc<Galley> {
let text_style = self.text_style_or_default(ui.style()); let text_style = self.text_style_or_default(ui.style());
let wrap_width = if self.should_wrap(ui) { let wrap_width = if self.should_wrap(ui) {
max_width max_width
@ -174,11 +175,11 @@ impl Label {
// TODO: a paint method for painting anywhere in a ui. // TODO: a paint method for painting anywhere in a ui.
// This should be the easiest method of putting text anywhere. // This should be the easiest method of putting text anywhere.
pub fn paint_galley(&self, ui: &mut Ui, pos: Pos2, galley: Galley) { pub fn paint_galley(&self, ui: &mut Ui, pos: Pos2, galley: Arc<Galley>) {
self.paint_galley_focus(ui, pos, galley, false) self.paint_galley_focus(ui, pos, galley, false)
} }
fn paint_galley_focus(&self, ui: &mut Ui, pos: Pos2, galley: Galley, focus: bool) { fn paint_galley_focus(&self, ui: &mut Ui, pos: Pos2, galley: Arc<Galley>, focus: bool) {
let Self { let Self {
mut background_color, mut background_color,
code, code,
@ -256,7 +257,12 @@ impl Label {
}) })
} }
fn valign_galley(&self, ui: &Ui, text_style: TextStyle, mut galley: Galley) -> Galley { fn valign_galley(
&self,
ui: &Ui,
text_style: TextStyle,
mut galley: Arc<Galley>,
) -> Arc<Galley> {
if text_style == TextStyle::Small { if text_style == TextStyle::Small {
// Hacky McHackface strikes again: // Hacky McHackface strikes again:
let dy = if self.raised { let dy = if self.raised {
@ -269,10 +275,12 @@ impl Label {
// normal_text_heigth - font_height // align bottom // normal_text_heigth - font_height // align bottom
}; };
for row in &mut galley.rows { if dy != 0.0 {
for row in &mut Arc::make_mut(&mut galley).rows {
row.translate_y(dy); row.translate_y(dy);
} }
} }
}
galley galley
} }
} }
@ -293,12 +301,13 @@ impl Widget for Label {
let first_row_indentation = max_width - ui.available_size_before_wrap().x; let first_row_indentation = max_width - ui.available_size_before_wrap().x;
let text_style = self.text_style_or_default(ui.style()); let text_style = self.text_style_or_default(ui.style());
let mut galley = ui.fonts().layout_multiline_with_indentation_and_max_width( let galley = ui.fonts().layout_multiline_with_indentation_and_max_width(
text_style, text_style,
self.text.clone(), self.text.clone(),
first_row_indentation, first_row_indentation,
max_width, max_width,
); );
let mut galley: Galley = (*galley).clone();
let pos = pos2(ui.max_rect().left(), ui.cursor().top()); let pos = pos2(ui.max_rect().left(), ui.cursor().top());
@ -321,7 +330,7 @@ impl Widget for Label {
} }
} }
let galley = self.valign_galley(ui, text_style, galley); let galley = self.valign_galley(ui, text_style, Arc::new(galley));
let rect = galley.rows[0].rect().translate(vec2(pos.x, pos.y)); let rect = galley.rows[0].rect().translate(vec2(pos.x, pos.y));
let mut response = ui.allocate_rect(rect, sense); let mut response = ui.allocate_rect(rect, sense);

View file

@ -75,9 +75,12 @@ pub fn criterion_benchmark(c: &mut Criterion) {
egui::FontDefinitions::default(), egui::FontDefinitions::default(),
); );
let font = &fonts[text_style]; let font = &fonts[text_style];
c.bench_function("text layout", |b| { c.bench_function("text layout (uncached)", |b| {
b.iter(|| font.layout_multiline(LOREM_IPSUM_LONG.to_owned(), wrap_width)) b.iter(|| font.layout_multiline(LOREM_IPSUM_LONG.to_owned(), wrap_width))
}); });
c.bench_function("text layout (cached)", |b| {
b.iter(|| fonts.layout_multiline(text_style, LOREM_IPSUM_LONG.to_owned(), wrap_width))
});
let galley = font.layout_multiline(LOREM_IPSUM_LONG.to_owned(), wrap_width); let galley = font.layout_multiline(LOREM_IPSUM_LONG.to_owned(), wrap_width);
let mut tessellator = egui::epaint::Tessellator::from_options(Default::default()); let mut tessellator = egui::epaint::Tessellator::from_options(Default::default());

View file

@ -43,7 +43,7 @@ pub enum Shape {
/// Top left corner of the first character.. /// Top left corner of the first character..
pos: Pos2, pos: Pos2,
/// The layed out text. /// The layed out text.
galley: Galley, galley: std::sync::Arc<Galley>,
/// Text color (foreground). /// Text color (foreground).
color: Color32, color: Color32,
/// If true, tilt the letters for a hacky italics effect. /// If true, tilt the letters for a hacky italics effect.

View file

@ -278,7 +278,7 @@ impl Fonts {
/// Always returns at least one row. /// Always returns at least one row.
/// Will line break at `\n`. /// Will line break at `\n`.
pub fn layout_no_wrap(&self, text_style: TextStyle, text: String) -> Galley { pub fn layout_no_wrap(&self, text_style: TextStyle, text: String) -> Arc<Galley> {
self.layout_multiline(text_style, text, f32::INFINITY) self.layout_multiline(text_style, text, f32::INFINITY)
} }
@ -288,7 +288,7 @@ impl Fonts {
/// ///
/// Most often you probably want `\n` to produce a new row, /// Most often you probably want `\n` to produce a new row,
/// and so [`Self::layout_no_wrap`] may be a better choice. /// and so [`Self::layout_no_wrap`] may be a better choice.
pub fn layout_single_line(&self, text_style: TextStyle, text: String) -> Galley { pub fn layout_single_line(&self, text_style: TextStyle, text: String) -> Arc<Galley> {
self.galley_cache.lock().layout( self.galley_cache.lock().layout(
&self.fonts, &self.fonts,
LayoutJob { LayoutJob {
@ -306,7 +306,7 @@ impl Fonts {
text_style: TextStyle, text_style: TextStyle,
text: String, text: String,
max_width_in_points: f32, max_width_in_points: f32,
) -> Galley { ) -> Arc<Galley> {
self.layout_multiline_with_indentation_and_max_width( self.layout_multiline_with_indentation_and_max_width(
text_style, text_style,
text, text,
@ -324,7 +324,7 @@ impl Fonts {
text: String, text: String,
first_row_indentation: f32, first_row_indentation: f32,
max_width_in_points: f32, max_width_in_points: f32,
) -> Galley { ) -> Arc<Galley> {
self.galley_cache.lock().layout( self.galley_cache.lock().layout(
&self.fonts, &self.fonts,
LayoutJob { LayoutJob {
@ -377,7 +377,7 @@ struct LayoutJob {
struct CachedGalley { struct CachedGalley {
/// When it was last used /// When it was last used
last_used: u32, last_used: u32,
galley: Galley, // TODO: use an Arc instead! galley: Arc<Galley>,
} }
#[derive(Default)] #[derive(Default)]
@ -388,7 +388,7 @@ struct GalleyCache {
} }
impl GalleyCache { impl GalleyCache {
fn layout(&mut self, fonts: &BTreeMap<TextStyle, Font>, job: LayoutJob) -> Galley { fn layout(&mut self, fonts: &BTreeMap<TextStyle, Font>, job: LayoutJob) -> Arc<Galley> {
if let Some(cached) = self.cache.get_mut(&job) { if let Some(cached) = self.cache.get_mut(&job) {
cached.last_used = self.generation; cached.last_used = self.generation;
cached.galley.clone() cached.galley.clone()
@ -410,6 +410,7 @@ impl GalleyCache {
max_width_in_points.into_inner(), max_width_in_points.into_inner(),
), ),
}; };
let galley = Arc::new(galley);
self.cache.insert( self.cache.insert(
job, job,
CachedGalley { CachedGalley {