Skip to main content

apple_metal/
command.rs

1use crate::{
2    ffi, util::take_optional_string, CommandBuffer, CommandQueue, ComputePipelineState,
3    CounterSampleBuffer, DepthStencilState, Event, Fence, MetalBuffer, MetalTexture,
4    RenderPipelineState, SamplerState,
5};
6use core::ffi::c_void;
7use core::ops::Range;
8
9macro_rules! opaque_encoder {
10    ($(#[$meta:meta])* pub struct $name:ident;) => {
11        $(#[$meta])*
12/// Mirrors the `Metal` framework counterpart for this type.
13        pub struct $name {
14            ptr: *mut c_void,
15        }
16
17        impl Drop for $name {
18            fn drop(&mut self) {
19                if !self.ptr.is_null() {
20                    unsafe { ffi::am_object_release(self.ptr) };
21                    self.ptr = core::ptr::null_mut();
22                }
23            }
24        }
25
26        impl $name {
27/// Mirrors the `Metal` framework constant `fn`.
28            #[must_use]
29            pub const fn as_ptr(&self) -> *mut c_void {
30                self.ptr
31            }
32
33            fn wrap(ptr: *mut c_void) -> Option<Self> {
34                if ptr.is_null() {
35                    None
36                } else {
37                    Some(Self { ptr })
38                }
39            }
40        }
41    };
42}
43
44/// `MTLCommandBufferStatus` enum values.
45pub mod command_buffer_status {
46    /// Mirrors the `Metal` framework constant `NOT_ENQUEUED`.
47    pub const NOT_ENQUEUED: usize = 0;
48    /// Mirrors the `Metal` framework constant `ENQUEUED`.
49    pub const ENQUEUED: usize = 1;
50    /// Mirrors the `Metal` framework constant `COMMITTED`.
51    pub const COMMITTED: usize = 2;
52    /// Mirrors the `Metal` framework constant `SCHEDULED`.
53    pub const SCHEDULED: usize = 3;
54    /// Mirrors the `Metal` framework constant `COMPLETED`.
55    pub const COMPLETED: usize = 4;
56    /// Mirrors the `Metal` framework constant `ERROR`.
57    pub const ERROR: usize = 5;
58}
59
60opaque_encoder!(
61    /// Apple's `id<MTLBlitCommandEncoder>` — encodes buffer and texture copy work.
62    pub struct BlitCommandEncoder;
63);
64opaque_encoder!(
65    /// Apple's `id<MTLComputeCommandEncoder>` — encodes compute dispatches.
66    pub struct ComputeCommandEncoder;
67);
68opaque_encoder!(
69    /// Apple's `id<MTLRenderCommandEncoder>` — encodes render passes.
70    pub struct RenderCommandEncoder;
71);
72
73impl CommandQueue {
74    /// Create a new command buffer that does not retain resources it references.
75    #[must_use]
76    pub fn new_command_buffer_with_unretained_references(&self) -> Option<CommandBuffer> {
77        let ptr = unsafe {
78            ffi::am_command_queue_new_command_buffer_with_unretained_references(self.as_ptr())
79        };
80        if ptr.is_null() {
81            None
82        } else {
83            Some(unsafe { CommandBuffer::from_retained_ptr(ptr) })
84        }
85    }
86}
87
88impl CommandBuffer {
89    /// Enqueue the command buffer on its queue without immediately committing it.
90    pub fn enqueue(&self) {
91        unsafe { ffi::am_command_buffer_enqueue(self.as_ptr()) };
92    }
93
94    /// Block the current thread until Metal schedules the command buffer.
95    pub fn wait_until_scheduled(&self) {
96        unsafe { ffi::am_command_buffer_wait_until_scheduled(self.as_ptr()) };
97    }
98
99    /// Current `MTLCommandBufferStatus` value — see [`command_buffer_status`].
100    #[must_use]
101    pub fn status(&self) -> usize {
102        unsafe { ffi::am_command_buffer_status(self.as_ptr()) }
103    }
104
105    /// Localized Metal error string for a failed command buffer.
106    #[must_use]
107    pub fn error(&self) -> Option<String> {
108        unsafe { take_optional_string(ffi::am_command_buffer_error_message(self.as_ptr())) }
109    }
110
111    /// Create a standalone blit command encoder for this command buffer.
112    #[must_use]
113    pub fn new_blit_command_encoder(&self) -> Option<BlitCommandEncoder> {
114        BlitCommandEncoder::wrap(unsafe {
115            ffi::am_command_buffer_new_blit_command_encoder(self.as_ptr())
116        })
117    }
118
119    /// Create a standalone compute command encoder for this command buffer.
120    #[must_use]
121    pub fn new_compute_command_encoder(&self) -> Option<ComputeCommandEncoder> {
122        ComputeCommandEncoder::wrap(unsafe {
123            ffi::am_command_buffer_new_compute_command_encoder(self.as_ptr())
124        })
125    }
126
127    /// Create a render command encoder that renders into `texture`.
128    #[must_use]
129    pub fn new_render_command_encoder(
130        &self,
131        texture: &MetalTexture,
132        load_action: usize,
133        store_action: usize,
134        clear_color: [f64; 4],
135    ) -> Option<RenderCommandEncoder> {
136        RenderCommandEncoder::wrap(unsafe {
137            ffi::am_command_buffer_new_render_command_encoder(
138                self.as_ptr(),
139                texture.as_ptr(),
140                load_action,
141                store_action,
142                clear_color[0],
143                clear_color[1],
144                clear_color[2],
145                clear_color[3],
146            )
147        })
148    }
149
150    /// Encode a wait until `event` reaches at least `value`.
151    pub fn encode_wait_for_event(&self, event: &Event, value: u64) {
152        unsafe {
153            ffi::am_command_buffer_encode_wait_for_event(self.as_ptr(), event.as_ptr(), value);
154        };
155    }
156
157    /// Encode a signal that updates `event` to `value`.
158    pub fn encode_signal_event(&self, event: &Event, value: u64) {
159        unsafe { ffi::am_command_buffer_encode_signal_event(self.as_ptr(), event.as_ptr(), value) };
160    }
161}
162
163impl BlitCommandEncoder {
164    /// Copy `size` bytes from `src` into `dst`.
165    #[must_use]
166    pub fn copy_buffer(
167        &self,
168        src: &MetalBuffer,
169        src_offset: usize,
170        dst: &MetalBuffer,
171        dst_offset: usize,
172        size: usize,
173    ) -> bool {
174        unsafe {
175            ffi::am_blit_command_encoder_copy_buffer(
176                self.as_ptr(),
177                src.as_ptr(),
178                src_offset,
179                dst.as_ptr(),
180                dst_offset,
181                size,
182            )
183        }
184    }
185
186    /// Fill a byte range of `buffer` with `value`.
187    #[must_use]
188    pub fn fill_buffer(&self, buffer: &MetalBuffer, range: Range<usize>, value: u8) -> bool {
189        let length = range.end.saturating_sub(range.start);
190        unsafe {
191            ffi::am_blit_command_encoder_fill_buffer(
192                self.as_ptr(),
193                buffer.as_ptr(),
194                range.start,
195                length,
196                value,
197            )
198        }
199    }
200
201    /// Sample hardware counters into `sample_buffer`.
202    #[must_use]
203    pub fn sample_counters(
204        &self,
205        sample_buffer: &CounterSampleBuffer,
206        sample_index: usize,
207        barrier: bool,
208    ) -> bool {
209        unsafe {
210            ffi::am_blit_command_encoder_sample_counters(
211                self.as_ptr(),
212                sample_buffer.as_ptr(),
213                sample_index,
214                barrier,
215            )
216        }
217    }
218
219    /// Update `fence` with work encoded so far.
220    pub fn update_fence(&self, fence: &Fence) {
221        unsafe { ffi::am_blit_command_encoder_update_fence(self.as_ptr(), fence.as_ptr()) };
222    }
223
224    /// Wait for `fence` before executing subsequent work.
225    pub fn wait_for_fence(&self, fence: &Fence) {
226        unsafe { ffi::am_blit_command_encoder_wait_for_fence(self.as_ptr(), fence.as_ptr()) };
227    }
228
229    /// Finish encoding commands.
230    pub fn end_encoding(&self) {
231        unsafe { ffi::am_command_encoder_end_encoding(self.as_ptr()) };
232    }
233}
234
235impl ComputeCommandEncoder {
236    /// Bind a compute pipeline state.
237    pub fn set_compute_pipeline_state(&self, pipeline: &ComputePipelineState) {
238        unsafe {
239            ffi::am_compute_command_encoder_set_pipeline_state(self.as_ptr(), pipeline.as_ptr());
240        };
241    }
242
243    /// Bind a buffer at `index`.
244    pub fn set_buffer(&self, buffer: &MetalBuffer, offset: usize, index: usize) {
245        unsafe {
246            ffi::am_compute_command_encoder_set_buffer(
247                self.as_ptr(),
248                buffer.as_ptr(),
249                offset,
250                index,
251            );
252        };
253    }
254
255    /// Bind a texture at `index`.
256    pub fn set_texture(&self, texture: &MetalTexture, index: usize) {
257        unsafe {
258            ffi::am_compute_command_encoder_set_texture(self.as_ptr(), texture.as_ptr(), index);
259        };
260    }
261
262    /// Bind a sampler state at `index`.
263    pub fn set_sampler_state(&self, sampler: &SamplerState, index: usize) {
264        unsafe {
265            ffi::am_compute_command_encoder_set_sampler_state(
266                self.as_ptr(),
267                sampler.as_ptr(),
268                index,
269            );
270        };
271    }
272
273    /// Bind a visible function table at `index`.
274    pub fn set_visible_function_table(&self, table: &crate::VisibleFunctionTable, index: usize) {
275        unsafe {
276            ffi::am_compute_command_encoder_set_visible_function_table(
277                self.as_ptr(),
278                table.as_ptr(),
279                index,
280            );
281        };
282    }
283
284    /// Bind an intersection function table at `index`.
285    pub fn set_intersection_function_table(
286        &self,
287        table: &crate::IntersectionFunctionTable,
288        index: usize,
289    ) {
290        unsafe {
291            ffi::am_compute_command_encoder_set_intersection_function_table(
292                self.as_ptr(),
293                table.as_ptr(),
294                index,
295            );
296        };
297    }
298
299    /// Bind an acceleration structure at `index`.
300    pub fn set_acceleration_structure(
301        &self,
302        acceleration_structure: &crate::AccelerationStructure,
303        index: usize,
304    ) {
305        unsafe {
306            ffi::am_compute_command_encoder_set_acceleration_structure(
307                self.as_ptr(),
308                acceleration_structure.as_ptr(),
309                index,
310            );
311        };
312    }
313
314    /// Dispatch threadgroups of fixed size.
315    pub fn dispatch_threadgroups(
316        &self,
317        threadgroups: (usize, usize, usize),
318        threads_per_threadgroup: (usize, usize, usize),
319    ) {
320        unsafe {
321            ffi::am_compute_command_encoder_dispatch_threadgroups(
322                self.as_ptr(),
323                threadgroups.0,
324                threadgroups.1,
325                threadgroups.2,
326                threads_per_threadgroup.0,
327                threads_per_threadgroup.1,
328                threads_per_threadgroup.2,
329            );
330        };
331    }
332
333    /// Dispatch an arbitrary thread grid.
334    pub fn dispatch_threads(
335        &self,
336        threads: (usize, usize, usize),
337        threads_per_threadgroup: (usize, usize, usize),
338    ) {
339        unsafe {
340            ffi::am_compute_command_encoder_dispatch_threads(
341                self.as_ptr(),
342                threads.0,
343                threads.1,
344                threads.2,
345                threads_per_threadgroup.0,
346                threads_per_threadgroup.1,
347                threads_per_threadgroup.2,
348            );
349        };
350    }
351
352    /// Update `fence` with work encoded so far.
353    pub fn update_fence(&self, fence: &Fence) {
354        unsafe { ffi::am_compute_command_encoder_update_fence(self.as_ptr(), fence.as_ptr()) };
355    }
356
357    /// Wait for `fence` before executing subsequent work.
358    pub fn wait_for_fence(&self, fence: &Fence) {
359        unsafe { ffi::am_compute_command_encoder_wait_for_fence(self.as_ptr(), fence.as_ptr()) };
360    }
361
362    /// Finish encoding commands.
363    pub fn end_encoding(&self) {
364        unsafe { ffi::am_command_encoder_end_encoding(self.as_ptr()) };
365    }
366}
367
368impl RenderCommandEncoder {
369    /// Bind a render pipeline state.
370    pub fn set_render_pipeline_state(&self, pipeline: &RenderPipelineState) {
371        unsafe {
372            ffi::am_render_command_encoder_set_render_pipeline_state(
373                self.as_ptr(),
374                pipeline.as_ptr(),
375            );
376        };
377    }
378
379    /// Bind a vertex buffer at `index`.
380    pub fn set_vertex_buffer(&self, buffer: &MetalBuffer, offset: usize, index: usize) {
381        unsafe {
382            ffi::am_render_command_encoder_set_vertex_buffer(
383                self.as_ptr(),
384                buffer.as_ptr(),
385                offset,
386                index,
387            );
388        };
389    }
390
391    /// Bind a fragment sampler state at `index`.
392    pub fn set_fragment_sampler_state(&self, sampler: &SamplerState, index: usize) {
393        unsafe {
394            ffi::am_render_command_encoder_set_fragment_sampler_state(
395                self.as_ptr(),
396                sampler.as_ptr(),
397                index,
398            );
399        };
400    }
401
402    /// Bind a depth/stencil state object.
403    pub fn set_depth_stencil_state(&self, state: &DepthStencilState) {
404        unsafe {
405            ffi::am_render_command_encoder_set_depth_stencil_state(self.as_ptr(), state.as_ptr());
406        };
407    }
408
409    /// Draw a non-indexed primitive range.
410    pub fn draw_primitives(&self, primitive_type: usize, vertex_start: usize, vertex_count: usize) {
411        unsafe {
412            ffi::am_render_command_encoder_draw_primitives(
413                self.as_ptr(),
414                primitive_type,
415                vertex_start,
416                vertex_count,
417            );
418        };
419    }
420
421    /// Update `fence` with work encoded so far.
422    pub fn update_fence(&self, fence: &Fence) {
423        unsafe { ffi::am_render_command_encoder_update_fence(self.as_ptr(), fence.as_ptr()) };
424    }
425
426    /// Wait for `fence` before executing subsequent work.
427    pub fn wait_for_fence(&self, fence: &Fence) {
428        unsafe { ffi::am_render_command_encoder_wait_for_fence(self.as_ptr(), fence.as_ptr()) };
429    }
430
431    /// Finish encoding commands.
432    pub fn end_encoding(&self) {
433        unsafe { ffi::am_command_encoder_end_encoding(self.as_ptr()) };
434    }
435}