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}