Skip to main content

mtl_gpu/command_buffer/
mod.rs

1//! Metal command buffer.
2//!
3//! Corresponds to `Metal/MTLCommandBuffer.hpp`.
4//!
5//! A command buffer stores encoded commands that the GPU will execute.
6
7use std::ffi::c_void;
8use std::ptr::NonNull;
9
10use mtl_foundation::{Referencing, TimeInterval, UInteger};
11use mtl_sys::{msg_send_0, msg_send_1, sel};
12
13use crate::enums::{CommandBufferErrorOption, CommandBufferStatus, DispatchType};
14
15// ============================================================================
16// CommandBufferDescriptor
17// ============================================================================
18
19/// A descriptor for configuring command buffer creation.
20///
21/// C++ equivalent: `MTL::CommandBufferDescriptor`
22#[repr(transparent)]
23pub struct CommandBufferDescriptor(NonNull<c_void>);
24
25impl CommandBufferDescriptor {
26    /// Create a new CommandBufferDescriptor from a raw pointer.
27    ///
28    /// # Safety
29    ///
30    /// The pointer must be a valid Metal command buffer descriptor object.
31    #[inline]
32    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
33        NonNull::new(ptr).map(Self)
34    }
35
36    /// Get the raw pointer to the descriptor.
37    #[inline]
38    pub fn as_raw(&self) -> *mut c_void {
39        self.0.as_ptr()
40    }
41
42    /// Create a new command buffer descriptor.
43    ///
44    /// C++ equivalent: `CommandBufferDescriptor::alloc()->init()`
45    pub fn new() -> Option<Self> {
46        unsafe {
47            let class = mtl_sys::class!(MTLCommandBufferDescriptor);
48            let obj: *mut c_void = msg_send_0(class.as_ptr(), sel!(alloc));
49            if obj.is_null() {
50                return None;
51            }
52            let obj: *mut c_void = msg_send_0(obj, sel!(init));
53            Self::from_raw(obj)
54        }
55    }
56
57    // =========================================================================
58    // Properties
59    // =========================================================================
60
61    /// Get the error options for the command buffer.
62    ///
63    /// C++ equivalent: `CommandBufferErrorOption errorOptions() const`
64    #[inline]
65    pub fn error_options(&self) -> CommandBufferErrorOption {
66        unsafe { msg_send_0(self.as_ptr(), sel!(errorOptions)) }
67    }
68
69    /// Set the error options for the command buffer.
70    ///
71    /// C++ equivalent: `void setErrorOptions(CommandBufferErrorOption errorOptions)`
72    #[inline]
73    pub fn set_error_options(&self, error_options: CommandBufferErrorOption) {
74        unsafe {
75            msg_send_1::<(), CommandBufferErrorOption>(
76                self.as_ptr(),
77                sel!(setErrorOptions:),
78                error_options,
79            );
80        }
81    }
82
83    /// Check if the command buffer retains references to resources.
84    ///
85    /// C++ equivalent: `bool retainedReferences() const`
86    #[inline]
87    pub fn retained_references(&self) -> bool {
88        unsafe { msg_send_0(self.as_ptr(), sel!(retainedReferences)) }
89    }
90
91    /// Set whether the command buffer retains references to resources.
92    ///
93    /// C++ equivalent: `void setRetainedReferences(bool retainedReferences)`
94    #[inline]
95    pub fn set_retained_references(&self, retained_references: bool) {
96        unsafe {
97            msg_send_1::<(), bool>(
98                self.as_ptr(),
99                sel!(setRetainedReferences:),
100                retained_references,
101            );
102        }
103    }
104
105    /// Get the log state for the command buffer.
106    ///
107    /// C++ equivalent: `LogState* logState() const`
108    ///
109    /// Returns a raw pointer to the log state object.
110    #[inline]
111    pub fn log_state(&self) -> *mut c_void {
112        unsafe { msg_send_0(self.as_ptr(), sel!(logState)) }
113    }
114
115    /// Set the log state for the command buffer.
116    ///
117    /// C++ equivalent: `void setLogState(const LogState* logState)`
118    ///
119    /// # Safety
120    ///
121    /// The log_state pointer must be valid or null.
122    #[inline]
123    pub unsafe fn set_log_state(&self, log_state: *const c_void) {
124        unsafe {
125            msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setLogState:), log_state);
126        }
127    }
128}
129
130impl Default for CommandBufferDescriptor {
131    fn default() -> Self {
132        Self::new().expect("failed to create CommandBufferDescriptor")
133    }
134}
135
136impl Clone for CommandBufferDescriptor {
137    fn clone(&self) -> Self {
138        unsafe {
139            msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
140        }
141        Self(self.0)
142    }
143}
144
145impl Drop for CommandBufferDescriptor {
146    fn drop(&mut self) {
147        unsafe {
148            msg_send_0::<()>(self.as_ptr(), sel!(release));
149        }
150    }
151}
152
153impl Referencing for CommandBufferDescriptor {
154    #[inline]
155    fn as_ptr(&self) -> *const c_void {
156        self.0.as_ptr()
157    }
158}
159
160unsafe impl Send for CommandBufferDescriptor {}
161unsafe impl Sync for CommandBufferDescriptor {}
162
163impl std::fmt::Debug for CommandBufferDescriptor {
164    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165        f.debug_struct("CommandBufferDescriptor")
166            .field("error_options", &self.error_options())
167            .field("retained_references", &self.retained_references())
168            .finish()
169    }
170}
171
172// ============================================================================
173// CommandBuffer
174// ============================================================================
175
176/// A buffer that stores encoded commands for the GPU to execute.
177///
178/// C++ equivalent: `MTL::CommandBuffer`
179///
180/// Command buffers are transient objects - you create them, encode commands,
181/// commit them for execution, and then discard them.
182#[repr(transparent)]
183pub struct CommandBuffer(pub(crate) NonNull<c_void>);
184
185impl CommandBuffer {
186    /// Create a CommandBuffer from a raw pointer.
187    ///
188    /// # Safety
189    ///
190    /// The pointer must be a valid Metal command buffer object.
191    #[inline]
192    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
193        NonNull::new(ptr).map(Self)
194    }
195
196    /// Get the raw pointer to the command buffer.
197    #[inline]
198    pub fn as_raw(&self) -> *mut c_void {
199        self.0.as_ptr()
200    }
201
202    // =========================================================================
203    // Command Buffer Lifecycle
204    // =========================================================================
205
206    /// Commit the command buffer for execution.
207    ///
208    /// After calling this, you cannot encode any more commands into the buffer.
209    ///
210    /// C++ equivalent: `void commit()`
211    #[inline]
212    pub fn commit(&self) {
213        unsafe {
214            msg_send_0::<()>(self.as_ptr(), sel!(commit));
215        }
216    }
217
218    /// Block until the command buffer completes execution.
219    ///
220    /// C++ equivalent: `void waitUntilCompleted()`
221    #[inline]
222    pub fn wait_until_completed(&self) {
223        unsafe {
224            msg_send_0::<()>(self.as_ptr(), sel!(waitUntilCompleted));
225        }
226    }
227
228    /// Block until the command buffer is scheduled for execution.
229    ///
230    /// C++ equivalent: `void waitUntilScheduled()`
231    #[inline]
232    pub fn wait_until_scheduled(&self) {
233        unsafe {
234            msg_send_0::<()>(self.as_ptr(), sel!(waitUntilScheduled));
235        }
236    }
237
238    /// Enqueue the command buffer for execution.
239    ///
240    /// C++ equivalent: `void enqueue()`
241    #[inline]
242    pub fn enqueue(&self) {
243        unsafe {
244            msg_send_0::<()>(self.as_ptr(), sel!(enqueue));
245        }
246    }
247
248    // =========================================================================
249    // Status and Timing
250    // =========================================================================
251
252    /// Get the current status of the command buffer.
253    ///
254    /// C++ equivalent: `CommandBufferStatus status() const`
255    #[inline]
256    pub fn status(&self) -> CommandBufferStatus {
257        unsafe { msg_send_0(self.as_ptr(), sel!(status)) }
258    }
259
260    /// Get the error if the command buffer failed.
261    ///
262    /// C++ equivalent: `NS::Error* error() const`
263    pub fn error(&self) -> Option<mtl_foundation::Error> {
264        unsafe {
265            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(error));
266            if ptr.is_null() {
267                return None;
268            }
269            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
270            mtl_foundation::Error::from_ptr(ptr)
271        }
272    }
273
274    /// Get the time when the GPU started executing the command buffer.
275    ///
276    /// C++ equivalent: `CFTimeInterval GPUStartTime() const`
277    #[inline]
278    pub fn gpu_start_time(&self) -> TimeInterval {
279        unsafe { msg_send_0(self.as_ptr(), sel!(GPUStartTime)) }
280    }
281
282    /// Get the time when the GPU finished executing the command buffer.
283    ///
284    /// C++ equivalent: `CFTimeInterval GPUEndTime() const`
285    #[inline]
286    pub fn gpu_end_time(&self) -> TimeInterval {
287        unsafe { msg_send_0(self.as_ptr(), sel!(GPUEndTime)) }
288    }
289
290    /// Get the time when the kernel started executing the command buffer.
291    ///
292    /// C++ equivalent: `CFTimeInterval kernelStartTime() const`
293    #[inline]
294    pub fn kernel_start_time(&self) -> TimeInterval {
295        unsafe { msg_send_0(self.as_ptr(), sel!(kernelStartTime)) }
296    }
297
298    /// Get the time when the kernel finished executing the command buffer.
299    ///
300    /// C++ equivalent: `CFTimeInterval kernelEndTime() const`
301    #[inline]
302    pub fn kernel_end_time(&self) -> TimeInterval {
303        unsafe { msg_send_0(self.as_ptr(), sel!(kernelEndTime)) }
304    }
305
306    /// Check if the command buffer retains resources.
307    ///
308    /// C++ equivalent: `bool retainedReferences() const`
309    #[inline]
310    pub fn retained_references(&self) -> bool {
311        unsafe { msg_send_0(self.as_ptr(), sel!(retainedReferences)) }
312    }
313
314    // =========================================================================
315    // Completion Handlers
316    // =========================================================================
317
318    /// Add a handler to be called when the command buffer completes.
319    ///
320    /// C++ equivalent: `void addCompletedHandler(void (^)(CommandBuffer*))`
321    pub fn add_completed_handler<F>(&self, handler: F)
322    where
323        F: Fn(&CommandBuffer) + Send + 'static,
324    {
325        // Use heap-allocated block to ensure it outlives this function
326        let block = mtl_sys::HeapOneArgBlock::from_fn(move |cmd_buf: *mut c_void| {
327            unsafe {
328                if let Some(buf) = CommandBuffer::from_raw(cmd_buf) {
329                    handler(&buf);
330                    // Don't drop - we don't own this reference
331                    std::mem::forget(buf);
332                }
333            }
334        });
335
336        unsafe {
337            msg_send_1::<(), *const c_void>(
338                self.as_ptr(),
339                sel!(addCompletedHandler:),
340                block.as_ptr(),
341            );
342        }
343
344        // The block is heap-allocated and retained by Metal - don't drop the wrapper
345        std::mem::forget(block);
346    }
347
348    /// Add a handler to be called when the command buffer is scheduled.
349    ///
350    /// C++ equivalent: `void addScheduledHandler(void (^)(CommandBuffer*))`
351    pub fn add_scheduled_handler<F>(&self, handler: F)
352    where
353        F: Fn(&CommandBuffer) + Send + 'static,
354    {
355        // Use heap-allocated block to ensure it outlives this function
356        let block = mtl_sys::HeapOneArgBlock::from_fn(move |cmd_buf: *mut c_void| unsafe {
357            if let Some(buf) = CommandBuffer::from_raw(cmd_buf) {
358                handler(&buf);
359                std::mem::forget(buf);
360            }
361        });
362
363        unsafe {
364            msg_send_1::<(), *const c_void>(
365                self.as_ptr(),
366                sel!(addScheduledHandler:),
367                block.as_ptr(),
368            );
369        }
370
371        // The block is heap-allocated and retained by Metal - don't drop the wrapper
372        std::mem::forget(block);
373    }
374
375    // =========================================================================
376    // Drawable Presentation
377    // =========================================================================
378
379    /// Schedule a drawable for presentation at the earliest opportunity.
380    ///
381    /// C++ equivalent: `void presentDrawable(Drawable*)`
382    ///
383    /// # Safety
384    ///
385    /// The drawable pointer must be valid.
386    pub unsafe fn present_drawable(&self, drawable: *const c_void) {
387        unsafe {
388            msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(presentDrawable:), drawable);
389        }
390    }
391
392    /// Schedule a drawable for presentation at a specific time.
393    ///
394    /// C++ equivalent: `void presentDrawable(Drawable*, CFTimeInterval)`
395    ///
396    /// # Safety
397    ///
398    /// The drawable pointer must be valid.
399    pub unsafe fn present_drawable_at_time(&self, drawable: *const c_void, time: TimeInterval) {
400        unsafe {
401            mtl_sys::msg_send_2::<(), *const c_void, TimeInterval>(
402                self.as_ptr(),
403                sel!(presentDrawable: atTime:),
404                drawable,
405                time,
406            );
407        }
408    }
409
410    /// Schedule a drawable for presentation after a minimum duration.
411    ///
412    /// C++ equivalent: `void presentDrawableAfterMinimumDuration(Drawable*, CFTimeInterval)`
413    ///
414    /// # Safety
415    ///
416    /// The drawable pointer must be valid.
417    pub unsafe fn present_drawable_after_minimum_duration(
418        &self,
419        drawable: *const c_void,
420        duration: TimeInterval,
421    ) {
422        unsafe {
423            mtl_sys::msg_send_2::<(), *const c_void, TimeInterval>(
424                self.as_ptr(),
425                sel!(presentDrawable: afterMinimumDuration:),
426                drawable,
427                duration,
428            );
429        }
430    }
431
432    // =========================================================================
433    // Event Signaling
434    // =========================================================================
435
436    /// Encode a signal for an event.
437    ///
438    /// C++ equivalent: `void encodeSignalEvent(const Event*, uint64_t value)`
439    pub fn encode_signal_event(&self, event: &crate::sync::Event, value: u64) {
440        unsafe {
441            mtl_sys::msg_send_2::<(), *const c_void, u64>(
442                self.as_ptr(),
443                sel!(encodeSignalEvent: value:),
444                event.as_ptr(),
445                value,
446            );
447        }
448    }
449
450    /// Encode a wait for an event.
451    ///
452    /// C++ equivalent: `void encodeWait(const Event*, uint64_t value)`
453    pub fn encode_wait_for_event(&self, event: &crate::sync::Event, value: u64) {
454        unsafe {
455            mtl_sys::msg_send_2::<(), *const c_void, u64>(
456                self.as_ptr(),
457                sel!(encodeWaitForEvent: value:),
458                event.as_ptr(),
459                value,
460            );
461        }
462    }
463
464    // =========================================================================
465    // Residency Sets
466    // =========================================================================
467
468    /// Use a residency set for this command buffer.
469    ///
470    /// C++ equivalent: `void useResidencySet(const ResidencySet*)`
471    ///
472    /// # Safety
473    ///
474    /// The residency_set pointer must be valid.
475    pub unsafe fn use_residency_set(&self, residency_set: *const c_void) {
476        unsafe {
477            msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(useResidencySet:), residency_set);
478        }
479    }
480
481    /// Use multiple residency sets for this command buffer.
482    ///
483    /// C++ equivalent: `void useResidencySets(const ResidencySet* const*, NS::UInteger count)`
484    ///
485    /// # Safety
486    ///
487    /// The residency_sets pointer must be valid and point to count valid pointers.
488    pub unsafe fn use_residency_sets(&self, residency_sets: *const *const c_void, count: UInteger) {
489        unsafe {
490            mtl_sys::msg_send_2::<(), *const *const c_void, UInteger>(
491                self.as_ptr(),
492                sel!(useResidencySets: count:),
493                residency_sets,
494                count,
495            );
496        }
497    }
498
499    // =========================================================================
500    // Error Options
501    // =========================================================================
502
503    /// Get the error options for this command buffer.
504    ///
505    /// C++ equivalent: `CommandBufferErrorOption errorOptions() const`
506    #[inline]
507    pub fn error_options(&self) -> CommandBufferErrorOption {
508        unsafe { msg_send_0(self.as_ptr(), sel!(errorOptions)) }
509    }
510
511    // =========================================================================
512    // Command Encoders
513    // =========================================================================
514
515    /// Create a blit command encoder.
516    ///
517    /// C++ equivalent: `BlitCommandEncoder* blitCommandEncoder()`
518    ///
519    /// Returns a raw pointer to the encoder. The caller is responsible for ending encoding.
520    pub fn blit_command_encoder(&self) -> *mut c_void {
521        unsafe {
522            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(blitCommandEncoder));
523            if !ptr.is_null() {
524                let _: *mut c_void = msg_send_0(ptr, sel!(retain));
525            }
526            ptr
527        }
528    }
529
530    /// Create a blit command encoder with a descriptor.
531    ///
532    /// C++ equivalent: `BlitCommandEncoder* blitCommandEncoder(const BlitPassDescriptor*)`
533    ///
534    /// # Safety
535    ///
536    /// The descriptor pointer must be valid.
537    pub unsafe fn blit_command_encoder_with_descriptor(
538        &self,
539        descriptor: *const c_void,
540    ) -> *mut c_void {
541        unsafe {
542            let ptr: *mut c_void = msg_send_1(
543                self.as_ptr(),
544                sel!(blitCommandEncoderWithDescriptor:),
545                descriptor,
546            );
547            if !ptr.is_null() {
548                let _: *mut c_void = msg_send_0(ptr, sel!(retain));
549            }
550            ptr
551        }
552    }
553
554    /// Create a compute command encoder.
555    ///
556    /// C++ equivalent: `ComputeCommandEncoder* computeCommandEncoder()`
557    ///
558    /// Returns a raw pointer to the encoder. The caller is responsible for ending encoding.
559    pub fn compute_command_encoder(&self) -> *mut c_void {
560        unsafe {
561            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(computeCommandEncoder));
562            if !ptr.is_null() {
563                let _: *mut c_void = msg_send_0(ptr, sel!(retain));
564            }
565            ptr
566        }
567    }
568
569    /// Create a compute command encoder with a dispatch type.
570    ///
571    /// C++ equivalent: `ComputeCommandEncoder* computeCommandEncoder(DispatchType)`
572    ///
573    /// Returns a raw pointer to the encoder. The caller is responsible for ending encoding.
574    pub fn compute_command_encoder_with_dispatch_type(
575        &self,
576        dispatch_type: DispatchType,
577    ) -> *mut c_void {
578        unsafe {
579            let ptr: *mut c_void = msg_send_1(
580                self.as_ptr(),
581                sel!(computeCommandEncoderWithDispatchType:),
582                dispatch_type,
583            );
584            if !ptr.is_null() {
585                let _: *mut c_void = msg_send_0(ptr, sel!(retain));
586            }
587            ptr
588        }
589    }
590
591    /// Create a compute command encoder with a descriptor.
592    ///
593    /// C++ equivalent: `ComputeCommandEncoder* computeCommandEncoder(const ComputePassDescriptor*)`
594    ///
595    /// # Safety
596    ///
597    /// The descriptor pointer must be valid.
598    pub unsafe fn compute_command_encoder_with_descriptor(
599        &self,
600        descriptor: *const c_void,
601    ) -> *mut c_void {
602        unsafe {
603            let ptr: *mut c_void = msg_send_1(
604                self.as_ptr(),
605                sel!(computeCommandEncoderWithDescriptor:),
606                descriptor,
607            );
608            if !ptr.is_null() {
609                let _: *mut c_void = msg_send_0(ptr, sel!(retain));
610            }
611            ptr
612        }
613    }
614
615    /// Create a render command encoder.
616    ///
617    /// C++ equivalent: `RenderCommandEncoder* renderCommandEncoder(const RenderPassDescriptor*)`
618    ///
619    /// # Safety
620    ///
621    /// The descriptor pointer must be valid.
622    pub unsafe fn render_command_encoder_with_descriptor(
623        &self,
624        descriptor: *const c_void,
625    ) -> *mut c_void {
626        unsafe {
627            let ptr: *mut c_void = msg_send_1(
628                self.as_ptr(),
629                sel!(renderCommandEncoderWithDescriptor:),
630                descriptor,
631            );
632            if !ptr.is_null() {
633                let _: *mut c_void = msg_send_0(ptr, sel!(retain));
634            }
635            ptr
636        }
637    }
638
639    /// Create a parallel render command encoder.
640    ///
641    /// C++ equivalent: `ParallelRenderCommandEncoder* parallelRenderCommandEncoder(const RenderPassDescriptor*)`
642    ///
643    /// # Safety
644    ///
645    /// The descriptor pointer must be valid.
646    pub unsafe fn parallel_render_command_encoder_with_descriptor(
647        &self,
648        descriptor: *const c_void,
649    ) -> *mut c_void {
650        unsafe {
651            let ptr: *mut c_void = msg_send_1(
652                self.as_ptr(),
653                sel!(parallelRenderCommandEncoderWithDescriptor:),
654                descriptor,
655            );
656            if !ptr.is_null() {
657                let _: *mut c_void = msg_send_0(ptr, sel!(retain));
658            }
659            ptr
660        }
661    }
662
663    /// Create a resource state command encoder.
664    ///
665    /// C++ equivalent: `ResourceStateCommandEncoder* resourceStateCommandEncoder()`
666    ///
667    /// Returns a raw pointer to the encoder. The caller is responsible for ending encoding.
668    pub fn resource_state_command_encoder(&self) -> *mut c_void {
669        unsafe {
670            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(resourceStateCommandEncoder));
671            if !ptr.is_null() {
672                let _: *mut c_void = msg_send_0(ptr, sel!(retain));
673            }
674            ptr
675        }
676    }
677
678    /// Create a resource state command encoder with a descriptor.
679    ///
680    /// C++ equivalent: `ResourceStateCommandEncoder* resourceStateCommandEncoder(const ResourceStatePassDescriptor*)`
681    ///
682    /// # Safety
683    ///
684    /// The descriptor pointer must be valid.
685    pub unsafe fn resource_state_command_encoder_with_descriptor(
686        &self,
687        descriptor: *const c_void,
688    ) -> *mut c_void {
689        unsafe {
690            let ptr: *mut c_void = msg_send_1(
691                self.as_ptr(),
692                sel!(resourceStateCommandEncoderWithDescriptor:),
693                descriptor,
694            );
695            if !ptr.is_null() {
696                let _: *mut c_void = msg_send_0(ptr, sel!(retain));
697            }
698            ptr
699        }
700    }
701
702    /// Create an acceleration structure command encoder.
703    ///
704    /// C++ equivalent: `AccelerationStructureCommandEncoder* accelerationStructureCommandEncoder()`
705    ///
706    /// Returns a raw pointer to the encoder. The caller is responsible for ending encoding.
707    pub fn acceleration_structure_command_encoder(&self) -> *mut c_void {
708        unsafe {
709            let ptr: *mut c_void =
710                msg_send_0(self.as_ptr(), sel!(accelerationStructureCommandEncoder));
711            if !ptr.is_null() {
712                let _: *mut c_void = msg_send_0(ptr, sel!(retain));
713            }
714            ptr
715        }
716    }
717
718    /// Create an acceleration structure command encoder with a descriptor.
719    ///
720    /// C++ equivalent: `AccelerationStructureCommandEncoder* accelerationStructureCommandEncoder(const AccelerationStructurePassDescriptor*)`
721    ///
722    /// # Safety
723    ///
724    /// The descriptor pointer must be valid.
725    pub unsafe fn acceleration_structure_command_encoder_with_descriptor(
726        &self,
727        descriptor: *const c_void,
728    ) -> *mut c_void {
729        unsafe {
730            let ptr: *mut c_void = msg_send_1(
731                self.as_ptr(),
732                sel!(accelerationStructureCommandEncoderWithDescriptor:),
733                descriptor,
734            );
735            if !ptr.is_null() {
736                let _: *mut c_void = msg_send_0(ptr, sel!(retain));
737            }
738            ptr
739        }
740    }
741
742    // =========================================================================
743    // Safe Encoder Creation (with typed descriptors)
744    // =========================================================================
745
746    /// Create a render command encoder with a typed descriptor.
747    ///
748    /// C++ equivalent: `RenderCommandEncoder* renderCommandEncoder(const RenderPassDescriptor*)`
749    ///
750    /// Returns a raw pointer to the encoder. The caller is responsible for ending encoding.
751    pub fn render_command_encoder(
752        &self,
753        descriptor: &crate::pass::RenderPassDescriptor,
754    ) -> *mut c_void {
755        unsafe { self.render_command_encoder_with_descriptor(descriptor.as_ptr()) }
756    }
757
758    /// Create a parallel render command encoder with a typed descriptor.
759    ///
760    /// C++ equivalent: `ParallelRenderCommandEncoder* parallelRenderCommandEncoder(const RenderPassDescriptor*)`
761    ///
762    /// Returns a raw pointer to the encoder. The caller is responsible for ending encoding.
763    pub fn parallel_render_command_encoder(
764        &self,
765        descriptor: &crate::pass::RenderPassDescriptor,
766    ) -> *mut c_void {
767        unsafe { self.parallel_render_command_encoder_with_descriptor(descriptor.as_ptr()) }
768    }
769
770    /// Create a compute command encoder with a typed descriptor.
771    ///
772    /// C++ equivalent: `ComputeCommandEncoder* computeCommandEncoder(const ComputePassDescriptor*)`
773    ///
774    /// Returns a raw pointer to the encoder. The caller is responsible for ending encoding.
775    pub fn compute_command_encoder_with_pass_descriptor(
776        &self,
777        descriptor: &crate::pass::ComputePassDescriptor,
778    ) -> *mut c_void {
779        unsafe { self.compute_command_encoder_with_descriptor(descriptor.as_ptr()) }
780    }
781
782    /// Create a blit command encoder with a typed descriptor.
783    ///
784    /// C++ equivalent: `BlitCommandEncoder* blitCommandEncoder(const BlitPassDescriptor*)`
785    ///
786    /// Returns a raw pointer to the encoder. The caller is responsible for ending encoding.
787    pub fn blit_command_encoder_with_pass_descriptor(
788        &self,
789        descriptor: &crate::pass::BlitPassDescriptor,
790    ) -> *mut c_void {
791        unsafe { self.blit_command_encoder_with_descriptor(descriptor.as_ptr()) }
792    }
793
794    // =========================================================================
795    // Properties
796    // =========================================================================
797
798    /// Get the label for this command buffer.
799    ///
800    /// C++ equivalent: `NS::String* label() const`
801    pub fn label(&self) -> Option<String> {
802        unsafe {
803            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
804            if ptr.is_null() {
805                return None;
806            }
807            let utf8_ptr: *const std::ffi::c_char =
808                mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
809            if utf8_ptr.is_null() {
810                return None;
811            }
812            let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
813            Some(c_str.to_string_lossy().into_owned())
814        }
815    }
816
817    /// Set the label for this command buffer.
818    ///
819    /// C++ equivalent: `void setLabel(const NS::String*)`
820    pub fn set_label(&self, label: &str) {
821        if let Some(ns_label) = mtl_foundation::String::from_str(label) {
822            unsafe {
823                msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
824            }
825        }
826    }
827
828    /// Get the device that created this command buffer.
829    ///
830    /// C++ equivalent: `Device* device() const`
831    pub fn device(&self) -> crate::Device {
832        unsafe {
833            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(device));
834            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
835            crate::Device::from_raw(ptr).expect("command buffer has no device")
836        }
837    }
838
839    /// Get the command queue that created this command buffer.
840    ///
841    /// C++ equivalent: `CommandQueue* commandQueue() const`
842    pub fn command_queue(&self) -> crate::command_queue::CommandQueue {
843        unsafe {
844            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(commandQueue));
845            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
846            crate::command_queue::CommandQueue::from_raw(ptr)
847                .expect("command buffer has no command queue")
848        }
849    }
850
851    // =========================================================================
852    // Debug
853    // =========================================================================
854
855    /// Push a debug group.
856    ///
857    /// C++ equivalent: `void pushDebugGroup(const NS::String*)`
858    pub fn push_debug_group(&self, name: &str) {
859        if let Some(ns_name) = mtl_foundation::String::from_str(name) {
860            unsafe {
861                msg_send_1::<(), *const c_void>(
862                    self.as_ptr(),
863                    sel!(pushDebugGroup:),
864                    ns_name.as_ptr(),
865                );
866            }
867        }
868    }
869
870    /// Pop a debug group.
871    ///
872    /// C++ equivalent: `void popDebugGroup()`
873    #[inline]
874    pub fn pop_debug_group(&self) {
875        unsafe {
876            msg_send_0::<()>(self.as_ptr(), sel!(popDebugGroup));
877        }
878    }
879
880    // =========================================================================
881    // Logging
882    // =========================================================================
883
884    /// Get the log container for this command buffer.
885    ///
886    /// C++ equivalent: `LogContainer* logs() const`
887    ///
888    /// Returns a container of function logs generated during command buffer execution.
889    /// The log container conforms to FastEnumeration and can be iterated.
890    pub fn logs(&self) -> Option<crate::function_log::LogContainer> {
891        unsafe {
892            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(logs));
893            crate::function_log::LogContainer::from_raw(ptr)
894        }
895    }
896}
897
898impl Clone for CommandBuffer {
899    fn clone(&self) -> Self {
900        unsafe {
901            msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
902        }
903        Self(self.0)
904    }
905}
906
907impl Drop for CommandBuffer {
908    fn drop(&mut self) {
909        unsafe {
910            msg_send_0::<()>(self.as_ptr(), sel!(release));
911        }
912    }
913}
914
915impl Referencing for CommandBuffer {
916    #[inline]
917    fn as_ptr(&self) -> *const c_void {
918        self.0.as_ptr()
919    }
920}
921
922unsafe impl Send for CommandBuffer {}
923unsafe impl Sync for CommandBuffer {}
924
925impl std::fmt::Debug for CommandBuffer {
926    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
927        f.debug_struct("CommandBuffer")
928            .field("status", &self.status())
929            .field("label", &self.label())
930            .finish()
931    }
932}
933
934#[cfg(test)]
935mod tests {
936    use super::*;
937
938    #[test]
939    fn test_command_buffer_size() {
940        assert_eq!(
941            std::mem::size_of::<CommandBuffer>(),
942            std::mem::size_of::<*mut c_void>()
943        );
944    }
945}