From 3f5cd74de747cb7b7294d7dd4a8330a889c2c50b Mon Sep 17 00:00:00 2001 From: StarStarJ <77283933+StarStarJ@users.noreply.github.com> Date: Fri, 1 Nov 2024 13:30:02 +0100 Subject: [PATCH] Put font data into Arc to reduce memory consumption (#5276) egui never accesses the `FontDefinitions`' member fields mutably, except in `fonts_tweak_ui` where it cloned the `FontDefinitions` object anyway. This patch reduces system memory consumption for shared font definitions. And also removes some overhead from copying (e.g. for the per `pixel_per_points` font atlas) Also it allows to keep a copy of the font definitions outside of egui. In my App that uses international fonts: Before: ![image](https://github.com/user-attachments/assets/f8dfb4f4-a21c-447c-8cf9-83025ad6e960) New: ![image](https://github.com/user-attachments/assets/9f297fbd-e620-4b7e-a32a-65073ee805ed) Note: If `Arc` is not wanted, then it could ofc be abstracted away. I know this is quite a breaking change API wise, but would like to hear your opinion. --- crates/egui/src/context.rs | 8 ++++++-- crates/epaint/src/text/fonts.rs | 27 +++++++++++++++++---------- examples/custom_font/src/main.rs | 4 ++-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 3dedb2c8a7d..331430c7865 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -597,7 +597,9 @@ impl ContextImpl { FontPriority::Lowest => fam.push(font.name.clone()), } } - self.font_definitions.font_data.insert(font.name, font.data); + self.font_definitions + .font_data + .insert(font.name, Arc::new(font.data)); } #[cfg(feature = "log")] @@ -2944,7 +2946,9 @@ impl Context { for (name, data) in &mut font_definitions.font_data { ui.collapsing(name, |ui| { - if data.tweak.ui(ui).changed() { + let mut tweak = data.tweak; + if tweak.ui(ui).changed() { + Arc::make_mut(data).tweak = tweak; changed = true; } }); diff --git a/crates/epaint/src/text/fonts.rs b/crates/epaint/src/text/fonts.rs index 3cb8e854d9c..4be2fdfe8d0 100644 --- a/crates/epaint/src/text/fonts.rs +++ b/crates/epaint/src/text/fonts.rs @@ -224,7 +224,11 @@ fn ab_glyph_font_from_font_data(name: &str, data: &FontData) -> ab_glyph::FontAr /// /// // Install my own font (maybe supporting non-latin characters): /// fonts.font_data.insert("my_font".to_owned(), -/// FontData::from_static(include_bytes!("../../../epaint_default_fonts/fonts/Ubuntu-Light.ttf"))); // .ttf and .otf supported +/// std::sync::Arc::new( +/// // .ttf and .otf supported +/// FontData::from_static(include_bytes!("../../../epaint_default_fonts/fonts/Ubuntu-Light.ttf")) +/// ) +/// ); /// /// // Put my font first (highest priority): /// fonts.families.get_mut(&FontFamily::Proportional).unwrap() @@ -243,7 +247,7 @@ pub struct FontDefinitions { /// List of font names and their definitions. /// /// `epaint` has built-in-default for these, but you can override them if you like. - pub font_data: BTreeMap, + pub font_data: BTreeMap>, /// Which fonts (names) to use for each [`FontFamily`]. /// @@ -310,33 +314,36 @@ impl Default for FontDefinitions { /// otherwise this is the same as [`Self::empty`]. #[cfg(feature = "default_fonts")] fn default() -> Self { - let mut font_data: BTreeMap = BTreeMap::new(); + let mut font_data: BTreeMap> = BTreeMap::new(); let mut families = BTreeMap::new(); - font_data.insert("Hack".to_owned(), FontData::from_static(HACK_REGULAR)); + font_data.insert( + "Hack".to_owned(), + Arc::new(FontData::from_static(HACK_REGULAR)), + ); // Some good looking emojis. Use as first priority: font_data.insert( "NotoEmoji-Regular".to_owned(), - FontData::from_static(NOTO_EMOJI_REGULAR).tweak(FontTweak { + Arc::new(FontData::from_static(NOTO_EMOJI_REGULAR).tweak(FontTweak { scale: 0.81, // Make smaller ..Default::default() - }), + })), ); font_data.insert( "Ubuntu-Light".to_owned(), - FontData::from_static(UBUNTU_LIGHT), + Arc::new(FontData::from_static(UBUNTU_LIGHT)), ); // Bigger emojis, and more. : font_data.insert( "emoji-icon-font".to_owned(), - FontData::from_static(EMOJI_ICON).tweak(FontTweak { + Arc::new(FontData::from_static(EMOJI_ICON).tweak(FontTweak { scale: 0.90, // Make smaller ..Default::default() - }), + })), ); families.insert( @@ -795,7 +802,7 @@ impl FontImplCache { pub fn new( atlas: Arc>, pixels_per_point: f32, - font_data: &BTreeMap, + font_data: &BTreeMap>, ) -> Self { let ab_glyph_fonts = font_data .iter() diff --git a/examples/custom_font/src/main.rs b/examples/custom_font/src/main.rs index 99c98a6e158..c487b6714c0 100644 --- a/examples/custom_font/src/main.rs +++ b/examples/custom_font/src/main.rs @@ -48,9 +48,9 @@ fn replace_fonts(ctx: &egui::Context) { // .ttf and .otf files supported. fonts.font_data.insert( "my_font".to_owned(), - egui::FontData::from_static(include_bytes!( + std::sync::Arc::new(egui::FontData::from_static(include_bytes!( "../../../crates/epaint_default_fonts/fonts/Hack-Regular.ttf" - )), + ))), ); // Put my font first (highest priority) for proportional text: