Skip to main content

apple_mps/
core.rs

1use crate::ffi;
2use apple_metal::{
3    CommandBuffer as MetalCommandBuffer, CommandQueue, ManuallyDropDevice, MetalBuffer, MetalDevice,
4};
5use core::ffi::c_void;
6use core::ptr;
7
8pub mod device_options {
9    pub const DEFAULT: usize = 0;
10    pub const LOW_POWER: usize = 1;
11    pub const SKIP_REMOVABLE: usize = 2;
12}
13
14macro_rules! opaque_handle {
15    ($name:ident) => {
16        pub struct $name {
17            ptr: *mut c_void,
18        }
19
20        // SAFETY: MPS handles are opaque pointers to thread-safe Swift/ObjC objects.
21        unsafe impl Send for $name {}
22        // SAFETY: MPS handles are opaque pointers to thread-safe Swift/ObjC objects.
23        unsafe impl Sync for $name {}
24
25        impl Drop for $name {
26            fn drop(&mut self) {
27                if !self.ptr.is_null() {
28                    // SAFETY: `ptr` is a +1 retained MPS object owned by this wrapper.
29                    unsafe { ffi::mps_object_release(self.ptr) };
30                    self.ptr = ptr::null_mut();
31                }
32            }
33        }
34
35        impl $name {
36            #[must_use]
37            pub const fn as_ptr(&self) -> *mut c_void {
38                self.ptr
39            }
40        }
41    };
42}
43
44pub fn supports_mtl_device(device: &MetalDevice) -> bool {
45    // SAFETY: The device pointer is valid for the call and we just read the return value.
46    unsafe { ffi::mps_supports_mtl_device(device.as_ptr()) }
47}
48
49opaque_handle!(PreferredDevice);
50impl PreferredDevice {
51    #[must_use]
52    pub fn as_borrowed_device(&self) -> ManuallyDropDevice {
53        // SAFETY: The device pointer is a valid MTLDevice reference held by this wrapper.
54        unsafe { MetalDevice::from_raw_borrowed(self.ptr) }
55    }
56}
57
58#[must_use]
59pub fn preferred_device(options: usize) -> Option<PreferredDevice> {
60    // SAFETY: This function returns a +1 retained MTLDevice or null.
61    let ptr = unsafe { ffi::mps_get_preferred_device(options) };
62    if ptr.is_null() {
63        None
64    } else {
65        Some(PreferredDevice { ptr })
66    }
67}
68
69pub fn hint_temporary_memory_high_water_mark(command_buffer: &MetalCommandBuffer, bytes: usize) {
70    // SAFETY: The command buffer pointer is valid for the call.
71    unsafe { ffi::mps_hint_temporary_memory_high_water_mark(command_buffer.as_ptr(), bytes) };
72}
73
74pub use crate::generated::core::*;
75
76pub fn set_heap_cache_duration(command_buffer: &MetalCommandBuffer, seconds: f64) {
77    // SAFETY: The command buffer pointer is valid for the call.
78    unsafe { ffi::mps_set_heap_cache_duration(command_buffer.as_ptr(), seconds) };
79}
80
81opaque_handle!(Predicate);
82impl Predicate {
83    #[must_use]
84    pub fn new_with_buffer(buffer: &MetalBuffer, offset: usize) -> Option<Self> {
85        // SAFETY: This function returns a +1 retained predicate or null.
86        let ptr = unsafe { ffi::mps_predicate_new_with_buffer(buffer.as_ptr(), offset) };
87        if ptr.is_null() {
88            None
89        } else {
90            Some(Self { ptr })
91        }
92    }
93
94    #[must_use]
95    pub fn new_with_device(device: &MetalDevice) -> Option<Self> {
96        // SAFETY: This function returns a +1 retained predicate or null.
97        let ptr = unsafe { ffi::mps_predicate_new_with_device(device.as_ptr()) };
98        if ptr.is_null() {
99            None
100        } else {
101            Some(Self { ptr })
102        }
103    }
104
105    #[must_use]
106    pub fn predicate_offset(&self) -> usize {
107        // SAFETY: The predicate pointer is valid for the call.
108        unsafe { ffi::mps_predicate_offset(self.ptr) }
109    }
110}
111
112opaque_handle!(CommandBuffer);
113impl CommandBuffer {
114    #[must_use]
115    pub fn new_with_command_buffer(command_buffer: &MetalCommandBuffer) -> Option<Self> {
116        // SAFETY: This function returns a +1 retained command buffer or null.
117        let ptr =
118            unsafe { ffi::mps_command_buffer_new_with_command_buffer(command_buffer.as_ptr()) };
119        if ptr.is_null() {
120            None
121        } else {
122            Some(Self { ptr })
123        }
124    }
125
126    #[must_use]
127    pub fn from_command_queue(command_queue: &CommandQueue) -> Option<Self> {
128        // SAFETY: This function returns a +1 retained command buffer or null.
129        let ptr = unsafe { ffi::mps_command_buffer_from_command_queue(command_queue.as_ptr()) };
130        if ptr.is_null() {
131            None
132        } else {
133            Some(Self { ptr })
134        }
135    }
136
137    pub fn set_predicate(&self, predicate: &Predicate) {
138        // SAFETY: Both pointers are valid for the call.
139        unsafe { ffi::mps_command_buffer_set_predicate(self.ptr, predicate.as_ptr()) };
140    }
141
142    pub fn clear_predicate(&self) {
143        // SAFETY: The command buffer pointer is valid for the call.
144        unsafe { ffi::mps_command_buffer_clear_predicate(self.ptr) };
145    }
146
147    pub fn prefetch_heap_for_workload_size(&self, size: usize) {
148        // SAFETY: The command buffer pointer is valid for the call.
149        unsafe { ffi::mps_command_buffer_prefetch_heap(self.ptr, size) };
150    }
151
152    pub fn commit_and_continue(&self) {
153        // SAFETY: The command buffer pointer is valid for the call.
154        unsafe { ffi::mps_command_buffer_commit_and_continue(self.ptr) };
155    }
156}