Encapsulate metal layer into Renderer

This commit is contained in:
Antonio Scandurra 2022-09-12 10:07:13 +02:00
parent e803dd9f72
commit f50c6af001
2 changed files with 79 additions and 84 deletions

View file

@ -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(

View file

@ -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();
}; };
} }
} }