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}