Skip to main content

apple_metal/
render.rs

1use crate::{
2    ffi, texture_usage, util::take_optional_string, MetalDevice, MetalFunction, TextureDescriptor,
3};
4use core::ffi::c_void;
5
6/// `MTLPrimitiveType` enum values.
7pub mod primitive_type {
8    /// Mirrors the `Metal` framework constant `POINT`.
9    pub const POINT: usize = 0;
10    /// Mirrors the `Metal` framework constant `LINE`.
11    pub const LINE: usize = 1;
12    /// Mirrors the `Metal` framework constant `LINE_STRIP`.
13    pub const LINE_STRIP: usize = 2;
14    /// Mirrors the `Metal` framework constant `TRIANGLE`.
15    pub const TRIANGLE: usize = 3;
16    /// Mirrors the `Metal` framework constant `TRIANGLE_STRIP`.
17    pub const TRIANGLE_STRIP: usize = 4;
18}
19
20/// `MTLLoadAction` enum values.
21pub mod load_action {
22    /// Mirrors the `Metal` framework constant `DONT_CARE`.
23    pub const DONT_CARE: usize = 0;
24    /// Mirrors the `Metal` framework constant `LOAD`.
25    pub const LOAD: usize = 1;
26    /// Mirrors the `Metal` framework constant `CLEAR`.
27    pub const CLEAR: usize = 2;
28}
29
30/// `MTLStoreAction` enum values.
31pub mod store_action {
32    /// Mirrors the `Metal` framework constant `DONT_CARE`.
33    pub const DONT_CARE: usize = 0;
34    /// Mirrors the `Metal` framework constant `STORE`.
35    pub const STORE: usize = 1;
36    /// Mirrors the `Metal` framework constant `MULTISAMPLE_RESOLVE`.
37    pub const MULTISAMPLE_RESOLVE: usize = 2;
38    /// Mirrors the `Metal` framework constant `STORE_AND_MULTISAMPLE_RESOLVE`.
39    pub const STORE_AND_MULTISAMPLE_RESOLVE: usize = 3;
40}
41
42/// Apple's `id<MTLRenderPipelineState>` — a compiled render pipeline.
43pub struct RenderPipelineState {
44    ptr: *mut c_void,
45}
46
47// SAFETY: `id<MTLRenderPipelineState>` is immutable after creation and
48// thread-safe per Apple documentation.
49unsafe impl Send for RenderPipelineState {}
50unsafe impl Sync for RenderPipelineState {}
51
52impl Drop for RenderPipelineState {
53    fn drop(&mut self) {
54        if !self.ptr.is_null() {
55            unsafe { ffi::am_object_release(self.ptr) };
56            self.ptr = core::ptr::null_mut();
57        }
58    }
59}
60
61impl RenderPipelineState {
62    /// Mirrors the `Metal` framework constant `fn`.
63    #[must_use]
64    pub const fn as_ptr(&self) -> *mut c_void {
65        self.ptr
66    }
67
68    fn wrap(ptr: *mut c_void) -> Option<Self> {
69        if ptr.is_null() {
70            None
71        } else {
72            Some(Self { ptr })
73        }
74    }
75
76    pub(crate) const unsafe fn from_retained_ptr(ptr: *mut c_void) -> Self {
77        Self { ptr }
78    }
79
80    /// Metal's label for this pipeline, if one was set.
81    #[must_use]
82    pub fn label(&self) -> Option<String> {
83        unsafe { take_optional_string(ffi::am_object_copy_label(self.ptr)) }
84    }
85}
86
87impl MetalDevice {
88    /// Compile a render pipeline state from `vertex` and `fragment` functions.
89    ///
90    /// # Errors
91    ///
92    /// Returns Metal's localized pipeline compiler error on failure.
93    pub fn new_render_pipeline_state(
94        &self,
95        vertex: &MetalFunction,
96        fragment: &MetalFunction,
97        color_pixel_format: usize,
98        sample_count: usize,
99    ) -> Result<RenderPipelineState, String> {
100        let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
101        let ptr = unsafe {
102            ffi::am_device_new_render_pipeline_state(
103                self.as_ptr(),
104                vertex.as_ptr(),
105                fragment.as_ptr(),
106                color_pixel_format,
107                sample_count,
108                &mut err,
109            )
110        };
111        RenderPipelineState::wrap(ptr).ok_or_else(|| unsafe {
112            take_optional_string(err)
113                .unwrap_or_else(|| "MTLDevice.makeRenderPipelineState returned nil".to_string())
114        })
115    }
116}
117
118impl TextureDescriptor {
119    /// Sensible defaults for an offscreen 2D render target texture.
120    #[must_use]
121    pub const fn render_target_2d(width: usize, height: usize, pixel_format: usize) -> Self {
122        Self {
123            pixel_format,
124            width,
125            height,
126            mipmapped: false,
127            usage: texture_usage::RENDER_TARGET | texture_usage::SHADER_READ,
128            storage_mode: crate::storage_mode::PRIVATE,
129        }
130    }
131}