Checkpoint

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/gpui3/src/app.rs                         |  18 -
crates/gpui3/src/color.rs                       |   2 
crates/gpui3/src/platform/mac.rs                |   1 
crates/gpui3/src/platform/mac/metal_renderer.rs |  42 ++++-
crates/gpui3/src/platform/mac/screen.rs         |   3 
crates/gpui3/src/platform/mac/shaders.metal     |  25 ++-
crates/gpui3/src/platform/mac/window.rs         | 140 ++++++++++--------
crates/gpui3/src/scene.rs                       |  16 -
crates/gpui3/src/window.rs                      |  25 +--
9 files changed, 153 insertions(+), 119 deletions(-)

Detailed changes

crates/gpui3/src/app.rs 🔗

@@ -3,7 +3,7 @@ use crate::{
     Window, WindowContext, WindowHandle, WindowId,
 };
 use anyhow::{anyhow, Result};
-use futures::Future;
+use futures::{future, Future};
 use parking_lot::Mutex;
 use slotmap::SlotMap;
 use std::{
@@ -77,7 +77,7 @@ impl AppContext {
         f: impl FnOnce(&dyn Platform, &mut Self) -> F + Send + 'static,
     ) -> impl Future<Output = R>
     where
-        F: Future<Output = R> + Send + 'static,
+        F: Future<Output = R> + 'static,
         R: Send + 'static,
     {
         let this = self.this.upgrade().unwrap();
@@ -94,19 +94,13 @@ impl AppContext {
     ) -> impl Future<Output = WindowHandle<S>> {
         let id = self.windows.insert(None);
         let handle = WindowHandle::new(id);
-        let window =
-            self.spawn_on_main(move |platform, _cx| Window::new(handle.into(), options, platform));
-
-        let this = self.this.upgrade().unwrap();
-        async move {
-            let mut window = window.await;
-            let cx = &mut *this.lock();
+        self.spawn_on_main(move |platform, cx| {
+            let mut window = Window::new(handle.into(), options, platform);
             let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
             window.root_view.replace(Box::new(root_view));
-
             cx.windows.get_mut(id).unwrap().replace(window);
-            handle
-        }
+            future::ready(handle)
+        })
     }
 
     pub(crate) fn update_window<R>(

crates/gpui3/src/color.rs 🔗

@@ -5,7 +5,7 @@ use serde::de::{self, Deserialize, Deserializer, Visitor};
 use std::fmt;
 use std::num::ParseIntError;
 
-pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
+pub fn rgb(hex: u32) -> Rgba {
     let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
     let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
     let b = (hex & 0xFF) as f32 / 255.0;

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

@@ -16,6 +16,7 @@ use cocoa::{
     base::{id, nil},
     foundation::{NSAutoreleasePool, NSNotFound, NSRect, NSSize, NSString, NSUInteger, NSURL},
 };
+use metal_renderer::*;
 use objc::{
     msg_send,
     runtime::{BOOL, NO, YES},

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

@@ -12,7 +12,7 @@ use std::{ffi::c_void, mem, ptr};
 const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
 const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
 
-pub struct Renderer {
+pub struct MetalRenderer {
     layer: metal::MetalLayer,
     command_queue: CommandQueue,
     quad_pipeline_state: metal::RenderPipelineState,
@@ -20,7 +20,7 @@ pub struct Renderer {
     instances: metal::Buffer,
 }
 
-impl Renderer {
+impl MetalRenderer {
     pub fn new(is_opaque: bool) -> Self {
         const PIXEL_FORMAT: MTLPixelFormat = MTLPixelFormat::BGRA8Unorm;
 
@@ -50,10 +50,26 @@ impl Renderer {
             .new_library_with_data(SHADERS_METALLIB)
             .expect("error building metal library");
 
-        let unit_vertices = [point(1., 1.), point(1., 0.), point(0., 0.), point(0., 1.)];
+        fn to_float2_bits(point: crate::PointF) -> u64 {
+            unsafe {
+                let mut output = mem::transmute::<_, u32>(point.y.to_bits()) as u64;
+                output <<= 32;
+                output |= mem::transmute::<_, u32>(point.x.to_bits()) as u64;
+                output
+            }
+        }
+
+        let unit_vertices = [
+            to_float2_bits(point(0., 0.)),
+            to_float2_bits(point(1., 0.)),
+            to_float2_bits(point(0., 1.)),
+            to_float2_bits(point(0., 1.)),
+            to_float2_bits(point(1., 0.)),
+            to_float2_bits(point(1., 1.)),
+        ];
         let unit_vertices = device.new_buffer_with_data(
             unit_vertices.as_ptr() as *const c_void,
-            (unit_vertices.len() * mem::size_of::<PointF>()) as u64,
+            (unit_vertices.len() * mem::size_of::<u64>()) as u64,
             MTLResourceOptions::StorageModeManaged,
         );
         let instances = device.new_buffer(
@@ -79,6 +95,10 @@ impl Renderer {
         }
     }
 
+    pub fn layer(&self) -> &metal::MetalLayerRef {
+        &*self.layer
+    }
+
     pub fn draw(&mut self, scene: &Scene, scale_factor: f32) {
         let layer = self.layer.clone();
         let viewport_size = layer.drawable_size();
@@ -125,11 +145,13 @@ impl Renderer {
             scene.max_order(),
             command_encoder,
         );
+        command_encoder.end_encoding();
 
         self.instances.did_modify_range(NSRange {
             location: 0,
             length: buffer_offset as NSUInteger,
         });
+
         command_buffer.commit();
         command_buffer.wait_until_completed();
         drawable.present();
@@ -165,6 +187,7 @@ impl Renderer {
             scale_factor,
             max_order,
         };
+
         let quad_uniform_bytes = bytemuck::bytes_of(&quad_uniforms);
         command_encoder.set_vertex_bytes(
             QuadInputIndex::Uniforms as u64,
@@ -184,10 +207,11 @@ impl Renderer {
             "instance buffer exhausted"
         );
 
+        dbg!(quads.len());
         command_encoder.draw_primitives_instanced(
-            metal::MTLPrimitiveType::TriangleStrip,
+            metal::MTLPrimitiveType::Triangle,
             0,
-            4,
+            6,
             quads.len() as u64,
         );
         *offset = next_offset;
@@ -235,9 +259,9 @@ fn align_offset(offset: &mut usize) {
 
 #[repr(C)]
 enum QuadInputIndex {
-    Vertices,
-    Quads,
-    Uniforms,
+    Vertices = 0,
+    Quads = 1,
+    Uniforms = 2,
 }
 
 #[derive(Debug, Clone, Copy, Zeroable, Pod)]

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

@@ -1,7 +1,6 @@
 use super::ns_string;
 use crate::{
-    platform, point, px, size, Bounds, MainThreadOnly, Pixels, PlatformScreen,
-    PlatformScreenHandle, ScreenId,
+    platform, point, px, size, Bounds, Pixels, PlatformScreen, PlatformScreenHandle, ScreenId,
 };
 use cocoa::{
     appkit::NSScreen,

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

@@ -8,6 +8,8 @@ float4 to_device_position(float2 pixel_position, uint order, uint max_order, flo
 
 struct QuadVertexOutput {
     float4 position [[position]];
+    float4 background_color;
+    float4 border_color;
     uint quad_id;
 };
 
@@ -23,7 +25,14 @@ vertex QuadVertexOutput quad_vertex(
     float2 position_2d = unit_vertex * float2(quad.bounds.size.width, quad.bounds.size.height) + float2(quad.bounds.origin.x, quad.bounds.origin.y);
     float2 viewport_size = float2(uniforms->viewport_size.width, uniforms->viewport_size.height);
     float4 device_position = to_device_position(position_2d, quad.order, uniforms->max_order, viewport_size);
-    return QuadVertexOutput { device_position, quad_id };
+    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
+    };
 }
 
 fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]], constant Quad *quads [[buffer(QuadInputIndex_Quads)]]) {
@@ -64,26 +73,26 @@ fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]], constant Quad
 
     float4 color;
     if (border_width == 0.) {
-        color = float4(quad.background.h, quad.background.s, quad.background.l, quad.background.a);
+        color = input.background_color;
     } else {
         float inset_distance = distance + border_width;
 
         // Decrease border's opacity as we move inside the background.
-        quad.border_color.a *= 1. - saturate(0.5 - inset_distance);
+        input.border_color.a *= 1. - saturate(0.5 - inset_distance);
 
         // Alpha-blend the border and the background.
         float output_alpha = quad.border_color.a + quad.background.a * (1. - quad.border_color.a);
-        float3 premultiplied_border_rgb = float3(quad.border_color.h, quad.border_color.s, quad.border_color.l) * quad.border_color.a;
-        float3 premultiplied_background_rgb = float3(quad.background.h, quad.background.s, quad.background.l) * quad.background.a;
-        float3 premultiplied_output_rgb = premultiplied_border_rgb + premultiplied_background_rgb * (1. - quad.border_color.a);
-        color = float4(premultiplied_output_rgb.x, premultiplied_output_rgb.y, premultiplied_output_rgb.z, output_alpha);
+        float3 premultiplied_border_rgb = input.border_color.rgb * quad.border_color.a;
+        float3 premultiplied_background_rgb = input.background_color.rgb * input.background_color.a;
+        float3 premultiplied_output_rgb = premultiplied_border_rgb + premultiplied_background_rgb * (1. - input.border_color.a);
+        color = float4(premultiplied_output_rgb, output_alpha);
     }
 
     return color;
 }
 
 float4 hsla_to_rgba(Hsla hsla) {
-    float h = hsla.h;
+    float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range
     float s = hsla.s;
     float l = hsla.l;
     float a = hsla.a;

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

@@ -1,3 +1,4 @@
+use super::{ns_string, MetalRenderer, NSRange};
 use crate::{
     point, px, size, AnyWindowHandle, Bounds, Event, InputHandler, KeyDownEvent, Keystroke,
     MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent,
@@ -17,6 +18,7 @@ use cocoa::{
 };
 use core_graphics::display::CGRect;
 use ctor::ctor;
+use foreign_types::{ForeignType, ForeignTypeRef};
 use futures::channel::oneshot;
 use objc::{
     class,
@@ -39,8 +41,6 @@ use std::{
     time::Duration,
 };
 
-use super::{ns_string, NSRange};
-
 const WINDOW_STATE_IVAR: &str = "windowState";
 
 static mut WINDOW_CLASS: *const Class = ptr::null();
@@ -134,10 +134,10 @@ unsafe fn build_classes() {
             cancel_operation as extern "C" fn(&Object, Sel, id),
         );
 
-        // decl.add_method(
-        //     sel!(makeBackingLayer),
-        //     make_backing_layer as extern "C" fn(&Object, Sel) -> id,
-        // );
+        decl.add_method(
+            sel!(makeBackingLayer),
+            make_backing_layer as extern "C" fn(&Object, Sel) -> id,
+        );
 
         decl.add_protocol(Protocol::get("CALayerDelegate").unwrap());
         decl.add_method(
@@ -277,10 +277,11 @@ struct InsertText {
     text: String,
 }
 
-struct WindowState {
+struct MacWindowState {
     handle: AnyWindowHandle,
     dispatcher: Arc<dyn PlatformDispatcher>,
     native_window: id,
+    renderer: MetalRenderer,
     kind: WindowKind,
     event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
     activate_callback: Option<Box<dyn FnMut(bool)>>,
@@ -303,7 +304,7 @@ struct WindowState {
     ime_text: Option<String>,
 }
 
-impl WindowState {
+impl MacWindowState {
     fn move_traffic_light(&self) {
         if let Some(traffic_light_position) = self.traffic_light_position {
             let titlebar_height = self.titlebar_height();
@@ -408,9 +409,9 @@ impl WindowState {
     }
 }
 
-unsafe impl Send for WindowState {}
+unsafe impl Send for MacWindowState {}
 
-pub struct MacWindow(Arc<Mutex<WindowState>>);
+pub struct MacWindow(Arc<Mutex<MacWindowState>>);
 
 impl MacWindow {
     pub fn open(handle: AnyWindowHandle, options: WindowOptions, platform: &dyn Platform) -> Self {
@@ -475,18 +476,19 @@ impl MacWindow {
 
             assert!(!native_view.is_null());
 
-            let window = Self(Arc::new(Mutex::new(WindowState {
+            let window = Self(Arc::new(Mutex::new(MacWindowState {
                 handle,
                 dispatcher: platform.dispatcher(),
                 native_window,
+                renderer: MetalRenderer::new(true),
                 kind: options.kind,
                 event_callback: None,
-                resize_callback: None,
-                should_close_callback: None,
-                close_callback: None,
                 activate_callback: None,
+                resize_callback: None,
                 fullscreen_callback: None,
                 moved_callback: None,
+                should_close_callback: None,
+                close_callback: None,
                 appearance_changed_callback: None,
                 input_handler: None,
                 pending_key_down: None,
@@ -882,9 +884,9 @@ fn get_scale_factor(native_window: id) -> f32 {
     }
 }
 
-unsafe fn get_window_state(object: &Object) -> Arc<Mutex<WindowState>> {
+unsafe fn get_window_state(object: &Object) -> Arc<Mutex<MacWindowState>> {
     let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
-    let rc1 = Arc::from_raw(raw as *mut Mutex<WindowState>);
+    let rc1 = Arc::from_raw(raw as *mut Mutex<MacWindowState>);
     let rc2 = rc1.clone();
     mem::forget(rc1);
     rc2
@@ -892,7 +894,7 @@ unsafe fn get_window_state(object: &Object) -> Arc<Mutex<WindowState>> {
 
 unsafe fn drop_window_state(object: &Object) {
     let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
-    Rc::from_raw(raw as *mut RefCell<WindowState>);
+    Rc::from_raw(raw as *mut RefCell<MacWindowState>);
 }
 
 extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
@@ -1272,33 +1274,33 @@ extern "C" fn close_window(this: &Object, _: Sel) {
     }
 }
 
-// extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
-//     let window_state = unsafe { get_window_state(this) };
-//     let window_state = window_state.as_ref().lock();
-//     window_state.renderer.layer().as_ptr() as id
-// }
+extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
+    let window_state = unsafe { get_window_state(this) };
+    let window_state = window_state.as_ref().lock();
+    window_state.renderer.layer().as_ptr() as id
+}
 
 extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
     let window_state = unsafe { get_window_state(this) };
     let mut window_state_borrow = window_state.as_ref().lock();
 
-    // unsafe {
-    //     let scale_factor = window_state_borrow.scale_factor() as f64;
-    //     let size = window_state_borrow.content_size();
-    //     let drawable_size: NSSize = NSSize {
-    //         width: f64::from(size.width) * scale_factor,
-    //         height: f64::from(size.height) * scale_factor,
-    //     };
-
-    //     // let _: () = msg_send![
-    //     //     window_state_borrow.renderer.layer(),
-    //     //     setContentsScale: scale_factor
-    //     // ];
-    //     // let _: () = msg_send![
-    //     //     window_state_borrow.renderer.layer(),
-    //     //     setDrawableSize: drawable_size
-    //     // ];
-    // }
+    unsafe {
+        let scale_factor = window_state_borrow.scale_factor() as f64;
+        let size = window_state_borrow.content_size();
+        let drawable_size: NSSize = NSSize {
+            width: f64::from(size.width) * scale_factor,
+            height: f64::from(size.height) * scale_factor,
+        };
+
+        let _: () = msg_send![
+            window_state_borrow.renderer.layer(),
+            setContentsScale: scale_factor
+        ];
+        let _: () = msg_send![
+            window_state_borrow.renderer.layer(),
+            setDrawableSize: drawable_size
+        ];
+    }
 
     if let Some(mut callback) = window_state_borrow.resize_callback.take() {
         drop(window_state_borrow);
@@ -1319,18 +1321,18 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
         let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
     }
 
-    // let scale_factor = window_state_borrow.scale_factor() as f64;
-    // let drawable_size: NSSize = NSSize {
-    //     width: size.width * scale_factor,
-    //     height: size.height * scale_factor,
-    // };
-    //
-    // unsafe {
-    //     let _: () = msg_send![
-    //         window_state_borrow.renderer.layer(),
-    //         setDrawableSize: drawable_size
-    //     ];
-    // }
+    let scale_factor = window_state_borrow.scale_factor() as f64;
+    let drawable_size: NSSize = NSSize {
+        width: size.width * scale_factor,
+        height: size.height * scale_factor,
+    };
+
+    unsafe {
+        let _: () = msg_send![
+            window_state_borrow.renderer.layer(),
+            setDrawableSize: drawable_size
+        ];
+    }
 
     drop(window_state_borrow);
     let mut window_state_borrow = window_state.lock();
@@ -1341,14 +1343,32 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
     };
 }
 
-extern "C" fn display_layer(_this: &Object, _: Sel, _: id) {
-    // unsafe {
-    // let window_state = get_window_state(this);
-    // let mut window_state = window_state.as_ref().lock();
-    // if let Some(scene) = window_state.scene_to_render.take() {
-    //     window_state.renderer.render(&scene);
-    // };
-    // }
+extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
+    unsafe {
+        let window_state = get_window_state(this);
+        let mut window_state = window_state.as_ref().lock();
+
+        let mut scene = crate::Scene::new();
+        scene.insert(crate::Quad {
+            order: 0,
+            bounds: Bounds {
+                origin: point(10., 10.).map(px),
+                size: size(100., 100.).map(px),
+            },
+            clip_bounds: Bounds {
+                origin: point(10., 10.).map(px),
+                size: size(100., 100.).map(px),
+            },
+            clip_corner_radii: Default::default(),
+            background: crate::rgb(0x00ff00).into(),
+            border_color: Default::default(),
+            corner_radii: Default::default(),
+            border_widths: Default::default(),
+        });
+        dbg!("!!!!!!!!!");
+        let scale_factor = window_state.scale_factor();
+        window_state.renderer.draw(&scene, scale_factor);
+    }
 }
 
 extern "C" fn valid_attributes_for_marked_text(_: &Object, _: Sel) -> id {
@@ -1544,7 +1564,7 @@ extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
 }
 
 async fn synthetic_drag(
-    window_state: Weak<Mutex<WindowState>>,
+    window_state: Weak<Mutex<MacWindowState>>,
     drag_id: usize,
     event: MouseMovedEvent,
 ) {

crates/gpui3/src/scene.rs 🔗

@@ -25,19 +25,13 @@ impl Scene {
         }
     }
 
-    pub fn insert(&mut self, primitive: impl Into<Primitive>, is_transparent: bool) {
+    pub fn insert(&mut self, primitive: impl Into<Primitive>) {
         let primitive = primitive.into();
         self.max_order = cmp::max(self.max_order, primitive.order());
-        if is_transparent {
-            self.transparent_primitives.insert(primitive);
-        } else {
-            match primitive {
-                Primitive::Quad(quad) => self.opaque_primitives.quads.push(quad),
-                Primitive::Glyph(glyph) => self.opaque_primitives.glyphs.push(glyph),
-                Primitive::Underline(underline) => {
-                    self.opaque_primitives.underlines.push(underline)
-                }
-            }
+        match primitive {
+            Primitive::Quad(quad) => self.opaque_primitives.quads.push(quad),
+            Primitive::Glyph(glyph) => self.opaque_primitives.glyphs.push(glyph),
+            Primitive::Underline(underline) => self.opaque_primitives.underlines.push(underline),
         }
     }
 

crates/gpui3/src/window.rs 🔗

@@ -26,25 +26,18 @@ pub struct Window {
 }
 
 impl Window {
-    pub fn new(
-        handle: AnyWindowHandle,
-        options: WindowOptions,
-        platform: &dyn Platform,
-    ) -> impl Future<Output = Window> + 'static {
+    pub fn new(handle: AnyWindowHandle, options: WindowOptions, platform: &dyn Platform) -> Self {
         let platform_window = platform.open_window(handle, options);
         let mouse_position = platform_window.mouse_position();
         let platform_window = MainThreadOnly::new(Arc::new(platform_window), platform.dispatcher());
-
-        async move {
-            Window {
-                handle,
-                platform_window,
-                rem_size: px(16.),
-                layout_engine: TaffyLayoutEngine::new(),
-                text_style_stack: Vec::new(),
-                root_view: None,
-                mouse_position,
-            }
+        Window {
+            handle,
+            platform_window,
+            rem_size: px(16.),
+            layout_engine: TaffyLayoutEngine::new(),
+            text_style_stack: Vec::new(),
+            root_view: None,
+            mouse_position,
         }
     }
 }