Skip to main content

mtl_gpu/indirect/
render_command.rs

1//! A render command within an indirect command buffer.
2
3use std::ffi::c_void;
4use std::ptr::NonNull;
5
6use mtl_foundation::{Integer, Referencing, UInteger};
7use mtl_sys::{msg_send_0, msg_send_1, msg_send_2, msg_send_3, sel};
8
9use crate::enums::{CullMode, DepthClipMode, IndexType, PrimitiveType, TriangleFillMode, Winding};
10use crate::types::Size;
11use crate::{Buffer, DepthStencilState, RenderPipelineState};
12
13/// A render command within an indirect command buffer.
14///
15/// C++ equivalent: `MTL::IndirectRenderCommand`
16///
17/// Indirect render commands can encode draw calls and state changes
18/// that will be executed when the indirect command buffer is executed.
19#[repr(transparent)]
20pub struct IndirectRenderCommand(NonNull<c_void>);
21
22impl IndirectRenderCommand {
23    /// Create from a raw pointer.
24    ///
25    /// # Safety
26    ///
27    /// The pointer must be a valid Metal indirect render command.
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.
34    #[inline]
35    pub fn as_raw(&self) -> *mut c_void {
36        self.0.as_ptr()
37    }
38
39    /// Reset this command.
40    ///
41    /// C++ equivalent: `void reset()`
42    #[inline]
43    pub fn reset(&self) {
44        unsafe {
45            msg_send_0::<()>(self.as_ptr(), sel!(reset));
46        }
47    }
48
49    /// Set a barrier for this command.
50    ///
51    /// C++ equivalent: `void setBarrier()`
52    #[inline]
53    pub fn set_barrier(&self) {
54        unsafe {
55            msg_send_0::<()>(self.as_ptr(), sel!(setBarrier));
56        }
57    }
58
59    /// Clear a barrier for this command.
60    ///
61    /// C++ equivalent: `void clearBarrier()`
62    #[inline]
63    pub fn clear_barrier(&self) {
64        unsafe {
65            msg_send_0::<()>(self.as_ptr(), sel!(clearBarrier));
66        }
67    }
68
69    /// Set the render pipeline state.
70    ///
71    /// C++ equivalent: `void setRenderPipelineState(const RenderPipelineState*)`
72    pub fn set_render_pipeline_state(&self, state: &RenderPipelineState) {
73        unsafe {
74            msg_send_1::<(), *const c_void>(
75                self.as_ptr(),
76                sel!(setRenderPipelineState:),
77                state.as_ptr(),
78            );
79        }
80    }
81
82    /// Set a vertex buffer.
83    ///
84    /// C++ equivalent: `void setVertexBuffer(const Buffer*, NS::UInteger offset, NS::UInteger index)`
85    pub fn set_vertex_buffer(&self, buffer: &Buffer, offset: UInteger, index: UInteger) {
86        unsafe {
87            msg_send_3::<(), *const c_void, UInteger, UInteger>(
88                self.as_ptr(),
89                sel!(setVertexBuffer: offset: atIndex:),
90                buffer.as_ptr(),
91                offset,
92                index,
93            );
94        }
95    }
96
97    /// Set a vertex buffer with stride.
98    ///
99    /// C++ equivalent: `void setVertexBuffer(const Buffer*, NS::UInteger offset, NS::UInteger stride, NS::UInteger index)`
100    pub fn set_vertex_buffer_with_stride(
101        &self,
102        buffer: &Buffer,
103        offset: UInteger,
104        stride: UInteger,
105        index: UInteger,
106    ) {
107        unsafe {
108            mtl_sys::msg_send_4::<(), *const c_void, UInteger, UInteger, UInteger>(
109                self.as_ptr(),
110                sel!(setVertexBuffer: offset: attributeStride: atIndex:),
111                buffer.as_ptr(),
112                offset,
113                stride,
114                index,
115            );
116        }
117    }
118
119    /// Set a fragment buffer.
120    ///
121    /// C++ equivalent: `void setFragmentBuffer(const Buffer*, NS::UInteger offset, NS::UInteger index)`
122    pub fn set_fragment_buffer(&self, buffer: &Buffer, offset: UInteger, index: UInteger) {
123        unsafe {
124            msg_send_3::<(), *const c_void, UInteger, UInteger>(
125                self.as_ptr(),
126                sel!(setFragmentBuffer: offset: atIndex:),
127                buffer.as_ptr(),
128                offset,
129                index,
130            );
131        }
132    }
133
134    /// Set a mesh buffer.
135    ///
136    /// C++ equivalent: `void setMeshBuffer(const Buffer*, NS::UInteger offset, NS::UInteger index)`
137    pub fn set_mesh_buffer(&self, buffer: &Buffer, offset: UInteger, index: UInteger) {
138        unsafe {
139            msg_send_3::<(), *const c_void, UInteger, UInteger>(
140                self.as_ptr(),
141                sel!(setMeshBuffer: offset: atIndex:),
142                buffer.as_ptr(),
143                offset,
144                index,
145            );
146        }
147    }
148
149    /// Set an object buffer.
150    ///
151    /// C++ equivalent: `void setObjectBuffer(const Buffer*, NS::UInteger offset, NS::UInteger index)`
152    pub fn set_object_buffer(&self, buffer: &Buffer, offset: UInteger, index: UInteger) {
153        unsafe {
154            msg_send_3::<(), *const c_void, UInteger, UInteger>(
155                self.as_ptr(),
156                sel!(setObjectBuffer: offset: atIndex:),
157                buffer.as_ptr(),
158                offset,
159                index,
160            );
161        }
162    }
163
164    /// Set object threadgroup memory length.
165    ///
166    /// C++ equivalent: `void setObjectThreadgroupMemoryLength(NS::UInteger, NS::UInteger)`
167    pub fn set_object_threadgroup_memory_length(&self, length: UInteger, index: UInteger) {
168        unsafe {
169            msg_send_2::<(), UInteger, UInteger>(
170                self.as_ptr(),
171                sel!(setObjectThreadgroupMemoryLength: atIndex:),
172                length,
173                index,
174            );
175        }
176    }
177
178    /// Set the cull mode.
179    ///
180    /// C++ equivalent: `void setCullMode(CullMode)`
181    #[inline]
182    pub fn set_cull_mode(&self, mode: CullMode) {
183        unsafe {
184            msg_send_1::<(), CullMode>(self.as_ptr(), sel!(setCullMode:), mode);
185        }
186    }
187
188    /// Set the depth bias.
189    ///
190    /// C++ equivalent: `void setDepthBias(float, float, float)`
191    pub fn set_depth_bias(&self, depth_bias: f32, slope_scale: f32, clamp: f32) {
192        unsafe {
193            msg_send_3::<(), f32, f32, f32>(
194                self.as_ptr(),
195                sel!(setDepthBias: slopeScale: clamp:),
196                depth_bias,
197                slope_scale,
198                clamp,
199            );
200        }
201    }
202
203    /// Set the depth clip mode.
204    ///
205    /// C++ equivalent: `void setDepthClipMode(DepthClipMode)`
206    #[inline]
207    pub fn set_depth_clip_mode(&self, mode: DepthClipMode) {
208        unsafe {
209            msg_send_1::<(), DepthClipMode>(self.as_ptr(), sel!(setDepthClipMode:), mode);
210        }
211    }
212
213    /// Set the depth stencil state.
214    ///
215    /// C++ equivalent: `void setDepthStencilState(const DepthStencilState*)`
216    pub fn set_depth_stencil_state(&self, state: &DepthStencilState) {
217        unsafe {
218            msg_send_1::<(), *const c_void>(
219                self.as_ptr(),
220                sel!(setDepthStencilState:),
221                state.as_ptr(),
222            );
223        }
224    }
225
226    /// Set the front facing winding.
227    ///
228    /// C++ equivalent: `void setFrontFacingWinding(Winding)`
229    #[inline]
230    pub fn set_front_facing_winding(&self, winding: Winding) {
231        unsafe {
232            msg_send_1::<(), Winding>(self.as_ptr(), sel!(setFrontFacingWinding:), winding);
233        }
234    }
235
236    /// Set the triangle fill mode.
237    ///
238    /// C++ equivalent: `void setTriangleFillMode(TriangleFillMode)`
239    #[inline]
240    pub fn set_triangle_fill_mode(&self, mode: TriangleFillMode) {
241        unsafe {
242            msg_send_1::<(), TriangleFillMode>(self.as_ptr(), sel!(setTriangleFillMode:), mode);
243        }
244    }
245
246    /// Draw primitives.
247    ///
248    /// C++ equivalent: `void drawPrimitives(...)`
249    pub fn draw_primitives(
250        &self,
251        primitive_type: PrimitiveType,
252        vertex_start: UInteger,
253        vertex_count: UInteger,
254        instance_count: UInteger,
255        base_instance: UInteger,
256    ) {
257        unsafe {
258            mtl_sys::msg_send_5::<(), PrimitiveType, UInteger, UInteger, UInteger, UInteger>(
259                self.as_ptr(),
260                sel!(drawPrimitives: vertexStart: vertexCount: instanceCount: baseInstance:),
261                primitive_type,
262                vertex_start,
263                vertex_count,
264                instance_count,
265                base_instance,
266            );
267        }
268    }
269
270    /// Draw indexed primitives.
271    ///
272    /// C++ equivalent: `void drawIndexedPrimitives(...)`
273    #[allow(clippy::too_many_arguments)]
274    pub fn draw_indexed_primitives(
275        &self,
276        primitive_type: PrimitiveType,
277        index_count: UInteger,
278        index_type: IndexType,
279        index_buffer: &Buffer,
280        index_buffer_offset: UInteger,
281        instance_count: UInteger,
282        base_vertex: Integer,
283        base_instance: UInteger,
284    ) {
285        unsafe {
286            mtl_sys::msg_send_8::<
287                (),
288                PrimitiveType,
289                UInteger,
290                IndexType,
291                *const c_void,
292                UInteger,
293                UInteger,
294                Integer,
295                UInteger,
296            >(
297                self.as_ptr(),
298                sel!(drawIndexedPrimitives: indexCount: indexType: indexBuffer: indexBufferOffset: instanceCount: baseVertex: baseInstance:),
299                primitive_type,
300                index_count,
301                index_type,
302                index_buffer.as_ptr(),
303                index_buffer_offset,
304                instance_count,
305                base_vertex,
306                base_instance,
307            );
308        }
309    }
310
311    /// Draw mesh threadgroups.
312    ///
313    /// C++ equivalent: `void drawMeshThreadgroups(...)`
314    pub fn draw_mesh_threadgroups(
315        &self,
316        threadgroups_per_grid: Size,
317        threads_per_object_threadgroup: Size,
318        threads_per_mesh_threadgroup: Size,
319    ) {
320        unsafe {
321            msg_send_3::<(), Size, Size, Size>(
322                self.as_ptr(),
323                sel!(drawMeshThreadgroups: threadsPerObjectThreadgroup: threadsPerMeshThreadgroup:),
324                threadgroups_per_grid,
325                threads_per_object_threadgroup,
326                threads_per_mesh_threadgroup,
327            );
328        }
329    }
330
331    /// Draw mesh threads.
332    ///
333    /// C++ equivalent: `void drawMeshThreads(...)`
334    pub fn draw_mesh_threads(
335        &self,
336        threads_per_grid: Size,
337        threads_per_object_threadgroup: Size,
338        threads_per_mesh_threadgroup: Size,
339    ) {
340        unsafe {
341            msg_send_3::<(), Size, Size, Size>(
342                self.as_ptr(),
343                sel!(drawMeshThreads: threadsPerObjectThreadgroup: threadsPerMeshThreadgroup:),
344                threads_per_grid,
345                threads_per_object_threadgroup,
346                threads_per_mesh_threadgroup,
347            );
348        }
349    }
350}
351
352impl Referencing for IndirectRenderCommand {
353    #[inline]
354    fn as_ptr(&self) -> *const c_void {
355        self.0.as_ptr()
356    }
357}
358
359// Note: IndirectRenderCommand is not reference counted - it's a view into the ICB
360
361impl std::fmt::Debug for IndirectRenderCommand {
362    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
363        f.debug_struct("IndirectRenderCommand").finish()
364    }
365}