Files
2025-03-12 12:56:27 +00:00

129 lines
4.4 KiB
C#

using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Barotrauma
{
public class PerformanceCounter
{
private readonly object mutex = new object();
public double AverageFramesPerSecond { get; private set; }
public double CurrentFramesPerSecond { get; private set; }
public double AverageFramesPerSecondInPastMinute { get; private set; }
public const int MaximumSamples = 10;
private readonly Queue<double> sampleBuffer = new Queue<double>();
private readonly Queue<double> averageFramesPerSecondBuffer = new Queue<double>();
private readonly Stopwatch timer = new Stopwatch();
private long lastSecondMark = 0;
private long lastMinuteMark = 0;
public class TickInfo
{
public Queue<long> ElapsedTicks { get; set; } = new Queue<long>();
public long AvgTicksPerFrame { get; set; }
}
private readonly Dictionary<string, Queue<long>> elapsedTicks = new Dictionary<string, Queue<long>>();
private readonly Dictionary<string, long> avgTicksPerFrame = new Dictionary<string, long>();
#if CLIENT
internal Graph UpdateTimeGraph = new Graph(500), DrawTimeGraph = new Graph(500);
#endif
private readonly List<string> tempSavedIdentifiers = new List<string>();
public IReadOnlyList<string> GetSavedIdentifiers
{
get
{
lock (mutex)
{
tempSavedIdentifiers.Clear();
tempSavedIdentifiers.AddRange(avgTicksPerFrame.Keys);
}
return tempSavedIdentifiers;
}
}
public PerformanceCounter()
{
timer.Start();
}
public void AddElapsedTicks(string identifier, long ticks)
{
lock (mutex)
{
if (!elapsedTicks.ContainsKey(identifier)) { elapsedTicks.Add(identifier, new Queue<long>()); }
elapsedTicks[identifier].Enqueue(ticks);
if (elapsedTicks[identifier].Count > MaximumSamples)
{
elapsedTicks[identifier].Dequeue();
avgTicksPerFrame[identifier] = (long)elapsedTicks[identifier].Average(i => i);
}
}
}
public float GetAverageElapsedMillisecs(string identifier)
{
long ticksPerFrame = 0;
lock (mutex)
{
avgTicksPerFrame.TryGetValue(identifier, out ticksPerFrame);
}
return ticksPerFrame * 1000.0f / Stopwatch.Frequency;
}
public bool Update(double deltaTime)
{
if (deltaTime == 0.0f) { return false; }
CurrentFramesPerSecond = 1.0 / deltaTime;
sampleBuffer.Enqueue(CurrentFramesPerSecond);
if (sampleBuffer.Count > MaximumSamples)
{
sampleBuffer.Dequeue();
AverageFramesPerSecond = sampleBuffer.Average();
}
else
{
AverageFramesPerSecond = CurrentFramesPerSecond;
}
long currentTime = timer.ElapsedMilliseconds;
long currentSecond = currentTime / 1000;
if (currentSecond > lastSecondMark)
{
averageFramesPerSecondBuffer.Enqueue(AverageFramesPerSecond);
lastSecondMark = currentSecond;
}
if (currentTime - lastMinuteMark >= 60 * 1000 &&
/* we don't need info of the FPS every minute, we can get a good sample size just by logging a small sample */
GameAnalyticsManager.ShouldLogRandomSample())
{
//the FPS could be even higher than this on a high-end monitor, but let's restrict it to 144 to reduce the number of distinct event IDs
const int MaxFPS = 144;
AverageFramesPerSecondInPastMinute = averageFramesPerSecondBuffer.Average();
GameAnalyticsManager.AddDesignEvent($"FPS:{MathHelper.Clamp((int)AverageFramesPerSecondInPastMinute, 0, MaxFPS)}");
GameAnalyticsManager.AddDesignEvent($"FPSLowest:{MathHelper.Clamp((int)averageFramesPerSecondBuffer.Min(), 0, MaxFPS)}");
averageFramesPerSecondBuffer.Clear();
lastMinuteMark = currentTime;
}
return true;
}
}
}