aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Headless.SDL2
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Headless.SDL2')
-rw-r--r--Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs13
-rw-r--r--Ryujinx.Headless.SDL2/Options.cs9
-rw-r--r--Ryujinx.Headless.SDL2/Program.cs53
-rw-r--r--Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj2
-rw-r--r--Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs83
-rw-r--r--Ryujinx.Headless.SDL2/WindowBase.cs19
6 files changed, 161 insertions, 18 deletions
diff --git a/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs b/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs
index a6995143..aa2e86d9 100644
--- a/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs
+++ b/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs
@@ -108,14 +108,9 @@ namespace Ryujinx.Headless.SDL2.OpenGL
_glLogLevel = glLogLevel;
}
- protected override string GetGpuVendorName()
- {
- return ((Renderer)Renderer).GpuVendor;
- }
-
public override SDL_WindowFlags GetWindowFlags() => SDL_WindowFlags.SDL_WINDOW_OPENGL;
- protected override void InitializeRenderer()
+ protected override void InitializeWindowRenderer()
{
// Ensure to not share this context with other contexts before this point.
SetupOpenGLAttributes(false, _glLogLevel);
@@ -135,7 +130,7 @@ namespace Ryujinx.Headless.SDL2.OpenGL
_openGLContext = new SDL2OpenGLContext(context, WindowHandle, false);
// First take exclusivity on the OpenGL context.
- ((Renderer)Renderer).InitializeBackgroundContext(SDL2OpenGLContext.CreateBackgroundContext(_openGLContext));
+ ((OpenGLRenderer)Renderer).InitializeBackgroundContext(SDL2OpenGLContext.CreateBackgroundContext(_openGLContext));
_openGLContext.MakeCurrent();
@@ -147,7 +142,9 @@ namespace Ryujinx.Headless.SDL2.OpenGL
MouseDriver.SetClientSize(DefaultWidth, DefaultHeight);
}
- protected override void FinalizeRenderer()
+ protected override void InitializeRenderer() { }
+
+ protected override void FinalizeWindowRenderer()
{
// Try to bind the OpenGL context before calling the gpu disposal.
_openGLContext.MakeCurrent();
diff --git a/Ryujinx.Headless.SDL2/Options.cs b/Ryujinx.Headless.SDL2/Options.cs
index 4c9b83c4..95694179 100644
--- a/Ryujinx.Headless.SDL2/Options.cs
+++ b/Ryujinx.Headless.SDL2/Options.cs
@@ -94,6 +94,9 @@ namespace Ryujinx.Headless.SDL2
[Option("enable-shader-cache", Required = false, Default = true, HelpText = "Enables Shader cache.")]
public bool? EnableShaderCache { get; set; }
+ [Option("enable-texture-recompression", Required = false, Default = false, HelpText = "Enables Texture recompression.")]
+ public bool? EnableTextureRecompression { get; set; }
+
[Option("enable-docked-mode", Required = false, Default = true, HelpText = "Enables Docked Mode.")]
public bool? EnableDockedMode { get; set; }
@@ -164,6 +167,12 @@ namespace Ryujinx.Headless.SDL2
[Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")]
public string GraphicsShadersDumpPath { get; set; }
+ [Option("graphics-backend", Required = false, Default = GraphicsBackend.OpenGl, HelpText = "Change Graphics Backend to use.")]
+ public GraphicsBackend GraphicsBackend { get; set; }
+
+ [Option("preferred-gpu-vendor", Required = false, Default = "", HelpText = "When using the Vulkan backend, prefer using the GPU from the specified vendor.")]
+ public string PreferredGpuVendor { get; set; }
+
// Hacks
[Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GB to 6GB.")]
diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs
index 1d64a8c6..5c24095e 100644
--- a/Ryujinx.Headless.SDL2/Program.cs
+++ b/Ryujinx.Headless.SDL2/Program.cs
@@ -17,7 +17,9 @@ using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.OpenGL;
+using Ryujinx.Graphics.Vulkan;
using Ryujinx.Headless.SDL2.OpenGL;
+using Ryujinx.Headless.SDL2.Vulkan;
using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
@@ -25,6 +27,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Input;
using Ryujinx.Input.HLE;
using Ryujinx.Input.SDL2;
+using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.IO;
@@ -404,6 +407,7 @@ namespace Ryujinx.Headless.SDL2
// Setup graphics configuration
GraphicsConfig.EnableShaderCache = (bool)option.EnableShaderCache;
+ GraphicsConfig.EnableTextureRecompression = (bool)option.EnableTextureRecompression;
GraphicsConfig.ResScale = option.ResScale;
GraphicsConfig.MaxAnisotropy = option.MaxAnisotropy;
GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
@@ -449,10 +453,47 @@ namespace Ryujinx.Headless.SDL2
Logger.Info?.Print(LogClass.Application, label);
}
- private static Switch InitializeEmulationContext(WindowBase window, Options options)
+ private static WindowBase CreateWindow(Options options)
{
- IRenderer renderer = new Renderer();
+ return options.GraphicsBackend == GraphicsBackend.Vulkan
+ ? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, (bool)options.EnableMouse)
+ : new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, (bool)options.EnableMouse);
+ }
+
+ private static IRenderer CreateRenderer(Options options, WindowBase window)
+ {
+ if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow)
+ {
+ string preferredGpuId = string.Empty;
+
+ if (!string.IsNullOrEmpty(options.PreferredGpuVendor))
+ {
+ string preferredGpuVendor = options.PreferredGpuVendor.ToLowerInvariant();
+ var devices = VulkanRenderer.GetPhysicalDevices();
+ foreach (var device in devices)
+ {
+ if (device.Vendor.ToLowerInvariant() == preferredGpuVendor)
+ {
+ preferredGpuId = device.Id;
+ break;
+ }
+ }
+ }
+
+ return new VulkanRenderer(
+ (instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))),
+ vulkanWindow.GetRequiredInstanceExtensions,
+ preferredGpuId);
+ }
+ else
+ {
+ return new OpenGLRenderer();
+ }
+ }
+
+ private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options)
+ {
BackendThreading threadingMode = options.BackendThreading;
bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
@@ -521,8 +562,12 @@ namespace Ryujinx.Headless.SDL2
Logger.RestartTime();
- _window = new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, (bool)options.EnableMouse);
- _emulationContext = InitializeEmulationContext(_window, options);
+ WindowBase window = CreateWindow(options);
+ IRenderer renderer = CreateRenderer(options, window);
+
+ _window = window;
+
+ _emulationContext = InitializeEmulationContext(window, renderer, options);
SetupProgressHandler();
diff --git a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
index 2e9fd1a0..929b4131 100644
--- a/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
+++ b/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj
@@ -10,10 +10,12 @@
</PropertyGroup>
<ItemGroup>
+ <PackageReference Include="OpenTK.Core" Version="4.7.2" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
</ItemGroup>
<ItemGroup>
+ <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.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
diff --git a/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs b/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs
new file mode 100644
index 00000000..d8eafba4
--- /dev/null
+++ b/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs
@@ -0,0 +1,83 @@
+using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Logging;
+using Ryujinx.Input.HLE;
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using static SDL2.SDL;
+
+namespace Ryujinx.Headless.SDL2.Vulkan
+{
+ class VulkanWindow : WindowBase
+ {
+ private GraphicsDebugLevel _glLogLevel;
+
+ public VulkanWindow(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) : base(inputManager, glLogLevel, aspectRatio, enableMouse)
+ {
+ _glLogLevel = glLogLevel;
+ }
+
+ public override SDL_WindowFlags GetWindowFlags() => SDL_WindowFlags.SDL_WINDOW_VULKAN;
+
+ protected override void InitializeWindowRenderer() { }
+
+ protected override void InitializeRenderer()
+ {
+ Renderer?.Window.SetSize(DefaultWidth, DefaultHeight);
+ MouseDriver.SetClientSize(DefaultWidth, DefaultHeight);
+ }
+
+ public unsafe IntPtr CreateWindowSurface(IntPtr instance)
+ {
+ if (SDL_Vulkan_CreateSurface(WindowHandle, instance, out ulong surfaceHandle) == SDL_bool.SDL_FALSE)
+ {
+ string errorMessage = $"SDL_Vulkan_CreateSurface failed with error \"{SDL_GetError()}\"";
+
+ Logger.Error?.Print(LogClass.Application, errorMessage);
+
+ throw new Exception(errorMessage);
+ }
+
+ return (IntPtr)surfaceHandle;
+ }
+
+ // TODO: Fix this in SDL2-CS.
+ [DllImport("SDL2", EntryPoint = "SDL_Vulkan_GetInstanceExtensions", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SDL_bool SDL_Vulkan_GetInstanceExtensions_Workaround(IntPtr window, out uint count, IntPtr names);
+
+ public unsafe string[] GetRequiredInstanceExtensions()
+ {
+ if (SDL_Vulkan_GetInstanceExtensions_Workaround(WindowHandle, out uint extensionsCount, IntPtr.Zero) == SDL_bool.SDL_TRUE)
+ {
+ IntPtr[] rawExtensions = new IntPtr[(int)extensionsCount];
+ string[] extensions = new string[(int)extensionsCount];
+
+ fixed (IntPtr* rawExtensionsPtr = rawExtensions)
+ {
+ if (SDL_Vulkan_GetInstanceExtensions_Workaround(WindowHandle, out extensionsCount, (IntPtr)rawExtensionsPtr) == SDL_bool.SDL_TRUE)
+ {
+ for (int i = 0; i < extensions.Length; i++)
+ {
+ extensions[i] = Marshal.PtrToStringUTF8(rawExtensions[i]);
+ }
+
+ return extensions;
+ }
+ }
+ }
+
+ string errorMessage = $"SDL_Vulkan_GetInstanceExtensions failed with error \"{SDL_GetError()}\"";
+
+ Logger.Error?.Print(LogClass.Application, errorMessage);
+
+ throw new Exception(errorMessage);
+ }
+
+ protected override void FinalizeWindowRenderer()
+ {
+ Device.DisposeGpu();
+ }
+
+ protected override void SwapBuffers(object texture) { }
+ }
+}
diff --git a/Ryujinx.Headless.SDL2/WindowBase.cs b/Ryujinx.Headless.SDL2/WindowBase.cs
index 58aa8d07..cc0986a0 100644
--- a/Ryujinx.Headless.SDL2/WindowBase.cs
+++ b/Ryujinx.Headless.SDL2/WindowBase.cs
@@ -151,22 +151,29 @@ namespace Ryujinx.Headless.SDL2
}
}
+ protected abstract void InitializeWindowRenderer();
+
protected abstract void InitializeRenderer();
- protected abstract void FinalizeRenderer();
+ protected abstract void FinalizeWindowRenderer();
protected abstract void SwapBuffers(object image);
- protected abstract string GetGpuVendorName();
-
public abstract SDL_WindowFlags GetWindowFlags();
+ private string GetGpuVendorName()
+ {
+ return Renderer.GetHardwareInfo().GpuVendor;
+ }
+
public void Render()
{
- InitializeRenderer();
+ InitializeWindowRenderer();
Device.Gpu.Renderer.Initialize(_glLogLevel);
+ InitializeRenderer();
+
_gpuVendorName = GetGpuVendorName();
Device.Gpu.Renderer.RunLoop(() =>
@@ -220,7 +227,7 @@ namespace Ryujinx.Headless.SDL2
}
});
- FinalizeRenderer();
+ FinalizeWindowRenderer();
}
public void Exit()
@@ -323,7 +330,7 @@ namespace Ryujinx.Headless.SDL2
renderLoopThread.Start();
Thread nvStutterWorkaround = null;
- if (Renderer is Graphics.OpenGL.Renderer)
+ if (Renderer is Graphics.OpenGL.OpenGLRenderer)
{
nvStutterWorkaround = new Thread(NVStutterWorkaround)
{