use crate::buffer::Buffer;
use crate::command::Queue;
use crate::ffi::*;
use crate::pipeline::Pipeline;
use crate::shader::{Shader, ShaderLib};
use crate::texture::Texture;
use crate::GpuError;
use std::ffi::c_void;
pub struct Gpu {
raw: ObjcId,
}
impl Gpu {
#[mutants::skip] pub fn open() -> Result<Self, GpuError> {
let raw = unsafe { MTLCreateSystemDefaultDevice() };
if !raw.is_null() {
return Ok(Gpu { raw });
}
let devices = Self::all()?;
devices.into_iter().next().ok_or(GpuError::DeviceNotFound)
}
pub fn all() -> Result<Vec<Self>, GpuError> {
let arr = unsafe { MTLCopyAllDevices() };
if arr.is_null() {
return Err(GpuError::DeviceNotFound);
}
let count = unsafe { msg0_usize(arr, SEL_count()) };
let mut devices = Vec::with_capacity(count);
for i in 0..count {
let dev = unsafe { msg1::<NSUInteger>(arr, SEL_objectAtIndex(), i) };
if !dev.is_null() {
unsafe { retain(dev) };
devices.push(Gpu { raw: dev });
}
}
unsafe { release(arr) };
Ok(devices)
}
pub fn name(&self) -> String {
unsafe {
let ns = msg0(self.raw, SEL_name());
nsstring_to_rust(ns).unwrap_or_else(|| "unknown".into())
}
}
#[mutants::skip] pub fn has_unified_memory(&self) -> bool {
unsafe { msg0_bool(self.raw, SEL_hasUnifiedMemory()) }
}
pub fn max_buffer_length(&self) -> usize {
unsafe { msg0_usize(self.raw, SEL_maxBufferLength()) }
}
pub fn max_threads_per_threadgroup(&self) -> MTLSize {
unsafe { msg0_mtlsize(self.raw, SEL_maxThreadsPerThreadgroup()) }
}
pub fn recommended_max_working_set_size(&self) -> u64 {
unsafe { msg0_u64(self.raw, SEL_recommendedMaxWorkingSetSize()) }
}
pub fn new_command_queue(&self) -> Result<Queue, GpuError> {
let raw = unsafe { msg0(self.raw, SEL_newCommandQueue()) };
if raw.is_null() {
return Err(GpuError::QueueCreationFailed);
}
Ok(Queue::from_raw(raw))
}
pub fn buffer(&self, size: usize) -> Result<Buffer, GpuError> {
if size == 0 {
return Err(GpuError::BufferCreationFailed("size must be > 0".into()));
}
let raw = unsafe {
msg2::<NSUInteger, NSUInteger>(
self.raw,
SEL_newBufferWithLength_options(),
size,
MTLResourceStorageModeShared,
)
};
if raw.is_null() {
return Err(GpuError::BufferCreationFailed(format!("{} bytes", size)));
}
Ok(Buffer::from_raw(raw, size))
}
pub fn buffer_private(&self, size: usize) -> Result<Buffer, GpuError> {
if size == 0 {
return Err(GpuError::BufferCreationFailed("size must be > 0".into()));
}
let raw = unsafe {
msg2::<NSUInteger, NSUInteger>(
self.raw,
SEL_newBufferWithLength_options(),
size,
MTLResourceStorageModePrivate,
)
};
if raw.is_null() {
return Err(GpuError::BufferCreationFailed(format!("{} bytes", size)));
}
Ok(Buffer::from_raw_private(raw, size))
}
pub unsafe fn buffer_wrap(
&self,
ptr: *mut std::ffi::c_void,
size: usize,
) -> Result<Buffer, GpuError> {
if ptr.is_null() || size == 0 {
return Err(GpuError::BufferCreationFailed(
"null ptr or zero size".into(),
));
}
let raw = msg4::<*mut c_void, NSUInteger, NSUInteger, *const c_void>(
self.raw,
SEL_newBufferWithBytesNoCopy_length_options_deallocator(),
ptr,
size,
MTLResourceStorageModeShared,
std::ptr::null(), );
if raw.is_null() {
return Err(GpuError::BufferCreationFailed(format!(
"newBufferWithBytesNoCopy failed: ptr={:?} size={}",
ptr, size
)));
}
Ok(Buffer::from_raw(raw, size))
}
pub fn wrap(&self, block: &unimem::Block) -> Result<Buffer, GpuError> {
unsafe { self.buffer_wrap(block.address() as *mut std::ffi::c_void, block.size()) }
}
pub fn buffer_with_data(&self, data: &[u8]) -> Result<Buffer, GpuError> {
let raw = unsafe {
msg3::<*const c_void, NSUInteger, NSUInteger>(
self.raw,
SEL_newBufferWithBytes_length_options(),
data.as_ptr() as *const c_void,
data.len(),
MTLResourceStorageModeShared,
)
};
if raw.is_null() {
return Err(GpuError::BufferCreationFailed(format!(
"{} bytes",
data.len()
)));
}
Ok(Buffer::from_raw(raw, data.len()))
}
pub fn compile(&self, source: &str) -> Result<ShaderLib, GpuError> {
let ns_source = nsstring(source);
let mut error: ObjcId = std::ptr::null_mut();
let raw = unsafe {
type F = unsafe extern "C" fn(ObjcId, ObjcSel, ObjcId, ObjcId, *mut ObjcId) -> ObjcId;
let f: F = std::mem::transmute(objc_msgSend as *const c_void);
let r = f(
self.raw,
SEL_newLibraryWithSource_options_error(),
ns_source,
std::ptr::null_mut(),
&mut error,
);
CFRelease(ns_source as CFTypeRef);
r
};
if raw.is_null() {
let msg = nserror_string(error).unwrap_or_else(|| "unknown error".into());
return Err(GpuError::LibraryCompilationFailed(msg));
}
Ok(ShaderLib::from_raw(raw))
}
pub fn pipeline(&self, function: &Shader) -> Result<Pipeline, GpuError> {
let mut error: ObjcId = std::ptr::null_mut();
let raw = unsafe {
type F = unsafe extern "C" fn(ObjcId, ObjcSel, ObjcId, *mut ObjcId) -> ObjcId;
let f: F = std::mem::transmute(objc_msgSend as *const c_void);
f(
self.raw,
SEL_newComputePipelineStateWithFunction_error(),
function.as_raw(),
&mut error,
)
};
if raw.is_null() {
let msg = nserror_string(error).unwrap_or_else(|| "unknown error".into());
return Err(GpuError::PipelineCreationFailed(msg));
}
Ok(Pipeline::from_raw(raw))
}
pub unsafe fn texture(&self, desc: ObjcId) -> Result<Texture, GpuError> {
let raw = msg1::<ObjcId>(self.raw, SEL_newTextureWithDescriptor(), desc);
if raw.is_null() {
return Err(GpuError::TextureCreationFailed(
"descriptor rejected".into(),
));
}
Ok(Texture::from_raw(raw))
}
pub fn fence(&self) -> Result<crate::sync::Fence, GpuError> {
let raw = unsafe { msg0(self.raw, SEL_newFence()) };
if raw.is_null() {
return Err(GpuError::CommandBufferError("fence creation failed".into()));
}
Ok(crate::sync::Fence::from_raw(raw))
}
pub fn event(&self) -> Result<crate::sync::Event, GpuError> {
let raw = unsafe { msg0(self.raw, SEL_newEvent()) };
if raw.is_null() {
return Err(GpuError::CommandBufferError("event creation failed".into()));
}
Ok(crate::sync::Event::from_raw(raw))
}
pub fn shared_event(&self) -> Result<crate::sync::SharedEvent, GpuError> {
let raw = unsafe { msg0(self.raw, SEL_newSharedEvent()) };
if raw.is_null() {
return Err(GpuError::CommandBufferError(
"shared event creation failed".into(),
));
}
Ok(crate::sync::SharedEvent::from_raw(raw))
}
pub fn as_raw(&self) -> ObjcId {
self.raw
}
}
impl Drop for Gpu {
#[mutants::skip] fn drop(&mut self) {
unsafe { release(self.raw) };
}
}