mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-05-13 08:45:47 +00:00
chore: SDL3 (ryubing/ryujinx!207)
See merge request ryubing/ryujinx!207
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
using DiscordRPC;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
using Ryujinx.Audio.Backends.SDL3;
|
||||
using Ryujinx.Ava;
|
||||
using Ryujinx.Ava.Systems;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
@@ -157,7 +157,7 @@ namespace Ryujinx.Headless
|
||||
config = new StandardControllerInputConfig
|
||||
{
|
||||
Version = InputConfig.CurrentVersion,
|
||||
Backend = InputBackendType.GamepadSDL2,
|
||||
Backend = InputBackendType.GamepadSDL3,
|
||||
Id = null,
|
||||
ControllerType = ControllerType.JoyconPair,
|
||||
DeadzoneLeft = 0.1f,
|
||||
@@ -305,8 +305,8 @@ namespace Ryujinx.Headless
|
||||
|
||||
return new VulkanRenderer(
|
||||
api,
|
||||
(instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))),
|
||||
vulkanWindow.GetRequiredInstanceExtensions,
|
||||
(instance, vk) => new SurfaceKHR((ulong)vulkanWindow.CreateWindowSurface(instance.Handle)),
|
||||
VulkanWindow.GetRequiredInstanceExtensions,
|
||||
preferredGpuId);
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ namespace Ryujinx.Headless
|
||||
_accountManager,
|
||||
_userChannelPersistence,
|
||||
renderer.TryMakeThreaded(options.BackendThreading),
|
||||
new SDL2HardwareDeviceDriver(),
|
||||
new SDL3HardwareDeviceDriver(),
|
||||
window
|
||||
)
|
||||
);
|
||||
|
||||
@@ -22,13 +22,14 @@ using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using SDL;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
{
|
||||
@@ -61,7 +62,7 @@ namespace Ryujinx.Headless
|
||||
AutoResetEvent invoked = new(false);
|
||||
|
||||
// MacOS must perform SDL polls from the main thread.
|
||||
SDL2Driver.MainThreadDispatcher = action =>
|
||||
SDL3Driver.MainThreadDispatcher = action =>
|
||||
{
|
||||
invoked.Reset();
|
||||
|
||||
@@ -180,7 +181,7 @@ namespace Ryujinx.Headless
|
||||
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile);
|
||||
_userChannelPersistence = new UserChannelPersistence();
|
||||
|
||||
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
||||
_inputManager = new InputManager(new SDL3KeyboardDriver(), new SDL3GamepadDriver());
|
||||
|
||||
GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;
|
||||
|
||||
@@ -396,7 +397,7 @@ namespace Ryujinx.Headless
|
||||
_window = window;
|
||||
|
||||
_window.IsFullscreen = options.IsFullscreen;
|
||||
_window.DisplayId = options.DisplayId;
|
||||
_window.DisplayId = (SDL_DisplayID)options.DisplayId;
|
||||
_window.IsExclusiveFullscreen = options.IsExclusiveFullscreen;
|
||||
_window.ExclusiveFullscreenWidth = options.ExclusiveFullscreenWidth;
|
||||
_window.ExclusiveFullscreenHeight = options.ExclusiveFullscreenHeight;
|
||||
|
||||
@@ -5,15 +5,17 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.OpenGL;
|
||||
using Ryujinx.Input.HLE;
|
||||
using System;
|
||||
using static SDL2.SDL;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
{
|
||||
class OpenGLWindow : WindowBase
|
||||
unsafe class OpenGLWindow : WindowBase
|
||||
{
|
||||
private static void CheckResult(int result)
|
||||
private static void CheckResult(bool result)
|
||||
{
|
||||
if (result < 0)
|
||||
if (!result)
|
||||
{
|
||||
throw new InvalidOperationException($"SDL_GL function returned an error: {SDL_GetError()}");
|
||||
}
|
||||
@@ -21,21 +23,21 @@ namespace Ryujinx.Headless
|
||||
|
||||
private static void SetupOpenGLAttributes(bool sharedContext, GraphicsDebugLevel debugLevel)
|
||||
{
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 3));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_COMPATIBILITY));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_FLAGS, debugLevel != GraphicsDebugLevel.None ? (int)SDL_GLcontext.SDL_GL_CONTEXT_DEBUG_FLAG : 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, sharedContext ? 1 : 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MAJOR_VERSION, 4));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MINOR_VERSION, 3));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_PROFILE_MASK, (int)SDL_GLProfile.SDL_GL_CONTEXT_PROFILE_COMPATIBILITY));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_FLAGS, debugLevel != GraphicsDebugLevel.None ? (int)SDL_GLContextFlag.SDL_GL_CONTEXT_DEBUG_FLAG : 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, sharedContext ? 1 : 0));
|
||||
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ACCELERATED_VISUAL, 1));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_RED_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_GREEN_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_BLUE_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ALPHA_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DEPTH_SIZE, 16));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STENCIL_SIZE, 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STEREO, 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_ACCELERATED_VISUAL, 1));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_RED_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_GREEN_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_BLUE_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_ALPHA_SIZE, 8));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_DEPTH_SIZE, 16));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_STENCIL_SIZE, 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_DOUBLEBUFFER, 1));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_STEREO, 0));
|
||||
}
|
||||
|
||||
private class OpenToolkitBindingsContext : IBindingsContext
|
||||
@@ -46,35 +48,35 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
private class SDL2OpenGLContext : IOpenGLContext
|
||||
private class SDL3OpenGLContext : IOpenGLContext
|
||||
{
|
||||
private readonly nint _context;
|
||||
private readonly nint _window;
|
||||
private readonly SDL_GLContextState* _context;
|
||||
private readonly SDL_Window* _window;
|
||||
private readonly bool _shouldDisposeWindow;
|
||||
|
||||
public SDL2OpenGLContext(nint context, nint window, bool shouldDisposeWindow = true)
|
||||
public SDL3OpenGLContext(SDL_GLContextState* context, SDL_Window* window, bool shouldDisposeWindow = true)
|
||||
{
|
||||
_context = context;
|
||||
_window = window;
|
||||
_shouldDisposeWindow = shouldDisposeWindow;
|
||||
}
|
||||
|
||||
public static SDL2OpenGLContext CreateBackgroundContext(SDL2OpenGLContext sharedContext)
|
||||
public unsafe static SDL3OpenGLContext CreateBackgroundContext(SDL3OpenGLContext sharedContext)
|
||||
{
|
||||
sharedContext.MakeCurrent();
|
||||
|
||||
// Ensure we share our contexts.
|
||||
SetupOpenGLAttributes(true, GraphicsDebugLevel.None);
|
||||
nint windowHandle = SDL_CreateWindow("Ryujinx background context window", 0, 0, 1, 1, SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN);
|
||||
nint context = SDL_GL_CreateContext(windowHandle);
|
||||
SDL_Window* windowHandle = SDL_CreateWindow("Ryujinx background context window", 1, 1, SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN);
|
||||
SDL_GLContextState* context = SDL_GL_CreateContext(windowHandle);
|
||||
|
||||
GL.LoadBindings(new OpenToolkitBindingsContext());
|
||||
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0));
|
||||
CheckResult(SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0));
|
||||
|
||||
CheckResult(SDL_GL_MakeCurrent(windowHandle, nint.Zero));
|
||||
CheckResult(SDL_GL_MakeCurrent(windowHandle, null));
|
||||
|
||||
return new SDL2OpenGLContext(context, windowHandle);
|
||||
return new SDL3OpenGLContext(context, windowHandle);
|
||||
}
|
||||
|
||||
public void MakeCurrent()
|
||||
@@ -84,9 +86,9 @@ namespace Ryujinx.Headless
|
||||
return;
|
||||
}
|
||||
|
||||
int res = SDL_GL_MakeCurrent(_window, _context);
|
||||
bool res = SDL_GL_MakeCurrent(_window, _context);
|
||||
|
||||
if (res != 0)
|
||||
if (!res)
|
||||
{
|
||||
string errorMessage = $"SDL_GL_CreateContext failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
@@ -96,11 +98,11 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasContext() => SDL_GL_GetCurrentContext() != nint.Zero;
|
||||
public bool HasContext() => SDL_GL_GetCurrentContext() != null;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SDL_GL_DeleteContext(_context);
|
||||
SDL_GL_DestroyContext(_context);
|
||||
|
||||
if (_shouldDisposeWindow)
|
||||
{
|
||||
@@ -109,7 +111,7 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
private SDL2OpenGLContext _openGLContext;
|
||||
private SDL3OpenGLContext _openGLContext;
|
||||
|
||||
public OpenGLWindow(
|
||||
InputManager inputManager,
|
||||
@@ -128,10 +130,10 @@ namespace Ryujinx.Headless
|
||||
{
|
||||
// Ensure to not share this context with other contexts before this point.
|
||||
SetupOpenGLAttributes(false, GlLogLevel);
|
||||
nint context = SDL_GL_CreateContext(WindowHandle);
|
||||
SDL_GLContextState* context = SDL_GL_CreateContext(WindowHandle);
|
||||
CheckResult(SDL_GL_SetSwapInterval(1));
|
||||
|
||||
if (context == nint.Zero)
|
||||
if (context == null)
|
||||
{
|
||||
string errorMessage = $"SDL_GL_CreateContext failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
@@ -141,10 +143,10 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
|
||||
// NOTE: The window handle needs to be disposed by the thread that created it and is handled separately.
|
||||
_openGLContext = new SDL2OpenGLContext(context, WindowHandle, false);
|
||||
_openGLContext = new SDL3OpenGLContext(context, WindowHandle, false);
|
||||
|
||||
// First take exclusivity on the OpenGL context.
|
||||
((OpenGLRenderer)Renderer).InitializeBackgroundContext(SDL2OpenGLContext.CreateBackgroundContext(_openGLContext));
|
||||
((OpenGLRenderer)Renderer).InitializeBackgroundContext(SDL3OpenGLContext.CreateBackgroundContext(_openGLContext));
|
||||
|
||||
_openGLContext.MakeCurrent();
|
||||
|
||||
@@ -160,7 +162,8 @@ namespace Ryujinx.Headless
|
||||
else if (IsFullscreen)
|
||||
{
|
||||
// NOTE: grabbing the main display's dimensions directly as OpenGL doesn't scale along like the VulkanWindow.
|
||||
if (SDL_GetDisplayBounds(DisplayId, out SDL_Rect displayBounds) < 0)
|
||||
SDL_Rect displayBounds = new();
|
||||
if (!SDL_GetDisplayBounds(DisplayId, &displayBounds))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Could not retrieve display bounds: {SDL_GetError()}");
|
||||
|
||||
@@ -189,7 +192,7 @@ namespace Ryujinx.Headless
|
||||
Device.DisposeGpu();
|
||||
|
||||
// Unbind context and destroy everything
|
||||
CheckResult(SDL_GL_MakeCurrent(WindowHandle, nint.Zero));
|
||||
CheckResult(SDL_GL_MakeCurrent(WindowHandle, null));
|
||||
_openGLContext.Dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
using System.Runtime.InteropServices;
|
||||
using static SDL2.SDL;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
{
|
||||
@@ -39,18 +40,15 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
private static void BasicInvoke(Action action)
|
||||
public unsafe nint CreateWindowSurface(nint instance)
|
||||
{
|
||||
action();
|
||||
}
|
||||
|
||||
public nint CreateWindowSurface(nint instance)
|
||||
{
|
||||
ulong surfaceHandle = 0;
|
||||
VkSurfaceKHR_T surface = new();
|
||||
VkSurfaceKHR_T* surfaceHandle = &surface;
|
||||
VkSurfaceKHR_T** surfaceHandleHandle = &surfaceHandle;
|
||||
|
||||
void CreateSurface()
|
||||
{
|
||||
if (SDL_Vulkan_CreateSurface(WindowHandle, instance, out surfaceHandle) == SDL_bool.SDL_FALSE)
|
||||
if (!SDL_Vulkan_CreateSurface(WindowHandle, (VkInstance_T*)instance, null, surfaceHandleHandle))
|
||||
{
|
||||
string errorMessage = $"SDL_Vulkan_CreateSurface failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
@@ -60,9 +58,9 @@ namespace Ryujinx.Headless
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL2Driver.MainThreadDispatcher != null)
|
||||
if (SDL3Driver.MainThreadDispatcher != null)
|
||||
{
|
||||
SDL2Driver.MainThreadDispatcher(CreateSurface);
|
||||
SDL3Driver.MainThreadDispatcher(CreateSurface);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -72,32 +70,22 @@ namespace Ryujinx.Headless
|
||||
return (nint)surfaceHandle;
|
||||
}
|
||||
|
||||
public unsafe string[] GetRequiredInstanceExtensions()
|
||||
public unsafe static string[] GetRequiredInstanceExtensions()
|
||||
{
|
||||
if (SDL_Vulkan_GetInstanceExtensions(WindowHandle, out uint extensionsCount, nint.Zero) == SDL_bool.SDL_TRUE)
|
||||
{
|
||||
nint[] rawExtensions = new nint[(int)extensionsCount];
|
||||
string[] extensions = new string[(int)extensionsCount];
|
||||
uint extensionCount = 0;
|
||||
byte** extensions = SDL_Vulkan_GetInstanceExtensions(&extensionCount);
|
||||
if (extensionCount == 0) {
|
||||
string errorMessage = $"SDL_Vulkan_GetInstanceExtensions failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
fixed (nint* rawExtensionsPtr = rawExtensions)
|
||||
{
|
||||
if (SDL_Vulkan_GetInstanceExtensions(WindowHandle, out extensionsCount, (nint)rawExtensionsPtr) == SDL_bool.SDL_TRUE)
|
||||
{
|
||||
for (int i = 0; i < extensions.Length; i++)
|
||||
{
|
||||
extensions[i] = Marshal.PtrToStringUTF8(rawExtensions[i]);
|
||||
}
|
||||
Logger.Error?.Print(LogClass.Application, errorMessage);
|
||||
|
||||
return extensions;
|
||||
}
|
||||
}
|
||||
throw new Exception(errorMessage);
|
||||
}
|
||||
|
||||
string errorMessage = $"SDL_Vulkan_GetInstanceExtensions failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
Logger.Error?.Print(LogClass.Application, errorMessage);
|
||||
|
||||
throw new Exception(errorMessage);
|
||||
string[] extensionArr = new string[extensionCount];
|
||||
for (int i = 0; i < extensionCount; i++) {
|
||||
extensionArr[i] = Marshal.PtrToStringUTF8((nint)extensions[i]);
|
||||
}
|
||||
return extensionArr;
|
||||
}
|
||||
|
||||
protected override void FinalizeWindowRenderer()
|
||||
|
||||
@@ -15,37 +15,34 @@ using Ryujinx.HLE.Loaders.Processes;
|
||||
using Ryujinx.HLE.UI;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
|
||||
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
|
||||
using Switch = Ryujinx.HLE.Switch;
|
||||
using UserProfile = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace Ryujinx.Headless
|
||||
{
|
||||
abstract partial class WindowBase : IHostUIHandler, IDisposable
|
||||
abstract unsafe partial class WindowBase : IHostUIHandler, IDisposable
|
||||
{
|
||||
protected const int DefaultWidth = 1280;
|
||||
protected const int DefaultHeight = 720;
|
||||
private const int TargetFps = 60;
|
||||
private 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 SDL_WindowFlags DefaultFlags = SDL_WindowFlags.SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS;
|
||||
private SDL_WindowFlags FullscreenFlag = 0;
|
||||
|
||||
private static readonly ConcurrentQueue<Action> _mainThreadActions = new();
|
||||
|
||||
[LibraryImport("SDL2")]
|
||||
// TODO: Remove this as soon as SDL2-CS was updated to expose this method publicly
|
||||
private static partial nint SDL_LoadBMP_RW(nint src, int freesrc);
|
||||
|
||||
public static void QueueMainThreadAction(Action action)
|
||||
{
|
||||
_mainThreadActions.Enqueue(action);
|
||||
@@ -56,12 +53,12 @@ namespace Ryujinx.Headless
|
||||
public Switch Device { get; private set; }
|
||||
public IRenderer Renderer { get; private set; }
|
||||
|
||||
protected nint WindowHandle { get; set; }
|
||||
protected SDL_Window* WindowHandle { get; set; }
|
||||
|
||||
public IHostUITheme HostUITheme { get; }
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
public int DisplayId { get; set; }
|
||||
public SDL_DisplayID DisplayId { get; set; }
|
||||
public bool IsFullscreen { get; set; }
|
||||
public bool IsExclusiveFullscreen { get; set; }
|
||||
public int ExclusiveFullscreenWidth { get; set; }
|
||||
@@ -70,7 +67,7 @@ namespace Ryujinx.Headless
|
||||
public ScalingFilter ScalingFilter { get; set; }
|
||||
public int ScalingFilterLevel { get; set; }
|
||||
|
||||
protected SDL2MouseDriver MouseDriver;
|
||||
protected SDL3MouseDriver MouseDriver;
|
||||
private readonly InputManager _inputManager;
|
||||
private readonly IKeyboard _keyboardInterface;
|
||||
protected readonly GraphicsDebugLevel GlLogLevel;
|
||||
@@ -83,7 +80,7 @@ namespace Ryujinx.Headless
|
||||
private long _ticks;
|
||||
private bool _isActive;
|
||||
private bool _isStopped;
|
||||
private uint _windowId;
|
||||
private SDL_WindowID _windowId;
|
||||
|
||||
private string _gpuDriverName;
|
||||
|
||||
@@ -99,7 +96,7 @@ namespace Ryujinx.Headless
|
||||
HideCursorMode hideCursorMode,
|
||||
bool ignoreControllerApplet)
|
||||
{
|
||||
MouseDriver = new SDL2MouseDriver(hideCursorMode);
|
||||
MouseDriver = new SDL3MouseDriver(hideCursorMode);
|
||||
_inputManager = inputManager;
|
||||
_inputManager.SetMouseDriver(MouseDriver);
|
||||
NpadManager = _inputManager.CreateNpadManager();
|
||||
@@ -116,7 +113,7 @@ namespace Ryujinx.Headless
|
||||
_ignoreControllerApplet = ignoreControllerApplet;
|
||||
HostUITheme = new HeadlessHostUiTheme();
|
||||
|
||||
SDL2Driver.Instance.Initialize();
|
||||
SDL3Driver.Instance.Initialize();
|
||||
}
|
||||
|
||||
public void Initialize(Switch device, List<InputConfig> inputConfigs, bool enableKeyboard, bool enableMouse)
|
||||
@@ -155,11 +152,11 @@ namespace Ryujinx.Headless
|
||||
{
|
||||
fixed (byte* iconPtr = iconBytes)
|
||||
{
|
||||
nint rwOpsStruct = SDL_RWFromConstMem((nint)iconPtr, iconBytes.Length);
|
||||
nint iconHandle = SDL_LoadBMP_RW(rwOpsStruct, 1);
|
||||
SDL_IOStream* rwOpsStruct = SDL_IOFromConstMem((nint)iconPtr, (nuint)iconBytes.Length);
|
||||
SDL_Surface* iconHandle = SDL_LoadBMP_IO(rwOpsStruct, true);
|
||||
|
||||
SDL_SetWindowIcon(WindowHandle, iconHandle);
|
||||
SDL_FreeSurface(iconHandle);
|
||||
SDL_DestroySurface(iconHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,18 +180,27 @@ namespace Ryujinx.Headless
|
||||
Width = ExclusiveFullscreenWidth;
|
||||
Height = ExclusiveFullscreenHeight;
|
||||
|
||||
DefaultFlags = SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
DefaultFlags = SDL_WindowFlags.SDL_WINDOW_HIGH_PIXEL_DENSITY;
|
||||
FullscreenFlag = SDL_WindowFlags.SDL_WINDOW_FULLSCREEN;
|
||||
}
|
||||
else if (IsFullscreen)
|
||||
{
|
||||
DefaultFlags = SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
FullscreenFlag = SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
DefaultFlags = SDL_WindowFlags.SDL_WINDOW_HIGH_PIXEL_DENSITY;
|
||||
FullscreenFlag = SDL_WindowFlags.SDL_WINDOW_BORDERLESS;
|
||||
}
|
||||
|
||||
WindowHandle = SDL_CreateWindow($"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}", SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId), SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId), Width, Height, DefaultFlags | FullscreenFlag | WindowFlags);
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}");
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId));
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId));
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, Width);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, Height);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, (long)(DefaultFlags | FullscreenFlag | WindowFlags));
|
||||
|
||||
if (WindowHandle == nint.Zero)
|
||||
WindowHandle = SDL_CreateWindowWithProperties(props);
|
||||
SDL_DestroyProperties(props);
|
||||
|
||||
if (WindowHandle == null)
|
||||
{
|
||||
string errorMessage = $"SDL_CreateWindow failed with error \"{SDL_GetError()}\"";
|
||||
|
||||
@@ -206,16 +212,16 @@ namespace Ryujinx.Headless
|
||||
SetWindowIcon();
|
||||
|
||||
_windowId = SDL_GetWindowID(WindowHandle);
|
||||
SDL2Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent);
|
||||
SDL3Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent);
|
||||
}
|
||||
|
||||
private void HandleWindowEvent(SDL_Event evnt)
|
||||
{
|
||||
if (evnt.type == SDL_EventType.SDL_WINDOWEVENT)
|
||||
if ((uint)evnt.Type >= (uint)SDL_EventType.SDL_EVENT_WINDOW_FIRST && (uint)evnt.Type <= (uint)SDL_EventType.SDL_EVENT_WINDOW_LAST)
|
||||
{
|
||||
switch (evnt.window.windowEvent)
|
||||
switch (evnt.Type)
|
||||
{
|
||||
case SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_EventType.SDL_EVENT_WINDOW_RESIZED:
|
||||
// Unlike on Windows, this event fires on macOS when triggering fullscreen mode.
|
||||
// And promptly crashes the process because `Renderer?.window.SetSize` is undefined.
|
||||
// As we don't need this to fire in either case we can test for fullscreen.
|
||||
@@ -229,7 +235,7 @@ namespace Ryujinx.Headless
|
||||
|
||||
break;
|
||||
|
||||
case SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE:
|
||||
case SDL_EventType.SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
Exit();
|
||||
break;
|
||||
}
|
||||
@@ -409,7 +415,7 @@ namespace Ryujinx.Headless
|
||||
// Get screen touch position
|
||||
if (!_enableMouse)
|
||||
{
|
||||
hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as SDL2MouseDriver).IsButtonPressed(MouseButton.Button1), _aspectRatio.ToFloat());
|
||||
hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as SDL3MouseDriver).IsButtonPressed(MouseButton.Button1), _aspectRatio.ToFloat());
|
||||
}
|
||||
|
||||
if (!hasTouch)
|
||||
@@ -461,7 +467,7 @@ namespace Ryujinx.Headless
|
||||
|
||||
public bool DisplayInputDialog(SoftwareKeyboardUIArgs args, out string userText)
|
||||
{
|
||||
// SDL2 doesn't support input dialogs
|
||||
// SDL3 doesn't support input dialogs
|
||||
userText = "Ryujinx";
|
||||
|
||||
return true;
|
||||
@@ -476,7 +482,7 @@ namespace Ryujinx.Headless
|
||||
|
||||
public bool DisplayCabinetDialog(out string userText)
|
||||
{
|
||||
// SDL2 doesn't support input dialogs
|
||||
// SDL3 doesn't support input dialogs
|
||||
userText = "Ryujinx";
|
||||
|
||||
return true;
|
||||
@@ -515,27 +521,36 @@ namespace Ryujinx.Headless
|
||||
Exit();
|
||||
}
|
||||
|
||||
public bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText, (uint Module, uint Description)? errorCode = null)
|
||||
public unsafe bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText, (uint Module, uint Description)? errorCode = null)
|
||||
{
|
||||
SDL_MessageBoxData data = new()
|
||||
{
|
||||
title = title,
|
||||
message = message,
|
||||
buttons = new SDL_MessageBoxButtonData[buttonsText.Length],
|
||||
numbuttons = buttonsText.Length,
|
||||
window = WindowHandle
|
||||
};
|
||||
SDL_MessageBoxButtonData[] buttons = new SDL_MessageBoxButtonData[buttonsText.Length];
|
||||
|
||||
for (int i = 0; i < buttonsText.Length; i++)
|
||||
{
|
||||
data.buttons[i] = new SDL_MessageBoxButtonData
|
||||
string buttonText = buttonsText[i];
|
||||
fixed (byte* pButtonText = &buttonText.ToBytes()[0])
|
||||
buttons[i] = new SDL_MessageBoxButtonData
|
||||
{
|
||||
buttonid = i,
|
||||
text = buttonsText[i],
|
||||
buttonID = i,
|
||||
text = pButtonText,
|
||||
};
|
||||
}
|
||||
|
||||
SDL_ShowMessageBox(ref data, out int _);
|
||||
fixed (byte* pTitle = &title.ToBytes()[0])
|
||||
fixed (byte* pMessage = &message.ToBytes()[0])
|
||||
fixed (SDL_MessageBoxButtonData* p = &buttons[0]) {
|
||||
SDL_MessageBoxData data = new()
|
||||
{
|
||||
title = pTitle,
|
||||
message = pMessage,
|
||||
buttons = p,
|
||||
numbuttons = buttonsText.Length,
|
||||
window = WindowHandle
|
||||
};
|
||||
|
||||
|
||||
SDL_ShowMessageBox(&data, null);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -553,11 +568,11 @@ namespace Ryujinx.Headless
|
||||
TouchScreenManager?.Dispose();
|
||||
NpadManager.Dispose();
|
||||
|
||||
SDL2Driver.Instance.UnregisterWindow(_windowId);
|
||||
SDL3Driver.Instance.UnregisterWindow(_windowId);
|
||||
|
||||
SDL_DestroyWindow(WindowHandle);
|
||||
|
||||
SDL2Driver.Instance.Dispose();
|
||||
SDL3Driver.Instance.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.SystemInterop;
|
||||
using Ryujinx.Graphics.Vulkan.MoltenVK;
|
||||
using Ryujinx.Headless;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.SDL3.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -149,8 +149,8 @@ namespace Ryujinx.Ava
|
||||
// Initialize Discord integration.
|
||||
DiscordIntegrationModule.Initialize();
|
||||
|
||||
// Initialize SDL2 driver
|
||||
SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input);
|
||||
// Initialize SDL3 driver
|
||||
SDL3Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input);
|
||||
|
||||
ReloadConfig();
|
||||
|
||||
|
||||
@@ -77,10 +77,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input.SDL3\Ryujinx.Input.SDL3.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
|
||||
@@ -8,7 +8,7 @@ using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Audio.Backends.Dummy;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
using Ryujinx.Audio.Backends.SDL3;
|
||||
using Ryujinx.Audio.Backends.SoundIo;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Ava.Common;
|
||||
@@ -949,7 +949,7 @@ namespace Ryujinx.Ava.Systems
|
||||
{
|
||||
List<AudioBackend> availableBackends =
|
||||
[
|
||||
AudioBackend.SDL2,
|
||||
AudioBackend.SDL3,
|
||||
AudioBackend.SoundIo,
|
||||
AudioBackend.OpenAl,
|
||||
AudioBackend.Dummy
|
||||
@@ -988,7 +988,7 @@ namespace Ryujinx.Ava.Systems
|
||||
|
||||
deviceDriver = currentBackend switch
|
||||
{
|
||||
AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend),
|
||||
AudioBackend.SDL3 => InitializeAudioBackend<SDL3HardwareDeviceDriver>(AudioBackend.SDL3, nextBackend),
|
||||
AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend),
|
||||
AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend),
|
||||
_ => new DummyHardwareDeviceDriver(),
|
||||
|
||||
@@ -9,6 +9,6 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
Dummy,
|
||||
OpenAl,
|
||||
SoundIo,
|
||||
SDL2,
|
||||
SDL3,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
System.EnableInternetAccess.Value = false;
|
||||
System.EnableFsIntegrityChecks.Value = true;
|
||||
System.FsGlobalAccessLogMode.Value = 0;
|
||||
System.AudioBackend.Value = AudioBackend.SDL2;
|
||||
System.AudioBackend.Value = AudioBackend.SDL3;
|
||||
System.AudioVolume.Value = 1;
|
||||
System.MemoryManagerMode.Value = MemoryManagerMode.HostMappedUnsafe;
|
||||
System.DramSize.Value = MemoryConfiguration.MemoryConfiguration4GiB;
|
||||
|
||||
@@ -205,7 +205,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||
{
|
||||
Id = Id,
|
||||
Name = Name,
|
||||
Backend = InputBackendType.GamepadSDL2,
|
||||
Backend = InputBackendType.GamepadSDL3,
|
||||
PlayerIndex = PlayerIndex,
|
||||
ControllerType = ControllerType,
|
||||
LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId>
|
||||
|
||||
@@ -714,7 +714,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
config = new StandardControllerInputConfig
|
||||
{
|
||||
Version = InputConfig.CurrentVersion,
|
||||
Backend = InputBackendType.GamepadSDL2,
|
||||
Backend = InputBackendType.GamepadSDL3,
|
||||
Id = id,
|
||||
Name = name,
|
||||
ControllerType = ControllerType.ProController,
|
||||
|
||||
@@ -6,7 +6,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
using Ryujinx.Audio.Backends.SDL3;
|
||||
using Ryujinx.Audio.Backends.SoundIo;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
@@ -269,7 +269,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool EnableDebug { get; set; }
|
||||
public bool IsOpenAlEnabled { get; set; }
|
||||
public bool IsSoundIoEnabled { get; set; }
|
||||
public bool IsSDL2Enabled { get; set; }
|
||||
public bool IsSDL3Enabled { get; set; }
|
||||
public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
|
||||
public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr;
|
||||
|
||||
@@ -512,13 +512,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported;
|
||||
IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported;
|
||||
IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported;
|
||||
IsSDL3Enabled = SDL3HardwareDeviceDriver.IsSupported;
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
OnPropertyChanged(nameof(IsOpenAlEnabled));
|
||||
OnPropertyChanged(nameof(IsSoundIoEnabled));
|
||||
OnPropertyChanged(nameof(IsSDL2Enabled));
|
||||
OnPropertyChanged(nameof(IsSDL3Enabled));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
IsEnabled="{Binding IsSoundIoEnabled}"
|
||||
Content="{ext:Locale SettingsTabSystemAudioBackendSoundIO}" />
|
||||
<ComboBoxItem
|
||||
IsEnabled="{Binding IsSDL2Enabled}"
|
||||
Content="{ext:Locale SettingsTabSystemAudioBackendSDL2}" />
|
||||
IsEnabled="{Binding IsSDL3Enabled}"
|
||||
Content="{ext:Locale SettingsTabSystemAudioBackendSDL3}" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||
|
||||
@@ -29,7 +29,7 @@ using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -105,7 +105,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
|
||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL3GamepadDriver());
|
||||
|
||||
_ = this.GetObservable(IsActiveProperty).Subscribe(it => ViewModel.IsActive = it);
|
||||
this.ScalingChanged += OnScalingChanged;
|
||||
|
||||
Reference in New Issue
Block a user