1use crate::{
2 ffi,
3 util::{c_string, take_optional_string, take_string},
4 CommandQueue, ComputePipelineState, MetalBuffer, MetalDevice, MetalFunction, MetalTexture,
5 TextureDescriptor,
6};
7use core::ffi::c_void;
8use core::ops::Range;
9use std::path::Path;
10
11macro_rules! opaque_handle {
12 ($(#[$meta:meta])* pub struct $name:ident;) => {
13 $(#[$meta])*
14pub struct $name {
16 ptr: *mut c_void,
17 }
18
19 unsafe impl Send for $name {}
23 unsafe impl Sync for $name {}
24
25 impl Drop for $name {
26 fn drop(&mut self) {
27 if !self.ptr.is_null() {
28 unsafe { ffi::am_object_release(self.ptr) };
32 self.ptr = core::ptr::null_mut();
33 }
34 }
35 }
36
37 impl $name {
38#[must_use]
40 pub const fn as_ptr(&self) -> *mut c_void {
41 self.ptr
42 }
43
44 fn wrap(ptr: *mut c_void) -> Option<Self> {
45 if ptr.is_null() {
46 None
47 } else {
48 Some(Self { ptr })
49 }
50 }
51 }
52 };
53}
54
55pub mod indirect_command_type {
57 pub const DRAW: usize = 1 << 0;
59 pub const DRAW_INDEXED: usize = 1 << 1;
61 pub const CONCURRENT_DISPATCH: usize = 1 << 5;
63 pub const CONCURRENT_DISPATCH_THREADS: usize = 1 << 6;
65}
66
67pub mod counter_sampling_point {
69 pub const AT_STAGE_BOUNDARY: usize = 0;
71 pub const AT_DRAW_BOUNDARY: usize = 1;
73 pub const AT_DISPATCH_BOUNDARY: usize = 2;
75 pub const AT_TILE_DISPATCH_BOUNDARY: usize = 3;
77 pub const AT_BLIT_BOUNDARY: usize = 4;
79}
80
81pub mod log_level {
83 pub const UNDEFINED: usize = 0;
85 pub const DEBUG: usize = 1;
87 pub const INFO: usize = 2;
89 pub const NOTICE: usize = 3;
91 pub const ERROR: usize = 4;
93 pub const FAULT: usize = 5;
95}
96
97pub mod purgeable_state {
99 pub const KEEP_CURRENT: usize = 1;
101 pub const NON_VOLATILE: usize = 2;
103 pub const VOLATILE: usize = 3;
105 pub const EMPTY: usize = 4;
107}
108
109pub mod capture_destination {
111 pub const DEVELOPER_TOOLS: usize = 1;
113 pub const GPU_TRACE_DOCUMENT: usize = 2;
115}
116
117pub mod intersection_function_signature {
119 pub const NONE: usize = 0;
121 pub const INSTANCING: usize = 1 << 0;
123 pub const TRIANGLE_DATA: usize = 1 << 1;
125 pub const WORLD_SPACE_DATA: usize = 1 << 2;
127}
128
129opaque_handle!(
130 pub struct Heap;
132);
133opaque_handle!(
134 pub struct Event;
136);
137opaque_handle!(
138 pub struct Fence;
140);
141opaque_handle!(
142 pub struct DynamicLibrary;
144);
145opaque_handle!(
146 pub struct BinaryArchive;
148);
149opaque_handle!(
150 pub struct ArgumentEncoder;
152);
153opaque_handle!(
154 pub struct IndirectCommandBuffer;
156);
157opaque_handle!(
158 pub struct AccelerationStructure;
160);
161opaque_handle!(
162 pub struct IntersectionFunctionTable;
164);
165opaque_handle!(
166 pub struct VisibleFunctionTable;
168);
169opaque_handle!(
170 pub struct CounterSampleBuffer;
172);
173opaque_handle!(
174 pub struct LogState;
176);
177opaque_handle!(
178 pub struct ResidencySet;
180);
181opaque_handle!(
182 pub struct CaptureManager;
184);
185opaque_handle!(
186 pub struct CaptureScope;
188);
189
190impl MetalDevice {
191 #[must_use]
193 pub fn name(&self) -> String {
194 unsafe { take_string(ffi::am_device_name(self.as_ptr())) }
195 }
196
197 #[must_use]
199 pub fn registry_id(&self) -> u64 {
200 unsafe { ffi::am_device_registry_id(self.as_ptr()) }
201 }
202
203 #[must_use]
205 pub fn supports_dynamic_libraries(&self) -> bool {
206 unsafe { ffi::am_device_supports_dynamic_libraries(self.as_ptr()) }
207 }
208
209 #[must_use]
211 pub fn supports_render_dynamic_libraries(&self) -> bool {
212 unsafe { ffi::am_device_supports_render_dynamic_libraries(self.as_ptr()) }
213 }
214
215 #[must_use]
217 pub fn supports_raytracing(&self) -> bool {
218 unsafe { ffi::am_device_supports_raytracing(self.as_ptr()) }
219 }
220
221 #[must_use]
223 pub fn supports_counter_sampling(&self, sampling_point: usize) -> bool {
224 unsafe { ffi::am_device_supports_counter_sampling(self.as_ptr(), sampling_point) }
225 }
226
227 #[must_use]
229 pub fn counter_set_names(&self) -> Vec<String> {
230 let count = unsafe { ffi::am_device_counter_set_count(self.as_ptr()) };
231 (0..count)
232 .filter_map(|index| unsafe {
233 take_optional_string(ffi::am_device_counter_set_name_at(self.as_ptr(), index))
234 })
235 .collect()
236 }
237
238 #[must_use]
240 pub fn new_command_queue_with_max_command_buffer_count(
241 &self,
242 max_command_buffer_count: usize,
243 ) -> Option<CommandQueue> {
244 let ptr = unsafe {
245 ffi::am_device_new_command_queue_with_max_command_buffer_count(
246 self.as_ptr(),
247 max_command_buffer_count,
248 )
249 };
250 if ptr.is_null() {
251 None
252 } else {
253 Some(unsafe { CommandQueue::from_retained_ptr(ptr) })
254 }
255 }
256
257 #[must_use]
259 pub fn new_command_queue_with_log_state(
260 &self,
261 max_command_buffer_count: usize,
262 log_state: &LogState,
263 ) -> Option<CommandQueue> {
264 let ptr = unsafe {
265 ffi::am_device_new_command_queue_with_log_state(
266 self.as_ptr(),
267 max_command_buffer_count,
268 log_state.as_ptr(),
269 )
270 };
271 if ptr.is_null() {
272 None
273 } else {
274 Some(unsafe { CommandQueue::from_retained_ptr(ptr) })
275 }
276 }
277
278 #[must_use]
280 pub fn new_heap(&self, size: usize, storage_mode: usize) -> Option<Heap> {
281 Heap::wrap(unsafe { ffi::am_device_new_heap(self.as_ptr(), size, storage_mode) })
282 }
283
284 #[must_use]
286 pub fn new_fence(&self) -> Option<Fence> {
287 Fence::wrap(unsafe { ffi::am_device_new_fence(self.as_ptr()) })
288 }
289
290 #[must_use]
292 pub fn new_shared_event(&self) -> Option<Event> {
293 Event::wrap(unsafe { ffi::am_device_new_shared_event(self.as_ptr()) })
294 }
295
296 pub fn new_dynamic_library_with_source(
302 &self,
303 source: &str,
304 install_name: &str,
305 ) -> Result<DynamicLibrary, String> {
306 let source = c_string(source)?;
307 let install_name = c_string(install_name)?;
308 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
309 let ptr = unsafe {
310 ffi::am_device_new_dynamic_library_with_source(
311 self.as_ptr(),
312 source.as_ptr(),
313 install_name.as_ptr(),
314 &mut err,
315 )
316 };
317 DynamicLibrary::wrap(ptr).ok_or_else(|| unsafe {
318 take_optional_string(err)
319 .unwrap_or_else(|| "MTLDevice.makeDynamicLibrary(source:) returned nil".to_string())
320 })
321 }
322
323 pub fn load_dynamic_library(&self, path: &Path) -> Result<DynamicLibrary, String> {
329 let path = c_string(path.to_string_lossy().as_ref())?;
330 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
331 let ptr = unsafe {
332 ffi::am_device_new_dynamic_library_with_url(self.as_ptr(), path.as_ptr(), &mut err)
333 };
334 DynamicLibrary::wrap(ptr).ok_or_else(|| unsafe {
335 take_optional_string(err)
336 .unwrap_or_else(|| "MTLDevice.makeDynamicLibrary(URL:) returned nil".to_string())
337 })
338 }
339
340 pub fn new_binary_archive(&self, path: Option<&Path>) -> Result<BinaryArchive, String> {
346 let owned_path = path
347 .map(|path| c_string(path.to_string_lossy().as_ref()))
348 .transpose()?;
349 let raw_path = owned_path
350 .as_ref()
351 .map_or(core::ptr::null(), |path| path.as_c_str().as_ptr());
352 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
353 let ptr = unsafe { ffi::am_device_new_binary_archive(self.as_ptr(), raw_path, &mut err) };
354 BinaryArchive::wrap(ptr).ok_or_else(|| unsafe {
355 take_optional_string(err)
356 .unwrap_or_else(|| "MTLDevice.makeBinaryArchive returned nil".to_string())
357 })
358 }
359
360 #[must_use]
362 pub fn new_indirect_command_buffer(
363 &self,
364 command_types: usize,
365 max_command_count: usize,
366 max_vertex_buffer_bind_count: usize,
367 max_fragment_buffer_bind_count: usize,
368 max_kernel_buffer_bind_count: usize,
369 options: usize,
370 ) -> Option<IndirectCommandBuffer> {
371 IndirectCommandBuffer::wrap(unsafe {
372 ffi::am_device_new_indirect_command_buffer(
373 self.as_ptr(),
374 command_types,
375 max_command_count,
376 max_vertex_buffer_bind_count,
377 max_fragment_buffer_bind_count,
378 max_kernel_buffer_bind_count,
379 options,
380 )
381 })
382 }
383
384 #[must_use]
386 pub fn new_acceleration_structure_with_size(
387 &self,
388 size: usize,
389 ) -> Option<AccelerationStructure> {
390 AccelerationStructure::wrap(unsafe {
391 ffi::am_device_new_acceleration_structure_with_size(self.as_ptr(), size)
392 })
393 }
394
395 pub fn new_counter_sample_buffer(
401 &self,
402 counter_set_name: &str,
403 sample_count: usize,
404 storage_mode: usize,
405 label: Option<&str>,
406 ) -> Result<CounterSampleBuffer, String> {
407 let counter_set_name = c_string(counter_set_name)?;
408 let label = label.map(c_string).transpose()?;
409 let raw_label = label
410 .as_ref()
411 .map_or(core::ptr::null(), |label| label.as_c_str().as_ptr());
412 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
413 let ptr = unsafe {
414 ffi::am_device_new_counter_sample_buffer(
415 self.as_ptr(),
416 counter_set_name.as_ptr(),
417 sample_count,
418 storage_mode,
419 raw_label,
420 &mut err,
421 )
422 };
423 CounterSampleBuffer::wrap(ptr).ok_or_else(|| unsafe {
424 take_optional_string(err)
425 .unwrap_or_else(|| "MTLDevice.makeCounterSampleBuffer returned nil".to_string())
426 })
427 }
428
429 pub fn new_log_state(&self, level: usize, buffer_size: isize) -> Result<LogState, String> {
435 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
436 let ptr =
437 unsafe { ffi::am_device_new_log_state(self.as_ptr(), level, buffer_size, &mut err) };
438 LogState::wrap(ptr).ok_or_else(|| unsafe {
439 take_optional_string(err)
440 .unwrap_or_else(|| "MTLDevice.makeLogState returned nil".to_string())
441 })
442 }
443
444 pub fn new_residency_set(
450 &self,
451 label: Option<&str>,
452 initial_capacity: usize,
453 ) -> Result<ResidencySet, String> {
454 let label = label.map(c_string).transpose()?;
455 let raw_label = label
456 .as_ref()
457 .map_or(core::ptr::null(), |label| label.as_c_str().as_ptr());
458 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
459 let ptr = unsafe {
460 ffi::am_device_new_residency_set(self.as_ptr(), raw_label, initial_capacity, &mut err)
461 };
462 ResidencySet::wrap(ptr).ok_or_else(|| unsafe {
463 take_optional_string(err)
464 .unwrap_or_else(|| "MTLDevice.makeResidencySet returned nil".to_string())
465 })
466 }
467}
468
469impl CommandQueue {
470 pub fn add_residency_set(&self, residency_set: &ResidencySet) {
472 unsafe { ffi::am_command_queue_add_residency_set(self.as_ptr(), residency_set.as_ptr()) };
473 }
474
475 pub fn remove_residency_set(&self, residency_set: &ResidencySet) {
477 unsafe {
478 ffi::am_command_queue_remove_residency_set(self.as_ptr(), residency_set.as_ptr());
479 };
480 }
481}
482
483impl MetalBuffer {
484 pub fn did_modify_range(&self, range: Range<usize>) {
486 unsafe {
487 ffi::am_buffer_did_modify_range(
488 self.as_ptr(),
489 range.start,
490 range.end.saturating_sub(range.start),
491 );
492 };
493 }
494
495 #[must_use]
497 pub fn new_texture_view_2d(
498 &self,
499 pixel_format: usize,
500 width: usize,
501 height: usize,
502 bytes_per_row: usize,
503 offset: usize,
504 ) -> Option<MetalTexture> {
505 let ptr = unsafe {
506 ffi::am_buffer_new_texture_view_2d(
507 self.as_ptr(),
508 pixel_format,
509 width,
510 height,
511 bytes_per_row,
512 offset,
513 )
514 };
515 if ptr.is_null() {
516 None
517 } else {
518 Some(unsafe { MetalTexture::from_raw(ptr) })
519 }
520 }
521}
522
523impl MetalTexture {
524 #[must_use]
526 pub fn depth(&self) -> usize {
527 unsafe { ffi::am_texture_depth(self.as_ptr()) }
528 }
529
530 #[must_use]
532 pub fn mipmap_level_count(&self) -> usize {
533 unsafe { ffi::am_texture_mipmap_level_count(self.as_ptr()) }
534 }
535
536 #[must_use]
538 pub fn array_length(&self) -> usize {
539 unsafe { ffi::am_texture_array_length(self.as_ptr()) }
540 }
541
542 #[must_use]
544 pub fn usage(&self) -> usize {
545 unsafe { ffi::am_texture_usage(self.as_ptr()) }
546 }
547
548 #[must_use]
550 pub fn storage_mode(&self) -> usize {
551 unsafe { ffi::am_texture_storage_mode(self.as_ptr()) }
552 }
553
554 #[must_use]
556 pub fn replace_region_2d(
557 &self,
558 bytes: &[u8],
559 bytes_per_row: usize,
560 origin: (usize, usize),
561 size: (usize, usize),
562 mipmap_level: usize,
563 ) -> bool {
564 let required = bytes_per_row.saturating_mul(size.1);
565 if bytes.len() < required {
566 return false;
567 }
568 unsafe {
569 ffi::am_texture_replace_region_2d(
570 self.as_ptr(),
571 origin.0,
572 origin.1,
573 size.0,
574 size.1,
575 mipmap_level,
576 bytes.as_ptr(),
577 bytes_per_row,
578 )
579 }
580 }
581
582 #[must_use]
584 pub fn read_bytes_2d(
585 &self,
586 out: &mut [u8],
587 bytes_per_row: usize,
588 origin: (usize, usize),
589 size: (usize, usize),
590 mipmap_level: usize,
591 ) -> bool {
592 unsafe {
593 ffi::am_texture_get_bytes_2d(
594 self.as_ptr(),
595 out.as_mut_ptr(),
596 out.len(),
597 bytes_per_row,
598 origin.0,
599 origin.1,
600 size.0,
601 size.1,
602 mipmap_level,
603 )
604 }
605 }
606
607 #[must_use]
609 pub fn new_view(&self, pixel_format: usize) -> Option<Self> {
610 let ptr = unsafe { ffi::am_texture_new_view(self.as_ptr(), pixel_format) };
611 if ptr.is_null() {
612 None
613 } else {
614 Some(unsafe { Self::from_raw(ptr) })
615 }
616 }
617}
618
619impl ComputePipelineState {
620 #[must_use]
622 pub fn thread_execution_width(&self) -> usize {
623 unsafe { ffi::am_compute_pipeline_state_thread_execution_width(self.as_ptr()) }
624 }
625
626 #[must_use]
628 pub fn max_total_threads_per_threadgroup(&self) -> usize {
629 unsafe { ffi::am_compute_pipeline_state_max_total_threads_per_threadgroup(self.as_ptr()) }
630 }
631
632 #[must_use]
634 pub fn new_visible_function_table(
635 &self,
636 function_count: usize,
637 ) -> Option<VisibleFunctionTable> {
638 VisibleFunctionTable::wrap(unsafe {
639 ffi::am_compute_pipeline_state_new_visible_function_table(self.as_ptr(), function_count)
640 })
641 }
642
643 #[must_use]
645 pub fn new_intersection_function_table(
646 &self,
647 function_count: usize,
648 ) -> Option<IntersectionFunctionTable> {
649 IntersectionFunctionTable::wrap(unsafe {
650 ffi::am_compute_pipeline_state_new_intersection_function_table(
651 self.as_ptr(),
652 function_count,
653 )
654 })
655 }
656}
657
658impl MetalFunction {
659 #[must_use]
661 pub fn new_argument_encoder(&self, buffer_index: usize) -> Option<ArgumentEncoder> {
662 ArgumentEncoder::wrap(unsafe {
663 ffi::am_function_new_argument_encoder(self.as_ptr(), buffer_index)
664 })
665 }
666}
667
668impl Heap {
669 #[must_use]
671 pub fn size(&self) -> usize {
672 unsafe { ffi::am_heap_size(self.as_ptr()) }
673 }
674
675 #[must_use]
677 pub fn used_size(&self) -> usize {
678 unsafe { ffi::am_heap_used_size(self.as_ptr()) }
679 }
680
681 #[must_use]
683 pub fn current_allocated_size(&self) -> usize {
684 unsafe { ffi::am_heap_current_allocated_size(self.as_ptr()) }
685 }
686
687 #[must_use]
689 pub fn max_available_size(&self, alignment: usize) -> usize {
690 unsafe { ffi::am_heap_max_available_size(self.as_ptr(), alignment) }
691 }
692
693 #[must_use]
695 pub fn new_buffer(&self, length: usize, options: usize) -> Option<MetalBuffer> {
696 let ptr = unsafe { ffi::am_heap_new_buffer(self.as_ptr(), length, options) };
697 if ptr.is_null() {
698 None
699 } else {
700 Some(unsafe { MetalBuffer::from_retained_ptr(ptr) })
701 }
702 }
703
704 #[must_use]
706 pub fn new_texture(&self, descriptor: TextureDescriptor) -> Option<MetalTexture> {
707 let ptr = unsafe {
708 ffi::am_heap_new_texture_2d(
709 self.as_ptr(),
710 descriptor.pixel_format,
711 descriptor.width,
712 descriptor.height,
713 descriptor.mipmapped,
714 descriptor.usage,
715 descriptor.storage_mode,
716 )
717 };
718 if ptr.is_null() {
719 None
720 } else {
721 Some(unsafe { MetalTexture::from_raw(ptr) })
722 }
723 }
724
725 #[must_use]
727 pub fn new_acceleration_structure_with_size(
728 &self,
729 size: usize,
730 ) -> Option<AccelerationStructure> {
731 AccelerationStructure::wrap(unsafe {
732 ffi::am_heap_new_acceleration_structure_with_size(self.as_ptr(), size)
733 })
734 }
735
736 #[must_use]
738 pub fn set_purgeable_state(&self, state: usize) -> usize {
739 unsafe { ffi::am_heap_set_purgeable_state(self.as_ptr(), state) }
740 }
741}
742
743impl Event {
744 #[must_use]
746 pub fn signaled_value(&self) -> u64 {
747 unsafe { ffi::am_event_signaled_value(self.as_ptr()) }
748 }
749
750 pub fn set_signaled_value(&self, value: u64) {
752 unsafe { ffi::am_event_set_signaled_value(self.as_ptr(), value) };
753 }
754
755 #[must_use]
757 pub fn wait_until_signaled_value(&self, value: u64, timeout_ms: u64) -> bool {
758 unsafe { ffi::am_event_wait_until_signaled_value(self.as_ptr(), value, timeout_ms) }
759 }
760}
761
762impl DynamicLibrary {
763 #[must_use]
765 pub fn install_name(&self) -> String {
766 unsafe { take_string(ffi::am_dynamic_library_install_name(self.as_ptr())) }
767 }
768
769 pub fn serialize_to_file(&self, path: &Path) -> Result<(), String> {
775 let path = c_string(path.to_string_lossy().as_ref())?;
776 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
777 let ok = unsafe {
778 ffi::am_dynamic_library_serialize_to_url(self.as_ptr(), path.as_ptr(), &mut err)
779 };
780 if ok {
781 Ok(())
782 } else {
783 Err(unsafe {
784 take_optional_string(err)
785 .unwrap_or_else(|| "MTLDynamicLibrary.serialize(to:) failed".to_string())
786 })
787 }
788 }
789}
790
791impl BinaryArchive {
792 pub fn add_compute_function(&self, function: &MetalFunction) -> Result<(), String> {
798 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
799 let ok = unsafe {
800 ffi::am_binary_archive_add_compute_function(self.as_ptr(), function.as_ptr(), &mut err)
801 };
802 if ok {
803 Ok(())
804 } else {
805 Err(unsafe {
806 take_optional_string(err).unwrap_or_else(|| {
807 "MTLBinaryArchive.addComputePipelineFunctions failed".to_string()
808 })
809 })
810 }
811 }
812
813 pub fn add_render_functions(
819 &self,
820 vertex: &MetalFunction,
821 fragment: &MetalFunction,
822 color_pixel_format: usize,
823 sample_count: usize,
824 ) -> Result<(), String> {
825 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
826 let ok = unsafe {
827 ffi::am_binary_archive_add_render_functions(
828 self.as_ptr(),
829 vertex.as_ptr(),
830 fragment.as_ptr(),
831 color_pixel_format,
832 sample_count,
833 &mut err,
834 )
835 };
836 if ok {
837 Ok(())
838 } else {
839 Err(unsafe {
840 take_optional_string(err).unwrap_or_else(|| {
841 "MTLBinaryArchive.addRenderPipelineFunctions failed".to_string()
842 })
843 })
844 }
845 }
846
847 pub fn serialize_to_file(&self, path: &Path) -> Result<(), String> {
853 let path = c_string(path.to_string_lossy().as_ref())?;
854 let mut err: *mut core::ffi::c_char = core::ptr::null_mut();
855 let ok = unsafe {
856 ffi::am_binary_archive_serialize_to_url(self.as_ptr(), path.as_ptr(), &mut err)
857 };
858 if ok {
859 Ok(())
860 } else {
861 Err(unsafe {
862 take_optional_string(err)
863 .unwrap_or_else(|| "MTLBinaryArchive.serialize(to:) failed".to_string())
864 })
865 }
866 }
867}
868
869impl ArgumentEncoder {
870 #[must_use]
872 pub fn encoded_length(&self) -> usize {
873 unsafe { ffi::am_argument_encoder_encoded_length(self.as_ptr()) }
874 }
875
876 #[must_use]
878 pub fn alignment(&self) -> usize {
879 unsafe { ffi::am_argument_encoder_alignment(self.as_ptr()) }
880 }
881
882 pub fn set_argument_buffer(&self, buffer: &MetalBuffer, offset: usize) {
884 unsafe {
885 ffi::am_argument_encoder_set_argument_buffer(self.as_ptr(), buffer.as_ptr(), offset);
886 };
887 }
888
889 pub fn set_buffer(&self, buffer: &MetalBuffer, offset: usize, index: usize) {
891 unsafe {
892 ffi::am_argument_encoder_set_buffer(self.as_ptr(), buffer.as_ptr(), offset, index);
893 };
894 }
895
896 pub fn set_texture(&self, texture: &MetalTexture, index: usize) {
898 unsafe { ffi::am_argument_encoder_set_texture(self.as_ptr(), texture.as_ptr(), index) };
899 }
900}
901
902impl IndirectCommandBuffer {
903 #[must_use]
905 pub fn size(&self) -> usize {
906 unsafe { ffi::am_indirect_command_buffer_size(self.as_ptr()) }
907 }
908
909 pub fn reset_range(&self, range: Range<usize>) {
911 unsafe {
912 ffi::am_indirect_command_buffer_reset_range(
913 self.as_ptr(),
914 range.start,
915 range.end.saturating_sub(range.start),
916 );
917 };
918 }
919}
920
921impl AccelerationStructure {
922 #[must_use]
924 pub fn size(&self) -> usize {
925 unsafe { ffi::am_acceleration_structure_size(self.as_ptr()) }
926 }
927}
928
929impl IntersectionFunctionTable {
930 pub fn set_opaque_triangle_intersection_function(&self, signature: usize, index: usize) {
932 unsafe {
933 ffi::am_intersection_function_table_set_opaque_triangle(
934 self.as_ptr(),
935 signature,
936 index,
937 );
938 };
939 }
940}
941
942impl CounterSampleBuffer {
943 #[must_use]
945 pub fn sample_count(&self) -> usize {
946 unsafe { ffi::am_counter_sample_buffer_sample_count(self.as_ptr()) }
947 }
948
949 #[must_use]
951 pub fn resolve_range(&self, range: Range<usize>) -> Option<Vec<u8>> {
952 let mut out_len = 0usize;
953 let ptr = unsafe {
954 ffi::am_counter_sample_buffer_resolve_range(
955 self.as_ptr(),
956 range.start,
957 range.end.saturating_sub(range.start),
958 &mut out_len,
959 )
960 };
961 if ptr.is_null() {
962 None
963 } else {
964 let bytes = unsafe { core::slice::from_raw_parts(ptr.cast::<u8>(), out_len) }.to_vec();
965 unsafe { libc::free(ptr.cast()) };
966 Some(bytes)
967 }
968 }
969}
970
971impl ResidencySet {
972 pub fn add_buffer(&self, buffer: &MetalBuffer) {
974 unsafe { ffi::am_residency_set_add_buffer(self.as_ptr(), buffer.as_ptr()) };
975 }
976
977 pub fn add_texture(&self, texture: &MetalTexture) {
979 unsafe { ffi::am_residency_set_add_texture(self.as_ptr(), texture.as_ptr()) };
980 }
981
982 pub fn add_heap(&self, heap: &Heap) {
984 unsafe { ffi::am_residency_set_add_heap(self.as_ptr(), heap.as_ptr()) };
985 }
986
987 pub fn remove_buffer(&self, buffer: &MetalBuffer) {
989 unsafe { ffi::am_residency_set_remove_buffer(self.as_ptr(), buffer.as_ptr()) };
990 }
991
992 pub fn remove_texture(&self, texture: &MetalTexture) {
994 unsafe { ffi::am_residency_set_remove_texture(self.as_ptr(), texture.as_ptr()) };
995 }
996
997 pub fn remove_heap(&self, heap: &Heap) {
999 unsafe { ffi::am_residency_set_remove_heap(self.as_ptr(), heap.as_ptr()) };
1000 }
1001
1002 pub fn remove_all_allocations(&self) {
1004 unsafe { ffi::am_residency_set_remove_all_allocations(self.as_ptr()) };
1005 }
1006
1007 #[must_use]
1009 pub fn contains_buffer(&self, buffer: &MetalBuffer) -> bool {
1010 unsafe { ffi::am_residency_set_contains_buffer(self.as_ptr(), buffer.as_ptr()) }
1011 }
1012
1013 #[must_use]
1015 pub fn contains_texture(&self, texture: &MetalTexture) -> bool {
1016 unsafe { ffi::am_residency_set_contains_texture(self.as_ptr(), texture.as_ptr()) }
1017 }
1018
1019 #[must_use]
1021 pub fn allocation_count(&self) -> usize {
1022 unsafe { ffi::am_residency_set_allocation_count(self.as_ptr()) }
1023 }
1024
1025 pub fn commit(&self) {
1027 unsafe { ffi::am_residency_set_commit(self.as_ptr()) };
1028 }
1029
1030 pub fn request_residency(&self) {
1032 unsafe { ffi::am_residency_set_request_residency(self.as_ptr()) };
1033 }
1034
1035 pub fn end_residency(&self) {
1037 unsafe { ffi::am_residency_set_end_residency(self.as_ptr()) };
1038 }
1039}
1040
1041impl CaptureManager {
1042 #[must_use]
1044 pub fn shared() -> Option<Self> {
1045 Self::wrap(unsafe { ffi::am_capture_manager_shared() })
1046 }
1047
1048 #[must_use]
1050 pub fn supports_destination(&self, destination: usize) -> bool {
1051 unsafe { ffi::am_capture_manager_supports_destination(self.as_ptr(), destination) }
1052 }
1053
1054 #[must_use]
1056 pub fn is_capturing(&self) -> bool {
1057 unsafe { ffi::am_capture_manager_is_capturing(self.as_ptr()) }
1058 }
1059
1060 #[must_use]
1062 pub fn new_capture_scope_with_device(&self, device: &MetalDevice) -> Option<CaptureScope> {
1063 CaptureScope::wrap(unsafe {
1064 ffi::am_capture_manager_new_scope_with_device(self.as_ptr(), device.as_ptr())
1065 })
1066 }
1067
1068 #[must_use]
1070 pub fn new_capture_scope_with_command_queue(
1071 &self,
1072 command_queue: &CommandQueue,
1073 ) -> Option<CaptureScope> {
1074 CaptureScope::wrap(unsafe {
1075 ffi::am_capture_manager_new_scope_with_command_queue(
1076 self.as_ptr(),
1077 command_queue.as_ptr(),
1078 )
1079 })
1080 }
1081}
1082
1083impl CaptureScope {
1084 pub fn begin(&self) {
1086 unsafe { ffi::am_capture_scope_begin(self.as_ptr()) };
1087 }
1088
1089 pub fn end(&self) {
1091 unsafe { ffi::am_capture_scope_end(self.as_ptr()) };
1092 }
1093}
1094
1095impl ArgumentEncoder {
1096 pub(crate) const unsafe fn from_retained_ptr(ptr: *mut c_void) -> Self {
1097 Self { ptr }
1098 }
1099}