My code calculates average FPS. Every 0.4s it updates the FPS text. Every 2s it resets the number of average FPS. I need to calculate accurate and smooth FPS (so any temporary spikes will be eliminated). So is this FPS counter code will follow my requirements?
[RequireComponent(typeof(TMP_Text))]
public class FpsCounter : MonoBehaviour
{
private struct AverageFloat
{
public float Average => _value / _count;
private float _value;
private int _count;
public void Add(float number)
{
_value = number;
_count ;
}
public void Reset()
{
_value = 0f;
_count = 0;
}
}
[SerializeField] private float _averageValueCollectionTime = 2f;
[SerializeField] private float _refreshFrequency = 0.4f;
private float _timeSinceFpsReset = 0f;
private float _timeSinceUpdate = 0f;
private AverageFloat _fpsValue;
private TMP_Text _text;
private void Start()
{
_text = GetComponent<TMP_Text>();
}
private void Update()
{
if (_timeSinceFpsReset > _averageValueCollectionTime)
{
_timeSinceFpsReset = 0;
_fpsValue.Reset();
}
_fpsValue.Add(1f / Time.unscaledDeltaTime);
_timeSinceFpsReset = Time.deltaTime;
if (_timeSinceUpdate < _refreshFrequency)
{
_timeSinceUpdate = Time.deltaTime;
return;
}
int fps = Mathf.RoundToInt(_fpsValue.Average);
_text.text = fps.ToString();
_timeSinceUpdate = 0f;
}
}
CodePudding user response:
You could use InvokeRepeating in order to reset / refresh your number of average FPS, in addition to Time.frameCount to get the number of frames.
CodePudding user response:
So... It's better to calculate FPS with exponentially weighted moving average. This solution gives more accurate and smooth results.
[RequireComponent(typeof(TMP_Text))]
public class FpsCounter : MonoBehaviour
{
[SerializeField] [Range(0f, 1f)] private float _expSmoothingFactor = 0.9f;
[SerializeField] private float _refreshFrequency = 0.4f;
private float _timeSinceUpdate = 0f;
private float _averageFps = 1f;
private TMP_Text _text;
private void Start()
{
_text = GetComponent<TMP_Text>();
}
private void Update()
{
// Exponentially weighted moving average (EWMA)
_averageFps = _expSmoothingFactor * _averageFps (1f - _expSmoothingFactor) * 1f / Time.unscaledDeltaTime;
if (_timeSinceUpdate < _refreshFrequency)
{
_timeSinceUpdate = Time.deltaTime;
return;
}
int fps = Mathf.RoundToInt(_averageFps);
_text.text = fps.ToString();
_timeSinceUpdate = 0f;
}
}
_expSmoothingFactor must be set from 0 to 1. It determines how much smoothing will be applied. 0.9 value is most optimal (higher value = more smoothing).