metal/
lib.rs

1//! # ⚠️ Deprecated ⚠️
2//!
3//! Use of this crate is deprecated as the [`objc`] ecosystem of mac system bindings are unmaintained.
4//! For new development, please use [`objc2`] and [`objc2-metal`] instead. We will continue to merge basic
5//! PRs and keep things maintained, at least as long as it takes to migrate [`wgpu`] to the `objc2` ecosystem [PR 5641].
6//!
7//! [`objc`]: https://crates.io/crates/objc
8//! [`objc2`]: https://crates.io/crates/objc2
9//! [`objc2-metal`]: https://crates.io/crates/objc2-metal
10//! [`wgpu`]: https://crates.io/crates/wgpu
11//! [PR 5641]: https://github.com/gfx-rs/wgpu/pull/5641
12
13// Copyright 2023 GFX developers
14//
15// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
16// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
17// http://opensource.org/licenses/MIT>, at your option. This file may not be
18// copied, modified, or distributed except according to those terms.
19
20#![allow(deprecated)]
21#![allow(non_snake_case)]
22#![allow(non_upper_case_globals)]
23#![allow(clippy::new_without_default, clippy::new_ret_no_self)]
24
25#[macro_use]
26pub extern crate objc;
27#[macro_use]
28pub extern crate foreign_types;
29#[macro_use]
30pub extern crate paste;
31
32use std::{
33    borrow::Borrow,
34    ffi::{c_char, c_void},
35    marker::PhantomData,
36    mem,
37    ops::Deref,
38};
39
40use core_graphics_types::{base::CGFloat, geometry::CGSize};
41use foreign_types::ForeignType;
42use objc::runtime::{Object, NO, YES};
43
44/// See <https://developer.apple.com/documentation/objectivec/nsinteger>
45#[cfg(target_pointer_width = "64")]
46pub type NSInteger = i64;
47
48/// See <https://developer.apple.com/documentation/objectivec/nsinteger>
49#[cfg(not(target_pointer_width = "64"))]
50pub type NSInteger = i32;
51
52/// See <https://developer.apple.com/documentation/objectivec/nsuinteger>
53#[cfg(target_pointer_width = "64")]
54pub type NSUInteger = u64;
55
56/// See <https://developer.apple.com/documentation/objectivec/nsuinteger>
57#[cfg(target_pointer_width = "32")]
58pub type NSUInteger = u32;
59
60/// See <https://developer.apple.com/documentation/foundation/nsrange>
61#[repr(C)]
62#[derive(Copy, Clone)]
63pub struct NSRange {
64    pub location: NSUInteger,
65    pub length: NSUInteger,
66}
67
68impl NSRange {
69    #[inline]
70    pub fn new(location: NSUInteger, length: NSUInteger) -> NSRange {
71        NSRange { location, length }
72    }
73}
74
75fn nsstring_as_str(nsstr: &objc::runtime::Object) -> &str {
76    let bytes = unsafe {
77        let bytes: *const c_char = msg_send![nsstr, UTF8String];
78        bytes.cast()
79    };
80    let len: NSUInteger = unsafe { msg_send![nsstr, length] };
81    unsafe {
82        let bytes = std::slice::from_raw_parts(bytes, len as usize);
83        std::str::from_utf8(bytes).unwrap()
84    }
85}
86
87fn nsstring_from_str(string: &str) -> *mut objc::runtime::Object {
88    const UTF8_ENCODING: usize = 4;
89
90    let cls = class!(NSString);
91    let bytes = string.as_ptr().cast::<c_void>();
92    unsafe {
93        let obj: *mut objc::runtime::Object = msg_send![cls, alloc];
94        let obj: *mut objc::runtime::Object = msg_send![
95            obj,
96            initWithBytes:bytes
97            length:string.len()
98            encoding:UTF8_ENCODING
99        ];
100        let _: *mut c_void = msg_send![obj, autorelease];
101        obj
102    }
103}
104
105/// Define a Rust wrapper for an Objective-C opaque type.
106///
107/// This macro adapts the `foreign-types` crate's [`foreign_type!`]
108/// macro to Objective-C, defining Rust types that represent owned and
109/// borrowed forms of some underlying Objective-C type, using
110/// Objective-C's reference counting to manage its lifetime.
111///
112/// Given a use of the form:
113///
114/// ```ignore
115/// foreign_obj_type! {
116///     type CType = MTLBuffer;   // underlying Objective-C type
117///     pub struct Buffer;        // owned Rust type
118///     pub struct BufferRef;     // borrowed Rust type
119///     type ParentType = ResourceRef;  // borrowed parent class
120/// }
121/// ```
122///
123/// This defines the types `Buffer` and `BufferRef` as owning and
124/// non-owning types, analogous to `String` and `str`, that manage
125/// some underlying `*mut MTLBuffer`:
126///
127/// - Both `Buffer` and `BufferRef` implement [`obj::Message`], indicating
128///   that they can be sent Objective-C messages.
129///
130/// - Dropping a `Buffer` sends the underlying `MTLBuffer` a `release`
131///   message, and cloning a `BufferRef` sends a `retain` message and
132///   returns a new `Buffer`.
133///
134/// - `Buffer` dereferences to `BufferRef`.
135///
136/// - `BufferRef` dereferences to its parent type `ResourceRef`. The
137///   `ParentType` component is optional; if omitted, the `Ref` type
138///   doesn't implement `Deref` or `DerefMut`.
139///
140/// - Both `Buffer` and `BufferRef` implement `std::fmt::Debug`,
141///   sending an Objective-C `debugDescription` message to the
142///   underlying `MTLBuffer`.
143///
144/// Following the `foreign_types` crate's nomenclature, the `Ref`
145/// suffix indicates that `BufferRef` and `ResourceRef` are non-owning
146/// types, used *by reference*, like `&BufferRef` or `&ResourceRef`.
147/// These types are not, themselves, references.
148macro_rules! foreign_obj_type {
149    {
150        type CType = $raw_ident:ident;
151        pub struct $owned_ident:ident;
152        type ParentType = $parent_ident:ident;
153    } => {
154        foreign_obj_type! {
155            type CType = $raw_ident;
156            pub struct $owned_ident;
157        }
158
159        impl ::std::ops::Deref for paste!{[<$owned_ident Ref>]} {
160            type Target = paste!{[<$parent_ident Ref>]};
161
162            #[inline]
163            fn deref(&self) -> &Self::Target {
164                unsafe { std::mem::transmute(self) }
165            }
166        }
167
168        impl ::std::convert::From<$owned_ident> for $parent_ident {
169            fn from(item: $owned_ident) -> Self {
170                unsafe { Self::from_ptr(item.into_ptr().cast()) }
171            }
172        }
173    };
174    {
175        type CType = $raw_ident:ident;
176        pub struct $owned_ident:ident;
177    } => {
178        foreign_type! {
179            pub unsafe type $owned_ident: Sync + Send {
180                type CType = $raw_ident;
181                fn drop = crate::obj_drop;
182                fn clone = crate::obj_clone;
183            }
184        }
185
186        unsafe impl ::objc::Message for $raw_ident {
187        }
188        unsafe impl ::objc::Message for paste!{[<$owned_ident Ref>]} {
189        }
190
191        impl ::std::fmt::Debug for paste!{[<$owned_ident Ref>]} {
192            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
193                unsafe {
194                    let string: *mut ::objc::runtime::Object = msg_send![self, debugDescription];
195                    write!(f, "{}", crate::nsstring_as_str(&*string))
196                }
197            }
198        }
199
200        impl ::std::fmt::Debug for $owned_ident {
201            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
202                ::std::ops::Deref::deref(self).fmt(f)
203            }
204        }
205    };
206}
207
208macro_rules! try_objc {
209    {
210        $err_name: ident => $body:expr
211    } => {
212        {
213            let mut $err_name: *mut Object = ::std::ptr::null_mut();
214            let value = $body;
215            if !$err_name.is_null() {
216                let desc: *mut Object = msg_send![$err_name, localizedDescription];
217                let compile_error: *const c_char = msg_send![desc, UTF8String];
218                let message = CStr::from_ptr(compile_error).to_string_lossy().into_owned();
219                return Err(message);
220            }
221            value
222        }
223    };
224}
225
226macro_rules! msg_send_bool {
227    ($obj:expr, $name:ident) => {{
228        match msg_send![$obj, $name] {
229            YES => true,
230            NO => false,
231            #[cfg(not(target_arch = "aarch64"))]
232            _ => unreachable!(),
233        }
234    }};
235    ($obj:expr, $name:ident : $arg:expr) => {{
236        match msg_send![$obj, $name: $arg] {
237            YES => true,
238            NO => false,
239            #[cfg(not(target_arch = "aarch64"))]
240            _ => unreachable!(),
241        }
242    }};
243}
244
245macro_rules! msg_send_bool_error_check {
246    ($obj:expr, $name:ident: $arg:expr) => {{
247        let mut err: *mut Object = ptr::null_mut();
248        let result: BOOL = msg_send![$obj, $name:$arg
249                                                    error:&mut err];
250        if !err.is_null() {
251            let desc: *mut Object = msg_send![err, localizedDescription];
252            let c_msg: *const c_char = msg_send![desc, UTF8String];
253            let message = CStr::from_ptr(c_msg).to_string_lossy().into_owned();
254            Err(message)
255        } else {
256            match result {
257                YES => Ok(true),
258                NO => Ok(false),
259                #[cfg(not(target_arch = "aarch64"))]
260                _ => unreachable!(),
261            }
262        }
263    }};
264}
265
266/// See <https://developer.apple.com/documentation/foundation/nsarray>
267pub struct NSArray<T> {
268    _phantom: PhantomData<T>,
269}
270
271pub struct Array<T>(*mut NSArray<T>)
272where
273    T: ForeignType + 'static,
274    T::Ref: objc::Message + 'static;
275
276pub struct ArrayRef<T>(foreign_types::Opaque, PhantomData<T>)
277where
278    T: ForeignType + 'static,
279    T::Ref: objc::Message + 'static;
280
281impl<T> Drop for Array<T>
282where
283    T: ForeignType + 'static,
284    T::Ref: objc::Message + 'static,
285{
286    fn drop(&mut self) {
287        unsafe {
288            let () = msg_send![self.0, release];
289        }
290    }
291}
292
293impl<T> Clone for Array<T>
294where
295    T: ForeignType + 'static,
296    T::Ref: objc::Message + 'static,
297{
298    fn clone(&self) -> Self {
299        unsafe { Array(msg_send![self.0, retain]) }
300    }
301}
302
303unsafe impl<T> objc::Message for NSArray<T>
304where
305    T: ForeignType + 'static,
306    T::Ref: objc::Message + 'static,
307{
308}
309
310unsafe impl<T> objc::Message for ArrayRef<T>
311where
312    T: ForeignType + 'static,
313    T::Ref: objc::Message + 'static,
314{
315}
316
317impl<T> Array<T>
318where
319    T: ForeignType + 'static,
320    T::Ref: objc::Message + 'static,
321{
322    pub fn from_slice<'a>(s: &[&T::Ref]) -> &'a ArrayRef<T> {
323        unsafe {
324            let class = class!(NSArray);
325            msg_send![class, arrayWithObjects: s.as_ptr() count: s.len()]
326        }
327    }
328
329    pub fn from_owned_slice<'a>(s: &[T]) -> &'a ArrayRef<T> {
330        unsafe {
331            let class = class!(NSArray);
332            msg_send![class, arrayWithObjects: s.as_ptr() count: s.len()]
333        }
334    }
335}
336
337unsafe impl<T> foreign_types::ForeignType for Array<T>
338where
339    T: ForeignType + 'static,
340    T::Ref: objc::Message + 'static,
341{
342    type CType = NSArray<T>;
343    type Ref = ArrayRef<T>;
344
345    unsafe fn from_ptr(p: *mut NSArray<T>) -> Self {
346        Array(p)
347    }
348
349    fn as_ptr(&self) -> *mut NSArray<T> {
350        self.0
351    }
352}
353
354unsafe impl<T> foreign_types::ForeignTypeRef for ArrayRef<T>
355where
356    T: ForeignType + 'static,
357    T::Ref: objc::Message + 'static,
358{
359    type CType = NSArray<T>;
360}
361
362impl<T> Deref for Array<T>
363where
364    T: ForeignType + 'static,
365    T::Ref: objc::Message + 'static,
366{
367    type Target = ArrayRef<T>;
368
369    #[inline]
370    fn deref(&self) -> &ArrayRef<T> {
371        unsafe { std::mem::transmute(self) }
372    }
373}
374
375impl<T> Borrow<ArrayRef<T>> for Array<T>
376where
377    T: ForeignType + 'static,
378    T::Ref: objc::Message + 'static,
379{
380    fn borrow(&self) -> &ArrayRef<T> {
381        unsafe { std::mem::transmute(self) }
382    }
383}
384
385impl<T> ToOwned for ArrayRef<T>
386where
387    T: ForeignType + 'static,
388    T::Ref: objc::Message + 'static,
389{
390    type Owned = Array<T>;
391
392    fn to_owned(&self) -> Array<T> {
393        unsafe { Array::from_ptr(msg_send![self, retain]) }
394    }
395}
396
397/// See <https://developer.apple.com/documentation/quartzcore/cametaldrawable>
398pub enum CAMetalDrawable {}
399
400foreign_obj_type! {
401    type CType = CAMetalDrawable;
402    pub struct MetalDrawable;
403    type ParentType = Drawable;
404}
405
406impl MetalDrawableRef {
407    pub fn texture(&self) -> &TextureRef {
408        unsafe { msg_send![self, texture] }
409    }
410}
411
412pub enum NSObject {}
413
414foreign_obj_type! {
415    type CType = NSObject;
416    pub struct NsObject;
417}
418
419impl NsObjectRef {
420    pub fn conforms_to_protocol<T>(&self) -> Result<bool, String> {
421        let name = ::std::any::type_name::<T>();
422        if let Some(name) = name.split("::").last() {
423            if let Some(protocol) = objc::runtime::Protocol::get(name) {
424                Ok(unsafe { msg_send![self, conformsToProtocol: protocol] })
425            } else {
426                Err(format!("Can not find the protocol for type: {}.", name))
427            }
428        } else {
429            Err(format!("Unexpected type name: {}.", name))
430        }
431    }
432}
433
434// See <https://developer.apple.com/documentation/quartzcore/cametallayer>
435pub enum CAMetalLayer {}
436
437foreign_obj_type! {
438    type CType = CAMetalLayer;
439    pub struct MetalLayer;
440}
441
442impl MetalLayer {
443    pub fn new() -> Self {
444        unsafe {
445            let class = class!(CAMetalLayer);
446            msg_send![class, new]
447        }
448    }
449}
450
451impl MetalLayerRef {
452    pub fn device(&self) -> &DeviceRef {
453        unsafe { msg_send![self, device] }
454    }
455
456    pub fn set_device(&self, device: &DeviceRef) {
457        unsafe { msg_send![self, setDevice: device] }
458    }
459
460    pub fn pixel_format(&self) -> MTLPixelFormat {
461        unsafe { msg_send![self, pixelFormat] }
462    }
463
464    pub fn set_pixel_format(&self, pixel_format: MTLPixelFormat) {
465        unsafe { msg_send![self, setPixelFormat: pixel_format] }
466    }
467
468    pub fn drawable_size(&self) -> CGSize {
469        unsafe { msg_send![self, drawableSize] }
470    }
471
472    pub fn set_drawable_size(&self, size: CGSize) {
473        unsafe { msg_send![self, setDrawableSize: size] }
474    }
475
476    pub fn presents_with_transaction(&self) -> bool {
477        unsafe { msg_send_bool![self, presentsWithTransaction] }
478    }
479
480    pub fn set_presents_with_transaction(&self, transaction: bool) {
481        unsafe { msg_send![self, setPresentsWithTransaction: transaction] }
482    }
483
484    pub fn display_sync_enabled(&self) -> bool {
485        unsafe { msg_send_bool![self, displaySyncEnabled] }
486    }
487
488    pub fn set_display_sync_enabled(&self, enabled: bool) {
489        unsafe { msg_send![self, setDisplaySyncEnabled: enabled] }
490    }
491
492    pub fn maximum_drawable_count(&self) -> NSUInteger {
493        unsafe { msg_send![self, maximumDrawableCount] }
494    }
495
496    pub fn set_maximum_drawable_count(&self, count: NSUInteger) {
497        unsafe { msg_send![self, setMaximumDrawableCount: count] }
498    }
499
500    pub fn set_edge_antialiasing_mask(&self, mask: u64) {
501        unsafe { msg_send![self, setEdgeAntialiasingMask: mask] }
502    }
503
504    pub fn set_masks_to_bounds(&self, masks: bool) {
505        unsafe { msg_send![self, setMasksToBounds: masks] }
506    }
507
508    pub fn remove_all_animations(&self) {
509        unsafe { msg_send![self, removeAllAnimations] }
510    }
511
512    pub fn next_drawable(&self) -> Option<&MetalDrawableRef> {
513        unsafe { msg_send![self, nextDrawable] }
514    }
515
516    pub fn contents_scale(&self) -> CGFloat {
517        unsafe { msg_send![self, contentsScale] }
518    }
519
520    pub fn set_contents_scale(&self, scale: CGFloat) {
521        unsafe { msg_send![self, setContentsScale: scale] }
522    }
523
524    /// [framebufferOnly Apple Docs](https://developer.apple.com/documentation/metal/mtltexture/1515749-framebufferonly?language=objc)
525    pub fn framebuffer_only(&self) -> bool {
526        unsafe { msg_send_bool!(self, framebufferOnly) }
527    }
528
529    pub fn set_framebuffer_only(&self, framebuffer_only: bool) {
530        unsafe { msg_send![self, setFramebufferOnly: framebuffer_only] }
531    }
532
533    pub fn is_opaque(&self) -> bool {
534        unsafe { msg_send_bool!(self, isOpaque) }
535    }
536
537    pub fn set_opaque(&self, opaque: bool) {
538        unsafe { msg_send![self, setOpaque: opaque] }
539    }
540
541    pub fn wants_extended_dynamic_range_content(&self) -> bool {
542        unsafe { msg_send_bool![self, wantsExtendedDynamicRangeContent] }
543    }
544
545    pub fn set_wants_extended_dynamic_range_content(
546        &self,
547        wants_extended_dynamic_range_content: bool,
548    ) {
549        unsafe {
550            msg_send![
551                self,
552                setWantsExtendedDynamicRangeContent: wants_extended_dynamic_range_content
553            ]
554        }
555    }
556}
557
558mod acceleration_structure;
559mod acceleration_structure_pass;
560mod argument;
561mod blitpass;
562mod buffer;
563mod capturedescriptor;
564mod capturemanager;
565mod commandbuffer;
566mod commandqueue;
567mod computepass;
568mod constants;
569mod counters;
570mod depthstencil;
571mod device;
572mod drawable;
573mod encoder;
574mod heap;
575mod indirect_encoder;
576mod library;
577#[cfg(feature = "mps")]
578pub mod mps;
579mod pipeline;
580mod renderpass;
581mod resource;
582mod sampler;
583mod sync;
584mod texture;
585mod types;
586mod vertexdescriptor;
587
588#[rustfmt::skip]
589pub use {
590    acceleration_structure::*,
591    acceleration_structure_pass::*,
592    argument::*,
593    blitpass::*,
594    buffer::*,
595    counters::*,
596    computepass::*,
597    capturedescriptor::*,
598    capturemanager::*,
599    commandbuffer::*,
600    commandqueue::*,
601    constants::*,
602    depthstencil::*,
603    device::*,
604    drawable::*,
605    encoder::*,
606    heap::*,
607    indirect_encoder::*,
608    library::*,
609    pipeline::*,
610    renderpass::*,
611    resource::*,
612    sampler::*,
613    texture::*,
614    types::*,
615    vertexdescriptor::*,
616    sync::*,
617};
618
619#[inline]
620unsafe fn obj_drop<T>(p: *mut T) {
621    msg_send![p.cast::<Object>(), release]
622}
623
624#[inline]
625unsafe fn obj_clone<T: 'static>(p: *mut T) -> *mut T {
626    msg_send![p.cast::<Object>(), retain]
627}
628
629#[allow(non_camel_case_types)]
630type c_size_t = usize;
631
632// TODO: expand supported interface
633/// See <https://developer.apple.com/documentation/foundation/nsurl>
634pub enum NSURL {}
635
636foreign_obj_type! {
637    type CType = NSURL;
638    pub struct URL;
639}
640
641impl URL {
642    pub fn new_with_string(string: &str) -> Self {
643        unsafe {
644            let ns_str = crate::nsstring_from_str(string);
645            let class = class!(NSURL);
646            msg_send![class, URLWithString: ns_str]
647        }
648    }
649}
650
651impl URLRef {
652    pub fn absolute_string(&self) -> &str {
653        unsafe {
654            let absolute_string = msg_send![self, absoluteString];
655            crate::nsstring_as_str(absolute_string)
656        }
657    }
658
659    pub fn path(&self) -> &str {
660        unsafe {
661            let path = msg_send![self, path];
662            crate::nsstring_as_str(path)
663        }
664    }
665}