diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs index 0bc682a70f..7b999eaed1 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.Gpu.Memory; using System.Collections.Concurrent; +using System.Threading; namespace Ryujinx.HLE.Gpu.Engines { @@ -18,6 +19,8 @@ namespace Ryujinx.HLE.Gpu.Engines private NvGpuEngine[] SubChannels; + public AutoResetEvent Event { get; private set; } + private struct CachedMacro { public int Position { get; private set; } @@ -60,6 +63,8 @@ namespace Ryujinx.HLE.Gpu.Engines Macros = new CachedMacro[MacrosCount]; Mme = new int[MmeWords]; + + Event = new AutoResetEvent(false); } public void PushBuffer(NvGpuVmm Vmm, NvGpuPBEntry[] Buffer) @@ -68,6 +73,8 @@ namespace Ryujinx.HLE.Gpu.Engines { BufferQueue.Enqueue((Vmm, PBEntry)); } + + Event.Set(); } public void DispatchCalls() diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index f7b263cd0f..1946b187ba 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -71,6 +71,11 @@ namespace Ryujinx.HLE Os.LoadProgram(FileName); } + public bool WaitFifo() + { + return Gpu.Fifo.Event.WaitOne(8); + } + public void ProcessFrame() { Gpu.Fifo.DispatchCalls(); diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 7a4e42e9e2..9b5dda4f0c 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -5,6 +5,9 @@ using Ryujinx.Graphics.Gal; using Ryujinx.HLE; using Ryujinx.HLE.Input; using System; +using System.Threading; + +using Stopwatch = System.Diagnostics.Stopwatch; namespace Ryujinx { @@ -16,6 +19,8 @@ namespace Ryujinx private const float TouchScreenRatioX = (float)TouchScreenWidth / TouchScreenHeight; private const float TouchScreenRatioY = (float)TouchScreenHeight / TouchScreenWidth; + private const int TargetFPS = 60; + private Switch Ns; private IGalRenderer Renderer; @@ -24,6 +29,14 @@ namespace Ryujinx private MouseState? Mouse = null; + private Thread RenderThread; + + private bool ResizeEvent; + + private bool TitleEvent; + + private string NewTitle; + public GLScreen(Switch Ns, IGalRenderer Renderer) : base(1280, 720, new GraphicsMode(), "Ryujinx", 0, @@ -36,13 +49,85 @@ namespace Ryujinx Location = new Point( (DisplayDevice.Default.Width / 2) - (Width / 2), (DisplayDevice.Default.Height / 2) - (Height / 2)); + + ResizeEvent = false; + + TitleEvent = false; } - protected override void OnLoad(EventArgs e) + private void RenderLoop() { - VSync = VSyncMode.On; + MakeCurrent(); + + Stopwatch Chrono = new Stopwatch(); + + Chrono.Start(); + + long TicksPerFrame = Stopwatch.Frequency / TargetFPS; + + long Ticks = 0; + + while (Exists && !IsExiting) + { + if (Ns.WaitFifo()) + { + Ns.ProcessFrame(); + } + + Renderer.RunActions(); + + if (ResizeEvent) + { + ResizeEvent = false; + + Renderer.FrameBuffer.SetWindowSize(Width, Height); + } + + Ticks += Chrono.ElapsedTicks; + + Chrono.Restart(); + + if (Ticks >= TicksPerFrame) + { + RenderFrame(); + + //Queue max. 1 vsync + Ticks = Math.Min(Ticks - TicksPerFrame, TicksPerFrame); + } + } + } + + public void MainLoop() + { + VSync = VSyncMode.Off; + + Visible = true; Renderer.FrameBuffer.SetWindowSize(Width, Height); + + Context.MakeCurrent(null); + + //OpenTK doesn't like sleeps in its thread, to avoid this a renderer thread is created + RenderThread = new Thread(RenderLoop); + + RenderThread.Start(); + + while (Exists && !IsExiting) + { + ProcessEvents(); + + if (!IsExiting) + { + UpdateFrame(); + + if (TitleEvent) + { + TitleEvent = false; + + Title = NewTitle; + } + } + } } private bool IsGamePadButtonPressedFromString(GamePadState GamePad, string Button) @@ -99,7 +184,7 @@ namespace Ryujinx } } - protected override void OnUpdateFrame(FrameEventArgs e) + private new void UpdateFrame() { HidControllerButtons CurrentButton = 0; HidJoystickPosition LeftJoystick; @@ -278,13 +363,9 @@ namespace Ryujinx CurrentButton, LeftJoystick, RightJoystick); - - Ns.ProcessFrame(); - - Renderer.RunActions(); } - protected override void OnRenderFrame(FrameEventArgs e) + private new void RenderFrame() { Renderer.FrameBuffer.Render(); @@ -293,16 +374,25 @@ namespace Ryujinx double HostFps = Ns.Statistics.GetSystemFrameRate(); double GameFps = Ns.Statistics.GetGameFrameRate(); - Title = $"Ryujinx | Host FPS: {HostFps:0.0} | Game FPS: {GameFps:0.0}"; + NewTitle = $"Ryujinx | Host FPS: {HostFps:0.0} | Game FPS: {GameFps:0.0}"; + + TitleEvent = true; SwapBuffers(); Ns.Os.SignalVsync(); } + protected override void OnUnload(EventArgs e) + { + RenderThread.Join(); + + base.OnUnload(e); + } + protected override void OnResize(EventArgs e) { - Renderer.FrameBuffer.SetWindowSize(Width, Height); + ResizeEvent = true; } protected override void OnKeyDown(KeyboardKeyEventArgs e) diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs index b14897695d..5cacc6228b 100644 --- a/Ryujinx/Ui/Program.cs +++ b/Ryujinx/Ui/Program.cs @@ -67,7 +67,7 @@ namespace Ryujinx Screen.Exit(); }; - Screen.Run(0.0, 60.0); + Screen.MainLoop(); } Environment.Exit(0);