Skip to main content

mtl_gpu/library/
dynamic_library.rs

1//! A dynamic Metal library that can be loaded at runtime.
2
3use std::ffi::c_void;
4use std::ptr::NonNull;
5
6use mtl_foundation::Referencing;
7use mtl_sys::{msg_send_0, msg_send_1, sel};
8
9/// A dynamic Metal library that can be loaded at runtime.
10///
11/// C++ equivalent: `MTL::DynamicLibrary`
12///
13/// Dynamic libraries allow you to load Metal shader code at runtime
14/// and link functions dynamically.
15#[repr(transparent)]
16pub struct DynamicLibrary(pub(crate) NonNull<c_void>);
17
18impl DynamicLibrary {
19    /// Create from a raw pointer.
20    ///
21    /// # Safety
22    ///
23    /// The pointer must be a valid Metal dynamic library.
24    #[inline]
25    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
26        NonNull::new(ptr).map(Self)
27    }
28
29    /// Get the raw pointer.
30    #[inline]
31    pub fn as_raw(&self) -> *mut c_void {
32        self.0.as_ptr()
33    }
34
35    /// Get the device that created this dynamic library.
36    ///
37    /// C++ equivalent: `Device* device() const`
38    pub fn device(&self) -> crate::Device {
39        unsafe {
40            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(device));
41            if ptr.is_null() {
42                panic!("dynamic library has no device");
43            }
44            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
45            crate::Device::from_raw(ptr).expect("dynamic library has no device")
46        }
47    }
48
49    /// Get the label for this dynamic library.
50    ///
51    /// C++ equivalent: `NS::String* label() const`
52    pub fn label(&self) -> Option<String> {
53        unsafe {
54            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
55            if ptr.is_null() {
56                return None;
57            }
58            let utf8_ptr: *const std::ffi::c_char =
59                mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
60            if utf8_ptr.is_null() {
61                return None;
62            }
63            let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
64            Some(c_str.to_string_lossy().into_owned())
65        }
66    }
67
68    /// Set the label for this dynamic library.
69    ///
70    /// C++ equivalent: `void setLabel(const NS::String*)`
71    pub fn set_label(&self, label: &str) {
72        if let Some(ns_label) = mtl_foundation::String::from_str(label) {
73            unsafe {
74                msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
75            }
76        }
77    }
78
79    /// Get the install name for this dynamic library.
80    ///
81    /// C++ equivalent: `NS::String* installName() const`
82    pub fn install_name(&self) -> Option<String> {
83        unsafe {
84            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(installName));
85            if ptr.is_null() {
86                return None;
87            }
88            let utf8_ptr: *const std::ffi::c_char =
89                mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
90            if utf8_ptr.is_null() {
91                return None;
92            }
93            let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
94            Some(c_str.to_string_lossy().into_owned())
95        }
96    }
97
98    /// Serialize the dynamic library to a URL.
99    ///
100    /// C++ equivalent: `bool serializeToURL(const NS::URL*, NS::Error**)`
101    pub fn serialize_to_url(
102        &self,
103        url: &mtl_foundation::Url,
104    ) -> Result<(), mtl_foundation::Error> {
105        unsafe {
106            let mut error: *mut c_void = std::ptr::null_mut();
107            let success: bool = mtl_sys::msg_send_2(
108                self.as_ptr(),
109                sel!(serializeToURL: error:),
110                url.as_ptr(),
111                &mut error as *mut _,
112            );
113            if success {
114                Ok(())
115            } else if !error.is_null() {
116                let _: *mut c_void = msg_send_0(error, sel!(retain));
117                Err(mtl_foundation::Error::from_ptr(error)
118                    .expect("error pointer should be valid"))
119            } else {
120                Err(
121                    mtl_foundation::Error::error(std::ptr::null_mut(), -1, std::ptr::null_mut())
122                        .expect("failed to create error object"),
123                )
124            }
125        }
126    }
127}
128
129impl Clone for DynamicLibrary {
130    fn clone(&self) -> Self {
131        unsafe {
132            msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
133        }
134        Self(self.0)
135    }
136}
137
138impl Drop for DynamicLibrary {
139    fn drop(&mut self) {
140        unsafe {
141            msg_send_0::<()>(self.as_ptr(), sel!(release));
142        }
143    }
144}
145
146impl Referencing for DynamicLibrary {
147    #[inline]
148    fn as_ptr(&self) -> *const c_void {
149        self.0.as_ptr()
150    }
151}
152
153unsafe impl Send for DynamicLibrary {}
154unsafe impl Sync for DynamicLibrary {}
155
156impl std::fmt::Debug for DynamicLibrary {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        f.debug_struct("DynamicLibrary")
159            .field("label", &self.label())
160            .field("install_name", &self.install_name())
161            .finish()
162    }
163}