Make the FPS reported by Engine.get_frames_per_second() smoother

This uses an averaging algorithm to smooth out the FPS counter.
This commit is contained in:
Hugo Locurcio 2022-07-23 20:05:49 +02:00
parent 4e5ed0bbfb
commit 7826d68236
No known key found for this signature in database
GPG Key ID: 39E8F8BE30B0A49C
5 changed files with 15 additions and 7 deletions

View File

@ -62,7 +62,9 @@ private:
int ips = 60;
double physics_jitter_fix = 0.5;
double _fps = 1;
// Assume to be rendering at 60 FPS at first, so that the FPS counter can
// smoothly interpolate from that value (which is more realistic than going up from 1 FPS).
double _fps = 60;
int _max_fps = 0;
int _audio_output_latency = 0;
double _time_scale = 1.0;

View File

@ -56,7 +56,7 @@
#define UNIT_EPSILON 0.001
#endif
#define USEC_TO_SEC(m_usec) ((m_usec) / 1000000.0)
#define USEC_TO_SEC(m_usec) ((m_usec) * 0.000'001)
enum ClockDirection {
CLOCKWISE,

View File

@ -64,7 +64,8 @@
<method name="get_frames_per_second" qualifiers="const">
<return type="float" />
<description>
Returns the average frames rendered every second (FPS), also known as the framerate.
Returns the average frames rendered every second (FPS), also known as the framerate. This uses an averaging algorithm to smooth out the reported FPS over (roughly) the last second.¨
[b]Note:[/b] Comparing performance is usually better done using [i]milliseconds per frame[/i] (mspf) instead of [i]frames per second[/i], as mspf measurements are linear across the entire spectrum of values. To get the milliseconds per frame, use [code]1000.0 / Engine.get_frames_per_second()[/code].
</description>
</method>
<method name="get_license_info" qualifiers="const">

View File

@ -4141,7 +4141,13 @@ bool Main::iteration() {
frames++;
Engine::get_singleton()->_process_frames++;
if (frame > 1000000) {
// Smoothly update FPS over time, while ensuring the speed at which FPS stabilizes is not dependent on FPS.
// The FPS smooth factor is tuned to always *mostly* converge within a second.
constexpr int FPS_SMOOTH_FACTOR = 5;
const double ratio = MIN(1.0, FPS_SMOOTH_FACTOR * USEC_TO_SEC(ticks_elapsed));
Engine::get_singleton()->_fps = Engine::get_singleton()->_fps * (1 - ratio) + ratio * 1'000'000.0 / ticks_elapsed;
if (frame > 1'000'000) {
// Wait a few seconds before printing FPS, as FPS reporting just after the engine has started is inaccurate.
if (hide_print_fps_attempts == 0) {
if (editor || project_manager) {
@ -4155,7 +4161,6 @@ bool Main::iteration() {
hide_print_fps_attempts--;
}
Engine::get_singleton()->_fps = frames;
performance->set_process_time(USEC_TO_SEC(process_max));
performance->set_physics_process_time(USEC_TO_SEC(physics_process_max));
performance->set_navigation_process_time(USEC_TO_SEC(navigation_process_max));
@ -4163,7 +4168,7 @@ bool Main::iteration() {
physics_process_max = 0;
navigation_process_max = 0;
frame %= 1000000;
frame %= 1'000'000;
frames = 0;
}

View File

@ -150,7 +150,7 @@ String Performance::get_monitor_name(Monitor p_monitor) const {
double Performance::get_monitor(Monitor p_monitor) const {
switch (p_monitor) {
case TIME_FPS:
return Engine::get_singleton()->get_frames_per_second();
return Math::round(Engine::get_singleton()->get_frames_per_second());
case TIME_PROCESS:
return _process_time;
case TIME_PHYSICS_PROCESS: