1use std::ffi::c_void;
6use std::ptr::NonNull;
7
8use mtl_foundation::{Referencing, UInteger};
9use mtl_sys::{msg_send_0, msg_send_1, sel};
10
11use crate::Buffer;
12use crate::Texture;
13use crate::enums::BlitOption;
14use crate::types::{Origin, Region, Size};
15
16#[repr(transparent)]
23pub struct BlitCommandEncoder(pub(crate) NonNull<c_void>);
24
25impl BlitCommandEncoder {
26 #[inline]
32 pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
33 NonNull::new(ptr).map(Self)
34 }
35
36 #[inline]
38 pub fn as_raw(&self) -> *mut c_void {
39 self.0.as_ptr()
40 }
41
42 pub fn device(&self) -> crate::Device {
50 unsafe {
51 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(device));
52 let _: *mut c_void = msg_send_0(ptr, sel!(retain));
53 crate::Device::from_raw(ptr).expect("encoder has no device")
54 }
55 }
56
57 pub fn command_buffer(&self) -> crate::CommandBuffer {
61 unsafe {
62 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(commandBuffer));
63 let _: *mut c_void = msg_send_0(ptr, sel!(retain));
64 crate::CommandBuffer::from_raw(ptr).expect("encoder has no command buffer")
65 }
66 }
67
68 pub fn label(&self) -> Option<String> {
72 unsafe {
73 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
74 if ptr.is_null() {
75 return None;
76 }
77 let utf8_ptr: *const std::ffi::c_char =
78 mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
79 if utf8_ptr.is_null() {
80 return None;
81 }
82 let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
83 Some(c_str.to_string_lossy().into_owned())
84 }
85 }
86
87 pub fn set_label(&self, label: &str) {
91 if let Some(ns_label) = mtl_foundation::String::from_str(label) {
92 unsafe {
93 msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
94 }
95 }
96 }
97
98 #[inline]
102 pub fn end_encoding(&self) {
103 unsafe {
104 msg_send_0::<()>(self.as_ptr(), sel!(endEncoding));
105 }
106 }
107
108 pub fn insert_debug_signpost(&self, string: &str) {
112 if let Some(ns_string) = mtl_foundation::String::from_str(string) {
113 unsafe {
114 msg_send_1::<(), *const c_void>(
115 self.as_ptr(),
116 sel!(insertDebugSignpost:),
117 ns_string.as_ptr(),
118 );
119 }
120 }
121 }
122
123 pub fn push_debug_group(&self, string: &str) {
127 if let Some(ns_string) = mtl_foundation::String::from_str(string) {
128 unsafe {
129 msg_send_1::<(), *const c_void>(
130 self.as_ptr(),
131 sel!(pushDebugGroup:),
132 ns_string.as_ptr(),
133 );
134 }
135 }
136 }
137
138 #[inline]
142 pub fn pop_debug_group(&self) {
143 unsafe {
144 msg_send_0::<()>(self.as_ptr(), sel!(popDebugGroup));
145 }
146 }
147
148 #[inline]
152 pub fn barrier_after_queue_stages(
153 &self,
154 after_stages: crate::enums::Stages,
155 before_stages: crate::enums::Stages,
156 ) {
157 unsafe {
158 mtl_sys::msg_send_2::<(), crate::enums::Stages, crate::enums::Stages>(
159 self.as_ptr(),
160 sel!(barrierAfterQueueStages:beforeQueueStages:),
161 after_stages,
162 before_stages,
163 );
164 }
165 }
166
167 #[allow(clippy::too_many_arguments)]
179 pub unsafe fn copy_from_tensor_ptr(
180 &self,
181 source_tensor: *const c_void,
182 source_origin: *const c_void,
183 source_dimensions: *const c_void,
184 destination_tensor: *const c_void,
185 destination_origin: *const c_void,
186 destination_dimensions: *const c_void,
187 ) {
188 unsafe {
189 mtl_sys::msg_send_6::<
190 (),
191 *const c_void,
192 *const c_void,
193 *const c_void,
194 *const c_void,
195 *const c_void,
196 *const c_void,
197 >(
198 self.as_ptr(),
199 sel!(copyFromTensor: sourceOrigin: sourceDimensions: toTensor: destinationOrigin: destinationDimensions:),
200 source_tensor,
201 source_origin,
202 source_dimensions,
203 destination_tensor,
204 destination_origin,
205 destination_dimensions,
206 );
207 }
208 }
209
210 #[inline]
218 pub fn copy_from_buffer_to_buffer(
219 &self,
220 source_buffer: &Buffer,
221 source_offset: UInteger,
222 destination_buffer: &Buffer,
223 destination_offset: UInteger,
224 size: UInteger,
225 ) {
226 unsafe {
227 mtl_sys::msg_send_5::<(), *const c_void, UInteger, *const c_void, UInteger, UInteger>(
228 self.as_ptr(),
229 sel!(copyFromBuffer: sourceOffset: toBuffer: destinationOffset: size:),
230 source_buffer.as_ptr(),
231 source_offset,
232 destination_buffer.as_ptr(),
233 destination_offset,
234 size,
235 );
236 }
237 }
238
239 #[allow(clippy::too_many_arguments)]
247 pub fn copy_from_buffer_to_texture(
248 &self,
249 source_buffer: &Buffer,
250 source_offset: UInteger,
251 source_bytes_per_row: UInteger,
252 source_bytes_per_image: UInteger,
253 source_size: Size,
254 destination_texture: &Texture,
255 destination_slice: UInteger,
256 destination_level: UInteger,
257 destination_origin: Origin,
258 ) {
259 unsafe {
260 mtl_sys::msg_send_9::<
261 (),
262 *const c_void,
263 UInteger,
264 UInteger,
265 UInteger,
266 Size,
267 *const c_void,
268 UInteger,
269 UInteger,
270 Origin,
271 >(
272 self.as_ptr(),
273 sel!(copyFromBuffer: sourceOffset: sourceBytesPerRow: sourceBytesPerImage: sourceSize: toTexture: destinationSlice: destinationLevel: destinationOrigin:),
274 source_buffer.as_ptr(),
275 source_offset,
276 source_bytes_per_row,
277 source_bytes_per_image,
278 source_size,
279 destination_texture.as_ptr(),
280 destination_slice,
281 destination_level,
282 destination_origin,
283 );
284 }
285 }
286
287 #[allow(clippy::too_many_arguments)]
291 pub fn copy_from_buffer_to_texture_with_options(
292 &self,
293 source_buffer: &Buffer,
294 source_offset: UInteger,
295 source_bytes_per_row: UInteger,
296 source_bytes_per_image: UInteger,
297 source_size: Size,
298 destination_texture: &Texture,
299 destination_slice: UInteger,
300 destination_level: UInteger,
301 destination_origin: Origin,
302 options: BlitOption,
303 ) {
304 unsafe {
305 mtl_sys::msg_send_10::<
306 (),
307 *const c_void,
308 UInteger,
309 UInteger,
310 UInteger,
311 Size,
312 *const c_void,
313 UInteger,
314 UInteger,
315 Origin,
316 BlitOption,
317 >(
318 self.as_ptr(),
319 sel!(copyFromBuffer: sourceOffset: sourceBytesPerRow: sourceBytesPerImage: sourceSize: toTexture: destinationSlice: destinationLevel: destinationOrigin: options:),
320 source_buffer.as_ptr(),
321 source_offset,
322 source_bytes_per_row,
323 source_bytes_per_image,
324 source_size,
325 destination_texture.as_ptr(),
326 destination_slice,
327 destination_level,
328 destination_origin,
329 options,
330 );
331 }
332 }
333
334 #[allow(clippy::too_many_arguments)]
342 pub fn copy_from_texture_to_buffer(
343 &self,
344 source_texture: &Texture,
345 source_slice: UInteger,
346 source_level: UInteger,
347 source_origin: Origin,
348 source_size: Size,
349 destination_buffer: &Buffer,
350 destination_offset: UInteger,
351 destination_bytes_per_row: UInteger,
352 destination_bytes_per_image: UInteger,
353 ) {
354 unsafe {
355 mtl_sys::msg_send_9::<
356 (),
357 *const c_void,
358 UInteger,
359 UInteger,
360 Origin,
361 Size,
362 *const c_void,
363 UInteger,
364 UInteger,
365 UInteger,
366 >(
367 self.as_ptr(),
368 sel!(copyFromTexture: sourceSlice: sourceLevel: sourceOrigin: sourceSize: toBuffer: destinationOffset: destinationBytesPerRow: destinationBytesPerImage:),
369 source_texture.as_ptr(),
370 source_slice,
371 source_level,
372 source_origin,
373 source_size,
374 destination_buffer.as_ptr(),
375 destination_offset,
376 destination_bytes_per_row,
377 destination_bytes_per_image,
378 );
379 }
380 }
381
382 #[allow(clippy::too_many_arguments)]
386 pub fn copy_from_texture_to_buffer_with_options(
387 &self,
388 source_texture: &Texture,
389 source_slice: UInteger,
390 source_level: UInteger,
391 source_origin: Origin,
392 source_size: Size,
393 destination_buffer: &Buffer,
394 destination_offset: UInteger,
395 destination_bytes_per_row: UInteger,
396 destination_bytes_per_image: UInteger,
397 options: BlitOption,
398 ) {
399 unsafe {
400 mtl_sys::msg_send_10::<
401 (),
402 *const c_void,
403 UInteger,
404 UInteger,
405 Origin,
406 Size,
407 *const c_void,
408 UInteger,
409 UInteger,
410 UInteger,
411 BlitOption,
412 >(
413 self.as_ptr(),
414 sel!(copyFromTexture: sourceSlice: sourceLevel: sourceOrigin: sourceSize: toBuffer: destinationOffset: destinationBytesPerRow: destinationBytesPerImage: options:),
415 source_texture.as_ptr(),
416 source_slice,
417 source_level,
418 source_origin,
419 source_size,
420 destination_buffer.as_ptr(),
421 destination_offset,
422 destination_bytes_per_row,
423 destination_bytes_per_image,
424 options,
425 );
426 }
427 }
428
429 #[inline]
437 pub fn copy_from_texture_to_texture(
438 &self,
439 source_texture: &Texture,
440 destination_texture: &Texture,
441 ) {
442 unsafe {
443 mtl_sys::msg_send_2::<(), *const c_void, *const c_void>(
444 self.as_ptr(),
445 sel!(copyFromTexture: toTexture:),
446 source_texture.as_ptr(),
447 destination_texture.as_ptr(),
448 );
449 }
450 }
451
452 #[allow(clippy::too_many_arguments)]
456 pub fn copy_from_texture_to_texture_region(
457 &self,
458 source_texture: &Texture,
459 source_slice: UInteger,
460 source_level: UInteger,
461 source_origin: Origin,
462 source_size: Size,
463 destination_texture: &Texture,
464 destination_slice: UInteger,
465 destination_level: UInteger,
466 destination_origin: Origin,
467 ) {
468 unsafe {
469 mtl_sys::msg_send_9::<
470 (),
471 *const c_void,
472 UInteger,
473 UInteger,
474 Origin,
475 Size,
476 *const c_void,
477 UInteger,
478 UInteger,
479 Origin,
480 >(
481 self.as_ptr(),
482 sel!(copyFromTexture: sourceSlice: sourceLevel: sourceOrigin: sourceSize: toTexture: destinationSlice: destinationLevel: destinationOrigin:),
483 source_texture.as_ptr(),
484 source_slice,
485 source_level,
486 source_origin,
487 source_size,
488 destination_texture.as_ptr(),
489 destination_slice,
490 destination_level,
491 destination_origin,
492 );
493 }
494 }
495
496 #[allow(clippy::too_many_arguments)]
500 pub fn copy_from_texture_to_texture_slices(
501 &self,
502 source_texture: &Texture,
503 source_slice: UInteger,
504 source_level: UInteger,
505 destination_texture: &Texture,
506 destination_slice: UInteger,
507 destination_level: UInteger,
508 slice_count: UInteger,
509 level_count: UInteger,
510 ) {
511 unsafe {
512 mtl_sys::msg_send_8::<
513 (),
514 *const c_void,
515 UInteger,
516 UInteger,
517 *const c_void,
518 UInteger,
519 UInteger,
520 UInteger,
521 UInteger,
522 >(
523 self.as_ptr(),
524 sel!(copyFromTexture: sourceSlice: sourceLevel: toTexture: destinationSlice: destinationLevel: sliceCount: levelCount:),
525 source_texture.as_ptr(),
526 source_slice,
527 source_level,
528 destination_texture.as_ptr(),
529 destination_slice,
530 destination_level,
531 slice_count,
532 level_count,
533 );
534 }
535 }
536
537 pub fn fill_buffer(&self, buffer: &Buffer, offset: UInteger, length: UInteger, value: u8) {
545 let range = mtl_foundation::Range::new(offset, length);
546 unsafe {
547 mtl_sys::msg_send_3::<(), *const c_void, mtl_foundation::Range, u8>(
548 self.as_ptr(),
549 sel!(fillBuffer: range: value:),
550 buffer.as_ptr(),
551 range,
552 value,
553 );
554 }
555 }
556
557 #[inline]
565 pub fn generate_mipmaps(&self, texture: &Texture) {
566 unsafe {
567 msg_send_1::<(), *const c_void>(
568 self.as_ptr(),
569 sel!(generateMipmapsForTexture:),
570 texture.as_ptr(),
571 );
572 }
573 }
574
575 #[inline]
583 pub fn optimize_contents_for_cpu_access(&self, texture: &Texture) {
584 unsafe {
585 msg_send_1::<(), *const c_void>(
586 self.as_ptr(),
587 sel!(optimizeContentsForCPUAccess:),
588 texture.as_ptr(),
589 );
590 }
591 }
592
593 #[inline]
597 pub fn optimize_contents_for_cpu_access_slice(
598 &self,
599 texture: &Texture,
600 slice: UInteger,
601 level: UInteger,
602 ) {
603 unsafe {
604 mtl_sys::msg_send_3::<(), *const c_void, UInteger, UInteger>(
605 self.as_ptr(),
606 sel!(optimizeContentsForCPUAccess: slice: level:),
607 texture.as_ptr(),
608 slice,
609 level,
610 );
611 }
612 }
613
614 #[inline]
618 pub fn optimize_contents_for_gpu_access(&self, texture: &Texture) {
619 unsafe {
620 msg_send_1::<(), *const c_void>(
621 self.as_ptr(),
622 sel!(optimizeContentsForGPUAccess:),
623 texture.as_ptr(),
624 );
625 }
626 }
627
628 #[inline]
632 pub fn optimize_contents_for_gpu_access_slice(
633 &self,
634 texture: &Texture,
635 slice: UInteger,
636 level: UInteger,
637 ) {
638 unsafe {
639 mtl_sys::msg_send_3::<(), *const c_void, UInteger, UInteger>(
640 self.as_ptr(),
641 sel!(optimizeContentsForGPUAccess: slice: level:),
642 texture.as_ptr(),
643 slice,
644 level,
645 );
646 }
647 }
648
649 #[inline]
663 pub unsafe fn synchronize_resource_ptr(&self, resource: *const c_void) {
664 unsafe {
665 msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(synchronizeResource:), resource);
666 }
667 }
668
669 #[inline]
673 pub fn synchronize_buffer(&self, buffer: &Buffer) {
674 unsafe { self.synchronize_resource_ptr(buffer.as_ptr()) };
675 }
676
677 #[inline]
681 pub fn synchronize_texture(&self, texture: &Texture) {
682 unsafe { self.synchronize_resource_ptr(texture.as_ptr()) };
683 }
684
685 #[inline]
689 pub fn synchronize_texture_slice(&self, texture: &Texture, slice: UInteger, level: UInteger) {
690 unsafe {
691 mtl_sys::msg_send_3::<(), *const c_void, UInteger, UInteger>(
692 self.as_ptr(),
693 sel!(synchronizeTexture: slice: level:),
694 texture.as_ptr(),
695 slice,
696 level,
697 );
698 }
699 }
700
701 #[inline]
713 pub unsafe fn update_fence_ptr(&self, fence: *const c_void) {
714 unsafe {
715 msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(updateFence:), fence);
716 }
717 }
718
719 #[inline]
723 pub fn update_fence(&self, fence: &crate::Fence) {
724 unsafe { self.update_fence_ptr(fence.as_ptr()) };
725 }
726
727 #[inline]
735 pub unsafe fn wait_for_fence_ptr(&self, fence: *const c_void) {
736 unsafe {
737 msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(waitForFence:), fence);
738 }
739 }
740
741 #[inline]
745 pub fn wait_for_fence(&self, fence: &crate::Fence) {
746 unsafe { self.wait_for_fence_ptr(fence.as_ptr()) };
747 }
748
749 #[allow(clippy::too_many_arguments)]
757 pub fn get_texture_access_counters(
758 &self,
759 texture: &Texture,
760 region: Region,
761 mip_level: UInteger,
762 slice: UInteger,
763 reset_counters: bool,
764 counters_buffer: &Buffer,
765 counters_buffer_offset: UInteger,
766 ) {
767 unsafe {
768 mtl_sys::msg_send_7::<
769 (),
770 *const c_void,
771 Region,
772 UInteger,
773 UInteger,
774 bool,
775 *const c_void,
776 UInteger,
777 >(
778 self.as_ptr(),
779 sel!(getTextureAccessCounters: region: mipLevel: slice: resetCounters: countersBuffer: countersBufferOffset:),
780 texture.as_ptr(),
781 region,
782 mip_level,
783 slice,
784 reset_counters,
785 counters_buffer.as_ptr(),
786 counters_buffer_offset,
787 );
788 }
789 }
790
791 pub fn reset_texture_access_counters(
795 &self,
796 texture: &Texture,
797 region: Region,
798 mip_level: UInteger,
799 slice: UInteger,
800 ) {
801 unsafe {
802 mtl_sys::msg_send_4::<(), *const c_void, Region, UInteger, UInteger>(
803 self.as_ptr(),
804 sel!(resetTextureAccessCounters: region: mipLevel: slice:),
805 texture.as_ptr(),
806 region,
807 mip_level,
808 slice,
809 );
810 }
811 }
812
813 pub unsafe fn copy_indirect_command_buffer_ptr(
825 &self,
826 source: *const c_void,
827 source_offset: UInteger,
828 source_length: UInteger,
829 destination: *const c_void,
830 destination_index: UInteger,
831 ) {
832 let range = mtl_foundation::Range::new(source_offset, source_length);
833 unsafe {
834 mtl_sys::msg_send_4::<
835 (),
836 *const c_void,
837 mtl_foundation::Range,
838 *const c_void,
839 UInteger,
840 >(
841 self.as_ptr(),
842 sel!(copyIndirectCommandBuffer: sourceRange: destination: destinationIndex:),
843 source,
844 range,
845 destination,
846 destination_index,
847 );
848 }
849 }
850
851 pub unsafe fn optimize_indirect_command_buffer_ptr(
859 &self,
860 indirect_command_buffer: *const c_void,
861 offset: UInteger,
862 length: UInteger,
863 ) {
864 let range = mtl_foundation::Range::new(offset, length);
865 unsafe {
866 mtl_sys::msg_send_2::<(), *const c_void, mtl_foundation::Range>(
867 self.as_ptr(),
868 sel!(optimizeIndirectCommandBuffer: withRange:),
869 indirect_command_buffer,
870 range,
871 );
872 }
873 }
874
875 pub unsafe fn reset_commands_in_buffer_ptr(
883 &self,
884 buffer: *const c_void,
885 offset: UInteger,
886 length: UInteger,
887 ) {
888 let range = mtl_foundation::Range::new(offset, length);
889 unsafe {
890 mtl_sys::msg_send_2::<(), *const c_void, mtl_foundation::Range>(
891 self.as_ptr(),
892 sel!(resetCommandsInBuffer: withRange:),
893 buffer,
894 range,
895 );
896 }
897 }
898
899 pub unsafe fn sample_counters_in_buffer_ptr(
911 &self,
912 sample_buffer: *const c_void,
913 sample_index: UInteger,
914 barrier: bool,
915 ) {
916 unsafe {
917 mtl_sys::msg_send_3::<(), *const c_void, UInteger, bool>(
918 self.as_ptr(),
919 sel!(sampleCountersInBuffer: atSampleIndex: withBarrier:),
920 sample_buffer,
921 sample_index,
922 barrier,
923 );
924 }
925 }
926
927 pub unsafe fn resolve_counters_ptr(
935 &self,
936 sample_buffer: *const c_void,
937 offset: UInteger,
938 length: UInteger,
939 destination_buffer: &Buffer,
940 destination_offset: UInteger,
941 ) {
942 let range = mtl_foundation::Range::new(offset, length);
943 unsafe {
944 mtl_sys::msg_send_4::<
945 (),
946 *const c_void,
947 mtl_foundation::Range,
948 *const c_void,
949 UInteger,
950 >(
951 self.as_ptr(),
952 sel!(resolveCounters: inRange: destinationBuffer: destinationOffset:),
953 sample_buffer,
954 range,
955 destination_buffer.as_ptr(),
956 destination_offset,
957 );
958 }
959 }
960}
961
962impl Clone for BlitCommandEncoder {
963 fn clone(&self) -> Self {
964 unsafe {
965 msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
966 }
967 Self(self.0)
968 }
969}
970
971impl Drop for BlitCommandEncoder {
972 fn drop(&mut self) {
973 unsafe {
974 msg_send_0::<()>(self.as_ptr(), sel!(release));
975 }
976 }
977}
978
979impl Referencing for BlitCommandEncoder {
980 #[inline]
981 fn as_ptr(&self) -> *const c_void {
982 self.0.as_ptr()
983 }
984}
985
986unsafe impl Send for BlitCommandEncoder {}
987unsafe impl Sync for BlitCommandEncoder {}
988
989impl std::fmt::Debug for BlitCommandEncoder {
990 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
991 f.debug_struct("BlitCommandEncoder")
992 .field("label", &self.label())
993 .finish()
994 }
995}
996
997#[cfg(test)]
998mod tests {
999 use super::*;
1000
1001 #[test]
1002 fn test_blit_encoder_size() {
1003 assert_eq!(
1004 std::mem::size_of::<BlitCommandEncoder>(),
1005 std::mem::size_of::<*mut c_void>()
1006 );
1007 }
1008}