Skip to main content

mtl_sys/
functions.rs

1//! Metal C function exports.
2//!
3//! These are standalone C functions exported by the Metal framework,
4//! not Objective-C methods. They must be linked directly.
5//!
6//! # C++ Equivalent
7//!
8//! From metal-cpp MTLDevice.hpp:
9//! ```cpp
10//! Device*    CreateSystemDefaultDevice();
11//! NS::Array* CopyAllDevices();
12//! NS::Array* CopyAllDevicesWithObserver(NS::Object** pOutObserver, MTL::DeviceNotificationHandlerBlock handler);
13//! void       RemoveDeviceObserver(const NS::Object* pObserver);
14//! ```
15//!
16//! # Device Creation
17//!
18//! - `MTLCreateSystemDefaultDevice()` - Creates the system default Metal device
19//! - `MTLCopyAllDevices()` - Copies all available Metal devices (macOS only)
20//! - `MTLCopyAllDevicesWithObserver()` - Copies devices with hot-plug notifications
21//! - `MTLRemoveDeviceObserver()` - Removes a device observer
22//!
23//! # IO Compression
24//!
25//! - `MTLIOCompressionContextDefaultChunkSize()` - Default chunk size for compression
26//! - `MTLIOCreateCompressionContext()` - Creates a compression context
27//! - `MTLIOCompressionContextAppendData()` - Appends data to compression context
28//! - `MTLIOFlushAndDestroyCompressionContext()` - Flushes and destroys context
29
30use std::ffi::c_void;
31
32// =============================================================================
33// Device Creation Functions
34// =============================================================================
35
36/// Opaque type for device observers (used with hot-plug notifications).
37pub type DeviceObserver = *mut c_void;
38
39// Link against the Metal framework for device creation functions
40#[link(name = "Metal", kind = "framework")]
41unsafe extern "C" {
42    /// Create the system default Metal device.
43    ///
44    /// Returns a pointer to the default `MTLDevice`, or null if no Metal
45    /// device is available. The returned object is autoreleased.
46    ///
47    /// # C++ Equivalent
48    ///
49    /// ```cpp
50    /// MTL::Device* MTL::CreateSystemDefaultDevice();
51    /// ```
52    ///
53    /// # Returns
54    ///
55    /// A pointer to the default Metal device, or null.
56    pub fn MTLCreateSystemDefaultDevice() -> *mut c_void;
57
58    /// Copy all available Metal devices.
59    ///
60    /// Returns an `NSArray` containing all available Metal devices.
61    /// This function is only available on macOS.
62    ///
63    /// # C++ Equivalent
64    ///
65    /// ```cpp
66    /// NS::Array* MTL::CopyAllDevices();
67    /// ```
68    ///
69    /// # Returns
70    ///
71    /// A pointer to an `NSArray` of Metal devices. The caller owns the array.
72    #[cfg(target_os = "macos")]
73    pub fn MTLCopyAllDevices() -> *mut c_void;
74
75    /// Copy all devices with a notification observer.
76    ///
77    /// Returns an `NSArray` containing all available Metal devices and
78    /// sets up a notification observer for device hot-plug events.
79    ///
80    /// # C++ Equivalent
81    ///
82    /// ```cpp
83    /// NS::Array* MTL::CopyAllDevicesWithObserver(
84    ///     NS::Object** pOutObserver,
85    ///     MTL::DeviceNotificationHandlerBlock handler
86    /// );
87    /// ```
88    ///
89    /// # Arguments
90    ///
91    /// * `observer` - Output parameter that receives the observer reference
92    /// * `handler` - Block called when device changes occur
93    ///
94    /// # Returns
95    ///
96    /// A pointer to an `NSArray` of Metal devices. The caller owns the array.
97    #[cfg(target_os = "macos")]
98    pub fn MTLCopyAllDevicesWithObserver(
99        observer: *mut DeviceObserver,
100        handler: *const c_void, // Block: void(^)(MTLDevice*, MTLDeviceNotificationName)
101    ) -> *mut c_void;
102
103    /// Remove a device observer.
104    ///
105    /// Stops receiving device hot-plug notifications.
106    ///
107    /// # C++ Equivalent
108    ///
109    /// ```cpp
110    /// void MTL::RemoveDeviceObserver(const NS::Object* pObserver);
111    /// ```
112    ///
113    /// # Arguments
114    ///
115    /// * `observer` - The observer to remove
116    #[cfg(target_os = "macos")]
117    pub fn MTLRemoveDeviceObserver(observer: DeviceObserver);
118}
119
120// =============================================================================
121// IO Compression Types
122// =============================================================================
123
124/// IO compression method.
125///
126/// # C++ Equivalent
127///
128/// ```cpp
129/// _MTL_ENUM(NS::Integer, IOCompressionMethod) {
130///     IOCompressionMethodZlib = 0,
131///     IOCompressionMethodLZFSE = 1,
132///     IOCompressionMethodLZ4 = 2,
133///     IOCompressionMethodLZMA = 3,
134///     IOCompressionMethodLZBitmap = 4,
135/// };
136/// ```
137#[repr(i64)]
138#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
139pub enum IOCompressionMethod {
140    /// Zlib compression.
141    Zlib = 0,
142    /// LZFSE compression (Apple's fast compression).
143    LZFSE = 1,
144    /// LZ4 compression (very fast).
145    LZ4 = 2,
146    /// LZMA compression (high ratio).
147    LZMA = 3,
148    /// LZ Bitstream compression.
149    LZBitmap = 4,
150}
151
152/// IO compression status.
153#[repr(i64)]
154#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
155pub enum IOCompressionStatus {
156    /// Compression completed successfully.
157    Complete = 0,
158    /// Compression encountered an error.
159    Error = -1,
160}
161
162/// Opaque compression context handle.
163pub type IOCompressionContext = *mut c_void;
164
165// =============================================================================
166// IO Compression Functions
167// =============================================================================
168
169#[link(name = "Metal", kind = "framework")]
170unsafe extern "C" {
171    /// Get the default chunk size for IO compression.
172    ///
173    /// # Returns
174    ///
175    /// The recommended chunk size in bytes.
176    pub fn MTLIOCompressionContextDefaultChunkSize() -> usize;
177
178    /// Create an IO compression context.
179    ///
180    /// # Arguments
181    ///
182    /// * `path` - Path to the output file (C string)
183    /// * `compression_method` - The compression method to use
184    /// * `chunk_size` - The chunk size for compression
185    ///
186    /// # Returns
187    ///
188    /// A compression context handle, or null on failure.
189    pub fn MTLIOCreateCompressionContext(
190        path: *const std::ffi::c_char,
191        compression_method: IOCompressionMethod,
192        chunk_size: usize,
193    ) -> IOCompressionContext;
194
195    /// Append data to a compression context.
196    ///
197    /// # Arguments
198    ///
199    /// * `context` - The compression context
200    /// * `data` - Pointer to the data to compress
201    /// * `size` - Size of the data in bytes
202    pub fn MTLIOCompressionContextAppendData(
203        context: IOCompressionContext,
204        data: *const c_void,
205        size: usize,
206    );
207
208    /// Flush and destroy a compression context.
209    ///
210    /// Flushes any remaining data, writes the compressed output, and
211    /// destroys the context.
212    ///
213    /// # Arguments
214    ///
215    /// * `context` - The compression context to finalize
216    ///
217    /// # Returns
218    ///
219    /// The compression status.
220    pub fn MTLIOFlushAndDestroyCompressionContext(
221        context: IOCompressionContext,
222    ) -> IOCompressionStatus;
223}
224
225// =============================================================================
226// Safe Wrapper Functions
227// =============================================================================
228
229/// Safely create the system default Metal device.
230///
231/// # Returns
232///
233/// The raw pointer to the default device, or `None` if unavailable.
234///
235/// # Safety
236///
237/// The returned pointer is autoreleased. Callers should retain it if
238/// they need to keep it beyond the current autorelease pool scope.
239#[inline]
240pub fn create_system_default_device() -> Option<*mut c_void> {
241    let ptr = unsafe { MTLCreateSystemDefaultDevice() };
242    if ptr.is_null() { None } else { Some(ptr) }
243}
244
245/// Safely copy all available Metal devices (macOS only).
246///
247/// # Returns
248///
249/// The raw pointer to an NSArray of devices, or `None` if unavailable.
250///
251/// # Safety
252///
253/// The caller owns the returned array and must release it.
254#[cfg(target_os = "macos")]
255#[inline]
256pub fn copy_all_devices() -> Option<*mut c_void> {
257    let ptr = unsafe { MTLCopyAllDevices() };
258    if ptr.is_null() { None } else { Some(ptr) }
259}
260
261/// Get the default IO compression chunk size.
262#[inline]
263pub fn io_compression_default_chunk_size() -> usize {
264    unsafe { MTLIOCompressionContextDefaultChunkSize() }
265}
266
267// =============================================================================
268// Tests
269// =============================================================================
270
271#[cfg(test)]
272mod tests {
273    use super::*;
274
275    #[test]
276    fn test_create_system_default_device() {
277        // This may return None in headless environments
278        let device = create_system_default_device();
279        // Just check it doesn't crash
280        let _ = device;
281    }
282
283    #[cfg(target_os = "macos")]
284    #[test]
285    fn test_copy_all_devices() {
286        let devices = copy_all_devices();
287        // Just check it doesn't crash
288        let _ = devices;
289    }
290
291    #[test]
292    fn test_compression_chunk_size() {
293        let size = io_compression_default_chunk_size();
294        // Should be a reasonable size (typically 64KB or larger)
295        assert!(size > 0);
296    }
297}