gpui/metal: Clamp max texture width/height to 16kB (#10314)

Piotr Osiewicz created

Fixed #10149
A user had Zed crash due to invalid font size in settings. It turned out
the width/height of glyphs does not pass validation in Metal texture
initialization with a large enough font size.

All modern Macs have a max texture width/height of 16kB (barring Apple
A8, used by iPhone 6 back in 2014, which uses 8kB). This commit clamps
texture size at 16kB. Note that while it fixes Zed crash, using a font
size that hits the limit is still pretty unusable - the users will still
have a pretty unusable editor, but at least it won't crash for them.



Release Notes:

- Fixed crashes with huge `buffer_font_size` values.

Change summary

crates/gpui/src/geometry.rs                 | 29 +++++++++++++++++++++++
crates/gpui/src/platform/mac/metal_atlas.rs |  9 +++++-
2 files changed, 36 insertions(+), 2 deletions(-)

Detailed changes

crates/gpui/src/geometry.rs 🔗

@@ -528,6 +528,35 @@ where
             },
         }
     }
+    /// Returns a new `Size` with the minimum width and height from `self` and `other`.
+    ///
+    /// # Arguments
+    ///
+    /// * `other` - A reference to another `Size` to compare with `self`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Size;
+    /// let size1 = Size { width: 30, height: 40 };
+    /// let size2 = Size { width: 50, height: 20 };
+    /// let min_size = size1.min(&size2);
+    /// assert_eq!(min_size, Size { width: 30, height: 20 });
+    /// ```
+    pub fn min(&self, other: &Self) -> Self {
+        Size {
+            width: if self.width >= other.width {
+                other.width.clone()
+            } else {
+                self.width.clone()
+            },
+            height: if self.height >= other.height {
+                other.height.clone()
+            } else {
+                self.height.clone()
+            },
+        }
+    }
 }
 
 impl<T> Sub for Size<T>

crates/gpui/src/platform/mac/metal_atlas.rs 🔗

@@ -83,6 +83,7 @@ impl MetalAtlasState {
             AtlasTextureKind::Polychrome => &mut self.polychrome_textures,
             AtlasTextureKind::Path => &mut self.path_textures,
         };
+
         textures
             .iter_mut()
             .rev()
@@ -102,8 +103,12 @@ impl MetalAtlasState {
             width: DevicePixels(1024),
             height: DevicePixels(1024),
         };
-
-        let size = min_size.max(&DEFAULT_ATLAS_SIZE);
+        // Max texture size on all modern Apple GPUs. Anything bigger than that crashes in validateWithDevice.
+        const MAX_ATLAS_SIZE: Size<DevicePixels> = Size {
+            width: DevicePixels(16384),
+            height: DevicePixels(16384),
+        };
+        let size = min_size.min(&MAX_ATLAS_SIZE).max(&DEFAULT_ATLAS_SIZE);
         let texture_descriptor = metal::TextureDescriptor::new();
         texture_descriptor.set_width(size.width.into());
         texture_descriptor.set_height(size.height.into());