Skip to main content

mtl_gpu/encoder/
parallel_render_encoder.rs

1//! Parallel render command encoder.
2//!
3//! Corresponds to `Metal/MTLParallelRenderCommandEncoder.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 crate::enums::{StoreAction, StoreActionOptions};
12
13/// A command encoder that creates multiple render command encoders that render in parallel.
14///
15/// C++ equivalent: `MTL::ParallelRenderCommandEncoder`
16///
17/// Parallel render encoders allow you to split rendering work across multiple
18/// render command encoders that can execute concurrently on the GPU.
19#[repr(transparent)]
20pub struct ParallelRenderCommandEncoder(pub(crate) NonNull<c_void>);
21
22impl ParallelRenderCommandEncoder {
23    /// Create a ParallelRenderCommandEncoder from a raw pointer.
24    ///
25    /// # Safety
26    ///
27    /// The pointer must be a valid Metal parallel render command encoder object.
28    #[inline]
29    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
30        NonNull::new(ptr).map(Self)
31    }
32
33    /// Get the raw pointer to the encoder.
34    #[inline]
35    pub fn as_raw(&self) -> *mut c_void {
36        self.0.as_ptr()
37    }
38
39    // =========================================================================
40    // CommandEncoder base methods
41    // =========================================================================
42
43    /// Get the device that created this encoder.
44    ///
45    /// C++ equivalent: `Device* device() const`
46    pub fn device(&self) -> crate::Device {
47        unsafe {
48            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(device));
49            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
50            crate::Device::from_raw(ptr).expect("encoder has no device")
51        }
52    }
53
54    /// Get the command buffer that this encoder is encoding into.
55    ///
56    /// C++ equivalent: `CommandBuffer* commandBuffer() const`
57    pub fn command_buffer(&self) -> crate::CommandBuffer {
58        unsafe {
59            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(commandBuffer));
60            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
61            crate::CommandBuffer::from_raw(ptr).expect("encoder has no command buffer")
62        }
63    }
64
65    /// Get the label for this encoder.
66    ///
67    /// C++ equivalent: `NS::String* label() const`
68    pub fn label(&self) -> Option<String> {
69        unsafe {
70            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
71            if ptr.is_null() {
72                return None;
73            }
74            let utf8_ptr: *const std::ffi::c_char =
75                mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
76            if utf8_ptr.is_null() {
77                return None;
78            }
79            let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
80            Some(c_str.to_string_lossy().into_owned())
81        }
82    }
83
84    /// Set the label for this encoder.
85    ///
86    /// C++ equivalent: `void setLabel(const NS::String*)`
87    pub fn set_label(&self, label: &str) {
88        if let Some(ns_label) = mtl_foundation::String::from_str(label) {
89            unsafe {
90                msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
91            }
92        }
93    }
94
95    /// End encoding commands with this encoder.
96    ///
97    /// C++ equivalent: `void endEncoding()`
98    #[inline]
99    pub fn end_encoding(&self) {
100        unsafe {
101            msg_send_0::<()>(self.as_ptr(), sel!(endEncoding));
102        }
103    }
104
105    /// Insert a debug signpost.
106    ///
107    /// C++ equivalent: `void insertDebugSignpost(const NS::String*)`
108    pub fn insert_debug_signpost(&self, string: &str) {
109        if let Some(ns_string) = mtl_foundation::String::from_str(string) {
110            unsafe {
111                msg_send_1::<(), *const c_void>(
112                    self.as_ptr(),
113                    sel!(insertDebugSignpost:),
114                    ns_string.as_ptr(),
115                );
116            }
117        }
118    }
119
120    /// Push a debug group.
121    ///
122    /// C++ equivalent: `void pushDebugGroup(const NS::String*)`
123    pub fn push_debug_group(&self, string: &str) {
124        if let Some(ns_string) = mtl_foundation::String::from_str(string) {
125            unsafe {
126                msg_send_1::<(), *const c_void>(
127                    self.as_ptr(),
128                    sel!(pushDebugGroup:),
129                    ns_string.as_ptr(),
130                );
131            }
132        }
133    }
134
135    /// Pop the current debug group.
136    ///
137    /// C++ equivalent: `void popDebugGroup()`
138    #[inline]
139    pub fn pop_debug_group(&self) {
140        unsafe {
141            msg_send_0::<()>(self.as_ptr(), sel!(popDebugGroup));
142        }
143    }
144
145    /// Insert a barrier to synchronize queue stages.
146    ///
147    /// C++ equivalent: `void barrierAfterQueueStages(Stages, Stages)`
148    #[inline]
149    pub fn barrier_after_queue_stages(
150        &self,
151        after_stages: crate::enums::Stages,
152        before_stages: crate::enums::Stages,
153    ) {
154        unsafe {
155            mtl_sys::msg_send_2::<(), crate::enums::Stages, crate::enums::Stages>(
156                self.as_ptr(),
157                sel!(barrierAfterQueueStages:beforeQueueStages:),
158                after_stages,
159                before_stages,
160            );
161        }
162    }
163
164    // =========================================================================
165    // Parallel Render Encoder Methods
166    // =========================================================================
167
168    /// Create a new render command encoder that renders in parallel.
169    ///
170    /// C++ equivalent: `RenderCommandEncoder* renderCommandEncoder()`
171    ///
172    /// Each call creates a new render encoder that shares the render pass
173    /// with other encoders created from this parallel encoder.
174    #[inline]
175    pub fn render_command_encoder(&self) -> Option<crate::RenderCommandEncoder> {
176        unsafe {
177            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(renderCommandEncoder));
178            if ptr.is_null() {
179                None
180            } else {
181                // The returned encoder is autoreleased, so we retain it
182                let _: *mut c_void = msg_send_0(ptr, sel!(retain));
183                crate::encoder::RenderCommandEncoder::from_raw(ptr)
184            }
185        }
186    }
187
188    /// Set the store action for a color attachment.
189    ///
190    /// C++ equivalent: `void setColorStoreAction(StoreAction, NS::UInteger)`
191    #[inline]
192    pub fn set_color_store_action(
193        &self,
194        store_action: StoreAction,
195        color_attachment_index: UInteger,
196    ) {
197        unsafe {
198            mtl_sys::msg_send_2::<(), StoreAction, UInteger>(
199                self.as_ptr(),
200                sel!(setColorStoreAction: atIndex:),
201                store_action,
202                color_attachment_index,
203            );
204        }
205    }
206
207    /// Set the store action options for a color attachment.
208    ///
209    /// C++ equivalent: `void setColorStoreActionOptions(StoreActionOptions, NS::UInteger)`
210    #[inline]
211    pub fn set_color_store_action_options(
212        &self,
213        store_action_options: StoreActionOptions,
214        color_attachment_index: UInteger,
215    ) {
216        unsafe {
217            mtl_sys::msg_send_2::<(), StoreActionOptions, UInteger>(
218                self.as_ptr(),
219                sel!(setColorStoreActionOptions: atIndex:),
220                store_action_options,
221                color_attachment_index,
222            );
223        }
224    }
225
226    /// Set the store action for the depth attachment.
227    ///
228    /// C++ equivalent: `void setDepthStoreAction(StoreAction)`
229    #[inline]
230    pub fn set_depth_store_action(&self, store_action: StoreAction) {
231        unsafe {
232            msg_send_1::<(), StoreAction>(self.as_ptr(), sel!(setDepthStoreAction:), store_action);
233        }
234    }
235
236    /// Set the store action options for the depth attachment.
237    ///
238    /// C++ equivalent: `void setDepthStoreActionOptions(StoreActionOptions)`
239    #[inline]
240    pub fn set_depth_store_action_options(&self, store_action_options: StoreActionOptions) {
241        unsafe {
242            msg_send_1::<(), StoreActionOptions>(
243                self.as_ptr(),
244                sel!(setDepthStoreActionOptions:),
245                store_action_options,
246            );
247        }
248    }
249
250    /// Set the store action for the stencil attachment.
251    ///
252    /// C++ equivalent: `void setStencilStoreAction(StoreAction)`
253    #[inline]
254    pub fn set_stencil_store_action(&self, store_action: StoreAction) {
255        unsafe {
256            msg_send_1::<(), StoreAction>(
257                self.as_ptr(),
258                sel!(setStencilStoreAction:),
259                store_action,
260            );
261        }
262    }
263
264    /// Set the store action options for the stencil attachment.
265    ///
266    /// C++ equivalent: `void setStencilStoreActionOptions(StoreActionOptions)`
267    #[inline]
268    pub fn set_stencil_store_action_options(&self, store_action_options: StoreActionOptions) {
269        unsafe {
270            msg_send_1::<(), StoreActionOptions>(
271                self.as_ptr(),
272                sel!(setStencilStoreActionOptions:),
273                store_action_options,
274            );
275        }
276    }
277}
278
279impl Clone for ParallelRenderCommandEncoder {
280    fn clone(&self) -> Self {
281        unsafe {
282            msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
283        }
284        Self(self.0)
285    }
286}
287
288impl Drop for ParallelRenderCommandEncoder {
289    fn drop(&mut self) {
290        unsafe {
291            msg_send_0::<()>(self.as_ptr(), sel!(release));
292        }
293    }
294}
295
296impl Referencing for ParallelRenderCommandEncoder {
297    #[inline]
298    fn as_ptr(&self) -> *const c_void {
299        self.0.as_ptr()
300    }
301}
302
303unsafe impl Send for ParallelRenderCommandEncoder {}
304unsafe impl Sync for ParallelRenderCommandEncoder {}
305
306impl std::fmt::Debug for ParallelRenderCommandEncoder {
307    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308        f.debug_struct("ParallelRenderCommandEncoder")
309            .field("label", &self.label())
310            .finish()
311    }
312}
313
314#[cfg(test)]
315mod tests {
316    use super::*;
317
318    #[test]
319    fn test_parallel_render_encoder_size() {
320        assert_eq!(
321            std::mem::size_of::<ParallelRenderCommandEncoder>(),
322            std::mem::size_of::<*mut c_void>()
323        );
324    }
325}