Skip to main content

apple_mps/
state.rs

1use crate::ffi;
2use apple_metal::{CommandBuffer as MetalCommandBuffer, MetalDevice};
3use core::ffi::c_void;
4use core::ptr;
5
6/// `MPSStateResourceType` constants.
7pub mod state_resource_type {
8    /// Wraps a `MPSStateResourceType` raw value.
9    pub const NONE: usize = 0;
10    /// Wraps a `MPSStateResourceType` raw value.
11    pub const BUFFER: usize = 1;
12    /// Wraps a `MPSStateResourceType` raw value.
13    pub const TEXTURE: usize = 2;
14}
15
16/// Plain-Rust view of `MPSStateTextureInfo`.
17#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
18pub struct StateTextureInfo {
19    /// Corresponds to the `width` field on `MPSStateTextureInfo`.
20    pub width: usize,
21    /// Corresponds to the `height` field on `MPSStateTextureInfo`.
22    pub height: usize,
23    /// Corresponds to the `depth` field on `MPSStateTextureInfo`.
24    pub depth: usize,
25    /// Corresponds to the `array_length` field on `MPSStateTextureInfo`.
26    pub array_length: usize,
27    /// Corresponds to the `pixel_format` field on `MPSStateTextureInfo`.
28    pub pixel_format: usize,
29    /// Corresponds to the `texture_type` field on `MPSStateTextureInfo`.
30    pub texture_type: usize,
31    /// Corresponds to the `usage` field on `MPSStateTextureInfo`.
32    pub usage: usize,
33}
34
35macro_rules! opaque_handle {
36    ($name:ident, $doc:expr) => {
37        #[doc = $doc]
38        pub struct $name {
39            ptr: *mut c_void,
40        }
41
42        // SAFETY: MPS handles are opaque pointers to thread-safe Swift/ObjC objects.
43        unsafe impl Send for $name {}
44        // SAFETY: MPS handles are opaque pointers to thread-safe Swift/ObjC objects.
45        unsafe impl Sync for $name {}
46
47        impl Drop for $name {
48            fn drop(&mut self) {
49                if !self.ptr.is_null() {
50                    // SAFETY: `ptr` is a +1 retained MPS object owned by this wrapper.
51                    unsafe { ffi::mps_object_release(self.ptr) };
52                    self.ptr = ptr::null_mut();
53                }
54            }
55        }
56
57        impl $name {
58            /// Returns the retained Objective-C pointer backing this wrapper.
59            #[must_use]
60            pub const fn as_ptr(&self) -> *mut c_void {
61                self.ptr
62            }
63        }
64    };
65}
66
67opaque_handle!(StateResourceList, "Wraps `MPSStateResourceList`.");
68impl StateResourceList {
69    /// Wraps a constructor on `MPSStateResourceList`.
70    #[must_use]
71    pub fn new() -> Option<Self> {
72        // SAFETY: This function returns a new StateResourceList or null.
73        let ptr = unsafe { ffi::mps_state_resource_list_new() };
74        if ptr.is_null() {
75            None
76        } else {
77            Some(Self { ptr })
78        }
79    }
80
81    /// Wraps the corresponding `MPSStateResourceList` method.
82    pub fn append_buffer(&self, size: usize) {
83        // SAFETY: self.ptr is a valid StateResourceList.
84        unsafe { ffi::mps_state_resource_list_append_buffer(self.ptr, size) };
85    }
86}
87
88opaque_handle!(State, "Wraps `MPSState`.");
89impl State {
90    /// Wraps a constructor on `MPSState`.
91    #[must_use]
92    pub fn temporary(command_buffer: &MetalCommandBuffer) -> Option<Self> {
93        // SAFETY: command_buffer pointer is valid for the call.
94        let ptr = unsafe { ffi::mps_state_temporary_new(command_buffer.as_ptr()) };
95        if ptr.is_null() {
96            None
97        } else {
98            Some(Self { ptr })
99        }
100    }
101
102    /// Wraps a constructor on `MPSState`.
103    #[must_use]
104    pub fn temporary_with_buffer_size(
105        command_buffer: &MetalCommandBuffer,
106        buffer_size: usize,
107    ) -> Option<Self> {
108        let ptr = unsafe {
109            ffi::mps_state_temporary_new_with_buffer_size(command_buffer.as_ptr(), buffer_size)
110        };
111        if ptr.is_null() {
112            None
113        } else {
114            Some(Self { ptr })
115        }
116    }
117
118    /// Wraps a constructor on `MPSState`.
119    #[must_use]
120    pub fn new_with_buffer_size(device: &MetalDevice, buffer_size: usize) -> Option<Self> {
121        // SAFETY: device pointer is valid for the call.
122        let ptr = unsafe { ffi::mps_state_new_with_buffer_size(device.as_ptr(), buffer_size) };
123        if ptr.is_null() {
124            None
125        } else {
126            Some(Self { ptr })
127        }
128    }
129
130    /// Wraps a constructor on `MPSState`.
131    #[must_use]
132    pub fn new_with_resource_list(
133        device: &MetalDevice,
134        resource_list: &StateResourceList,
135    ) -> Option<Self> {
136        let ptr = unsafe {
137            ffi::mps_state_new_with_resource_list(device.as_ptr(), resource_list.as_ptr())
138        };
139        if ptr.is_null() {
140            None
141        } else {
142            Some(Self { ptr })
143        }
144    }
145
146    /// Wraps a constructor on `MPSState`.
147    #[must_use]
148    pub fn temporary_with_resource_list(
149        command_buffer: &MetalCommandBuffer,
150        resource_list: &StateResourceList,
151    ) -> Option<Self> {
152        let ptr = unsafe {
153            ffi::mps_state_temporary_new_with_resource_list(
154                command_buffer.as_ptr(),
155                resource_list.as_ptr(),
156            )
157        };
158        if ptr.is_null() {
159            None
160        } else {
161            Some(Self { ptr })
162        }
163    }
164
165    /// Wraps the corresponding `MPSState` method.
166    #[must_use]
167    pub fn resource_count(&self) -> usize {
168        // SAFETY: self.ptr is a valid State object.
169        unsafe { ffi::mps_state_resource_count(self.ptr) }
170    }
171
172    /// Wraps the corresponding `MPSState` method.
173    #[must_use]
174    pub fn read_count(&self) -> usize {
175        // SAFETY: self.ptr is a valid State object.
176        unsafe { ffi::mps_state_read_count(self.ptr) }
177    }
178
179    /// Wraps the corresponding `MPSState` setter.
180    pub fn set_read_count(&self, count: usize) {
181        // SAFETY: self.ptr is a valid State object.
182        unsafe { ffi::mps_state_set_read_count(self.ptr, count) };
183    }
184
185    /// Wraps the corresponding `MPSState` method.
186    #[must_use]
187    pub fn is_temporary(&self) -> bool {
188        unsafe { ffi::mps_state_is_temporary(self.ptr) }
189    }
190
191    /// Wraps the corresponding `MPSState` method.
192    #[must_use]
193    pub fn buffer_size_at_index(&self, index: usize) -> usize {
194        unsafe { ffi::mps_state_buffer_size_at_index(self.ptr, index) }
195    }
196
197    /// Wraps the corresponding `MPSState` method.
198    #[must_use]
199    pub fn texture_info_at_index(&self, index: usize) -> StateTextureInfo {
200        let mut width = 0;
201        let mut height = 0;
202        let mut depth = 0;
203        let mut array_length = 0;
204        let mut pixel_format = 0;
205        let mut texture_type = 0;
206        let mut usage = 0;
207        unsafe {
208            ffi::mps_state_texture_info(
209                self.ptr,
210                index,
211                &mut width,
212                &mut height,
213                &mut depth,
214                &mut array_length,
215                &mut pixel_format,
216                &mut texture_type,
217                &mut usage,
218            );
219        };
220        StateTextureInfo {
221            width,
222            height,
223            depth,
224            array_length,
225            pixel_format,
226            texture_type,
227            usage,
228        }
229    }
230
231    /// Wraps the corresponding `MPSState` method.
232    #[must_use]
233    pub fn resource_type_at_index(&self, index: usize) -> usize {
234        unsafe { ffi::mps_state_resource_type_at_index(self.ptr, index) }
235    }
236
237    /// Wraps the corresponding `MPSState` method.
238    pub fn synchronize_on_command_buffer(&self, command_buffer: &MetalCommandBuffer) {
239        unsafe { ffi::mps_state_synchronize_on_command_buffer(self.ptr, command_buffer.as_ptr()) };
240    }
241
242    /// Wraps the corresponding `MPSState` method.
243    #[must_use]
244    pub fn resource_size(&self) -> usize {
245        unsafe { ffi::mps_state_resource_size(self.ptr) }
246    }
247}
248
249/// Calls `MPSStateBatchIncrementReadCount` for the provided `MPSState` values.
250#[must_use]
251pub fn state_batch_increment_read_count(states: &[&State], amount: isize) -> usize {
252    let handles: Vec<_> = states.iter().map(|state| state.as_ptr()).collect();
253    let handles_ptr = if handles.is_empty() {
254        ptr::null()
255    } else {
256        handles.as_ptr()
257    };
258    unsafe { ffi::mps_state_batch_increment_read_count(handles_ptr, handles.len(), amount) }
259}
260
261/// Calls `MPSStateBatchResourceSize` for the provided `MPSState` values.
262#[must_use]
263pub fn state_batch_resource_size(states: &[&State]) -> usize {
264    let handles: Vec<_> = states.iter().map(|state| state.as_ptr()).collect();
265    let handles_ptr = if handles.is_empty() {
266        ptr::null()
267    } else {
268        handles.as_ptr()
269    };
270    unsafe { ffi::mps_state_batch_resource_size(handles_ptr, handles.len()) }
271}
272
273/// Calls `MPSStateBatchSynchronize` for the provided `MPSState` values.
274pub fn state_batch_synchronize(states: &[&State], command_buffer: &MetalCommandBuffer) {
275    let handles: Vec<_> = states.iter().map(|state| state.as_ptr()).collect();
276    let handles_ptr = if handles.is_empty() {
277        ptr::null()
278    } else {
279        handles.as_ptr()
280    };
281    unsafe {
282        ffi::mps_state_batch_synchronize(handles_ptr, handles.len(), command_buffer.as_ptr());
283    };
284}