forked from Ryubing/Ryujinx
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
frontend: Add a SDL2 headless window (#2310)
- Loading branch information
Mary
authored
Jul 6, 2021
1 parent
d125fce
commit 31cbd09
Showing
13 changed files
with
1,615 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
using OpenTK; | ||
using OpenTK.Graphics.OpenGL; | ||
using Ryujinx.Common.Configuration; | ||
using Ryujinx.Common.Logging; | ||
using Ryujinx.Graphics.OpenGL; | ||
using Ryujinx.Input.HLE; | ||
using System; | ||
|
||
using static SDL2.SDL; | ||
|
||
namespace Ryujinx.Headless.SDL2.OpenGL | ||
{ | ||
class OpenGLWindow : WindowBase | ||
{ | ||
private static void SetupOpenGLAttributes(bool sharedContext, GraphicsDebugLevel debugLevel) | ||
{ | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3); | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 3); | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_FLAGS, debugLevel != GraphicsDebugLevel.None ? (int)SDL_GLcontext.SDL_GL_CONTEXT_DEBUG_FLAG : 0); | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, sharedContext ? 1 : 0); | ||
|
||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ACCELERATED_VISUAL, 1); | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_RED_SIZE, 8); | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_GREEN_SIZE, 8); | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_BLUE_SIZE, 8); | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ALPHA_SIZE, 8); | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DEPTH_SIZE, 16); | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STENCIL_SIZE, 0); | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1); | ||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STEREO, 0); | ||
} | ||
|
||
private class OpenToolkitBindingsContext : IBindingsContext | ||
{ | ||
public IntPtr GetProcAddress(string procName) | ||
{ | ||
return SDL_GL_GetProcAddress(procName); | ||
} | ||
} | ||
|
||
private class SDL2OpenGLContext : IOpenGLContext | ||
{ | ||
private IntPtr _context; | ||
private IntPtr _window; | ||
private bool _shouldDisposeWindow; | ||
|
||
public SDL2OpenGLContext(IntPtr context, IntPtr window, bool shouldDisposeWindow = true) | ||
{ | ||
_context = context; | ||
_window = window; | ||
_shouldDisposeWindow = shouldDisposeWindow; | ||
} | ||
|
||
public static SDL2OpenGLContext CreateBackgroundContext(SDL2OpenGLContext sharedContext) | ||
{ | ||
sharedContext.MakeCurrent(); | ||
|
||
// Ensure we share our contexts. | ||
SetupOpenGLAttributes(true, GraphicsDebugLevel.None); | ||
IntPtr windowHandle = SDL_CreateWindow("Ryujinx background context window", 0, 0, 1, 1, SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN); | ||
IntPtr context = SDL_GL_CreateContext(windowHandle); | ||
|
||
GL.LoadBindings(new OpenToolkitBindingsContext()); | ||
|
||
SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0); | ||
|
||
SDL_GL_MakeCurrent(windowHandle, IntPtr.Zero); | ||
|
||
return new SDL2OpenGLContext(context, windowHandle); | ||
} | ||
|
||
public void MakeCurrent() | ||
{ | ||
if (SDL_GL_GetCurrentContext() == _context || SDL_GL_GetCurrentWindow() == _window) | ||
{ | ||
return; | ||
} | ||
|
||
int res = SDL_GL_MakeCurrent(_window, _context); | ||
|
||
if (res != 0) | ||
{ | ||
string errorMessage = $"SDL_GL_CreateContext failed with error \"{SDL_GetError()}\""; | ||
|
||
Logger.Error?.Print(LogClass.Application, errorMessage); | ||
|
||
throw new Exception(errorMessage); | ||
} | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
SDL_GL_DeleteContext(_context); | ||
|
||
if (_shouldDisposeWindow) | ||
{ | ||
SDL_DestroyWindow(_window); | ||
} | ||
} | ||
} | ||
|
||
private GraphicsDebugLevel _glLogLevel; | ||
private SDL2OpenGLContext _openGLContext; | ||
|
||
public OpenGLWindow(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) : base(inputManager, glLogLevel, aspectRatio, enableMouse) | ||
{ | ||
_glLogLevel = glLogLevel; | ||
} | ||
|
||
protected override string GetGpuVendorName() | ||
{ | ||
return ((Renderer)Renderer).GpuVendor; | ||
} | ||
|
||
public override SDL_WindowFlags GetWindowFlags() => SDL_WindowFlags.SDL_WINDOW_OPENGL; | ||
|
||
protected override void InitializeRenderer() | ||
{ | ||
// Ensure to not share this context with other contexts before this point. | ||
SetupOpenGLAttributes(false, _glLogLevel); | ||
IntPtr context = SDL_GL_CreateContext(WindowHandle); | ||
SDL_GL_SetSwapInterval(1); | ||
|
||
if (context == IntPtr.Zero) | ||
{ | ||
string errorMessage = $"SDL_GL_CreateContext failed with error \"{SDL_GetError()}\""; | ||
|
||
Logger.Error?.Print(LogClass.Application, errorMessage); | ||
|
||
throw new Exception(errorMessage); | ||
} | ||
|
||
// NOTE: The window handle needs to be disposed by the thread that created it and is handled separately. | ||
_openGLContext = new SDL2OpenGLContext(context, WindowHandle, false); | ||
|
||
// First take exclusivity on the OpenGL context. | ||
((Renderer)Renderer).InitializeBackgroundContext(SDL2OpenGLContext.CreateBackgroundContext(_openGLContext)); | ||
|
||
_openGLContext.MakeCurrent(); | ||
|
||
GL.ClearColor(0, 0, 0, 1.0f); | ||
GL.Clear(ClearBufferMask.ColorBufferBit); | ||
SwapBuffers(); | ||
|
||
Renderer?.Window.SetSize(DefaultWidth, DefaultHeight); | ||
MouseDriver.SetClientSize(DefaultWidth, DefaultHeight); | ||
} | ||
|
||
protected override void FinalizeRenderer() | ||
{ | ||
// Try to bind the OpenGL context before calling the gpu disposal. | ||
_openGLContext.MakeCurrent(); | ||
|
||
Device.DisposeGpu(); | ||
|
||
// Unbind context and destroy everything | ||
SDL_GL_MakeCurrent(WindowHandle, IntPtr.Zero); | ||
_openGLContext.Dispose(); | ||
} | ||
|
||
protected override void SwapBuffers() | ||
{ | ||
SDL_GL_SwapWindow(WindowHandle); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
using CommandLine; | ||
using Ryujinx.Common.Configuration; | ||
using Ryujinx.HLE.HOS.SystemState; | ||
|
||
namespace Ryujinx.Headless.SDL2 | ||
{ | ||
public class Options | ||
{ | ||
// Input | ||
|
||
[Option("input-profile-1", Required = false, HelpText = "Set the input profile in use for Player 1.")] | ||
public string InputProfile1Name { get; set; } | ||
|
||
[Option("input-profile-2", Required = false, HelpText = "Set the input profile in use for Player 2.")] | ||
public string InputProfile2Name { get; set; } | ||
|
||
[Option("input-profile-3", Required = false, HelpText = "Set the input profile in use for Player 3.")] | ||
public string InputProfile3Name { get; set; } | ||
|
||
[Option("input-profile-4", Required = false, HelpText = "Set the input profile in use for Player 4.")] | ||
public string InputProfile4Name { get; set; } | ||
|
||
[Option("input-profile-5", Required = false, HelpText = "Set the input profile in use for Player 5.")] | ||
public string InputProfile5Name { get; set; } | ||
|
||
[Option("input-profile-6", Required = false, HelpText = "Set the input profile in use for Player 5.")] | ||
public string InputProfile6Name { get; set; } | ||
|
||
[Option("input-profile-7", Required = false, HelpText = "Set the input profile in use for Player 7.")] | ||
public string InputProfile7Name { get; set; } | ||
|
||
[Option("input-profile-8", Required = false, HelpText = "Set the input profile in use for Player 8.")] | ||
public string InputProfile8Name { get; set; } | ||
|
||
[Option("input-profile-handheld", Required = false, HelpText = "Set the input profile in use for the Handheld Player.")] | ||
public string InputProfileHandheldName { get; set; } | ||
|
||
[Option("input-id-1", Required = false, HelpText = "Set the input id in use for Player 1.")] | ||
public string InputId1 { get; set; } | ||
|
||
[Option("input-id-2", Required = false, HelpText = "Set the input id in use for Player 2.")] | ||
public string InputId2 { get; set; } | ||
|
||
[Option("input-id-3", Required = false, HelpText = "Set the input id in use for Player 3.")] | ||
public string InputId3 { get; set; } | ||
|
||
[Option("input-id-4", Required = false, HelpText = "Set the input id in use for Player 4.")] | ||
public string InputId4 { get; set; } | ||
|
||
[Option("input-id-5", Required = false, HelpText = "Set the input id in use for Player 5.")] | ||
public string InputId5 { get; set; } | ||
|
||
[Option("input-id-6", Required = false, HelpText = "Set the input id in use for Player 6.")] | ||
public string InputId6 { get; set; } | ||
|
||
[Option("input-id-7", Required = false, HelpText = "Set the input id in use for Player 7.")] | ||
public string InputId7 { get; set; } | ||
|
||
[Option("input-id-8", Required = false, HelpText = "Set the input id in use for Player 8.")] | ||
public string InputId8 { get; set; } | ||
|
||
[Option("input-id-handheld", Required = false, HelpText = "Set the input id in use for the Handheld Player.")] | ||
public string InputIdHandheld { get; set; } | ||
|
||
[Option("enable-keyboard", Required = false, Default = false, HelpText = "Enable or disable keyboard support (Independent from controllers binding).")] | ||
public bool? EnableKeyboard { get; set; } | ||
|
||
[Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")] | ||
public bool? EnableMouse { get; set; } | ||
|
||
[Option("list-input-profiles", Required = false, HelpText = "List inputs profiles.")] | ||
public bool? ListInputProfiles { get; set; } | ||
|
||
[Option("list-inputs-ids", Required = false, HelpText = "List inputs ids.")] | ||
public bool ListInputIds { get; set; } | ||
|
||
// System | ||
|
||
[Option("enable-ptc", Required = false, Default = true, HelpText = "Enables profiled translation cache persistency.")] | ||
public bool? EnablePtc { get; set; } | ||
|
||
[Option("enable-fs-integrity-checks", Required = false, Default = true, HelpText = "Enables integrity checks on Game content files.")] | ||
public bool? EnableFsIntegrityChecks { get; set; } | ||
|
||
[Option("fs-global-access-log-mode", Required = false, Default = 0, HelpText = "Enables FS access log output to the console.")] | ||
public int FsGlobalAccessLogMode { get; set; } | ||
|
||
[Option("enable-vsync", Required = false, Default = true, HelpText = "Enables Vertical Sync.")] | ||
public bool? EnableVsync { get; set; } | ||
|
||
[Option("enable-shader-cache", Required = false, Default = true, HelpText = "Enables Shader cache.")] | ||
public bool? EnableShaderCache { get; set; } | ||
|
||
[Option("enable-docked-mode", Required = false, Default = true, HelpText = "Enables Docked Mode.")] | ||
public bool? EnableDockedMode { get; set; } | ||
|
||
[Option("system-language", Required = false, Default = SystemLanguage.AmericanEnglish, HelpText = "Change System Language.")] | ||
public SystemLanguage SystemLanguage { get; set; } | ||
|
||
[Option("system-language", Required = false, Default = RegionCode.USA, HelpText = "Change System Region.")] | ||
public RegionCode SystemRegion { get; set; } | ||
|
||
[Option("system-timezone", Required = false, Default = "UTC", HelpText = "Change System TimeZone.")] | ||
public string SystemTimeZone { get; set; } | ||
|
||
[Option("system-time-offset", Required = false, Default = 0, HelpText = "Change System Time Offset in seconds.")] | ||
public long SystemTimeOffset { get; set; } | ||
|
||
[Option("memory-manager-mode", Required = false, Default = MemoryManagerMode.HostMappedUnsafe, HelpText = "The selected memory manager mode.")] | ||
public MemoryManagerMode MemoryManagerMode { get; set; } | ||
|
||
// Logging | ||
|
||
[Option("enable-file-logging", Required = false, Default = false, HelpText = "Enables logging to a file on disk.")] | ||
public bool? EnableFileLog { get; set; } | ||
|
||
[Option("enable-debug-logs", Required = false, Default = false, HelpText = "Enables printing debug log messages.")] | ||
public bool? LoggingEnableDebug { get; set; } | ||
|
||
[Option("enable-stub-logs", Required = false, Default = true, HelpText = "Enables printing stub log messages.")] | ||
public bool? LoggingEnableStub { get; set; } | ||
|
||
[Option("enable-info-logs", Required = false, Default = true, HelpText = "Enables printing info log messages.")] | ||
public bool? LoggingEnableInfo { get; set; } | ||
|
||
[Option("enable-warning-logs", Required = false, Default = true, HelpText = "Enables printing warning log messages.")] | ||
public bool? LoggingEnableWarning { get; set; } | ||
|
||
[Option("enable-warning-logs", Required = false, Default = true, HelpText = "Enables printing error log messages.")] | ||
public bool? LoggingEnableError { get; set; } | ||
|
||
[Option("enable-guest-logs", Required = false, Default = true, HelpText = "Enables printing guest log messages.")] | ||
public bool? LoggingEnableGuest { get; set; } | ||
|
||
[Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")] | ||
public bool? LoggingEnableFsAccessLog { get; set; } | ||
|
||
[Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")] | ||
public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; } | ||
|
||
// Graphics | ||
|
||
[Option("resolution-scale", Required = false, Default = 1, HelpText = "Resolution Scale. A floating point scale applied to applicable render targets.")] | ||
public float ResScale { get; set; } | ||
|
||
[Option("max-anisotropy", Required = false, Default = -1, HelpText = "Max Anisotropy. Values range from 0 - 16. Set to -1 to let the game decide.")] | ||
public float MaxAnisotropy { get; set; } | ||
|
||
[Option("aspect-ratio", Required = false, Default = AspectRatio.Fixed16x9, HelpText = "Aspect Ratio applied to the renderer window.")] | ||
public AspectRatio AspectRatio { get; set; } | ||
|
||
[Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")] | ||
public string GraphicsShadersDumpPath { get; set; } | ||
|
||
// Hacks | ||
|
||
[Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GB to 6GB.")] | ||
public bool? ExpandRam { get; set; } | ||
|
||
[Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")] | ||
public bool? IgnoreMissingServices { get; set; } | ||
|
||
// Values | ||
|
||
[Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)] | ||
public string InputPath { get; set; } | ||
} | ||
} |
Oops, something went wrong.