use objc2::ClassType;
use objc2::rc::Retained;
use objc2_core_foundation::CGSize;
use objc2_quartz_core::CAMetalLayer;
const SURFACE_INFO: crate::SurfaceInfo = crate::SurfaceInfo {
format: crate::TextureFormat::Rgba8Unorm,
alpha: crate::AlphaMode::Ignored,
};
impl super::Surface {
pub fn metal_layer(&self) -> Retained<CAMetalLayer> {
self.render_layer.clone()
}
pub fn info(&self) -> crate::SurfaceInfo {
self.info
}
pub fn acquire_frame(&self) -> super::Frame {
use objc2_quartz_core::CAMetalDrawable as _;
let (drawable, texture) = objc2::rc::autoreleasepool(|_| unsafe {
let drawable = self.render_layer.nextDrawable().unwrap();
let texture = drawable.texture();
(Retained::cast_unchecked(drawable), texture)
});
super::Frame { drawable, texture }
}
}
impl super::Context {
pub fn create_surface<I: raw_window_handle::HasWindowHandle>(
&self,
window: &I,
) -> Result<super::Surface, crate::NotSupportedError> {
use objc2_foundation::NSObjectProtocol as _;
Ok(match window.window_handle().unwrap().as_raw() {
#[cfg(target_os = "ios")]
raw_window_handle::RawWindowHandle::UiKit(handle) => unsafe {
let view =
Retained::retain(handle.ui_view.as_ptr() as *mut objc2_ui_kit::UIView).unwrap();
let main_layer = view.layer();
let render_layer = if main_layer.isKindOfClass(&CAMetalLayer::class()) {
Retained::cast_unchecked(main_layer)
} else {
use objc2_ui_kit::UIViewAutoresizing as Var;
let new_layer = CAMetalLayer::new();
new_layer.setFrame(main_layer.frame());
main_layer.addSublayer(&new_layer);
view.setAutoresizingMask(
Var::FlexibleLeftMargin
| Var::FlexibleWidth
| Var::FlexibleRightMargin
| Var::FlexibleTopMargin
| Var::FlexibleHeight
| Var::FlexibleBottomMargin,
);
if let Some(scene) = view.window().and_then(|w| w.windowScene()) {
new_layer.setContentsScale(scene.screen().nativeScale());
}
new_layer
};
super::Surface {
view: Some(Retained::cast_unchecked(view)),
render_layer,
info: SURFACE_INFO,
}
},
#[cfg(target_os = "macos")]
raw_window_handle::RawWindowHandle::AppKit(handle) => unsafe {
let view = Retained::retain(handle.ns_view.as_ptr() as *mut objc2_app_kit::NSView)
.unwrap();
let main_layer = view.layer();
let render_layer = if main_layer
.as_ref()
.map_or(false, |layer| layer.isKindOfClass(&CAMetalLayer::class()))
{
Retained::cast_unchecked(main_layer.unwrap())
} else {
let new_layer = CAMetalLayer::new();
if let Some(layer) = main_layer {
new_layer.setFrame(layer.frame());
}
view.setLayer(Some(&new_layer));
view.setWantsLayer(true);
new_layer.setContentsGravity(objc2_quartz_core::kCAGravityTopLeft);
if let Some(window) = view.window() {
new_layer.setContentsScale(window.backingScaleFactor());
}
new_layer
};
super::Surface {
view: Some(view.downcast().unwrap()),
render_layer,
info: SURFACE_INFO,
}
},
_ => return Err(crate::NotSupportedError::PlatformNotSupported),
})
}
pub fn destroy_surface(&self, surface: &mut super::Surface) {
surface.view = None;
}
pub fn reconfigure_surface(&self, surface: &mut super::Surface, config: crate::SurfaceConfig) {
let device = self.device.lock().unwrap();
surface.info = crate::SurfaceInfo {
format: match config.color_space {
crate::ColorSpace::Linear => crate::TextureFormat::Bgra8UnormSrgb,
crate::ColorSpace::Srgb => crate::TextureFormat::Bgra8Unorm,
},
alpha: if config.transparent {
crate::AlphaMode::PostMultiplied
} else {
crate::AlphaMode::Ignored
},
};
let vsync = match config.display_sync {
crate::DisplaySync::Block => true,
crate::DisplaySync::Recent | crate::DisplaySync::Tear => false,
};
unsafe {
surface.render_layer.setOpaque(!config.transparent);
surface.render_layer.setDevice(Some(device.as_ref()));
surface
.render_layer
.setPixelFormat(super::map_texture_format(surface.info.format));
surface
.render_layer
.setFramebufferOnly(config.usage == crate::TextureUsage::TARGET);
surface.render_layer.setMaximumDrawableCount(3);
surface.render_layer.setDrawableSize(CGSize {
width: config.size.width as f64,
height: config.size.height as f64,
});
surface.render_layer.setDisplaySyncEnabled(vsync);
}
}
}