Skip to main content

apple_cf/cv/
metal_texture_cache.rs

1//! `CVMetalTextureCache` wrapper.
2//!
3#![allow(clippy::missing_panics_doc)]
4
5//! ```rust,no_run
6//! use apple_cf::cv::CVMetalTextureCache;
7//!
8//! if let Some(cache) = CVMetalTextureCache::system_default() {
9//!     cache.flush();
10//! }
11//! ```
12
13use crate::cv::CVPixelBuffer;
14use crate::ffi;
15use std::ffi::c_void;
16use std::fmt;
17
18#[link(name = "CoreVideo", kind = "framework")]
19extern "C" {
20    fn CVMetalTextureCacheGetTypeID() -> usize;
21    fn CVMetalTextureGetTypeID() -> usize;
22    fn CVMetalTextureCacheCreate(
23        allocator: *const c_void,
24        cache_attributes: *const c_void,
25        metal_device: *mut c_void,
26        texture_attributes: *const c_void,
27        cache_out: *mut *mut c_void,
28    ) -> i32;
29    fn CVMetalTextureCacheCreateTextureFromImage(
30        allocator: *const c_void,
31        texture_cache: *mut c_void,
32        source_image: *mut c_void,
33        texture_attributes: *const c_void,
34        pixel_format: usize,
35        width: usize,
36        height: usize,
37        plane_index: usize,
38        texture_out: *mut *mut c_void,
39    ) -> i32;
40    fn CVMetalTextureGetTexture(image: *mut c_void) -> *mut c_void;
41}
42
43const CV_RETURN_INVALID_ARGUMENT: i32 = -6661;
44const CV_RETURN_ALLOCATION_FAILED: i32 = -6662;
45
46/// Owned wrapper around `CVMetalTextureCacheRef`.
47pub struct CVMetalTextureCache(*mut c_void);
48
49impl CVMetalTextureCache {
50    /// Create a texture cache using the system-default Metal device.
51    #[must_use]
52    pub fn system_default() -> Option<Self> {
53        let ptr = unsafe { ffi::cv_metal_texture_cache_create_system_default() };
54        unsafe { Self::from_raw(ptr) }
55    }
56
57    /// Adopts a +1 retained `CVMetalTextureCacheRef` and returns `None` for null.
58    ///
59    /// # Safety
60    ///
61    /// A non-null `ptr` must be a live `CVMetalTextureCacheRef` of the exact
62    /// type carrying one retain transferred to this wrapper. The caller must
63    /// not release or separately adopt that transferred retain.
64    #[must_use]
65    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
66        if ptr.is_null() {
67            None
68        } else {
69            Some(Self(ptr))
70        }
71    }
72
73    /// Retains a +0 borrowed cache pointer and returns an owned wrapper.
74    ///
75    /// # Safety
76    ///
77    /// A non-null `ptr` must be a live `CVMetalTextureCacheRef` of the exact
78    /// type for the duration of the retain call.
79    #[must_use]
80    pub unsafe fn from_raw_borrowed(ptr: *mut c_void) -> Option<Self> {
81        if ptr.is_null() {
82            None
83        } else {
84            let retained = unsafe { ffi::cf_type_retain(ptr) };
85            unsafe { Self::from_raw(retained) }
86        }
87    }
88
89    /// Borrow the raw +0 cache pointer while `self` remains alive.
90    #[must_use]
91    pub const fn as_ptr(&self) -> *mut c_void {
92        self.0
93    }
94
95    /// Core Video type identifier.
96    #[must_use]
97    pub fn type_id() -> usize {
98        unsafe { CVMetalTextureCacheGetTypeID() }
99    }
100
101    /// Flush pending cached textures.
102    pub fn flush(&self) {
103        unsafe { ffi::cv_metal_texture_cache_flush(self.0) };
104    }
105
106    #[allow(clippy::missing_safety_doc, clippy::missing_errors_doc)]
107    pub unsafe fn with_device(device: *mut c_void) -> Result<Self, i32> {
108        if device.is_null() {
109            return Err(CV_RETURN_INVALID_ARGUMENT);
110        }
111        let mut cache = std::ptr::null_mut();
112        let status = unsafe {
113            CVMetalTextureCacheCreate(
114                std::ptr::null(),
115                std::ptr::null(),
116                device,
117                std::ptr::null(),
118                &raw mut cache,
119            )
120        };
121        if status != 0 {
122            return Err(status);
123        }
124        unsafe { Self::from_raw(cache) }.ok_or(CV_RETURN_ALLOCATION_FAILED)
125    }
126
127    #[allow(clippy::missing_errors_doc)]
128    pub fn create_texture_from_image(
129        &self,
130        image: &CVPixelBuffer,
131        pixel_format: usize,
132        width: usize,
133        height: usize,
134        plane_index: usize,
135    ) -> Result<CVMetalTexture, i32> {
136        let mut texture = std::ptr::null_mut();
137        let status = unsafe {
138            CVMetalTextureCacheCreateTextureFromImage(
139                std::ptr::null(),
140                self.0,
141                image.as_ptr(),
142                std::ptr::null(),
143                pixel_format,
144                width,
145                height,
146                plane_index,
147                &raw mut texture,
148            )
149        };
150        if status != 0 {
151            return Err(status);
152        }
153        unsafe { CVMetalTexture::from_raw(texture) }.ok_or(CV_RETURN_ALLOCATION_FAILED)
154    }
155}
156
157pub struct CVMetalTexture(*mut c_void);
158
159unsafe impl Send for CVMetalTexture {}
160unsafe impl Sync for CVMetalTexture {}
161
162impl CVMetalTexture {
163    #[allow(clippy::missing_safety_doc)]
164    #[must_use]
165    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
166        if ptr.is_null() {
167            None
168        } else {
169            Some(Self(ptr))
170        }
171    }
172
173    #[must_use]
174    pub const fn as_ptr(&self) -> *mut c_void {
175        self.0
176    }
177
178    #[must_use]
179    pub fn type_id() -> usize {
180        unsafe { CVMetalTextureGetTypeID() }
181    }
182
183    #[must_use]
184    pub fn texture(&self) -> *mut c_void {
185        unsafe { CVMetalTextureGetTexture(self.0) }
186    }
187}
188
189crate::utils::retained::cf_retained!(
190    CVMetalTexture,
191    retain = ffi::cf_type_retain,
192    release = ffi::cf_type_release,
193);
194
195impl fmt::Debug for CVMetalTexture {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        f.debug_struct("CVMetalTexture")
198            .field("ptr", &self.0)
199            .finish()
200    }
201}
202
203crate::utils::retained::cf_retained!(
204    CVMetalTextureCache,
205    retain = ffi::cf_type_retain,
206    release = ffi::cf_type_release,
207);
208
209impl PartialEq for CVMetalTextureCache {
210    fn eq(&self, other: &Self) -> bool {
211        self.0 == other.0
212    }
213}
214
215impl Eq for CVMetalTextureCache {}
216
217impl std::hash::Hash for CVMetalTextureCache {
218    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
219        unsafe { ffi::cf_type_hash(self.0) }.hash(state);
220    }
221}
222
223impl fmt::Debug for CVMetalTextureCache {
224    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225        f.debug_struct("CVMetalTextureCache")
226            .field("ptr", &self.0)
227            .field("type_id", &Self::type_id())
228            .finish()
229    }
230}