Switch to input buffering on Android

Key, touch and joystick events will be passed directly from the UI thread to Godot, so they can benefit from agile input flushing.

As another consequence of this new way of passing events, less Java object are created at runtime (`Runnable`), which is good since the garbage collector needs to run less.

`AndroidInputHandler` is introduced to have a smaller cross-thread surface. `main_loop_request_go_back()` is removed in favor just inline calling `notification()` on the `MainLoop` at the most caller's convenience.

Lastly, `get_mouse_position()` and `get_mouse_button_state()` now just call through `InputDefault` to avoid the need of sync of mouse data tracked on the UI thread.
This commit is contained in:
Pedro J. Estébanez 2021-08-04 21:22:11 +02:00
parent 31a0ca2cac
commit 45c2a7159e
9 changed files with 576 additions and 495 deletions

View file

@ -33,7 +33,7 @@
#include "java_godot_wrapper.h"
#include "android/asset_manager_jni.h"
#include "android_keys_utils.h"
#include "android_input_handler.h"
#include "api/java_class_wrapper.h"
#include "api/jni_singleton.h"
#include "core/engine.h"
@ -50,14 +50,16 @@
#include <android/input.h>
#include <unistd.h>
#include <atomic>
static JavaClassWrapper *java_class_wrapper = NULL;
static OS_Android *os_android = NULL;
static AndroidInputHandler *input_handler = NULL;
static GodotJavaWrapper *godot_java = NULL;
static GodotIOJavaWrapper *godot_io_java = NULL;
static bool initialized = false;
static int step = 0;
static std::atomic<int> step; // Shared between UI and render threads
static Size2 new_size;
static Vector3 accelerometer;
@ -153,6 +155,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
if (godot_java) {
delete godot_java;
}
if (input_handler) {
delete input_handler;
}
if (os_android) {
delete os_android;
}
@ -215,9 +220,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en
os_android->set_context_is_16_bits(!p_32_bits);
} else {
// GL context recreated because it was lost; restart app to let it reload everything
step = -1; // Ensure no further steps are attempted and no further events are sent
os_android->main_loop_end();
godot_java->restart(env);
step = -1; // Ensure no further steps are attempted
}
}
}
@ -226,7 +231,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jcl
if (step == 0)
return;
os_android->main_loop_request_go_back();
if (os_android->get_main_loop()) {
os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_GO_BACK_REQUEST);
}
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz) {
@ -237,6 +244,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
// Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id,
// but for Godot purposes, the main thread is the one running the game loop
Main::setup2(Thread::get_caller_id());
input_handler = new AndroidInputHandler();
++step;
return;
}
@ -263,92 +271,101 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
}
void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) {
if (step == 0)
if (step <= 0)
return;
Vector<OS_Android::TouchPos> points;
Vector<AndroidInputHandler::TouchPos> points;
for (int i = 0; i < pointer_count; i++) {
jfloat p[3];
env->GetFloatArrayRegion(positions, i * 3, 3, p);
OS_Android::TouchPos tp;
AndroidInputHandler::TouchPos tp;
tp.pos = Point2(p[1], p[2]);
tp.id = (int)p[0];
points.push_back(tp);
}
if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
os_android->process_mouse_event(ev, buttons_mask, points[0].pos, vertical_factor, horizontal_factor);
input_handler->process_mouse_event(ev, buttons_mask, points[0].pos, vertical_factor, horizontal_factor);
} else {
os_android->process_touch(ev, pointer, points);
input_handler->process_touch(ev, pointer, points);
}
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position) {
touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position);
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask) {
touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask);
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) {
touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask, vertical_factor, horizontal_factor);
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y) {
if (step == 0)
if (step <= 0)
return;
os_android->process_hover(p_type, Point2(p_x, p_y));
input_handler->process_hover(p_type, Point2(p_x, p_y));
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y) {
if (step == 0)
if (step <= 0)
return;
os_android->process_double_tap(p_button_mask, Point2(p_x, p_y));
input_handler->process_double_tap(p_button_mask, Point2(p_x, p_y));
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y) {
if (step == 0)
if (step <= 0)
return;
os_android->process_scroll(Point2(p_x, p_y));
input_handler->process_scroll(Point2(p_x, p_y));
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed) {
if (step == 0)
if (step <= 0)
return;
OS_Android::JoypadEvent jevent;
AndroidInputHandler::JoypadEvent jevent;
jevent.device = p_device;
jevent.type = OS_Android::JOY_EVENT_BUTTON;
jevent.type = AndroidInputHandler::JOY_EVENT_BUTTON;
jevent.index = p_button;
jevent.pressed = p_pressed;
os_android->process_joy_event(jevent);
input_handler->process_joy_event(jevent);
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value) {
if (step == 0)
if (step <= 0)
return;
OS_Android::JoypadEvent jevent;
AndroidInputHandler::JoypadEvent jevent;
jevent.device = p_device;
jevent.type = OS_Android::JOY_EVENT_AXIS;
jevent.type = AndroidInputHandler::JOY_EVENT_AXIS;
jevent.index = p_axis;
jevent.value = p_value;
os_android->process_joy_event(jevent);
input_handler->process_joy_event(jevent);
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y) {
if (step == 0)
if (step <= 0)
return;
OS_Android::JoypadEvent jevent;
AndroidInputHandler::JoypadEvent jevent;
jevent.device = p_device;
jevent.type = OS_Android::JOY_EVENT_HAT;
jevent.type = AndroidInputHandler::JOY_EVENT_HAT;
int hat = 0;
if (p_hat_x != 0) {
if (p_hat_x < 0)
@ -364,21 +381,24 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, j
}
jevent.hat = hat;
os_android->process_joy_event(jevent);
input_handler->process_joy_event(jevent);
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jclass clazz, jint p_device, jboolean p_connected, jstring p_name) {
if (os_android) {
String name = jstring_to_string(p_name, env);
os_android->joy_connection_changed(p_device, p_connected, name);
}
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed) {
if (step == 0)
if (step <= 0)
return;
os_android->process_key_event(p_keycode, p_scancode, p_unicode_char, p_pressed);
String name = jstring_to_string(p_name, env);
input_handler->joy_connection_changed(p_device, p_connected, name);
}
// Called on the UI thread
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed) {
if (step <= 0)
return;
input_handler->process_key_event(p_keycode, p_scancode, p_unicode_char, p_pressed);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) {
@ -398,14 +418,14 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jclass clazz) {
if (step == 0)
if (step <= 0)
return;
os_android->main_loop_focusin();
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jclass clazz) {
if (step == 0)
if (step <= 0)
return;
os_android->main_loop_focusout();
@ -483,7 +503,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResu
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz) {
if (step == 0)
if (step <= 0)
return;
if (os_android->get_main_loop()) {
@ -492,7 +512,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNI
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz) {
if (step == 0)
if (step <= 0)
return;
if (os_android->get_main_loop()) {