Rename Engine.target_fps and associated project setting to max_fps

This makes the setting easier to find, as research has found there are
numerous use cases to limiting FPS. This also improves documentation
related to the Engine property and project setting.

The project setting also works in projects exported in release mode,
so its location in the `debug/` section was misleading.
This commit is contained in:
Hugo Locurcio 2022-09-22 22:15:07 +02:00
parent 1371a97acf
commit 1c6c72caf1
No known key found for this signature in database
GPG key ID: 39E8F8BE30B0A49C
9 changed files with 44 additions and 36 deletions

View file

@ -59,12 +59,12 @@ double Engine::get_physics_jitter_fix() const {
return physics_jitter_fix;
}
void Engine::set_target_fps(int p_fps) {
_target_fps = p_fps > 0 ? p_fps : 0;
void Engine::set_max_fps(int p_fps) {
_max_fps = p_fps > 0 ? p_fps : 0;
}
int Engine::get_target_fps() const {
return _target_fps;
int Engine::get_max_fps() const {
return _max_fps;
}
uint64_t Engine::get_frames_drawn() {

View file

@ -60,7 +60,7 @@ private:
int ips = 60;
double physics_jitter_fix = 0.5;
double _fps = 1;
int _target_fps = 0;
int _max_fps = 0;
double _time_scale = 1.0;
uint64_t _physics_frames = 0;
double _physics_interpolation_fraction = 0.0f;
@ -96,8 +96,8 @@ public:
void set_physics_jitter_fix(double p_threshold);
double get_physics_jitter_fix() const;
virtual void set_target_fps(int p_fps);
virtual int get_target_fps() const;
virtual void set_max_fps(int p_fps);
virtual int get_max_fps() const;
virtual double get_frames_per_second() const { return _fps; }

View file

@ -1483,12 +1483,12 @@ double Engine::get_physics_interpolation_fraction() const {
return ::Engine::get_singleton()->get_physics_interpolation_fraction();
}
void Engine::set_target_fps(int p_fps) {
::Engine::get_singleton()->set_target_fps(p_fps);
void Engine::set_max_fps(int p_fps) {
::Engine::get_singleton()->set_max_fps(p_fps);
}
int Engine::get_target_fps() const {
return ::Engine::get_singleton()->get_target_fps();
int Engine::get_max_fps() const {
return ::Engine::get_singleton()->get_max_fps();
}
double Engine::get_frames_per_second() const {
@ -1625,8 +1625,8 @@ void Engine::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_physics_jitter_fix", "physics_jitter_fix"), &Engine::set_physics_jitter_fix);
ClassDB::bind_method(D_METHOD("get_physics_jitter_fix"), &Engine::get_physics_jitter_fix);
ClassDB::bind_method(D_METHOD("get_physics_interpolation_fraction"), &Engine::get_physics_interpolation_fraction);
ClassDB::bind_method(D_METHOD("set_target_fps", "target_fps"), &Engine::set_target_fps);
ClassDB::bind_method(D_METHOD("get_target_fps"), &Engine::get_target_fps);
ClassDB::bind_method(D_METHOD("set_max_fps", "max_fps"), &Engine::set_max_fps);
ClassDB::bind_method(D_METHOD("get_max_fps"), &Engine::get_max_fps);
ClassDB::bind_method(D_METHOD("set_time_scale", "time_scale"), &Engine::set_time_scale);
ClassDB::bind_method(D_METHOD("get_time_scale"), &Engine::get_time_scale);
@ -1669,7 +1669,7 @@ void Engine::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_error_messages"), "set_print_error_messages", "is_printing_error_messages");
ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_ticks_per_second"), "set_physics_ticks_per_second", "get_physics_ticks_per_second");
ADD_PROPERTY(PropertyInfo(Variant::INT, "target_fps"), "set_target_fps", "get_target_fps");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_fps"), "set_max_fps", "get_max_fps");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_scale"), "set_time_scale", "get_time_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "physics_jitter_fix"), "set_physics_jitter_fix", "get_physics_jitter_fix");
}

View file

@ -488,8 +488,8 @@ public:
double get_physics_jitter_fix() const;
double get_physics_interpolation_fraction() const;
void set_target_fps(int p_fps);
int get_target_fps() const;
void set_max_fps(int p_fps);
int get_max_fps() const;
double get_frames_per_second() const;
uint64_t get_physics_frames() const;

View file

@ -511,10 +511,10 @@ void OS::add_frame_delay(bool p_can_draw) {
if (is_in_low_processor_usage_mode() || !p_can_draw) {
dynamic_delay = get_low_processor_usage_mode_sleep_usec();
}
const int target_fps = Engine::get_singleton()->get_target_fps();
if (target_fps > 0 && !Engine::get_singleton()->is_editor_hint()) {
const int max_fps = Engine::get_singleton()->get_max_fps();
if (max_fps > 0 && !Engine::get_singleton()->is_editor_hint()) {
// Override the low processor usage mode sleep delay if the target FPS is lower.
dynamic_delay = MAX(dynamic_delay, (uint64_t)(1000000 / target_fps));
dynamic_delay = MAX(dynamic_delay, (uint64_t)(1000000 / max_fps));
}
if (dynamic_delay > 0) {

View file

@ -261,12 +261,20 @@
</method>
</methods>
<members>
<member name="max_fps" type="int" setter="set_max_fps" getter="get_max_fps" default="0">
The maximum number of frames per second that can be rendered. A value of [code]0[/code] means "no limit". The actual number of frames per second may still be below this value if the CPU or GPU cannot keep up with the project logic and rendering.
Limiting the FPS can be useful to reduce system power consumption, which reduces heat and noise emissions (and improves battery life on mobile devices).
If [member ProjectSettings.display/window/vsync/vsync_mode] is [code]Enabled[/code] or [code]Adaptive[/code], it takes precedence and the forced FPS number cannot exceed the monitor's refresh rate.
If [member ProjectSettings.display/window/vsync/vsync_mode] is [code]Enabled[/code], on monitors with variable refresh rate enabled (G-Sync/FreeSync), using a FPS limit a few frames lower than the monitor's refresh rate will [url=https://blurbusters.com/howto-low-lag-vsync-on/]reduce input lag while avoiding tearing[/url].
If [member ProjectSettings.display/window/vsync/vsync_mode] is [code]Disabled[/code], limiting the FPS to a high value that can be consistently reached on the system can reduce input lag compared to an uncapped framerate. Since this works by ensuring the GPU load is lower than 100%, this latency reduction is only effective in GPU-bottlenecked scenarios, not CPU-bottlenecked scenarios.
See also [member physics_ticks_per_second] and [member ProjectSettings.application/run/max_fps].
</member>
<member name="physics_jitter_fix" type="float" setter="set_physics_jitter_fix" getter="get_physics_jitter_fix" default="0.5">
Controls how much physics ticks are synchronized with real time. For 0 or less, the ticks are synchronized. Such values are recommended for network games, where clock synchronization matters. Higher values cause higher deviation of the in-game clock and real clock but smooth out framerate jitters. The default value of 0.5 should be fine for most; values above 2 could cause the game to react to dropped frames with a noticeable delay and are not recommended.
[b]Note:[/b] For best results, when using a custom physics interpolation solution, the physics jitter fix should be disabled by setting [member physics_jitter_fix] to [code]0[/code].
</member>
<member name="physics_ticks_per_second" type="int" setter="set_physics_ticks_per_second" getter="get_physics_ticks_per_second" default="60">
The number of fixed iterations per second. This controls how often physics simulation and [method Node._physics_process] methods are run. This value should generally always be set to [code]60[/code] or above, as Godot doesn't interpolate the physics step. As a result, values lower than [code]60[/code] will look stuttery. This value can be increased to make input more reactive or work around collision tunneling issues, but keep in mind doing so will increase CPU usage. See also [member target_fps] and [member ProjectSettings.physics/common/physics_ticks_per_second].
The number of fixed iterations per second. This controls how often physics simulation and [method Node._physics_process] methods are run. This value should generally always be set to [code]60[/code] or above, as Godot doesn't interpolate the physics step. As a result, values lower than [code]60[/code] will look stuttery. This value can be increased to make input more reactive or work around collision tunneling issues, but keep in mind doing so will increase CPU usage. See also [member max_fps] and [member ProjectSettings.physics/common/physics_ticks_per_second].
[b]Note:[/b] Only 8 physics ticks may be simulated per rendered frame at most. If more than 8 physics ticks have to be simulated per rendered frame to keep up with rendering, the game will appear to slow down (even if [code]delta[/code] is used consistently in physics calculations). Therefore, it is recommended not to increase [member physics_ticks_per_second] above 240. Otherwise, the game will slow down when the rendering framerate goes below 30 FPS.
</member>
<member name="print_error_messages" type="bool" setter="set_print_error_messages" getter="is_printing_error_messages" default="true">
@ -274,9 +282,6 @@
[b]Warning:[/b] If you set this to [code]false[/code] anywhere in the project, important error messages may be hidden even if they are emitted from other scripts. If this is set to [code]false[/code] in a [code]@tool[/code] script, this will also impact the editor itself. Do [i]not[/i] report bugs before ensuring error messages are enabled (as they are by default).
[b]Note:[/b] This property does not impact the editor's Errors tab when running a project from the editor.
</member>
<member name="target_fps" type="int" setter="set_target_fps" getter="get_target_fps" default="0">
The desired frames per second. If the hardware cannot keep up, this setting may not be respected. A value of 0 means no limit. See also [member physics_ticks_per_second] and [member ProjectSettings.debug/settings/fps/force_fps].
</member>
<member name="time_scale" type="float" setter="set_time_scale" getter="get_time_scale" default="1.0">
Controls how fast or slow the in-game clock ticks versus the real life one. It defaults to 1.0. A value of 2.0 means the game moves twice as fast as real life, whilst a value of 0.5 means the game moves at half the regular speed.
</member>

View file

@ -276,6 +276,15 @@
<member name="application/run/main_scene" type="String" setter="" getter="" default="&quot;&quot;">
Path to the main scene file that will be loaded when the project runs.
</member>
<member name="application/run/max_fps" type="int" setter="" getter="" default="0">
Maximum number of frames per second allowed. A value of [code]0[/code] means "no limit". The actual number of frames per second may still be below this value if the CPU or GPU cannot keep up with the project logic and rendering.
Limiting the FPS can be useful to reduce system power consumption, which reduces heat and noise emissions (and improves battery life on mobile devices).
If [member display/window/vsync/vsync_mode] is set to [code]Enabled[/code] or [code]Adaptive[/code], it takes precedence and the forced FPS number cannot exceed the monitor's refresh rate.
If [member display/window/vsync/vsync_mode] is [code]Enabled[/code], on monitors with variable refresh rate enabled (G-Sync/FreeSync), using a FPS limit a few frames lower than the monitor's refresh rate will [url=https://blurbusters.com/howto-low-lag-vsync-on/]reduce input lag while avoiding tearing[/url].
If [member display/window/vsync/vsync_mode] is [code]Disabled[/code], limiting the FPS to a high value that can be consistently reached on the system can reduce input lag compared to an uncapped framerate. Since this works by ensuring the GPU load is lower than 100%, this latency reduction is only effective in GPU-bottlenecked scenarios, not CPU-bottlenecked scenarios.
See also [member physics/common/physics_ticks_per_second].
[b]Note:[/b] This property is only read when the project starts. To change the rendering FPS cap at runtime, set [member Engine.max_fps] instead.
</member>
<member name="audio/buses/channel_disable_threshold_db" type="float" setter="" getter="" default="-60.0">
Audio buses will disable automatically when sound goes below a given dB threshold for a given time. This saves CPU as effects assigned to that bus will no longer do any processing.
</member>
@ -454,12 +463,6 @@
<member name="debug/settings/crash_handler/message.editor" type="String" setter="" getter="" default="&quot;Please include this when reporting the bug on: https://github.com/godotengine/godot/issues&quot;">
Editor-only override for [member debug/settings/crash_handler/message]. Does not affect exported projects in debug or release mode.
</member>
<member name="debug/settings/fps/force_fps" type="int" setter="" getter="" default="0">
Maximum number of frames per second allowed. The actual number of frames per second may still be below this value if the game is lagging.
If [member display/window/vsync/vsync_mode] is set to [code]Enabled[/code] or [code]Adaptive[/code], it takes precedence and the forced FPS number cannot exceed the monitor's refresh rate. See also [member physics/common/physics_ticks_per_second].
This setting is therefore mostly relevant for lowering the maximum FPS below VSync, e.g. to perform non-real-time rendering of static frames, or test the project under lag conditions.
[b]Note:[/b] This property is only read when the project starts. To change the rendering FPS cap at runtime, set [member Engine.target_fps] instead.
</member>
<member name="debug/settings/gdscript/max_call_stack" type="int" setter="" getter="" default="1024">
Maximum call stack allowed for debugging GDScript.
</member>
@ -1672,7 +1675,7 @@
[b]Note:[/b] This property is only read when the project starts. To change the physics FPS at runtime, set [member Engine.physics_jitter_fix] instead.
</member>
<member name="physics/common/physics_ticks_per_second" type="int" setter="" getter="" default="60">
The number of fixed iterations per second. This controls how often physics simulation and [method Node._physics_process] methods are run. See also [member debug/settings/fps/force_fps].
The number of fixed iterations per second. This controls how often physics simulation and [method Node._physics_process] methods are run. See also [member application/run/max_fps].
[b]Note:[/b] This property is only read when the project starts. To change the physics FPS at runtime, set [member Engine.physics_ticks_per_second] instead.
[b]Note:[/b] Only 8 physics ticks may be simulated per rendered frame at most. If more than 8 physics ticks have to be simulated per rendered frame to keep up with rendering, the game will appear to slow down (even if [code]delta[/code] is used consistently in physics calculations). Therefore, it is recommended not to increase [member physics/common/physics_ticks_per_second] above 240. Otherwise, the game will slow down when the rendering framerate goes below 30 FPS.
</member>

View file

@ -1775,10 +1775,10 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
PropertyInfo(Variant::INT, "physics/common/physics_ticks_per_second",
PROPERTY_HINT_RANGE, "1,1000,1"));
Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5));
Engine::get_singleton()->set_target_fps(GLOBAL_DEF("debug/settings/fps/force_fps", 0));
ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/fps/force_fps",
Engine::get_singleton()->set_max_fps(GLOBAL_DEF("application/run/max_fps", 0));
ProjectSettings::get_singleton()->set_custom_property_info("application/run/max_fps",
PropertyInfo(Variant::INT,
"debug/settings/fps/force_fps",
"application/run/max_fps",
PROPERTY_HINT_RANGE, "0,1000,1"));
GLOBAL_DEF("debug/settings/stdout/print_fps", false);

View file

@ -77,14 +77,14 @@ void main_loop_callback() {
return; // Skip frame.
}
int target_fps = Engine::get_singleton()->get_target_fps();
if (target_fps > 0) {
int max_fps = Engine::get_singleton()->get_max_fps();
if (max_fps > 0) {
if (current_ticks - target_ticks > 1000000) {
// When the window loses focus, we stop getting updates and accumulate delay.
// For this reason, if the difference is too big, we reset target ticks to the current ticks.
target_ticks = current_ticks;
}
target_ticks += (uint64_t)(1000000 / target_fps);
target_ticks += (uint64_t)(1000000 / max_fps);
}
if (os->main_loop_iterate()) {
emscripten_cancel_main_loop(); // Cancel current loop and wait for cleanup_after_sync.