use crate::sugarloaf::{SugarloafWindow, SugarloafWindowSize};
use ::objc_rs::runtime::Object;
use ::objc_rs::{msg_send, sel, sel_impl};
use core_graphics_types::geometry::CGSize;
use metal::{
CommandBuffer, CommandQueue, Device, MTLGPUFamily, MTLPixelFormat, MetalDrawable,
MetalLayer, RenderCommandEncoder, Texture,
};
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
pub struct MetalContext {
pub device: Device,
pub command_queue: CommandQueue,
pub layer: MetalLayer,
pub size: SugarloafWindowSize,
pub scale: f32,
pub supports_f16: bool,
}
pub struct MetalTexture {
pub texture: Texture,
pub drawable: MetalDrawable,
}
pub struct MetalCommandEncoder {
pub command_buffer: CommandBuffer,
}
pub struct MetalRenderPass<'a> {
pub encoder: RenderCommandEncoder,
_phantom: std::marker::PhantomData<&'a ()>,
}
impl MetalContext {
pub fn new(sugarloaf_window: SugarloafWindow) -> Self {
let device = Device::system_default().expect("Failed to create Metal device");
let command_queue = device.new_command_queue();
let size = sugarloaf_window.size;
let scale = sugarloaf_window.scale;
let layer = MetalLayer::new();
layer.set_device(&device);
layer.set_pixel_format(MTLPixelFormat::BGRA8Unorm);
layer.set_presents_with_transaction(false);
let drawable_size = CGSize {
width: size.width as f64,
height: size.height as f64,
};
layer.set_drawable_size(drawable_size);
layer.set_contents_scale(scale as f64);
unsafe {
match sugarloaf_window.window_handle().unwrap().as_raw() {
RawWindowHandle::AppKit(handle) => {
let ns_view = handle.ns_view.as_ptr() as *mut Object;
let _: () = msg_send![ns_view, setLayer: layer.as_ref()];
let _: () = msg_send![ns_view, setWantsLayer: true];
}
_ => panic!("Metal backend only supports macOS AppKit windows"),
}
}
let supports_f16 = device.supports_family(MTLGPUFamily::Apple1);
tracing::info!("Metal device created: {:?}", device.name());
MetalContext {
device,
command_queue,
layer,
size: SugarloafWindowSize {
width: size.width,
height: size.height,
},
scale,
supports_f16,
}
}
#[inline]
pub fn resize(&mut self, width: u32, height: u32) {
self.size.width = width as f32;
self.size.height = height as f32;
let drawable_size = CGSize {
width: width as f64,
height: height as f64,
};
self.layer.set_drawable_size(drawable_size);
}
#[inline]
pub fn set_scale(&mut self, scale: f32) {
self.scale = scale;
}
#[inline]
pub fn get_current_texture(&self) -> Result<MetalTexture, String> {
if let Some(drawable) = self.layer.next_drawable() {
let texture = drawable.texture().to_owned();
Ok(MetalTexture {
texture,
drawable: drawable.to_owned(),
})
} else {
Err("Failed to get next drawable".to_string())
}
}
pub fn supports_f16(&self) -> bool {
self.supports_f16
}
}