Checkpoint

Nathan Sobo created

Change summary

crates/gpui3/src/geometry.rs                    |  62 +++++++++-
crates/gpui3/src/platform.rs                    |  10 
crates/gpui3/src/platform/mac/metal_atlas.rs    |  22 ++-
crates/gpui3/src/platform/mac/metal_renderer.rs | 110 ++++++++++++++++--
crates/gpui3/src/platform/mac/shaders.metal     |  59 +---------
crates/gpui3/src/platform/mac/text_system.rs    |  52 +++++---
crates/gpui3/src/platform/mac/window.rs         |   6 
crates/gpui3/src/scene.rs                       |  23 +--
crates/gpui3/src/style.rs                       |  17 ++
crates/gpui3/src/text_system.rs                 |  10 
crates/gpui3/src/text_system/line.rs            |  24 +--
crates/gpui3/src/window.rs                      |  36 +----
crates/storybook2/src/workspace.rs              |   3 
13 files changed, 248 insertions(+), 186 deletions(-)

Detailed changes

crates/gpui3/src/geometry.rs 🔗

@@ -28,6 +28,15 @@ impl<T: Clone + Debug> Point<T> {
     }
 }
 
+impl Point<Pixels> {
+    pub fn scale(&self, factor: f32) -> Point<ScaledPixels> {
+        Point {
+            x: self.x.scale(factor),
+            y: self.y.scale(factor),
+        }
+    }
+}
+
 impl<T, Rhs> Mul<Rhs> for Point<T>
 where
     T: Mul<Rhs, Output = T> + Clone + Debug,
@@ -122,6 +131,15 @@ impl<T: Clone + Debug> Size<T> {
     }
 }
 
