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
179#[derive(Debug, Clone, Copy)]
183pub struct TextureParams {
184 pub width: usize,
186 pub height: usize,
188 pub format: MetalPixelFormat,
190 pub plane: usize,
192}
193
194impl TextureParams {
195 #[must_use]
197 pub const fn metal_pixel_format(&self) -> u64 {
198 self.format.raw()
199 }
200}
201
202#[derive(Debug)]
204pub struct CapturedTextures<T> {
205 pub plane0: T,
207 pub plane1: Option<T>,
209 pub pixel_format: FourCharCode,
211 pub width: usize,
213 pub height: usize,
215}
216
217impl<T> CapturedTextures<T> {
218 #[must_use]
220 pub fn is_ycbcr(&self) -> bool {
221 pixel_format::is_ycbcr_biplanar(self.pixel_format)
222 }
223}
224
225pub const SHADER_SOURCE: &str = r"
247#include <metal_stdlib>
248using namespace metal;
249
250struct Uniforms {
251 float2 viewport_size;
252 float2 texture_size;
253 float time;
254 uint pixel_format;
255 float padding[2];
256};
257
258struct TexturedVertexOut {
259 float4 position [[position]];
260 float2 texcoord;
261};
262
263// Fullscreen quad vertex shader with aspect ratio correction
264vertex TexturedVertexOut vertex_fullscreen(uint vid [[vertex_id]], constant Uniforms& uniforms [[buffer(0)]]) {
265 TexturedVertexOut out;
266 float va = uniforms.viewport_size.x / uniforms.viewport_size.y;
267 float ta = uniforms.texture_size.x / uniforms.texture_size.y;
268 float sx = ta > va ? 1.0 : ta / va;
269 float sy = ta > va ? va / ta : 1.0;
270 float2 positions[4] = { float2(-sx, -sy), float2(sx, -sy), float2(-sx, sy), float2(sx, sy) };
271 float2 texcoords[4] = { float2(0.0, 1.0), float2(1.0, 1.0), float2(0.0, 0.0), float2(1.0, 0.0) };
272 out.position = float4(positions[vid], 0.0, 1.0);
273 out.texcoord = texcoords[vid];
274 return out;
275}
276
277// BGRA/RGB texture fragment shader
278fragment float4 fragment_textured(TexturedVertexOut in [[stage_in]], texture2d<float> tex [[texture(0)]]) {
279 constexpr sampler s(mag_filter::linear, min_filter::linear);
280 return tex.sample(s, in.texcoord);
281}
282
283// YCbCr to RGB conversion (BT.709 matrix for HD video)
284float4 ycbcr_to_rgb(float y, float2 cbcr, bool full_range) {
285 float y_adj = full_range ? y : (y - 16.0/255.0) * (255.0/219.0);
286 float cb = cbcr.x - 0.5;
287 float cr = cbcr.y - 0.5;
288 // BT.709 conversion matrix
289 float r = y_adj + 1.5748 * cr;
290 float g = y_adj - 0.1873 * cb - 0.4681 * cr;
291 float b = y_adj + 1.8556 * cb;
292 return float4(saturate(float3(r, g, b)), 1.0);
293}
294
295// YCbCr biplanar (420v/420f) fragment shader
296fragment float4 fragment_ycbcr(TexturedVertexOut in [[stage_in]],
297 texture2d<float> y_tex [[texture(0)]],
298 texture2d<float> cbcr_tex [[texture(1)]],
299 constant Uniforms& uniforms [[buffer(0)]]) {
300 constexpr sampler s(mag_filter::linear, min_filter::linear);
301 float y = y_tex.sample(s, in.texcoord).r;
302 float2 cbcr = cbcr_tex.sample(s, in.texcoord).rg;
303 bool full_range = (uniforms.pixel_format == 0x34323066); // '420f'
304 return ycbcr_to_rgb(y, cbcr, full_range);
305}
306
307// Colored vertex input/output for UI overlays
308struct ColoredVertex {
309 float2 position [[attribute(0)]];
310 float4 color [[attribute(1)]];
311};
312
313struct ColoredVertexOut {
314 float4 position [[position]];
315 float4 color;
316};
317
318// Colored vertex shader for UI elements (position in pixels, converted to NDC)
319vertex ColoredVertexOut vertex_colored(ColoredVertex in [[stage_in]], constant Uniforms& uniforms [[buffer(1)]]) {
320 ColoredVertexOut out;
321 float2 ndc = (in.position / uniforms.viewport_size) * 2.0 - 1.0;
322 ndc.y = -ndc.y;
323 out.position = float4(ndc, 0.0, 1.0);
324 out.color = in.color;
325 return out;
326}
327
328// Colored fragment shader for UI elements
329fragment float4 fragment_colored(ColoredVertexOut in [[stage_in]]) {
330 return in.color;
331}
332";
333
334#[repr(C)]
338#[derive(Debug, Clone, Copy, Default)]
339pub struct Uniforms {
340 pub viewport_size: [f32; 2],
342 pub texture_size: [f32; 2],
344 pub time: f32,
346 pub pixel_format: u32,
348 #[doc(hidden)]
350 pub _padding: [f32; 2],
351}
352
353impl Uniforms {
354 #[must_use]
356 pub fn new(
357 viewport_width: f32,
358 viewport_height: f32,
359 texture_width: f32,
360 texture_height: f32,
361 ) -> Self {
362 Self {
363 viewport_size: [viewport_width, viewport_height],
364 texture_size: [texture_width, texture_height],
365 time: 0.0,
366 pixel_format: 0,
367 _padding: [0.0; 2],
368 }
369 }
370
371 #[must_use]
388 #[allow(clippy::cast_precision_loss)] pub fn from_captured_textures<T>(
390 viewport_width: f32,
391 viewport_height: f32,
392 textures: &CapturedTextures<T>,
393 ) -> Self {
394 Self {
395 viewport_size: [viewport_width, viewport_height],
396 texture_size: [textures.width as f32, textures.height as f32],
397 time: 0.0,
398 pixel_format: textures.pixel_format.as_u32(),
399 _padding: [0.0; 2],
400 }
401 }
402
403 #[must_use]
413 pub fn with_pixel_format(mut self, format: impl Into<FourCharCode>) -> Self {
414 self.pixel_format = format.into().as_u32();
415 self
416 }
417
418 #[must_use]
420 pub fn with_time(mut self, time: f32) -> Self {
421 self.time = time;
422 self
423 }
424}
425
426#[link(name = "Metal", kind = "framework")]
429extern "C" {}
430
431#[link(name = "QuartzCore", kind = "framework")]
432extern "C" {}
433
434extern "C" {
435 fn metal_create_system_default_device() -> *mut c_void;
437 fn metal_device_release(device: *mut c_void);
438 fn metal_device_get_name(device: *mut c_void) -> *const std::ffi::c_char;
439 fn metal_device_create_command_queue(device: *mut c_void) -> *mut c_void;
440 fn metal_device_create_render_pipeline_state(
441 device: *mut c_void,
442 desc: *mut c_void,
443 ) -> *mut c_void;
444
445 fn metal_create_texture_from_iosurface(
447 device: *mut c_void,
448 iosurface: *mut c_void,
449 plane: usize,
450 width: usize,
451 height: usize,
452 pixel_format: u64,
453 ) -> *mut c_void;
454 fn metal_texture_release(texture: *mut c_void);
455 fn metal_texture_retain(texture: *mut c_void) -> *mut c_void;
456 fn metal_texture_get_width(texture: *mut c_void) -> usize;
457 fn metal_texture_get_height(texture: *mut c_void) -> usize;
458 fn metal_texture_get_pixel_format(texture: *mut c_void) -> u64;
459
460 fn metal_command_queue_release(queue: *mut c_void);
462 fn metal_command_queue_command_buffer(queue: *mut c_void) -> *mut c_void;
463
464 fn metal_device_create_library_with_source(
466 device: *mut c_void,
467 source: *const std::ffi::c_char,
468 error_out: *mut *const std::ffi::c_char,
469 ) -> *mut c_void;
470 fn metal_library_release(library: *mut c_void);
471 fn metal_library_get_function(
472 library: *mut c_void,
473 name: *const std::ffi::c_char,
474 ) -> *mut c_void;
475 fn metal_function_release(function: *mut c_void);
476
477 fn metal_device_create_buffer(device: *mut c_void, length: usize, options: u64) -> *mut c_void;
479 fn metal_buffer_contents(buffer: *mut c_void) -> *mut c_void;
480 fn metal_buffer_length(buffer: *mut c_void) -> usize;
481 fn metal_buffer_did_modify_range(buffer: *mut c_void, location: usize, length: usize);
482 fn metal_buffer_release(buffer: *mut c_void);
483
484 fn metal_layer_create() -> *mut c_void;
486 fn metal_layer_set_device(layer: *mut c_void, device: *mut c_void);
487 fn metal_layer_set_pixel_format(layer: *mut c_void, format: u64);
488 fn metal_layer_set_drawable_size(layer: *mut c_void, width: f64, height: f64);
489 fn metal_layer_set_presents_with_transaction(layer: *mut c_void, value: bool);
490 fn metal_layer_next_drawable(layer: *mut c_void) -> *mut c_void;
491 fn metal_layer_release(layer: *mut c_void);
492
493 fn metal_drawable_texture(drawable: *mut c_void) -> *mut c_void;
495 fn metal_drawable_release(drawable: *mut c_void);
496
497 fn metal_command_buffer_present_drawable(cmd_buffer: *mut c_void, drawable: *mut c_void);
499 fn metal_command_buffer_commit(cmd_buffer: *mut c_void);
500 fn metal_command_buffer_release(cmd_buffer: *mut c_void);
501
502 fn metal_render_pass_descriptor_create() -> *mut c_void;
504 fn metal_render_pass_set_color_attachment_texture(
505 desc: *mut c_void,
506 index: usize,
507 texture: *mut c_void,
508 );
509 fn metal_render_pass_set_color_attachment_load_action(
510 desc: *mut c_void,
511 index: usize,
512 action: u64,
513 );
514 fn metal_render_pass_set_color_attachment_store_action(
515 desc: *mut c_void,
516 index: usize,
517 action: u64,
518 );
519 fn metal_render_pass_set_color_attachment_clear_color(
520 desc: *mut c_void,
521 index: usize,
522 r: f64,
523 g: f64,
524 b: f64,
525 a: f64,
526 );
527 fn metal_render_pass_descriptor_release(desc: *mut c_void);
528
529 fn metal_vertex_descriptor_create() -> *mut c_void;
531 fn metal_vertex_descriptor_set_attribute(
532 desc: *mut c_void,
533 index: usize,
534 format: u64,
535 offset: usize,
536 buffer_index: usize,
537 );
538 fn metal_vertex_descriptor_set_layout(
539 desc: *mut c_void,
540 buffer_index: usize,
541 stride: usize,
542 step_function: u64,
543 );
544 fn metal_vertex_descriptor_release(desc: *mut c_void);
545
546 fn metal_render_pipeline_descriptor_create() -> *mut c_void;
548 fn metal_render_pipeline_descriptor_set_vertex_function(
549 desc: *mut c_void,
550 function: *mut c_void,
551 );
552 fn metal_render_pipeline_descriptor_set_fragment_function(
553 desc: *mut c_void,
554 function: *mut c_void,
555 );
556 fn metal_render_pipeline_descriptor_set_vertex_descriptor(
557 desc: *mut c_void,
558 vertex_descriptor: *mut c_void,
559 );
560 fn metal_render_pipeline_descriptor_set_color_attachment_pixel_format(
561 desc: *mut c_void,
562 index: usize,
563 format: u64,
564 );
565 fn metal_render_pipeline_descriptor_set_blending_enabled(
566 desc: *mut c_void,
567 index: usize,
568 enabled: bool,
569 );
570 fn metal_render_pipeline_descriptor_set_blend_operations(
571 desc: *mut c_void,
572 index: usize,
573 rgb_op: u64,
574 alpha_op: u64,
575 );
576 fn metal_render_pipeline_descriptor_set_blend_factors(
577 desc: *mut c_void,
578 index: usize,
579 src_rgb: u64,
580 dst_rgb: u64,
581 src_alpha: u64,
582 dst_alpha: u64,
583 );
584 fn metal_render_pipeline_descriptor_release(desc: *mut c_void);
585 fn metal_render_pipeline_state_release(state: *mut c_void);
586
587 fn metal_command_buffer_render_command_encoder(
589 cmd_buffer: *mut c_void,
590 render_pass: *mut c_void,
591 ) -> *mut c_void;
592 fn metal_render_encoder_set_pipeline_state(encoder: *mut c_void, state: *mut c_void);
593 fn metal_render_encoder_set_vertex_buffer(
594 encoder: *mut c_void,
595 buffer: *mut c_void,
596 offset: usize,
597 index: usize,
598 );
599 fn metal_render_encoder_set_fragment_buffer(
600 encoder: *mut c_void,
601 buffer: *mut c_void,
602 offset: usize,
603 index: usize,
604 );
605 fn metal_render_encoder_set_fragment_texture(
606 encoder: *mut c_void,
607 texture: *mut c_void,
608 index: usize,
609 );
610 fn metal_render_encoder_draw_primitives(
611 encoder: *mut c_void,
612 primitive_type: u64,
613 vertex_start: usize,
614 vertex_count: usize,
615 );
616 fn metal_render_encoder_end_encoding(encoder: *mut c_void);
617 fn metal_render_encoder_release(encoder: *mut c_void);
618
619 fn nsview_set_wants_layer(view: *mut c_void);
621 fn nsview_set_layer(view: *mut c_void, layer: *mut c_void);
622}
623
624#[derive(Debug)]
630pub struct MetalDevice {
631 ptr: NonNull<c_void>,
632}
633
634impl MetalDevice {
635 #[must_use]
639 pub fn system_default() -> Option<Self> {
640 let ptr = unsafe { metal_create_system_default_device() };
641 NonNull::new(ptr).map(|ptr| Self { ptr })
642 }
643
644 #[must_use]
655 pub unsafe fn from_ptr(ptr: *mut c_void) -> Option<Self> {
656 NonNull::new(ptr).map(|ptr| Self { ptr })
657 }
658
659 #[must_use]
665 pub unsafe fn from_ptr_retained(ptr: *mut c_void) -> Option<Self> {
666 if ptr.is_null() {
667 return None;
668 }
669 NonNull::new(ptr).map(|ptr| Self { ptr })
672 }
673
674 #[must_use]
676 pub fn name(&self) -> String {
677 unsafe {
678 let name_ptr = metal_device_get_name(self.ptr.as_ptr());
679 if name_ptr.is_null() {
680 return String::new();
681 }
682 CStr::from_ptr(name_ptr).to_string_lossy().into_owned()
683 }
684 }
685
686 #[must_use]
688 pub fn create_command_queue(&self) -> Option<MetalCommandQueue> {
689 let ptr = unsafe { metal_device_create_command_queue(self.ptr.as_ptr()) };
690 NonNull::new(ptr).map(|ptr| MetalCommandQueue { ptr })
691 }
692
693 pub fn create_library_with_source(&self, source: &str) -> Result<MetalLibrary, String> {
698 use std::ffi::CString;
699 let source_c = CString::new(source).map_err(|e| e.to_string())?;
700 let mut error_ptr: *const std::ffi::c_char = std::ptr::null();
701
702 let ptr = unsafe {
703 metal_device_create_library_with_source(
704 self.ptr.as_ptr(),
705 source_c.as_ptr(),
706 &mut error_ptr,
707 )
708 };
709
710 NonNull::new(ptr).map_or_else(
711 || {
712 let error = if error_ptr.is_null() {
713 "Unknown shader compilation error".to_string()
714 } else {
715 unsafe { CStr::from_ptr(error_ptr).to_string_lossy().into_owned() }
716 };
717 Err(error)
718 },
719 |ptr| Ok(MetalLibrary { ptr }),
720 )
721 }
722
723 #[must_use]
725 pub fn create_buffer(&self, length: usize, options: ResourceOptions) -> Option<MetalBuffer> {
726 let ptr = unsafe { metal_device_create_buffer(self.ptr.as_ptr(), length, options.0) };
727 NonNull::new(ptr).map(|ptr| MetalBuffer { ptr })
728 }
729
730 #[must_use]
747 pub fn create_buffer_with_data<T>(&self, data: &T) -> Option<MetalBuffer> {
748 let size = std::mem::size_of::<T>();
749 let buffer = self.create_buffer(size, ResourceOptions::CPU_CACHE_MODE_DEFAULT_CACHE)?;
750 unsafe {
751 std::ptr::copy_nonoverlapping(
752 std::ptr::addr_of!(*data).cast::<u8>(),
753 buffer.contents().cast(),
754 size,
755 );
756 }
757 Some(buffer)
758 }
759
760 #[must_use]
762 pub fn create_render_pipeline_state(
763 &self,
764 descriptor: &MetalRenderPipelineDescriptor,
765 ) -> Option<MetalRenderPipelineState> {
766 let ptr = unsafe {
767 metal_device_create_render_pipeline_state(self.ptr.as_ptr(), descriptor.as_ptr())
768 };
769 NonNull::new(ptr).map(|ptr| MetalRenderPipelineState { ptr })
770 }
771
772 #[must_use]
774 pub fn as_ptr(&self) -> *mut c_void {
775 self.ptr.as_ptr()
776 }
777
778 #[must_use]
787 pub fn as_apple_metal(&self) -> apple_metal::ManuallyDropDevice {
788 unsafe { apple_metal::MetalDevice::from_raw_borrowed(self.ptr.as_ptr()) }
789 }
790}
791
792impl Drop for MetalDevice {
793 fn drop(&mut self) {
794 unsafe { metal_device_release(self.ptr.as_ptr()) }
795 }
796}
797
798unsafe impl Send for MetalDevice {}
799unsafe impl Sync for MetalDevice {}
800
801#[derive(Debug)]
807pub struct MetalTexture {
808 ptr: NonNull<c_void>,
809}
810
811impl MetalTexture {
812 #[must_use]
814 pub fn width(&self) -> usize {
815 unsafe { metal_texture_get_width(self.ptr.as_ptr()) }
816 }
817
818 #[must_use]
820 pub fn height(&self) -> usize {
821 unsafe { metal_texture_get_height(self.ptr.as_ptr()) }
822 }
823
824 #[must_use]
826 pub fn pixel_format(&self) -> MetalPixelFormat {
827 let raw = unsafe { metal_texture_get_pixel_format(self.ptr.as_ptr()) };
828 MetalPixelFormat::from_raw(raw).unwrap_or(MetalPixelFormat::BGRA8Unorm)
829 }
830
831 #[must_use]
833 pub fn as_ptr(&self) -> *mut c_void {
834 self.ptr.as_ptr()
835 }
836}
837
838impl Clone for MetalTexture {
839 fn clone(&self) -> Self {
840 let ptr = unsafe { metal_texture_retain(self.ptr.as_ptr()) };
841 Self {
842 ptr: NonNull::new(ptr).expect("metal_texture_retain returned null"),
843 }
844 }
845}
846
847impl Drop for MetalTexture {
848 fn drop(&mut self) {
849 unsafe { metal_texture_release(self.ptr.as_ptr()) }
850 }
851}
852
853unsafe impl Send for MetalTexture {}
854unsafe impl Sync for MetalTexture {}
855
856#[derive(Debug)]
860pub struct MetalCommandQueue {
861 ptr: NonNull<c_void>,
862}
863
864impl MetalCommandQueue {
865 #[must_use]
867 pub fn command_buffer(&self) -> Option<MetalCommandBuffer> {
868 let ptr = unsafe { metal_command_queue_command_buffer(self.ptr.as_ptr()) };
869 NonNull::new(ptr).map(|ptr| MetalCommandBuffer { ptr })
870 }
871
872 #[must_use]
874 pub fn as_ptr(&self) -> *mut c_void {
875 self.ptr.as_ptr()
876 }
877}
878
879impl Drop for MetalCommandQueue {
880 fn drop(&mut self) {
881 unsafe { metal_command_queue_release(self.ptr.as_ptr()) }
882 }
883}
884
885unsafe impl Send for MetalCommandQueue {}
886unsafe impl Sync for MetalCommandQueue {}
887
888#[derive(Debug)]
892pub struct MetalLibrary {
893 ptr: NonNull<c_void>,
894}
895
896impl MetalLibrary {
897 #[must_use]
899 pub fn get_function(&self, name: &str) -> Option<MetalFunction> {
900 use std::ffi::CString;
901 let name_c = CString::new(name).ok()?;
902 let ptr = unsafe { metal_library_get_function(self.ptr.as_ptr(), name_c.as_ptr()) };
903 NonNull::new(ptr).map(|ptr| MetalFunction { ptr })
904 }
905
906 #[must_use]
908 pub fn as_ptr(&self) -> *mut c_void {
909 self.ptr.as_ptr()
910 }
911}
912
913impl Drop for MetalLibrary {
914 fn drop(&mut self) {
915 unsafe { metal_library_release(self.ptr.as_ptr()) }
916 }
917}
918
919unsafe impl Send for MetalLibrary {}
920unsafe impl Sync for MetalLibrary {}
921
922#[derive(Debug)]
926pub struct MetalFunction {
927 ptr: NonNull<c_void>,
928}
929
930impl MetalFunction {
931 #[must_use]
933 pub fn as_ptr(&self) -> *mut c_void {
934 self.ptr.as_ptr()
935 }
936}
937
938impl Drop for MetalFunction {
939 fn drop(&mut self) {
940 unsafe { metal_function_release(self.ptr.as_ptr()) }
941 }
942}
943
944unsafe impl Send for MetalFunction {}
945unsafe impl Sync for MetalFunction {}
946
947#[derive(Debug)]
951pub struct MetalBuffer {
952 ptr: NonNull<c_void>,
953}
954
955#[derive(Debug, Clone, Copy, Default)]
957pub struct ResourceOptions(u64);
958
959impl ResourceOptions {
960 pub const CPU_CACHE_MODE_DEFAULT_CACHE: Self = Self(0);
962 pub const STORAGE_MODE_SHARED: Self = Self(0);
964 pub const STORAGE_MODE_MANAGED: Self = Self(1 << 4);
966}
967
968impl MetalBuffer {
969 #[must_use]
971 pub fn contents(&self) -> *mut c_void {
972 unsafe { metal_buffer_contents(self.ptr.as_ptr()) }
973 }
974
975 #[must_use]
977 pub fn length(&self) -> usize {
978 unsafe { metal_buffer_length(self.ptr.as_ptr()) }
979 }
980
981 pub fn did_modify_range(&self, range: std::ops::Range<usize>) {
983 unsafe { metal_buffer_did_modify_range(self.ptr.as_ptr(), range.start, range.len()) }
984 }
985
986 #[must_use]
988 pub fn as_ptr(&self) -> *mut c_void {
989 self.ptr.as_ptr()
990 }
991}
992
993impl Drop for MetalBuffer {
994 fn drop(&mut self) {
995 unsafe { metal_buffer_release(self.ptr.as_ptr()) }
996 }
997}
998
999unsafe impl Send for MetalBuffer {}
1000unsafe impl Sync for MetalBuffer {}
1001
1002#[derive(Debug)]
1006pub struct MetalLayer {
1007 ptr: NonNull<c_void>,
1008}
1009
1010impl MetalLayer {
1011 #[must_use]
1016 pub fn new() -> Self {
1017 let ptr = unsafe { metal_layer_create() };
1018 Self {
1019 ptr: NonNull::new(ptr).expect("metal_layer_create returned null"),
1020 }
1021 }
1022
1023 pub fn set_device(&self, device: &MetalDevice) {
1025 unsafe { metal_layer_set_device(self.ptr.as_ptr(), device.as_ptr()) }
1026 }
1027
1028 pub fn set_pixel_format(&self, format: MTLPixelFormat) {
1030 unsafe { metal_layer_set_pixel_format(self.ptr.as_ptr(), format.raw()) }
1031 }
1032
1033 pub fn set_drawable_size(&self, width: f64, height: f64) {
1035 unsafe { metal_layer_set_drawable_size(self.ptr.as_ptr(), width, height) }
1036 }
1037
1038 pub fn set_presents_with_transaction(&self, value: bool) {
1040 unsafe { metal_layer_set_presents_with_transaction(self.ptr.as_ptr(), value) }
1041 }
1042
1043 #[must_use]
1045 pub fn next_drawable(&self) -> Option<MetalDrawable> {
1046 let ptr = unsafe { metal_layer_next_drawable(self.ptr.as_ptr()) };
1047 NonNull::new(ptr).map(|ptr| MetalDrawable { ptr })
1048 }
1049
1050 #[must_use]
1052 pub fn as_ptr(&self) -> *mut c_void {
1053 self.ptr.as_ptr()
1054 }
1055}
1056
1057impl Default for MetalLayer {
1058 fn default() -> Self {
1059 Self::new()
1060 }
1061}
1062
1063impl Drop for MetalLayer {
1064 fn drop(&mut self) {
1065 unsafe { metal_layer_release(self.ptr.as_ptr()) }
1066 }
1067}
1068
1069#[derive(Debug)]
1073pub struct MetalDrawable {
1074 ptr: NonNull<c_void>,
1075}
1076
1077impl MetalDrawable {
1078 #[must_use]
1083 pub fn texture(&self) -> MetalTexture {
1084 let ptr = unsafe { metal_drawable_texture(self.ptr.as_ptr()) };
1085 let ptr = unsafe { metal_texture_retain(ptr) };
1087 MetalTexture {
1088 ptr: NonNull::new(ptr).expect("drawable texture is null"),
1089 }
1090 }
1091
1092 #[must_use]
1094 pub fn as_ptr(&self) -> *mut c_void {
1095 self.ptr.as_ptr()
1096 }
1097}
1098
1099impl Drop for MetalDrawable {
1100 fn drop(&mut self) {
1101 unsafe { metal_drawable_release(self.ptr.as_ptr()) }
1102 }
1103}
1104
1105#[derive(Debug)]
1109pub struct MetalCommandBuffer {
1110 ptr: NonNull<c_void>,
1111}
1112
1113impl MetalCommandBuffer {
1114 #[must_use]
1116 pub fn render_command_encoder(
1117 &self,
1118 render_pass: &MetalRenderPassDescriptor,
1119 ) -> Option<MetalRenderCommandEncoder> {
1120 let ptr = unsafe {
1121 metal_command_buffer_render_command_encoder(self.ptr.as_ptr(), render_pass.as_ptr())
1122 };
1123 NonNull::new(ptr).map(|ptr| MetalRenderCommandEncoder { ptr })
1124 }
1125
1126 pub fn present_drawable(&self, drawable: &MetalDrawable) {
1128 unsafe { metal_command_buffer_present_drawable(self.ptr.as_ptr(), drawable.as_ptr()) }
1129 }
1130
1131 pub fn commit(&self) {
1133 unsafe { metal_command_buffer_commit(self.ptr.as_ptr()) }
1134 }
1135
1136 #[must_use]
1138 pub fn as_ptr(&self) -> *mut c_void {
1139 self.ptr.as_ptr()
1140 }
1141}
1142
1143impl Drop for MetalCommandBuffer {
1144 fn drop(&mut self) {
1145 unsafe { metal_command_buffer_release(self.ptr.as_ptr()) }
1146 }
1147}
1148
1149#[derive(Debug)]
1153pub struct MetalRenderPassDescriptor {
1154 ptr: NonNull<c_void>,
1155}
1156
1157#[derive(Debug, Clone, Copy, Default)]
1159#[repr(u64)]
1160pub enum MTLLoadAction {
1161 DontCare = 0,
1163 Load = 1,
1165 #[default]
1167 Clear = 2,
1168}
1169
1170#[derive(Debug, Clone, Copy, Default)]
1172#[repr(u64)]
1173pub enum MTLStoreAction {
1174 DontCare = 0,
1176 #[default]
1178 Store = 1,
1179}
1180
1181#[derive(Debug, Clone, Copy, Default)]
1183#[repr(u64)]
1184pub enum MTLPixelFormat {
1185 Invalid = 0,
1187 #[default]
1189 BGRA8Unorm = 80,
1190 BGR10A2Unorm = 94,
1192 R8Unorm = 10,
1194 RG8Unorm = 30,
1196}
1197
1198impl MTLPixelFormat {
1199 #[must_use]
1201 pub const fn raw(self) -> u64 {
1202 self as u64
1203 }
1204}
1205
1206#[derive(Debug, Clone, Copy, Default)]
1208#[repr(u64)]
1209pub enum MTLVertexFormat {
1210 Invalid = 0,
1212 #[default]
1214 Float2 = 29,
1215 Float3 = 30,
1217 Float4 = 31,
1219}
1220
1221impl MTLVertexFormat {
1222 #[must_use]
1224 pub const fn raw(self) -> u64 {
1225 self as u64
1226 }
1227}
1228
1229#[derive(Debug, Clone, Copy, Default)]
1231#[repr(u64)]
1232pub enum MTLVertexStepFunction {
1233 Constant = 0,
1235 #[default]
1237 PerVertex = 1,
1238 PerInstance = 2,
1240}
1241
1242impl MTLVertexStepFunction {
1243 #[must_use]
1245 pub const fn raw(self) -> u64 {
1246 self as u64
1247 }
1248}
1249
1250#[derive(Debug, Clone, Copy, Default)]
1252#[repr(u64)]
1253pub enum MTLPrimitiveType {
1254 Point = 0,
1256 Line = 1,
1258 LineStrip = 2,
1260 #[default]
1262 Triangle = 3,
1263 TriangleStrip = 4,
1265}
1266
1267impl MTLPrimitiveType {
1268 #[must_use]
1270 pub const fn raw(self) -> u64 {
1271 self as u64
1272 }
1273}
1274
1275#[derive(Debug, Clone, Copy, Default)]
1277#[repr(u64)]
1278pub enum MTLBlendOperation {
1279 #[default]
1281 Add = 0,
1282 Subtract = 1,
1284 ReverseSubtract = 2,
1286 Min = 3,
1288 Max = 4,
1290}
1291
1292#[derive(Debug, Clone, Copy, Default)]
1294#[repr(u64)]
1295pub enum MTLBlendFactor {
1296 Zero = 0,
1298 #[default]
1300 One = 1,
1301 SourceColor = 2,
1303 OneMinusSourceColor = 3,
1305 SourceAlpha = 4,
1307 OneMinusSourceAlpha = 5,
1309 DestinationColor = 6,
1311 OneMinusDestinationColor = 7,
1313 DestinationAlpha = 8,
1315 OneMinusDestinationAlpha = 9,
1317}
1318
1319impl MetalRenderPassDescriptor {
1320 #[must_use]
1325 pub fn new() -> Self {
1326 let ptr = unsafe { metal_render_pass_descriptor_create() };
1327 Self {
1328 ptr: NonNull::new(ptr).expect("render pass descriptor create failed"),
1329 }
1330 }
1331
1332 pub fn set_color_attachment_texture(&self, index: usize, texture: &MetalTexture) {
1334 unsafe {
1335 metal_render_pass_set_color_attachment_texture(
1336 self.ptr.as_ptr(),
1337 index,
1338 texture.as_ptr(),
1339 );
1340 }
1341 }
1342
1343 pub fn set_color_attachment_load_action(&self, index: usize, action: MTLLoadAction) {
1345 unsafe {
1346 metal_render_pass_set_color_attachment_load_action(
1347 self.ptr.as_ptr(),
1348 index,
1349 action as u64,
1350 );
1351 }
1352 }
1353
1354 pub fn set_color_attachment_store_action(&self, index: usize, action: MTLStoreAction) {
1356 unsafe {
1357 metal_render_pass_set_color_attachment_store_action(
1358 self.ptr.as_ptr(),
1359 index,
1360 action as u64,
1361 );
1362 }
1363 }
1364
1365 pub fn set_color_attachment_clear_color(&self, index: usize, r: f64, g: f64, b: f64, a: f64) {
1367 unsafe {
1368 metal_render_pass_set_color_attachment_clear_color(
1369 self.ptr.as_ptr(),
1370 index,
1371 r,
1372 g,
1373 b,
1374 a,
1375 );
1376 }
1377 }
1378
1379 #[must_use]
1381 pub fn as_ptr(&self) -> *mut c_void {
1382 self.ptr.as_ptr()
1383 }
1384}
1385
1386impl Default for MetalRenderPassDescriptor {
1387 fn default() -> Self {
1388 Self::new()
1389 }
1390}
1391
1392impl Drop for MetalRenderPassDescriptor {
1393 fn drop(&mut self) {
1394 unsafe { metal_render_pass_descriptor_release(self.ptr.as_ptr()) }
1395 }
1396}
1397
1398#[derive(Debug)]
1402pub struct MetalVertexDescriptor {
1403 ptr: NonNull<c_void>,
1404}
1405
1406impl MetalVertexDescriptor {
1407 #[must_use]
1412 pub fn new() -> Self {
1413 let ptr = unsafe { metal_vertex_descriptor_create() };
1414 Self {
1415 ptr: NonNull::new(ptr).expect("vertex descriptor create failed"),
1416 }
1417 }
1418
1419 pub fn set_attribute(
1421 &self,
1422 index: usize,
1423 format: MTLVertexFormat,
1424 offset: usize,
1425 buffer_index: usize,
1426 ) {
1427 unsafe {
1428 metal_vertex_descriptor_set_attribute(
1429 self.ptr.as_ptr(),
1430 index,
1431 format.raw(),
1432 offset,
1433 buffer_index,
1434 );
1435 }
1436 }
1437
1438 pub fn set_layout(
1440 &self,
1441 buffer_index: usize,
1442 stride: usize,
1443 step_function: MTLVertexStepFunction,
1444 ) {
1445 unsafe {
1446 metal_vertex_descriptor_set_layout(
1447 self.ptr.as_ptr(),
1448 buffer_index,
1449 stride,
1450 step_function.raw(),
1451 );
1452 }
1453 }
1454
1455 #[must_use]
1457 pub fn as_ptr(&self) -> *mut c_void {
1458 self.ptr.as_ptr()
1459 }
1460}
1461
1462impl Default for MetalVertexDescriptor {
1463 fn default() -> Self {
1464 Self::new()
1465 }
1466}
1467
1468impl Drop for MetalVertexDescriptor {
1469 fn drop(&mut self) {
1470 unsafe { metal_vertex_descriptor_release(self.ptr.as_ptr()) }
1471 }
1472}
1473
1474#[derive(Debug)]
1478pub struct MetalRenderPipelineDescriptor {
1479 ptr: NonNull<c_void>,
1480}
1481
1482impl MetalRenderPipelineDescriptor {
1483 #[must_use]
1488 pub fn new() -> Self {
1489 let ptr = unsafe { metal_render_pipeline_descriptor_create() };
1490 Self {
1491 ptr: NonNull::new(ptr).expect("render pipeline descriptor create failed"),
1492 }
1493 }
1494
1495 pub fn set_vertex_function(&self, function: &MetalFunction) {
1497 unsafe {
1498 metal_render_pipeline_descriptor_set_vertex_function(
1499 self.ptr.as_ptr(),
1500 function.as_ptr(),
1501 );
1502 }
1503 }
1504
1505 pub fn set_fragment_function(&self, function: &MetalFunction) {
1507 unsafe {
1508 metal_render_pipeline_descriptor_set_fragment_function(
1509 self.ptr.as_ptr(),
1510 function.as_ptr(),
1511 );
1512 }
1513 }
1514
1515 pub fn set_vertex_descriptor(&self, descriptor: &MetalVertexDescriptor) {
1517 unsafe {
1518 metal_render_pipeline_descriptor_set_vertex_descriptor(
1519 self.ptr.as_ptr(),
1520 descriptor.as_ptr(),
1521 );
1522 }
1523 }
1524
1525 pub fn set_color_attachment_pixel_format(&self, index: usize, format: MTLPixelFormat) {
1527 unsafe {
1528 metal_render_pipeline_descriptor_set_color_attachment_pixel_format(
1529 self.ptr.as_ptr(),
1530 index,
1531 format.raw(),
1532 );
1533 }
1534 }
1535
1536 pub fn set_blending_enabled(&self, index: usize, enabled: bool) {
1538 unsafe {
1539 metal_render_pipeline_descriptor_set_blending_enabled(
1540 self.ptr.as_ptr(),
1541 index,
1542 enabled,
1543 );
1544 }
1545 }
1546
1547 pub fn set_blend_operations(
1549 &self,
1550 index: usize,
1551 rgb_op: MTLBlendOperation,
1552 alpha_op: MTLBlendOperation,
1553 ) {
1554 unsafe {
1555 metal_render_pipeline_descriptor_set_blend_operations(
1556 self.ptr.as_ptr(),
1557 index,
1558 rgb_op as u64,
1559 alpha_op as u64,
1560 );
1561 }
1562 }
1563
1564 pub fn set_blend_factors(
1566 &self,
1567 index: usize,
1568 src_rgb: MTLBlendFactor,
1569 dst_rgb: MTLBlendFactor,
1570 src_alpha: MTLBlendFactor,
1571 dst_alpha: MTLBlendFactor,
1572 ) {
1573 unsafe {
1574 metal_render_pipeline_descriptor_set_blend_factors(
1575 self.ptr.as_ptr(),
1576 index,
1577 src_rgb as u64,
1578 dst_rgb as u64,
1579 src_alpha as u64,
1580 dst_alpha as u64,
1581 );
1582 }
1583 }
1584
1585 #[must_use]
1587 pub fn as_ptr(&self) -> *mut c_void {
1588 self.ptr.as_ptr()
1589 }
1590}
1591
1592impl Default for MetalRenderPipelineDescriptor {
1593 fn default() -> Self {
1594 Self::new()
1595 }
1596}
1597
1598impl Drop for MetalRenderPipelineDescriptor {
1599 fn drop(&mut self) {
1600 unsafe { metal_render_pipeline_descriptor_release(self.ptr.as_ptr()) }
1601 }
1602}
1603
1604#[derive(Debug)]
1608pub struct MetalRenderPipelineState {
1609 ptr: NonNull<c_void>,
1610}
1611
1612impl MetalRenderPipelineState {
1613 #[must_use]
1615 pub fn as_ptr(&self) -> *mut c_void {
1616 self.ptr.as_ptr()
1617 }
1618}
1619
1620impl Drop for MetalRenderPipelineState {
1621 fn drop(&mut self) {
1622 unsafe { metal_render_pipeline_state_release(self.ptr.as_ptr()) }
1623 }
1624}
1625
1626unsafe impl Send for MetalRenderPipelineState {}
1627unsafe impl Sync for MetalRenderPipelineState {}
1628
1629#[derive(Debug)]
1633pub struct MetalRenderCommandEncoder {
1634 ptr: NonNull<c_void>,
1635}
1636
1637impl MetalRenderCommandEncoder {
1638 pub fn set_render_pipeline_state(&self, state: &MetalRenderPipelineState) {
1640 unsafe { metal_render_encoder_set_pipeline_state(self.ptr.as_ptr(), state.as_ptr()) }
1641 }
1642
1643 pub fn set_vertex_buffer(&self, buffer: &MetalBuffer, offset: usize, index: usize) {
1645 unsafe {
1646 metal_render_encoder_set_vertex_buffer(
1647 self.ptr.as_ptr(),
1648 buffer.as_ptr(),
1649 offset,
1650 index,
1651 );
1652 }
1653 }
1654
1655 pub fn set_fragment_buffer(&self, buffer: &MetalBuffer, offset: usize, index: usize) {
1657 unsafe {
1658 metal_render_encoder_set_fragment_buffer(
1659 self.ptr.as_ptr(),
1660 buffer.as_ptr(),
1661 offset,
1662 index,
1663 );
1664 }
1665 }
1666
1667 pub fn set_fragment_texture(&self, texture: &MetalTexture, index: usize) {
1669 unsafe {
1670 metal_render_encoder_set_fragment_texture(self.ptr.as_ptr(), texture.as_ptr(), index);
1671 }
1672 }
1673
1674 pub fn draw_primitives(
1676 &self,
1677 primitive_type: MTLPrimitiveType,
1678 vertex_start: usize,
1679 vertex_count: usize,
1680 ) {
1681 unsafe {
1682 metal_render_encoder_draw_primitives(
1683 self.ptr.as_ptr(),
1684 primitive_type.raw(),
1685 vertex_start,
1686 vertex_count,
1687 );
1688 }
1689 }
1690
1691 pub fn end_encoding(&self) {
1693 unsafe { metal_render_encoder_end_encoding(self.ptr.as_ptr()) }
1694 }
1695
1696 #[must_use]
1698 pub fn as_ptr(&self) -> *mut c_void {
1699 self.ptr.as_ptr()
1700 }
1701}
1702
1703impl Drop for MetalRenderCommandEncoder {
1704 fn drop(&mut self) {
1705 unsafe { metal_render_encoder_release(self.ptr.as_ptr()) }
1706 }
1707}
1708
1709pub type MetalCapturedTextures = CapturedTextures<MetalTexture>;
1713
1714pub trait IOSurfaceMetalExt {
1724 fn info(&self) -> IOSurfaceInfo;
1726 fn is_ycbcr_biplanar(&self) -> bool;
1728 fn texture_params(&self) -> Vec<TextureParams>;
1730 fn metal_textures<T, F>(&self, create_texture: F) -> Option<CapturedTextures<T>>
1732 where
1733 F: Fn(&TextureParams, *const c_void) -> Option<T>;
1734 fn create_metal_textures(&self, device: &MetalDevice) -> Option<MetalCapturedTextures>;
1736}
1737
1738impl IOSurfaceMetalExt for IOSurface {
1739 fn info(&self) -> IOSurfaceInfo {
1741 let width = self.width();
1742 let height = self.height();
1743 let bytes_per_row = self.bytes_per_row();
1744 let pix_format: FourCharCode = self.pixel_format().into();
1745 let plane_count = self.plane_count();
1746
1747 let planes = if plane_count > 0 {
1748 (0..plane_count)
1749 .map(|i| PlaneInfo {
1750 index: i,
1751 width: self.width_of_plane(i),
1752 height: self.height_of_plane(i),
1753 bytes_per_row: self.bytes_per_row_of_plane(i),
1754 })
1755 .collect()
1756 } else {
1757 vec![]
1758 };
1759
1760 IOSurfaceInfo {
1761 width,
1762 height,
1763 bytes_per_row,
1764 pixel_format: pix_format,
1765 plane_count,
1766 planes,
1767 }
1768 }
1769
1770 fn is_ycbcr_biplanar(&self) -> bool {
1772 pixel_format::is_ycbcr_biplanar(self.pixel_format())
1773 }
1774
1775 fn texture_params(&self) -> Vec<TextureParams> {
1781 let pix_format: FourCharCode = self.pixel_format().into();
1782 let plane_count = self.plane_count();
1783
1784 if pix_format == pixel_format::BGRA {
1785 vec![TextureParams {
1786 width: self.width(),
1787 height: self.height(),
1788 format: MetalPixelFormat::BGRA8Unorm,
1789 plane: 0,
1790 }]
1791 } else if pix_format == pixel_format::L10R {
1792 vec![TextureParams {
1793 width: self.width(),
1794 height: self.height(),
1795 format: MetalPixelFormat::BGR10A2Unorm,
1796 plane: 0,
1797 }]
1798 } else if pixel_format::is_ycbcr_biplanar(pix_format) && plane_count >= 2 {
1799 vec![
1800 TextureParams {
1802 width: self.width_of_plane(0),
1803 height: self.height_of_plane(0),
1804 format: MetalPixelFormat::R8Unorm,
1805 plane: 0,
1806 },
1807 TextureParams {
1809 width: self.width_of_plane(1),
1810 height: self.height_of_plane(1),
1811 format: MetalPixelFormat::RG8Unorm,
1812 plane: 1,
1813 },
1814 ]
1815 } else {
1816 vec![TextureParams {
1818 width: self.width(),
1819 height: self.height(),
1820 format: MetalPixelFormat::BGRA8Unorm,
1821 plane: 0,
1822 }]
1823 }
1824 }
1825
1826 fn metal_textures<T, F>(&self, create_texture: F) -> Option<CapturedTextures<T>>
1860 where
1861 F: Fn(&TextureParams, *const c_void) -> Option<T>,
1862 {
1863 let width = self.width();
1864 let height = self.height();
1865 let pix_format: FourCharCode = self.pixel_format().into();
1866
1867 if width == 0 || height == 0 {
1868 return None;
1869 }
1870
1871 let iosurface_ptr = self.as_ptr();
1872 let params = self.texture_params();
1873
1874 if params.len() == 1 {
1875 let texture = create_texture(¶ms[0], iosurface_ptr)?;
1877 Some(CapturedTextures {
1878 plane0: texture,
1879 plane1: None,
1880 pixel_format: pix_format,
1881 width,
1882 height,
1883 })
1884 } else if params.len() >= 2 {
1885 let y_texture = create_texture(¶ms[0], iosurface_ptr)?;
1887 let uv_texture = create_texture(¶ms[1], iosurface_ptr)?;
1888 Some(CapturedTextures {
1889 plane0: y_texture,
1890 plane1: Some(uv_texture),
1891 pixel_format: pix_format,
1892 width,
1893 height,
1894 })
1895 } else {
1896 None
1897 }
1898 }
1899
1900 fn create_metal_textures(&self, device: &MetalDevice) -> Option<MetalCapturedTextures> {
1920 let width = self.width();
1921 let height = self.height();
1922 let pix_format: FourCharCode = self.pixel_format().into();
1923
1924 if width == 0 || height == 0 {
1925 return None;
1926 }
1927
1928 let params = self.texture_params();
1929
1930 if params.len() == 1 {
1931 let texture = create_texture_for_plane(self, device, ¶ms[0])?;
1933 Some(CapturedTextures {
1934 plane0: texture,
1935 plane1: None,
1936 pixel_format: pix_format,
1937 width,
1938 height,
1939 })
1940 } else if params.len() >= 2 {
1941 let y_texture = create_texture_for_plane(self, device, ¶ms[0])?;
1943 let uv_texture = create_texture_for_plane(self, device, ¶ms[1])?;
1944 Some(CapturedTextures {
1945 plane0: y_texture,
1946 plane1: Some(uv_texture),
1947 pixel_format: pix_format,
1948 width,
1949 height,
1950 })
1951 } else {
1952 None
1953 }
1954 }
1955}
1956
1957fn create_texture_for_plane(
1962 surface: &IOSurface,
1963 device: &MetalDevice,
1964 params: &TextureParams,
1965) -> Option<MetalTexture> {
1966 let ptr = unsafe {
1967 metal_create_texture_from_iosurface(
1968 device.as_ptr(),
1969 surface.as_ptr(),
1970 params.plane,
1971 params.width,
1972 params.height,
1973 params.format.raw(),
1974 )
1975 };
1976 NonNull::new(ptr).map(|ptr| MetalTexture { ptr })
1977}
1978
1979#[link(name = "Foundation", kind = "framework")]
1982extern "C" {
1983 fn objc_autoreleasePoolPush() -> *mut c_void;
1984 fn objc_autoreleasePoolPop(pool: *mut c_void);
1985}
1986
1987pub fn autoreleasepool<F, R>(f: F) -> R
2004where
2005 F: FnOnce() -> R,
2006{
2007 unsafe {
2008 let pool = objc_autoreleasePoolPush();
2009 let result = f();
2010 objc_autoreleasePoolPop(pool);
2011 result
2012 }
2013}
2014
2015pub unsafe fn setup_metal_view(view: *mut c_void, layer: &MetalLayer) {
2037 nsview_set_wants_layer(view);
2038 nsview_set_layer(view, layer.as_ptr());
2039}