use metal::{CommandQueue, Device, MTLResourceOptions};
use std::sync::OnceLock;
pub struct MetalDevice {
pub device: Device,
pub queue: CommandQueue,
pub name: String,
pub registry_id: u64,
pub max_working_set: u64,
pub has_unified_memory: bool,
}
impl MetalDevice {
fn new() -> Option<Self> {
let device = Device::system_default()?;
let queue = device.new_command_queue();
let name = device.name().to_string();
let registry_id = device.registry_id();
let max_working_set = device.recommended_max_working_set_size();
let has_unified_memory = device.has_unified_memory();
Some(Self {
device,
queue,
name,
registry_id,
max_working_set,
has_unified_memory,
})
}
pub fn alloc_shared(&self, bytes: usize) -> metal::Buffer {
self.device
.new_buffer(bytes as u64, MTLResourceOptions::StorageModeShared)
}
pub fn alloc_private(&self, bytes: usize) -> metal::Buffer {
self.device
.new_buffer(bytes as u64, MTLResourceOptions::StorageModePrivate)
}
}
unsafe impl Send for MetalDevice {}
unsafe impl Sync for MetalDevice {}
pub fn metal_device() -> Option<&'static MetalDevice> {
static DEVICE: OnceLock<Option<MetalDevice>> = OnceLock::new();
DEVICE.get_or_init(MetalDevice::new).as_ref()
}
#[cfg(target_os = "macos")]
pub fn drain_command_queue() {
if let Some(dev) = metal_device() {
let cb = dev.queue.new_command_buffer();
cb.commit();
cb.wait_until_completed();
}
}
#[cfg(not(target_os = "macos"))]
pub fn drain_command_queue() {}
pub fn has_metal_device() -> bool {
metal_device().is_some()
}