Skip to main content

mtl_gpu/
drawable.rs

1//! Metal drawable interface.
2//!
3//! Corresponds to `Metal/MTLDrawable.hpp`.
4//!
5//! The `Drawable` protocol represents a displayable resource that can be rendered to.
6
7use std::ffi::c_void;
8use std::ptr::NonNull;
9
10use mtl_foundation::{Referencing, UInteger};
11use mtl_sys::{msg_send_0, msg_send_1, sel};
12
13/// Time interval type (CFTimeInterval is a double).
14pub type TimeInterval = f64;
15
16/// A displayable resource that can be rendered to.
17///
18/// C++ equivalent: `MTL::Drawable`
19///
20/// The drawable protocol represents a displayable resource. Drawables are typically
21/// obtained from a CAMetalLayer and are used as render targets.
22#[repr(transparent)]
23pub struct Drawable(pub(crate) NonNull<c_void>);
24
25impl Drawable {
26    /// Create a Drawable from a raw pointer.
27    ///
28    /// # Safety
29    ///
30    /// The pointer must be a valid Metal drawable object.
31    #[inline]
32    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
33        NonNull::new(ptr).map(Self)
34    }
35
36    /// Get the raw pointer to the drawable.
37    #[inline]
38    pub fn as_raw(&self) -> *mut c_void {
39        self.0.as_ptr()
40    }
41
42    /// Present the drawable to the screen.
43    ///
44    /// C++ equivalent: `void present()`
45    pub fn present(&self) {
46        unsafe {
47            let _: () = msg_send_0(self.as_ptr(), sel!(present));
48        }
49    }
50
51    /// Present the drawable at the specified time.
52    ///
53    /// C++ equivalent: `void presentAtTime(CFTimeInterval presentationTime)`
54    ///
55    /// # Arguments
56    ///
57    /// * `presentation_time` - The host time at which to present the drawable.
58    pub fn present_at_time(&self, presentation_time: TimeInterval) {
59        unsafe {
60            let _: () = msg_send_1(self.as_ptr(), sel!(presentAtTime:), presentation_time);
61        }
62    }
63
64    /// Present the drawable after a minimum duration.
65    ///
66    /// C++ equivalent: `void presentAfterMinimumDuration(CFTimeInterval duration)`
67    ///
68    /// # Arguments
69    ///
70    /// * `duration` - The minimum duration before presenting.
71    pub fn present_after_minimum_duration(&self, duration: TimeInterval) {
72        unsafe {
73            let _: () = msg_send_1(self.as_ptr(), sel!(presentAfterMinimumDuration:), duration);
74        }
75    }
76
77    /// Get the unique identifier for this drawable.
78    ///
79    /// C++ equivalent: `NS::UInteger drawableID() const`
80    pub fn drawable_id(&self) -> UInteger {
81        unsafe { msg_send_0(self.as_ptr(), sel!(drawableID)) }
82    }
83
84    /// Get the time at which this drawable was presented.
85    ///
86    /// C++ equivalent: `CFTimeInterval presentedTime() const`
87    ///
88    /// Returns 0.0 if the drawable has not been presented yet.
89    pub fn presented_time(&self) -> TimeInterval {
90        unsafe { msg_send_0(self.as_ptr(), sel!(presentedTime)) }
91    }
92
93    /// Add a handler to be called when the drawable is presented.
94    ///
95    /// C++ equivalent: `void addPresentedHandler(void (^)(Drawable*))`
96    ///
97    /// The handler is called when the drawable has been presented to the display.
98    pub fn add_presented_handler<F>(&self, handler: F)
99    where
100        F: Fn(&Drawable) + Send + 'static,
101    {
102        let block = mtl_sys::OneArgBlock::from_fn(move |drawable_ptr: *mut c_void| {
103            unsafe {
104                if let Some(drawable) = Drawable::from_raw(drawable_ptr) {
105                    handler(&drawable);
106                    // Don't drop - Metal owns this reference
107                    std::mem::forget(drawable);
108                }
109            }
110        });
111
112        unsafe {
113            msg_send_1::<(), *const c_void>(
114                self.as_ptr(),
115                sel!(addPresentedHandler:),
116                block.as_ptr(),
117            );
118        }
119
120        // The block is retained by Metal
121        std::mem::forget(block);
122    }
123}
124
125impl Clone for Drawable {
126    fn clone(&self) -> Self {
127        unsafe {
128            mtl_sys::msg_send_0::<*mut c_void>(self.as_ptr(), mtl_sys::sel!(retain));
129        }
130        Self(self.0)
131    }
132}
133
134impl Drop for Drawable {
135    fn drop(&mut self) {
136        unsafe {
137            mtl_sys::msg_send_0::<()>(self.as_ptr(), mtl_sys::sel!(release));
138        }
139    }
140}
141
142impl Referencing for Drawable {
143    #[inline]
144    fn as_ptr(&self) -> *const c_void {
145        self.0.as_ptr()
146    }
147}
148
149// SAFETY: Drawable is a reference-counted object that is thread-safe
150unsafe impl Send for Drawable {}
151unsafe impl Sync for Drawable {}
152
153impl std::fmt::Debug for Drawable {
154    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155        f.debug_struct("Drawable")
156            .field("drawable_id", &self.drawable_id())
157            .finish()
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn test_drawable_size() {
167        // Drawable should be pointer-sized
168        assert_eq!(
169            std::mem::size_of::<Drawable>(),
170            std::mem::size_of::<*mut c_void>()
171        );
172    }
173}