Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 86 additions & 1 deletion core/os/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,88 @@ Input *Input::get_singleton() {
return singleton;
}

void Input::flush_buffered_events_post_frame() {
if (data.use_legacy_flushing) {
// Matches old logic - if buffering, but not agile.
if (data.buffering_mode == BUFFERING_MODE_FRAME) {
force_flush_buffered_events();
}
}
}

void Input::flush_buffered_events() {
// This function was called in hacky fashion
// all over the shop on different platforms,
// and will interfere with agile input,
// so is now a NOOP, unless the user chooses
// legacy flushing.
// Using legacy flushing will muck up
// agile input, so is not recommended,
// and is only included temporarily to
// allow fixing bugs in beta.
if (data.use_legacy_flushing) {
force_flush_buffered_events();
}
}

void Input::set_use_accumulated_input(bool p_enable) {
data.use_accumulated_input = p_enable;
_update_buffering_mode();
}

bool Input::is_using_accumulated_input() const {
return data.use_accumulated_input;
}

void Input::set_use_input_buffering(bool p_enable) {
data.use_buffering = p_enable;
_update_buffering_mode();
}

bool Input::is_using_input_buffering() const {
return data.use_buffering;
}

void Input::set_use_agile_flushing(bool p_enable) {
data.use_agile = p_enable;
_update_buffering_mode();
}

bool Input::is_using_agile_flushing() const {
return data.use_agile;
}

void Input::set_use_legacy_flushing(bool p_enable) {
data.use_legacy_flushing = p_enable;
}

void Input::set_has_input_thread(bool p_has_thread) {
data.has_input_thread = p_has_thread;
_update_buffering_mode();
}

void Input::_update_buffering_mode() {
// Logic here may appear confusing but it is historical,
// and to prevent compat breaking.
// Accumulated input was added in such a way as to override
// the use_buffering setting.
// i.e. Buffering is used if use_buffering is false, but accumulated is true,
// as accumulating needs the previous event in order to work.

// Additionally for agile input, it currently only makes sense to activate when
// the platform has a separate input thread, otherwise the extra processing
// on flush on each tick just wastes CPU.
if (data.use_accumulated_input || data.use_buffering) {
if (data.use_agile && data.has_input_thread) {
data.buffering_mode = BUFFERING_MODE_AGILE;
} else {
data.buffering_mode = BUFFERING_MODE_FRAME;
}
} else {
data.buffering_mode = BUFFERING_MODE_NONE;
}
}

void Input::set_mouse_mode(MouseMode p_mode) {
ERR_FAIL_INDEX((int)p_mode, 5);
OS::get_singleton()->set_mouse_mode((OS::MouseMode)p_mode);
Expand Down Expand Up @@ -104,7 +186,10 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event);
ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input);
ClassDB::bind_method(D_METHOD("is_using_accumulated_input"), &Input::is_using_accumulated_input);
ClassDB::bind_method(D_METHOD("flush_buffered_events"), &Input::flush_buffered_events);

// For backward compatibility this calls the force method.
// Use this at your peril as it will mess up agile input any frame it is called.
ClassDB::bind_method(D_METHOD("flush_buffered_events"), &Input::force_flush_buffered_events);

ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_mode"), "set_mouse_mode", "get_mouse_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_accumulated_input"), "set_use_accumulated_input", "is_using_accumulated_input");
Expand Down
56 changes: 51 additions & 5 deletions core/os/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,33 @@ class Input : public Object {
MOUSE_MODE_CONFINED_HIDDEN,
};

// There are three buffering modes.
// * No buffering (immediately process input)
// * or buffering and flush the input every frame,
// * or agile - buffering and flush the input every physics tick and every frame.
// The mode is decided logically in _update_buffering_mode()
// depending on the settings for
// use_accumulated_input, use_buffering, use_agile, and has_input_thread.
enum BufferingMode {
BUFFERING_MODE_NONE,
BUFFERING_MODE_FRAME,
BUFFERING_MODE_AGILE,
};

protected:
struct Data {
BufferingMode buffering_mode = BUFFERING_MODE_FRAME;
bool use_accumulated_input = true;
bool use_buffering = false;
bool use_agile = false;
bool use_legacy_flushing = false;
bool has_input_thread = false;
} data;

bool has_input_thread() const { return data.has_input_thread; }
void _update_buffering_mode();

public:
#undef CursorShape
enum CursorShape {
CURSOR_ARROW,
Expand Down Expand Up @@ -142,11 +169,30 @@ class Input : public Object {
virtual int get_joy_axis_index_from_string(String p_axis) = 0;

virtual void parse_input_event(const Ref<InputEvent> &p_event) = 0;
virtual void flush_buffered_events() = 0;
virtual bool is_using_input_buffering() = 0;
virtual void set_use_input_buffering(bool p_enable) = 0;
virtual bool is_using_accumulated_input() = 0;
virtual void set_use_accumulated_input(bool p_enable) = 0;

// DO NOT call force_flush_buffered_events() in normal course of events,
// as it will break agile input on any frame it is called.
// Instead rely on flush_buffered_events_tick() and
// flush_buffered_events_frame() which are called from
// Main::iteration().
virtual void force_flush_buffered_events() = 0;
virtual void flush_buffered_events_iteration() = 0;
virtual void flush_buffered_events_tick(uint64_t p_tick_timestamp) = 0;
virtual void flush_buffered_events_frame() = 0;
void flush_buffered_events();
void flush_buffered_events_post_frame();

void set_use_accumulated_input(bool p_enable);
bool is_using_accumulated_input() const;

void set_use_input_buffering(bool p_enable);
bool is_using_input_buffering() const;

void set_use_agile_flushing(bool p_enable);
bool is_using_agile_flushing() const;

void set_use_legacy_flushing(bool p_enable);
void set_has_input_thread(bool p_has_thread);

Input();
};
Expand Down
2 changes: 1 addition & 1 deletion core/os/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ class OS {
virtual int get_power_seconds_left();
virtual int get_power_percent_left();

virtual void force_process_input(){};
virtual void force_process_input() {}
bool has_feature(const String &p_feature);

void set_has_server_feature_callback(HasServerFeatureCallback p_callback);
Expand Down
6 changes: 5 additions & 1 deletion doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -654,12 +654,16 @@
Default [InputEventAction] to move up in the UI.
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
</member>
<member name="input_devices/buffering/agile_event_flushing" type="bool" setter="" getter="" default="false">
<member name="input_devices/buffering/agile_event_flushing" type="bool" setter="" getter="" default="true">
If [code]true[/code], key/touch/joystick events will be flushed just before every idle and physics frame.
If [code]false[/code], such events will be flushed only once per idle frame, between iterations of the engine.
Enabling this can greatly improve the responsiveness to input, specially in devices that need to run multiple physics frames per visible (idle) frame, because they can't run at the target frame rate.
[b]Note:[/b] Currently implemented only in Android.
</member>
<member name="input_devices/buffering/legacy_event_flushing" type="bool" setter="" getter="" default="false">
If [code]true[/code], input events will be flushed on demand according to platform, instead of the new platform independent method.
[b]Note:[/b] Enabling this will break agile event flushing, which may reduce input timing resolution on some platforms.
</member>
<member name="input_devices/pointing/emulate_mouse_from_touch" type="bool" setter="" getter="" default="true">
If [code]true[/code], sends mouse input events when tapping or swiping on the touchscreen.
</member>
Expand Down
Loading