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}