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:
parent
04e330cc00
commit
aec8177850
@ -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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user