+impl Size<Pixels> {
+    pub fn scale(&self, factor: f32) -> Size<ScaledPixels> {
+        Size {
+            width: self.width.scale(factor),
+            height: self.height.scale(factor),
+        }
+    }
+}
+
 impl<T: Clone + Debug + Ord> Size<T> {
     pub fn max(&self, other: &Self) -> Self {
         Size {
@@ -207,7 +225,6 @@ pub struct Bounds<T: Clone + Debug> {
     pub size: Size<T>,
 }
 
-// Bounds<f32> * Pixels = Bounds<Pixels>
 impl<T, Rhs> Mul<Rhs> for Bounds<T>
 where
     T: Mul<Rhs, Output = Rhs> + Clone + Debug,
@@ -277,6 +294,15 @@ impl<T: Clone + Debug + PartialOrd + Add<T, Output = T>> Bounds<T> {
     }
 }
 
+impl Bounds<Pixels> {
+    pub fn scale(&self, factor: f32) -> Bounds<ScaledPixels> {
+        Bounds {
+            origin: self.origin.scale(factor),
+            size: self.size.scale(factor),
+        }
+    }
+}
+
 impl<T: Clone + Debug + Copy> Copy for Bounds<T> {}
 
 #[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
@@ -462,8 +488,8 @@ impl Pixels {
         Self(self.0.floor())
     }
 
-    pub fn to_device_pixels(&self, scale: f32) -> DevicePixels {
-        DevicePixels((self.0 * scale).ceil() as u32)
+    pub fn scale(&self, factor: f32) -> ScaledPixels {
+        ScaledPixels(self.0 * factor)
     }
 }
 
@@ -542,22 +568,22 @@ impl From<Pixels> for f64 {
     SubAssign,
 )]
 #[repr(transparent)]
-pub struct DevicePixels(pub(crate) u32);
+pub struct DevicePixels(pub(crate) i32);
 
 impl DevicePixels {
     pub fn to_bytes(&self, bytes_per_pixel: u8) -> u32 {
-        self.0 * bytes_per_pixel as u32
+        self.0 as u32 * bytes_per_pixel as u32
     }
 }
 
-impl From<DevicePixels> for u32 {
+impl From<DevicePixels> for i32 {
     fn from(device_pixels: DevicePixels) -> Self {
         device_pixels.0
     }
 }
 
-impl From<u32> for DevicePixels {
-    fn from(val: u32) -> Self {
+impl From<i32> for DevicePixels {
+    fn from(val: i32) -> Self {
         DevicePixels(val)
     }
 }
@@ -570,7 +596,25 @@ impl From<DevicePixels> for u64 {
 
 impl From<u64> for DevicePixels {
     fn from(val: u64) -> Self {
-        DevicePixels(val as u32)
+        DevicePixels(val as i32)
+    }
+}
+
+#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)]
+#[repr(transparent)]
+pub struct ScaledPixels(pub(crate) f32);
+
+impl Eq for ScaledPixels {}
+
+impl Debug for ScaledPixels {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{} px (scaled)", self.0)
+    }
+}
+
+impl From<ScaledPixels> for DevicePixels {
+    fn from(scaled: ScaledPixels) -> Self {
+        DevicePixels(scaled.0.ceil() as i32)
     }
 }
 

crates/gpui3/src/platform.rs 🔗

@@ -7,7 +7,7 @@ mod test;
 
 use crate::{
     AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point,
-    RasterizedGlyphId, Result, Scene, ShapedLine, SharedString, Size,
+    RasterizeGlyphParams, Result, Scene, ShapedLine, SharedString, Size,
 };
 use anyhow::anyhow;
 use async_task::Runnable;
@@ -147,7 +147,7 @@ pub trait PlatformWindow {
     fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
     fn draw(&self, scene: Scene);
 
-    fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<RasterizedGlyphId>>;
+    fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<RasterizeGlyphParams>>;
 }
 
 pub trait PlatformDispatcher: Send + Sync {
@@ -165,8 +165,8 @@ pub trait PlatformTextSystem: Send + Sync {
     fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
     fn rasterize_glyph(
         &self,
-        glyph_id: &RasterizedGlyphId,
-    ) -> Result<(Bounds<DevicePixels>, Vec<u8>)>;
+        glyph_id: &RasterizeGlyphParams,
+    ) -> Result<(Size<DevicePixels>, Vec<u8>)>;
     fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> ShapedLine;
     fn wrap_line(
         &self,
@@ -197,7 +197,7 @@ pub struct AtlasTile {
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 #[repr(C)]
-pub(crate) struct AtlasTextureId(pub(crate) u32);
+pub(crate) struct AtlasTextureId(pub(crate) u32); // We use u32 instead of usize for Metal Shader Language compatibility
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 #[repr(C)]

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

@@ -28,6 +28,10 @@ impl<Key> MetalAtlas<Key> {
             tiles_by_key: Default::default(),
         }))
     }
+
+    pub(crate) fn texture(&self, id: AtlasTextureId) -> metal::Texture {
+        self.0.lock().textures[id.0 as usize].metal_texture.clone()
+    }
 }
 
 struct MetalAtlasState<Key> {
@@ -123,10 +127,10 @@ impl MetalAtlasTexture {
             bounds: allocation.rectangle.into(),
         };
         let region = metal::MTLRegion::new_2d(
-            u32::from(tile.bounds.origin.x) as u64,
-            u32::from(tile.bounds.origin.y) as u64,
-            u32::from(tile.bounds.size.width) as u64,
-            u32::from(tile.bounds.size.height) as u64,
+            tile.bounds.origin.x.into(),
+            tile.bounds.origin.y.into(),
+            tile.bounds.size.width.into(),
+            tile.bounds.size.height.into(),
         );
         self.metal_texture.replace_region(
             region,
@@ -149,15 +153,15 @@ impl MetalAtlasTexture {
 
 impl From<Size<DevicePixels>> for etagere::Size {
     fn from(size: Size<DevicePixels>) -> Self {
-        etagere::Size::new(u32::from(size.width) as i32, u32::from(size.width) as i32)
+        etagere::Size::new(size.width.into(), size.width.into())
     }
 }
 
 impl From<etagere::Point> for Point<DevicePixels> {
     fn from(value: etagere::Point) -> Self {
         Point {
-            x: DevicePixels::from(value.x as u32),
-            y: DevicePixels::from(value.y as u32),
+            x: DevicePixels::from(value.x),
+            y: DevicePixels::from(value.y),
         }
     }
 }
@@ -165,8 +169,8 @@ impl From<etagere::Point> for Point<DevicePixels> {
 impl From<etagere::Size> for Size<DevicePixels> {
     fn from(size: etagere::Size) -> Self {
         Size {
-            width: DevicePixels::from(size.width as u32),
-            height: DevicePixels::from(size.height as u32),
+            width: DevicePixels::from(size.width),
+            height: DevicePixels::from(size.height),
         }
     }
 }

crates/gpui3/src/platform/mac/metal_renderer.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, Quad,
-    RasterizedGlyphId, Scene, Size,
+    RasterizeGlyphParams, Scene, Size,
 };
 use cocoa::{
     base::{NO, YES},
@@ -18,10 +18,11 @@ pub struct MetalRenderer {
     device: metal::Device,
     layer: metal::MetalLayer,
     command_queue: CommandQueue,
-    quad_pipeline_state: metal::RenderPipelineState,
+    quads_pipeline_state: metal::RenderPipelineState,
+    sprites_pipeline_state: metal::RenderPipelineState,
     unit_vertices: metal::Buffer,
     instances: metal::Buffer,
-    glyph_atlas: Arc<MetalAtlas<RasterizedGlyphId>>,
+    glyph_atlas: Arc<MetalAtlas<RasterizeGlyphParams>>,
 }
 
 impl MetalRenderer {
@@ -81,15 +82,24 @@ impl MetalRenderer {
             MTLResourceOptions::StorageModeManaged,
         );
 
-        let quad_pipeline_state = build_pipeline_state(
+        let quads_pipeline_state = build_pipeline_state(
             &device,
             &library,
-            "quad",
+            "quads",
             "quad_vertex",
             "quad_fragment",
             PIXEL_FORMAT,
         );
 
+        let sprites_pipeline_state = build_pipeline_state(
+            &device,
+            &library,
+            "sprites",
+            "monochrome_sprite_vertex",
+            "monochrome_sprite_fragment",
+            PIXEL_FORMAT,
+        );
+
         let command_queue = device.new_command_queue();
         let glyph_atlas = Arc::new(MetalAtlas::new(
             Size {
@@ -104,7 +114,8 @@ impl MetalRenderer {
             device,
             layer,
             command_queue,
-            quad_pipeline_state,
+            quads_pipeline_state,
+            sprites_pipeline_state,
             unit_vertices,
             instances,
             glyph_atlas,
@@ -115,7 +126,7 @@ impl MetalRenderer {
         &*self.layer
     }
 
-    pub fn glyph_atlas(&self) -> &Arc<MetalAtlas<RasterizedGlyphId>> {
+    pub fn glyph_atlas(&self) -> &Arc<MetalAtlas<RasterizeGlyphParams>> {
         &self.glyph_atlas
     }
 
@@ -123,8 +134,8 @@ impl MetalRenderer {
         let layer = self.layer.clone();
         let viewport_size = layer.drawable_size();
         let viewport_size: Size<DevicePixels> = size(
-            (viewport_size.width.ceil() as u32).into(),
-            (viewport_size.height.ceil() as u32).into(),
+            (viewport_size.width.ceil() as i32).into(),
+            (viewport_size.height.ceil() as i32).into(),
         );
         let drawable = if let Some(drawable) = layer.next_drawable() {
             drawable
@@ -144,8 +155,8 @@ impl MetalRenderer {
         depth_texture_desc.set_pixel_format(metal::MTLPixelFormat::Depth32Float);
         depth_texture_desc.set_storage_mode(metal::MTLStorageMode::Private);
         depth_texture_desc.set_usage(metal::MTLTextureUsage::RenderTarget);
-        depth_texture_desc.set_width(u32::from(viewport_size.width) as u64);
-        depth_texture_desc.set_height(u32::from(viewport_size.height) as u64);
+        depth_texture_desc.set_width(i32::from(viewport_size.width) as u64);
+        depth_texture_desc.set_height(i32::from(viewport_size.height) as u64);
         let depth_texture = self.device.new_texture(&depth_texture_desc);
         let depth_attachment = render_pass_descriptor.depth_attachment().unwrap();
 
@@ -168,8 +179,8 @@ impl MetalRenderer {
         command_encoder.set_viewport(metal::MTLViewport {
             originX: 0.0,
             originY: 0.0,
-            width: u32::from(viewport_size.width) as f64,
-            height: u32::from(viewport_size.height) as f64,
+            width: i32::from(viewport_size.width) as f64,
+            height: i32::from(viewport_size.height) as f64,
             znear: 0.0,
             zfar: 1.0,
         });
@@ -226,7 +237,7 @@ impl MetalRenderer {
         }
         align_offset(offset);
 
-        command_encoder.set_render_pipeline_state(&self.quad_pipeline_state);
+        command_encoder.set_render_pipeline_state(&self.quads_pipeline_state);
         command_encoder.set_vertex_buffer(
             QuadInputIndex::Vertices as u64,
             Some(&self.unit_vertices),
@@ -273,12 +284,77 @@ impl MetalRenderer {
     fn draw_monochrome_sprites(
         &mut self,
         texture_id: AtlasTextureId,
-        monochrome: &[MonochromeSprite],
+        sprites: &[MonochromeSprite],
         offset: &mut usize,
         viewport_size: Size<DevicePixels>,
         command_encoder: &metal::RenderCommandEncoderRef,
     ) {
-        // todo!()
+        // dbg!(sprites);
+
+        if sprites.is_empty() {
+            return;
+        }
+        align_offset(offset);
+
+        let texture = self.glyph_atlas.texture(texture_id);
+        let texture_size = size(
+            DevicePixels(texture.width() as i32),
+            DevicePixels(texture.height() as i32),
+        );
+        command_encoder.set_render_pipeline_state(&self.sprites_pipeline_state);
+        command_encoder.set_vertex_buffer(
+            MonochromeSpriteInputIndex::Vertices as u64,
+            Some(&self.unit_vertices),
+            0,
+        );
+        command_encoder.set_vertex_buffer(
+            MonochromeSpriteInputIndex::Sprites as u64,
+            Some(&self.instances),
+            *offset as u64,
+        );
+        command_encoder.set_vertex_bytes(
+            MonochromeSpriteInputIndex::ViewportSize as u64,
+            mem::size_of_val(&viewport_size) as u64,
+            &viewport_size as *const Size<DevicePixels> as *const _,
+        );
+        command_encoder.set_vertex_bytes(
+            MonochromeSpriteInputIndex::AtlasTextureSize as u64,
+            mem::size_of_val(&texture_size) as u64,
+            &texture_size as *const Size<DevicePixels> as *const _,
+        );
+        command_encoder.set_fragment_buffer(
+            MonochromeSpriteInputIndex::Sprites as u64,
+            Some(&self.instances),
+            *offset as u64,
+        );
+        command_encoder.set_fragment_texture(
+            MonochromeSpriteInputIndex::AtlasTexture as u64,
+            Some(&texture),
+        );
+
+        let sprite_bytes_len = mem::size_of::<MonochromeSprite>() * sprites.len();
+        let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
+        unsafe {
+            ptr::copy_nonoverlapping(
+                sprites.as_ptr() as *const u8,
+                buffer_contents,
+                sprite_bytes_len,
+            );
+        }
+
+        let next_offset = *offset + sprite_bytes_len;
+        assert!(
+            next_offset <= INSTANCE_BUFFER_SIZE,
+            "instance buffer exhausted"
+        );
+
+        command_encoder.draw_primitives_instanced(
+            metal::MTLPrimitiveType::Triangle,
+            0,
+            6,
+            sprites.len() as u64,
+        );
+        *offset = next_offset;
     }
 }
 
@@ -334,6 +410,6 @@ enum MonochromeSpriteInputIndex {
     Vertices = 0,
     Sprites = 1,
     ViewportSize = 2,
-    AtlasSize = 3,
+    AtlasTextureSize = 3,
     AtlasTexture = 4,
 }

crates/gpui3/src/platform/mac/shaders.metal 🔗

@@ -126,7 +126,7 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
     constant Size_DevicePixels *viewport_size
     [[buffer(MonochromeSpriteInputIndex_ViewportSize)]],
     constant Size_DevicePixels *atlas_size
-    [[buffer(MonochromeSpriteInputIndex_AtlasSize)]]) {
+    [[buffer(MonochromeSpriteInputIndex_AtlasTextureSize)]]) {
 
   float2 unit_vertex = unit_vertices[unit_vertex_id];
   MonochromeSprite sprite = sprites[sprite_id];
@@ -149,11 +149,13 @@ fragment float4 monochrome_sprite_fragment(
     MonochromeSpriteVertexOutput input [[stage_in]],
     constant MonochromeSprite *sprites
     [[buffer(MonochromeSpriteInputIndex_Sprites)]],
-    texture2d<float> atlas
+    texture2d<float> atlas_texture
     [[texture(MonochromeSpriteInputIndex_AtlasTexture)]]) {
   MonochromeSprite sprite = sprites[input.sprite_id];
-  constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear);
-  float4 sample = atlas.sample(atlas_sampler, input.tile_position);
+  constexpr sampler atlas_texture_sampler(mag_filter::linear,
+                                          min_filter::linear);
+  float4 sample =
+      atlas_texture.sample(atlas_texture_sampler, input.tile_position);
   float clip_distance =
       quad_sdf(input.position.xy, sprite.clip_bounds, sprite.clip_corner_radii);
   float4 color = input.color;
@@ -256,52 +258,3 @@ float quad_sdf(float2 point, Bounds_Pixels bounds,
 
   return distance;
 }
-
-// struct SpriteFragmentInput {
-//     float4 position [[position]];
-//     float2 atlas_position;
-//     float4 color [[flat]];
-//     uchar compute_winding [[flat]];
-// };
-
-// vertex SpriteFragmentInput sprite_vertex(
-//     uint unit_vertex_id [[vertex_id]],
-//     uint sprite_id [[instance_id]],
-//     constant float2 *unit_vertices
-//     [[buffer(GPUISpriteVertexInputIndexVertices)]], constant GPUISprite
-//     *sprites [[buffer(GPUISpriteVertexInputIndexSprites)]], constant float2
-//     *viewport_size [[buffer(GPUISpriteVertexInputIndexViewportSize)]],
-//     constant float2 *atlas_size
-//     [[buffer(GPUISpriteVertexInputIndexAtlasSize)]]
-// ) {
-//     float2 unit_vertex = unit_vertices[unit_vertex_id];
-//     GPUISprite sprite = sprites[sprite_id];
-//     float2 position = unit_vertex * sprite.target_size + sprite.origin;
-//     float4 device_position = to_device_position(position, *viewport_size);
-//     float2 atlas_position = (unit_vertex * sprite.source_size +
-//     sprite.atlas_origin) / *atlas_size;
-
-//     return SpriteFragmentInput {
-//         device_position,
-//         atlas_position,
-//         coloru_to_colorf(sprite.color),
-//         sprite.compute_winding
-//     };
-// }
-
-// fragment float4 sprite_fragment(
-//     SpriteFragmentInput input [[stage_in]],
-//     texture2d<float> atlas [[ texture(GPUISpriteFragmentInputIndexAtlas) ]]
-// ) {
-//     constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear);
-//     float4 color = input.color;
-//     float4 sample = atlas.sample(atlas_sampler, input.atlas_position);
-//     float mask;
-//     if (input.compute_winding) {
-//         mask = 1. - abs(1. - fmod(sample.r, 2.));
-//     } else {
-//         mask = sample.a;
-//     }
-//     color.a *= mask;
-//     return color;
-// }

crates/gpui3/src/platform/mac/text_system.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     point, px, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontStyle,
-    FontWeight, GlyphId, Pixels, PlatformTextSystem, Point, RasterizedGlyphId, Result, ShapedGlyph,
-    ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS,
+    FontWeight, GlyphId, Pixels, PlatformTextSystem, Point, RasterizeGlyphParams, Result,
+    ShapedGlyph, ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS,
 };
 use anyhow::anyhow;
 use cocoa::appkit::{CGFloat, CGPoint};
@@ -136,8 +136,8 @@ impl PlatformTextSystem for MacTextSystem {
 
     fn rasterize_glyph(
         &self,
-        glyph_id: &RasterizedGlyphId,
-    ) -> Result<(Bounds<DevicePixels>, Vec<u8>)> {
+        glyph_id: &RasterizeGlyphParams,
+    ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
         self.0.read().rasterize_glyph(glyph_id)
     }
 
@@ -232,8 +232,8 @@ impl MacTextSystemState {
 
     fn rasterize_glyph(
         &self,
-        glyph_id: &RasterizedGlyphId,
-    ) -> Result<(Bounds<DevicePixels>, Vec<u8>)> {
+        glyph_id: &RasterizeGlyphParams,
+    ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
         let font = &self.fonts[glyph_id.font_id.0];
         let scale = Transform2F::from_scale(glyph_id.scale_factor);
         let glyph_bounds = font.raster_bounds(
@@ -252,18 +252,15 @@ impl MacTextSystemState {
                 glyph_id.subpixel_variant.x.min(1) as i32,
                 glyph_id.subpixel_variant.y.min(1) as i32,
             );
-            let cx_bounds = RectI::new(
-                glyph_bounds.origin(),
-                glyph_bounds.size() + subpixel_padding,
-            );
+            let bitmap_size = glyph_bounds.size() + subpixel_padding;
 
-            let mut bytes = vec![0; cx_bounds.width() as usize * cx_bounds.height() as usize];
+            let mut bytes = vec![0; bitmap_size.x() as usize * bitmap_size.y() as usize];
             let cx = CGContext::create_bitmap_context(
                 Some(bytes.as_mut_ptr() as *mut _),
-                cx_bounds.width() as usize,
-                cx_bounds.height() as usize,
+                bitmap_size.x() as usize,
+                bitmap_size.y() as usize,
                 8,
-                cx_bounds.width() as usize,
+                bitmap_size.x() as usize,
                 &CGColorSpace::create_device_gray(),
                 kCGImageAlphaOnly,
             );
@@ -298,7 +295,7 @@ impl MacTextSystemState {
                     cx,
                 );
 
-            Ok((cx_bounds.into(), bytes))
+            Ok((bitmap_size.into(), bytes))
         }
     }
 
@@ -511,14 +508,23 @@ impl From<RectF> for Bounds<f32> {
 impl From<RectI> for Bounds<DevicePixels> {
     fn from(rect: RectI) -> Self {
         Bounds {
-            origin: point(
-                DevicePixels(rect.origin_x() as u32),
-                DevicePixels(rect.origin_y() as u32),
-            ),
-            size: size(
-                DevicePixels(rect.width() as u32),
-                DevicePixels(rect.height() as u32),
-            ),
+            origin: point(DevicePixels(rect.origin_x()), DevicePixels(rect.origin_y())),
+            size: size(DevicePixels(rect.width()), DevicePixels(rect.height())),
+        }
+    }
+}
+
+impl From<Vector2I> for Size<DevicePixels> {
+    fn from(value: Vector2I) -> Self {
+        size(value.x().into(), value.y().into())
+    }
+}
+
+impl From<RectI> for Bounds<i32> {
+    fn from(rect: RectI) -> Self {
+        Bounds {
+            origin: point(rect.origin_x(), rect.origin_y()),
+            size: size(rect.width(), rect.height()),
         }
     }
 }

crates/gpui3/src/platform/mac/window.rs 🔗

@@ -3,8 +3,8 @@ use crate::{
     point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers,
     ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt,
     Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen,
-    PlatformWindow, Point, RasterizedGlyphId, Scene, Size, Timer, WindowAppearance, WindowBounds,
-    WindowKind, WindowOptions, WindowPromptLevel,
+    PlatformWindow, Point, RasterizeGlyphParams, Scene, Size, Timer, WindowAppearance,
+    WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
 };
 use block::ConcreteBlock;
 use cocoa::{
@@ -886,7 +886,7 @@ impl PlatformWindow for MacWindow {
         }
     }
 
-    fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<RasterizedGlyphId>> {
+    fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<RasterizeGlyphParams>> {
         self.0.lock().renderer.glyph_atlas().clone()
     }
 }

crates/gpui3/src/scene.rs 🔗

@@ -1,7 +1,7 @@
 use std::{iter::Peekable, mem};
 
 use super::{Bounds, Hsla, Pixels, Point};
-use crate::{AtlasTextureId, AtlasTile, Corners, Edges};
+use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledPixels};
 use collections::BTreeMap;
 use smallvec::SmallVec;
 
@@ -36,7 +36,6 @@ impl Scene {
         let primitive = primitive.into();
         match primitive {
             Primitive::Quad(mut quad) => {
-                quad.scale(self.scale_factor);
                 layer.quads.push(quad);
             }
             Primitive::Sprite(sprite) => {
@@ -187,17 +186,17 @@ pub(crate) enum PrimitiveBatch<'a> {
 #[repr(C)]
 pub struct Quad {
     pub order: u32,
-    pub bounds: Bounds<Pixels>,
-    pub clip_bounds: Bounds<Pixels>,
-    pub clip_corner_radii: Corners<Pixels>,
+    pub bounds: Bounds<ScaledPixels>,
+    pub clip_bounds: Bounds<ScaledPixels>,
+    pub clip_corner_radii: Corners<ScaledPixels>,
     pub background: Hsla,
     pub border_color: Hsla,
-    pub corner_radii: Corners<Pixels>,
-    pub border_widths: Edges<Pixels>,
+    pub corner_radii: Corners<ScaledPixels>,
+    pub border_widths: Edges<ScaledPixels>,
 }
 
 impl Quad {
-    pub fn vertices(&self) -> impl Iterator<Item = Point<Pixels>> {
+    pub fn vertices(&self) -> impl Iterator<Item = Point<ScaledPixels>> {
         let x1 = self.bounds.origin.x;
         let y1 = self.bounds.origin.y;
         let x2 = x1 + self.bounds.size.width;
@@ -210,14 +209,6 @@ impl Quad {
         ]
         .into_iter()
     }
-
-    pub fn scale(&mut self, factor: f32) {
-        self.bounds *= factor;
-        self.clip_bounds *= factor;
-        self.clip_corner_radii *= factor;
-        self.corner_radii *= factor;
-        self.border_widths *= factor;
-    }
 }
 
 impl Ord for Quad {

crates/gpui3/src/style.rs 🔗

@@ -182,6 +182,7 @@ impl Style {
     /// Paints the background of an element styled with this style.
     pub fn paint<V: 'static>(&self, order: u32, bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) {
         let rem_size = cx.rem_size();
+        let scale = cx.scale_factor();
 
         let background_color = self.fill.as_ref().and_then(Fill::color);
         if background_color.is_some() || self.is_border_visible() {
@@ -190,13 +191,19 @@ impl Style {
                 layer_id,
                 Quad {
                     order,
-                    bounds,
-                    clip_bounds: bounds, // todo!
-                    clip_corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)),
+                    bounds: bounds.scale(scale),
+                    clip_bounds: bounds.scale(scale), // todo!
+                    clip_corner_radii: self
+                        .corner_radii
+                        .map(|length| length.to_pixels(rem_size).scale(scale)),
                     background: background_color.unwrap_or_default(),
                     border_color: self.border_color.unwrap_or_default(),
-                    corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)),
-                    border_widths: self.border_widths.map(|length| length.to_pixels(rem_size)),
+                    corner_radii: self
+                        .corner_radii
+                        .map(|length| length.to_pixels(rem_size).scale(scale)),
+                    border_widths: self
+                        .border_widths
+                        .map(|length| length.to_pixels(rem_size).scale(scale)),
                 },
             );
         }

crates/gpui3/src/text_system.rs 🔗

@@ -217,8 +217,8 @@ impl TextSystem {
 
     pub fn rasterize_glyph(
         &self,
-        glyph_id: &RasterizedGlyphId,
-    ) -> Result<(Bounds<DevicePixels>, Vec<u8>)> {
+        glyph_id: &RasterizeGlyphParams,
+    ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
         self.platform_text_system.rasterize_glyph(glyph_id)
     }
 }
@@ -380,7 +380,7 @@ pub struct ShapedGlyph {
 }
 
 #[derive(Clone, Debug, PartialEq)]
-pub struct RasterizedGlyphId {
+pub struct RasterizeGlyphParams {
     pub(crate) font_id: FontId,
     pub(crate) glyph_id: GlyphId,
     pub(crate) font_size: Pixels,
@@ -388,9 +388,9 @@ pub struct RasterizedGlyphId {
     pub(crate) scale_factor: f32,
 }
 
-impl Eq for RasterizedGlyphId {}
+impl Eq for RasterizeGlyphParams {}
 
-impl Hash for RasterizedGlyphId {
+impl Hash for RasterizeGlyphParams {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.font_id.0.hash(state);
         self.glyph_id.0.hash(state);

crates/gpui3/src/text_system/line.rs 🔗

@@ -157,35 +157,29 @@ impl Line {
 
                     if let Some((_underline_origin, _underline_style)) = finished_underline {
                         todo!()
-                        // cx.scene().insert(Underline {
-                        //     origin: underline_origin,
-                        //     width: glyph_origin.x - underline_origin.x,
-                        //     thickness: underline_style.thickness.into(),
-                        //     color: underline_style.color.unwrap(),
-                        //     squiggly: underline_style.squiggly,
-                        // });
                     }
 
                     if glyph.is_emoji {
                         todo!()
-                        // cx.scene().push_image_glyph(scene::ImageGlyph {
-                        //     font_id: run.font_id,
-                        //     font_size: self.layout.font_size,
-                        //     id: glyph.id,
-                        //     origin: glyph_origin,
-                        // });
                     } else {
-                        if let Some((tile, bounds)) = cx
+                        if let Some(tile) = cx
                             .rasterize_glyph(
                                 run.font_id,
                                 glyph.id,
                                 self.layout.font_size,
-                                cx.scale_factor(),
                                 glyph_origin,
+                                cx.scale_factor(),
                             )
                             .log_err()
                         {
                             let layer_id = cx.current_layer_id();
+
+                            let bounds = Bounds {
+                                origin: glyph_origin + todo!(),
+                                size: todo!(),
+                            };
+                            // cx.text_system().raster_bounds()
+
                             cx.scene().insert(
                                 layer_id,
                                 MonochromeSprite {

crates/gpui3/src/window.rs 🔗

@@ -1,9 +1,8 @@
 use crate::{
-    px, AnyView, AppContext, AtlasTile, AvailableSpace, Bounds, Context, DevicePixels, Effect,
-    Element, EntityId, FontId, GlyphId, Handle, LayoutId, MainThread, MainThreadOnly,
-    MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, RasterizedGlyphId, Reference,
-    Scene, Size, StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions,
-    SUBPIXEL_VARIANTS,
+    px, AnyView, AppContext, AtlasTile, AvailableSpace, Bounds, Context, Effect, Element, EntityId,
+    FontId, GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, Pixels, PlatformAtlas,
+    PlatformWindow, Point, RasterizeGlyphParams, Reference, Scene, Size, StackContext,
+    StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
 };
 use anyhow::Result;
 use futures::Future;
@@ -16,7 +15,7 @@ pub struct AnyWindow {}
 pub struct Window {
     handle: AnyWindowHandle,
     platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
-    glyph_atlas: Arc<dyn PlatformAtlas<RasterizedGlyphId>>,
+    glyph_atlas: Arc<dyn PlatformAtlas<RasterizeGlyphParams>>,
     rem_size: Pixels,
     content_size: Size<Pixels>,
     layout_engine: TaffyLayoutEngine,
@@ -171,39 +170,26 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         font_id: FontId,
         glyph_id: GlyphId,
         font_size: Pixels,
-        scale_factor: f32,
         target_position: Point<Pixels>,
-    ) -> Result<(AtlasTile, Bounds<Pixels>)> {
+        scale_factor: f32,
+    ) -> Result<AtlasTile> {
         let target_position = target_position * scale_factor;
         let subpixel_variant = Point {
             x: (target_position.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
             y: (target_position.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
         };
-        let rasterized_glyph_id = RasterizedGlyphId {
+        let rasterized_glyph_id = RasterizeGlyphParams {
             font_id,
             glyph_id,
             font_size,
             subpixel_variant,
             scale_factor,
         };
-        let mut offset = Default::default();
-        let tile = self
-            .window
+        self.window
             .glyph_atlas
             .get_or_insert_with(&rasterized_glyph_id, &mut || {
-                let (bounds, pixels) = self.text_system().rasterize_glyph(&rasterized_glyph_id)?;
-                offset = bounds.origin;
-                Ok((bounds.size, pixels))
-            })?;
-
-        // Align bounding box surrounding glyph to pixel grid
-        let mut origin = (target_position * scale_factor).map(|p| p.floor());
-        // Position glyph within bounding box
-        origin += offset.map(|o| px(u32::from(o) as f32));
-        let size = tile.bounds.size.map(|b| px(b.0 as f32));
-        let bounds = Bounds { origin, size };
-
-        Ok((tile, bounds))
+                self.text_system().rasterize_glyph(&rasterized_glyph_id)
+            })
     }
 
     pub(crate) fn draw(&mut self) -> Result<()> {

crates/storybook2/src/workspace.rs 🔗

@@ -4,7 +4,7 @@ use crate::{
     themes::rose_pine_dawn,
 };
 use gpui3::{
-    div, img, svg, view, Context, Element, ParentElement, RootView, StyleHelpers, View,
+    black, div, img, svg, view, Context, Element, ParentElement, RootView, StyleHelpers, View,
     ViewContext, WindowContext,
 };
 
@@ -29,6 +29,7 @@ impl Workspace {
         let theme = rose_pine_dawn();
         div()
             .font("Helvetica")
+            .text_color(black())
             .text_base()
             .size_full()
             .fill(theme.middle.positive.default.background)