Checkpoint

Nathan Sobo created

Change summary

crates/gpui3/build.rs                           |   5 
crates/gpui3/src/platform.rs                    |   4 
crates/gpui3/src/platform/mac/metal_atlas.rs    |  19 -
crates/gpui3/src/platform/mac/metal_renderer.rs |  20 +-
crates/gpui3/src/platform/mac/shaders.metal     | 170 ++++++++++++------
crates/gpui3/src/platform/mac/window.rs         |  11 
crates/gpui3/src/scene.rs                       |   2 
crates/gpui3/src/text_system.rs                 |   4 
crates/gpui3/src/text_system/line.rs            |  16 -
crates/gpui3/src/window.rs                      |   2 
10 files changed, 151 insertions(+), 102 deletions(-)

Detailed changes

crates/gpui3/build.rs 🔗

@@ -45,11 +45,12 @@ fn generate_shader_bindings() -> PathBuf {
         "Pixels".into(),
         "PointF".into(),
         "Hsla".into(),
+        "Uniforms".into(),
+        "AtlasTile".into(),
         "Quad".into(),
         "QuadInputIndex".into(),
-        "QuadUniforms".into(),
-        "AtlasTile".into(),
         "MonochromeSprite".into(),
+        "MonochromeSpriteInputIndex".into(),
     ]);
     config.no_includes = true;
     config.enumeration.prefix_with_name = true;

crates/gpui3/src/platform.rs 🔗

