Ver código fonte

app: remove old TEXT_CTX and replace with parley new pattern

jkds 7 meses atrás
pai
commit
5c2bd621df

+ 0 - 1
bin/app/src/main.rs

@@ -115,7 +115,6 @@ impl God {
         // Abort the application on panic right away
         std::panic::set_hook(Box::new(panic_hook));
 
-        text::init_txt_ctx();
         let file_logging_guard = logger::setup_logging();
 
         info!(target: "main", "Creating the app");

+ 3 - 4
bin/app/src/text/editor/android.rs

@@ -24,7 +24,7 @@ use crate::{
     gfx::Point,
     mesh::Color,
     prop::{PropertyAtomicGuard, PropertyColor, PropertyFloat32, PropertyStr},
-    text::{TextContext, TEXT_CTX},
+    text,
 };
 use std::cmp::{max, min};
 
@@ -111,8 +111,7 @@ impl Editor {
             underlines.push(compose_start..compose_end);
         }
 
-        let mut txt_ctx = TEXT_CTX.get().await;
-        self.layout = txt_ctx.make_layout(
+        self.layout = text::make_layout(
             &self.state.text,
             text_color,
             font_size,
@@ -175,7 +174,7 @@ impl Editor {
 
     pub fn driver<'a>(
         &'a mut self,
-        _txt_ctx: &'a mut TextContext,
+        _layout_ctx: &'a mut parley::LayoutContext<Color>,
     ) -> Option<parley::PlainEditorDriver<'a, Color>> {
         None
     }

+ 20 - 16
bin/app/src/text/editor/parley.rs

@@ -20,7 +20,7 @@ use crate::{
     gfx::Point,
     mesh::Color,
     prop::{PropertyAtomicGuard, PropertyColor, PropertyFloat32, PropertyStr},
-    text::{TextContext, FONT_STACK, TEXT_CTX},
+    text::{self, FONT_STACK},
 };
 
 pub struct Editor {
@@ -83,9 +83,11 @@ impl Editor {
         styles.insert(parley::StyleProperty::OverflowWrap(parley::OverflowWrap::Anywhere));
         *self.editor.edit_styles() = styles;
 
-        let mut txt_ctx = TEXT_CTX.get().await;
-        let (font_ctx, layout_ctx) = txt_ctx.borrow();
-        self.editor.refresh_layout(font_ctx, layout_ctx);
+        let mut font_ctx = text::GLOBAL_FONT_CTX.clone();
+        text::THREAD_LAYOUT_CTX.with(|layout_ctx| {
+            let mut layout_ctx = layout_ctx.borrow_mut();
+            self.editor.refresh_layout(&mut font_ctx, &mut layout_ctx);
+        });
     }
 
     pub fn layout(&self) -> &parley::Layout<Color> {
@@ -106,19 +108,20 @@ impl Editor {
     }
 
     pub async fn insert(&mut self, txt: &str, atom: &mut PropertyAtomicGuard) {
-        let mut txt_ctx = TEXT_CTX.get().await;
-        let (font_ctx, layout_ctx) = txt_ctx.borrow();
-        let mut drv = self.editor.driver(font_ctx, layout_ctx);
-        drv.insert_or_replace_selection(&txt);
-        self.on_buffer_changed(atom).await;
+        text::THREAD_LAYOUT_CTX.with(|layout_ctx| {
+            let mut layout_ctx = layout_ctx.borrow_mut();
+            let mut drv = self.driver(&mut layout_ctx);
+            drv.insert_or_replace_selection(&txt);
+            self.on_buffer_changed(atom).await;
+        });
     }
 
     pub fn driver<'a>(
         &'a mut self,
-        txt_ctx: &'a mut TextContext,
+        layout_ctx: &'a mut parley::LayoutContext<Color>,
     ) -> Option<parley::PlainEditorDriver<'a, Color>> {
-        let (font_ctx, layout_ctx) = txt_ctx.borrow();
-        Some(self.editor.driver(font_ctx, layout_ctx))
+        let mut font_ctx = text::GLOBAL_FONT_CTX.clone();
+        Some(self.editor.driver(&mut font_ctx, layout_ctx))
     }
 
     pub fn set_width(&mut self, w: f32) {
@@ -141,10 +144,11 @@ impl Editor {
     /// Android uses byte indexes whereas parley has its own things. So this API is a compromise
     /// between them both.
     pub async fn set_selection(&mut self, select_start: usize, select_end: usize) {
-        let mut txt_ctx = TEXT_CTX.get().await;
-        let (font_ctx, layout_ctx) = txt_ctx.borrow();
-        let mut drv = self.editor.driver(font_ctx, layout_ctx);
-        drv.select_byte_range(select_start, select_end);
+        text::THREAD_LAYOUT_CTX.with(|layout_ctx| {
+            let mut layout_ctx = layout_ctx.borrow_mut();
+            let mut drv = self.driver(&mut layout_ctx);
+            drv.select_byte_range(select_start, select_end);
+        });
     }
 
     #[allow(dead_code)]

+ 74 - 99
bin/app/src/text/mod.rs

@@ -16,12 +16,15 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use async_lock::Mutex as AsyncMutex;
 use std::{
+    cell::RefCell,
     ops::Range,
-    sync::{Arc, OnceLock},
+    sync::Arc,
 };
 
+use fontique::{Collection, CollectionOptions, SourceCache, SourceCacheOptions};
+use once_cell::sync::Lazy;
+
 use crate::{mesh::Color, util::spawn_thread};
 
 pub mod atlas;
@@ -30,107 +33,84 @@ pub use editor::Editor;
 mod render;
 pub use render::{render_layout, render_layout_with_opts, DebugRenderOptions};
 
-use darkfi::system::CondVar;
+// Global shared FontContext (thread-safe via internal Arc<Mutex<>>)
+pub static GLOBAL_FONT_CTX: Lazy<parley::FontContext> = Lazy::new(|| {
+    let collection = Collection::new(CollectionOptions {
+        shared: true,
+        system_fonts: false,
+    });
 
-pub struct AsyncGlobal<T> {
-    cv: CondVar,
-    val: OnceLock<AsyncMutex<T>>,
-}
+    let source_cache = SourceCache::new(SourceCacheOptions {
+        shared: true,
+    });
 
-impl<T> AsyncGlobal<T> {
-    const fn new() -> Self {
-        Self { cv: CondVar::new(), val: OnceLock::new() }
-    }
+    let mut font_ctx = parley::FontContext { collection, source_cache };
 
-    fn set(&self, val: T) {
-        self.val.set(AsyncMutex::new(val)).ok().unwrap();
-        self.cv.notify();
-    }
+    let font_data = include_bytes!("../../ibm-plex-mono-regular.otf") as &[u8];
+    font_ctx.collection.register_fonts(
+        peniko::Blob::new(Arc::new(font_data)),
+        None
+    );
 
-    pub async fn get<'a>(&'a self) -> async_lock::MutexGuard<'a, T> {
-        self.cv.wait().await;
-        self.val.get().unwrap().lock().await
-    }
-}
+    let font_data = include_bytes!("../../NotoColorEmoji.ttf") as &[u8];
+    font_ctx.collection.register_fonts(
+        peniko::Blob::new(Arc::new(font_data)),
+        None
+    );
 
-pub static TEXT_CTX: AsyncGlobal<TextContext> = AsyncGlobal::new();
+    font_ctx
+});
 
-pub fn init_txt_ctx() {
-    spawn_thread("init_txt_ctx", || {
-        // This is quite slow. It takes 300ms
-        let txt_ctx = TextContext::new();
-        TEXT_CTX.set(txt_ctx);
-    });
+// Thread-local LayoutContext
+thread_local! {
+    pub static THREAD_LAYOUT_CTX: RefCell<parley::LayoutContext<'static, Color>> =
+        RefCell::new(parley::LayoutContext::new());
 }
 
-/// Initializing this is expensive ~300ms, but storage is ~2kb.
-/// It has to be created once and reused. Currently we use thread local storage.
-pub struct TextContext {
-    font_ctx: parley::FontContext,
-    layout_ctx: parley::LayoutContext<Color>,
+// Public constants
+pub const FONT_STACK: &[parley::FontFamily<'_>] = &[
+    parley::FontFamily::Named(std::borrow::Cow::Borrowed("IBM Plex Mono")),
+    parley::FontFamily::Named(std::borrow::Cow::Borrowed("Noto Color Emoji")),
+];
+
+// FREE FUNCTIONS (no TextContext wrapper!)
+pub fn make_layout(
+    text: &str,
+    text_color: Color,
+    font_size: f32,
+    lineheight: f32,
+    window_scale: f32,
+    width: Option<f32>,
+    underlines: &[Range<usize>],
+) -> parley::Layout<Color> {
+    make_layout2(
+        text,
+        text_color,
+        font_size,
+        lineheight,
+        window_scale,
+        width,
+        underlines,
+        &[],
+    )
 }
 
-impl TextContext {
-    fn new() -> Self {
-        let layout_ctx = parley::LayoutContext::new();
-        let mut font_ctx = parley::FontContext::new();
-
-        let font_data = include_bytes!("../../ibm-plex-mono-regular.otf") as &[u8];
-        let _font_inf =
-            font_ctx.collection.register_fonts(peniko::Blob::new(Arc::new(font_data)), None);
-
-        let font_data = include_bytes!("../../NotoColorEmoji.ttf") as &[u8];
-        let _font_inf =
-            font_ctx.collection.register_fonts(peniko::Blob::new(Arc::new(font_data)), None);
-
-        //for (family_id, _) in font_inf {
-        //    let family_name = font_ctx.collection.family_name(family_id).unwrap();
-        //    trace!(target: "text", "Loaded font: {family_name}");
-        //}
-
-        Self { font_ctx, layout_ctx }
-    }
-
-    #[cfg(not(target_os = "android"))]
-    pub fn borrow(&mut self) -> (&mut parley::FontContext, &mut parley::LayoutContext<Color>) {
-        (&mut self.font_ctx, &mut self.layout_ctx)
-    }
-
-    pub fn make_layout(
-        &mut self,
-        text: &str,
-        text_color: Color,
-        font_size: f32,
-        lineheight: f32,
-        window_scale: f32,
-        width: Option<f32>,
-        underlines: &[Range<usize>],
-    ) -> parley::Layout<Color> {
-        self.make_layout2(
-            text,
-            text_color,
-            font_size,
-            lineheight,
-            window_scale,
-            width,
-            underlines,
-            &[],
-        )
-    }
-
-    pub fn make_layout2(
-        &mut self,
-        text: &str,
-        text_color: Color,
-        font_size: f32,
-        lineheight: f32,
-        window_scale: f32,
-        width: Option<f32>,
-        underlines: &[Range<usize>],
-        foreground_colors: &[(Range<usize>, Color)],
-    ) -> parley::Layout<Color> {
+pub fn make_layout2(
+    text: &str,
+    text_color: Color,
+    font_size: f32,
+    lineheight: f32,
+    window_scale: f32,
+    width: Option<f32>,
+    underlines: &[Range<usize>],
+    foreground_colors: &[(Range<usize>, Color)],
+) -> parley::Layout<Color> {
+    THREAD_LAYOUT_CTX.with(|layout_ctx| {
+        let mut layout_ctx = layout_ctx.borrow_mut();
+        let mut font_ctx = GLOBAL_FONT_CTX.clone();
+
         let mut builder =
-            self.layout_ctx.ranged_builder(&mut self.font_ctx, &text, window_scale, false);
+            layout_ctx.ranged_builder(&mut font_ctx, text, window_scale, false);
         builder.push_default(parley::LineHeight::FontSizeRelative(lineheight));
         builder.push_default(parley::StyleProperty::FontSize(font_size));
         builder.push_default(parley::StyleProperty::FontStack(parley::FontStack::List(
@@ -147,14 +127,9 @@ impl TextContext {
             builder.push(parley::StyleProperty::Brush(*color), range.clone());
         }
 
-        let mut layout: parley::Layout<Color> = builder.build(&text);
+        let mut layout: parley::Layout<Color> = builder.build(text);
         layout.break_all_lines(width);
         layout.align(width, parley::Alignment::Start, parley::AlignmentOptions::default());
         layout
-    }
+    })
 }
-
-pub const FONT_STACK: &[parley::FontFamily<'_>] = &[
-    parley::FontFamily::Named(std::borrow::Cow::Borrowed("IBM Plex Mono")),
-    parley::FontFamily::Named(std::borrow::Cow::Borrowed("Noto Color Emoji")),
-];

+ 5 - 26
bin/app/src/ui/chatview/page.rs

@@ -109,7 +109,6 @@ impl PrivMessage {
 
     fn cache_txt_layout(
         &mut self,
-        txt_ctx: &mut text::TextContext,
         clip: &Rectangle,
         line_height: f32,
         timestamp_width: f32,
@@ -130,7 +129,7 @@ impl PrivMessage {
         let nick_color = select_nick_color(&self.nick, nick_colors);
 
         let txt_layout = if self.nick == "NOTICE" {
-            txt_ctx.make_layout(
+            text::make_layout(
                 &linetext,
                 text_color,
                 self.font_size,
@@ -142,7 +141,7 @@ impl PrivMessage {
         } else {
             let body_color = if self.confirmed { text_color } else { UNCONF_COLOR };
             let nick_end = self.nick.len() + 1;
-            txt_ctx.make_layout2(
+            text::make_layout2(
                 &linetext,
                 body_color,
                 self.font_size,
@@ -173,11 +172,9 @@ impl PrivMessage {
             return instrs.clone()
         }
 
-        let mut txt_ctx = text::TEXT_CTX.get().await;
-
         // Timestamp layout
         let timestr = Self::gen_timestr(self.timestamp);
-        let timestamp_layout = txt_ctx.make_layout(
+        let timestamp_layout = text::make_layout(
             &timestr,
             timestamp_color,
             self.timestamp_font_size,
@@ -188,7 +185,6 @@ impl PrivMessage {
         );
 
         self.cache_txt_layout(
-            &mut txt_ctx,
             clip,
             line_height,
             timestamp_width,
@@ -196,8 +192,6 @@ impl PrivMessage {
             text_color,
         );
 
-        drop(txt_ctx);
-
         let mut all_instrs = vec![];
 
         // Draw selection background if selected
@@ -307,8 +301,7 @@ impl DateMessage {
 
         let datestr = Self::datestr(self.timestamp);
 
-        let mut txt_ctx = text::TEXT_CTX.get().await;
-        let layout = txt_ctx.make_layout(
+        let layout = text::make_layout(
             &datestr,
             timestamp_color,
             self.font_size,
@@ -317,7 +310,6 @@ impl DateMessage {
             None,
             &[],
         );
-        drop(txt_ctx);
 
         let mut instrs = text::render_layout(&layout, render_api, gfxtag!("chatview_datemsg"));
         // Cache the instructions
@@ -519,9 +511,8 @@ impl FileMessage {
         let file_strs = Self::filestr(&self.file_url, &self.status);
 
         let mut layouts = Vec::with_capacity(file_strs.len());
-        let mut txt_ctx = text::TEXT_CTX.get().await;
         for file_str in &file_strs {
-            let layout = txt_ctx.make_layout(
+            let layout = text::make_layout(
                 file_str,
                 color,
                 self.font_size,
@@ -532,7 +523,6 @@ impl FileMessage {
             );
             layouts.push(layout);
         }
-        drop(txt_ctx);
 
         all_instrs
             .push(DrawInstruction::Move(Point::new(timestamp_width + Self::BOX_PADDING_X, 0.)));
@@ -654,7 +644,6 @@ impl Message {
 
     fn cache_txt_layout(
         &mut self,
-        txt_ctx: &mut text::TextContext,
         clip: &Rectangle,
         line_height: f32,
         timestamp_width: f32,
@@ -664,7 +653,6 @@ impl Message {
         match self {
             Self::Priv(m) => {
                 m.cache_txt_layout(
-                    txt_ctx,
                     clip,
                     line_height,
                     timestamp_width,
@@ -871,16 +859,13 @@ impl MessageBuffer {
                 height += msg_spacing;
             }
 
-            let mut txt_ctx = text::TEXT_CTX.get().await;
             msg.cache_txt_layout(
-                &mut txt_ctx,
                 &rect,
                 line_height,
                 timestamp_width,
                 &nick_colors,
                 text_color,
             );
-            drop(txt_ctx);
 
             height += msg.height(line_height);
         }
@@ -939,16 +924,13 @@ impl MessageBuffer {
             text,
         );
 
-        let mut txt_ctx = text::TEXT_CTX.get().await;
         msg.cache_txt_layout(
-            &mut txt_ctx,
             &rect,
             line_height,
             timestamp_width,
             &nick_colors,
             text_color,
         );
-        drop(txt_ctx);
 
         if self.msgs.is_empty() {
             self.msgs.push(msg);
@@ -1012,16 +994,13 @@ impl MessageBuffer {
             text,
         );
 
-        let mut txt_ctx = text::TEXT_CTX.get().await;
         msg.cache_txt_layout(
-            &mut txt_ctx,
             rect,
             line_height,
             timestamp_width,
             &nick_colors,
             text_color,
         );
-        drop(txt_ctx);
 
         let msg_height = msg.height(self.line_height.get());
         self.msgs.push(msg);

+ 16 - 12
bin/app/src/ui/edit/mod.rs

@@ -501,6 +501,7 @@ impl BaseEdit {
 
     async fn handle_shortcut(
         &self,
+        layout_ctx: &mut parley::LayoutContext<Color>,
         key: char,
         mods: &KeyMods,
         atom: &mut PropertyAtomicGuard,
@@ -516,9 +517,8 @@ impl BaseEdit {
         match key {
             'a' => {
                 if action_mod {
-                    let mut txt_ctx = text::TEXT_CTX.get().await;
                     let mut editor = self.lock_editor().await;
-                    let mut drv = editor.driver(&mut txt_ctx).unwrap();
+                    let mut drv = editor.driver(layout_ctx);
 
                     drv.select_all();
                     if let Some(seltext) = editor.selected_text() {
@@ -553,6 +553,7 @@ impl BaseEdit {
 
     async fn handle_key(
         &self,
+        layout_ctx: &mut parley::LayoutContext<Color>,
         key: &KeyCode,
         mods: &KeyMods,
         atom: &mut PropertyAtomicGuard,
@@ -565,9 +566,8 @@ impl BaseEdit {
 
         t!("handle_key({:?}, {:?}) action_mod={action_mod}", key, mods);
 
-        let mut txt_ctx = text::TEXT_CTX.get().await;
         let mut editor = self.lock_editor().await;
-        let mut drv = editor.driver(&mut txt_ctx).unwrap();
+        let mut drv = editor.driver(layout_ctx);
 
         match key {
             KeyCode::Left => {
@@ -1594,7 +1594,10 @@ impl UIObject for BaseEdit {
             if repeat {
                 return false
             }
-            return self.handle_shortcut(key, &mods, atom).await
+            return text::THREAD_LAYOUT_CTX.with(|layout_ctx| {
+                let mut layout_ctx = layout_ctx.borrow_mut();
+                self.handle_shortcut(&mut layout_ctx, key, &mods, atom).await
+            })
         }
 
         // Do nothing
@@ -1637,9 +1640,10 @@ impl UIObject for BaseEdit {
 
         let mut is_handled = false;
         for _ in 0..actions {
-            if self.handle_key(&key, &mods, atom).await {
-                is_handled = true;
-            }
+            is_handled = text::THREAD_LAYOUT_CTX.with(|layout_ctx| {
+                let mut layout_ctx = layout_ctx.borrow_mut();
+                self.handle_key(&mut layout_ctx, &key, &mods, atom).await
+            });
         }
         is_handled
     }
@@ -1680,12 +1684,12 @@ impl UIObject for BaseEdit {
         // Move mouse pos within this widget
         self.abs_to_local(&mut mouse_pos);
 
-        {
-            let mut txt_ctx = text::TEXT_CTX.get().await;
+        text::THREAD_LAYOUT_CTX.with(|layout_ctx| {
+            let mut layout_ctx = layout_ctx.borrow_mut();
             let mut editor = self.lock_editor().await;
-            let mut drv = editor.driver(&mut txt_ctx).unwrap();
+            let mut drv = editor.driver(&mut layout_ctx);
             drv.move_to_point(mouse_pos.x, mouse_pos.y);
-        }
+        });
 
         if !self.select_text.is_null(0).unwrap() {
             self.select_text.clone().set_null(atom, Role::Internal, 0).unwrap();

+ 1 - 4
bin/app/src/ui/emoji_picker/emoji.rs

@@ -68,11 +68,8 @@ impl EmojiMeshes {
     /// Make mesh for this emoji centered at (0, 0)
     async fn gen_emoji_mesh(&self, emoji: &str) -> DrawMesh {
         //d!("rendering emoji: '{emoji}'");
-        let mut txt_ctx = text::TEXT_CTX.get().await;
-
         // The params here don't actually matter since we're talking about BMP fixed sizes
-        let layout = txt_ctx.make_layout(emoji, COLOR_WHITE, self.emoji_size, 1., 1., None, &[]);
-        drop(txt_ctx);
+        let layout = text::make_layout(emoji, COLOR_WHITE, self.emoji_size, 1., 1., None, &[]);
 
         let instrs = text::render_layout(&layout, &self.render_api, gfxtag!("emoji_mesh"));
 

+ 2 - 5
bin/app/src/ui/text.rs

@@ -30,7 +30,7 @@ use crate::{
         PropertyRect, PropertyStr, PropertyUint32, Role,
     },
     scene::{Pimpl, SceneNodeWeak},
-    text::{self, TEXT_CTX},
+    text,
     util::i18n::I18nBabelFish,
     ExecutorPtr,
 };
@@ -123,10 +123,7 @@ impl Text {
             text
         };
 
-        let layout = {
-            let mut txt_ctx = TEXT_CTX.get().await;
-            txt_ctx.make_layout(&text, text_color, font_size, lineheight, window_scale, None, &[])
-        };
+        let layout = text::make_layout(&text, text_color, font_size, lineheight, window_scale, None, &[]);
 
         let mut debug_opts = text::DebugRenderOptions::OFF;
         if self.debug.get() {