diff --git a/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs b/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs
new file mode 100644
index 0000000000..c37bf2472a
--- /dev/null
+++ b/src/Ryujinx.Headless.SDL2/Metal/MetalWindow.cs
@@ -0,0 +1,49 @@
+using Ryujinx.Common.Configuration;
+using Ryujinx.Input.HLE;
+using Ryujinx.SDL2.Common;
+using SharpMetal.QuartzCore;
+using System.Runtime.Versioning;
+using static SDL2.SDL;
+
+namespace Ryujinx.Headless.SDL2.Metal
+{
+ [SupportedOSPlatform("macos")]
+ class MetalWindow : WindowBase
+ {
+ private CAMetalLayer _caMetalLayer;
+
+ public CAMetalLayer GetLayer()
+ {
+ return _caMetalLayer;
+ }
+
+ public MetalWindow(
+ InputManager inputManager,
+ GraphicsDebugLevel glLogLevel,
+ AspectRatio aspectRatio,
+ bool enableMouse,
+ HideCursorMode hideCursorMode)
+ : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode) { }
+
+ public override SDL_WindowFlags GetWindowFlags() => SDL_WindowFlags.SDL_WINDOW_METAL;
+
+ protected override void InitializeWindowRenderer()
+ {
+ void CreateLayer()
+ {
+ _caMetalLayer = new CAMetalLayer(SDL_Metal_GetLayer(SDL_Metal_CreateView(WindowHandle)));
+ }
+
+ if (SDL2Driver.MainThreadDispatcher != null)
+ {
+ SDL2Driver.MainThreadDispatcher(CreateLayer);
+ }
+ }
+
+ protected override void InitializeRenderer() { }
+
+ protected override void FinalizeWindowRenderer() { }
+
+ protected override void SwapBuffers() { }
+ }
+}
diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs
index 4ee2712037..27b91418d9 100644
--- a/src/Ryujinx.Headless.SDL2/Program.cs
+++ b/src/Ryujinx.Headless.SDL2/Program.cs
@@ -20,6 +20,8 @@ using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
using Ryujinx.Graphics.Vulkan.MoltenVK;
+using Ryujinx.Graphics.Metal;
+using Ryujinx.Headless.SDL2.Metal;
using Ryujinx.Headless.SDL2.OpenGL;
using Ryujinx.Headless.SDL2.Vulkan;
using Ryujinx.HLE;
@@ -507,9 +509,12 @@ namespace Ryujinx.Headless.SDL2
private static WindowBase CreateWindow(Options options)
{
- return options.GraphicsBackend == GraphicsBackend.Vulkan
- ? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode)
- : new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode);
+ return options.GraphicsBackend switch
+ {
+ GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode),
+ GraphicsBackend.Metal => new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode),
+ _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode)
+ };
}
private static IRenderer CreateRenderer(Options options, WindowBase window)
@@ -541,6 +546,11 @@ namespace Ryujinx.Headless.SDL2
preferredGpuId);
}
+ if (options.GraphicsBackend == GraphicsBackend.Metal && window is MetalWindow metalWindow && OperatingSystem.IsMacOS())
+ {
+ return new MetalRenderer(metalWindow.GetLayer);
+ }
+
return new OpenGLRenderer();
}
diff --git a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
index 6102295444..6846a6f7fb 100644
--- a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
+++ b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
@@ -29,6 +29,7 @@
+