Get basic graphics rendering via Metal

Also, handle window resize.
This commit is contained in:
Nathan Sobo 2021-03-20 22:15:04 -06:00
parent 292b41ad57
commit e5ffe43bb6
9 changed files with 328 additions and 120 deletions

3
.gitignore vendored
View file

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

View file

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

View file

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

View file

@ -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 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] = const SHADERS_METALLIB: &'static [u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib")); include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
const INSTANCE_BUFFER_SIZE: u64 = 1024 * 1024;
pub struct Renderer { pub struct Renderer {
quad_pipeline_state: metal::RenderPipelineState, quad_pipeline_state: metal::RenderPipelineState,
quad_vertices: metal::Buffer,
instances: metal::Buffer,
} }
impl Renderer { impl Renderer {
@ -17,6 +24,22 @@ impl Renderer {
.new_library_with_data(SHADERS_METALLIB) .new_library_with_data(SHADERS_METALLIB)
.map_err(|message| anyhow!("error building metal library: {}", message))?; .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 { Ok(Self {
quad_pipeline_state: build_pipeline_state( quad_pipeline_state: build_pipeline_state(
device, device,
@ -26,10 +49,78 @@ impl Renderer {
"quad_fragment", "quad_fragment",
pixel_format, 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( fn build_pipeline_state(
@ -47,7 +138,7 @@ fn build_pipeline_state(
.get_function(fragment_fn_name, None) .get_function(fragment_fn_name, None)
.map_err(|message| anyhow!("error locating fragment function: {}", message))?; .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_label(label);
descriptor.set_vertex_function(Some(vertex_fn.as_ref())); descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
descriptor.set_fragment_function(Some(fragment_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) .new_render_pipeline_state(&descriptor)
.map_err(|message| anyhow!("could not create render pipeline state: {}", message)) .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
}
}
}

View file

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

View file

@ -3,6 +3,10 @@
using namespace metal; using namespace metal;
float4 coloru_to_colorf(uchar4 coloru) {
return float4(coloru) / float4(0xff, 0xff, 0xff, 0xff);
}
struct QuadFragmentInput { struct QuadFragmentInput {
float4 position [[position]]; float4 position [[position]];
GPUIQuad quad; GPUIQuad quad;
@ -17,14 +21,15 @@ vertex QuadFragmentInput quad_vertex(
) { ) {
float2 unit_vertex = unit_vertices[unit_vertex_id]; float2 unit_vertex = unit_vertices[unit_vertex_id];
GPUIQuad quad = quads[quad_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 { return QuadFragmentInput {
position, device_position,
quad, quad,
}; };
} }
fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]]) { fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]]) {
return input.quad.background_color; return coloru_to_colorf(input.quad.background_color);
} }

View file

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

View file

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

View file

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