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