use gpui_wgpu::{WgpuContext, wgpu};
use objc2::{
msg_send,
rc::Retained,
runtime::{AnyClass, AnyObject},
};
use super::Runtime;
use crate::{GpuiError, platform::window::RawWindow};
impl Runtime {
pub(super) fn seed_metal_context(&self, raw: RawWindow) -> Result<(), GpuiError> {
if self.platform.gpu_context.borrow().is_some() {
return Ok(());
}
let context = context(Box::new(raw))?;
*self.platform.gpu_context.borrow_mut() = Some(context);
Ok(())
}
}
fn context(display: Box<dyn wgpu::wgt::WgpuHasDisplayHandle>) -> Result<WgpuContext, GpuiError> {
let init = |e: String| GpuiError::RendererInitialization(e);
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::METAL,
flags: wgpu::InstanceFlags::default(),
backend_options: wgpu::BackendOptions::default(),
memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(),
display: Some(display),
});
let layer = metal_layer().ok_or_else(|| init("no CAMetalLayer".into()))?;
let surface = unsafe {
instance.create_surface_unsafe(wgpu::SurfaceTargetUnsafe::CoreAnimationLayer(
Retained::as_ptr(&layer) as *mut std::ffi::c_void,
))
}
.map_err(|e| init(format!("Failed to create Metal surface: {e}")))?;
WgpuContext::new(instance, &surface, None).map_err(|e| init(format!("{e:#}")))
}
fn metal_layer() -> Option<Retained<AnyObject>> {
let class = AnyClass::get(c"CAMetalLayer")?;
unsafe { msg_send![class, new] }
}
#[cfg(all(test, target_os = "macos"))]
mod tests {
use gpui_wgpu::wgpu;
use raw_window_handle::{DisplayHandle, HandleError, HasDisplayHandle};
#[derive(Debug)]
struct AppKit;
impl HasDisplayHandle for AppKit {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
Ok(DisplayHandle::appkit())
}
}
#[test]
fn creates_the_context_on_metal() {
let context = super::context(Box::new(AppKit)).expect("a Metal context");
assert_eq!(context.adapter.get_info().backend, wgpu::Backend::Metal);
}
}