@@ -192,12 +192,12 @@ pub trait PlatformAtlas<Key>: Send + Sync {
 pub struct AtlasTile {
     pub(crate) texture_id: AtlasTextureId,
     pub(crate) tile_id: TileId,
-    pub(crate) bounds_in_atlas: Bounds<DevicePixels>,
+    pub(crate) bounds: Bounds<DevicePixels>,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 #[repr(C)]
-pub(crate) struct AtlasTextureId(pub(crate) usize);
+pub(crate) struct AtlasTextureId(pub(crate) u32);
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 #[repr(C)]

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

@@ -98,7 +98,7 @@ impl<Key> MetalAtlasState<Key> {
         }
 
         let atlas_texture = MetalAtlasTexture {
-            id: AtlasTextureId(self.textures.len()),
+            id: AtlasTextureId(self.textures.len() as u32),
             allocator: etagere::BucketedAtlasAllocator::new(size.into()),
             metal_texture: AssertSend(metal_texture),
         };
@@ -120,24 +120,19 @@ impl MetalAtlasTexture {
         let tile = AtlasTile {
             texture_id: self.id,
             tile_id: allocation.id.into(),
-            bounds_in_atlas: allocation.rectangle.into(),
+            bounds: allocation.rectangle.into(),
         };
         let region = metal::MTLRegion::new_2d(
-            u32::from(tile.bounds_in_atlas.origin.x) as u64,
-            u32::from(tile.bounds_in_atlas.origin.y) as u64,
-            u32::from(tile.bounds_in_atlas.size.width) as u64,
-            u32::from(tile.bounds_in_atlas.size.height) as u64,
+            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,
         );
         self.metal_texture.replace_region(
             region,
             0,
             bytes.as_ptr() as *const _,
-            u32::from(
-                tile.bounds_in_atlas
-                    .size
-                    .width
-                    .to_bytes(self.bytes_per_pixel()),
-            ) as u64,
+            u32::from(tile.bounds.size.width.to_bytes(self.bytes_per_pixel())) as u64,
         );
         Some(tile)
     }

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

@@ -242,12 +242,11 @@ impl MetalRenderer {
             Some(&self.instances),
             *offset as u64,
         );
-        let quad_uniforms = QuadUniforms { viewport_size };
 
         command_encoder.set_vertex_bytes(
-            QuadInputIndex::Uniforms as u64,
-            mem::size_of_val(&quad_uniforms) as u64,
-            &quad_uniforms as *const QuadUniforms as *const _,
+            QuadInputIndex::ViewportSize as u64,
+            mem::size_of_val(&viewport_size) as u64,
+            &viewport_size as *const Size<DevicePixels> as *const _,
         );
 
         let quad_bytes_len = mem::size_of::<Quad>() * quads.len();
@@ -279,7 +278,7 @@ impl MetalRenderer {
         viewport_size: Size<DevicePixels>,
         command_encoder: &metal::RenderCommandEncoderRef,
     ) {
-        todo!()
+        // todo!()
     }
 }
 
@@ -327,11 +326,14 @@ fn align_offset(offset: &mut usize) {
 enum QuadInputIndex {
     Vertices = 0,
     Quads = 1,
-    Uniforms = 2,
+    ViewportSize = 2,
 }
 
-#[derive(Debug, Clone, Copy)]
 #[repr(C)]
-pub(crate) struct QuadUniforms {
-    viewport_size: Size<DevicePixels>,
+enum MonochromeSpriteInputIndex {
+    Vertices = 0,
+    Sprites = 1,
+    ViewportSize = 2,
+    AtlasSize = 3,
+    AtlasTexture = 4,
 }

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

@@ -4,71 +4,36 @@
 using namespace metal;
 
 float4 hsla_to_rgba(Hsla hsla);
-float4 to_device_position(float2 pixel_position, float2 viewport_size);
+float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds,
+                          Bounds_Pixels clip_bounds,
+                          constant Size_DevicePixels *viewport_size);
+float quad_sdf(float2 point, Bounds_Pixels bounds, Corners_Pixels corner_radii);
 
 struct QuadVertexOutput {
   float4 position [[position]];
-  float4 background_color;
-  float4 border_color;
-  uint quad_id;
+  float4 background_color [[flat]];
+  float4 border_color [[flat]];
+  uint quad_id [[flat]];
 };
 
-vertex QuadVertexOutput quad_vertex(
-    uint unit_vertex_id [[vertex_id]], uint quad_id [[instance_id]],
-    constant float2 *unit_vertices [[buffer(QuadInputIndex_Vertices)]],
-    constant Quad *quads [[buffer(QuadInputIndex_Quads)]],
-    constant QuadUniforms *uniforms [[buffer(QuadInputIndex_Uniforms)]]) {
+vertex QuadVertexOutput quad_vertex(uint unit_vertex_id [[vertex_id]],
+                                    uint quad_id [[instance_id]],
+                                    constant float2 *unit_vertices
+                                    [[buffer(QuadInputIndex_Vertices)]],
+                                    constant Quad *quads
+                                    [[buffer(QuadInputIndex_Quads)]],
+                                    constant Size_DevicePixels *viewport_size
+                                    [[buffer(QuadInputIndex_ViewportSize)]]) {
   float2 unit_vertex = unit_vertices[unit_vertex_id];
   Quad quad = quads[quad_id];
-  float2 position_2d =
-      unit_vertex * float2(quad.bounds.size.width, quad.bounds.size.height) +
-      float2(quad.bounds.origin.x, quad.bounds.origin.y);
-  position_2d.x = max(quad.clip_bounds.origin.x, position_2d.x);
-  position_2d.x = min(quad.clip_bounds.origin.x + quad.clip_bounds.size.width,
-                      position_2d.x);
-  position_2d.y = max(quad.clip_bounds.origin.y, position_2d.y);
-  position_2d.y = min(quad.clip_bounds.origin.y + quad.clip_bounds.size.height,
-                      position_2d.y);
-
-  float2 viewport_size = float2((float)uniforms->viewport_size.width,
-                                (float)uniforms->viewport_size.height);
-  float4 device_position = to_device_position(position_2d, viewport_size);
+  float4 device_position = to_device_position(unit_vertex, quad.bounds,
+                                              quad.clip_bounds, viewport_size);
   float4 background_color = hsla_to_rgba(quad.background);
   float4 border_color = hsla_to_rgba(quad.border_color);
   return QuadVertexOutput{device_position, background_color, border_color,
                           quad_id};
 }
 
-float quad_sdf(float2 point, Bounds_Pixels bounds,
-               Corners_Pixels corner_radii) {
-  float2 half_size = float2(bounds.size.width, bounds.size.height) / 2.;
-  float2 center = float2(bounds.origin.x, bounds.origin.y) + half_size;
-  float2 center_to_point = point - center;
-  float corner_radius;
-  if (center_to_point.x < 0.) {
-    if (center_to_point.y < 0.) {
-      corner_radius = corner_radii.top_left;
-    } else {
-      corner_radius = corner_radii.bottom_left;
-    }
-  } else {
-    if (center_to_point.y < 0.) {
-      corner_radius = corner_radii.top_right;
-    } else {
-      corner_radius = corner_radii.bottom_right;
-    }
-  }
-
-  float2 rounded_edge_to_point =
-      abs(center_to_point) - half_size + corner_radius;
-  float distance =
-      length(max(0., rounded_edge_to_point)) +
-      min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) -
-      corner_radius;
-
-  return distance;
-}
-
 fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]],
                               constant Quad *quads
                               [[buffer(QuadInputIndex_Quads)]]) {
@@ -145,6 +110,57 @@ fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]],
                 saturate(0.5 - distance) * saturate(0.5 - clip_distance));
 }
 
