From 4038e63de14bf12c0cfbe885e2cac44577fe8a6a Mon Sep 17 00:00:00 2001
From: emmauss <emmausssss@gmail.com>
Date: Tue, 6 Mar 2018 22:18:49 +0200
Subject: [PATCH] Implement basic performance statistics (#53)

* implement basic frame time stats

* added game frame time

* made performancestatictics class non-static

* report average framerate instead of current framerate
---
 Ryujinx.Core/Logging.cs                     |  2 -
 Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs |  2 +
 Ryujinx.Core/PerformanceStatistics.cs       | 87 +++++++++++++++++++++
 Ryujinx.Core/Switch.cs                      |  7 +-
 Ryujinx/Ui/GLScreen.cs                      |  7 +-
 5 files changed, 100 insertions(+), 5 deletions(-)
 create mode 100644 Ryujinx.Core/PerformanceStatistics.cs

diff --git a/Ryujinx.Core/Logging.cs b/Ryujinx.Core/Logging.cs
index b14f266539..d544a5d64f 100644
--- a/Ryujinx.Core/Logging.cs
+++ b/Ryujinx.Core/Logging.cs
@@ -22,8 +22,6 @@ namespace Ryujinx.Core
 
         static Logging()
         {
-            ExecutionTime.Start();
-
             if (File.Exists(LogFileName)) File.Delete(LogFileName);
         }
 
diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
index 4b5f9819aa..720dd44f93 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
@@ -154,6 +154,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
 
         private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader)
         {
+            Context.Ns.Statistics.RecordGameFrameTime();
+
             //TODO: Errors.
             int Slot            = ParcelReader.ReadInt32();
             int Unknown4        = ParcelReader.ReadInt32();
diff --git a/Ryujinx.Core/PerformanceStatistics.cs b/Ryujinx.Core/PerformanceStatistics.cs
new file mode 100644
index 0000000000..3740daa584
--- /dev/null
+++ b/Ryujinx.Core/PerformanceStatistics.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+using System.Timers;
+
+namespace Ryujinx.Core
+{
+    public class PerformanceStatistics
+    {
+        Stopwatch ExecutionTime = new Stopwatch();
+        Timer ResetTimer = new Timer(1000);
+
+        long CurrentGameFrameEnded;
+        long CurrentSystemFrameEnded;
+        long CurrentSystemFrameStart;
+        long LastGameFrameEnded;
+        long LastSystemFrameEnded;
+
+        double AccumulatedGameFrameTime;
+        double AccumulatedSystemFrameTime;
+        double CurrentGameFrameTime;
+        double CurrentSystemFrameTime;
+        double PreviousGameFrameTime;
+        double PreviousSystemFrameTime;
+        public double GameFrameRate   { get; private set; }
+        public double SystemFrameRate { get; private set; }
+        public long SystemFramesRendered;
+        public long GameFramesRendered;
+        public long ElapsedMilliseconds => ExecutionTime.ElapsedMilliseconds;
+        public long ElapsedMicroseconds => (long)
+                (((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000);
+        public long ElapsedNanoseconds => (long)
+                (((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000000);
+
+        public PerformanceStatistics()
+        {
+            ExecutionTime.Start();
+            ResetTimer.Elapsed += ResetTimerElapsed;
+            ResetTimer.AutoReset = true;
+            ResetTimer.Start();
+        }
+
+        private void ResetTimerElapsed(object sender, ElapsedEventArgs e)
+        {
+            ResetStatistics();
+        }
+
+        public void StartSystemFrame()
+        {
+            PreviousSystemFrameTime = CurrentSystemFrameTime;
+            LastSystemFrameEnded = CurrentSystemFrameEnded;
+            CurrentSystemFrameStart = ElapsedMicroseconds;
+        }
+
+        public void EndSystemFrame()
+        {
+            CurrentSystemFrameEnded = ElapsedMicroseconds;
+            CurrentSystemFrameTime = CurrentSystemFrameEnded - CurrentSystemFrameStart;
+            AccumulatedSystemFrameTime += CurrentSystemFrameTime;
+            SystemFramesRendered++;
+        }
+
+        public void RecordGameFrameTime()
+        {
+            CurrentGameFrameEnded = ElapsedMicroseconds;
+            CurrentGameFrameTime = CurrentGameFrameEnded - LastGameFrameEnded;
+            PreviousGameFrameTime = CurrentGameFrameTime;
+            LastGameFrameEnded = CurrentGameFrameEnded;
+            AccumulatedGameFrameTime += CurrentGameFrameTime;
+            GameFramesRendered++;
+        }
+
+        public void ResetStatistics()
+        {
+            GameFrameRate = 1000 / ((AccumulatedGameFrameTime / GameFramesRendered) / 1000);
+            GameFrameRate = double.IsNaN(GameFrameRate) ? 0 : GameFrameRate;
+            SystemFrameRate = 1000 / ((AccumulatedSystemFrameTime / SystemFramesRendered) / 1000);
+            SystemFrameRate = double.IsNaN(SystemFrameRate) ? 0 : SystemFrameRate;
+
+            GameFramesRendered = 0;
+            SystemFramesRendered = 0;
+            AccumulatedGameFrameTime = 0;
+            AccumulatedSystemFrameTime = 0;
+        }
+    }
+}
diff --git a/Ryujinx.Core/Switch.cs b/Ryujinx.Core/Switch.cs
index 3f7b1e2b81..1acd87f018 100644
--- a/Ryujinx.Core/Switch.cs
+++ b/Ryujinx.Core/Switch.cs
@@ -17,8 +17,9 @@ namespace Ryujinx.Core
         internal Horizon   Os  { get; private set; }
         internal VirtualFs VFs { get; private set; }
 
-        public Hid    Hid      { get; private set; }
-        public SetSys Settings { get; private set; }
+        public Hid    Hid                       { get; private set; }        
+        public SetSys Settings                  { get; private set; }
+        public PerformanceStatistics Statistics { get; private set; }
 
         public event EventHandler Finish;
 
@@ -32,6 +33,8 @@ namespace Ryujinx.Core
 
             Hid = new Hid(Ram);
 
+            Statistics = new PerformanceStatistics();
+
             Os = new Horizon(this);
 
             Os.HidSharedMem.MemoryMapped   += Hid.ShMemMap;
diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs
index 4c7cee4672..b0dca81b77 100644
--- a/Ryujinx/Ui/GLScreen.cs
+++ b/Ryujinx/Ui/GLScreen.cs
@@ -166,9 +166,12 @@ namespace Ryujinx
 
         protected override void OnRenderFrame(FrameEventArgs e)
         {
+            Ns.Statistics.StartSystemFrame();
+
             GL.Viewport(0, 0, Width, Height);
 
-            Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {1f / e.Time:0})";
+            Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " +
+                $"{Ns.Statistics.GameFrameRate:0})";
 
             GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
@@ -176,6 +179,8 @@ namespace Ryujinx
             Renderer.Render();
 
             SwapBuffers();
+
+            Ns.Statistics.EndSystemFrame();
         }
 
         protected override void OnResize(EventArgs e)