From e211c3f00a847f50b286349918e5c51967862e93 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Tue, 6 Dec 2022 22:00:25 +0000 Subject: UI: Add Metal surface creation for MoltenVK (#3980) * Initial implementation of metal surface across UIs * Fix SDL2 on windows * Update Ryujinx/Ryujinx.csproj Co-authored-by: Mary-nyan * Address Feedback Co-authored-by: Mary-nyan --- Ryujinx.Headless.SDL2/Program.cs | 20 +++++++++++++ Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj | 3 +- Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs | 28 +++++++++++++++--- Ryujinx.Headless.SDL2/WindowBase.cs | 34 +++++++++++++++++----- 4 files changed, 72 insertions(+), 13 deletions(-) (limited to 'Ryujinx.Headless.SDL2') diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index bfc33edc..50a90763 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -77,6 +77,26 @@ namespace Ryujinx.Headless.SDL2 _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient); _userChannelPersistence = new UserChannelPersistence(); + if (OperatingSystem.IsMacOS()) + { + AutoResetEvent invoked = new AutoResetEvent(false); + + // MacOS must perform SDL polls from the main thread. + Ryujinx.SDL2.Common.SDL2Driver.MainThreadDispatcher = (Action action) => + { + invoked.Reset(); + + WindowBase.QueueMainThreadAction(() => + { + action(); + + invoked.Set(); + }); + + invoked.WaitOne(); + }; + } + _inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver()); GraphicsConfig.EnableShaderCache = true; diff --git a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index 15286ea3..83ae87eb 100644 --- a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -12,7 +12,8 @@ - + + diff --git a/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs b/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs index 6eacadc1..18323339 100644 --- a/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs +++ b/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Input.HLE; +using Ryujinx.SDL2.Common; using System; using System.Runtime.InteropServices; using static SDL2.SDL; @@ -26,15 +27,34 @@ namespace Ryujinx.Headless.SDL2.Vulkan MouseDriver.SetClientSize(DefaultWidth, DefaultHeight); } + private void BasicInvoke(Action action) + { + action(); + } + public unsafe IntPtr CreateWindowSurface(IntPtr instance) { - if (SDL_Vulkan_CreateSurface(WindowHandle, instance, out ulong surfaceHandle) == SDL_bool.SDL_FALSE) + ulong surfaceHandle = 0; + + Action createSurface = () => { - string errorMessage = $"SDL_Vulkan_CreateSurface failed with error \"{SDL_GetError()}\""; + if (SDL_Vulkan_CreateSurface(WindowHandle, instance, out surfaceHandle) == SDL_bool.SDL_FALSE) + { + string errorMessage = $"SDL_Vulkan_CreateSurface failed with error \"{SDL_GetError()}\""; - Logger.Error?.Print(LogClass.Application, errorMessage); + Logger.Error?.Print(LogClass.Application, errorMessage); - throw new Exception(errorMessage); + throw new Exception(errorMessage); + } + }; + + if (SDL2Driver.MainThreadDispatcher != null) + { + SDL2Driver.MainThreadDispatcher(createSurface); + } + else + { + createSurface(); } return (IntPtr)surfaceHandle; diff --git a/Ryujinx.Headless.SDL2/WindowBase.cs b/Ryujinx.Headless.SDL2/WindowBase.cs index 9aa17936..88b0d573 100644 --- a/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/Ryujinx.Headless.SDL2/WindowBase.cs @@ -11,6 +11,7 @@ using Ryujinx.Input; using Ryujinx.Input.HLE; using Ryujinx.SDL2.Common; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Threading; @@ -26,6 +27,13 @@ namespace Ryujinx.Headless.SDL2 private const SDL_WindowFlags DefaultFlags = SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI | SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS | SDL_WindowFlags.SDL_WINDOW_SHOWN; private const int TargetFps = 60; + private static ConcurrentQueue MainThreadActions = new ConcurrentQueue(); + + public static void QueueMainThreadAction(Action action) + { + MainThreadActions.Enqueue(action); + } + public NpadManager NpadManager { get; } public TouchScreenManager TouchScreenManager { get; } public Switch Device { get; private set; } @@ -168,6 +176,14 @@ namespace Ryujinx.Headless.SDL2 public void Render() { + InitializeWindowRenderer(); + + Device.Gpu.Renderer.Initialize(_glLogLevel); + + InitializeRenderer(); + + _gpuVendorName = GetGpuVendorName(); + Device.Gpu.Renderer.RunLoop(() => { Device.Gpu.SetGpuThread(); @@ -241,6 +257,14 @@ namespace Ryujinx.Headless.SDL2 _exitEvent.Dispose(); } + public void ProcessMainThreadQueue() + { + while (MainThreadActions.TryDequeue(out Action action)) + { + action(); + } + } + public void MainLoop() { while (_isActive) @@ -249,6 +273,8 @@ namespace Ryujinx.Headless.SDL2 SDL_PumpEvents(); + ProcessMainThreadQueue(); + // Polling becomes expensive if it's not slept Thread.Sleep(1); } @@ -315,14 +341,6 @@ namespace Ryujinx.Headless.SDL2 InitializeWindow(); - InitializeWindowRenderer(); - - Device.Gpu.Renderer.Initialize(_glLogLevel); - - InitializeRenderer(); - - _gpuVendorName = GetGpuVendorName(); - Thread renderLoopThread = new Thread(Render) { Name = "GUI.RenderLoop" -- cgit v1.2.3