Skip to content

Commit

Permalink
Add frame delta smoothing option (4.x)
Browse files Browse the repository at this point in the history
Frame deltas are currently measured by querying the OS timer each frame. This is subject to random error. Frame delta smoothing instead filters the delta read from the OS by replacing it with the refresh rate delta wherever possible.

This PR also contains code to estimate the refresh rate based on the input deltas, without reading the refresh rate from the host OS.

The delta_smooth_enabled setting can also be modified at runtime through OS::, and there is also now a command line setting to override the project setting.
  • Loading branch information
lawnjelly committed May 16, 2023
1 parent ffd32a2 commit 7925670
Show file tree
Hide file tree
Showing 9 changed files with 385 additions and 0 deletions.
12 changes: 12 additions & 0 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,14 @@ int OS::get_low_processor_usage_mode_sleep_usec() const {
return ::OS::get_singleton()->get_low_processor_usage_mode_sleep_usec();
}

void OS::set_delta_smoothing(bool p_enabled) {
::OS::get_singleton()->set_delta_smoothing(p_enabled);
}

bool OS::is_delta_smoothing_enabled() const {
return ::OS::get_singleton()->is_delta_smoothing_enabled();
}

void OS::alert(const String &p_alert, const String &p_title) {
::OS::get_singleton()->alert(p_alert, p_title);
}
Expand Down Expand Up @@ -556,6 +564,9 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_low_processor_usage_mode_sleep_usec", "usec"), &OS::set_low_processor_usage_mode_sleep_usec);
ClassDB::bind_method(D_METHOD("get_low_processor_usage_mode_sleep_usec"), &OS::get_low_processor_usage_mode_sleep_usec);

ClassDB::bind_method(D_METHOD("set_delta_smoothing", "delta_smoothing_enabled"), &OS::set_delta_smoothing);
ClassDB::bind_method(D_METHOD("is_delta_smoothing_enabled"), &OS::is_delta_smoothing_enabled);

ClassDB::bind_method(D_METHOD("get_processor_count"), &OS::get_processor_count);
ClassDB::bind_method(D_METHOD("get_processor_name"), &OS::get_processor_name);

