Overlays: Apply range_mod only for the touch inputs that trigger it (#15534)

This commit is contained in:
neil4 2023-07-30 02:49:59 -05:00 committed by GitHub
parent e6aba004a6
commit fbd8498686
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 147 additions and 60 deletions

View File

@ -1760,11 +1760,11 @@ static bool input_overlay_add_inputs_inner(overlay_desc_t *desc,
{ {
/* We need ALL of the inputs to be active, /* We need ALL of the inputs to be active,
* abort. */ * abort. */
desc->updated = 0; desc->touch_mask = 0;
return false; return false;
} }
desc->updated = 1; desc->touch_mask |= (1 << OVERLAY_MAX_TOUCH);
} }
bank_mask >>= 1; bank_mask >>= 1;
@ -1772,7 +1772,7 @@ static bool input_overlay_add_inputs_inner(overlay_desc_t *desc,
} }
} }
return (desc->updated != 0); return (desc->touch_mask != 0);
} }
case OVERLAY_TYPE_ANALOG_LEFT: case OVERLAY_TYPE_ANALOG_LEFT:
@ -1780,9 +1780,9 @@ static bool input_overlay_add_inputs_inner(overlay_desc_t *desc,
if (ol_state) if (ol_state)
{ {
unsigned index_offset = (desc->type == OVERLAY_TYPE_ANALOG_RIGHT) ? 2 : 0; unsigned index_offset = (desc->type == OVERLAY_TYPE_ANALOG_RIGHT) ? 2 : 0;
desc->updated |= ( desc->touch_mask |= (
ol_state->analog[index_offset] ol_state->analog[index_offset]
| ol_state->analog[index_offset + 1]); | ol_state->analog[index_offset + 1]) << OVERLAY_MAX_TOUCH;
} }
else else
{ {
@ -1805,7 +1805,7 @@ static bool input_overlay_add_inputs_inner(overlay_desc_t *desc,
case OVERLAY_TYPE_DPAD_AREA: case OVERLAY_TYPE_DPAD_AREA:
case OVERLAY_TYPE_ABXY_AREA: case OVERLAY_TYPE_ABXY_AREA:
return (desc->updated != 0); return (desc->touch_mask != 0);
case OVERLAY_TYPE_KEYBOARD: case OVERLAY_TYPE_KEYBOARD:
{ {
@ -1821,7 +1821,7 @@ static bool input_overlay_add_inputs_inner(overlay_desc_t *desc,
if (tmp) if (tmp)
{ {
desc->updated = 1; desc->touch_mask |= (1 << OVERLAY_MAX_TOUCH);
return true; return true;
} }
} }
@ -1978,6 +1978,7 @@ static INLINE void input_overlay_get_eightway_state(
* @desc : Overlay descriptor handle. * @desc : Overlay descriptor handle.
* @x : X coordinate value. * @x : X coordinate value.
* @y : Y coordinate value. * @y : Y coordinate value.
* @use_range_mod : Set true to use range_mod hitbox
* *
* Check whether the given @x and @y coordinates of the overlay * Check whether the given @x and @y coordinates of the overlay
* descriptor @desc is inside the overlay descriptor's hitbox. * descriptor @desc is inside the overlay descriptor's hitbox.
@ -1985,22 +1986,36 @@ static INLINE void input_overlay_get_eightway_state(
* Returns: true (1) if X, Y coordinates are inside a hitbox, * Returns: true (1) if X, Y coordinates are inside a hitbox,
* otherwise false (0). * otherwise false (0).
**/ **/
static bool input_overlay_coords_inside_hitbox(const struct overlay_desc *desc, float x, float y) static bool input_overlay_coords_inside_hitbox(const struct overlay_desc *desc,
float x, float y, bool use_range_mod)
{ {
float range_x, range_y;
if (use_range_mod)
{
range_x = desc->range_x_mod;
range_y = desc->range_y_mod;
}
else
{
range_x = desc->range_x_hitbox;
range_y = desc->range_y_hitbox;
}
switch (desc->hitbox) switch (desc->hitbox)
{ {
case OVERLAY_HITBOX_RADIAL: case OVERLAY_HITBOX_RADIAL:
{ {
/* Ellipse. */ /* Ellipse. */
float x_dist = (x - desc->x_hitbox) / desc->range_x_mod; float x_dist = (x - desc->x_hitbox) / range_x;
float y_dist = (y - desc->y_hitbox) / desc->range_y_mod; float y_dist = (y - desc->y_hitbox) / range_y;
float sq_dist = x_dist * x_dist + y_dist * y_dist; float sq_dist = x_dist * x_dist + y_dist * y_dist;
return (sq_dist <= 1.0f); return (sq_dist <= 1.0f);
} }
case OVERLAY_HITBOX_RECT: case OVERLAY_HITBOX_RECT:
return return
(fabs(x - desc->x_hitbox) <= desc->range_x_mod) (fabs(x - desc->x_hitbox) <= range_x)
&& (fabs(y - desc->y_hitbox) <= desc->range_y_mod); && (fabs(y - desc->y_hitbox) <= range_y);
case OVERLAY_HITBOX_NONE: case OVERLAY_HITBOX_NONE:
break; break;
} }
@ -2010,23 +2025,26 @@ static bool input_overlay_coords_inside_hitbox(const struct overlay_desc *desc,
/** /**
* input_overlay_poll: * input_overlay_poll:
* @out : Polled output data. * @out : Polled output data.
* @ptr_idx : Input pointer index. * @touch_idx : Touch pointer index.
* @norm_x : Normalized X coordinate. * @norm_x : Normalized X coordinate.
* @norm_y : Normalized Y coordinate. * @norm_y : Normalized Y coordinate.
* @touch_scale : Overlay scale.
* *
* Polls input overlay. * Polls input overlay for a single touch pointer.
* *
* @norm_x and @norm_y are the result of * @norm_x and @norm_y are the result of
* input_translate_coord_viewport(). * video_driver_translate_coord_viewport().
**/ **/
static void input_overlay_poll( static void input_overlay_poll(
input_overlay_t *ol, input_overlay_t *ol,
input_overlay_state_t *out, input_overlay_state_t *out,
unsigned ptr_idx, int16_t norm_x, int16_t norm_y, float touch_scale) int touch_idx, int16_t norm_x, int16_t norm_y, float touch_scale)
{ {
size_t i, j; size_t i, j;
struct overlay_desc *descs = ol->active->descs; struct overlay_desc *descs = ol->active->descs;
unsigned int highest_prio = 0; unsigned int highest_prio = 0;
int old_touch_idx = input_driver_st.old_touch_index_lut[touch_idx];
bool use_range_mod;
/* norm_x and norm_y is in [-0x7fff, 0x7fff] range, /* norm_x and norm_y is in [-0x7fff, 0x7fff] range,
* like RETRO_DEVICE_POINTER. */ * like RETRO_DEVICE_POINTER. */
@ -2048,13 +2066,17 @@ static void input_overlay_poll(
unsigned int desc_prio = 0; unsigned int desc_prio = 0;
struct overlay_desc *desc = &descs[i]; struct overlay_desc *desc = &descs[i];
if (!desc || !input_overlay_coords_inside_hitbox(desc, x, y)) /* Use range_mod if this touch pointer contributed
* to desc's touch_mask in the previous poll */
use_range_mod = (old_touch_idx != -1)
&& BIT32_GET(desc->old_touch_mask, old_touch_idx);
if (!input_overlay_coords_inside_hitbox(desc, x, y, use_range_mod))
continue; continue;
/* Check for exclusive hitbox, which blocks other input. /* Check for exclusive hitbox, which blocks other input.
* range_mod_exclusive has priority over exclusive. */ * range_mod_exclusive has priority over exclusive. */
if ((desc->flags & OVERLAY_DESC_RANGE_MOD_EXCLUSIVE) if (use_range_mod && (desc->flags & OVERLAY_DESC_RANGE_MOD_EXCLUSIVE))
&& desc->range_x_mod != desc->range_x_hitbox)
desc_prio = 2; desc_prio = 2;
else if (desc->flags & OVERLAY_DESC_EXCLUSIVE) else if (desc->flags & OVERLAY_DESC_EXCLUSIVE)
desc_prio = 1; desc_prio = 1;
@ -2067,10 +2089,10 @@ static void input_overlay_poll(
highest_prio = desc_prio; highest_prio = desc_prio;
memset(out, 0, sizeof(*out)); memset(out, 0, sizeof(*out));
for (j = 0; j < i; j++) for (j = 0; j < i; j++)
BIT16_CLEAR(descs[j].updated, ptr_idx); BIT32_CLEAR(descs[j].touch_mask, touch_idx);
} }
BIT16_SET(desc->updated, ptr_idx); BIT32_SET(desc->touch_mask, touch_idx);
x_dist = x - desc->x_shift; x_dist = x - desc->x_shift;
y_dist = y - desc->y_shift; y_dist = y - desc->y_shift;
@ -2150,8 +2172,7 @@ static void input_overlay_update_desc_geom(input_overlay_t *ol,
* input_overlay_post_poll: * input_overlay_post_poll:
* *
* Called after all the input_overlay_poll() calls to * Called after all the input_overlay_poll() calls to
* update the range modifiers for pressed/unpressed regions * update alpha mods for pressed/unpressed controls
* and alpha mods.
**/ **/
static void input_overlay_post_poll( static void input_overlay_post_poll(
enum overlay_visibility *visibility, enum overlay_visibility *visibility,
@ -2166,25 +2187,16 @@ static void input_overlay_post_poll(
{ {
struct overlay_desc *desc = &ol->active->descs[i]; struct overlay_desc *desc = &ol->active->descs[i];
desc->range_x_mod = desc->range_x_hitbox; if ( desc->touch_mask != 0
desc->range_y_mod = desc->range_y_hitbox; && show_input && desc->image.pixels
&& ol->iface->set_alpha)
if (desc->updated != 0) ol->iface->set_alpha(ol->iface_data, desc->image_index,
{ desc->alpha_mod * opacity);
/* If pressed this frame, change the hitbox. */
desc->range_x_mod *= desc->range_mod;
desc->range_y_mod *= desc->range_mod;
if (show_input && desc->image.pixels)
{
if (ol->iface->set_alpha)
ol->iface->set_alpha(ol->iface_data, desc->image_index,
desc->alpha_mod * opacity);
}
}
input_overlay_update_desc_geom(ol, desc); input_overlay_update_desc_geom(ol, desc);
desc->updated = 0;
desc->old_touch_mask = desc->touch_mask;
desc->touch_mask = 0;
} }
} }
@ -2206,8 +2218,8 @@ static void input_overlay_desc_init_hitbox(struct overlay_desc *desc)
(desc->range_y * desc->reach_down + (desc->range_y * desc->reach_down +
desc->range_y * desc->reach_up) / 2.0f; desc->range_y * desc->reach_up) / 2.0f;
desc->range_x_mod = desc->range_x_hitbox; desc->range_x_mod = desc->range_x_hitbox * desc->range_mod;
desc->range_y_mod = desc->range_y_hitbox; desc->range_y_mod = desc->range_y_hitbox * desc->range_mod;
} }
/** /**
@ -2483,9 +2495,8 @@ static void input_overlay_poll_clear(
{ {
struct overlay_desc *desc = &ol->active->descs[i]; struct overlay_desc *desc = &ol->active->descs[i];
desc->range_x_mod = desc->range_x_hitbox; desc->old_touch_mask = desc->touch_mask;
desc->range_y_mod = desc->range_y_hitbox; desc->touch_mask = 0;
desc->updated = 0;
input_overlay_update_desc_geom(ol, desc); input_overlay_update_desc_geom(ol, desc);
} }
@ -2652,6 +2663,62 @@ void input_overlay_auto_rotate_(
} }
} }
/**
* input_overlay_track_touch_inputs
* @state : Overlay input state for this poll
* @old_state : Overlay input state for previous poll
*
* Matches current touch inputs to previous poll's, based on distance.
* Updates old_touch_index_lut and assigns -1 to any new inputs.
*/
static void input_overlay_track_touch_inputs(
input_overlay_state_t *state, input_overlay_state_t *old_state)
{
int *const old_index_lut = input_driver_st.old_touch_index_lut;
int i, j, t, new_idx;
float x_dist, y_dist, sq_dist, outlier;
float min_sq_dist[OVERLAY_MAX_TOUCH];
memset(old_index_lut, -1, sizeof(int) * OVERLAY_MAX_TOUCH);
/* Compute (squared) distances and match new indexes to old */
for (i = 0; i < state->touch_count; i++)
{
min_sq_dist[i] = 1e10f;
for (j = 0; j < old_state->touch_count; j++)
{
x_dist = state->touch[i].x - old_state->touch[j].x;
y_dist = state->touch[i].y - old_state->touch[j].y;
sq_dist = x_dist * x_dist + y_dist * y_dist;
if (sq_dist < min_sq_dist[i])
{
min_sq_dist[i] = sq_dist;
old_index_lut[i] = j;
}
}
}
/* If touch_count increased, find the outliers and assign -1 */
for (t = old_state->touch_count; t < state->touch_count; t++)
{
new_idx = OVERLAY_MAX_TOUCH - 1;
outlier = 0;
for (i = 0; i < state->touch_count; i++)
if ( min_sq_dist[i] > outlier
&& old_index_lut[i] != -1)
{
outlier = min_sq_dist[i];
new_idx = i;
}
old_index_lut[new_idx] = -1;
}
}
/* /*
* input_poll_overlay: * input_poll_overlay:
* *
@ -2681,8 +2748,6 @@ static void input_poll_overlay(
unsigned input_overlay_show_inputs_port = settings->uints.input_overlay_show_inputs_port; unsigned input_overlay_show_inputs_port = settings->uints.input_overlay_show_inputs_port;
float touch_scale = (float)settings->uints.input_touch_scale; float touch_scale = (float)settings->uints.input_touch_scale;
bool osk_state_changed = false; bool osk_state_changed = false;
unsigned touch_count = 0;
static unsigned old_touch_count;
if (!ol_state) if (!ol_state)
return; return;
@ -2711,6 +2776,7 @@ static void input_poll_overlay(
joypad_info.auto_binds = NULL; joypad_info.auto_binds = NULL;
joypad_info.axis_threshold = 0.0f; joypad_info.axis_threshold = 0.0f;
/* Get driver input */
for (i = 0; for (i = 0;
current_input->input_state( current_input->input_state(
input_data, input_data,
@ -2722,11 +2788,11 @@ static void input_poll_overlay(
0, 0,
device, device,
i, i,
RETRO_DEVICE_ID_POINTER_PRESSED); RETRO_DEVICE_ID_POINTER_PRESSED)
&& i < OVERLAY_MAX_TOUCH;
i++) i++)
{ {
input_overlay_state_t polled_data; ol_state->touch[i].x = current_input->input_state(
int16_t x = current_input->input_state(
input_data, input_data,
joypad, joypad,
sec_joypad, sec_joypad,
@ -2737,7 +2803,7 @@ static void input_poll_overlay(
device, device,
i, i,
RETRO_DEVICE_ID_POINTER_X); RETRO_DEVICE_ID_POINTER_X);
int16_t y = current_input->input_state( ol_state->touch[i].y = current_input->input_state(
input_data, input_data,
joypad, joypad,
sec_joypad, sec_joypad,
@ -2748,11 +2814,21 @@ static void input_poll_overlay(
device, device,
i, i,
RETRO_DEVICE_ID_POINTER_Y); RETRO_DEVICE_ID_POINTER_Y);
}
ol_state->touch_count = i;
/* Update lookup table of new to old touch indexes */
input_overlay_track_touch_inputs(ol_state, &old_ol_state);
/* Poll overlay */
for (i = 0; i < ol_state->touch_count; i++)
{
input_overlay_state_t polled_data;
memset(&polled_data, 0, sizeof(struct input_overlay_state)); memset(&polled_data, 0, sizeof(struct input_overlay_state));
if (ol->flags & INPUT_OVERLAY_ENABLE) if (ol->flags & INPUT_OVERLAY_ENABLE)
input_overlay_poll(ol, &polled_data, i, x, y, touch_scale); input_overlay_poll(ol, &polled_data, i,
ol_state->touch[i].x, ol_state->touch[i].y, touch_scale);
else else
ol->flags &= ~INPUT_OVERLAY_BLOCKED; ol->flags &= ~INPUT_OVERLAY_BLOCKED;
@ -2769,8 +2845,6 @@ static void input_poll_overlay(
if (polled_data.analog[j]) if (polled_data.analog[j])
ol_state->analog[j] = polled_data.analog[j]; ol_state->analog[j] = polled_data.analog[j];
} }
touch_count = i;
} }
if ( OVERLAY_GET_KEY(ol_state, RETROK_LSHIFT) || if ( OVERLAY_GET_KEY(ol_state, RETROK_LSHIFT) ||
@ -2868,7 +2942,7 @@ static void input_poll_overlay(
if (input_overlay_show_inputs == OVERLAY_SHOW_INPUT_NONE) if (input_overlay_show_inputs == OVERLAY_SHOW_INPUT_NONE)
button_pressed = false; button_pressed = false;
if (button_pressed || touch_count) if (button_pressed || ol_state->touch_count)
input_overlay_post_poll(overlay_visibility, ol, input_overlay_post_poll(overlay_visibility, ol,
button_pressed, opacity); button_pressed, opacity);
else else
@ -2878,7 +2952,8 @@ static void input_poll_overlay(
* unless touch_count decreased. */ * unless touch_count decreased. */
if ( current_input->keypress_vibrate if ( current_input->keypress_vibrate
&& settings->bools.vibrate_on_keypress && settings->bools.vibrate_on_keypress
&& touch_count && touch_count >= old_touch_count && ol_state->touch_count
&& ol_state->touch_count >= old_ol_state.touch_count
&& !(ol->flags & INPUT_OVERLAY_BLOCKED)) && !(ol->flags & INPUT_OVERLAY_BLOCKED))
{ {
if ( osk_state_changed if ( osk_state_changed
@ -2889,8 +2964,6 @@ static void input_poll_overlay(
) )
current_input->keypress_vibrate(); current_input->keypress_vibrate();
} }
old_touch_count = touch_count;
} }
#endif #endif

View File

@ -519,6 +519,9 @@ typedef struct
enum overlay_visibility *overlay_visibility; enum overlay_visibility *overlay_visibility;
float overlay_eightway_dpad_slopes[2]; float overlay_eightway_dpad_slopes[2];
float overlay_eightway_abxy_slopes[2]; float overlay_eightway_abxy_slopes[2];
/* touch pointer indexes from previous poll */
int old_touch_index_lut[OVERLAY_MAX_TOUCH];
#endif #endif
uint16_t flags; uint16_t flags;
#ifdef HAVE_NETWORKGAMEPAD #ifdef HAVE_NETWORKGAMEPAD

View File

@ -34,6 +34,8 @@
#define CUSTOM_BINDS_U32_COUNT ((RARCH_CUSTOM_BIND_LIST_END - 1) / 32 + 1) #define CUSTOM_BINDS_U32_COUNT ((RARCH_CUSTOM_BIND_LIST_END - 1) / 32 + 1)
#define OVERLAY_MAX_TOUCH 16
RETRO_BEGIN_DECLS RETRO_BEGIN_DECLS
enum overlay_hitbox enum overlay_hitbox
@ -223,8 +225,9 @@ struct overlay_desc
char next_index_name[64]; char next_index_name[64];
/* Nonzero if pressed. One bit per input pointer */ /* Nonzero if pressed. Lower bits used for pointer indexes */
uint16_t updated; uint32_t touch_mask;
uint32_t old_touch_mask;
uint8_t flags; uint8_t flags;
}; };
@ -291,6 +294,14 @@ typedef struct input_overlay_state
int16_t analog[4]; int16_t analog[4];
/* This is a bitmask of (1 << key_bind_id). */ /* This is a bitmask of (1 << key_bind_id). */
input_bits_t buttons; input_bits_t buttons;
/* Input pointers from input_state */
struct
{
int16_t x;
int16_t y;
} touch[OVERLAY_MAX_TOUCH];
int touch_count;
} input_overlay_state_t; } input_overlay_state_t;
struct input_overlay struct input_overlay