1use std::ffi::{c_void, CStr};
72use std::ptr::NonNull;
73
74use crate::cm::IOSurface;
75use crate::FourCharCode;
76
77pub mod pixel_format {
81 use crate::FourCharCode;
82
83 pub const BGRA: FourCharCode = FourCharCode::from_bytes(*b"BGRA");
85
86 pub const L10R: FourCharCode = FourCharCode::from_bytes(*b"l10r");
88
89 pub const YCBCR_420V: FourCharCode = FourCharCode::from_bytes(*b"420v");
91
92 pub const YCBCR_420F: FourCharCode = FourCharCode::from_bytes(*b"420f");
94
95 #[must_use]
99 pub fn is_ycbcr_biplanar(format: impl Into<FourCharCode>) -> bool {
100 let f = format.into();
101 f.equals(YCBCR_420V) || f.equals(YCBCR_420F)
102 }
103
104 #[must_use]
108 pub fn is_full_range(format: impl Into<FourCharCode>) -> bool {
109 format.into().equals(YCBCR_420F)
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
117#[repr(u64)]
118pub enum MetalPixelFormat {
119 BGRA8Unorm = 80,
121 BGR10A2Unorm = 94,
123 R8Unorm = 10,
125 RG8Unorm = 30,
127}
128
129impl MetalPixelFormat {
130 #[must_use]
132 pub const fn raw(self) -> u64 {
133 self as u64
134 }
135
136 #[must_use]
138 pub const fn from_raw(value: u64) -> Option<Self> {
139 match value {
140 80 => Some(Self::BGRA8Unorm),
141 94 => Some(Self::BGR10A2Unorm),
142 10 => Some(Self::R8Unorm),
143 30 => Some(Self::RG8Unorm),
144 _ => None,
145 }
146 }
147}
148
149#[derive(Debug, Clone)]
151pub struct IOSurfaceInfo {
152 pub width: usize,
154 pub height: usize,
156 pub bytes_per_row: usize,
158 pub pixel_format: FourCharCode,
160 pub plane_count: usize,
162 pub planes: Vec<PlaneInfo>,
164}
165
166#[derive(Debug, Clone)]
168pub struct PlaneInfo {
169 pub index: usize,
171 pub width: usize,
173 pub height: usize,
175 pub bytes_per_row: usize,
177}
178
179impl IOSurface {
180 #[must_use]
182 pub fn info(&self) -> IOSurfaceInfo {
183 let width = self.width();
184 let height = self.height();
185 let bytes_per_row = self.bytes_per_row();
186 let pix_format: FourCharCode = self.pixel_format().into();
187 let plane_count = self.plane_count();
188
189 let planes = if plane_count > 0 {
190 (0..plane_count)
191 .map(|i| PlaneInfo {
192 index: i,
193 width: self.width_of_plane(i),
194 height: self.height_of_plane(i),
195 bytes_per_row: self.bytes_per_row_of_plane(i),
196 })
197 .collect()
198 } else {
199 vec![]
200 };
201
202 IOSurfaceInfo {
203 width,
204 height,
205 bytes_per_row,
206 pixel_format: pix_format,
207 plane_count,
208 planes,
209 }
210 }
211
212 #[must_use]
214 pub fn is_ycbcr_biplanar(&self) -> bool {
215 pixel_format::is_ycbcr_biplanar(self.pixel_format())
216 }
217}
218
219#[derive(Debug, Clone, Copy)]
223pub struct TextureParams {
224 pub width: usize,
226 pub height: usize,
228 pub format: MetalPixelFormat,
230 pub plane: usize,
232}
233
234impl TextureParams {
235 #[must_use]
237 pub const fn metal_pixel_format(&self) -> u64 {
238 self.format.raw()
239 }
240}
241
242impl IOSurface {
243 #[must_use]
249 pub fn texture_params(&self) -> Vec<TextureParams> {
250 let pix_format: FourCharCode = self.pixel_format().into();
251 let plane_count = self.plane_count();
252
253 if pix_format == pixel_format::BGRA {
254 vec![TextureParams {
255 width: self.width(),
256 height: self.height(),
257 format: MetalPixelFormat::BGRA8Unorm,
258 plane: 0,
259 }]
260 } else if pix_format == pixel_format::L10R {
261 vec![TextureParams {
262 width: self.width(),
263 height: self.height(),
264 format: MetalPixelFormat::BGR10A2Unorm,
265 plane: 0,
266 }]
267 } else if pixel_format::is_ycbcr_biplanar(pix_format) && plane_count >= 2 {
268 vec![
269 TextureParams {
271 width: self.width_of_plane(0),
272 height: self.height_of_plane(0),
273 format: MetalPixelFormat::R8Unorm,
274 plane: 0,
275 },
276 TextureParams {
278 width: self.width_of_plane(1),
279 height: self.height_of_plane(1),
280 format: MetalPixelFormat::RG8Unorm,
281 plane: 1,
282 },
283 ]
284 } else {
285 vec![TextureParams {
287 width: self.width(),
288 height: self.height(),
289 format: MetalPixelFormat::BGRA8Unorm,
290 plane: 0,
291 }]
292 }
293 }
294}
295
296#[derive(Debug)]
298pub struct CapturedTextures<T> {
299 pub plane0: T,
301 pub plane1: Option<T>,
303 pub pixel_format: FourCharCode,
305 pub width: usize,
307 pub height: usize,
309}
310
311impl<T> CapturedTextures<T> {
312 #[must_use]
314 pub fn is_ycbcr(&self) -> bool {
315 pixel_format::is_ycbcr_biplanar(self.pixel_format)
316 }
317}
318
319impl IOSurface {
320 pub fn metal_textures<T, F>(&self, create_texture: F) -> Option<CapturedTextures<T>>
353 where
354 F: Fn(&TextureParams, *const c_void) -> Option<T>,
355 {
356 let width = self.width();
357 let height = self.height();
358 let pix_format: FourCharCode = self.pixel_format().into();
359
360 if width == 0 || height == 0 {
361 return None;
362 }
363
364 let iosurface_ptr = self.as_ptr();
365 let params = self.texture_params();
366
367 if params.len() == 1 {
368 let texture = create_texture(¶ms[0], iosurface_ptr)?;
370 Some(CapturedTextures {
371 plane0: texture,
372 plane1: None,
373 pixel_format: pix_format,
374 width,
375 height,
376 })
377 } else if params.len() >= 2 {
378 let y_texture = create_texture(¶ms[0], iosurface_ptr)?;
380 let uv_texture = create_texture(¶ms[1], iosurface_ptr)?;
381 Some(CapturedTextures {
382 plane0: y_texture,
383 plane1: Some(uv_texture),
384 pixel_format: pix_format,
385 width,
386 height,
387 })
388 } else {
389 None
390 }
391 }
392}
393
394pub const SHADER_SOURCE: &str = r"
416#include <metal_stdlib>
417using namespace metal;
418
419struct Uniforms {
420 float2 viewport_size;
421 float2 texture_size;
422 float time;
423 uint pixel_format;
424 float padding[2];
425};
426
427struct TexturedVertexOut {
428 float4 position [[position]];
429 float2 texcoord;
430};
431
432// Fullscreen quad vertex shader with aspect ratio correction
433vertex TexturedVertexOut vertex_fullscreen(uint vid [[vertex_id]], constant Uniforms& uniforms [[buffer(0)]]) {
434 TexturedVertexOut out;
435 float va = uniforms.viewport_size.x / uniforms.viewport_size.y;
436 float ta = uniforms.texture_size.x / uniforms.texture_size.y;
437 float sx = ta > va ? 1.0 : ta / va;
438 float sy = ta > va ? va / ta : 1.0;
439 float2 positions[4] = { float2(-sx, -sy), float2(sx, -sy), float2(-sx, sy), float2(sx, sy) };
440 float2 texcoords[4] = { float2(0.0, 1.0), float2(1.0, 1.0), float2(0.0, 0.0), float2(1.0, 0.0) };
441 out.position = float4(positions[vid], 0.0, 1.0);
442 out.texcoord = texcoords[vid];
443 return out;
444}
445
446// BGRA/RGB texture fragment shader
447fragment float4 fragment_textured(TexturedVertexOut in [[stage_in]], texture2d<float> tex [[texture(0)]]) {
448 constexpr sampler s(mag_filter::linear, min_filter::linear);
449 return tex.sample(s, in.texcoord);
450}
451
452// YCbCr to RGB conversion (BT.709 matrix for HD video)
453float4 ycbcr_to_rgb(float y, float2 cbcr, bool full_range) {
454 float y_adj = full_range ? y : (y - 16.0/255.0) * (255.0/219.0);
455 float cb = cbcr.x - 0.5;
456 float cr = cbcr.y - 0.5;
457 // BT.709 conversion matrix
458 float r = y_adj + 1.5748 * cr;
459 float g = y_adj - 0.1873 * cb - 0.4681 * cr;
460 float b = y_adj + 1.8556 * cb;
461 return float4(saturate(float3(r, g, b)), 1.0);
462}
463
464// YCbCr biplanar (420v/420f) fragment shader
465fragment float4 fragment_ycbcr(TexturedVertexOut in [[stage_in]],
466 texture2d<float> y_tex [[texture(0)]],
467 texture2d<float> cbcr_tex [[texture(1)]],
468 constant Uniforms& uniforms [[buffer(0)]]) {
469 constexpr sampler s(mag_filter::linear, min_filter::linear);
470 float y = y_tex.sample(s, in.texcoord).r;
471 float2 cbcr = cbcr_tex.sample(s, in.texcoord).rg;
472 bool full_range = (uniforms.pixel_format == 0x34323066); // '420f'
473 return ycbcr_to_rgb(y, cbcr, full_range);
474}
475
476// Colored vertex input/output for UI overlays
477struct ColoredVertex {
478 float2 position [[attribute(0)]];
479 float4 color [[attribute(1)]];
480};
481
482struct ColoredVertexOut {
483 float4 position [[position]];
484 float4 color;
485};
486
487// Colored vertex shader for UI elements (position in pixels, converted to NDC)
488vertex ColoredVertexOut vertex_colored(ColoredVertex in [[stage_in]], constant Uniforms& uniforms [[buffer(1)]]) {
489 ColoredVertexOut out;
490 float2 ndc = (in.position / uniforms.viewport_size) * 2.0 - 1.0;
491 ndc.y = -ndc.y;
492 out.position = float4(ndc, 0.0, 1.0);
493 out.color = in.color;
494 return out;
495}
496
497// Colored fragment shader for UI elements
498fragment float4 fragment_colored(ColoredVertexOut in [[stage_in]]) {
499 return in.color;
500}
501";
502
503#[repr(C)]
507#[derive(Debug, Clone, Copy, Default)]
508pub struct Uniforms {
509 pub viewport_size: [f32; 2],
511 pub texture_size: [f32; 2],
513 pub time: f32,
515 pub pixel_format: u32,
517 #[doc(hidden)]
519 pub _padding: [f32; 2],
520}
521
522impl Uniforms {
523 #[must_use]
525 pub fn new(
526 viewport_width: f32,
527 viewport_height: f32,
528 texture_width: f32,
529 texture_height: f32,
530 ) -> Self {
531 Self {
532 viewport_size: [viewport_width, viewport_height],
533 texture_size: [texture_width, texture_height],
534 time: 0.0,
535 pixel_format: 0,
536 _padding: [0.0; 2],
537 }
538 }
539
540 #[must_use]
557 #[allow(clippy::cast_precision_loss)] pub fn from_captured_textures<T>(
559 viewport_width: f32,
560 viewport_height: f32,
561 textures: &CapturedTextures<T>,
562 ) -> Self {
563 Self {
564 viewport_size: [viewport_width, viewport_height],
565 texture_size: [textures.width as f32, textures.height as f32],
566 time: 0.0,
567 pixel_format: textures.pixel_format.as_u32(),
568 _padding: [0.0; 2],
569 }
570 }
571
572 #[must_use]
582 pub fn with_pixel_format(mut self, format: impl Into<FourCharCode>) -> Self {
583 self.pixel_format = format.into().as_u32();
584 self
585 }
586
587 #[must_use]
589 pub fn with_time(mut self, time: f32) -> Self {
590 self.time = time;
591 self
592 }
593}
594
595#[link(name = "Metal", kind = "framework")]
598extern "C" {}
599
600#[link(name = "QuartzCore", kind = "framework")]
601extern "C" {}
602
603extern "C" {
604 fn metal_create_system_default_device() -> *mut c_void;
606 fn metal_device_release(device: *mut c_void);
607 fn metal_device_get_name(device: *mut c_void) -> *const std::ffi::c_char;
608 fn metal_device_create_command_queue(device: *mut c_void) -> *mut c_void;
609 fn metal_device_create_render_pipeline_state(
610 device: *mut c_void,
611 desc: *mut c_void,
612 ) -> *mut c_void;
613
614 fn metal_create_texture_from_iosurface(
616 device: *mut c_void,
617 iosurface: *mut c_void,
618 plane: usize,
619 width: usize,
620 height: usize,
621 pixel_format: u64,
622 ) -> *mut c_void;
623 fn metal_texture_release(texture: *mut c_void);
624 fn metal_texture_retain(texture: *mut c_void) -> *mut c_void;
625 fn metal_texture_get_width(texture: *mut c_void) -> usize;
626 fn metal_texture_get_height(texture: *mut c_void) -> usize;
627 fn metal_texture_get_pixel_format(texture: *mut c_void) -> u64;
628
629 fn metal_command_queue_release(queue: *mut c_void);
631 fn metal_command_queue_command_buffer(queue: *mut c_void) -> *mut c_void;
632
633 fn metal_device_create_library_with_source(
635 device: *mut c_void,
636 source: *const std::ffi::c_char,
637 error_out: *mut *const std::ffi::c_char,
638 ) -> *mut c_void;
639 fn metal_library_release(library: *mut c_void);
640 fn metal_library_get_function(
641 library: *mut c_void,
642 name: *const std::ffi::c_char,
643 ) -> *mut c_void;
644 fn metal_function_release(function: *mut c_void);
645
646 fn metal_device_create_buffer(device: *mut c_void, length: usize, options: u64) -> *mut c_void;
648 fn metal_buffer_contents(buffer: *mut c_void) -> *mut c_void;
649 fn metal_buffer_length(buffer: *mut c_void) -> usize;
650 fn metal_buffer_did_modify_range(buffer: *mut c_void, location: usize, length: usize);
651 fn metal_buffer_release(buffer: *mut c_void);
652
653 fn metal_layer_create() -> *mut c_void;
655 fn metal_layer_set_device(layer: *mut c_void, device: *mut c_void);
656 fn metal_layer_set_pixel_format(layer: *mut c_void, format: u64);
657 fn metal_layer_set_drawable_size(layer: *mut c_void, width: f64, height: f64);
658 fn metal_layer_set_presents_with_transaction(layer: *mut c_void, value: bool);
659 fn metal_layer_next_drawable(layer: *mut c_void) -> *mut c_void;
660 fn metal_layer_release(layer: *mut c_void);
661
662 fn metal_drawable_texture(drawable: *mut c_void) -> *mut c_void;
664 fn metal_drawable_release(drawable: *mut c_void);
665
666 fn metal_command_buffer_present_drawable(cmd_buffer: *mut c_void, drawable: *mut c_void);
668 fn metal_command_buffer_commit(cmd_buffer: *mut c_void);
669 fn metal_command_buffer_release(cmd_buffer: *mut c_void);
670
671 fn metal_render_pass_descriptor_create() -> *mut c_void;
673 fn metal_render_pass_set_color_attachment_texture(
674 desc: *mut c_void,
675 index: usize,
676 texture: *mut c_void,
677 );
678 fn metal_render_pass_set_color_attachment_load_action(
679 desc: *mut c_void,
680 index: usize,
681 action: u64,
682 );
683 fn metal_render_pass_set_color_attachment_store_action(
684 desc: *mut c_void,
685 index: usize,
686 action: u64,
687 );
688 fn metal_render_pass_set_color_attachment_clear_color(
689 desc: *mut c_void,
690 index: usize,
691 r: f64,
692 g: f64,
693 b: f64,
694 a: f64,
695 );
696 fn metal_render_pass_descriptor_release(desc: *mut c_void);
697
698 fn metal_vertex_descriptor_create() -> *mut c_void;
700 fn metal_vertex_descriptor_set_attribute(
701 desc: *mut c_void,
702 index: usize,
703 format: u64,
704 offset: usize,
705 buffer_index: usize,
706 );
707 fn metal_vertex_descriptor_set_layout(
708 desc: *mut c_void,
709 buffer_index: usize,
710 stride: usize,
711 step_function: u64,
712 );
713 fn metal_vertex_descriptor_release(desc: *mut c_void);
714
715 fn metal_render_pipeline_descriptor_create() -> *mut c_void;
717 fn metal_render_pipeline_descriptor_set_vertex_function(
718 desc: *mut c_void,
719 function: *mut c_void,
720 );
721 fn metal_render_pipeline_descriptor_set_fragment_function(
722 desc: *mut c_void,
723 function: *mut c_void,
724 );
725 fn metal_render_pipeline_descriptor_set_vertex_descriptor(
726 desc: *mut c_void,
727 vertex_descriptor: *mut c_void,
728 );
729 fn metal_render_pipeline_descriptor_set_color_attachment_pixel_format(
730 desc: *mut c_void,
731 index: usize,
732 format: u64,
733 );
734 fn metal_render_pipeline_descriptor_set_blending_enabled(
735 desc: *mut c_void,
736 index: usize,
737 enabled: bool,
738 );
739 fn metal_render_pipeline_descriptor_set_blend_operations(
740 desc: *mut c_void,
741 index: usize,
742 rgb_op: u64,
743 alpha_op: u64,
744 );
745 fn metal_render_pipeline_descriptor_set_blend_factors(
746 desc: *mut c_void,
747 index: usize,
748 src_rgb: u64,
749 dst_rgb: u64,
750 src_alpha: u64,
751 dst_alpha: u64,
752 );
753 fn metal_render_pipeline_descriptor_release(desc: *mut c_void);
754 fn metal_render_pipeline_state_release(state: *mut c_void);
755
756 fn metal_command_buffer_render_command_encoder(
758 cmd_buffer: *mut c_void,
759 render_pass: *mut c_void,
760 ) -> *mut c_void;
761 fn metal_render_encoder_set_pipeline_state(encoder: *mut c_void, state: *mut c_void);
762 fn metal_render_encoder_set_vertex_buffer(
763 encoder: *mut c_void,
764 buffer: *mut c_void,
765 offset: usize,
766 index: usize,
767 );
768 fn metal_render_encoder_set_fragment_buffer(
769 encoder: *mut c_void,
770 buffer: *mut c_void,
771 offset: usize,
772 index: usize,
773 );
774 fn metal_render_encoder_set_fragment_texture(
775 encoder: *mut c_void,
776 texture: *mut c_void,
777 index: usize,
778 );
779 fn metal_render_encoder_draw_primitives(
780 encoder: *mut c_void,
781 primitive_type: u64,
782 vertex_start: usize,
783 vertex_count: usize,
784 );
785 fn metal_render_encoder_end_encoding(encoder: *mut c_void);
786 fn metal_render_encoder_release(encoder: *mut c_void);
787
788 fn nsview_set_wants_layer(view: *mut c_void);
790 fn nsview_set_layer(view: *mut c_void, layer: *mut c_void);
791}
792
793#[derive(Debug)]
799pub struct MetalDevice {
800 ptr: NonNull<c_void>,
801}
802
803impl MetalDevice {
804 #[must_use]
808 pub fn system_default() -> Option<Self> {
809 let ptr = unsafe { metal_create_system_default_device() };
810 NonNull::new(ptr).map(|ptr| Self { ptr })
811 }
812
813 #[must_use]
824 pub unsafe fn from_ptr(ptr: *mut c_void) -> Option<Self> {
825 NonNull::new(ptr).map(|ptr| Self { ptr })
826 }
827
828 #[must_use]
834 pub unsafe fn from_ptr_retained(ptr: *mut c_void) -> Option<Self> {
835 if ptr.is_null() {
836 return None;
837 }
838 NonNull::new(ptr).map(|ptr| Self { ptr })
841 }
842
843 #[must_use]
845 pub fn name(&self) -> String {
846 unsafe {
847 let name_ptr = metal_device_get_name(self.ptr.as_ptr());
848 if name_ptr.is_null() {
849 return String::new();
850 }
851 CStr::from_ptr(name_ptr).to_string_lossy().into_owned()
852 }
853 }
854
855 #[must_use]
857 pub fn create_command_queue(&self) -> Option<MetalCommandQueue> {
858 let ptr = unsafe { metal_device_create_command_queue(self.ptr.as_ptr()) };
859 NonNull::new(ptr).map(|ptr| MetalCommandQueue { ptr })
860 }
861
862 pub fn create_library_with_source(&self, source: &str) -> Result<MetalLibrary, String> {
867 use std::ffi::CString;
868 let source_c = CString::new(source).map_err(|e| e.to_string())?;
869 let mut error_ptr: *const std::ffi::c_char = std::ptr::null();
870
871 let ptr = unsafe {
872 metal_device_create_library_with_source(
873 self.ptr.as_ptr(),
874 source_c.as_ptr(),
875 &mut error_ptr,
876 )
877 };
878
879 NonNull::new(ptr).map_or_else(
880 || {
881 let error = if error_ptr.is_null() {
882 "Unknown shader compilation error".to_string()
883 } else {
884 unsafe { CStr::from_ptr(error_ptr).to_string_lossy().into_owned() }
885 };
886 Err(error)
887 },
888 |ptr| Ok(MetalLibrary { ptr }),
889 )
890 }
891
892 #[must_use]
894 pub fn create_buffer(&self, length: usize, options: ResourceOptions) -> Option<MetalBuffer> {
895 let ptr = unsafe { metal_device_create_buffer(self.ptr.as_ptr(), length, options.0) };
896 NonNull::new(ptr).map(|ptr| MetalBuffer { ptr })
897 }
898
899 #[must_use]
916 pub fn create_buffer_with_data<T>(&self, data: &T) -> Option<MetalBuffer> {
917 let size = std::mem::size_of::<T>();
918 let buffer = self.create_buffer(size, ResourceOptions::CPU_CACHE_MODE_DEFAULT_CACHE)?;
919 unsafe {
920 std::ptr::copy_nonoverlapping(
921 std::ptr::addr_of!(*data).cast::<u8>(),
922 buffer.contents().cast(),
923 size,
924 );
925 }
926 Some(buffer)
927 }
928
929 #[must_use]
931 pub fn create_render_pipeline_state(
932 &self,
933 descriptor: &MetalRenderPipelineDescriptor,
934 ) -> Option<MetalRenderPipelineState> {
935 let ptr = unsafe {
936 metal_device_create_render_pipeline_state(self.ptr.as_ptr(), descriptor.as_ptr())
937 };
938 NonNull::new(ptr).map(|ptr| MetalRenderPipelineState { ptr })
939 }
940
941 #[must_use]
943 pub fn as_ptr(&self) -> *mut c_void {
944 self.ptr.as_ptr()
945 }
946}
947
948impl Drop for MetalDevice {
949 fn drop(&mut self) {
950 unsafe { metal_device_release(self.ptr.as_ptr()) }
951 }
952}
953
954unsafe impl Send for MetalDevice {}
955unsafe impl Sync for MetalDevice {}
956
957#[derive(Debug)]
963pub struct MetalTexture {
964 ptr: NonNull<c_void>,
965}
966
967impl MetalTexture {
968 #[must_use]
970 pub fn width(&self) -> usize {
971 unsafe { metal_texture_get_width(self.ptr.as_ptr()) }
972 }
973
974 #[must_use]
976 pub fn height(&self) -> usize {
977 unsafe { metal_texture_get_height(self.ptr.as_ptr()) }
978 }
979
980 #[must_use]
982 pub fn pixel_format(&self) -> MetalPixelFormat {
983 let raw = unsafe { metal_texture_get_pixel_format(self.ptr.as_ptr()) };
984 MetalPixelFormat::from_raw(raw).unwrap_or(MetalPixelFormat::BGRA8Unorm)
985 }
986
987 #[must_use]
989 pub fn as_ptr(&self) -> *mut c_void {
990 self.ptr.as_ptr()
991 }
992}
993
994impl Clone for MetalTexture {
995 fn clone(&self) -> Self {
996 let ptr = unsafe { metal_texture_retain(self.ptr.as_ptr()) };
997 Self {
998 ptr: NonNull::new(ptr).expect("metal_texture_retain returned null"),
999 }
1000 }
1001}
1002
1003impl Drop for MetalTexture {
1004 fn drop(&mut self) {
1005 unsafe { metal_texture_release(self.ptr.as_ptr()) }
1006 }
1007}
1008
1009unsafe impl Send for MetalTexture {}
1010unsafe impl Sync for MetalTexture {}
1011
1012#[derive(Debug)]
1016pub struct MetalCommandQueue {
1017 ptr: NonNull<c_void>,
1018}
1019
1020impl MetalCommandQueue {
1021 #[must_use]
1023 pub fn command_buffer(&self) -> Option<MetalCommandBuffer> {
1024 let ptr = unsafe { metal_command_queue_command_buffer(self.ptr.as_ptr()) };
1025 NonNull::new(ptr).map(|ptr| MetalCommandBuffer { ptr })
1026 }
1027
1028 #[must_use]
1030 pub fn as_ptr(&self) -> *mut c_void {
1031 self.ptr.as_ptr()
1032 }
1033}
1034
1035impl Drop for MetalCommandQueue {
1036 fn drop(&mut self) {
1037 unsafe { metal_command_queue_release(self.ptr.as_ptr()) }
1038 }
1039}
1040
1041unsafe impl Send for MetalCommandQueue {}
1042unsafe impl Sync for MetalCommandQueue {}
1043
1044#[derive(Debug)]
1048pub struct MetalLibrary {
1049 ptr: NonNull<c_void>,
1050}
1051
1052impl MetalLibrary {
1053 #[must_use]
1055 pub fn get_function(&self, name: &str) -> Option<MetalFunction> {
1056 use std::ffi::CString;
1057 let name_c = CString::new(name).ok()?;
1058 let ptr = unsafe { metal_library_get_function(self.ptr.as_ptr(), name_c.as_ptr()) };
1059 NonNull::new(ptr).map(|ptr| MetalFunction { ptr })
1060 }
1061
1062 #[must_use]
1064 pub fn as_ptr(&self) -> *mut c_void {
1065 self.ptr.as_ptr()
1066 }
1067}
1068
1069impl Drop for MetalLibrary {
1070 fn drop(&mut self) {
1071 unsafe { metal_library_release(self.ptr.as_ptr()) }
1072 }
1073}
1074
1075unsafe impl Send for MetalLibrary {}
1076unsafe impl Sync for MetalLibrary {}
1077
1078#[derive(Debug)]
1082pub struct MetalFunction {
1083 ptr: NonNull<c_void>,
1084}
1085
1086impl MetalFunction {
1087 #[must_use]
1089 pub fn as_ptr(&self) -> *mut c_void {
1090 self.ptr.as_ptr()
1091 }
1092}
1093
1094impl Drop for MetalFunction {
1095 fn drop(&mut self) {
1096 unsafe { metal_function_release(self.ptr.as_ptr()) }
1097 }
1098}
1099
1100unsafe impl Send for MetalFunction {}
1101unsafe impl Sync for MetalFunction {}
1102
1103#[derive(Debug)]
1107pub struct MetalBuffer {
1108 ptr: NonNull<c_void>,
1109}
1110
1111#[derive(Debug, Clone, Copy, Default)]
1113pub struct ResourceOptions(u64);
1114
1115impl ResourceOptions {
1116 pub const CPU_CACHE_MODE_DEFAULT_CACHE: Self = Self(0);
1118 pub const STORAGE_MODE_SHARED: Self = Self(0);
1120 pub const STORAGE_MODE_MANAGED: Self = Self(1 << 4);
1122}
1123
1124impl MetalBuffer {
1125 #[must_use]
1127 pub fn contents(&self) -> *mut c_void {
1128 unsafe { metal_buffer_contents(self.ptr.as_ptr()) }
1129 }
1130
1131 #[must_use]
1133 pub fn length(&self) -> usize {
1134 unsafe { metal_buffer_length(self.ptr.as_ptr()) }
1135 }
1136
1137 pub fn did_modify_range(&self, range: std::ops::Range<usize>) {
1139 unsafe { metal_buffer_did_modify_range(self.ptr.as_ptr(), range.start, range.len()) }
1140 }
1141
1142 #[must_use]
1144 pub fn as_ptr(&self) -> *mut c_void {
1145 self.ptr.as_ptr()
1146 }
1147}
1148
1149impl Drop for MetalBuffer {
1150 fn drop(&mut self) {
1151 unsafe { metal_buffer_release(self.ptr.as_ptr()) }
1152 }
1153}
1154
1155unsafe impl Send for MetalBuffer {}
1156unsafe impl Sync for MetalBuffer {}
1157
1158#[derive(Debug)]
1162pub struct MetalLayer {
1163 ptr: NonNull<c_void>,
1164}
1165
1166impl MetalLayer {
1167 #[must_use]
1172 pub fn new() -> Self {
1173 let ptr = unsafe { metal_layer_create() };
1174 Self {
1175 ptr: NonNull::new(ptr).expect("metal_layer_create returned null"),
1176 }
1177 }
1178
1179 pub fn set_device(&self, device: &MetalDevice) {
1181 unsafe { metal_layer_set_device(self.ptr.as_ptr(), device.as_ptr()) }
1182 }
1183
1184 pub fn set_pixel_format(&self, format: MTLPixelFormat) {
1186 unsafe { metal_layer_set_pixel_format(self.ptr.as_ptr(), format.raw()) }
1187 }
1188
1189 pub fn set_drawable_size(&self, width: f64, height: f64) {
1191 unsafe { metal_layer_set_drawable_size(self.ptr.as_ptr(), width, height) }
1192 }
1193
1194 pub fn set_presents_with_transaction(&self, value: bool) {
1196 unsafe { metal_layer_set_presents_with_transaction(self.ptr.as_ptr(), value) }
1197 }
1198
1199 #[must_use]
1201 pub fn next_drawable(&self) -> Option<MetalDrawable> {
1202 let ptr = unsafe { metal_layer_next_drawable(self.ptr.as_ptr()) };
1203 NonNull::new(ptr).map(|ptr| MetalDrawable { ptr })
1204 }
1205
1206 #[must_use]
1208 pub fn as_ptr(&self) -> *mut c_void {
1209 self.ptr.as_ptr()
1210 }
1211}
1212
1213impl Default for MetalLayer {
1214 fn default() -> Self {
1215 Self::new()
1216 }
1217}
1218
1219impl Drop for MetalLayer {
1220 fn drop(&mut self) {
1221 unsafe { metal_layer_release(self.ptr.as_ptr()) }
1222 }
1223}
1224
1225#[derive(Debug)]
1229pub struct MetalDrawable {
1230 ptr: NonNull<c_void>,
1231}
1232
1233impl MetalDrawable {
1234 #[must_use]
1239 pub fn texture(&self) -> MetalTexture {
1240 let ptr = unsafe { metal_drawable_texture(self.ptr.as_ptr()) };
1241 let ptr = unsafe { metal_texture_retain(ptr) };
1243 MetalTexture {
1244 ptr: NonNull::new(ptr).expect("drawable texture is null"),
1245 }
1246 }
1247
1248 #[must_use]
1250 pub fn as_ptr(&self) -> *mut c_void {
1251 self.ptr.as_ptr()
1252 }
1253}
1254
1255impl Drop for MetalDrawable {
1256 fn drop(&mut self) {
1257 unsafe { metal_drawable_release(self.ptr.as_ptr()) }
1258 }
1259}
1260
1261#[derive(Debug)]
1265pub struct MetalCommandBuffer {
1266 ptr: NonNull<c_void>,
1267}
1268
1269impl MetalCommandBuffer {
1270 #[must_use]
1272 pub fn render_command_encoder(
1273 &self,
1274 render_pass: &MetalRenderPassDescriptor,
1275 ) -> Option<MetalRenderCommandEncoder> {
1276 let ptr = unsafe {
1277 metal_command_buffer_render_command_encoder(self.ptr.as_ptr(), render_pass.as_ptr())
1278 };
1279 NonNull::new(ptr).map(|ptr| MetalRenderCommandEncoder { ptr })
1280 }
1281
1282 pub fn present_drawable(&self, drawable: &MetalDrawable) {
1284 unsafe { metal_command_buffer_present_drawable(self.ptr.as_ptr(), drawable.as_ptr()) }
1285 }
1286
1287 pub fn commit(&self) {
1289 unsafe { metal_command_buffer_commit(self.ptr.as_ptr()) }
1290 }
1291
1292 #[must_use]
1294 pub fn as_ptr(&self) -> *mut c_void {
1295 self.ptr.as_ptr()
1296 }
1297}
1298
1299impl Drop for MetalCommandBuffer {
1300 fn drop(&mut self) {
1301 unsafe { metal_command_buffer_release(self.ptr.as_ptr()) }
1302 }
1303}
1304
1305#[derive(Debug)]
1309pub struct MetalRenderPassDescriptor {
1310 ptr: NonNull<c_void>,
1311}
1312
1313#[derive(Debug, Clone, Copy, Default)]
1315#[repr(u64)]
1316pub enum MTLLoadAction {
1317 DontCare = 0,
1319 Load = 1,
1321 #[default]
1323 Clear = 2,
1324}
1325
1326#[derive(Debug, Clone, Copy, Default)]
1328#[repr(u64)]
1329pub enum MTLStoreAction {
1330 DontCare = 0,
1332 #[default]
1334 Store = 1,
1335}
1336
1337#[derive(Debug, Clone, Copy, Default)]
1339#[repr(u64)]
1340pub enum MTLPixelFormat {
1341 Invalid = 0,
1343 #[default]
1345 BGRA8Unorm = 80,
1346 BGR10A2Unorm = 94,
1348 R8Unorm = 10,
1350 RG8Unorm = 30,
1352}
1353
1354impl MTLPixelFormat {
1355 #[must_use]
1357 pub const fn raw(self) -> u64 {
1358 self as u64
1359 }
1360}
1361
1362#[derive(Debug, Clone, Copy, Default)]
1364#[repr(u64)]
1365pub enum MTLVertexFormat {
1366 Invalid = 0,
1368 #[default]
1370 Float2 = 29,
1371 Float3 = 30,
1373 Float4 = 31,
1375}
1376
1377impl MTLVertexFormat {
1378 #[must_use]
1380 pub const fn raw(self) -> u64 {
1381 self as u64
1382 }
1383}
1384
1385#[derive(Debug, Clone, Copy, Default)]
1387#[repr(u64)]
1388pub enum MTLVertexStepFunction {
1389 Constant = 0,
1391 #[default]
1393 PerVertex = 1,
1394 PerInstance = 2,
1396}
1397
1398impl MTLVertexStepFunction {
1399 #[must_use]
1401 pub const fn raw(self) -> u64 {
1402 self as u64
1403 }
1404}
1405
1406#[derive(Debug, Clone, Copy, Default)]
1408#[repr(u64)]
1409pub enum MTLPrimitiveType {
1410 Point = 0,
1412 Line = 1,
1414 LineStrip = 2,
1416 #[default]
1418 Triangle = 3,
1419 TriangleStrip = 4,
1421}
1422
1423impl MTLPrimitiveType {
1424 #[must_use]
1426 pub const fn raw(self) -> u64 {
1427 self as u64
1428 }
1429}
1430
1431#[derive(Debug, Clone, Copy, Default)]
1433#[repr(u64)]
1434pub enum MTLBlendOperation {
1435 #[default]
1437 Add = 0,
1438 Subtract = 1,
1440 ReverseSubtract = 2,
1442 Min = 3,
1444 Max = 4,
1446}
1447
1448#[derive(Debug, Clone, Copy, Default)]
1450#[repr(u64)]
1451pub enum MTLBlendFactor {
1452 Zero = 0,
1454 #[default]
1456 One = 1,
1457 SourceColor = 2,
1459 OneMinusSourceColor = 3,
1461 SourceAlpha = 4,
1463 OneMinusSourceAlpha = 5,
1465 DestinationColor = 6,
1467 OneMinusDestinationColor = 7,
1469 DestinationAlpha = 8,
1471 OneMinusDestinationAlpha = 9,
1473}
1474
1475impl MetalRenderPassDescriptor {
1476 #[must_use]
1481 pub fn new() -> Self {
1482 let ptr = unsafe { metal_render_pass_descriptor_create() };
1483 Self {
1484 ptr: NonNull::new(ptr).expect("render pass descriptor create failed"),
1485 }
1486 }
1487
1488 pub fn set_color_attachment_texture(&self, index: usize, texture: &MetalTexture) {
1490 unsafe {
1491 metal_render_pass_set_color_attachment_texture(
1492 self.ptr.as_ptr(),
1493 index,
1494 texture.as_ptr(),
1495 );
1496 }
1497 }
1498
1499 pub fn set_color_attachment_load_action(&self, index: usize, action: MTLLoadAction) {
1501 unsafe {
1502 metal_render_pass_set_color_attachment_load_action(
1503 self.ptr.as_ptr(),
1504 index,
1505 action as u64,
1506 );
1507 }
1508 }
1509
1510 pub fn set_color_attachment_store_action(&self, index: usize, action: MTLStoreAction) {
1512 unsafe {
1513 metal_render_pass_set_color_attachment_store_action(
1514 self.ptr.as_ptr(),
1515 index,
1516 action as u64,
1517 );
1518 }
1519 }
1520
1521 pub fn set_color_attachment_clear_color(&self, index: usize, r: f64, g: f64, b: f64, a: f64) {
1523 unsafe {
1524 metal_render_pass_set_color_attachment_clear_color(
1525 self.ptr.as_ptr(),
1526 index,
1527 r,
1528 g,
1529 b,
1530 a,
1531 );
1532 }
1533 }
1534
1535 #[must_use]
1537 pub fn as_ptr(&self) -> *mut c_void {
1538 self.ptr.as_ptr()
1539 }
1540}
1541
1542impl Default for MetalRenderPassDescriptor {
1543 fn default() -> Self {
1544 Self::new()
1545 }
1546}
1547
1548impl Drop for MetalRenderPassDescriptor {
1549 fn drop(&mut self) {
1550 unsafe { metal_render_pass_descriptor_release(self.ptr.as_ptr()) }
1551 }
1552}
1553
1554#[derive(Debug)]
1558pub struct MetalVertexDescriptor {
1559 ptr: NonNull<c_void>,
1560}
1561
1562impl MetalVertexDescriptor {
1563 #[must_use]
1568 pub fn new() -> Self {
1569 let ptr = unsafe { metal_vertex_descriptor_create() };
1570 Self {
1571 ptr: NonNull::new(ptr).expect("vertex descriptor create failed"),
1572 }
1573 }
1574
1575 pub fn set_attribute(
1577 &self,
1578 index: usize,
1579 format: MTLVertexFormat,
1580 offset: usize,
1581 buffer_index: usize,
1582 ) {
1583 unsafe {
1584 metal_vertex_descriptor_set_attribute(
1585 self.ptr.as_ptr(),
1586 index,
1587 format.raw(),
1588 offset,
1589 buffer_index,
1590 );
1591 }
1592 }
1593
1594 pub fn set_layout(
1596 &self,
1597 buffer_index: usize,
1598 stride: usize,
1599 step_function: MTLVertexStepFunction,
1600 ) {
1601 unsafe {
1602 metal_vertex_descriptor_set_layout(
1603 self.ptr.as_ptr(),
1604 buffer_index,
1605 stride,
1606 step_function.raw(),
1607 );
1608 }
1609 }
1610
1611 #[must_use]
1613 pub fn as_ptr(&self) -> *mut c_void {
1614 self.ptr.as_ptr()
1615 }
1616}
1617
1618impl Default for MetalVertexDescriptor {
1619 fn default() -> Self {
1620 Self::new()
1621 }
1622}
1623
1624impl Drop for MetalVertexDescriptor {
1625 fn drop(&mut self) {
1626 unsafe { metal_vertex_descriptor_release(self.ptr.as_ptr()) }
1627 }
1628}
1629
1630#[derive(Debug)]
1634pub struct MetalRenderPipelineDescriptor {
1635 ptr: NonNull<c_void>,
1636}
1637
1638impl MetalRenderPipelineDescriptor {
1639 #[must_use]
1644 pub fn new() -> Self {
1645 let ptr = unsafe { metal_render_pipeline_descriptor_create() };
1646 Self {
1647 ptr: NonNull::new(ptr).expect("render pipeline descriptor create failed"),
1648 }
1649 }
1650
1651 pub fn set_vertex_function(&self, function: &MetalFunction) {
1653 unsafe {
1654 metal_render_pipeline_descriptor_set_vertex_function(
1655 self.ptr.as_ptr(),
1656 function.as_ptr(),
1657 );
1658 }
1659 }
1660
1661 pub fn set_fragment_function(&self, function: &MetalFunction) {
1663 unsafe {
1664 metal_render_pipeline_descriptor_set_fragment_function(
1665 self.ptr.as_ptr(),
1666 function.as_ptr(),
1667 );
1668 }
1669 }
1670
1671 pub fn set_vertex_descriptor(&self, descriptor: &MetalVertexDescriptor) {
1673 unsafe {
1674 metal_render_pipeline_descriptor_set_vertex_descriptor(
1675 self.ptr.as_ptr(),
1676 descriptor.as_ptr(),
1677 );
1678 }
1679 }
1680
1681 pub fn set_color_attachment_pixel_format(&self, index: usize, format: MTLPixelFormat) {
1683 unsafe {
1684 metal_render_pipeline_descriptor_set_color_attachment_pixel_format(
1685 self.ptr.as_ptr(),
1686 index,
1687 format.raw(),
1688 );
1689 }
1690 }
1691
1692 pub fn set_blending_enabled(&self, index: usize, enabled: bool) {
1694 unsafe {
1695 metal_render_pipeline_descriptor_set_blending_enabled(
1696 self.ptr.as_ptr(),
1697 index,
1698 enabled,
1699 );
1700 }
1701 }
1702
1703 pub fn set_blend_operations(
1705 &self,
1706 index: usize,
1707 rgb_op: MTLBlendOperation,
1708 alpha_op: MTLBlendOperation,
1709 ) {
1710 unsafe {
1711 metal_render_pipeline_descriptor_set_blend_operations(
1712 self.ptr.as_ptr(),
1713 index,
1714 rgb_op as u64,
1715 alpha_op as u64,
1716 );
1717 }
1718 }
1719
1720 pub fn set_blend_factors(
1722 &self,
1723 index: usize,
1724 src_rgb: MTLBlendFactor,
1725 dst_rgb: MTLBlendFactor,
1726 src_alpha: MTLBlendFactor,
1727 dst_alpha: MTLBlendFactor,
1728 ) {
1729 unsafe {
1730 metal_render_pipeline_descriptor_set_blend_factors(
1731 self.ptr.as_ptr(),
1732 index,
1733 src_rgb as u64,
1734 dst_rgb as u64,
1735 src_alpha as u64,
1736 dst_alpha as u64,
1737 );
1738 }
1739 }
1740
1741 #[must_use]
1743 pub fn as_ptr(&self) -> *mut c_void {
1744 self.ptr.as_ptr()
1745 }
1746}
1747
1748impl Default for MetalRenderPipelineDescriptor {
1749 fn default() -> Self {
1750 Self::new()
1751 }
1752}
1753
1754impl Drop for MetalRenderPipelineDescriptor {
1755 fn drop(&mut self) {
1756 unsafe { metal_render_pipeline_descriptor_release(self.ptr.as_ptr()) }
1757 }
1758}
1759
1760#[derive(Debug)]
1764pub struct MetalRenderPipelineState {
1765 ptr: NonNull<c_void>,
1766}
1767
1768impl MetalRenderPipelineState {
1769 #[must_use]
1771 pub fn as_ptr(&self) -> *mut c_void {
1772 self.ptr.as_ptr()
1773 }
1774}
1775
1776impl Drop for MetalRenderPipelineState {
1777 fn drop(&mut self) {
1778 unsafe { metal_render_pipeline_state_release(self.ptr.as_ptr()) }
1779 }
1780}
1781
1782unsafe impl Send for MetalRenderPipelineState {}
1783unsafe impl Sync for MetalRenderPipelineState {}
1784
1785#[derive(Debug)]
1789pub struct MetalRenderCommandEncoder {
1790 ptr: NonNull<c_void>,
1791}
1792
1793impl MetalRenderCommandEncoder {
1794 pub fn set_render_pipeline_state(&self, state: &MetalRenderPipelineState) {
1796 unsafe { metal_render_encoder_set_pipeline_state(self.ptr.as_ptr(), state.as_ptr()) }
1797 }
1798
1799 pub fn set_vertex_buffer(&self, buffer: &MetalBuffer, offset: usize, index: usize) {
1801 unsafe {
1802 metal_render_encoder_set_vertex_buffer(
1803 self.ptr.as_ptr(),
1804 buffer.as_ptr(),
1805 offset,
1806 index,
1807 );
1808 }
1809 }
1810
1811 pub fn set_fragment_buffer(&self, buffer: &MetalBuffer, offset: usize, index: usize) {
1813 unsafe {
1814 metal_render_encoder_set_fragment_buffer(
1815 self.ptr.as_ptr(),
1816 buffer.as_ptr(),
1817 offset,
1818 index,
1819 );
1820 }
1821 }
1822
1823 pub fn set_fragment_texture(&self, texture: &MetalTexture, index: usize) {
1825 unsafe {
1826 metal_render_encoder_set_fragment_texture(self.ptr.as_ptr(), texture.as_ptr(), index);
1827 }
1828 }
1829
1830 pub fn draw_primitives(
1832 &self,
1833 primitive_type: MTLPrimitiveType,
1834 vertex_start: usize,
1835 vertex_count: usize,
1836 ) {
1837 unsafe {
1838 metal_render_encoder_draw_primitives(
1839 self.ptr.as_ptr(),
1840 primitive_type.raw(),
1841 vertex_start,
1842 vertex_count,
1843 );
1844 }
1845 }
1846
1847 pub fn end_encoding(&self) {
1849 unsafe { metal_render_encoder_end_encoding(self.ptr.as_ptr()) }
1850 }
1851
1852 #[must_use]
1854 pub fn as_ptr(&self) -> *mut c_void {
1855 self.ptr.as_ptr()
1856 }
1857}
1858
1859impl Drop for MetalRenderCommandEncoder {
1860 fn drop(&mut self) {
1861 unsafe { metal_render_encoder_release(self.ptr.as_ptr()) }
1862 }
1863}
1864
1865pub type MetalCapturedTextures = CapturedTextures<MetalTexture>;
1869
1870impl IOSurface {
1871 #[must_use]
1891 pub fn create_metal_textures(&self, device: &MetalDevice) -> Option<MetalCapturedTextures> {
1892 let width = self.width();
1893 let height = self.height();
1894 let pix_format: FourCharCode = self.pixel_format().into();
1895
1896 if width == 0 || height == 0 {
1897 return None;
1898 }
1899
1900 let params = self.texture_params();
1901
1902 if params.len() == 1 {
1903 let texture = self.create_texture_for_plane(device, ¶ms[0])?;
1905 Some(CapturedTextures {
1906 plane0: texture,
1907 plane1: None,
1908 pixel_format: pix_format,
1909 width,
1910 height,
1911 })
1912 } else if params.len() >= 2 {
1913 let y_texture = self.create_texture_for_plane(device, ¶ms[0])?;
1915 let uv_texture = self.create_texture_for_plane(device, ¶ms[1])?;
1916 Some(CapturedTextures {
1917 plane0: y_texture,
1918 plane1: Some(uv_texture),
1919 pixel_format: pix_format,
1920 width,
1921 height,
1922 })
1923 } else {
1924 None
1925 }
1926 }
1927
1928 fn create_texture_for_plane(
1929 &self,
1930 device: &MetalDevice,
1931 params: &TextureParams,
1932 ) -> Option<MetalTexture> {
1933 let ptr = unsafe {
1934 metal_create_texture_from_iosurface(
1935 device.as_ptr(),
1936 self.as_ptr(),
1937 params.plane,
1938 params.width,
1939 params.height,
1940 params.format.raw(),
1941 )
1942 };
1943 NonNull::new(ptr).map(|ptr| MetalTexture { ptr })
1944 }
1945}
1946
1947#[link(name = "Foundation", kind = "framework")]
1950extern "C" {
1951 fn objc_autoreleasePoolPush() -> *mut c_void;
1952 fn objc_autoreleasePoolPop(pool: *mut c_void);
1953}
1954
1955pub fn autoreleasepool<F, R>(f: F) -> R
1972where
1973 F: FnOnce() -> R,
1974{
1975 unsafe {
1976 let pool = objc_autoreleasePoolPush();
1977 let result = f();
1978 objc_autoreleasePoolPop(pool);
1979 result
1980 }
1981}
1982
1983pub unsafe fn setup_metal_view(view: *mut c_void, layer: &MetalLayer) {
2005 nsview_set_wants_layer(view);
2006 nsview_set_layer(view, layer.as_ptr());
2007}