Encapsulate metal layer into Renderer
This commit is contained in:
parent
e803dd9f72
commit
f50c6af001
2 changed files with 79 additions and 84 deletions
|
@ -8,12 +8,17 @@ use crate::{
|
||||||
platform,
|
platform,
|
||||||
scene::{Glyph, Icon, Image, ImageGlyph, Layer, Quad, Scene, Shadow, Underline},
|
scene::{Glyph, Icon, Image, ImageGlyph, Layer, Quad, Scene, Shadow, Underline},
|
||||||
};
|
};
|
||||||
use cocoa::foundation::NSUInteger;
|
use cocoa::{
|
||||||
|
base::{NO, YES},
|
||||||
|
foundation::{NSRect, NSUInteger},
|
||||||
|
quartzcore::AutoresizingMask,
|
||||||
|
};
|
||||||
use core_foundation::base::TCFType;
|
use core_foundation::base::TCFType;
|
||||||
use foreign_types::ForeignTypeRef;
|
use foreign_types::ForeignTypeRef;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use media::core_video::{self, CVMetalTextureCache};
|
use media::core_video::{self, CVMetalTextureCache};
|
||||||
use metal::{MTLPixelFormat, MTLResourceOptions, NSRange};
|
use metal::{CGFloat, CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
|
||||||
|
use objc::{self, msg_send, sel, sel_impl};
|
||||||
use shaders::ToFloat2 as _;
|
use shaders::ToFloat2 as _;
|
||||||
use std::{collections::HashMap, ffi::c_void, iter::Peekable, mem, ptr, sync::Arc, vec};
|
use std::{collections::HashMap, ffi::c_void, iter::Peekable, mem, ptr, sync::Arc, vec};
|
||||||
|
|
||||||
|
@ -21,6 +26,8 @@ const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shader
|
||||||
const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
|
const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
|
||||||
|
|
||||||
pub struct Renderer {
|
pub struct Renderer {
|
||||||
|
layer: metal::MetalLayer,
|
||||||
|
command_queue: CommandQueue,
|
||||||
sprite_cache: SpriteCache,
|
sprite_cache: SpriteCache,
|
||||||
image_cache: ImageCache,
|
image_cache: ImageCache,
|
||||||
path_atlases: AtlasAllocator,
|
path_atlases: AtlasAllocator,
|
||||||
|
@ -48,12 +55,30 @@ pub struct Surface {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
pub fn new(
|
pub fn new(fonts: Arc<dyn platform::FontSystem>) -> Self {
|
||||||
device: metal::Device,
|
const PIXEL_FORMAT: MTLPixelFormat = MTLPixelFormat::BGRA8Unorm;
|
||||||
pixel_format: metal::MTLPixelFormat,
|
|
||||||
scale_factor: f32,
|
let device: metal::Device = if let Some(device) = metal::Device::system_default() {
|
||||||
fonts: Arc<dyn platform::FontSystem>,
|
device
|
||||||
) -> Self {
|
} else {
|
||||||
|
log::error!("unable to access a compatible graphics device");
|
||||||
|
std::process::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let layer = metal::MetalLayer::new();
|
||||||
|
layer.set_device(&device);
|
||||||
|
layer.set_pixel_format(PIXEL_FORMAT);
|
||||||
|
layer.set_presents_with_transaction(true);
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![&*layer, setAllowsNextDrawableTimeout: NO];
|
||||||
|
let _: () = msg_send![&*layer, setNeedsDisplayOnBoundsChange: YES];
|
||||||
|
let _: () = msg_send![
|
||||||
|
&*layer,
|
||||||
|
setAutoresizingMask: AutoresizingMask::WIDTH_SIZABLE
|
||||||
|
| AutoresizingMask::HEIGHT_SIZABLE
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
let library = device
|
let library = device
|
||||||
.new_library_with_data(SHADERS_METALLIB)
|
.new_library_with_data(SHADERS_METALLIB)
|
||||||
.expect("error building metal library");
|
.expect("error building metal library");
|
||||||
|
@ -76,13 +101,8 @@ impl Renderer {
|
||||||
MTLResourceOptions::StorageModeManaged,
|
MTLResourceOptions::StorageModeManaged,
|
||||||
);
|
);
|
||||||
|
|
||||||
let sprite_cache = SpriteCache::new(
|
let sprite_cache = SpriteCache::new(device.clone(), vec2i(1024, 768), 1., fonts.clone());
|
||||||
device.clone(),
|
let image_cache = ImageCache::new(device.clone(), vec2i(1024, 768), 1., fonts);
|
||||||
vec2i(1024, 768),
|
|
||||||
scale_factor,
|
|
||||||
fonts.clone(),
|
|
||||||
);
|
|
||||||
let image_cache = ImageCache::new(device.clone(), vec2i(1024, 768), scale_factor, fonts);
|
|
||||||
let path_atlases =
|
let path_atlases =
|
||||||
AtlasAllocator::new(device.clone(), build_path_atlas_texture_descriptor());
|
AtlasAllocator::new(device.clone(), build_path_atlas_texture_descriptor());
|
||||||
let quad_pipeline_state = build_pipeline_state(
|
let quad_pipeline_state = build_pipeline_state(
|
||||||
|
@ -91,7 +111,7 @@ impl Renderer {
|
||||||
"quad",
|
"quad",
|
||||||
"quad_vertex",
|
"quad_vertex",
|
||||||
"quad_fragment",
|
"quad_fragment",
|
||||||
pixel_format,
|
PIXEL_FORMAT,
|
||||||
);
|
);
|
||||||
let shadow_pipeline_state = build_pipeline_state(
|
let shadow_pipeline_state = build_pipeline_state(
|
||||||
&device,
|
&device,
|
||||||
|
@ -99,7 +119,7 @@ impl Renderer {
|
||||||
"shadow",
|
"shadow",
|
||||||
"shadow_vertex",
|
"shadow_vertex",
|
||||||
"shadow_fragment",
|
"shadow_fragment",
|
||||||
pixel_format,
|
PIXEL_FORMAT,
|
||||||
);
|
);
|
||||||
let sprite_pipeline_state = build_pipeline_state(
|
let sprite_pipeline_state = build_pipeline_state(
|
||||||
&device,
|
&device,
|
||||||
|
@ -107,7 +127,7 @@ impl Renderer {
|
||||||
"sprite",
|
"sprite",
|
||||||
"sprite_vertex",
|
"sprite_vertex",
|
||||||
"sprite_fragment",
|
"sprite_fragment",
|
||||||
pixel_format,
|
PIXEL_FORMAT,
|
||||||
);
|
);
|
||||||
let image_pipeline_state = build_pipeline_state(
|
let image_pipeline_state = build_pipeline_state(
|
||||||
&device,
|
&device,
|
||||||
|
@ -115,7 +135,7 @@ impl Renderer {
|
||||||
"image",
|
"image",
|
||||||
"image_vertex",
|
"image_vertex",
|
||||||
"image_fragment",
|
"image_fragment",
|
||||||
pixel_format,
|
PIXEL_FORMAT,
|
||||||
);
|
);
|
||||||
let surface_pipeline_state = build_pipeline_state(
|
let surface_pipeline_state = build_pipeline_state(
|
||||||
&device,
|
&device,
|
||||||
|
@ -123,7 +143,7 @@ impl Renderer {
|
||||||
"surface",
|
"surface",
|
||||||
"surface_vertex",
|
"surface_vertex",
|
||||||
"surface_fragment",
|
"surface_fragment",
|
||||||
pixel_format,
|
PIXEL_FORMAT,
|
||||||
);
|
);
|
||||||
let path_atlas_pipeline_state = build_path_atlas_pipeline_state(
|
let path_atlas_pipeline_state = build_path_atlas_pipeline_state(
|
||||||
&device,
|
&device,
|
||||||
|
@ -139,10 +159,12 @@ impl Renderer {
|
||||||
"underline",
|
"underline",
|
||||||
"underline_vertex",
|
"underline_vertex",
|
||||||
"underline_fragment",
|
"underline_fragment",
|
||||||
pixel_format,
|
PIXEL_FORMAT,
|
||||||
);
|
);
|
||||||
let cv_texture_cache = CVMetalTextureCache::new(device.as_ptr()).unwrap();
|
let cv_texture_cache = CVMetalTextureCache::new(device.as_ptr()).unwrap();
|
||||||
Self {
|
Self {
|
||||||
|
layer,
|
||||||
|
command_queue: device.new_command_queue(),
|
||||||
sprite_cache,
|
sprite_cache,
|
||||||
image_cache,
|
image_cache,
|
||||||
path_atlases,
|
path_atlases,
|
||||||
|
@ -159,13 +181,21 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(
|
pub fn layer(&self) -> &metal::MetalLayerRef {
|
||||||
&mut self,
|
&*self.layer
|
||||||
scene: &Scene,
|
}
|
||||||
drawable_size: Vector2F,
|
|
||||||
command_buffer: &metal::CommandBufferRef,
|
pub fn render(&mut self, scene: &Scene) {
|
||||||
output: &metal::TextureRef,
|
let layer = self.layer.clone();
|
||||||
) {
|
let drawable = layer.next_drawable().unwrap();
|
||||||
|
let command_queue = self.command_queue.clone();
|
||||||
|
let command_buffer = command_queue.new_command_buffer();
|
||||||
|
|
||||||
|
let frame: NSRect = unsafe { msg_send![self.layer(), frame] };
|
||||||
|
let scale_factor: CGFloat = unsafe { msg_send![self.layer(), contentsScale] };
|
||||||
|
let drawable_size =
|
||||||
|
vec2f(frame.size.width as f32, frame.size.height as f32) * scale_factor as f32;
|
||||||
|
|
||||||
self.sprite_cache.set_scale_factor(scene.scale_factor());
|
self.sprite_cache.set_scale_factor(scene.scale_factor());
|
||||||
self.image_cache.set_scale_factor(scene.scale_factor());
|
self.image_cache.set_scale_factor(scene.scale_factor());
|
||||||
|
|
||||||
|
@ -178,13 +208,17 @@ impl Renderer {
|
||||||
&mut offset,
|
&mut offset,
|
||||||
drawable_size,
|
drawable_size,
|
||||||
command_buffer,
|
command_buffer,
|
||||||
output,
|
drawable.texture(),
|
||||||
);
|
);
|
||||||
self.instances.did_modify_range(NSRange {
|
self.instances.did_modify_range(NSRange {
|
||||||
location: 0,
|
location: 0,
|
||||||
length: offset as NSUInteger,
|
length: offset as NSUInteger,
|
||||||
});
|
});
|
||||||
self.image_cache.finish_frame();
|
self.image_cache.finish_frame();
|
||||||
|
|
||||||
|
command_buffer.commit();
|
||||||
|
command_buffer.wait_until_completed();
|
||||||
|
drawable.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_path_atlases(
|
fn render_path_atlases(
|
||||||
|
|
|
@ -20,11 +20,10 @@ use cocoa::{
|
||||||
foundation::{
|
foundation::{
|
||||||
NSAutoreleasePool, NSInteger, NSNotFound, NSPoint, NSRect, NSSize, NSString, NSUInteger,
|
NSAutoreleasePool, NSInteger, NSNotFound, NSPoint, NSRect, NSSize, NSString, NSUInteger,
|
||||||
},
|
},
|
||||||
quartzcore::AutoresizingMask,
|
|
||||||
};
|
};
|
||||||
use core_graphics::display::CGRect;
|
use core_graphics::display::CGRect;
|
||||||
use ctor::ctor;
|
use ctor::ctor;
|
||||||
use foreign_types::ForeignType as _;
|
use foreign_types::ForeignTypeRef;
|
||||||
use objc::{
|
use objc::{
|
||||||
class,
|
class,
|
||||||
declare::ClassDecl,
|
declare::ClassDecl,
|
||||||
|
@ -306,9 +305,7 @@ struct WindowState {
|
||||||
executor: Rc<executor::Foreground>,
|
executor: Rc<executor::Foreground>,
|
||||||
scene_to_render: Option<Scene>,
|
scene_to_render: Option<Scene>,
|
||||||
renderer: Renderer,
|
renderer: Renderer,
|
||||||
command_queue: metal::CommandQueue,
|
|
||||||
last_fresh_keydown: Option<Keystroke>,
|
last_fresh_keydown: Option<Keystroke>,
|
||||||
layer: id,
|
|
||||||
traffic_light_position: Option<Vector2F>,
|
traffic_light_position: Option<Vector2F>,
|
||||||
previous_modifiers_changed_event: Option<Event>,
|
previous_modifiers_changed_event: Option<Event>,
|
||||||
//State tracking what the IME did after the last request
|
//State tracking what the IME did after the last request
|
||||||
|
@ -329,8 +326,6 @@ impl Window {
|
||||||
executor: Rc<executor::Foreground>,
|
executor: Rc<executor::Foreground>,
|
||||||
fonts: Arc<dyn platform::FontSystem>,
|
fonts: Arc<dyn platform::FontSystem>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
const PIXEL_FORMAT: metal::MTLPixelFormat = metal::MTLPixelFormat::BGRA8Unorm;
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let pool = NSAutoreleasePool::new(nil);
|
let pool = NSAutoreleasePool::new(nil);
|
||||||
|
|
||||||
|
@ -368,25 +363,6 @@ impl Window {
|
||||||
native_window.setFrame_display_(screen.visibleFrame(), YES);
|
native_window.setFrame_display_(screen.visibleFrame(), YES);
|
||||||
}
|
}
|
||||||
|
|
||||||
let device: metal::Device = if let Some(device) = metal::Device::system_default() {
|
|
||||||
device
|
|
||||||
} else {
|
|
||||||
log::error!("unable to access a compatible graphics device");
|
|
||||||
std::process::exit(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
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: id = msg_send![VIEW_CLASS, alloc];
|
||||||
let native_view = NSView::init(native_view);
|
let native_view = NSView::init(native_view);
|
||||||
assert!(!native_view.is_null());
|
assert!(!native_view.is_null());
|
||||||
|
@ -406,15 +382,8 @@ impl Window {
|
||||||
synthetic_drag_counter: 0,
|
synthetic_drag_counter: 0,
|
||||||
executor,
|
executor,
|
||||||
scene_to_render: Default::default(),
|
scene_to_render: Default::default(),
|
||||||
renderer: Renderer::new(
|
renderer: Renderer::new(fonts),
|
||||||
device.clone(),
|
|
||||||
PIXEL_FORMAT,
|
|
||||||
get_scale_factor(native_window),
|
|
||||||
fonts,
|
|
||||||
),
|
|
||||||
command_queue: device.new_command_queue(),
|
|
||||||
last_fresh_keydown: None,
|
last_fresh_keydown: None,
|
||||||
layer,
|
|
||||||
traffic_light_position: options
|
traffic_light_position: options
|
||||||
.titlebar
|
.titlebar
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -1057,7 +1026,7 @@ extern "C" fn close_window(this: &Object, _: Sel) {
|
||||||
extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
|
extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
|
||||||
let window_state = unsafe { get_window_state(this) };
|
let window_state = unsafe { get_window_state(this) };
|
||||||
let window_state = window_state.as_ref().borrow();
|
let window_state = window_state.as_ref().borrow();
|
||||||
window_state.layer
|
window_state.renderer.layer().as_ptr() as id
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
|
extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
|
||||||
|
@ -1072,8 +1041,14 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
|
||||||
height: size.y() as f64 * scale_factor,
|
height: size.y() as f64 * scale_factor,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _: () = msg_send![window_state_borrow.layer, setContentsScale: scale_factor];
|
let _: () = msg_send![
|
||||||
let _: () = msg_send![window_state_borrow.layer, setDrawableSize: drawable_size];
|
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() {
|
if let Some(mut callback) = window_state_borrow.resize_callback.take() {
|
||||||
|
@ -1102,7 +1077,10 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: () = msg_send![window_state_borrow.layer, setDrawableSize: drawable_size];
|
let _: () = msg_send![
|
||||||
|
window_state_borrow.renderer.layer(),
|
||||||
|
setDrawableSize: drawable_size
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(window_state_borrow);
|
drop(window_state_borrow);
|
||||||
|
@ -1118,25 +1096,8 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let window_state = get_window_state(this);
|
let window_state = get_window_state(this);
|
||||||
let mut window_state = window_state.as_ref().borrow_mut();
|
let mut window_state = window_state.as_ref().borrow_mut();
|
||||||
|
|
||||||
if let Some(scene) = window_state.scene_to_render.take() {
|
if let Some(scene) = window_state.scene_to_render.take() {
|
||||||
let drawable: &metal::MetalDrawableRef = msg_send![window_state.layer, nextDrawable];
|
window_state.renderer.render(&scene);
|
||||||
let command_queue = window_state.command_queue.clone();
|
|
||||||
let command_buffer = command_queue.new_command_buffer();
|
|
||||||
|
|
||||||
let size = window_state.size();
|
|
||||||
let scale_factor = window_state.scale_factor();
|
|
||||||
|
|
||||||
window_state.renderer.render(
|
|
||||||
&scene,
|
|
||||||
size * scale_factor,
|
|
||||||
command_buffer,
|
|
||||||
drawable.texture(),
|
|
||||||
);
|
|
||||||
|
|
||||||
command_buffer.commit();
|
|
||||||
command_buffer.wait_until_completed();
|
|
||||||
drawable.present();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue