Skip to main content

mtl_gpu/encoder/render_encoder/
mod.rs

1//! Render command encoder.
2//!
3//! Corresponds to `Metal/MTLRenderCommandEncoder.hpp`.
4
5mod advanced;
6mod binding;
7mod draw;
8mod mesh;
9mod state;
10mod tile;
11mod viewport;
12
13use std::ffi::c_void;
14use std::ptr::NonNull;
15
16use mtl_foundation::Referencing;
17use mtl_sys::{msg_send_0, msg_send_1, sel};
18
19/// A command encoder for rendering operations.
20///
21/// C++ equivalent: `MTL::RenderCommandEncoder`
22///
23/// Render encoders are used to encode drawing commands, set render state,
24/// and bind resources for rendering operations.
25#[repr(transparent)]
26pub struct RenderCommandEncoder(pub(crate) NonNull<c_void>);
27
28impl RenderCommandEncoder {
29    /// Create a RenderCommandEncoder from a raw pointer.
30    ///
31    /// # Safety
32    ///
33    /// The pointer must be a valid Metal render command encoder object.
34    #[inline]
35    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
36        NonNull::new(ptr).map(Self)
37    }
38
39    /// Get the raw pointer to the encoder.
40    #[inline]
41    pub fn as_raw(&self) -> *mut c_void {
42        self.0.as_ptr()
43    }
44
45    // =========================================================================
46    // CommandEncoder base methods
47    // =========================================================================
48
49    /// Get the device that created this encoder.
50    ///
51    /// C++ equivalent: `Device* device() const`
52    pub fn device(&self) -> crate::Device {
53        unsafe {
54            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(device));
55            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
56            crate::Device::from_raw(ptr).expect("encoder has no device")
57        }
58    }
59
60    /// Get the command buffer that this encoder is encoding into.
61    ///
62    /// C++ equivalent: `CommandBuffer* commandBuffer() const`
63    pub fn command_buffer(&self) -> crate::CommandBuffer {
64        unsafe {
65            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(commandBuffer));
66            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
67            crate::CommandBuffer::from_raw(ptr).expect("encoder has no command buffer")
68        }
69    }
70
71    /// Get the label for this encoder.
72    ///
73    /// C++ equivalent: `NS::String* label() const`
74    pub fn label(&self) -> Option<String> {
75        unsafe {
76            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
77            if ptr.is_null() {
78                return None;
79            }
80            let utf8_ptr: *const std::ffi::c_char =
81                mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
82            if utf8_ptr.is_null() {
83                return None;
84            }
85            let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
86            Some(c_str.to_string_lossy().into_owned())
87        }
88    }
89
90    /// Set the label for this encoder.
91    ///
92    /// C++ equivalent: `void setLabel(const NS::String*)`
93    pub fn set_label(&self, label: &str) {
94        if let Some(ns_label) = mtl_foundation::String::from_str(label) {
95            unsafe {
96                msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
97            }
98        }
99    }
100
101    /// End encoding commands with this encoder.
102    ///
103    /// C++ equivalent: `void endEncoding()`
104    #[inline]
105    pub fn end_encoding(&self) {
106        unsafe {
107            msg_send_0::<()>(self.as_ptr(), sel!(endEncoding));
108        }
109    }
110
111    /// Insert a debug signpost.
112    ///
113    /// C++ equivalent: `void insertDebugSignpost(const NS::String*)`
114    pub fn insert_debug_signpost(&self, string: &str) {
115        if let Some(ns_string) = mtl_foundation::String::from_str(string) {
116            unsafe {
117                msg_send_1::<(), *const c_void>(
118                    self.as_ptr(),
119                    sel!(insertDebugSignpost:),
120                    ns_string.as_ptr(),
121                );
122            }
123        }
124    }
125
126    /// Push a debug group.
127    ///
128    /// C++ equivalent: `void pushDebugGroup(const NS::String*)`
129    pub fn push_debug_group(&self, string: &str) {
130        if let Some(ns_string) = mtl_foundation::String::from_str(string) {
131            unsafe {
132                msg_send_1::<(), *const c_void>(
133                    self.as_ptr(),
134                    sel!(pushDebugGroup:),
135                    ns_string.as_ptr(),
136                );
137            }
138        }
139    }
140
141    /// Pop the current debug group.
142    ///
143    /// C++ equivalent: `void popDebugGroup()`
144    #[inline]
145    pub fn pop_debug_group(&self) {
146        unsafe {
147            msg_send_0::<()>(self.as_ptr(), sel!(popDebugGroup));
148        }
149    }
150
151    /// Insert a barrier to synchronize queue stages.
152    ///
153    /// C++ equivalent: `void barrierAfterQueueStages(Stages, Stages)`
154    #[inline]
155    pub fn barrier_after_queue_stages(
156        &self,
157        after_stages: crate::enums::Stages,
158        before_stages: crate::enums::Stages,
159    ) {
160        unsafe {
161            mtl_sys::msg_send_2::<(), crate::enums::Stages, crate::enums::Stages>(
162                self.as_ptr(),
163                sel!(barrierAfterQueueStages:beforeQueueStages:),
164                after_stages,
165                before_stages,
166            );
167        }
168    }
169
170    // =========================================================================
171    // Pipeline State
172    // =========================================================================
173
174    /// Set the render pipeline state.
175    ///
176    /// C++ equivalent: `void setRenderPipelineState(const RenderPipelineState*)`
177    #[inline]
178    pub fn set_render_pipeline_state(&self, state: &crate::RenderPipelineState) {
179        unsafe {
180            msg_send_1::<(), *const c_void>(
181                self.as_ptr(),
182                sel!(setRenderPipelineState:),
183                state.as_ptr(),
184            );
185        }
186    }
187}
188
189impl Clone for RenderCommandEncoder {
190    fn clone(&self) -> Self {
191        unsafe {
192            msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
193        }
194        Self(self.0)
195    }
196}
197
198impl Drop for RenderCommandEncoder {
199    fn drop(&mut self) {
200        unsafe {
201            msg_send_0::<()>(self.as_ptr(), sel!(release));
202        }
203    }
204}
205
206impl Referencing for RenderCommandEncoder {
207    #[inline]
208    fn as_ptr(&self) -> *const c_void {
209        self.0.as_ptr()
210    }
211}
212
213unsafe impl Send for RenderCommandEncoder {}
214unsafe impl Sync for RenderCommandEncoder {}
215
216impl std::fmt::Debug for RenderCommandEncoder {
217    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218        f.debug_struct("RenderCommandEncoder")
219            .field("label", &self.label())
220            .finish()
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227
228    #[test]
229    fn test_render_encoder_size() {
230        assert_eq!(
231            std::mem::size_of::<RenderCommandEncoder>(),
232            std::mem::size_of::<*mut c_void>()
233        );
234    }
235}