Expand Down Expand Up @@ -631,6 +642,7 @@ void OS::_bind_methods() {

ADD_PROPERTY(PropertyInfo(Variant::BOOL, "low_processor_usage_mode"), "set_low_processor_usage_mode", "is_in_low_processor_usage_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "low_processor_usage_mode_sleep_usec"), "set_low_processor_usage_mode_sleep_usec", "get_low_processor_usage_mode_sleep_usec");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "delta_smoothing"), "set_delta_smoothing", "is_delta_smoothing_enabled");

// Those default values need to be specified for the docs generator,
// to avoid using values from the documentation writer's own OS instance.
Expand Down
3 changes: 3 additions & 0 deletions core/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ class OS : public Object {
void set_low_processor_usage_mode_sleep_usec(int p_usec);
int get_low_processor_usage_mode_sleep_usec() const;

void set_delta_smoothing(bool p_enabled);
bool is_delta_smoothing_enabled() const;

void alert(const String &p_alert, const String &p_title = "ALERT!");
void crash(const String &p_message);

Expand Down
8 changes: 8 additions & 0 deletions core/os/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ int OS::get_low_processor_usage_mode_sleep_usec() const {
return low_processor_usage_mode_sleep_usec;
}

void OS::set_delta_smoothing(bool p_enabled) {
_delta_smoothing_enabled = p_enabled;
}

bool OS::is_delta_smoothing_enabled() const {
return _delta_smoothing_enabled;
}

String OS::get_executable_path() const {
return _execpath;
}
Expand Down
4 changes: 4 additions & 0 deletions core/os/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class OS {
bool _keep_screen_on = true; // set default value to true, because this had been true before godot 2.0.
bool low_processor_usage_mode = false;
int low_processor_usage_mode_sleep_usec = 10000;
bool _delta_smoothing_enabled = false;
bool _verbose_stdout = false;
bool _debug_stdout = false;
String _local_clipboard;
Expand Down Expand Up @@ -154,6 +155,9 @@ class OS {
virtual void set_low_processor_usage_mode_sleep_usec(int p_usec);
virtual int get_low_processor_usage_mode_sleep_usec() const;

void set_delta_smoothing(bool p_enabled);
bool is_delta_smoothing_enabled() const;

virtual Vector<String> get_system_fonts() const { return Vector<String>(); };
virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return String(); };
virtual Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return Vector<String>(); };
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/OS.xml
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,9 @@
</method>
</methods>
<members>
<member name="delta_smoothing" type="bool" setter="set_delta_smoothing" getter="is_delta_smoothing_enabled" default="true">
If [code]true[/code], the engine filters the time delta measured between each frame, and attempts to compensate for random variation. This will only operate on systems where V-Sync is active.
</member>
<member name="low_processor_usage_mode" type="bool" setter="set_low_processor_usage_mode" getter="is_in_low_processor_usage_mode" default="false">
If [code]true[/code], the engine optimizes for low processor usage by only refreshing the screen if needed. Can improve battery consumption on mobile.
</member>
Expand Down
5 changes: 5 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@
<member name="application/config/windows_native_icon" type="String" setter="" getter="" default="&quot;&quot;">
Icon set in [code].ico[/code] format used on Windows to set the game's icon. This is done automatically on start by calling [method DisplayServer.set_native_icon].
</member>
<member name="application/run/delta_smoothing" type="bool" setter="" getter="" default="true">
Time samples for frame deltas are subject to random variation introduced by the platform, even when frames are displayed at regular intervals thanks to V-Sync. This can lead to jitter. Delta smoothing can often give a better result by filtering the input deltas to correct for minor fluctuations from the refresh rate.
[b]Note:[/b] Delta smoothing is only attempted when [member display/window/vsync/vsync_mode] is set to [code]enabled[/code], as it does not work well without V-Sync.
It may take several seconds at a stable frame rate before the smoothing is initially activated. It will only be active on machines where performance is adequate to render frames at the refresh rate.
</member>
<member name="application/run/disable_stderr" type="bool" setter="" getter="" default="false">
If [code]true[/code], disables printing to standard error. If [code]true[/code], this also hides error and warning messages printed by [method @GlobalScope.push_error] and [method @GlobalScope.push_warning]. See also [member application/run/disable_stdout].
Changes to this setting will only be applied upon restarting the application.
Expand Down
30 changes: 30 additions & 0 deletions main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" --disable-render-loop Disable render loop so rendering only occurs when called explicitly from script.\n");
OS::get_singleton()->print(" --disable-crash-handler Disable crash handler when supported by the platform code.\n");
OS::get_singleton()->print(" --fixed-fps <fps> Force a fixed number of frames per second. This setting disables real-time synchronization.\n");
OS::get_singleton()->print(" --delta-smoothing <enable> Enable or disable frame delta smoothing ['enable', 'disable'].\n");
OS::get_singleton()->print(" --print-fps Print the frames per second to the stdout.\n");
OS::get_singleton()->print("\n");

Expand Down Expand Up @@ -791,6 +792,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
Vector<String> breakpoints;
bool use_custom_res = true;
bool force_res = false;
bool delta_smoothing_override = false;

String default_renderer = "";
String default_renderer_mobile = "";
Expand Down Expand Up @@ -1000,6 +1002,29 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->print("Missing tablet driver argument, aborting.\n");
goto error;
}
} else if (I->get() == "--delta-smoothing") {
if (I->next()) {
String string = I->next()->get();
bool recognised = false;
if (string == "enable") {
OS::get_singleton()->set_delta_smoothing(true);
delta_smoothing_override = true;
recognised = true;
}
if (string == "disable") {
OS::get_singleton()->set_delta_smoothing(false);
delta_smoothing_override = false;
recognised = true;
}
if (!recognised) {
OS::get_singleton()->print("Delta-smoothing argument not recognised, aborting.\n");
goto error;
}
N = I->next()->next();
} else {
OS::get_singleton()->print("Missing delta-smoothing argument, aborting.\n");
goto error;
}
} else if (I->get() == "--single-window") { // force single window

single_window = true;
Expand Down Expand Up @@ -1908,6 +1933,11 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(
GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "0,33200,1,or_greater"), 6900)); // Roughly 144 FPS

GLOBAL_DEF("application/run/delta_smoothing", true);
if (!delta_smoothing_override) {
OS::get_singleton()->set_delta_smoothing(GLOBAL_GET("application/run/delta_smoothing"));
}

GLOBAL_DEF("display/window/ios/allow_high_refresh_rate", true);
GLOBAL_DEF("display/window/ios/hide_home_indicator", true);
GLOBAL_DEF("display/window/ios/hide_status_bar", true);
Expand Down
Loading

0 comments on commit 7925670

Please sign in to comment.