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