Get basic graphics rendering via Metal

Nathan Sobo created

Also, handle window resize.

Change summary

.gitignore                                  |   3 
gpui/src/app.rs                             |  30 ++
gpui/src/platform/mac/app.rs                |   4 
gpui/src/platform/mac/renderer.rs           | 153 +++++++++++++++++
gpui/src/platform/mac/shaders/shaders.h     |   2 
gpui/src/platform/mac/shaders/shaders.metal |  13 +
gpui/src/platform/mac/window.rs             | 194 ++++++++++++----------
gpui/src/platform/mod.rs                    |  11 
gpui/src/scene.rs                           |  26 ++
9 files changed, 322 insertions(+), 114 deletions(-)

Detailed changes

.gitignore 🔗

@@ -1,2 +1,3 @@
 /target
-.DS_Store
+/zed.xcworkspace
+.DS_Store

gpui/src/app.rs 🔗

@@ -13,7 +13,6 @@ use pathfinder_geometry::{rect::RectF, vector::vec2f};
 use smol::{channel, prelude::*};
 use std::{
     any::{type_name, Any, TypeId},
-    borrow,
     cell::RefCell,
     collections::{HashMap, HashSet, VecDeque},
     fmt::{self, Debug},
@@ -292,6 +291,7 @@ type ActionCallback =
 type GlobalActionCallback = dyn FnMut(&dyn Any, &mut MutableAppContext);
 
 pub struct MutableAppContext {
+    weak_self: Option<rc::Weak<RefCell<Self>>>,
     platform: Arc<dyn platform::App>,
     fonts: Arc<FontCache>,
     assets: Arc<AssetCache>,
@@ -302,7 +302,6 @@ pub struct MutableAppContext {
     next_entity_id: usize,
     next_window_id: usize,
     next_task_id: usize,
-    weak_self: Option<rc::Weak<RefCell<Self>>>,
     subscriptions: HashMap<usize, Vec<Subscription>>,
     observations: HashMap<usize, Vec<Observation>>,
     window_invalidations: HashMap<usize, WindowInvalidation>,
@@ -325,6 +324,7 @@ impl MutableAppContext {
         asset_source: impl AssetSource,
     ) -> Self {
         Self {
+            weak_self: None,
             platform,
             fonts: Arc::new(FontCache::new()),
             assets: Arc::new(AssetCache::new(asset_source)),
@@ -339,7 +339,6 @@ impl MutableAppContext {
             next_entity_id: 0,
             next_window_id: 0,
             next_task_id: 0,
-            weak_self: None,
             subscriptions: HashMap::new(),
             observations: HashMap::new(),
             window_invalidations: HashMap::new(),
@@ -355,6 +354,10 @@ impl MutableAppContext {
         }
     }
 
+    pub fn upgrade(&self) -> App {
+        App(self.weak_self.as_ref().unwrap().upgrade().unwrap())
+    }
+
     pub fn downgrade(&self) -> &AppContext {
         &self.ctx
     }
@@ -624,7 +627,7 @@ impl MutableAppContext {
             self.foreground.clone(),
         ) {
             Err(e) => log::error!("error opening window: {}", e),
-            Ok(window) => {
+            Ok(mut window) => {
                 let presenter = Rc::new(RefCell::new(Presenter::new(
                     window_id,
                     self.fonts.clone(),
@@ -632,11 +635,26 @@ impl MutableAppContext {
                     self,
                 )));
 
+                {
+                    let mut app = self.upgrade();
+                    let presenter = presenter.clone();
+                    window.on_resize(Box::new(move |window| {
+                        app.update(|ctx| {
+                            let scene = presenter.borrow_mut().build_scene(
+                                window.size(),
+                                window.scale_factor(),
+                                ctx,
+                            );
+                            window.present_scene(scene);
+                        })
+                    }));
+                }
+
                 self.on_window_invalidated(window_id, move |invalidation, ctx| {
                     let mut presenter = presenter.borrow_mut();
                     presenter.invalidate(invalidation, ctx.downgrade());
                     let scene = presenter.build_scene(window.size(), window.scale_factor(), ctx);
-                    window.render_scene(scene);
+                    window.present_scene(scene);
                 });
             }
         }
@@ -1909,7 +1927,7 @@ impl<T> Hash for ModelHandle<T> {
     }
 }
 
-impl<T> borrow::Borrow<usize> for ModelHandle<T> {
+impl<T> std::borrow::Borrow<usize> for ModelHandle<T> {
     fn borrow(&self) -> &usize {
         &self.model_id
     }

gpui/src/platform/mac/app.rs 🔗

@@ -33,7 +33,7 @@ impl platform::App for App {
         &self,
         options: platform::WindowOptions,
         executor: Rc<executor::Foreground>,
-    ) -> Result<Rc<dyn platform::Window>> {
-        Ok(Rc::new(Window::open(options, executor)?))
+    ) -> Result<Box<dyn platform::Window>> {
+        Ok(Box::new(Window::open(options, executor)?))
     }
 }

gpui/src/platform/mac/renderer.rs 🔗

@@ -1,14 +1,21 @@
-use anyhow::{anyhow, Result};
+use std::{ffi::c_void, mem};
 
-use crate::Scene;
+use self::shaders::ToUchar4;
 
 use super::window::RenderContext;
+use crate::{color::ColorU, scene::Layer, Scene};
+use anyhow::{anyhow, Result};
+use metal::{MTLResourceOptions, NSRange};
+use shaders::ToFloat2 as _;
 
 const SHADERS_METALLIB: &'static [u8] =
     include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
+const INSTANCE_BUFFER_SIZE: u64 = 1024 * 1024;
 
 pub struct Renderer {
     quad_pipeline_state: metal::RenderPipelineState,
+    quad_vertices: metal::Buffer,
+    instances: metal::Buffer,
 }
 
 impl Renderer {
@@ -17,6 +24,22 @@ impl Renderer {
             .new_library_with_data(SHADERS_METALLIB)
             .map_err(|message| anyhow!("error building metal library: {}", message))?;
 
+        let quad_vertices = [
+            (0., 0.).to_float2(),
+            (1., 0.).to_float2(),
+            (0., 1.).to_float2(),
+            (0., 1.).to_float2(),
+            (1., 0.).to_float2(),
+            (1., 1.).to_float2(),
+        ];
+        let quad_vertices = device.new_buffer_with_data(
+            quad_vertices.as_ptr() as *const c_void,
+            (quad_vertices.len() * mem::size_of::<shaders::vector_float2>()) as u64,
+            MTLResourceOptions::StorageModeManaged,
+        );
+        let instances =
+            device.new_buffer(INSTANCE_BUFFER_SIZE, MTLResourceOptions::StorageModeManaged);
+
         Ok(Self {
             quad_pipeline_state: build_pipeline_state(
                 device,
@@ -26,10 +49,78 @@ impl Renderer {
                 "quad_fragment",
                 pixel_format,
             )?,
+            quad_vertices,
+            instances,
         })
     }
 
-    pub fn render(&mut self, scene: &Scene, ctx: RenderContext) {}
+    pub fn render(&mut self, scene: &Scene, ctx: &RenderContext) {
+        ctx.command_encoder.set_viewport(metal::MTLViewport {
+            originX: 0.0,
+            originY: 0.0,
+            width: ctx.drawable_size.x() as f64,
+            height: ctx.drawable_size.y() as f64,
+            znear: 0.0,
+            zfar: 1.0,
+        });
+
+        for layer in scene.layers() {
+            self.render_quads(layer, ctx);
+        }
+    }
+
+    fn render_quads(&mut self, layer: &Layer, ctx: &RenderContext) {
+        ctx.command_encoder
+            .set_render_pipeline_state(&self.quad_pipeline_state);
+        ctx.command_encoder.set_vertex_buffer(
+            shaders::GPUIQuadInputIndex_GPUIQuadInputIndexVertices as u64,
+            Some(&self.quad_vertices),
+            0,
+        );
+        ctx.command_encoder.set_vertex_buffer(
+            shaders::GPUIQuadInputIndex_GPUIQuadInputIndexQuads as u64,
+            Some(&self.instances),
+            0,
+        );
+        ctx.command_encoder.set_vertex_bytes(
+            shaders::GPUIQuadInputIndex_GPUIQuadInputIndexUniforms as u64,
+            mem::size_of::<shaders::GPUIQuadUniforms>() as u64,
+            [shaders::GPUIQuadUniforms {
+                viewport_size: ctx.drawable_size.to_float2(),
+            }]
+            .as_ptr() as *const c_void,
+        );
+
+        let batch_size = self.instances.length() as usize / mem::size_of::<shaders::GPUIQuad>();
+
+        let buffer_contents = self.instances.contents() as *mut shaders::GPUIQuad;
+        for quad_batch in layer.quads().chunks(batch_size) {
+            for (ix, quad) in quad_batch.iter().enumerate() {
+                log::info!("render {} {:?}", ix, quad);
+                unsafe {
+                    *(buffer_contents.offset(ix as isize)) = shaders::GPUIQuad {
+                        origin: quad.bounds.origin().to_float2(),
+                        size: quad.bounds.size().to_float2(),
+                        background_color: quad
+                            .background
+                            .unwrap_or(ColorU::transparent_black())
+                            .to_uchar4(),
+                    };
+                }
+            }
+            self.instances.did_modify_range(NSRange {
+                location: 0,
+                length: (quad_batch.len() * mem::size_of::<shaders::GPUIQuad>()) as u64,
+            });
+
+            ctx.command_encoder.draw_primitives_instanced(
+                metal::MTLPrimitiveType::Triangle,
+                0,
+                6,
+                quad_batch.len() as u64,
+            );
+        }
+    }
 }
 
 fn build_pipeline_state(
@@ -47,7 +138,7 @@ fn build_pipeline_state(
         .get_function(fragment_fn_name, None)
         .map_err(|message| anyhow!("error locating fragment function: {}", message))?;
 
-    let mut descriptor = metal::RenderPipelineDescriptor::new();
+    let descriptor = metal::RenderPipelineDescriptor::new();
     descriptor.set_label(label);
     descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
     descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
@@ -61,3 +152,57 @@ fn build_pipeline_state(
         .new_render_pipeline_state(&descriptor)
         .map_err(|message| anyhow!("could not create render pipeline state: {}", message))
 }
+
+mod shaders {
+    #![allow(non_upper_case_globals)]
+    #![allow(non_camel_case_types)]
+    #![allow(non_snake_case)]
+
+    use crate::{color::ColorU, geometry::vector::Vector2F};
+    use std::mem;
+
+    include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
+
+    pub trait ToFloat2 {
+        fn to_float2(&self) -> vector_float2;
+    }
+
+    pub trait ToUchar4 {
+        fn to_uchar4(&self) -> vector_uchar4;
+    }
+
+    impl ToFloat2 for (f32, f32) {
+        fn to_float2(&self) -> vector_float2 {
+            unsafe {
+                let mut output = mem::transmute::<_, u32>(self.1.to_bits()) as vector_float2;
+                output <<= 32;
+                output |= mem::transmute::<_, u32>(self.0.to_bits()) as vector_float2;
+                output
+            }
+        }
+    }
+
+    impl ToFloat2 for Vector2F {
+        fn to_float2(&self) -> vector_float2 {
+            unsafe {
+                let mut output = mem::transmute::<_, u32>(self.y().to_bits()) as vector_float2;
+                output <<= 32;
+                output |= mem::transmute::<_, u32>(self.x().to_bits()) as vector_float2;
+                output
+            }
+        }
+    }
+
+    impl ToUchar4 for ColorU {
+        fn to_uchar4(&self) -> vector_uchar4 {
+            let mut vec = self.a as vector_uchar4;
+            vec <<= 8;
+            vec |= self.b as vector_uchar4;
+            vec <<= 8;
+            vec |= self.g as vector_uchar4;
+            vec <<= 8;
+            vec |= self.r as vector_uchar4;
+            vec
+        }
+    }
+}

gpui/src/platform/mac/shaders/shaders.h 🔗

@@ -9,7 +9,7 @@ typedef enum {
 typedef struct {
     vector_float2 origin;
     vector_float2 size;
-    vector_float4 background_color;
+    vector_uchar4 background_color;
 } GPUIQuad;
 
 typedef struct {

gpui/src/platform/mac/shaders/shaders.metal 🔗

@@ -3,6 +3,10 @@
 
 using namespace metal;
 
+float4 coloru_to_colorf(uchar4 coloru) {
+    return float4(coloru) / float4(0xff, 0xff, 0xff, 0xff);
+}
+
 struct QuadFragmentInput {
     float4 position [[position]];
     GPUIQuad quad;
@@ -17,14 +21,15 @@ vertex QuadFragmentInput quad_vertex(
 ) {
     float2 unit_vertex = unit_vertices[unit_vertex_id];
     GPUIQuad quad = quads[quad_id];
-    float4 position = float4((unit_vertex * quad.size + quad.origin) / (uniforms->viewport_size / 2.0), 0.0, 1.0);
+    float2 position = (unit_vertex * quad.size + quad.origin) / (uniforms->viewport_size / 2.0);
+    float4 device_position = float4(position * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
 
     return QuadFragmentInput {
-        position,
+        device_position,
         quad,
     };
 }
 
 fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]]) {
-    return input.quad.background_color;
-}
+    return coloru_to_colorf(input.quad.background_color);
+}

gpui/src/platform/mac/window.rs 🔗

@@ -1,7 +1,8 @@
 use crate::{
     executor,
     geometry::vector::Vector2F,
-    platform::{self, Event},
+    platform::{self, Event, WindowContext},
+    util::post_inc,
     Scene,
 };
 use anyhow::{anyhow, Result};
@@ -27,7 +28,7 @@ use objc::{
 use pathfinder_geometry::vector::vec2f;
 use smol::Timer;
 use std::{
-    cell::{Cell, RefCell},
+    cell::RefCell,
     ffi::c_void,
     mem, ptr,
     rc::Rc,
@@ -111,16 +112,16 @@ unsafe fn build_classes() {
     };
 }
 
-pub struct Window(Rc<WindowState>);
+pub struct Window(Rc<RefCell<WindowState>>);
 
 struct WindowState {
     native_window: id,
-    event_callback: RefCell<Option<Box<dyn FnMut(Event) -> bool>>>,
-    resize_callback: RefCell<Option<Box<dyn FnMut(Vector2F, f32)>>>,
-    synthetic_drag_counter: Cell<usize>,
+    event_callback: Option<Box<dyn FnMut(Event, &mut dyn platform::WindowContext)>>,
+    resize_callback: Option<Box<dyn FnMut(&mut dyn platform::WindowContext)>>,
+    synthetic_drag_counter: usize,
     executor: Rc<executor::Foreground>,
-    scene_to_render: RefCell<Option<Scene>>,
-    renderer: RefCell<Renderer>,
+    scene_to_render: Option<Scene>,
+    renderer: Renderer,
     command_queue: metal::CommandQueue,
     device: metal::Device,
     layer: id,
@@ -181,18 +182,18 @@ impl Window {
                 return Err(anyhow!("view return nil from initializer"));
             }
 
-            let window = Self(Rc::new(WindowState {
+            let window = Self(Rc::new(RefCell::new(WindowState {
                 native_window,
-                event_callback: RefCell::new(None),
-                resize_callback: RefCell::new(None),
-                synthetic_drag_counter: Cell::new(0),
+                event_callback: None,
+                resize_callback: None,
+                synthetic_drag_counter: 0,
                 executor,
                 scene_to_render: Default::default(),
-                renderer: RefCell::new(Renderer::new(&device, PIXEL_FORMAT)?),
+                renderer: Renderer::new(&device, PIXEL_FORMAT)?,
                 command_queue: device.new_command_queue(),
                 device,
                 layer,
-            }));
+            })));
 
             (*native_window).set_ivar(
                 WINDOW_STATE_IVAR,
@@ -236,46 +237,44 @@ impl Window {
 
     pub fn zoom(&self) {
         unsafe {
-            self.0.native_window.performZoom_(nil);
+            self.0.as_ref().borrow().native_window.performZoom_(nil);
         }
     }
-
-    pub fn on_event<F: 'static + FnMut(Event) -> bool>(&mut self, callback: F) {
-        *self.0.event_callback.borrow_mut() = Some(Box::new(callback));
-    }
-
-    pub fn on_resize<F: 'static + FnMut(Vector2F, f32)>(&mut self, callback: F) {
-        *self.0.resize_callback.borrow_mut() = Some(Box::new(callback));
-    }
 }
 
 impl Drop for Window {
     fn drop(&mut self) {
         unsafe {
-            self.0.native_window.close();
-            let _: () = msg_send![self.0.native_window.delegate(), release];
+            self.0.as_ref().borrow().native_window.close();
         }
     }
 }
 
 impl platform::Window for Window {
+    fn on_event(&mut self, callback: Box<dyn FnMut(Event, &mut dyn platform::WindowContext)>) {
+        self.0.as_ref().borrow_mut().event_callback = Some(callback);
+    }
+
+    fn on_resize(&mut self, callback: Box<dyn FnMut(&mut dyn platform::WindowContext)>) {
+        self.0.as_ref().borrow_mut().resize_callback = Some(callback);
+    }
+}
+
+impl platform::WindowContext for Window {
     fn size(&self) -> Vector2F {
-        self.0.size()
+        self.0.as_ref().borrow().size()
     }
 
     fn scale_factor(&self) -> f32 {
-        self.0.scale_factor()
+        self.0.as_ref().borrow().scale_factor()
     }
 
-    fn render_scene(&self, scene: Scene) {
-        *self.0.scene_to_render.borrow_mut() = Some(scene);
-        unsafe {
-            let _: () = msg_send![self.0.native_window.contentView(), setNeedsDisplay: YES];
-        }
+    fn present_scene(&mut self, scene: Scene) {
+        self.0.as_ref().borrow_mut().present_scene(scene);
     }
 }
 
-impl WindowState {
+impl platform::WindowContext for WindowState {
     fn size(&self) -> Vector2F {
         let NSSize { width, height, .. } =
             unsafe { NSView::frame(self.native_window.contentView()) }.size;
@@ -289,16 +288,17 @@ impl WindowState {
         }
     }
 
-    fn next_synthetic_drag_id(&self) -> usize {
-        let next_id = self.synthetic_drag_counter.get() + 1;
-        self.synthetic_drag_counter.set(next_id);
-        next_id
+    fn present_scene(&mut self, scene: Scene) {
+        self.scene_to_render = Some(scene);
+        unsafe {
+            let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES];
+        }
     }
 }
 
-unsafe fn window_state(object: &Object) -> Rc<WindowState> {
+unsafe fn get_window_state(object: &Object) -> Rc<RefCell<WindowState>> {
     let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
-    let rc1 = Rc::from_raw(raw as *mut WindowState);
+    let rc1 = Rc::from_raw(raw as *mut RefCell<WindowState>);
     let rc2 = rc1.clone();
     mem::forget(rc1);
     rc2
@@ -328,23 +328,25 @@ extern "C" fn dealloc_view(this: &Object, _: Sel) {
 }
 
 extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
-    let window = unsafe { window_state(this) };
+    let window_state = unsafe { get_window_state(this) };
+    let mut window_state_borrow = window_state.as_ref().borrow_mut();
 
-    let event = unsafe { Event::from_native(native_event, Some(window.size().y())) };
+    let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
 
     if let Some(event) = event {
         match event {
-            Event::LeftMouseDragged { position } => schedule_synthetic_drag(&window, position),
+            Event::LeftMouseDragged { position } => {
+                schedule_synthetic_drag(&&window_state, position)
+            }
             Event::LeftMouseUp { .. } => {
-                window.next_synthetic_drag_id();
+                post_inc(&mut window_state_borrow.synthetic_drag_counter);
             }
             _ => {}
         }
 
-        if let Some(callback) = window.event_callback.borrow_mut().as_mut() {
-            if callback(event) {
-                return;
-            }
+        if let Some(mut callback) = window_state_borrow.event_callback.take() {
+            callback(event, &mut *window_state_borrow);
+            window_state_borrow.event_callback = Some(callback);
         }
     }
 }
@@ -356,55 +358,62 @@ extern "C" fn send_event(this: &Object, _: Sel, native_event: id) {
 }
 
 extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
-    let window = unsafe { window_state(this) };
-    window.layer
+    let window_state = unsafe { get_window_state(this) };
+    let window_state = window_state.as_ref().borrow();
+    window_state.layer
 }
 
 extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
-    let window;
+    let window_state = unsafe { get_window_state(this) };
+    let mut window_state = window_state.as_ref().borrow_mut();
+
     unsafe {
-        window = window_state(this);
-        let _: () = msg_send![window.layer, setContentsScale: window.scale_factor() as f64];
+        let _: () =
+            msg_send![window_state.layer, setContentsScale: window_state.scale_factor() as f64];
     }
 
-    if let Some(callback) = window.resize_callback.borrow_mut().as_mut() {
-        let size = window.size();
-        let scale_factor = window.scale_factor();
-        callback(size, scale_factor);
+    if let Some(mut callback) = window_state.resize_callback.take() {
+        callback(&mut *window_state);
+        window_state.resize_callback = Some(callback);
     };
 }
 
 extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
-    let window;
-    unsafe {
-        window = window_state(this);
-        if window.size() == vec2f(size.width as f32, size.height as f32) {
-            return;
-        }
+    let window_state = unsafe { get_window_state(this) };
+    let mut window_state = window_state.as_ref().borrow_mut();
+
+    if window_state.size() == vec2f(size.width as f32, size.height as f32) {
+        return;
+    }
 
+    unsafe {
         let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
+    }
 
-        let scale_factor = window.scale_factor() as f64;
-        let drawable_size: NSSize = NSSize {
-            width: size.width * scale_factor,
-            height: size.height * scale_factor,
-        };
-        let _: () = msg_send![window.layer, setDrawableSize: drawable_size];
+    let scale_factor = window_state.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.layer, setDrawableSize: drawable_size];
     }
 
-    if let Some(callback) = window.resize_callback.borrow_mut().as_mut() {
-        let size = window.size();
-        let scale_factor = window.scale_factor();
-        callback(size, scale_factor);
+    if let Some(mut callback) = window_state.resize_callback.take() {
+        callback(&mut *window_state);
+        window_state.resize_callback = Some(callback);
     };
 }
 
 extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
+    log::info!("display layer");
     unsafe {
-        let window = window_state(this);
+        let window_state = get_window_state(this);
+        let mut window_state = window_state.as_ref().borrow_mut();
 
-        if let Some(scene) = window.scene_to_render.borrow_mut().take() {
-            let drawable: &metal::MetalDrawableRef = msg_send![window.layer, nextDrawable];
+        if let Some(scene) = window_state.scene_to_render.take() {
+            let drawable: &metal::MetalDrawableRef = msg_send![window_state.layer, nextDrawable];
 
             let render_pass_descriptor = metal::RenderPassDescriptor::new();
             let color_attachment = render_pass_descriptor
@@ -416,14 +425,19 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
             color_attachment.set_store_action(MTLStoreAction::Store);
             color_attachment.set_clear_color(MTLClearColor::new(0., 0., 0., 1.));
 
-            let command_buffer = window.command_queue.new_command_buffer();
+            let command_queue = window_state.command_queue.clone();
+            let command_buffer = command_queue.new_command_buffer();
             let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
 
-            window.renderer.borrow_mut().render(
+            let size = window_state.size();
+            let scale_factor = window_state.scale_factor();
+            let device = window_state.device.clone();
+
+            window_state.renderer.render(
                 &scene,
-                RenderContext {
-                    drawable_size: window.size() * window.scale_factor(),
-                    device: &window.device,
+                &RenderContext {
+                    drawable_size: size * scale_factor,
+                    device: &device,
                     command_encoder,
                 },
             );
@@ -432,23 +446,33 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
             command_buffer.commit();
             command_buffer.wait_until_completed();
             drawable.present();
+
+            log::info!("present");
         };
     }
 }
 
-fn schedule_synthetic_drag(window_state: &Rc<WindowState>, position: Vector2F) {
-    let drag_id = window_state.next_synthetic_drag_id();
+fn schedule_synthetic_drag(window_state: &Rc<RefCell<WindowState>>, position: Vector2F) {
     let weak_window_state = Rc::downgrade(window_state);
+    let mut window_state = window_state.as_ref().borrow_mut();
+
+    let drag_id = post_inc(&mut window_state.synthetic_drag_counter);
     let instant = Instant::now() + Duration::from_millis(16);
+
     window_state
         .executor
         .spawn(async move {
             Timer::at(instant).await;
             if let Some(window_state) = weak_window_state.upgrade() {
-                if window_state.synthetic_drag_counter.get() == drag_id {
-                    if let Some(callback) = window_state.event_callback.borrow_mut().as_mut() {
+                let mut window_state_borrow = window_state.as_ref().borrow_mut();
+                if window_state_borrow.synthetic_drag_counter == drag_id {
+                    if let Some(mut callback) = window_state_borrow.event_callback.take() {
                         schedule_synthetic_drag(&window_state, position);
-                        callback(Event::LeftMouseDragged { position });
+                        callback(
+                            Event::LeftMouseDragged { position },
+                            &mut *window_state_borrow,
+                        );
+                        window_state_borrow.event_callback = Some(callback);
                     }
                 }
             }

gpui/src/platform/mod.rs 🔗

@@ -32,7 +32,7 @@ pub trait App {
         &self,
         options: WindowOptions,
         executor: Rc<executor::Foreground>,
-    ) -> Result<Rc<dyn Window>>;
+    ) -> Result<Box<dyn Window>>;
 }
 
 pub trait Dispatcher: Send + Sync {
@@ -40,10 +40,15 @@ pub trait Dispatcher: Send + Sync {
     fn run_on_main_thread(&self, task: Runnable);
 }
 
-pub trait Window {
+pub trait Window: WindowContext {
+    fn on_event(&mut self, callback: Box<dyn FnMut(Event, &mut dyn WindowContext)>);
+    fn on_resize(&mut self, callback: Box<dyn FnMut(&mut dyn WindowContext)>);
+}
+
+pub trait WindowContext {
     fn size(&self) -> Vector2F;
     fn scale_factor(&self) -> f32;
-    fn render_scene(&self, scene: Scene);
+    fn present_scene(&mut self, scene: Scene);
 }
 
 pub struct WindowOptions<'a> {

gpui/src/scene.rs 🔗

@@ -6,12 +6,12 @@ pub struct Scene {
 }
 
 #[derive(Default)]
-struct Layer {
+pub struct Layer {
     clip_bounds: Option<RectF>,
     quads: Vec<Quad>,
 }
 
-#[derive(Default)]
+#[derive(Default, Debug)]
 pub struct Quad {
     pub bounds: RectF,
     pub background: Option<ColorU>,
@@ -19,7 +19,7 @@ pub struct Quad {
     pub corder_radius: f32,
 }
 
-#[derive(Clone, Copy, Default)]
+#[derive(Clone, Copy, Default, Debug)]
 pub struct Border {
     pub width: f32,
     pub color: Option<ColorU>,
@@ -38,13 +38,19 @@ impl Scene {
         }
     }
 
-    pub fn push_layer(&mut self, clip_bounds: Option<RectF>) {}
-
-    pub fn pop_layer(&mut self) {
-        assert!(self.active_layer_stack.len() > 1);
-        self.active_layer_stack.pop();
+    pub fn layers(&self) -> &[Layer] {
+        self.layers.as_slice()
     }
 
+    // pub fn push_layer(&mut self, clip_bounds: Option<RectF>) {
+
+    // }
+
+    // pub fn pop_layer(&mut self) {
+    //     assert!(self.active_layer_stack.len() > 1);
+    //     self.active_layer_stack.pop();
+    // }
+
     pub fn push_quad(&mut self, quad: Quad) {
         self.active_layer().push_quad(quad)
     }
@@ -58,6 +64,10 @@ impl Layer {
     fn push_quad(&mut self, quad: Quad) {
         self.quads.push(quad);
     }
+
+    pub fn quads(&self) -> &[Quad] {
+        self.quads.as_slice()
+    }
 }
 
 impl Border {