Skip to main content

mtl_gpu/mtl4/
tile_render_pipeline.rs

1//! MTL4 TileRenderPipeline implementation.
2//!
3//! Corresponds to `Metal/MTL4TileRenderPipeline.hpp`.
4
5use std::ffi::c_void;
6use std::ptr::NonNull;
7
8use mtl_foundation::{Referencing, UInteger};
9use mtl_sys::{msg_send_0, msg_send_1, sel};
10
11use super::{FunctionDescriptor, PipelineOptions, StaticLinkingDescriptor};
12use crate::Size;
13
14// ============================================================
15// TileRenderPipelineDescriptor
16// ============================================================
17
18/// Descriptor for MTL4 tile render pipelines.
19///
20/// C++ equivalent: `MTL4::TileRenderPipelineDescriptor`
21///
22/// TileRenderPipelineDescriptor configures tile shading pipelines
23/// for on-tile compute operations.
24#[repr(transparent)]
25pub struct TileRenderPipelineDescriptor(NonNull<c_void>);
26
27impl TileRenderPipelineDescriptor {
28    /// Create a TileRenderPipelineDescriptor from a raw pointer.
29    #[inline]
30    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
31        NonNull::new(ptr).map(Self)
32    }
33
34    /// Get the raw pointer.
35    #[inline]
36    pub fn as_raw(&self) -> *mut c_void {
37        self.0.as_ptr()
38    }
39
40    /// Create a new tile render pipeline descriptor.
41    pub fn new() -> Option<Self> {
42        unsafe {
43            let class = mtl_sys::Class::get("MTL4TileRenderPipelineDescriptor")?;
44            let ptr: *mut c_void = msg_send_0(class.as_ptr(), sel!(alloc));
45            if ptr.is_null() {
46                return None;
47            }
48            let ptr: *mut c_void = msg_send_0(ptr, sel!(init));
49            Self::from_raw(ptr)
50        }
51    }
52
53    // ========== Base Pipeline Properties ==========
54
55    /// Get the label.
56    pub fn label(&self) -> Option<String> {
57        unsafe {
58            let ns_string: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
59            if ns_string.is_null() {
60                return None;
61            }
62            let c_str: *const i8 = msg_send_0(ns_string, sel!(UTF8String));
63            if c_str.is_null() {
64                return None;
65            }
66            Some(
67                std::ffi::CStr::from_ptr(c_str)
68                    .to_string_lossy()
69                    .into_owned(),
70            )
71        }
72    }
73
74    /// Set the label.
75    pub fn set_label(&self, label: &str) {
76        if let Some(ns_label) = mtl_foundation::String::from_str(label) {
77            unsafe {
78                let _: () = msg_send_1(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
79            }
80        }
81    }
82
83    /// Get the pipeline options.
84    pub fn options(&self) -> Option<PipelineOptions> {
85        unsafe {
86            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(options));
87            PipelineOptions::from_raw(ptr)
88        }
89    }
90
91    /// Set the pipeline options.
92    pub fn set_options(&self, options: &PipelineOptions) {
93        unsafe {
94            let _: () = msg_send_1(self.as_ptr(), sel!(setOptions:), options.as_ptr());
95        }
96    }
97
98    // ========== Tile Function ==========
99
100    /// Get the tile function descriptor.
101    pub fn tile_function_descriptor(&self) -> Option<FunctionDescriptor> {
102        unsafe {
103            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(tileFunctionDescriptor));
104            FunctionDescriptor::from_raw(ptr)
105        }
106    }
107
108    /// Set the tile function descriptor.
109    pub fn set_tile_function_descriptor(&self, descriptor: &FunctionDescriptor) {
110        unsafe {
111            let _: () = msg_send_1(
112                self.as_ptr(),
113                sel!(setTileFunctionDescriptor:),
114                descriptor.as_ptr(),
115            );
116        }
117    }
118
119    /// Get the static linking descriptor.
120    pub fn static_linking_descriptor(&self) -> Option<StaticLinkingDescriptor> {
121        unsafe {
122            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(staticLinkingDescriptor));
123            StaticLinkingDescriptor::from_raw(ptr)
124        }
125    }
126
127    /// Set the static linking descriptor.
128    pub fn set_static_linking_descriptor(&self, descriptor: &StaticLinkingDescriptor) {
129        unsafe {
130            let _: () = msg_send_1(
131                self.as_ptr(),
132                sel!(setStaticLinkingDescriptor:),
133                descriptor.as_ptr(),
134            );
135        }
136    }
137
138    // ========== Color Attachments ==========
139
140    /// Get the color attachments array (as raw pointer to MTL::TileRenderPipelineColorAttachmentDescriptorArray).
141    pub fn color_attachments_raw(&self) -> *mut c_void {
142        unsafe { msg_send_0(self.as_ptr(), sel!(colorAttachments)) }
143    }
144
145    // ========== Threadgroup ==========
146
147    /// Get the maximum total threads per threadgroup.
148    pub fn max_total_threads_per_threadgroup(&self) -> UInteger {
149        unsafe { msg_send_0(self.as_ptr(), sel!(maxTotalThreadsPerThreadgroup)) }
150    }
151
152    /// Set the maximum total threads per threadgroup.
153    pub fn set_max_total_threads_per_threadgroup(&self, max: UInteger) {
154        unsafe {
155            let _: () = msg_send_1(self.as_ptr(), sel!(setMaxTotalThreadsPerThreadgroup:), max);
156        }
157    }
158
159    /// Get the required threads per threadgroup.
160    pub fn required_threads_per_threadgroup(&self) -> Size {
161        unsafe { msg_send_0(self.as_ptr(), sel!(requiredThreadsPerThreadgroup)) }
162    }
163
164    /// Set the required threads per threadgroup.
165    pub fn set_required_threads_per_threadgroup(&self, size: Size) {
166        unsafe {
167            let _: () = msg_send_1(self.as_ptr(), sel!(setRequiredThreadsPerThreadgroup:), size);
168        }
169    }
170
171    /// Get whether threadgroup size matches tile size.
172    pub fn threadgroup_size_matches_tile_size(&self) -> bool {
173        unsafe { msg_send_0(self.as_ptr(), sel!(threadgroupSizeMatchesTileSize)) }
174    }
175
176    /// Set whether threadgroup size matches tile size.
177    pub fn set_threadgroup_size_matches_tile_size(&self, value: bool) {
178        unsafe {
179            let _: () = msg_send_1(
180                self.as_ptr(),
181                sel!(setThreadgroupSizeMatchesTileSize:),
182                value,
183            );
184        }
185    }
186
187    // ========== Rasterization ==========
188
189    /// Get the raster sample count.
190    pub fn raster_sample_count(&self) -> UInteger {
191        unsafe { msg_send_0(self.as_ptr(), sel!(rasterSampleCount)) }
192    }
193
194    /// Set the raster sample count.
195    pub fn set_raster_sample_count(&self, count: UInteger) {
196        unsafe {
197            let _: () = msg_send_1(self.as_ptr(), sel!(setRasterSampleCount:), count);
198        }
199    }
200
201    // ========== Binary Linking ==========
202
203    /// Get whether binary linking is supported.
204    pub fn support_binary_linking(&self) -> bool {
205        unsafe { msg_send_0(self.as_ptr(), sel!(supportBinaryLinking)) }
206    }
207
208    /// Set whether binary linking is supported.
209    pub fn set_support_binary_linking(&self, support: bool) {
210        unsafe {
211            let _: () = msg_send_1(self.as_ptr(), sel!(setSupportBinaryLinking:), support);
212        }
213    }
214
215    /// Reset the descriptor to its default state.
216    pub fn reset(&self) {
217        unsafe {
218            let _: () = msg_send_0(self.as_ptr(), sel!(reset));
219        }
220    }
221}
222
223impl Clone for TileRenderPipelineDescriptor {
224    fn clone(&self) -> Self {
225        unsafe {
226            mtl_sys::msg_send_0::<*mut c_void>(self.as_ptr(), mtl_sys::sel!(retain));
227        }
228        Self(self.0)
229    }
230}
231
232impl Drop for TileRenderPipelineDescriptor {
233    fn drop(&mut self) {
234        unsafe {
235            mtl_sys::msg_send_0::<()>(self.as_ptr(), mtl_sys::sel!(release));
236        }
237    }
238}
239
240impl Referencing for TileRenderPipelineDescriptor {
241    #[inline]
242    fn as_ptr(&self) -> *const c_void {
243        self.0.as_ptr()
244    }
245}
246
247unsafe impl Send for TileRenderPipelineDescriptor {}
248unsafe impl Sync for TileRenderPipelineDescriptor {}
249
250impl std::fmt::Debug for TileRenderPipelineDescriptor {
251    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252        f.debug_struct("TileRenderPipelineDescriptor")
253            .field("label", &self.label())
254            .field("raster_sample_count", &self.raster_sample_count())
255            .field(
256                "max_total_threads_per_threadgroup",
257                &self.max_total_threads_per_threadgroup(),
258            )
259            .finish()
260    }
261}
262
263#[cfg(test)]
264mod tests {
265    use super::*;
266
267    #[test]
268    fn test_tile_render_pipeline_descriptor_size() {
269        assert_eq!(
270            std::mem::size_of::<TileRenderPipelineDescriptor>(),
271            std::mem::size_of::<*mut c_void>()
272        );
273    }
274}