Replace Host FPS with GPU command queue load ("Fifo %") (#1585)

* Replace Host FPS with FIFO%

* Change measurement order. Improve calculation.

Now at 100% when FIFO is blocking game exectution, rather than "0".

* Address feedback (1)

* Remove Host FPS

* FIFO rather than Fifo

* Address Ac_k feedback

* Rebase
This commit is contained in:
riperiperi 2020-10-13 21:54:42 +01:00 committed by GitHub
parent 04e330cc00
commit aec8177850
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 102 additions and 40 deletions

View file

@ -1,4 +1,5 @@
using System.Diagnostics; using Ryujinx.Common;
using System.Diagnostics;
using System.Timers; using System.Timers;
namespace Ryujinx.HLE namespace Ryujinx.HLE
@ -7,36 +8,44 @@ namespace Ryujinx.HLE
{ {
private const double FrameRateWeight = 0.5; private const double FrameRateWeight = 0.5;
private const int FrameTypeSystem = 0; private const int FrameTypeGame = 0;
private const int FrameTypeGame = 1; private const int PercentTypeFifo = 0;
private double[] _averageFrameRate; private double[] _averageFrameRate;
private double[] _accumulatedFrameTime; private double[] _accumulatedFrameTime;
private double[] _previousFrameTime; private double[] _previousFrameTime;
private double[] _averagePercent;
private double[] _accumulatedPercent;
private double[] _percentLastEndTime;
private double[] _percentStartTime;
private long[] _framesRendered; private long[] _framesRendered;
private long[] _percentCount;
private object[] _frameLock; private object[] _frameLock;
private object[] _percentLock;
private double _ticksToSeconds; private double _ticksToSeconds;
private Stopwatch _executionTime;
private Timer _resetTimer; private Timer _resetTimer;
public PerformanceStatistics() public PerformanceStatistics()
{ {
_averageFrameRate = new double[2]; _averageFrameRate = new double[1];
_accumulatedFrameTime = new double[2]; _accumulatedFrameTime = new double[1];
_previousFrameTime = new double[2]; _previousFrameTime = new double[1];
_framesRendered = new long[2]; _averagePercent = new double[1];
_accumulatedPercent = new double[1];
_percentLastEndTime = new double[1];
_percentStartTime = new double[1];
_frameLock = new object[] { new object(), new object() }; _framesRendered = new long[1];
_percentCount = new long[1];
_executionTime = new Stopwatch(); _frameLock = new object[] { new object() };
_percentLock = new object[] { new object() };
_executionTime.Start();
_resetTimer = new Timer(1000); _resetTimer = new Timer(1000);
@ -46,26 +55,26 @@ namespace Ryujinx.HLE
_resetTimer.Start(); _resetTimer.Start();
_ticksToSeconds = 1.0 / Stopwatch.Frequency; _ticksToSeconds = 1.0 / PerformanceCounter.TicksPerSecond;
} }
private void ResetTimerElapsed(object sender, ElapsedEventArgs e) private void ResetTimerElapsed(object sender, ElapsedEventArgs e)
{ {
CalculateAverageFrameRate(FrameTypeSystem);
CalculateAverageFrameRate(FrameTypeGame); CalculateAverageFrameRate(FrameTypeGame);
CalculateAveragePercent(PercentTypeFifo);
} }
private void CalculateAverageFrameRate(int frameType) private void CalculateAverageFrameRate(int frameType)
{ {
double frameRate = 0; double frameRate = 0;
lock (_frameLock[frameType])
{
if (_accumulatedFrameTime[frameType] > 0) if (_accumulatedFrameTime[frameType] > 0)
{ {
frameRate = _framesRendered[frameType] / _accumulatedFrameTime[frameType]; frameRate = _framesRendered[frameType] / _accumulatedFrameTime[frameType];
} }
lock (_frameLock[frameType])
{
_averageFrameRate[frameType] = LinearInterpolate(_averageFrameRate[frameType], frameRate); _averageFrameRate[frameType] = LinearInterpolate(_averageFrameRate[frameType], frameRate);
_framesRendered[frameType] = 0; _framesRendered[frameType] = 0;
@ -74,14 +83,30 @@ namespace Ryujinx.HLE
} }
} }
private double LinearInterpolate(double old, double New) private void CalculateAveragePercent(int percentType)
{ {
return old * (1.0 - FrameRateWeight) + New * FrameRateWeight; // If start time is non-zero, a percent reading is still being measured.
// If there aren't any readings, the default should be 100% if still being measured, or 0% if not.
double percent = (_percentStartTime[percentType] == 0) ? 0 : 100;
lock (_percentLock[percentType])
{
if (_percentCount[percentType] > 0)
{
percent = _accumulatedPercent[percentType] / _percentCount[percentType];
} }
public void RecordSystemFrameTime() _averagePercent[percentType] = percent;
_percentCount[percentType] = 0;
_accumulatedPercent[percentType] = 0;
}
}
private double LinearInterpolate(double lhs, double rhs)
{ {
RecordFrameTime(FrameTypeSystem); return lhs * (1.0 - FrameRateWeight) + rhs * FrameRateWeight;
} }
public void RecordGameFrameTime() public void RecordGameFrameTime()
@ -89,9 +114,46 @@ namespace Ryujinx.HLE
RecordFrameTime(FrameTypeGame); RecordFrameTime(FrameTypeGame);
} }
public void RecordFifoStart()
{
StartPercentTime(PercentTypeFifo);
}
public void RecordFifoEnd()
{
EndPercentTime(PercentTypeFifo);
}
private void StartPercentTime(int percentType)
{
double currentTime = PerformanceCounter.ElapsedTicks * _ticksToSeconds;
_percentStartTime[percentType] = currentTime;
}
private void EndPercentTime(int percentType)
{
double currentTime = PerformanceCounter.ElapsedTicks * _ticksToSeconds;
double elapsedTime = currentTime - _percentLastEndTime[percentType];
double elapsedActiveTime = currentTime - _percentStartTime[percentType];
double percentActive = (elapsedActiveTime / elapsedTime) * 100;
lock (_percentLock[percentType])
{
_accumulatedPercent[percentType] += percentActive;
_percentCount[percentType]++;
}
_percentLastEndTime[percentType] = currentTime;
_percentStartTime[percentType] = 0;
}
private void RecordFrameTime(int frameType) private void RecordFrameTime(int frameType)
{ {
double currentFrameTime = _executionTime.ElapsedTicks * _ticksToSeconds; double currentFrameTime = PerformanceCounter.ElapsedTicks * _ticksToSeconds;
double elapsedFrameTime = currentFrameTime - _previousFrameTime[frameType]; double elapsedFrameTime = currentFrameTime - _previousFrameTime[frameType];
@ -105,14 +167,14 @@ namespace Ryujinx.HLE
} }
} }
public double GetSystemFrameRate()
{
return _averageFrameRate[FrameTypeSystem];
}
public double GetGameFrameRate() public double GetGameFrameRate()
{ {
return _averageFrameRate[FrameTypeGame]; return _averageFrameRate[FrameTypeGame];
} }
public double GetFifoPercent()
{
return _averagePercent[PercentTypeFifo];
}
} }
} }

