Skip to content

Commit

Permalink
Cleanup Android input on render thread settings
Browse files Browse the repository at this point in the history
Follow up to godotengine#93933
Clean up the set of settings use to control whether Android input should be dispatched on the render thread.

Addresses comments in godotengine#93933 (comment)
  • Loading branch information
m4gr3d committed Jul 9, 2024
1 parent f3af22b commit 5e59819
Show file tree
Hide file tree
Showing 17 changed files with 64 additions and 90 deletions.
10 changes: 5 additions & 5 deletions core/input/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,7 @@ void Input::parse_input_event(const Ref<InputEvent> &p_event) {
if (buffered_events.is_empty() || !buffered_events.back()->get()->accumulate(p_event)) {
buffered_events.push_back(p_event);
}
} else if (use_input_buffering) {
} else if (agile_input_event_flushing) {
buffered_events.push_back(p_event);
} else {
_parse_input_event_impl(p_event, false);
Expand Down Expand Up @@ -1053,12 +1053,12 @@ void Input::flush_buffered_events() {
}
}

bool Input::is_using_input_buffering() {
return use_input_buffering;
bool Input::is_agile_input_event_flushing() {
return agile_input_event_flushing;
}

void Input::set_use_input_buffering(bool p_enable) {
use_input_buffering = p_enable;
void Input::set_agile_input_event_flushing(bool p_enable) {
agile_input_event_flushing = p_enable;
}

void Input::set_use_accumulated_input(bool p_enable) {
Expand Down
6 changes: 3 additions & 3 deletions core/input/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class Input : public Object {

bool emulate_touch_from_mouse = false;
bool emulate_mouse_from_touch = false;
bool use_input_buffering = false;
bool agile_input_event_flushing = false;
bool use_accumulated_input = true;

int mouse_from_touch_index = -1;
Expand Down Expand Up @@ -367,8 +367,8 @@ class Input : public Object {
void flush_frame_parsed_events();
#endif
void flush_buffered_events();
bool is_using_input_buffering();
void set_use_input_buffering(bool p_enable);
bool is_agile_input_event_flushing();
void set_agile_input_event_flushing(bool p_enable);
void set_use_accumulated_input(bool p_enable);
bool is_using_accumulated_input();

Expand Down
10 changes: 10 additions & 0 deletions doc/classes/EditorSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,16 @@
The path to the directory containing the Open Image Denoise (OIDN) executable, used optionally for denoising lightmaps. It can be downloaded from [url=https://www.openimagedenoise.org/downloads.html]openimagedenoise.org[/url].
To enable this feature for your specific project, use [member ProjectSettings.rendering/lightmapping/denoising/denoiser].
</member>
<member name="input/buffering/agile_event_flushing" type="bool" setter="" getter="">
If [code]true[/code], input events will be flushed just before every idle and physics frame.
If [code]false[/code], these events will be flushed only once per process frame, between iterations of the engine.
Enabling this setting can greatly improve input responsiveness, especially in devices that struggle to run at the project's intended frame rate.
</member>
<member name="input/buffering/use_accumulated_input" type="bool" setter="" getter="">
If [code]true[/code], similar input events sent by the operating system are accumulated. When input accumulation is enabled, all input events generated during a frame will be merged and emitted when the frame is done rendering. Therefore, this limits the number of input method calls per second to the rendering FPS.
Input accumulation can be disabled to get slightly more precise/reactive input at the cost of increased CPU usage.
[b]Note:[/b] Input accumulation is [i]enabled[/i] by default.
</member>
<member name="interface/editor/accept_dialog_cancel_ok_buttons" type="int" setter="" getter="">
How to position the Cancel and OK buttons in the editor's [AcceptDialog]s. Different platforms have different standard behaviors for this, which can be overridden using this setting. This is useful if you use Godot both on Windows and macOS/Linux and your Godot muscle memory is stronger than your OS specific one.
- [b]Auto[/b] follows the platform convention: Cancel first on macOS and Linux, OK first on Windows.
Expand Down
6 changes: 0 additions & 6 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1394,12 +1394,6 @@
Enabling this can greatly improve the responsiveness to input, specially in devices that need to run multiple physics frames per visible (process) frame, because they can't run at the target frame rate.
[b]Note:[/b] Currently implemented only on Android.
</member>
<member name="input_devices/buffering/android/use_accumulated_input" type="bool" setter="" getter="" default="true">
If [code]true[/code], multiple input events will be accumulated into a single input event when possible.
</member>
<member name="input_devices/buffering/android/use_input_buffering" type="bool" setter="" getter="" default="true">
If [code]true[/code], input events will be buffered prior to being dispatched.
</member>
<member name="input_devices/compatibility/legacy_just_pressed_behavior" type="bool" setter="" getter="" default="false">
If [code]true[/code], [method Input.is_action_just_pressed] and [method Input.is_action_just_released] will only return [code]true[/code] if the action is still in the respective state, i.e. an action that is pressed [i]and[/i] released on the same frame will be missed.
If [code]false[/code], no input will be lost.
Expand Down
9 changes: 8 additions & 1 deletion editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6431,7 +6431,6 @@ EditorNode::EditorNode() {
// No scripting by default if in editor (except for tool).
ScriptServer::set_scripting_enabled(false);

Input::get_singleton()->set_use_accumulated_input(true);
if (!DisplayServer::get_singleton()->is_touchscreen_available()) {
// Only if no touchscreen ui hint, disable emulation just in case.
Input::get_singleton()->set_emulate_touch_from_mouse(false);
Expand Down Expand Up @@ -6480,6 +6479,14 @@ EditorNode::EditorNode() {
SurfaceUpgradeTool::get_singleton()->begin_upgrade();
}

{
bool agile_input_event_flushing = EDITOR_GET("input/buffering/agile_event_flushing");
bool use_accumulated_input = EDITOR_GET("input/buffering/use_accumulated_input");

Input::get_singleton()->set_agile_input_event_flushing(agile_input_event_flushing);
Input::get_singleton()->set_use_accumulated_input(use_accumulated_input);
}

{
int display_scale = EDITOR_GET("interface/editor/display_scale");

Expand Down
8 changes: 3 additions & 5 deletions editor/editor_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,11 +474,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/vsync_mode", 1, "Disabled,Enabled,Adaptive,Mailbox")
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/update_continuously", false, "")

#ifdef ANDROID_ENABLED
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/android/use_accumulated_input", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/android/use_input_buffering", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
#endif

// Inspector
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/inspector/max_array_dictionary_items_per_page", 20, "10,100,1")
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/show_low_level_opentype_features", false, "")
Expand Down Expand Up @@ -866,6 +861,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {

/* Extra config */

EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "input/buffering/agile_event_flushing", false, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "input/buffering/use_accumulated_input", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)

// TRANSLATORS: Project Manager here refers to the tool used to create/manage Godot projects.
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "project_manager/sorting_order", 0, "Last Edited,Name,Path")
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "project_manager/directory_naming_convention", 1, "No convention,kebab-case,snake_case,camelCase,PascalCase,Title Case")
Expand Down
8 changes: 8 additions & 0 deletions editor/project_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,14 @@ ProjectManager::ProjectManager() {
}
EditorSettings::get_singleton()->set_optimize_save(false); // Just write settings as they come.

{
bool agile_input_event_flushing = EDITOR_GET("input/buffering/agile_event_flushing");
bool use_accumulated_input = EDITOR_GET("input/buffering/use_accumulated_input");

Input::get_singleton()->set_agile_input_event_flushing(agile_input_event_flushing);
Input::get_singleton()->set_use_accumulated_input(use_accumulated_input);
}

int display_scale = EDITOR_GET("interface/editor/display_scale");

switch (display_scale) {
Expand Down
16 changes: 4 additions & 12 deletions main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2514,7 +2514,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
memdelete(message_queue);
}

OS::get_singleton()->benchmark_end_measure("Startup", "Core");
OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup");

#if defined(STEAMAPI_ENABLED)
Expand Down Expand Up @@ -2926,7 +2925,8 @@ Error Main::setup2(bool p_show_boot_logo) {

Input *id = Input::get_singleton();
if (id) {
agile_input_event_flushing = GLOBAL_DEF("input_devices/buffering/agile_event_flushing", false);
bool agile_input_event_flushing = GLOBAL_DEF("input_devices/buffering/agile_event_flushing", false);
id->set_agile_input_event_flushing(agile_input_event_flushing);

if (bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_touch_from_mouse", false)) &&
!(editor || project_manager)) {
Expand All @@ -2939,8 +2939,6 @@ Error Main::setup2(bool p_show_boot_logo) {
id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true)));
}

GLOBAL_DEF("input_devices/buffering/android/use_accumulated_input", true);
GLOBAL_DEF("input_devices/buffering/android/use_input_buffering", true);
GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false);
GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1);
Expand Down Expand Up @@ -3977,7 +3975,6 @@ uint32_t Main::hide_print_fps_attempts = 3;
uint32_t Main::frame = 0;
bool Main::force_redraw_requested = false;
int Main::iterating = 0;
bool Main::agile_input_event_flushing = false;

bool Main::is_iterating() {
return iterating > 0;
Expand Down Expand Up @@ -4038,7 +4035,7 @@ bool Main::iteration() {
NavigationServer3D::get_singleton()->sync();

for (int iters = 0; iters < advance.physics_steps; ++iters) {
if (Input::get_singleton()->is_using_input_buffering() && agile_input_event_flushing) {
if (Input::get_singleton()->is_agile_input_event_flushing()) {
Input::get_singleton()->flush_buffered_events();
}

Expand Down Expand Up @@ -4095,7 +4092,7 @@ bool Main::iteration() {
Engine::get_singleton()->_in_physics = false;
}

if (Input::get_singleton()->is_using_input_buffering() && agile_input_event_flushing) {
if (Input::get_singleton()->is_agile_input_event_flushing()) {
Input::get_singleton()->flush_buffered_events();
}

Expand Down Expand Up @@ -4167,11 +4164,6 @@ bool Main::iteration() {

iterating--;

// Needed for OSs using input buffering regardless accumulation (like Android)
if (Input::get_singleton()->is_using_input_buffering() && !agile_input_event_flushing) {
Input::get_singleton()->flush_buffered_events();
}

if (movie_writer) {
movie_writer->add_frame();
}
Expand Down
1 change: 0 additions & 1 deletion main/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ class Main {
static uint32_t frame;
static bool force_redraw_requested;
static int iterating;
static bool agile_input_event_flushing;

public:
static bool is_cmdline_tool();
Expand Down
1 change: 0 additions & 1 deletion platform/android/display_server_android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,6 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis
#endif

Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
Input::get_singleton()->set_use_input_buffering(true); // Needed because events will come directly from the UI thread

r_error = OK;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,13 @@ open class GodotEditor : GodotActivity() {
val longPressEnabled = enableLongPressGestures()
val panScaleEnabled = enablePanAndScaleGestures()

val useInputBuffering = useInputBuffering()
val useAccumulatedInput = useAccumulatedInput()
GodotLib.updateInputDispatchSettings(useAccumulatedInput, useInputBuffering)

checkForProjectPermissionsToEnable()

runOnUiThread {
// Enable long press, panning and scaling gestures
godotFragment?.godot?.renderView?.inputHandler?.apply {
enableLongPress(longPressEnabled)
enablePanningAndScalingGestures(panScaleEnabled)
enableInputDispatchToRenderThread(!useInputBuffering && !useAccumulatedInput)
}
}
}
Expand Down Expand Up @@ -279,13 +274,6 @@ open class GodotEditor : GodotActivity() {
protected open fun enablePanAndScaleGestures() =
java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/touchscreen/enable_pan_and_scale_gestures"))

/**
* Use input buffering for the Godot Android editor.
*/
protected open fun useInputBuffering() = java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/editor/android/use_input_buffering"))

protected open fun useAccumulatedInput() = java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/editor/android/use_accumulated_input"))

/**
* Whether we should launch the new godot instance in an adjacent window
* @see https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@ class GodotGame : GodotEditor() {

override fun enablePanAndScaleGestures() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))

override fun useInputBuffering() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_input_buffering"))

override fun useAccumulatedInput() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_accumulated_input"))

override fun checkForProjectPermissionsToEnable() {
// Nothing to do.. by the time we get here, the project permissions will have already
// been requested by the Editor window.
Expand Down
33 changes: 13 additions & 20 deletions platform/android/java/lib/src/org/godotengine/godot/Godot.kt
Original file line number Diff line number Diff line change
Expand Up @@ -628,26 +628,19 @@ class Godot(private val context: Context) : SensorEventListener {
private fun onGodotSetupCompleted() {
Log.v(TAG, "OnGodotSetupCompleted")

if (!isEditorBuild()) {
// These properties are defined after Godot setup completion, so we retrieve them here.
val longPressEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
val panScaleEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
val rotaryInputAxisValue = GodotLib.getGlobal("input_devices/pointing/android/rotary_input_scroll_axis")

val useInputBuffering = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_input_buffering"))
val useAccumulatedInput = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/buffering/android/use_accumulated_input"))
GodotLib.updateInputDispatchSettings(useAccumulatedInput, useInputBuffering)

runOnUiThread {
renderView?.inputHandler?.apply {
enableLongPress(longPressEnabled)
enablePanningAndScalingGestures(panScaleEnabled)
enableInputDispatchToRenderThread(!useInputBuffering && !useAccumulatedInput)
try {
setRotaryInputAxis(Integer.parseInt(rotaryInputAxisValue))
} catch (e: NumberFormatException) {
Log.w(TAG, e)
}
// These properties are defined after Godot setup completion, so we retrieve them here.
val longPressEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_long_press_as_right_click"))
val panScaleEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
val rotaryInputAxisValue = GodotLib.getGlobal("input_devices/pointing/android/rotary_input_scroll_axis")

runOnUiThread {
renderView?.inputHandler?.apply {
enableLongPress(longPressEnabled)
enablePanningAndScalingGestures(panScaleEnabled)
try {
setRotaryInputAxis(Integer.parseInt(rotaryInputAxisValue))
} catch (e: NumberFormatException) {
Log.w(TAG, e)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,8 @@ public static native boolean initialize(Activity activity,
public static native void onRendererPaused();

/**
* Invoked on the GL thread to update the input dispatch settings
* @param useAccumulatedInput True to use accumulated input, false otherwise
* @param useInputBuffering True to use input buffering, false otherwise
* @return true if input must be dispatched from the render thread. If false, input is
* dispatched from the UI thread.
*/
public static native void updateInputDispatchSettings(boolean useAccumulatedInput, boolean useInputBuffering);
public static native boolean shouldDispatchInputToRenderThread();
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {

private int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS;

private boolean dispatchInputToRenderThread = false;

public GodotInputHandler(GodotRenderView godotView) {
final Context context = godotView.getView().getContext();
mRenderView = godotView;
Expand Down Expand Up @@ -110,20 +108,12 @@ public void enablePanningAndScalingGestures(boolean enable) {
this.godotGestureHandler.setPanningAndScalingEnabled(enable);
}

/**
* Specifies whether input should be dispatch on the UI thread or on the Render thread.
* @param enable true to dispatch input on the Render thread, false to dispatch input on the UI thread
*/
public void enableInputDispatchToRenderThread(boolean enable) {
this.dispatchInputToRenderThread = enable;
}

/**
* @return true if input must be dispatched from the render thread. If false, input is
* dispatched from the UI thread.
*/
private boolean shouldDispatchInputToRenderThread() {
return dispatchInputToRenderThread;
return GodotLib.shouldDispatchInputToRenderThread();
}

/**
Expand Down
9 changes: 5 additions & 4 deletions platform/android/java_godot_lib_jni.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,10 +550,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIE
}
}

JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_updateInputDispatchSettings(JNIEnv *env, jclass clazz, jboolean p_use_accumulated_input, jboolean p_use_input_buffering) {
if (Input::get_singleton()) {
Input::get_singleton()->set_use_accumulated_input(p_use_accumulated_input);
Input::get_singleton()->set_use_input_buffering(p_use_input_buffering);
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_shouldDispatchInputToRenderThread(JNIEnv *env, jclass clazz) {
Input *input = Input::get_singleton();
if (input) {
return !input->is_agile_input_event_flushing();
}
return false;
}
}
2 changes: 1 addition & 1 deletion platform/android/java_godot_lib_jni.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResu
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_updateInputDispatchSettings(JNIEnv *env, jclass clazz, jboolean p_use_accumulated_input, jboolean p_use_input_buffering);
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_shouldDispatchInputToRenderThread(JNIEnv *env, jclass clazz);
}

#endif // JAVA_GODOT_LIB_JNI_H

0 comments on commit 5e59819

Please sign in to comment.