diff --git a/core/config/engine.h b/core/config/engine.h index 7e617d8773a..ddb11116d1f 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -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; diff --git a/core/math/math_defs.h b/core/math/math_defs.h index fe53121a038..cb0b9a7067b 100644 --- a/core/math/math_defs.h +++ b/core/math/math_defs.h @@ -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, diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml index ca78054875b..b85bf2c3e49 100644 --- a/doc/classes/Engine.xml +++ b/doc/classes/Engine.xml @@ -64,7 +64,8 @@ - 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]. diff --git a/main/main.cpp b/main/main.cpp index e1d53e7f1be..ea35cadb07b 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -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; } diff --git a/main/performance.cpp b/main/performance.cpp index 8eda697b16d..3c66a40ba2e 100644 --- a/main/performance.cpp +++ b/main/performance.cpp @@ -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: