gpui: Rewrite Windows `PlatformTextSystem` for better performance (#48087)

Lukas Wirth created

The current setup is quite a mess, allocates a ton of unnecessary heap
data when rendering text or querying font info and not really caching
things well, this PR changes that.

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Change summary

.zed/settings.json                               |   9 
crates/gpui/src/platform/windows/direct_write.rs | 585 ++++++++---------
crates/gpui/src/text_system.rs                   |  16 
3 files changed, 290 insertions(+), 320 deletions(-)

Detailed changes

.zed/settings.json 🔗

@@ -38,6 +38,15 @@
       },
     },
   },
+  "lsp": {
+    "rust-analyzer": {
+      "initialization_options": {
+        "procMacro": {
+          "processes": 4,
+        },
+      },
+    },
+  },
   "file_types": {
     "Dockerfile": ["Dockerfile*[!dockerignore]"],
     "JSONC": ["**/assets/**/*.json", "renovate.json"],

crates/gpui/src/platform/windows/direct_write.rs 🔗

@@ -1,13 +1,12 @@
 use std::{
     borrow::Cow,
     ffi::{c_uint, c_void},
-    sync::Arc,
+    mem::ManuallyDrop,
 };
 
-use ::util::ResultExt;
+use ::util::{ResultExt, maybe};
 use anyhow::{Context, Result};
 use collections::HashMap;
-use itertools::Itertools;
 use parking_lot::{RwLock, RwLockUpgradableReadGuard};
 use windows::{
     Win32::{
@@ -28,80 +27,55 @@ use crate::*;
 
 #[derive(Debug)]
 struct FontInfo {
-    font_family: String,
+    font_family_h: HSTRING,
     font_face: IDWriteFontFace3,
     features: IDWriteTypography,
     fallbacks: Option<IDWriteFontFallback>,
-    is_system_font: bool,
+    font_collection: IDWriteFontCollection1,
 }
 
-pub(crate) struct DirectWriteTextSystem(RwLock<DirectWriteState>);
+pub(crate) struct DirectWriteTextSystem {
+    components: DirectWriteComponents,
+    state: RwLock<DirectWriteState>,
+}
 
-struct DirectWriteComponent {
-    locale: String,
+struct DirectWriteComponents {
+    locale: HSTRING,
     factory: IDWriteFactory5,
     in_memory_loader: IDWriteInMemoryFontFileLoader,
     builder: IDWriteFontSetBuilder1,
-    text_renderer: Arc<TextRendererWrapper>,
+    text_renderer: TextRendererWrapper,
+    system_ui_font_name: SharedString,
+    system_subpixel_rendering: bool,
+}
 
-    gpu_state: GPUState,
+impl Drop for DirectWriteComponents {
+    fn drop(&mut self) {
+        unsafe {
+            let _ = self
+                .factory
+                .UnregisterFontFileLoader(&self.in_memory_loader);
+        }
+    }
 }
 
 struct GPUState {
     device: ID3D11Device,
     device_context: ID3D11DeviceContext,
-    sampler: [Option<ID3D11SamplerState>; 1],
+    sampler: Option<ID3D11SamplerState>,
     blend_state: ID3D11BlendState,
     vertex_shader: ID3D11VertexShader,
     pixel_shader: ID3D11PixelShader,
 }
 
 struct DirectWriteState {
-    components: DirectWriteComponent,
-    system_ui_font_name: SharedString,
+    gpu_state: GPUState,
     system_font_collection: IDWriteFontCollection1,
     custom_font_collection: IDWriteFontCollection1,
     fonts: Vec<FontInfo>,
-    font_selections: HashMap<Font, FontId>,
-    font_id_by_identifier: HashMap<FontIdentifier, FontId>,
-    system_subpixel_rendering: bool,
-}
-
-#[derive(Debug, Clone, Hash, PartialEq, Eq)]
-struct FontIdentifier {
-    postscript_name: String,
-    weight: i32,
-    style: i32,
-}
-
-impl DirectWriteComponent {
-    pub fn new(directx_devices: &DirectXDevices) -> Result<Self> {
-        // todo: ideally this would not be a large unsafe block but smaller isolated ones for easier auditing
-        unsafe {
-            let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;
-            // The `IDWriteInMemoryFontFileLoader` here is supported starting from
-            // Windows 10 Creators Update, which consequently requires the entire
-            // `DirectWriteTextSystem` to run on `win10 1703`+.
-            let in_memory_loader = factory.CreateInMemoryFontFileLoader()?;
-            factory.RegisterFontFileLoader(&in_memory_loader)?;
-            let builder = factory.CreateFontSetBuilder()?;
-            let mut locale_vec = vec![0u16; LOCALE_NAME_MAX_LENGTH as usize];
-            GetUserDefaultLocaleName(&mut locale_vec);
-            let locale = String::from_utf16_lossy(&locale_vec);
-            let text_renderer = Arc::new(TextRendererWrapper::new(&locale));
-
-            let gpu_state = GPUState::new(directx_devices)?;
-
-            Ok(DirectWriteComponent {
-                locale,
-                factory,
-                in_memory_loader,
-                builder,
-                text_renderer,
-                gpu_state,
-            })
-        }
-    }
+    font_to_font_id: HashMap<Font, FontId>,
+    font_info_cache: HashMap<usize, FontId>,
+    layout_line_scratch: Vec<u16>,
 }
 
 impl GPUState {
@@ -153,7 +127,7 @@ impl GPUState {
                 MaxLOD: 0.0,
             };
             unsafe { device.CreateSamplerState(&desc, Some(&mut sampler)) }?;
-            [sampler]
+            sampler
         };
 
         let vertex_shader = {
@@ -189,13 +163,38 @@ impl GPUState {
 
 impl DirectWriteTextSystem {
     pub(crate) fn new(directx_devices: &DirectXDevices) -> Result<Self> {
-        let components = DirectWriteComponent::new(directx_devices)?;
+        let factory: IDWriteFactory5 = unsafe { DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)? };
+        // The `IDWriteInMemoryFontFileLoader` here is supported starting from
+        // Windows 10 Creators Update, which consequently requires the entire
+        // `DirectWriteTextSystem` to run on `win10 1703`+.
+        let in_memory_loader = unsafe { factory.CreateInMemoryFontFileLoader()? };
+        unsafe { factory.RegisterFontFileLoader(&in_memory_loader)? };
+        let builder = unsafe { factory.CreateFontSetBuilder()? };
+        let mut locale = [0u16; LOCALE_NAME_MAX_LENGTH as usize];
+        unsafe { GetUserDefaultLocaleName(&mut locale) };
+        let locale = HSTRING::from_wide(&locale);
+        let text_renderer = TextRendererWrapper::new(locale.clone());
+
+        let gpu_state = GPUState::new(directx_devices)?;
+
+        let system_subpixel_rendering = get_system_subpixel_rendering();
+        let system_ui_font_name = get_system_ui_font_name();
+        let components = DirectWriteComponents {
+            locale,
+            factory,
+            in_memory_loader,
+            builder,
+            text_renderer,
+            system_ui_font_name,
+            system_subpixel_rendering,
+        };
+
         let system_font_collection = unsafe {
-            let mut result = std::mem::zeroed();
+            let mut result = None;
             components
                 .factory
                 .GetSystemFontCollection(false, &mut result, true)?;
-            result.unwrap()
+            result.context("Failed to get system font collection")?
         };
         let custom_font_set = unsafe { components.builder.CreateFontSet()? };
         let custom_font_collection = unsafe {
@@ -203,70 +202,67 @@ impl DirectWriteTextSystem {
                 .factory
                 .CreateFontCollectionFromFontSet(&custom_font_set)?
         };
-        let system_ui_font_name = get_system_ui_font_name();
-        let system_subpixel_rendering = get_system_subpixel_rendering();
 
-        Ok(Self(RwLock::new(DirectWriteState {
+        Ok(Self {
             components,
-            system_ui_font_name,
-            system_font_collection,
-            custom_font_collection,
-            fonts: Vec::new(),
-            font_selections: HashMap::default(),
-            font_id_by_identifier: HashMap::default(),
-            system_subpixel_rendering,
-        })))
+            state: RwLock::new(DirectWriteState {
+                gpu_state,
+                system_font_collection,
+                custom_font_collection,
+                fonts: Vec::new(),
+                font_to_font_id: HashMap::default(),
+                font_info_cache: HashMap::default(),
+                layout_line_scratch: Vec::new(),
+            }),
+        })
     }
 
     pub(crate) fn handle_gpu_lost(&self, directx_devices: &DirectXDevices) -> Result<()> {
-        self.0.write().handle_gpu_lost(directx_devices)
+        self.state.write().handle_gpu_lost(directx_devices)
     }
 }
 
 impl PlatformTextSystem for DirectWriteTextSystem {
     fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
-        self.0.write().add_fonts(fonts)
+        self.state.write().add_fonts(&self.components, fonts)
     }
 
     fn all_font_names(&self) -> Vec<String> {
-        self.0.read().all_font_names()
+        self.state.read().all_font_names(&self.components)
     }
 
     fn font_id(&self, font: &Font) -> Result<FontId> {
-        let lock = self.0.upgradable_read();
-        if let Some(font_id) = lock.font_selections.get(font) {
+        let lock = self.state.upgradable_read();
+        if let Some(font_id) = lock.font_to_font_id.get(font) {
             Ok(*font_id)
         } else {
-            let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
-            let font_id = lock
-                .select_font(font)
-                .with_context(|| format!("Failed to select font: {:?}", font))?;
-            lock.font_selections.insert(font.clone(), font_id);
-            Ok(font_id)
+            RwLockUpgradableReadGuard::upgrade(lock)
+                .select_and_cache_font(&self.components, font)
+                .with_context(|| format!("Failed to select font: {:?}", font))
         }
     }
 
     fn font_metrics(&self, font_id: FontId) -> FontMetrics {
-        self.0.read().font_metrics(font_id)
+        self.state.read().font_metrics(font_id)
     }
 
     fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
-        self.0.read().get_typographic_bounds(font_id, glyph_id)
+        self.state.read().get_typographic_bounds(font_id, glyph_id)
     }
 
     fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Size<f32>> {
-        self.0.read().get_advance(font_id, glyph_id)
+        self.state.read().get_advance(font_id, glyph_id)
     }
 
     fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
-        self.0.read().glyph_for_char(font_id, ch)
+        self.state.read().glyph_for_char(font_id, ch)
     }
 
     fn glyph_raster_bounds(
         &self,
         params: &RenderGlyphParams,
     ) -> anyhow::Result<Bounds<DevicePixels>> {
-        self.0.read().raster_bounds(params)
+        self.state.read().raster_bounds(&self.components, params)
     }
 
     fn rasterize_glyph(
@@ -274,13 +270,15 @@ impl PlatformTextSystem for DirectWriteTextSystem {
         params: &RenderGlyphParams,
         raster_bounds: Bounds<DevicePixels>,
     ) -> anyhow::Result<(Size<DevicePixels>, Vec<u8>)> {
-        self.0.read().rasterize_glyph(params, raster_bounds)
+        self.state
+            .read()
+            .rasterize_glyph(&self.components, params, raster_bounds)
     }
 
     fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout {
-        self.0
+        self.state
             .write()
-            .layout_line(text, font_size, runs)
+            .layout_line(&self.components, text, font_size, runs)
             .log_err()
             .unwrap_or(LineLayout {
                 font_size,
@@ -293,7 +291,7 @@ impl PlatformTextSystem for DirectWriteTextSystem {
         _font_id: FontId,
         _font_size: Pixels,
     ) -> TextRenderingMode {
-        if self.0.read().system_subpixel_rendering {
+        if self.components.system_subpixel_rendering {
             TextRenderingMode::Subpixel
         } else {
             TextRenderingMode::Grayscale
@@ -302,60 +300,108 @@ impl PlatformTextSystem for DirectWriteTextSystem {
 }
 
 impl DirectWriteState {
-    fn add_fonts(&mut self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
+    fn select_and_cache_font(
+        &mut self,
+        components: &DirectWriteComponents,
+        font: &Font,
+    ) -> Option<FontId> {
+        let select_font = |this: &mut DirectWriteState, font: &Font| -> Option<FontId> {
+            let info = [&this.custom_font_collection, &this.system_font_collection]
+                .into_iter()
+                .find_map(|font_collection| unsafe {
+                    DirectWriteState::make_font_from_font_collection(
+                        font,
+                        font_collection,
+                        &components.factory,
+                        &this.system_font_collection,
+                        &components.system_ui_font_name,
+                    )
+                })?;
+
+            let font_id = FontId(this.fonts.len());
+            let font_face_key = info.font_face.cast::<IUnknown>().unwrap().as_raw().addr();
+            this.fonts.push(info);
+            this.font_info_cache.insert(font_face_key, font_id);
+            Some(font_id)
+        };
+
+        let mut font_id = select_font(self, font);
+        if font_id.is_none() {
+            // try updating system fonts and reselect
+            let mut collection = None;
+            let font_collection_updated = unsafe {
+                components
+                    .factory
+                    .GetSystemFontCollection(false, &mut collection, true)
+            }
+            .log_err()
+            .is_some();
+            if font_collection_updated && let Some(collection) = collection {
+                self.system_font_collection = collection;
+            }
+            font_id = select_font(self, font);
+        };
+        let font_id = font_id?;
+        self.font_to_font_id.insert(font.clone(), font_id);
+        Some(font_id)
+    }
+
+    fn add_fonts(
+        &mut self,
+        components: &DirectWriteComponents,
+        fonts: Vec<Cow<'static, [u8]>>,
+    ) -> Result<()> {
         for font_data in fonts {
             match font_data {
                 Cow::Borrowed(data) => unsafe {
-                    let font_file = self
-                        .components
+                    let font_file = components
                         .in_memory_loader
                         .CreateInMemoryFontFileReference(
-                            &self.components.factory,
-                            data.as_ptr() as _,
+                            &components.factory,
+                            data.as_ptr().cast(),
                             data.len() as _,
                             None,
                         )?;
-                    self.components.builder.AddFontFile(&font_file)?;
+                    components.builder.AddFontFile(&font_file)?;
                 },
                 Cow::Owned(data) => unsafe {
-                    let font_file = self
-                        .components
+                    let font_file = components
                         .in_memory_loader
                         .CreateInMemoryFontFileReference(
-                            &self.components.factory,
-                            data.as_ptr() as _,
+                            &components.factory,
+                            data.as_ptr().cast(),
                             data.len() as _,
                             None,
                         )?;
-                    self.components.builder.AddFontFile(&font_file)?;
+                    components.builder.AddFontFile(&font_file)?;
                 },
             }
         }
-        let set = unsafe { self.components.builder.CreateFontSet()? };
-        let collection = unsafe {
-            self.components
-                .factory
-                .CreateFontCollectionFromFontSet(&set)?
-        };
+        let set = unsafe { components.builder.CreateFontSet()? };
+        let collection = unsafe { components.factory.CreateFontCollectionFromFontSet(&set)? };
         self.custom_font_collection = collection;
 
         Ok(())
     }
 
     fn generate_font_fallbacks(
-        &self,
         fallbacks: &FontFallbacks,
+        factory: &IDWriteFactory5,
+        system_font_collection: &IDWriteFontCollection1,
     ) -> Result<Option<IDWriteFontFallback>> {
-        if fallbacks.fallback_list().is_empty() {
+        let fallback_list = fallbacks.fallback_list();
+        if fallback_list.is_empty() {
             return Ok(None);
         }
         unsafe {
-            let builder = self.components.factory.CreateFontFallbackBuilder()?;
-            let font_set = &self.system_font_collection.GetFontSet()?;
-            for family_name in fallbacks.fallback_list() {
+            let builder = factory.CreateFontFallbackBuilder()?;
+            let font_set = &system_font_collection.GetFontSet()?;
+            let mut unicode_ranges = Vec::new();
+            for family_name in fallback_list {
+                let family_name = HSTRING::from(family_name);
                 let Some(fonts) = font_set
                     .GetMatchingFonts(
-                        &HSTRING::from(family_name),
+                        &family_name,
                         DWRITE_FONT_WEIGHT_NORMAL,
                         DWRITE_FONT_STRETCH_NORMAL,
                         DWRITE_FONT_STYLE_NORMAL,
@@ -364,206 +410,109 @@ impl DirectWriteState {
                 else {
                     continue;
                 };
-                if fonts.GetFontCount() == 0 {
-                    log::error!("No matching font found for {}", family_name);
+                let Ok(font_face) = fonts.GetFontFaceReference(0) else {
                     continue;
-                }
-                let font = fonts.GetFontFaceReference(0)?.CreateFontFace()?;
+                };
+                let font = font_face.CreateFontFace()?;
                 let mut count = 0;
                 font.GetUnicodeRanges(None, &mut count).ok();
                 if count == 0 {
                     continue;
                 }
-                let mut unicode_ranges = vec![DWRITE_UNICODE_RANGE::default(); count as usize];
+                unicode_ranges.clear();
+                unicode_ranges.resize_with(count as usize, DWRITE_UNICODE_RANGE::default);
                 let Some(_) = font
                     .GetUnicodeRanges(Some(&mut unicode_ranges), &mut count)
                     .log_err()
                 else {
                     continue;
                 };
-                let target_family_name = HSTRING::from(family_name);
                 builder.AddMapping(
                     &unicode_ranges,
-                    &[target_family_name.as_ptr()],
+                    &[family_name.as_ptr()],
                     None,
                     None,
                     None,
                     1.0,
                 )?;
             }
-            let system_fallbacks = self.components.factory.GetSystemFontFallback()?;
+            let system_fallbacks = factory.GetSystemFontFallback()?;
             builder.AddMappings(&system_fallbacks)?;
             Ok(Some(builder.CreateFontFallback()?))
         }
     }
 
     unsafe fn generate_font_features(
-        &self,
+        factory: &IDWriteFactory5,
         font_features: &FontFeatures,
     ) -> Result<IDWriteTypography> {
-        let direct_write_features = unsafe { self.components.factory.CreateTypography()? };
+        let direct_write_features = unsafe { factory.CreateTypography()? };
         apply_font_features(&direct_write_features, font_features)?;
         Ok(direct_write_features)
     }
 
-    unsafe fn get_font_id_from_font_collection(
-        &mut self,
-        family_name: &str,
-        font_weight: FontWeight,
-        font_style: FontStyle,
-        font_features: &FontFeatures,
-        font_fallbacks: Option<&FontFallbacks>,
-        is_system_font: bool,
-    ) -> Option<FontId> {
-        let collection = if is_system_font {
-            &self.system_font_collection
+    unsafe fn make_font_from_font_collection(
+        &Font {
+            ref family,
+            ref features,
+            ref fallbacks,
+            weight,
+            style,
+        }: &Font,
+        collection: &IDWriteFontCollection1,
+        factory: &IDWriteFactory5,
+        system_font_collection: &IDWriteFontCollection1,
+        system_ui_font_name: &SharedString,
+    ) -> Option<FontInfo> {
+        const SYSTEM_UI_FONT_NAME: &str = ".SystemUIFont";
+        let family = if family == SYSTEM_UI_FONT_NAME {
+            system_ui_font_name
         } else {
-            &self.custom_font_collection
+            font_name_with_fallbacks_shared(&family, &system_ui_font_name)
         };
         let fontset = unsafe { collection.GetFontSet().log_err()? };
+        let font_family_h = HSTRING::from(family.as_str());
         let font = unsafe {
             fontset
                 .GetMatchingFonts(
-                    &HSTRING::from(family_name),
-                    font_weight.into(),
+                    &font_family_h,
+                    weight.into(),
                     DWRITE_FONT_STRETCH_NORMAL,
-                    font_style.into(),
+                    style.into(),
                 )
                 .log_err()?
         };
         let total_number = unsafe { font.GetFontCount() };
         for index in 0..total_number {
-            let Some(font_face_ref) = (unsafe { font.GetFontFaceReference(index).log_err() })
-            else {
-                continue;
-            };
-            let Some(font_face) = (unsafe { font_face_ref.CreateFontFace().log_err() }) else {
-                continue;
-            };
-            let Some(identifier) = get_font_identifier(&font_face, &self.components.locale) else {
-                continue;
-            };
-            let Some(direct_write_features) =
-                (unsafe { self.generate_font_features(font_features).log_err() })
-            else {
-                continue;
-            };
-            let fallbacks = font_fallbacks
-                .and_then(|fallbacks| self.generate_font_fallbacks(fallbacks).log_err().flatten());
-            let font_info = FontInfo {
-                font_family: family_name.to_owned(),
-                font_face,
-                features: direct_write_features,
-                fallbacks,
-                is_system_font,
-            };
-            let font_id = FontId(self.fonts.len());
-            self.fonts.push(font_info);
-            self.font_id_by_identifier.insert(identifier, font_id);
-            return Some(font_id);
-        }
-        None
-    }
-
-    unsafe fn update_system_font_collection(&mut self) {
-        let mut collection = unsafe { std::mem::zeroed() };
-        if unsafe {
-            self.components
-                .factory
-                .GetSystemFontCollection(false, &mut collection, true)
-                .log_err()
-                .is_some()
-        } {
-            self.system_font_collection = collection.unwrap();
-        }
-    }
-
-    fn select_font(&mut self, target_font: &Font) -> Option<FontId> {
-        unsafe {
-            if target_font.family == ".SystemUIFont" {
-                let family = self.system_ui_font_name.clone();
-                self.find_font_id(
-                    family.as_ref(),
-                    target_font.weight,
-                    target_font.style,
-                    &target_font.features,
-                    target_font.fallbacks.as_ref(),
-                )
-            } else {
-                let family = self.system_ui_font_name.clone();
-                self.find_font_id(
-                    font_name_with_fallbacks(target_font.family.as_ref(), family.as_ref()),
-                    target_font.weight,
-                    target_font.style,
-                    &target_font.features,
-                    target_font.fallbacks.as_ref(),
-                )
-                .or_else(|| {
-                    #[cfg(any(test, feature = "test-support"))]
-                    {
-                        panic!("ERROR: {} font not found!", target_font.family);
-                    }
-                    #[cfg(not(any(test, feature = "test-support")))]
-                    {
-                        log::error!("{} not found, use {} instead.", target_font.family, family);
-                        self.get_font_id_from_font_collection(
-                            family.as_ref(),
-                            target_font.weight,
-                            target_font.style,
-                            &target_font.features,
-                            target_font.fallbacks.as_ref(),
-                            true,
-                        )
-                    }
-                })
-            }
-        }
-    }
-
-    unsafe fn find_font_id(
-        &mut self,
-        family_name: &str,
-        weight: FontWeight,
-        style: FontStyle,
-        features: &FontFeatures,
-        fallbacks: Option<&FontFallbacks>,
-    ) -> Option<FontId> {
-        // try to find target font in custom font collection first
-        unsafe {
-            self.get_font_id_from_font_collection(
-                family_name,
-                weight,
-                style,
-                features,
-                fallbacks,
-                false,
-            )
-            .or_else(|| {
-                self.get_font_id_from_font_collection(
-                    family_name,
-                    weight,
-                    style,
-                    features,
-                    fallbacks,
-                    true,
-                )
-            })
-            .or_else(|| {
-                self.update_system_font_collection();
-                self.get_font_id_from_font_collection(
-                    family_name,
-                    weight,
-                    style,
-                    features,
+            let res = maybe!({
+                let font_face_ref = unsafe { font.GetFontFaceReference(index).log_err()? };
+                let font_face = unsafe { font_face_ref.CreateFontFace().log_err()? };
+                let direct_write_features =
+                    unsafe { Self::generate_font_features(factory, features).log_err()? };
+                let fallbacks = fallbacks.as_ref().and_then(|fallbacks| {
+                    Self::generate_font_fallbacks(fallbacks, factory, system_font_collection)
+                        .log_err()
+                        .flatten()
+                });
+                let font_info = FontInfo {
+                    font_family_h: font_family_h.clone(),
+                    font_face,
+                    features: direct_write_features,
                     fallbacks,
-                    true,
-                )
-            })
+                    font_collection: collection.clone(),
+                };
+                Some(font_info)
+            });
+            if res.is_some() {
+                return res;
+            }
         }
+        None
     }
 
     fn layout_line(
         &mut self,
+        components: &DirectWriteComponents,
         text: &str,
         font_size: Pixels,
         font_runs: &[FontRun],
@@ -575,38 +524,34 @@ impl DirectWriteState {
             });
         }
         unsafe {
-            let text_renderer = self.components.text_renderer.clone();
-            let text_wide = text.encode_utf16().collect_vec();
+            self.layout_line_scratch.clear();
+            self.layout_line_scratch.extend(text.encode_utf16());
+            let text_wide = &*self.layout_line_scratch;
 
             let mut utf8_offset = 0usize;
             let mut utf16_offset = 0u32;
             let text_layout = {
                 let first_run = &font_runs[0];
                 let font_info = &self.fonts[first_run.font_id.0];
-                let collection = if font_info.is_system_font {
-                    &self.system_font_collection
-                } else {
-                    &self.custom_font_collection
-                };
-                let format: IDWriteTextFormat1 = self
-                    .components
+                let collection = &font_info.font_collection;
+                let format: IDWriteTextFormat1 = components
                     .factory
                     .CreateTextFormat(
-                        &HSTRING::from(&font_info.font_family),
+                        &font_info.font_family_h,
                         collection,
                         font_info.font_face.GetWeight(),
                         font_info.font_face.GetStyle(),
                         DWRITE_FONT_STRETCH_NORMAL,
                         font_size.0,
-                        &HSTRING::from(&self.components.locale),
+                        &components.locale,
                     )?
                     .cast()?;
                 if let Some(ref fallbacks) = font_info.fallbacks {
                     format.SetFontFallback(fallbacks)?;
                 }
 
-                let layout = self.components.factory.CreateTextLayout(
-                    &text_wide,
+                let layout = components.factory.CreateTextLayout(
+                    text_wide,
                     &format,
                     f32::INFINITY,
                     f32::INFINITY,
@@ -624,39 +569,30 @@ impl DirectWriteState {
                 layout
             };
 
-            let mut first_run = true;
-            let mut ascent = Pixels::default();
-            let mut descent = Pixels::default();
-            let mut break_ligatures = false;
-            for run in font_runs {
-                if first_run {
-                    first_run = false;
-                    let mut metrics = vec![DWRITE_LINE_METRICS::default(); 4];
-                    let mut line_count = 0u32;
-                    text_layout.GetLineMetrics(Some(&mut metrics), &mut line_count as _)?;
-                    ascent = px(metrics[0].baseline);
-                    descent = px(metrics[0].height - metrics[0].baseline);
-                    break_ligatures = !break_ligatures;
-                    continue;
-                }
+            let (mut ascent, mut descent) = {
+                let mut first_metrics = [DWRITE_LINE_METRICS::default(); 4];
+                let mut line_count = 0u32;
+                text_layout.GetLineMetrics(Some(&mut first_metrics), &mut line_count)?;
+                (
+                    px(first_metrics[0].baseline),
+                    px(first_metrics[0].height - first_metrics[0].baseline),
+                )
+            };
+            let mut break_ligatures = true;
+            for run in &font_runs[1..] {
                 let font_info = &self.fonts[run.font_id.0];
                 let current_text = &text[utf8_offset..(utf8_offset + run.len)];
                 utf8_offset += run.len;
                 let current_text_utf16_length = current_text.encode_utf16().count() as u32;
 
-                let collection = if font_info.is_system_font {
-                    &self.system_font_collection
-                } else {
-                    &self.custom_font_collection
-                };
+                let collection = &font_info.font_collection;
                 let text_range = DWRITE_TEXT_RANGE {
                     startPosition: utf16_offset,
                     length: current_text_utf16_length,
                 };
                 utf16_offset += current_text_utf16_length;
                 text_layout.SetFontCollection(collection, text_range)?;
-                text_layout
-                    .SetFontFamilyName(&HSTRING::from(&font_info.font_family), text_range)?;
+                text_layout.SetFontFamilyName(&font_info.font_family_h, text_range)?;
                 let font_size = if break_ligatures {
                     font_size.0.next_up()
                 } else {
@@ -673,13 +609,14 @@ impl DirectWriteState {
             let mut runs = Vec::new();
             let renderer_context = RendererContext {
                 text_system: self,
+                components,
                 index_converter: StringIndexConverter::new(text),
                 runs: &mut runs,
                 width: 0.0,
             };
             text_layout.Draw(
-                Some(&renderer_context as *const _ as _),
-                &text_renderer.0,
+                Some((&raw const renderer_context).cast::<c_void>()),
+                &components.text_renderer.0,
                 0.0,
                 0.0,
             )?;
@@ -727,6 +664,7 @@ impl DirectWriteState {
 
     fn create_glyph_run_analysis(
         &self,
+        components: &DirectWriteComponents,
         params: &RenderGlyphParams,
     ) -> Result<IDWriteGlyphRunAnalysis> {
         let font = &self.fonts[params.font_id.0];
@@ -734,7 +672,7 @@ impl DirectWriteState {
         let advance = [0.0];
         let offset = [DWRITE_GLYPH_OFFSET::default()];
         let glyph_run = DWRITE_GLYPH_RUN {
-            fontFace: unsafe { std::mem::transmute_copy(&font.font_face) },
+            fontFace: ManuallyDrop::new(Some(unsafe { std::ptr::read(&***font.font_face) })),
             fontEmSize: params.font_size.0,
             glyphCount: 1,
             glyphIndices: glyph_id.as_ptr(),
@@ -785,7 +723,7 @@ impl DirectWriteState {
         };
 
         let glyph_analysis = unsafe {
-            self.components.factory.CreateGlyphRunAnalysis(
+            components.factory.CreateGlyphRunAnalysis(
                 &glyph_run,
                 Some(&transform),
                 rendering_mode,
@@ -799,8 +737,12 @@ impl DirectWriteState {
         Ok(glyph_analysis)
     }
 
-    fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
-        let glyph_analysis = self.create_glyph_run_analysis(params)?;
+    fn raster_bounds(
+        &self,
+        components: &DirectWriteComponents,
+        params: &RenderGlyphParams,
+    ) -> Result<Bounds<DevicePixels>> {
+        let glyph_analysis = self.create_glyph_run_analysis(components, params)?;
 
         let texture_type = if params.subpixel_rendering {
             DWRITE_TEXTURE_CLEARTYPE_3x1
@@ -828,19 +770,20 @@ impl DirectWriteState {
 
     fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
         let font_info = &self.fonts[font_id.0];
-        let codepoints = [ch as u32];
-        let mut glyph_indices = vec![0u16; 1];
+        let codepoints = ch as u32;
+        let mut glyph_indices = 0u16;
         unsafe {
             font_info
                 .font_face
-                .GetGlyphIndices(codepoints.as_ptr(), 1, glyph_indices.as_mut_ptr())
+                .GetGlyphIndices(&raw const codepoints, 1, &raw mut glyph_indices)
                 .log_err()
         }
-        .map(|_| GlyphId(glyph_indices[0] as u32))
+        .map(|_| GlyphId(glyph_indices as u32))
     }
 
     fn rasterize_glyph(
         &self,
+        components: &DirectWriteComponents,
         params: &RenderGlyphParams,
         glyph_bounds: Bounds<DevicePixels>,
     ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
@@ -849,17 +792,17 @@ impl DirectWriteState {
         }
 
         let bitmap_data = if params.is_emoji {
-            if let Ok(color) = self.rasterize_color(params, glyph_bounds) {
+            if let Ok(color) = self.rasterize_color(components, params, glyph_bounds) {
                 color
             } else {
-                let monochrome = self.rasterize_monochrome(params, glyph_bounds)?;
+                let monochrome = self.rasterize_monochrome(components, params, glyph_bounds)?;
                 monochrome
                     .into_iter()
                     .flat_map(|pixel| [0, 0, 0, pixel])
                     .collect::<Vec<_>>()
             }
         } else {
-            self.rasterize_monochrome(params, glyph_bounds)?
+            self.rasterize_monochrome(components, params, glyph_bounds)?
         };
 
         Ok((glyph_bounds.size, bitmap_data))
@@ -867,14 +810,14 @@ impl DirectWriteState {
 
     fn rasterize_monochrome(
         &self,
+        components: &DirectWriteComponents,
         params: &RenderGlyphParams,
         glyph_bounds: Bounds<DevicePixels>,
     ) -> Result<Vec<u8>> {
+        let glyph_analysis = self.create_glyph_run_analysis(components, params)?;
         if !params.subpixel_rendering {
             let mut bitmap_data =
                 vec![0u8; glyph_bounds.size.width.0 as usize * glyph_bounds.size.height.0 as usize];
-
-            let glyph_analysis = self.create_glyph_run_analysis(params)?;
             unsafe {
                 glyph_analysis.CreateAlphaTexture(
                     DWRITE_TEXTURE_ALIASED_1x1,
@@ -897,7 +840,6 @@ impl DirectWriteState {
 
         let mut bitmap_data = vec![0u8; pixel_count * 4];
 
-        let glyph_analysis = self.create_glyph_run_analysis(params)?;
         unsafe {
             glyph_analysis.CreateAlphaTexture(
                 DWRITE_TEXTURE_CLEARTYPE_3x1,
@@ -933,6 +875,7 @@ impl DirectWriteState {
 
     fn rasterize_color(
         &self,
+        components: &DirectWriteComponents,
         params: &RenderGlyphParams,
         glyph_bounds: Bounds<DevicePixels>,
     ) -> Result<Vec<u8>> {
@@ -960,7 +903,7 @@ impl DirectWriteState {
             ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor,
         }];
         let glyph_run = DWRITE_GLYPH_RUN {
-            fontFace: unsafe { std::mem::transmute_copy(&font.font_face) },
+            fontFace: ManuallyDrop::new(Some(unsafe { std::ptr::read(&***font.font_face) })),
             fontEmSize: params.font_size.0,
             glyphCount: 1,
             glyphIndices: glyph_id.as_ptr(),
@@ -972,7 +915,7 @@ impl DirectWriteState {
 
         // todo: support formats other than COLR
         let color_enumerator = unsafe {
-            self.components.factory.TranslateColorGlyphRun(
+            components.factory.TranslateColorGlyphRun(
                 Vector2::new(baseline_origin_x, baseline_origin_y),
                 &glyph_run,
                 None,
@@ -984,13 +927,14 @@ impl DirectWriteState {
         }?;
 
         let mut glyph_layers = Vec::new();
+        let mut alpha_data = Vec::new();
         loop {
             let color_run = unsafe { color_enumerator.GetCurrentRun() }?;
             let color_run = unsafe { &*color_run };
             let image_format = color_run.glyphImageFormat & !DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE;
             if image_format == DWRITE_GLYPH_IMAGE_FORMATS_COLR {
                 let color_analysis = unsafe {
-                    self.components.factory.CreateGlyphRunAnalysis(
+                    components.factory.CreateGlyphRunAnalysis(
                         &color_run.Base.glyphRun as *const _,
                         Some(&transform),
                         DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC,
@@ -1010,7 +954,8 @@ impl DirectWriteState {
                     color_bounds.bottom - color_bounds.top,
                 );
                 if color_size.width > 0 && color_size.height > 0 {
-                    let mut alpha_data = vec![0u8; (color_size.width * color_size.height) as usize];
+                    alpha_data.clear();
+                    alpha_data.resize((color_size.width * color_size.height) as usize, 0);
                     unsafe {
                         color_analysis.CreateAlphaTexture(
                             DWRITE_TEXTURE_ALIASED_1x1,
@@ -1030,7 +975,7 @@ impl DirectWriteState {
                     };
                     let bounds = bounds(point(color_bounds.left, color_bounds.top), color_size);
                     glyph_layers.push(GlyphLayerTexture::new(
-                        &self.components.gpu_state,
+                        &self.gpu_state,
                         run_color,
                         bounds,
                         &alpha_data,
@@ -1046,7 +991,7 @@ impl DirectWriteState {
             }
         }
 
-        let gpu_state = &self.components.gpu_state;
+        let gpu_state = &self.gpu_state;
         let params_buffer = {
             let desc = D3D11_BUFFER_DESC {
                 ByteWidth: std::mem::size_of::<GlyphLayerTextureParams>() as u32,
@@ -1063,7 +1008,7 @@ impl DirectWriteState {
                     .device
                     .CreateBuffer(&desc, None, Some(&mut buffer))
             }?;
-            [buffer]
+            buffer
         };
 
         let render_target_texture = {
@@ -1107,7 +1052,7 @@ impl DirectWriteState {
                     Some(&mut rtv),
                 )
             }?;
-            [rtv]
+            rtv
         };
 
         let staging_texture = {

crates/gpui/src/text_system.rs 🔗

@@ -943,3 +943,19 @@ pub(crate) fn font_name_with_fallbacks<'a>(name: &'a str, system: &'a str) -> &'
         _ => name,
     }
 }
+
+#[allow(unused)]
+pub(crate) fn font_name_with_fallbacks_shared<'a>(
+    name: &'a SharedString,
+    system: &'a SharedString,
+) -> &'a SharedString {
+    // Note: the "Zed Plex" fonts were deprecated as we are not allowed to use "Plex"
+    // in a derived font name. They are essentially indistinguishable from IBM Plex/Lilex,
+    // and so retained here for backward compatibility.
+    match name.as_str() {
+        ".SystemUIFont" => system,
+        ".ZedSans" | "Zed Plex Sans" => const { &SharedString::new_static("IBM Plex Sans") },
+        ".ZedMono" | "Zed Plex Mono" => const { &SharedString::new_static("Lilex") },
+        _ => name,
+    }
+}