Stale GL_UNPACK state in gl4es and a missing pre-release GPU sync caused diagonal-banded corruption in the 3D view. Synthetic key press/release must track the real controller button or touch down/up edges, not fire together in one call - fixes a typed movement key sticking forever, then a follow-up that had silently broken A-button jump and on-screen keyboard arrow keys. Also adds force-rebuild.sh for the known stageEngine stale-cache issue.
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
// needs, compiled into the same "main" library as engine/ (see
|
||||
// android/engine-patches/01-android-shared-lib.patch's ANDROID_EXTRA_SOURCES)
|
||||
// - engine/ itself is never modified.
|
||||
#include <ctype.h>
|
||||
#include <jni.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -20,17 +21,30 @@ Java_de_ladkau_questshock_QuestShockActivity_nativeChdir(JNIEnv *env, jclass cla
|
||||
}
|
||||
|
||||
// KeyboardOverlay's hand-built on-screen keyboard (see
|
||||
// KeyboardOverlay.commitPrintableChar()) has no real Android IME session
|
||||
// behind it, so printable characters can't reach the game via Android's
|
||||
// usual IME path - instead this builds and pushes an SDL_TEXTINPUT event
|
||||
// directly, the same event type/shape SDL's own Android backend pushes for
|
||||
// typed text, using only the public SDL_Event/SDL_PushEvent API. The
|
||||
// engine's pump_events() (sdl_events.c) only picks up printable characters
|
||||
// from this event, not from SDL_KEYDOWN (see handleKeyPress() in
|
||||
// KeyboardOverlay.java for why).
|
||||
// KeyboardOverlay.handleCharKeyDown()/handleCharKeyUp()) has no real
|
||||
// Android IME session behind it, so printable characters can't reach the
|
||||
// game via Android's usual IME path - instead this builds and pushes an
|
||||
// SDL_TEXTINPUT event directly, the same event type/shape SDL's own
|
||||
// Android backend pushes for typed text, using only the public
|
||||
// SDL_Event/SDL_PushEvent API. The engine's pump_events() (sdl_events.c)
|
||||
// only picks up printable characters from this event, not from
|
||||
// SDL_KEYDOWN.
|
||||
//
|
||||
// pump_events()'s SDL_TEXTINPUT handling sets sshockKeyStates[code] for
|
||||
// this character and only ever clears it again on a matching SDL_KEYUP.
|
||||
// Any character bound to a continuously-polled motion key (space -> jump,
|
||||
// w -> forward, etc., see MacSrc/Prefs.c's default keybinds.txt) is
|
||||
// polled every physics tick for as long as that state stays set, exactly
|
||||
// like a real held key - so nativeSendPrintableCharDown()/Up() are called
|
||||
// from the key button's own ACTION_DOWN/ACTION_UP (see
|
||||
// KeyboardOverlay.charKeyTouchListener()), not both at once: pushing the
|
||||
// release in the same call as the press would set and clear
|
||||
// sshockKeyStates[code] within a single pump_events() call, before the
|
||||
// game loop's poll (which runs once per frame, before pump_events - see
|
||||
// GameSrc/mainloop.c) ever has a chance to observe the press.
|
||||
JNIEXPORT void JNICALL
|
||||
Java_de_ladkau_questshock_KeyboardOverlay_nativeSendPrintableChar(JNIEnv *env, jclass clazz,
|
||||
jchar c) {
|
||||
Java_de_ladkau_questshock_KeyboardOverlay_nativeSendPrintableCharDown(JNIEnv *env, jclass clazz,
|
||||
jchar c) {
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = SDL_TEXTINPUT;
|
||||
@@ -41,6 +55,23 @@ Java_de_ladkau_questshock_KeyboardOverlay_nativeSendPrintableChar(JNIEnv *env, j
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_de_ladkau_questshock_KeyboardOverlay_nativeSendPrintableCharUp(JNIEnv *env, jclass clazz,
|
||||
jchar c) {
|
||||
SDL_Event releaseEvent;
|
||||
SDL_zero(releaseEvent);
|
||||
releaseEvent.type = SDL_KEYUP;
|
||||
releaseEvent.key.timestamp = SDL_GetTicks();
|
||||
releaseEvent.key.windowID = 0;
|
||||
releaseEvent.key.state = SDL_RELEASED;
|
||||
releaseEvent.key.repeat = 0;
|
||||
// SDL keycodes for printable characters are the character's own
|
||||
// (lowercased) byte value - see sdl_events.c's own comment on this.
|
||||
releaseEvent.key.keysym.sym = (SDL_Keycode)tolower((unsigned char)c);
|
||||
releaseEvent.key.keysym.mod = KMOD_NONE;
|
||||
SDL_PushEvent(&releaseEvent);
|
||||
}
|
||||
|
||||
// KeyboardOverlay's title-bar Close button - the keyboard is a fully
|
||||
// independent overlay quad from the menu launcher (see xr_overlay.h), so
|
||||
// this is the only way to hide it once it's open; the controller's menu
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <android/log.h>
|
||||
|
||||
#include <GLES3/gl3.h>
|
||||
#include <SDL.h>
|
||||
|
||||
#include "xr_mouse.h"
|
||||
#include "xr_overlay.h"
|
||||
@@ -43,6 +44,18 @@ static XrActionSet g_action_set = XR_NULL_HANDLE;
|
||||
static XrAction g_aim_pose_action = XR_NULL_HANDLE;
|
||||
static XrAction g_select_click_action = XR_NULL_HANDLE;
|
||||
static XrAction g_menu_toggle_action = XR_NULL_HANDLE;
|
||||
// WORKAROUND - the on-screen keyboard overlay is currently unusable,
|
||||
// which blocks dismissing the "press Space to continue" tutorial screen
|
||||
// shown right after starting a new game. This lets the right controller's
|
||||
// A button send Space straight into the engine, bypassing the broken
|
||||
// keyboard entirely. Remove once the keyboard overlay works.
|
||||
static XrAction g_space_key_action = XR_NULL_HANDLE;
|
||||
// Right controller's B button -> forward movement (Up arrow, one of the
|
||||
// engine's default M_FORWARD keybinds per MacSrc/Prefs.c). Unlike Space,
|
||||
// Up arrow is non-printable so a plain SDL_KEYDOWN/KEYUP is enough - no
|
||||
// SDL_TEXTINPUT needed (see the space_key injection below for why that
|
||||
// matters).
|
||||
static XrAction g_forward_key_action = XR_NULL_HANDLE;
|
||||
static XrPath g_hand_path[2];
|
||||
static XrSpace g_aim_space[2] = {XR_NULL_HANDLE, XR_NULL_HANDLE};
|
||||
|
||||
@@ -53,6 +66,8 @@ static XrSpace g_aim_space[2] = {XR_NULL_HANDLE, XR_NULL_HANDLE};
|
||||
// be visible at once.
|
||||
static bool g_prev_select[2] = {false, false};
|
||||
static bool g_prev_menu = false;
|
||||
static bool g_prev_space_key = false;
|
||||
static bool g_prev_forward_key = false;
|
||||
static bool g_game_prev_hit[2] = {false, false};
|
||||
static bool g_game_touch_active[2] = {false, false};
|
||||
static bool g_menu_prev_hit[2] = {false, false};
|
||||
@@ -229,6 +244,30 @@ bool xr_input_init(XrInstance instance, XrSession session, int64_t swapchain_for
|
||||
"xrCreateAction(menu_toggle)"))
|
||||
return false;
|
||||
|
||||
// WORKAROUND - right controller's A button, see comment on
|
||||
// g_space_key_action's declaration above.
|
||||
XrActionCreateInfo spaceKeyInfo = {XR_TYPE_ACTION_CREATE_INFO};
|
||||
strncpy(spaceKeyInfo.actionName, "space_key", XR_MAX_ACTION_NAME_SIZE - 1);
|
||||
strncpy(spaceKeyInfo.localizedActionName, "Space Key", XR_MAX_LOCALIZED_ACTION_NAME_SIZE - 1);
|
||||
spaceKeyInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
|
||||
spaceKeyInfo.countSubactionPaths = 1;
|
||||
spaceKeyInfo.subactionPaths = &g_hand_path[RIGHT];
|
||||
if (!xr_check(xrCreateAction(g_action_set, &spaceKeyInfo, &g_space_key_action),
|
||||
"xrCreateAction(space_key)"))
|
||||
return false;
|
||||
|
||||
// Right controller's B button, see comment on g_forward_key_action's
|
||||
// declaration above.
|
||||
XrActionCreateInfo forwardKeyInfo = {XR_TYPE_ACTION_CREATE_INFO};
|
||||
strncpy(forwardKeyInfo.actionName, "forward_key", XR_MAX_ACTION_NAME_SIZE - 1);
|
||||
strncpy(forwardKeyInfo.localizedActionName, "Forward Key", XR_MAX_LOCALIZED_ACTION_NAME_SIZE - 1);
|
||||
forwardKeyInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
|
||||
forwardKeyInfo.countSubactionPaths = 1;
|
||||
forwardKeyInfo.subactionPaths = &g_hand_path[RIGHT];
|
||||
if (!xr_check(xrCreateAction(g_action_set, &forwardKeyInfo, &g_forward_key_action),
|
||||
"xrCreateAction(forward_key)"))
|
||||
return false;
|
||||
|
||||
XrPath profilePath;
|
||||
xrStringToPath(instance, "/interaction_profiles/oculus/touch_controller", &profilePath);
|
||||
|
||||
@@ -240,17 +279,22 @@ bool xr_input_init(XrInstance instance, XrSession session, int64_t swapchain_for
|
||||
// action. Suggesting a binding to a component the profile doesn't
|
||||
// have at all (like a nonexistent .../trigger/click) fails the entire
|
||||
// xrSuggestInteractionProfileBindings call, not just that one entry.
|
||||
XrPath aimPoseLeftPath, aimPoseRightPath, triggerLeftPath, triggerRightPath, menuClickPath;
|
||||
XrPath aimPoseLeftPath, aimPoseRightPath, triggerLeftPath, triggerRightPath, menuClickPath,
|
||||
aClickPath, bClickPath;
|
||||
xrStringToPath(instance, "/user/hand/left/input/aim/pose", &aimPoseLeftPath);
|
||||
xrStringToPath(instance, "/user/hand/right/input/aim/pose", &aimPoseRightPath);
|
||||
xrStringToPath(instance, "/user/hand/left/input/trigger/value", &triggerLeftPath);
|
||||
xrStringToPath(instance, "/user/hand/right/input/trigger/value", &triggerRightPath);
|
||||
xrStringToPath(instance, "/user/hand/left/input/menu/click", &menuClickPath);
|
||||
xrStringToPath(instance, "/user/hand/right/input/a/click", &aClickPath);
|
||||
xrStringToPath(instance, "/user/hand/right/input/b/click", &bClickPath);
|
||||
|
||||
XrActionSuggestedBinding bindings[] = {
|
||||
{g_aim_pose_action, aimPoseLeftPath}, {g_aim_pose_action, aimPoseRightPath},
|
||||
{g_select_click_action, triggerLeftPath}, {g_select_click_action, triggerRightPath},
|
||||
{g_menu_toggle_action, menuClickPath},
|
||||
{g_space_key_action, aClickPath},
|
||||
{g_forward_key_action, bClickPath},
|
||||
};
|
||||
XrInteractionProfileSuggestedBinding suggested = {XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};
|
||||
suggested.interactionProfile = profilePath;
|
||||
@@ -291,7 +335,7 @@ bool xr_input_init(XrInstance instance, XrSession session, int64_t swapchain_for
|
||||
}
|
||||
}
|
||||
|
||||
LOGI("XR: input action set ready (aim pose + trigger + menu-toggle)");
|
||||
LOGI("XR: input action set ready (aim pose + trigger + menu-toggle + A=space + B=forward)");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -356,8 +400,9 @@ static bool xr_input_try_overlay(XrOverlay *overlay, bool dragCapable, int hand,
|
||||
*outPlaneDistance = t;
|
||||
|
||||
if (hit != *prevHit) {
|
||||
LOGI("XR: %s aim ray %s %s quad (u=%.2f v=%.2f)", kHandName[hand],
|
||||
hit ? "entered" : "left", quadName, u, v);
|
||||
// Disabled for now - noisy while chasing an unrelated texture bug.
|
||||
// LOGI("XR: %s aim ray %s %s quad (u=%.2f v=%.2f)", kHandName[hand],
|
||||
// hit ? "entered" : "left", quadName, u, v);
|
||||
*prevHit = hit;
|
||||
}
|
||||
if (hit) {
|
||||
@@ -559,8 +604,9 @@ static bool xr_input_try_game_quad(int hand, const XrSpaceLocation *location, fl
|
||||
*outV = v;
|
||||
|
||||
if (hit != g_game_prev_hit[hand]) {
|
||||
LOGI("XR: %s aim ray %s game quad (u=%.2f v=%.2f)", kHandName[hand],
|
||||
hit ? "entered" : "left", u, v);
|
||||
// Disabled for now - noisy while chasing an unrelated texture bug.
|
||||
// LOGI("XR: %s aim ray %s game quad (u=%.2f v=%.2f)", kHandName[hand],
|
||||
// hit ? "entered" : "left", u, v);
|
||||
g_game_prev_hit[hand] = hit;
|
||||
}
|
||||
return hit;
|
||||
@@ -655,6 +701,82 @@ void xr_input_sync_and_draw(XrSpace baseSpace, XrTime time, bool draw) {
|
||||
g_prev_menu = menuDown;
|
||||
}
|
||||
|
||||
if (hand == RIGHT) {
|
||||
XrActionStateGetInfo spaceKeyInfo = {XR_TYPE_ACTION_STATE_GET_INFO};
|
||||
spaceKeyInfo.action = g_space_key_action;
|
||||
spaceKeyInfo.subactionPath = g_hand_path[RIGHT];
|
||||
XrActionStateBoolean spaceKeyState = {XR_TYPE_ACTION_STATE_BOOLEAN};
|
||||
xrGetActionStateBoolean(g_session, &spaceKeyInfo, &spaceKeyState);
|
||||
bool spaceKeyDown = spaceKeyState.isActive && spaceKeyState.currentState;
|
||||
if (spaceKeyDown != g_prev_space_key) {
|
||||
LOGI("XR: A button (space key) %s", spaceKeyDown ? "DOWN" : "UP");
|
||||
if (spaceKeyDown) {
|
||||
// The engine's pump_events() (sdl_events.c) only queues a
|
||||
// KEYDOWN here for non-printable keys (ascii < 32) -
|
||||
// printable keys like Space are expected to arrive via
|
||||
// SDL_TEXTINPUT instead (that's the branch that actually
|
||||
// queues the KBS_DOWN event the tutorial-dismiss handler
|
||||
// checks for), so send that too, matching what real
|
||||
// keyboard input generates for a Space press.
|
||||
//
|
||||
// sdl_events.c's SDL_TEXTINPUT handling also sets
|
||||
// sshockKeyStates[Space] = pressed - the same array
|
||||
// Space's own keybind (M_JUMP, see MacSrc/Prefs.c's
|
||||
// default keybinds.txt) polls every physics tick via
|
||||
// ui_poll_keyboard(). It's only ever cleared again by a
|
||||
// matching SDL_KEYUP, sent below once this button's real
|
||||
// physical up-edge fires (spaceKeyDown false) - not
|
||||
// synthesized here alongside the press, since pushing
|
||||
// both in the same batch would set and clear it within
|
||||
// one pump_events() call, before the poll (which runs
|
||||
// once per frame, before pump_events) ever sees the
|
||||
// press.
|
||||
SDL_Event textEvent;
|
||||
SDL_zero(textEvent);
|
||||
textEvent.type = SDL_TEXTINPUT;
|
||||
textEvent.text.timestamp = SDL_GetTicks();
|
||||
textEvent.text.windowID = 0;
|
||||
textEvent.text.text[0] = ' ';
|
||||
textEvent.text.text[1] = '\0';
|
||||
SDL_PushEvent(&textEvent);
|
||||
}
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = spaceKeyDown ? SDL_KEYDOWN : SDL_KEYUP;
|
||||
event.key.timestamp = SDL_GetTicks();
|
||||
event.key.windowID = 0;
|
||||
event.key.state = spaceKeyDown ? SDL_PRESSED : SDL_RELEASED;
|
||||
event.key.repeat = 0;
|
||||
event.key.keysym.scancode = SDL_SCANCODE_SPACE;
|
||||
event.key.keysym.sym = SDLK_SPACE;
|
||||
event.key.keysym.mod = KMOD_NONE;
|
||||
SDL_PushEvent(&event);
|
||||
g_prev_space_key = spaceKeyDown;
|
||||
}
|
||||
|
||||
XrActionStateGetInfo forwardKeyInfo = {XR_TYPE_ACTION_STATE_GET_INFO};
|
||||
forwardKeyInfo.action = g_forward_key_action;
|
||||
forwardKeyInfo.subactionPath = g_hand_path[RIGHT];
|
||||
XrActionStateBoolean forwardKeyState = {XR_TYPE_ACTION_STATE_BOOLEAN};
|
||||
xrGetActionStateBoolean(g_session, &forwardKeyInfo, &forwardKeyState);
|
||||
bool forwardKeyDown = forwardKeyState.isActive && forwardKeyState.currentState;
|
||||
if (forwardKeyDown != g_prev_forward_key) {
|
||||
LOGI("XR: B button (forward key) %s", forwardKeyDown ? "DOWN" : "UP");
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = forwardKeyDown ? SDL_KEYDOWN : SDL_KEYUP;
|
||||
event.key.timestamp = SDL_GetTicks();
|
||||
event.key.windowID = 0;
|
||||
event.key.state = forwardKeyDown ? SDL_PRESSED : SDL_RELEASED;
|
||||
event.key.repeat = 0;
|
||||
event.key.keysym.scancode = SDL_SCANCODE_UP;
|
||||
event.key.keysym.sym = SDLK_UP;
|
||||
event.key.keysym.mod = KMOD_NONE;
|
||||
SDL_PushEvent(&event);
|
||||
g_prev_forward_key = forwardKeyDown;
|
||||
}
|
||||
}
|
||||
|
||||
XrActionStateGetInfo poseInfo = {XR_TYPE_ACTION_STATE_GET_INFO};
|
||||
poseInfo.action = g_aim_pose_action;
|
||||
poseInfo.subactionPath = g_hand_path[hand];
|
||||
@@ -782,6 +904,8 @@ void xr_input_shutdown(void) {
|
||||
g_aim_pose_action = XR_NULL_HANDLE;
|
||||
g_select_click_action = XR_NULL_HANDLE;
|
||||
g_menu_toggle_action = XR_NULL_HANDLE;
|
||||
g_space_key_action = XR_NULL_HANDLE;
|
||||
g_forward_key_action = XR_NULL_HANDLE;
|
||||
|
||||
g_instance = XR_NULL_HANDLE;
|
||||
g_session = XR_NULL_HANDLE;
|
||||
@@ -794,6 +918,8 @@ void xr_input_shutdown(void) {
|
||||
memset(g_keyboard_touch_active, 0, sizeof(g_keyboard_touch_active));
|
||||
g_beam_quad_valid[LEFT] = g_beam_quad_valid[RIGHT] = false;
|
||||
g_prev_menu = false;
|
||||
g_prev_space_key = false;
|
||||
g_prev_forward_key = false;
|
||||
g_keyboard_dragging = false;
|
||||
g_keyboard_drag_hand = -1;
|
||||
}
|
||||
|
||||
@@ -21,8 +21,9 @@ extern "C" {
|
||||
void xr_mouse_move(int x, int y);
|
||||
|
||||
// Pushes a synthetic SDL_MOUSEBUTTONDOWN/UP (SDL_BUTTON_LEFT) - matches
|
||||
// KeyboardOverlay's nativeSendPrintableChar() in questshock_native.c: a
|
||||
// plain SDL_Event built by hand and handed to the public SDL_PushEvent(),
|
||||
// KeyboardOverlay's nativeSendPrintableCharDown()/Up() in
|
||||
// questshock_native.c: a plain SDL_Event built by hand and handed to the
|
||||
// public SDL_PushEvent(),
|
||||
// no engine patch needed. pump_events() (sdl_events.c) reads whatever
|
||||
// mouse_put_xy() last set, not any x/y carried on the event itself - call
|
||||
// xr_mouse_move() first if position needs updating this frame.
|
||||
|
||||
@@ -427,6 +427,15 @@ bool xr_overlay_render_and_build_layer(XrOverlay *overlay, XrCompositionLayerQua
|
||||
overlay->width, 0, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
real_glBindFramebuffer(GL_READ_FRAMEBUFFER, (GLuint)prevReadFbo);
|
||||
|
||||
// Without this, nothing here waits for the blit to actually finish on
|
||||
// the GPU before xr_swapchain_release() hands the image back to the
|
||||
// compositor - glBlitFramebuffer only queues the copy. A cross-process
|
||||
// consumer reading the image before the driver has finished writing it
|
||||
// is a textbook source of the diagonal-banded/torn corruption seen on
|
||||
// device (see xr_swapchain_acquire()'s counterpart in OpenGL.cc, which
|
||||
// had the same gap).
|
||||
glFinish();
|
||||
|
||||
xr_swapchain_release(overlay->instance, &overlay->swapchain);
|
||||
|
||||
memset(out_quad, 0, sizeof(*out_quad));
|
||||
|
||||
Reference in New Issue
Block a user