+struct MonochromeSpriteVertexOutput {
+  float4 position [[position]];
+  float2 tile_position;
+  float4 color [[flat]];
+  uint sprite_id [[flat]];
+};
+
+vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
+    uint unit_vertex_id [[vertex_id]], uint sprite_id [[instance_id]],
+    constant float2 *unit_vertices
+    [[buffer(MonochromeSpriteInputIndex_Vertices)]],
+    constant MonochromeSprite *sprites
+    [[buffer(MonochromeSpriteInputIndex_Sprites)]],
+    constant Size_DevicePixels *viewport_size
+    [[buffer(MonochromeSpriteInputIndex_ViewportSize)]],
+    constant Size_DevicePixels *atlas_size
+    [[buffer(MonochromeSpriteInputIndex_AtlasSize)]]) {
+
+  float2 unit_vertex = unit_vertices[unit_vertex_id];
+  MonochromeSprite sprite = sprites[sprite_id];
+  float4 device_position = to_device_position(
+      unit_vertex, sprite.bounds, sprite.clip_bounds, viewport_size);
+
+  float2 tile_origin =
+      float2(sprite.tile.bounds.origin.x, sprite.tile.bounds.origin.y);
+  float2 tile_size =
+      float2(sprite.tile.bounds.size.width, sprite.tile.bounds.size.height);
+  float2 tile_position =
+      (tile_origin + unit_vertex * tile_size) /
+      float2((float)atlas_size->width, (float)atlas_size->height);
+  float4 color = hsla_to_rgba(sprite.color);
+  return MonochromeSpriteVertexOutput{device_position, tile_position, color,
+                                      sprite_id};
+}
+
+fragment float4 monochrome_sprite_fragment(
+    MonochromeSpriteVertexOutput input [[stage_in]],
+    constant MonochromeSprite *sprites
+    [[buffer(MonochromeSpriteInputIndex_Sprites)]],
+    texture2d<float> atlas
+    [[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);
+  float clip_distance =
+      quad_sdf(input.position.xy, sprite.clip_bounds, sprite.clip_corner_radii);
+  float4 color = input.color;
+  color.a *= sample.a * saturate(0.5 - clip_distance);
+  return color;
+}
+
 float4 hsla_to_rgba(Hsla hsla) {
   float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range
   float s = hsla.s;
@@ -193,10 +209,52 @@ float4 hsla_to_rgba(Hsla hsla) {
   return rgba;
 }
 
-float4 to_device_position(float2 pixel_position, float2 viewport_size) {
-  return float4(pixel_position / viewport_size * float2(2., -2.) +
-                    float2(-1., 1.),
-                0., 1.);
+float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds,
+                          Bounds_Pixels clip_bounds,
+                          constant Size_DevicePixels *input_viewport_size) {
+  float2 position =
+      unit_vertex * float2(bounds.size.width, bounds.size.height) +
+      float2(bounds.origin.x, bounds.origin.y);
+  position.x = max(clip_bounds.origin.x, position.x);
+  position.x = min(clip_bounds.origin.x + clip_bounds.size.width, position.x);
+  position.y = max(clip_bounds.origin.y, position.y);
+  position.y = min(clip_bounds.origin.y + clip_bounds.size.height, position.y);
+
+  float2 viewport_size = float2((float)input_viewport_size->width,
+                                (float)input_viewport_size->height);
+  float2 device_position =
+      position / viewport_size * float2(2., -2.) + float2(-1., 1.);
+  return float4(device_position, 0., 1.);
+}
+
+float quad_sdf(float2 point, Bounds_Pixels bounds,
+               Corners_Pixels corner_radii) {
+  float2 half_size = float2(bounds.size.width, bounds.size.height) / 2.;
+  float2 center = float2(bounds.origin.x, bounds.origin.y) + half_size;
+  float2 center_to_point = point - center;
+  float corner_radius;
+  if (center_to_point.x < 0.) {
+    if (center_to_point.y < 0.) {
+      corner_radius = corner_radii.top_left;
+    } else {
+      corner_radius = corner_radii.bottom_left;
+    }
+  } else {
+    if (center_to_point.y < 0.) {
+      corner_radius = corner_radii.top_right;
+    } else {
+      corner_radius = corner_radii.bottom_right;
+    }
+  }
+
+  float2 rounded_edge_to_point =
+      abs(center_to_point) - half_size + corner_radius;
+  float distance =
+      length(max(0., rounded_edge_to_point)) +
+      min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) -
+      corner_radius;
+
+  return distance;
 }
 
 // struct SpriteFragmentInput {

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

@@ -1,10 +1,10 @@
 use super::{ns_string, MetalRenderer, NSRange};
 use crate::{
-    point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen,
-    MetalAtlas, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent,
-    MouseUpEvent, NSRectExt, Pixels, Platform, PlatformAtlas, PlatformDispatcher,
-    PlatformInputHandler, PlatformScreen, PlatformWindow, Point, RasterizedGlyphId, Scene, Size,
-    Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
+    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,
 };
 use block::ConcreteBlock;
 use cocoa::{
@@ -20,7 +20,6 @@ use core_graphics::display::CGRect;
 use ctor::ctor;
 use foreign_types::ForeignTypeRef;
 use futures::channel::oneshot;
-use metal::{MTLPixelFormat, TextureDescriptor};
 use objc::{
     class,
     declare::ClassDecl,

crates/gpui3/src/scene.rs 🔗

@@ -243,6 +243,8 @@ impl From<Quad> for Primitive {
 pub struct MonochromeSprite {
     pub order: u32,
     pub bounds: Bounds<Pixels>,
+    pub clip_bounds: Bounds<Pixels>,
+    pub clip_corner_radii: Corners<Pixels>,
     pub color: Hsla,
     pub tile: AtlasTile,
 }

crates/gpui3/src/text_system.rs 🔗

@@ -10,8 +10,8 @@ use line_wrapper::*;
 pub use text_layout_cache::*;
 
 use crate::{
-    px, Bounds, DevicePixels, Hsla, Pixels, PlatformTextSystem, Point, RasterizationOptions,
-    Result, SharedString, Size, UnderlineStyle,
+    px, Bounds, DevicePixels, Hsla, Pixels, PlatformTextSystem, Point, Result, SharedString, Size,
+    UnderlineStyle,
 };
 use collections::HashMap;
 use core::fmt;

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

@@ -1,7 +1,6 @@
 use crate::{
-    black, point, px, Bounds, FontId, Hsla, Layout, MonochromeSprite, Pixels, Point,
-    RasterizedGlyphId, RunStyle, ShapedBoundary, ShapedLine, ShapedRun, UnderlineStyle,
-    WindowContext,
+    black, point, px, Bounds, Corners, FontId, Hsla, Layout, MonochromeSprite, Pixels, Point,
+    RunStyle, ShapedBoundary, ShapedLine, ShapedRun, UnderlineStyle, WindowContext,
 };
 use anyhow::Result;
 use smallvec::SmallVec;
@@ -192,20 +191,13 @@ impl Line {
                                 MonochromeSprite {
                                     order: layout.order,
                                     bounds,
+                                    clip_bounds: bounds,
+                                    clip_corner_radii: Corners::default(),
                                     color,
                                     tile,
                                 },
                             );
                         }
-
-                        // cx.scene().insert(Symbol {
-                        //     order: layout.order,
-                        //     origin,
-                        //     font_id: run.font_id,
-                        //     font_size: self.layout.font_size,
-                        //     id: glyph.id,
-                        //     color,
-                        // });
                     }
                 }
 

crates/gpui3/src/window.rs 🔗

@@ -200,7 +200,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         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_in_atlas.size.map(|b| px(b.0 as f32));
+        let size = tile.bounds.size.map(|b| px(b.0 as f32));
         let bounds = Bounds { origin, size };
 
         Ok((tile, bounds))