View file

@ -339,7 +339,9 @@ namespace Ryujinx.Ui
if (_device.WaitFifo()) if (_device.WaitFifo())
{ {
_device.Statistics.RecordFifoStart();
_device.ProcessFrame(); _device.ProcessFrame();
_device.Statistics.RecordFifoEnd();
} }
string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? "Docked" : "Handheld"; string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? "Docked" : "Handheld";
@ -353,13 +355,11 @@ namespace Ryujinx.Ui
{ {
_device.PresentFrame(SwapBuffers); _device.PresentFrame(SwapBuffers);
_device.Statistics.RecordSystemFrameTime();
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
_device.EnableDeviceVsync, _device.EnableDeviceVsync,
dockedMode, dockedMode,
$"Host: {_device.Statistics.GetSystemFrameRate():00.00} FPS",
$"Game: {_device.Statistics.GetGameFrameRate():00.00} FPS", $"Game: {_device.Statistics.GetGameFrameRate():00.00} FPS",
$"FIFO: {_device.Statistics.GetFifoPercent():0.00} %",
$"GPU: {_renderer.GpuVendor}")); $"GPU: {_renderer.GpuVendor}"));
_ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame); _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);

View file

@ -59,7 +59,7 @@ namespace Ryujinx.Ui
[GUI] CheckMenuItem _favToggle; [GUI] CheckMenuItem _favToggle;
[GUI] MenuItem _firmwareInstallDirectory; [GUI] MenuItem _firmwareInstallDirectory;
[GUI] MenuItem _firmwareInstallFile; [GUI] MenuItem _firmwareInstallFile;
[GUI] Label _hostStatus; [GUI] Label _fifoStatus;
[GUI] CheckMenuItem _iconToggle; [GUI] CheckMenuItem _iconToggle;
[GUI] CheckMenuItem _developerToggle; [GUI] CheckMenuItem _developerToggle;
[GUI] CheckMenuItem _appToggle; [GUI] CheckMenuItem _appToggle;
@ -792,8 +792,8 @@ namespace Ryujinx.Ui
{ {
Application.Invoke(delegate Application.Invoke(delegate
{ {
_hostStatus.Text = args.HostStatus;
_gameStatus.Text = args.GameStatus; _gameStatus.Text = args.GameStatus;
_fifoStatus.Text = args.FifoStatus;
_gpuName.Text = args.GpuName; _gpuName.Text = args.GpuName;
_dockedMode.Text = args.DockedMode; _dockedMode.Text = args.DockedMode;

View file

@ -521,7 +521,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="_hostStatus"> <object class="GtkLabel" id="_gameStatus">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
@ -546,7 +546,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="_gameStatus"> <object class="GtkLabel" id="_fifoStatus">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>

View file

@ -6,16 +6,16 @@ namespace Ryujinx.Ui
{ {
public bool VSyncEnabled; public bool VSyncEnabled;
public string DockedMode; public string DockedMode;
public string HostStatus;
public string GameStatus; public string GameStatus;
public string FifoStatus;
public string GpuName; public string GpuName;
public StatusUpdatedEventArgs(bool vSyncEnabled, string dockedMode, string hostStatus, string gameStatus, string gpuName) public StatusUpdatedEventArgs(bool vSyncEnabled, string dockedMode, string gameStatus, string fifoStatus, string gpuName)
{ {
VSyncEnabled = vSyncEnabled; VSyncEnabled = vSyncEnabled;
DockedMode = dockedMode; DockedMode = dockedMode;
HostStatus = hostStatus;
GameStatus = gameStatus; GameStatus = gameStatus;
FifoStatus = fifoStatus;
GpuName = gpuName; GpuName = gpuName;
} }
} }