@@ -336,7 +336,7 @@ dependencies = [
"cocoa-foundation",
"core-foundation",
"core-graphics",
- "foreign-types 0.3.2",
+ "foreign-types",
"libc",
"objc",
]
@@ -351,7 +351,7 @@ dependencies = [
"block",
"core-foundation",
"core-graphics-types",
- "foreign-types 0.3.2",
+ "foreign-types",
"libc",
"objc",
]
@@ -396,7 +396,7 @@ dependencies = [
"bitflags",
"core-foundation",
"core-graphics-types",
- "foreign-types 0.3.2",
+ "foreign-types",
"libc",
]
@@ -408,7 +408,7 @@ checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
dependencies = [
"bitflags",
"core-foundation",
- "foreign-types 0.3.2",
+ "foreign-types",
"libc",
]
@@ -420,7 +420,7 @@ checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25"
dependencies = [
"core-foundation",
"core-graphics",
- "foreign-types 0.3.2",
+ "foreign-types",
"libc",
]
@@ -616,28 +616,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
- "foreign-types-shared 0.1.1",
-]
-
-[[package]]
-name = "foreign-types"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
-dependencies = [
- "foreign-types-macros",
- "foreign-types-shared 0.3.0",
-]
-
-[[package]]
-name = "foreign-types-macros"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63f713f8b2aa9e24fec85b0e290c56caee12e3b6ae0aeeda238a75b28251afd6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "foreign-types-shared",
]
[[package]]
@@ -646,12 +625,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
-[[package]]
-name = "foreign-types-shared"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7684cf33bb7f28497939e8c7cf17e3e4e3b8d9a0080ffa4f8ae2f515442ee855"
-
[[package]]
name = "freetype"
version = "0.7.0"
@@ -773,7 +746,7 @@ dependencies = [
"core-text",
"ctor",
"font-kit",
- "foreign-types 0.5.0",
+ "foreign-types",
"log",
"metal",
"num_cpus",
@@ -924,7 +897,7 @@ dependencies = [
"bitflags",
"block",
"cocoa-foundation",
- "foreign-types 0.3.2",
+ "foreign-types",
"log",
"objc",
]
@@ -0,0 +1,63 @@
+use anyhow::{anyhow, Result};
+
+use crate::Scene;
+
+use super::window::RenderContext;
+
+const SHADERS_METALLIB: &'static [u8] =
+ include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
+
+pub struct Renderer {
+ quad_pipeline_state: metal::RenderPipelineState,
+}
+
+impl Renderer {
+ pub fn new(device: &metal::DeviceRef, pixel_format: metal::MTLPixelFormat) -> Result<Self> {
+ let library = device
+ .new_library_with_data(SHADERS_METALLIB)
+ .map_err(|message| anyhow!("error building metal library: {}", message))?;
+
+ Ok(Self {
+ quad_pipeline_state: build_pipeline_state(
+ device,
+ &library,
+ "quad",
+ "quad_vertex",
+ "quad_fragment",
+ pixel_format,
+ )?,
+ })
+ }
+
+ pub fn render(&mut self, scene: &Scene, ctx: RenderContext) {}
+}
+
+fn build_pipeline_state(
+ device: &metal::DeviceRef,
+ library: &metal::LibraryRef,
+ label: &str,
+ vertex_fn_name: &str,
+ fragment_fn_name: &str,
+ pixel_format: metal::MTLPixelFormat,
+) -> Result<metal::RenderPipelineState> {
+ let vertex_fn = library
+ .get_function(vertex_fn_name, None)
+ .map_err(|message| anyhow!("error locating vertex function: {}", message))?;
+ let fragment_fn = library
+ .get_function(fragment_fn_name, None)
+ .map_err(|message| anyhow!("error locating fragment function: {}", message))?;
+
+ let mut 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()));
+ descriptor
+ .color_attachments()
+ .object_at(0)
+ .unwrap()
+ .set_pixel_format(pixel_format);
+
+ device
+ .new_render_pipeline_state(&descriptor)
+ .map_err(|message| anyhow!("could not create render pipeline state: {}", message))
+}
@@ -12,13 +12,16 @@ use cocoa::{
},
base::{id, nil},
foundation::{NSAutoreleasePool, NSSize, NSString},
+ quartzcore::AutoresizingMask,
};
use ctor::ctor;
+use foreign_types::ForeignType as _;
+use metal::{MTLClearColor, MTLLoadAction, MTLStoreAction};
use objc::{
class,
declare::ClassDecl,
msg_send,
- runtime::{Class, Object, Sel, BOOL, NO, YES},
+ runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
sel, sel_impl,
};
use pathfinder_geometry::vector::vec2f;
@@ -31,13 +34,12 @@ use std::{
time::{Duration, Instant},
};
-use super::geometry::RectFExt;
+use super::{geometry::RectFExt, renderer::Renderer};
const WINDOW_STATE_IVAR: &'static str = "windowState";
static mut WINDOW_CLASS: *const Class = ptr::null();
static mut VIEW_CLASS: *const Class = ptr::null();
-static mut DELEGATE_CLASS: *const Class = ptr::null();
#[ctor]
unsafe fn build_classes() {
@@ -63,7 +65,9 @@ unsafe fn build_classes() {
VIEW_CLASS = {
let mut decl = ClassDecl::new("GPUIView", class!(NSView)).unwrap();
decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
+
decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));
+
decl.add_method(
sel!(keyDown:),
handle_view_event as extern "C" fn(&Object, Sel, id),
@@ -84,20 +88,25 @@ unsafe fn build_classes() {
sel!(scrollWheel:),
handle_view_event as extern "C" fn(&Object, Sel, id),
);
- decl.register()
- };
- DELEGATE_CLASS = {
- let mut decl = ClassDecl::new("GPUIWindowDelegate", class!(NSObject)).unwrap();
+ decl.add_protocol(Protocol::get("CALayerDelegate").unwrap());
decl.add_method(
- sel!(dealloc),
- dealloc_delegate as extern "C" fn(&Object, Sel),
+ sel!(makeBackingLayer),
+ make_backing_layer as extern "C" fn(&Object, Sel) -> id,
+ );
+ decl.add_method(
+ sel!(viewDidChangeBackingProperties),
+ view_did_change_backing_properties as extern "C" fn(&Object, Sel),
+ );
+ decl.add_method(
+ sel!(setFrameSize:),
+ set_frame_size as extern "C" fn(&Object, Sel, NSSize),
);
- decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
decl.add_method(
- sel!(windowDidResize:),
- window_did_resize as extern "C" fn(&Object, Sel, id),
+ sel!(displayLayer:),
+ display_layer as extern "C" fn(&Object, Sel, id),
);
+
decl.register()
};
}
@@ -110,6 +119,17 @@ struct WindowState {
resize_callback: RefCell<Option<Box<dyn FnMut(Vector2F, f32)>>>,
synthetic_drag_counter: Cell<usize>,
executor: Rc<executor::Foreground>,
+ scene_to_render: RefCell<Option<Scene>>,
+ renderer: RefCell<Renderer>,
+ command_queue: metal::CommandQueue,
+ device: metal::Device,
+ layer: id,
+}
+
+pub struct RenderContext<'a> {
+ pub drawable_size: Vector2F,
+ pub device: &'a metal::Device,
+ pub command_encoder: &'a metal::RenderCommandEncoderRef,
}
impl Window {
@@ -117,6 +137,8 @@ impl Window {
options: platform::WindowOptions,
executor: Rc<executor::Foreground>,
) -> Result<Self> {
+ const PIXEL_FORMAT: metal::MTLPixelFormat = metal::MTLPixelFormat::BGRA8Unorm;
+
unsafe {
let pool = NSAutoreleasePool::new(nil);
@@ -138,12 +160,20 @@ impl Window {
return Err(anyhow!("window returned nil from initializer"));
}
- let delegate: id = msg_send![DELEGATE_CLASS, alloc];
- let delegate = delegate.init();
- if native_window == nil {
- return Err(anyhow!("delegate returned nil from initializer"));
- }
- native_window.setDelegate_(delegate);
+ let device = metal::Device::system_default()
+ .ok_or_else(|| anyhow!("could not find default metal device"))?;
+
+ let layer: id = msg_send![class!(CAMetalLayer), layer];
+ let _: () = msg_send![layer, setDevice: device.as_ptr()];
+ let _: () = msg_send![layer, setPixelFormat: PIXEL_FORMAT];
+ let _: () = msg_send![layer, setAllowsNextDrawableTimeout: NO];
+ let _: () = msg_send![layer, setNeedsDisplayOnBoundsChange: YES];
+ let _: () = msg_send![layer, setPresentsWithTransaction: YES];
+ let _: () = msg_send![
+ layer,
+ setAutoresizingMask: AutoresizingMask::WIDTH_SIZABLE
+ | AutoresizingMask::HEIGHT_SIZABLE
+ ];
let native_view: id = msg_send![VIEW_CLASS, alloc];
let native_view = NSView::init(native_view);
@@ -157,6 +187,11 @@ impl Window {
resize_callback: RefCell::new(None),
synthetic_drag_counter: Cell::new(0),
executor,
+ scene_to_render: Default::default(),
+ renderer: RefCell::new(Renderer::new(&device, PIXEL_FORMAT)?),
+ command_queue: device.new_command_queue(),
+ device,
+ layer,
}));
(*native_window).set_ivar(
@@ -167,10 +202,6 @@ impl Window {
WINDOW_STATE_IVAR,
Rc::into_raw(window.0.clone()) as *const c_void,
);
- (*delegate).set_ivar(
- WINDOW_STATE_IVAR,
- Rc::into_raw(window.0.clone()) as *const c_void,
- );
if let Some(title) = options.title.as_ref() {
native_window.setTitle_(NSString::alloc(nil).init_str(title));
@@ -237,7 +268,10 @@ impl platform::Window for Window {
}
fn render_scene(&self, scene: Scene) {
- log::info!("render scene");
+ *self.0.scene_to_render.borrow_mut() = Some(scene);
+ unsafe {
+ let _: () = msg_send![self.0.native_window.contentView(), setNeedsDisplay: YES];
+ }
}
}
@@ -293,14 +327,6 @@ extern "C" fn dealloc_view(this: &Object, _: Sel) {
}
}
-extern "C" fn dealloc_delegate(this: &Object, _: Sel) {
- unsafe {
- let raw: *mut c_void = *this.get_ivar(WINDOW_STATE_IVAR);
- Rc::from_raw(raw as *mut WindowState);
- let () = msg_send![super(this, class!(NSObject)), dealloc];
- }
-}
-
extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
let window = unsafe { window_state(this) };
@@ -329,14 +355,85 @@ extern "C" fn send_event(this: &Object, _: Sel, native_event: id) {
}
}
-extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
+extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
let window = unsafe { window_state(this) };
- let size = window.size();
- let scale_factor = window.scale_factor();
+ window.layer
+}
+
+extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
+ let window;
+ unsafe {
+ window = window_state(this);
+ let _: () = msg_send![window.layer, setContentsScale: window.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);
+ };
+}
+
+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 _: () = 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];
+ }
+
+ 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);
+ };
+}
+
+extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
+ unsafe {
+ let window = window_state(this);
+
+ if let Some(scene) = window.scene_to_render.borrow_mut().take() {
+ let drawable: &metal::MetalDrawableRef = msg_send![window.layer, nextDrawable];
+
+ let render_pass_descriptor = metal::RenderPassDescriptor::new();
+ let color_attachment = render_pass_descriptor
+ .color_attachments()
+ .object_at(0)
+ .unwrap();
+ color_attachment.set_texture(Some(drawable.texture()));
+ color_attachment.set_load_action(MTLLoadAction::Clear);
+ 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_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
+
+ window.renderer.borrow_mut().render(
+ &scene,
+ RenderContext {
+ drawable_size: window.size() * window.scale_factor(),
+ device: &window.device,
+ command_encoder,
+ },
+ );
+
+ command_encoder.end_encoding();
+ command_buffer.commit();
+ command_buffer.wait_until_completed();
+ drawable.present();
+ };
}
- drop(window);
}
fn schedule_synthetic_drag(window_state: &Rc<WindowState>, position: Vector2F) {