|
|
@@ -1,13 +1,9 @@
|
|
|
using CommandLine;
|
|
|
using Gommon;
|
|
|
-using LibHac.Tools.FsSystem;
|
|
|
-using Ryujinx.Audio.Backends.SDL2;
|
|
|
+using Ryujinx.Ava;
|
|
|
using Ryujinx.Common;
|
|
|
using Ryujinx.Common.Configuration;
|
|
|
using Ryujinx.Common.Configuration.Hid;
|
|
|
-using Ryujinx.Common.Configuration.Hid.Controller;
|
|
|
-using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
|
|
-using Ryujinx.Common.Configuration.Hid.Keyboard;
|
|
|
using Ryujinx.Common.GraphicsDriver;
|
|
|
using Ryujinx.Common.Logging;
|
|
|
using Ryujinx.Common.Logging.Targets;
|
|
|
@@ -15,16 +11,12 @@ using Ryujinx.Common.SystemInterop;
|
|
|
using Ryujinx.Common.Utilities;
|
|
|
using Ryujinx.Cpu;
|
|
|
using Ryujinx.Graphics.GAL;
|
|
|
-using Ryujinx.Graphics.GAL.Multithreading;
|
|
|
using Ryujinx.Graphics.Gpu;
|
|
|
using Ryujinx.Graphics.Gpu.Shader;
|
|
|
using Ryujinx.Graphics.Metal;
|
|
|
using Ryujinx.Graphics.OpenGL;
|
|
|
using Ryujinx.Graphics.Vulkan;
|
|
|
using Ryujinx.Graphics.Vulkan.MoltenVK;
|
|
|
-using Ryujinx.Headless.SDL2.Metal;
|
|
|
-using Ryujinx.Headless.SDL2.OpenGL;
|
|
|
-using Ryujinx.Headless.SDL2.Vulkan;
|
|
|
using Ryujinx.HLE;
|
|
|
using Ryujinx.HLE.FileSystem;
|
|
|
using Ryujinx.HLE.HOS;
|
|
|
@@ -33,22 +25,16 @@ using Ryujinx.Input;
|
|
|
using Ryujinx.Input.HLE;
|
|
|
using Ryujinx.Input.SDL2;
|
|
|
using Ryujinx.SDL2.Common;
|
|
|
-using Silk.NET.Vulkan;
|
|
|
+using Ryujinx.UI.Common.Configuration;
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.IO;
|
|
|
-using System.Text.Json;
|
|
|
using System.Threading;
|
|
|
-using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
|
|
|
-using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
|
|
|
-using Key = Ryujinx.Common.Configuration.Hid.Key;
|
|
|
|
|
|
-namespace Ryujinx.Headless.SDL2
|
|
|
+namespace Ryujinx.Headless
|
|
|
{
|
|
|
- class Program
|
|
|
+ public partial class HeadlessRyujinx
|
|
|
{
|
|
|
- public static string Version { get; private set; }
|
|
|
-
|
|
|
private static VirtualFileSystem _virtualFileSystem;
|
|
|
private static ContentManager _contentManager;
|
|
|
private static AccountManager _accountManager;
|
|
|
@@ -58,20 +44,18 @@ namespace Ryujinx.Headless.SDL2
|
|
|
private static Switch _emulationContext;
|
|
|
private static WindowBase _window;
|
|
|
private static WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution;
|
|
|
- private static List<InputConfig> _inputConfiguration;
|
|
|
+ private static List<InputConfig> _inputConfiguration = [];
|
|
|
private static bool _enableKeyboard;
|
|
|
private static bool _enableMouse;
|
|
|
|
|
|
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
|
|
|
|
|
- static void Main(string[] args)
|
|
|
+ public static void Entrypoint(string[] args)
|
|
|
{
|
|
|
- Version = ReleaseInformation.Version;
|
|
|
-
|
|
|
// Make process DPI aware for proper window sizing on high-res screens.
|
|
|
ForceDpiAware.Windows();
|
|
|
|
|
|
- Console.Title = $"Ryujinx Console {Version} (Headless SDL2)";
|
|
|
+ Console.Title = $"Ryujinx Console {Program.Version} (Headless)";
|
|
|
|
|
|
if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux())
|
|
|
{
|
|
|
@@ -99,7 +83,7 @@ namespace Ryujinx.Headless.SDL2
|
|
|
}
|
|
|
|
|
|
Parser.Default.ParseArguments<Options>(args)
|
|
|
- .WithParsed(Load)
|
|
|
+ .WithParsed(options => Load(args, options))
|
|
|
.WithNotParsed(errors =>
|
|
|
{
|
|
|
Logger.Error?.PrintMsg(LogClass.Application, "Error parsing command-line arguments:");
|
|
|
@@ -107,239 +91,81 @@ namespace Ryujinx.Headless.SDL2
|
|
|
errors.ForEach(err => Logger.Error?.PrintMsg(LogClass.Application, $" - {err.Tag}"));
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
- private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
|
|
|
+
|
|
|
+ public static void ReloadConfig(string customConfigPath = null)
|
|
|
{
|
|
|
- if (inputId == null)
|
|
|
- {
|
|
|
- if (index == PlayerIndex.Player1)
|
|
|
- {
|
|
|
- Logger.Info?.Print(LogClass.Application, $"{index} not configured, defaulting to default keyboard.");
|
|
|
-
|
|
|
- // Default to keyboard
|
|
|
- inputId = "0";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Logger.Info?.Print(LogClass.Application, $"{index} not configured");
|
|
|
+ string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
|
|
+ string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
|
|
|
|
|
|
- return null;
|
|
|
- }
|
|
|
+ string configurationPath = null;
|
|
|
+
|
|
|
+ // Now load the configuration as the other subsystems are now registered
|
|
|
+ if (customConfigPath != null && File.Exists(customConfigPath))
|
|
|
+ {
|
|
|
+ configurationPath = customConfigPath;
|
|
|
+ }
|
|
|
+ else if (File.Exists(localConfigurationPath))
|
|
|
+ {
|
|
|
+ configurationPath = localConfigurationPath;
|
|
|
}
|
|
|
-
|
|
|
- IGamepad gamepad = _inputManager.KeyboardDriver.GetGamepad(inputId);
|
|
|
-
|
|
|
- bool isKeyboard = true;
|
|
|
-
|
|
|
- if (gamepad == null)
|
|
|
+ else if (File.Exists(appDataConfigurationPath))
|
|
|
{
|
|
|
- gamepad = _inputManager.GamepadDriver.GetGamepad(inputId);
|
|
|
- isKeyboard = false;
|
|
|
-
|
|
|
- if (gamepad == null)
|
|
|
- {
|
|
|
- Logger.Error?.Print(LogClass.Application, $"{index} gamepad not found (\"{inputId}\")");
|
|
|
-
|
|
|
- return null;
|
|
|
- }
|
|
|
+ configurationPath = appDataConfigurationPath;
|
|
|
}
|
|
|
|
|
|
- string gamepadName = gamepad.Name;
|
|
|
-
|
|
|
- gamepad.Dispose();
|
|
|
-
|
|
|
- InputConfig config;
|
|
|
-
|
|
|
- if (inputProfileName == null || inputProfileName.Equals("default"))
|
|
|
+ if (configurationPath == null)
|
|
|
{
|
|
|
- if (isKeyboard)
|
|
|
- {
|
|
|
- config = new StandardKeyboardInputConfig
|
|
|
- {
|
|
|
- Version = InputConfig.CurrentVersion,
|
|
|
- Backend = InputBackendType.WindowKeyboard,
|
|
|
- Id = null,
|
|
|
- ControllerType = ControllerType.JoyconPair,
|
|
|
- LeftJoycon = new LeftJoyconCommonConfig<Key>
|
|
|
- {
|
|
|
- DpadUp = Key.Up,
|
|
|
- DpadDown = Key.Down,
|
|
|
- DpadLeft = Key.Left,
|
|
|
- DpadRight = Key.Right,
|
|
|
- ButtonMinus = Key.Minus,
|
|
|
- ButtonL = Key.E,
|
|
|
- ButtonZl = Key.Q,
|
|
|
- ButtonSl = Key.Unbound,
|
|
|
- ButtonSr = Key.Unbound,
|
|
|
- },
|
|
|
-
|
|
|
- LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
|
|
|
- {
|
|
|
- StickUp = Key.W,
|
|
|
- StickDown = Key.S,
|
|
|
- StickLeft = Key.A,
|
|
|
- StickRight = Key.D,
|
|
|
- StickButton = Key.F,
|
|
|
- },
|
|
|
-
|
|
|
- RightJoycon = new RightJoyconCommonConfig<Key>
|
|
|
- {
|
|
|
- ButtonA = Key.Z,
|
|
|
- ButtonB = Key.X,
|
|
|
- ButtonX = Key.C,
|
|
|
- ButtonY = Key.V,
|
|
|
- ButtonPlus = Key.Plus,
|
|
|
- ButtonR = Key.U,
|
|
|
- ButtonZr = Key.O,
|
|
|
- ButtonSl = Key.Unbound,
|
|
|
- ButtonSr = Key.Unbound,
|
|
|
- },
|
|
|
-
|
|
|
- RightJoyconStick = new JoyconConfigKeyboardStick<Key>
|
|
|
- {
|
|
|
- StickUp = Key.I,
|
|
|
- StickDown = Key.K,
|
|
|
- StickLeft = Key.J,
|
|
|
- StickRight = Key.L,
|
|
|
- StickButton = Key.H,
|
|
|
- },
|
|
|
- };
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- bool isNintendoStyle = gamepadName.Contains("Nintendo");
|
|
|
+ // No configuration, we load the default values and save it to disk
|
|
|
+ configurationPath = appDataConfigurationPath;
|
|
|
+ Logger.Notice.Print(LogClass.Application, $"No configuration file found. Saving default configuration to: {configurationPath}");
|
|
|
|
|
|
- config = new StandardControllerInputConfig
|
|
|
- {
|
|
|
- Version = InputConfig.CurrentVersion,
|
|
|
- Backend = InputBackendType.GamepadSDL2,
|
|
|
- Id = null,
|
|
|
- ControllerType = ControllerType.JoyconPair,
|
|
|
- DeadzoneLeft = 0.1f,
|
|
|
- DeadzoneRight = 0.1f,
|
|
|
- RangeLeft = 1.0f,
|
|
|
- RangeRight = 1.0f,
|
|
|
- TriggerThreshold = 0.5f,
|
|
|
- LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId>
|
|
|
- {
|
|
|
- DpadUp = ConfigGamepadInputId.DpadUp,
|
|
|
- DpadDown = ConfigGamepadInputId.DpadDown,
|
|
|
- DpadLeft = ConfigGamepadInputId.DpadLeft,
|
|
|
- DpadRight = ConfigGamepadInputId.DpadRight,
|
|
|
- ButtonMinus = ConfigGamepadInputId.Minus,
|
|
|
- ButtonL = ConfigGamepadInputId.LeftShoulder,
|
|
|
- ButtonZl = ConfigGamepadInputId.LeftTrigger,
|
|
|
- ButtonSl = ConfigGamepadInputId.Unbound,
|
|
|
- ButtonSr = ConfigGamepadInputId.Unbound,
|
|
|
- },
|
|
|
-
|
|
|
- LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
|
|
|
- {
|
|
|
- Joystick = ConfigStickInputId.Left,
|
|
|
- StickButton = ConfigGamepadInputId.LeftStick,
|
|
|
- InvertStickX = false,
|
|
|
- InvertStickY = false,
|
|
|
- Rotate90CW = false,
|
|
|
- },
|
|
|
-
|
|
|
- RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
|
|
- {
|
|
|
- ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B,
|
|
|
- ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A,
|
|
|
- ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y,
|
|
|
- ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X,
|
|
|
- ButtonPlus = ConfigGamepadInputId.Plus,
|
|
|
- ButtonR = ConfigGamepadInputId.RightShoulder,
|
|
|
- ButtonZr = ConfigGamepadInputId.RightTrigger,
|
|
|
- ButtonSl = ConfigGamepadInputId.Unbound,
|
|
|
- ButtonSr = ConfigGamepadInputId.Unbound,
|
|
|
- },
|
|
|
-
|
|
|
- RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
|
|
|
- {
|
|
|
- Joystick = ConfigStickInputId.Right,
|
|
|
- StickButton = ConfigGamepadInputId.RightStick,
|
|
|
- InvertStickX = false,
|
|
|
- InvertStickY = false,
|
|
|
- Rotate90CW = false,
|
|
|
- },
|
|
|
-
|
|
|
- Motion = new StandardMotionConfigController
|
|
|
- {
|
|
|
- MotionBackend = MotionInputBackendType.GamepadDriver,
|
|
|
- EnableMotion = true,
|
|
|
- Sensitivity = 100,
|
|
|
- GyroDeadzone = 1,
|
|
|
- },
|
|
|
- Rumble = new RumbleConfigController
|
|
|
- {
|
|
|
- StrongRumble = 1f,
|
|
|
- WeakRumble = 1f,
|
|
|
- EnableRumble = false,
|
|
|
- },
|
|
|
- };
|
|
|
- }
|
|
|
+ ConfigurationState.Instance.LoadDefault();
|
|
|
+ ConfigurationState.Instance.ToFileFormat().SaveConfig(configurationPath);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- string profileBasePath;
|
|
|
+ Logger.Notice.Print(LogClass.Application, $"Loading configuration from: {configurationPath}");
|
|
|
|
|
|
- if (isKeyboard)
|
|
|
+ if (ConfigurationFileFormat.TryLoad(configurationPath, out ConfigurationFileFormat configurationFileFormat))
|
|
|
{
|
|
|
- profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "keyboard");
|
|
|
+ ConfigurationState.Instance.Load(configurationFileFormat, configurationPath);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- profileBasePath = Path.Combine(AppDataManager.ProfilesDirPath, "controller");
|
|
|
- }
|
|
|
-
|
|
|
- string path = Path.Combine(profileBasePath, inputProfileName + ".json");
|
|
|
-
|
|
|
- if (!File.Exists(path))
|
|
|
- {
|
|
|
- Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" not found for \"{inputId}\"");
|
|
|
-
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- try
|
|
|
- {
|
|
|
- config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig);
|
|
|
- }
|
|
|
- catch (JsonException)
|
|
|
- {
|
|
|
- Logger.Error?.Print(LogClass.Application, $"Input profile \"{inputProfileName}\" parsing failed for \"{inputId}\"");
|
|
|
+ Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location: {configurationPath}");
|
|
|
|
|
|
- return null;
|
|
|
+ ConfigurationState.Instance.LoadDefault();
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- config.Id = inputId;
|
|
|
- config.PlayerIndex = index;
|
|
|
+ static void Load(string[] originalArgs, Options option)
|
|
|
+ {
|
|
|
+ Initialize();
|
|
|
|
|
|
- string inputTypeName = isKeyboard ? "Keyboard" : "Gamepad";
|
|
|
+ bool useLastUsedProfile = false;
|
|
|
|
|
|
- Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} configured with {inputTypeName} \"{config.Id}\"");
|
|
|
+ if (option.InheritConfig)
|
|
|
+ {
|
|
|
+ option.InheritMainConfig(originalArgs, ConfigurationState.Instance, out useLastUsedProfile);
|
|
|
+ }
|
|
|
|
|
|
- // If both stick ranges are 0 (usually indicative of an outdated profile load) then both sticks will be set to 1.0.
|
|
|
- if (config is StandardControllerInputConfig controllerConfig)
|
|
|
+ AppDataManager.Initialize(option.BaseDataDir);
|
|
|
+
|
|
|
+ if (useLastUsedProfile && AccountSaveDataManager.GetLastUsedUser().TryGet(out var profile))
|
|
|
+ option.UserProfile = profile.Name;
|
|
|
+
|
|
|
+ // Check if keys exists.
|
|
|
+ if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
|
|
|
{
|
|
|
- if (controllerConfig.RangeLeft <= 0.0f && controllerConfig.RangeRight <= 0.0f)
|
|
|
+ if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys"))))
|
|
|
{
|
|
|
- controllerConfig.RangeLeft = 1.0f;
|
|
|
- controllerConfig.RangeRight = 1.0f;
|
|
|
-
|
|
|
- Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration");
|
|
|
+ Logger.Error?.Print(LogClass.Application, "Keys not found");
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- return config;
|
|
|
- }
|
|
|
-
|
|
|
- static void Load(Options option)
|
|
|
- {
|
|
|
- AppDataManager.Initialize(option.BaseDataDir);
|
|
|
-
|
|
|
+
|
|
|
+ ReloadConfig();
|
|
|
+
|
|
|
_virtualFileSystem = VirtualFileSystem.CreateInstance();
|
|
|
_libHacHorizonManager = new LibHacHorizonManager();
|
|
|
|
|
|
@@ -354,7 +180,7 @@ namespace Ryujinx.Headless.SDL2
|
|
|
|
|
|
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
|
|
|
|
|
|
- GraphicsConfig.EnableShaderCache = true;
|
|
|
+ GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;
|
|
|
|
|
|
if (OperatingSystem.IsMacOS())
|
|
|
{
|
|
|
@@ -365,15 +191,13 @@ namespace Ryujinx.Headless.SDL2
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- IGamepad gamepad;
|
|
|
-
|
|
|
if (option.ListInputIds)
|
|
|
{
|
|
|
Logger.Info?.Print(LogClass.Application, "Input Ids:");
|
|
|
|
|
|
foreach (string id in _inputManager.KeyboardDriver.GamepadsIds)
|
|
|
{
|
|
|
- gamepad = _inputManager.KeyboardDriver.GetGamepad(id);
|
|
|
+ IGamepad gamepad = _inputManager.KeyboardDriver.GetGamepad(id);
|
|
|
|
|
|
Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")");
|
|
|
|
|
|
@@ -382,7 +206,7 @@ namespace Ryujinx.Headless.SDL2
|
|
|
|
|
|
foreach (string id in _inputManager.GamepadDriver.GamepadsIds)
|
|
|
{
|
|
|
- gamepad = _inputManager.GamepadDriver.GetGamepad(id);
|
|
|
+ IGamepad gamepad = _inputManager.GamepadDriver.GetGamepad(id);
|
|
|
|
|
|
Logger.Info?.Print(LogClass.Application, $"- {id} (\"{gamepad.Name}\")");
|
|
|
|
|
|
@@ -399,7 +223,7 @@ namespace Ryujinx.Headless.SDL2
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- _inputConfiguration = new List<InputConfig>();
|
|
|
+ _inputConfiguration ??= [];
|
|
|
_enableKeyboard = option.EnableKeyboard;
|
|
|
_enableMouse = option.EnableMouse;
|
|
|
|
|
|
@@ -412,9 +236,9 @@ namespace Ryujinx.Headless.SDL2
|
|
|
_inputConfiguration.Add(inputConfig);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1);
|
|
|
- LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2);
|
|
|
+ LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2);
|
|
|
LoadPlayerConfiguration(option.InputProfile3Name, option.InputId3, PlayerIndex.Player3);
|
|
|
LoadPlayerConfiguration(option.InputProfile4Name, option.InputId4, PlayerIndex.Player4);
|
|
|
LoadPlayerConfiguration(option.InputProfile5Name, option.InputId5, PlayerIndex.Player5);
|
|
|
@@ -422,6 +246,7 @@ namespace Ryujinx.Headless.SDL2
|
|
|
LoadPlayerConfiguration(option.InputProfile7Name, option.InputId7, PlayerIndex.Player7);
|
|
|
LoadPlayerConfiguration(option.InputProfile8Name, option.InputId8, PlayerIndex.Player8);
|
|
|
LoadPlayerConfiguration(option.InputProfileHandheldName, option.InputIdHandheld, PlayerIndex.Handheld);
|
|
|
+
|
|
|
|
|
|
if (_inputConfiguration.Count == 0)
|
|
|
{
|
|
|
@@ -433,7 +258,7 @@ namespace Ryujinx.Headless.SDL2
|
|
|
Logger.SetEnable(LogLevel.Stub, !option.LoggingDisableStub);
|
|
|
Logger.SetEnable(LogLevel.Info, !option.LoggingDisableInfo);
|
|
|
Logger.SetEnable(LogLevel.Warning, !option.LoggingDisableWarning);
|
|
|
- Logger.SetEnable(LogLevel.Error, option.LoggingEnableError);
|
|
|
+ Logger.SetEnable(LogLevel.Error, !option.LoggingDisableError);
|
|
|
Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace);
|
|
|
Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest);
|
|
|
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
|
|
|
@@ -522,88 +347,6 @@ namespace Ryujinx.Headless.SDL2
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- private static IRenderer CreateRenderer(Options options, WindowBase window)
|
|
|
- {
|
|
|
- if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow)
|
|
|
- {
|
|
|
- string preferredGpuId = string.Empty;
|
|
|
- Vk api = Vk.GetApi();
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(options.PreferredGPUVendor))
|
|
|
- {
|
|
|
- string preferredGpuVendor = options.PreferredGPUVendor.ToLowerInvariant();
|
|
|
- var devices = VulkanRenderer.GetPhysicalDevices(api);
|
|
|
-
|
|
|
- foreach (var device in devices)
|
|
|
- {
|
|
|
- if (device.Vendor.ToLowerInvariant() == preferredGpuVendor)
|
|
|
- {
|
|
|
- preferredGpuId = device.Id;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return new VulkanRenderer(
|
|
|
- api,
|
|
|
- (instance, vk) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))),
|
|
|
- vulkanWindow.GetRequiredInstanceExtensions,
|
|
|
- preferredGpuId);
|
|
|
- }
|
|
|
-
|
|
|
- if (options.GraphicsBackend == GraphicsBackend.Metal && window is MetalWindow metalWindow && OperatingSystem.IsMacOS())
|
|
|
- {
|
|
|
- return new MetalRenderer(metalWindow.GetLayer);
|
|
|
- }
|
|
|
-
|
|
|
- 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);
|
|
|
-
|
|
|
- if (threadedGAL)
|
|
|
- {
|
|
|
- renderer = new ThreadedRenderer(renderer);
|
|
|
- }
|
|
|
-
|
|
|
- HLEConfiguration configuration = new(_virtualFileSystem,
|
|
|
- _libHacHorizonManager,
|
|
|
- _contentManager,
|
|
|
- _accountManager,
|
|
|
- _userChannelPersistence,
|
|
|
- renderer,
|
|
|
- new SDL2HardwareDeviceDriver(),
|
|
|
- options.DramSize,
|
|
|
- window,
|
|
|
- options.SystemLanguage,
|
|
|
- options.SystemRegion,
|
|
|
- options.VSyncMode,
|
|
|
- !options.DisableDockedMode,
|
|
|
- !options.DisablePTC,
|
|
|
- options.EnableInternetAccess,
|
|
|
- !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
|
|
- options.FsGlobalAccessLogMode,
|
|
|
- options.SystemTimeOffset,
|
|
|
- options.SystemTimeZone,
|
|
|
- options.MemoryManagerMode,
|
|
|
- options.IgnoreMissingServices,
|
|
|
- options.AspectRatio,
|
|
|
- options.AudioVolume,
|
|
|
- options.UseHypervisor ?? true,
|
|
|
- options.MultiplayerLanInterfaceId,
|
|
|
- Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
|
|
|
- false,
|
|
|
- string.Empty,
|
|
|
- string.Empty,
|
|
|
- options.CustomVSyncInterval);
|
|
|
-
|
|
|
- return new Switch(configuration);
|
|
|
- }
|
|
|
-
|
|
|
private static void ExecutionEntrypoint()
|
|
|
{
|
|
|
if (OperatingSystem.IsWindows())
|