Skip to main content

mtl_gpu/mtl4/
command_allocator.rs

1//! MTL4 CommandAllocator implementation.
2//!
3//! Corresponds to `Metal/MTL4CommandAllocator.hpp`.
4
5use std::ffi::c_void;
6use std::ptr::NonNull;
7
8use mtl_foundation::Referencing;
9use mtl_sys::{msg_send_0, msg_send_1, sel};
10
11use crate::Device;
12
13// ============================================================
14// CommandAllocatorDescriptor
15// ============================================================
16
17/// Descriptor for creating a MTL4 command allocator.
18///
19/// C++ equivalent: `MTL4::CommandAllocatorDescriptor`
20#[repr(transparent)]
21pub struct CommandAllocatorDescriptor(NonNull<c_void>);
22
23impl CommandAllocatorDescriptor {
24    /// Create a CommandAllocatorDescriptor from a raw pointer.
25    #[inline]
26    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
27        NonNull::new(ptr).map(Self)
28    }
29
30    /// Get the raw pointer.
31    #[inline]
32    pub fn as_raw(&self) -> *mut c_void {
33        self.0.as_ptr()
34    }
35
36    /// Create a new command allocator descriptor.
37    pub fn new() -> Option<Self> {
38        unsafe {
39            let class = mtl_sys::Class::get("MTL4CommandAllocatorDescriptor")?;
40            let ptr: *mut c_void = msg_send_0(class.as_ptr(), sel!(alloc));
41            if ptr.is_null() {
42                return None;
43            }
44            let ptr: *mut c_void = msg_send_0(ptr, sel!(init));
45            Self::from_raw(ptr)
46        }
47    }
48
49    /// Get the label.
50    pub fn label(&self) -> Option<String> {
51        unsafe {
52            let ns_string: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
53            if ns_string.is_null() {
54                return None;
55            }
56            let c_str: *const i8 = msg_send_0(ns_string, sel!(UTF8String));
57            if c_str.is_null() {
58                return None;
59            }
60            Some(
61                std::ffi::CStr::from_ptr(c_str)
62                    .to_string_lossy()
63                    .into_owned(),
64            )
65        }
66    }
67
68    /// Set the label.
69    pub fn set_label(&self, label: &str) {
70        if let Some(ns_label) = mtl_foundation::String::from_str(label) {
71            unsafe {
72                let _: () = msg_send_1(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
73            }
74        }
75    }
76}
77
78impl Clone for CommandAllocatorDescriptor {
79    fn clone(&self) -> Self {
80        unsafe {
81            mtl_sys::msg_send_0::<*mut c_void>(self.as_ptr(), mtl_sys::sel!(retain));
82        }
83        Self(self.0)
84    }
85}
86
87impl Drop for CommandAllocatorDescriptor {
88    fn drop(&mut self) {
89        unsafe {
90            mtl_sys::msg_send_0::<()>(self.as_ptr(), mtl_sys::sel!(release));
91        }
92    }
93}
94
95impl Referencing for CommandAllocatorDescriptor {
96    #[inline]
97    fn as_ptr(&self) -> *const c_void {
98        self.0.as_ptr()
99    }
100}
101
102unsafe impl Send for CommandAllocatorDescriptor {}
103unsafe impl Sync for CommandAllocatorDescriptor {}
104
105// ============================================================
106// CommandAllocator
107// ============================================================
108
109/// Memory allocator for MTL4 command buffer recording.
110///
111/// C++ equivalent: `MTL4::CommandAllocator`
112///
113/// CommandAllocator manages memory for command buffer recording in Metal 4.
114/// It provides explicit control over memory allocation for GPU commands.
115#[repr(transparent)]
116pub struct CommandAllocator(NonNull<c_void>);
117
118impl CommandAllocator {
119    /// Create a CommandAllocator from a raw pointer.
120    #[inline]
121    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
122        NonNull::new(ptr).map(Self)
123    }
124
125    /// Get the raw pointer.
126    #[inline]
127    pub fn as_raw(&self) -> *mut c_void {
128        self.0.as_ptr()
129    }
130
131    /// Get the device that created this allocator.
132    ///
133    /// C++ equivalent: `MTL::Device* device() const`
134    pub fn device(&self) -> Option<Device> {
135        unsafe {
136            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(device));
137            Device::from_raw(ptr)
138        }
139    }
140
141    /// Get the label.
142    ///
143    /// C++ equivalent: `NS::String* label() const`
144    pub fn label(&self) -> Option<String> {
145        unsafe {
146            let ns_string: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
147            if ns_string.is_null() {
148                return None;
149            }
150            let c_str: *const i8 = msg_send_0(ns_string, sel!(UTF8String));
151            if c_str.is_null() {
152                return None;
153            }
154            Some(
155                std::ffi::CStr::from_ptr(c_str)
156                    .to_string_lossy()
157                    .into_owned(),
158            )
159        }
160    }
161
162    /// Get the current allocated size.
163    ///
164    /// C++ equivalent: `uint64_t allocatedSize()`
165    pub fn allocated_size(&self) -> u64 {
166        unsafe { msg_send_0(self.as_ptr(), sel!(allocatedSize)) }
167    }
168
169    /// Reset the allocator, freeing all allocated memory.
170    ///
171    /// C++ equivalent: `void reset()`
172    pub fn reset(&self) {
173        unsafe {
174            let _: () = msg_send_0(self.as_ptr(), sel!(reset));
175        }
176    }
177}
178
179impl Clone for CommandAllocator {
180    fn clone(&self) -> Self {
181        unsafe {
182            mtl_sys::msg_send_0::<*mut c_void>(self.as_ptr(), mtl_sys::sel!(retain));
183        }
184        Self(self.0)
185    }
186}
187
188impl Drop for CommandAllocator {
189    fn drop(&mut self) {
190        unsafe {
191            mtl_sys::msg_send_0::<()>(self.as_ptr(), mtl_sys::sel!(release));
192        }
193    }
194}
195
196impl Referencing for CommandAllocator {
197    #[inline]
198    fn as_ptr(&self) -> *const c_void {
199        self.0.as_ptr()
200    }
201}
202
203unsafe impl Send for CommandAllocator {}
204unsafe impl Sync for CommandAllocator {}
205
206impl std::fmt::Debug for CommandAllocator {
207    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208        f.debug_struct("CommandAllocator")
209            .field("allocated_size", &self.allocated_size())
210            .finish()
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217
218    #[test]
219    fn test_command_allocator_descriptor_size() {
220        assert_eq!(
221            std::mem::size_of::<CommandAllocatorDescriptor>(),
222            std::mem::size_of::<*mut c_void>()
223        );
224    }
225
226    #[test]
227    fn test_command_allocator_size() {
228        assert_eq!(
229            std::mem::size_of::<CommandAllocator>(),
230            std::mem::size_of::<*mut c_void>()
231        );
232    }
233
234    #[test]
235    fn test_command_allocator_descriptor_creation() {
236        // May fail if MTL4 is not available on the system
237        if let Some(desc) = CommandAllocatorDescriptor::new() {
238            // Basic test - descriptor should exist
239            drop(desc);
240        }
241    }
242}