diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /Ryujinx.Headless.SDL2/WindowBase.cs | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'Ryujinx.Headless.SDL2/WindowBase.cs')
| -rw-r--r-- | Ryujinx.Headless.SDL2/WindowBase.cs | 499 |
1 files changed, 0 insertions, 499 deletions
diff --git a/Ryujinx.Headless.SDL2/WindowBase.cs b/Ryujinx.Headless.SDL2/WindowBase.cs deleted file mode 100644 index e3371042..00000000 --- a/Ryujinx.Headless.SDL2/WindowBase.cs +++ /dev/null @@ -1,499 +0,0 @@ -using ARMeilleure.Translation; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.GAL.Multithreading; -using Ryujinx.HLE.HOS.Applets; -using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types; -using Ryujinx.HLE.Ui; -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.IO; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Threading; -using static SDL2.SDL; -using Switch = Ryujinx.HLE.Switch; - -namespace Ryujinx.Headless.SDL2 -{ - abstract partial class WindowBase : IHostUiHandler, IDisposable - { - protected const int DefaultWidth = 1280; - protected const int DefaultHeight = 720; - 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<Action> MainThreadActions = new ConcurrentQueue<Action>(); - - [LibraryImport("SDL2")] - // TODO: Remove this as soon as SDL2-CS was updated to expose this method publicly - private static partial IntPtr SDL_LoadBMP_RW(IntPtr src, int freesrc); - - public static void QueueMainThreadAction(Action action) - { - MainThreadActions.Enqueue(action); - } - - public NpadManager NpadManager { get; } - public TouchScreenManager TouchScreenManager { get; } - public Switch Device { get; private set; } - public IRenderer Renderer { get; private set; } - - public event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent; - - protected IntPtr WindowHandle { get; set; } - - public IHostUiTheme HostUiTheme { get; } - public int Width { get; private set; } - public int Height { get; private set; } - - protected SDL2MouseDriver MouseDriver; - private InputManager _inputManager; - private IKeyboard _keyboardInterface; - private GraphicsDebugLevel _glLogLevel; - private readonly Stopwatch _chrono; - private readonly long _ticksPerFrame; - private readonly CancellationTokenSource _gpuCancellationTokenSource; - private readonly ManualResetEvent _exitEvent; - - private long _ticks; - private bool _isActive; - private bool _isStopped; - private uint _windowId; - - private string _gpuVendorName; - - private AspectRatio _aspectRatio; - private bool _enableMouse; - - public WindowBase( - InputManager inputManager, - GraphicsDebugLevel glLogLevel, - AspectRatio aspectRatio, - bool enableMouse, - HideCursor hideCursor) - { - MouseDriver = new SDL2MouseDriver(hideCursor); - _inputManager = inputManager; - _inputManager.SetMouseDriver(MouseDriver); - NpadManager = _inputManager.CreateNpadManager(); - TouchScreenManager = _inputManager.CreateTouchScreenManager(); - _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); - _glLogLevel = glLogLevel; - _chrono = new Stopwatch(); - _ticksPerFrame = Stopwatch.Frequency / TargetFps; - _gpuCancellationTokenSource = new CancellationTokenSource(); - _exitEvent = new ManualResetEvent(false); - _aspectRatio = aspectRatio; - _enableMouse = enableMouse; - HostUiTheme = new HeadlessHostUiTheme(); - - SDL2Driver.Instance.Initialize(); - } - - public void Initialize(Switch device, List<InputConfig> inputConfigs, bool enableKeyboard, bool enableMouse) - { - Device = device; - - IRenderer renderer = Device.Gpu.Renderer; - - if (renderer is ThreadedRenderer tr) - { - renderer = tr.BaseRenderer; - } - - Renderer = renderer; - - NpadManager.Initialize(device, inputConfigs, enableKeyboard, enableMouse); - TouchScreenManager.Initialize(device); - } - - private void SetWindowIcon() - { - Stream iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Ryujinx.Headless.SDL2.Ryujinx.bmp"); - byte[] iconBytes = new byte[iconStream!.Length]; - - if (iconStream.Read(iconBytes, 0, iconBytes.Length) != iconBytes.Length) - { - Logger.Error?.Print(LogClass.Application, "Failed to read icon to byte array."); - iconStream.Close(); - - return; - } - - iconStream.Close(); - - unsafe - { - fixed (byte* iconPtr = iconBytes) - { - IntPtr rwOpsStruct = SDL_RWFromConstMem((IntPtr)iconPtr, iconBytes.Length); - IntPtr iconHandle = SDL_LoadBMP_RW(rwOpsStruct, 1); - - SDL_SetWindowIcon(WindowHandle, iconHandle); - SDL_FreeSurface(iconHandle); - } - } - } - - private void InitializeWindow() - { - var activeProcess = Device.Processes.ActiveApplication; - var nacp = activeProcess.ApplicationControlProperties; - int desiredLanguage = (int)Device.System.State.DesiredTitleLanguage; - - string titleNameSection = string.IsNullOrWhiteSpace(nacp.Title[desiredLanguage].NameString.ToString()) ? string.Empty : $" - {nacp.Title[desiredLanguage].NameString.ToString()}"; - string titleVersionSection = string.IsNullOrWhiteSpace(nacp.DisplayVersionString.ToString()) ? string.Empty : $" v{nacp.DisplayVersionString.ToString()}"; - string titleIdSection = string.IsNullOrWhiteSpace(activeProcess.ProgramIdText) ? string.Empty : $" ({activeProcess.ProgramIdText.ToUpper()})"; - string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; - - WindowHandle = SDL_CreateWindow($"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, DefaultWidth, DefaultHeight, DefaultFlags | GetWindowFlags()); - - if (WindowHandle == IntPtr.Zero) - { - string errorMessage = $"SDL_CreateWindow failed with error \"{SDL_GetError()}\""; - - Logger.Error?.Print(LogClass.Application, errorMessage); - - throw new Exception(errorMessage); - } - - SetWindowIcon(); - - _windowId = SDL_GetWindowID(WindowHandle); - SDL2Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent); - - Width = DefaultWidth; - Height = DefaultHeight; - } - - private void HandleWindowEvent(SDL_Event evnt) - { - if (evnt.type == SDL_EventType.SDL_WINDOWEVENT) - { - switch (evnt.window.windowEvent) - { - case SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED: - Width = evnt.window.data1; - Height = evnt.window.data2; - Renderer?.Window.SetSize(Width, Height); - MouseDriver.SetClientSize(Width, Height); - break; - - case SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE: - Exit(); - break; - - default: - break; - } - } - else - { - MouseDriver.Update(evnt); - } - } - - protected abstract void InitializeWindowRenderer(); - - protected abstract void InitializeRenderer(); - - protected abstract void FinalizeWindowRenderer(); - - protected abstract void SwapBuffers(); - - public abstract SDL_WindowFlags GetWindowFlags(); - - private string GetGpuVendorName() - { - return Renderer.GetHardwareInfo().GpuVendor; - } - - public void Render() - { - InitializeWindowRenderer(); - - Device.Gpu.Renderer.Initialize(_glLogLevel); - - InitializeRenderer(); - - _gpuVendorName = GetGpuVendorName(); - - Device.Gpu.Renderer.RunLoop(() => - { - Device.Gpu.SetGpuThread(); - Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); - Translator.IsReadyForTranslation.Set(); - - while (_isActive) - { - if (_isStopped) - { - return; - } - - _ticks += _chrono.ElapsedTicks; - - _chrono.Restart(); - - if (Device.WaitFifo()) - { - Device.Statistics.RecordFifoStart(); - Device.ProcessFrame(); - Device.Statistics.RecordFifoEnd(); - } - - while (Device.ConsumeFrameAvailable()) - { - Device.PresentFrame(SwapBuffers); - } - - if (_ticks >= _ticksPerFrame) - { - string dockedMode = Device.System.State.DockedMode ? "Docked" : "Handheld"; - float scale = Graphics.Gpu.GraphicsConfig.ResScale; - if (scale != 1) - { - dockedMode += $" ({scale}x)"; - } - - StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( - Device.EnableDeviceVsync, - dockedMode, - Device.Configuration.AspectRatio.ToText(), - $"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", - $"FIFO: {Device.Statistics.GetFifoPercent():0.00} %", - $"GPU: {_gpuVendorName}")); - - _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame); - } - } - }); - - FinalizeWindowRenderer(); - } - - public void Exit() - { - TouchScreenManager?.Dispose(); - NpadManager?.Dispose(); - - if (_isStopped) - { - return; - } - - _gpuCancellationTokenSource.Cancel(); - - _isStopped = true; - _isActive = false; - - _exitEvent.WaitOne(); - _exitEvent.Dispose(); - } - - public void ProcessMainThreadQueue() - { - while (MainThreadActions.TryDequeue(out Action action)) - { - action(); - } - } - - public void MainLoop() - { - while (_isActive) - { - UpdateFrame(); - - SDL_PumpEvents(); - - ProcessMainThreadQueue(); - - // Polling becomes expensive if it's not slept - Thread.Sleep(1); - } - - _exitEvent.Set(); - } - - private void NVStutterWorkaround() - { - while (_isActive) - { - // When NVIDIA Threaded Optimization is on, the driver will snapshot all threads in the system whenever the application creates any new ones. - // The ThreadPool has something called a "GateThread" which terminates itself after some inactivity. - // However, it immediately starts up again, since the rules regarding when to terminate and when to start differ. - // This creates a new thread every second or so. - // The main problem with this is that the thread snapshot can take 70ms, is on the OpenGL thread and will delay rendering any graphics. - // This is a little over budget on a frame time of 16ms, so creates a large stutter. - // The solution is to keep the ThreadPool active so that it never has a reason to terminate the GateThread. - - // TODO: This should be removed when the issue with the GateThread is resolved. - - ThreadPool.QueueUserWorkItem((state) => { }); - Thread.Sleep(300); - } - } - - private bool UpdateFrame() - { - if (!_isActive) - { - return true; - } - - if (_isStopped) - { - return false; - } - - NpadManager.Update(); - - // Touchscreen - bool hasTouch = false; - - // Get screen touch position - if (!_enableMouse) - { - hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as SDL2MouseDriver).IsButtonPressed(MouseButton.Button1), _aspectRatio.ToFloat()); - } - - if (!hasTouch) - { - TouchScreenManager.Update(false); - } - - Device.Hid.DebugPad.Update(); - - // TODO: Replace this with MouseDriver.CheckIdle() when mouse motion events are received on every supported platform. - MouseDriver.UpdatePosition(); - - return true; - } - - public void Execute() - { - _chrono.Restart(); - _isActive = true; - - InitializeWindow(); - - Thread renderLoopThread = new Thread(Render) - { - Name = "GUI.RenderLoop" - }; - renderLoopThread.Start(); - - Thread nvStutterWorkaround = null; - if (Renderer is Graphics.OpenGL.OpenGLRenderer) - { - nvStutterWorkaround = new Thread(NVStutterWorkaround) - { - Name = "GUI.NVStutterWorkaround" - }; - nvStutterWorkaround.Start(); - } - - MainLoop(); - - renderLoopThread.Join(); - nvStutterWorkaround?.Join(); - - Exit(); - } - - public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText) - { - // SDL2 doesn't support input dialogs - userText = "Ryujinx"; - - return true; - } - - public bool DisplayMessageDialog(string title, string message) - { - SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags.SDL_MESSAGEBOX_INFORMATION, title, message, WindowHandle); - - return true; - } - - public bool DisplayMessageDialog(ControllerAppletUiArgs args) - { - string playerCount = args.PlayerCountMin == args.PlayerCountMax ? $"exactly {args.PlayerCountMin}" : $"{args.PlayerCountMin}-{args.PlayerCountMax}"; - - string message = $"Application requests {playerCount} player(s) with:\n\n" - + $"TYPES: {args.SupportedStyles}\n\n" - + $"PLAYERS: {string.Join(", ", args.SupportedPlayers)}\n\n" - + (args.IsDocked ? "Docked mode set. Handheld is also invalid.\n\n" : "") - + "Please reconfigure Input now and then press OK."; - - return DisplayMessageDialog("Controller Applet", message); - } - - public IDynamicTextInputHandler CreateDynamicTextInputHandler() - { - return new HeadlessDynamicTextInputHandler(); - } - - public void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value) - { - device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value); - - Exit(); - } - - public bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText) - { - SDL_MessageBoxData data = new SDL_MessageBoxData - { - title = title, - message = message, - buttons = new SDL_MessageBoxButtonData[buttonsText.Length], - numbuttons = buttonsText.Length, - window = WindowHandle - }; - - for (int i = 0; i < buttonsText.Length; i++) - { - data.buttons[i] = new SDL_MessageBoxButtonData - { - buttonid = i, - text = buttonsText[i] - }; - } - - SDL_ShowMessageBox(ref data, out int _); - - return true; - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _isActive = false; - TouchScreenManager?.Dispose(); - NpadManager.Dispose(); - - SDL2Driver.Instance.UnregisterWindow(_windowId); - - SDL_DestroyWindow(WindowHandle); - - SDL2Driver.Instance.Dispose(); - } - } - } -}
\ No newline at end of file |
