MetalHelper.cs
1 using Gdk; 2 using System; 3 using System.Runtime.InteropServices; 4 using System.Runtime.Versioning; 5 6 namespace Ryujinx.UI.Helper 7 { 8 public delegate void UpdateBoundsCallbackDelegate(Window window); 9 10 [SupportedOSPlatform("macos")] 11 static partial class MetalHelper 12 { 13 private const string LibObjCImport = "/usr/lib/libobjc.A.dylib"; 14 15 private readonly struct Selector 16 { 17 public readonly IntPtr NativePtr; 18 19 public unsafe Selector(string value) 20 { 21 int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length); 22 byte* data = stackalloc byte[size]; 23 24 fixed (char* pValue = value) 25 { 26 System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size); 27 } 28 29 NativePtr = sel_registerName(data); 30 } 31 32 public static implicit operator Selector(string value) => new(value); 33 } 34 35 private static unsafe IntPtr GetClass(string value) 36 { 37 int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length); 38 byte* data = stackalloc byte[size]; 39 40 fixed (char* pValue = value) 41 { 42 System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size); 43 } 44 45 return objc_getClass(data); 46 } 47 48 private struct NsPoint 49 { 50 public double X; 51 public double Y; 52 53 public NsPoint(double x, double y) 54 { 55 X = x; 56 Y = y; 57 } 58 } 59 60 private struct NsRect 61 { 62 public NsPoint Pos; 63 public NsPoint Size; 64 65 public NsRect(double x, double y, double width, double height) 66 { 67 Pos = new NsPoint(x, y); 68 Size = new NsPoint(width, height); 69 } 70 } 71 72 public static IntPtr GetMetalLayer(Display display, Window window, out IntPtr nsView, out UpdateBoundsCallbackDelegate updateBounds) 73 { 74 nsView = gdk_quartz_window_get_nsview(window.Handle); 75 76 // Create a new CAMetalLayer. 77 IntPtr layerClass = GetClass("CAMetalLayer"); 78 IntPtr metalLayer = IntPtr_objc_msgSend(layerClass, "alloc"); 79 objc_msgSend(metalLayer, "init"); 80 81 // Create a child NSView to render into. 82 IntPtr nsViewClass = GetClass("NSView"); 83 IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc"); 84 objc_msgSend(child, "init", new NsRect()); 85 86 // Add it as a child. 87 objc_msgSend(nsView, "addSubview:", child); 88 89 // Make its renderer our metal layer. 90 objc_msgSend(child, "setWantsLayer:", (byte)1); 91 objc_msgSend(child, "setLayer:", metalLayer); 92 objc_msgSend(metalLayer, "setContentsScale:", (double)display.GetMonitorAtWindow(window).ScaleFactor); 93 94 // Set the frame position/location. 95 updateBounds = (Window window) => 96 { 97 window.GetPosition(out int x, out int y); 98 int width = window.Width; 99 int height = window.Height; 100 objc_msgSend(child, "setFrame:", new NsRect(x, y, width, height)); 101 }; 102 103 updateBounds(window); 104 105 return metalLayer; 106 } 107 108 [LibraryImport(LibObjCImport)] 109 private static unsafe partial IntPtr sel_registerName(byte* data); 110 111 [LibraryImport(LibObjCImport)] 112 private static unsafe partial IntPtr objc_getClass(byte* data); 113 114 [LibraryImport(LibObjCImport)] 115 private static partial void objc_msgSend(IntPtr receiver, Selector selector); 116 117 [LibraryImport(LibObjCImport)] 118 private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value); 119 120 [LibraryImport(LibObjCImport)] 121 private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value); 122 123 [LibraryImport(LibObjCImport)] 124 private static partial void objc_msgSend(IntPtr receiver, Selector selector, NsRect point); 125 126 [LibraryImport(LibObjCImport)] 127 private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value); 128 129 [LibraryImport(LibObjCImport, EntryPoint = "objc_msgSend")] 130 private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector); 131 132 [LibraryImport("libgdk-3.0.dylib")] 133 private static partial IntPtr gdk_quartz_window_get_nsview(IntPtr gdkWindow); 134 } 135 }