Skip to content

Commit

Permalink
Use explicit polling mechanism for gamepads.
Browse files Browse the repository at this point in the history
Using events causes SDL_WaitEvent to spin instead (yikes).
  • Loading branch information
Themaister committed Nov 20, 2023
1 parent 7811983 commit a422999
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 109 deletions.
181 changes: 73 additions & 108 deletions application/input/input_sdl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,66 +24,6 @@

namespace Granite
{
static JoypadKey sdl_gamepad_button_to_granite(SDL_GamepadButton button)
{
switch (button)
{
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
return JoypadKey::Down;
case SDL_GAMEPAD_BUTTON_DPAD_UP:
return JoypadKey::Up;
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
return JoypadKey::Left;
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
return JoypadKey::Right;
case SDL_GAMEPAD_BUTTON_GUIDE:
return JoypadKey::Mode;
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
return JoypadKey::LeftShoulder;
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
return JoypadKey::RightShoulder;
case SDL_GAMEPAD_BUTTON_WEST:
return JoypadKey::West;
case SDL_GAMEPAD_BUTTON_EAST:
return JoypadKey::East;
case SDL_GAMEPAD_BUTTON_NORTH:
return JoypadKey::North;
case SDL_GAMEPAD_BUTTON_SOUTH:
return JoypadKey::South;
case SDL_GAMEPAD_BUTTON_START:
return JoypadKey::Start;
case SDL_GAMEPAD_BUTTON_BACK:
return JoypadKey::Select;
case SDL_GAMEPAD_BUTTON_LEFT_STICK:
return JoypadKey::LeftThumb;
case SDL_GAMEPAD_BUTTON_RIGHT_STICK:
return JoypadKey::RightThumb;
default:
return JoypadKey::Unknown;
}
}

static JoypadAxis sdl_gamepad_axis_to_granite(SDL_GamepadAxis axis)
{
switch (axis)
{
case SDL_GAMEPAD_AXIS_LEFTX:
return JoypadAxis::LeftX;
case SDL_GAMEPAD_AXIS_LEFTY:
return JoypadAxis::LeftY;
case SDL_GAMEPAD_AXIS_RIGHTX:
return JoypadAxis::RightX;
case SDL_GAMEPAD_AXIS_RIGHTY:
return JoypadAxis::RightY;
case SDL_GAMEPAD_AXIS_LEFT_TRIGGER:
return JoypadAxis::LeftTrigger;
case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER:
return JoypadAxis::RightTrigger;
default:
return JoypadAxis::Unknown;
}
}

bool InputTrackerSDL::init(InputTracker &tracker, const Dispatcher &dispatcher)
{
// Open existing gamepads.
Expand All @@ -93,10 +33,82 @@ bool InputTrackerSDL::init(InputTracker &tracker, const Dispatcher &dispatcher)
add_gamepad(gamepad_ids[i], tracker, dispatcher);
if (gamepad_ids)
SDL_free(gamepad_ids);
SDL_SetGamepadEventsEnabled(SDL_TRUE);

// Poll these separately, inline in poll_input().
SDL_SetEventEnabled(SDL_EVENT_GAMEPAD_BUTTON_DOWN, SDL_FALSE);
SDL_SetEventEnabled(SDL_EVENT_GAMEPAD_BUTTON_UP, SDL_FALSE);
SDL_SetEventEnabled(SDL_EVENT_GAMEPAD_AXIS_MOTION, SDL_FALSE);

return true;
}

void InputTrackerSDL::update(InputTracker &tracker)
{
SDL_UpdateGamepads();

for (int i = 0; i < int(InputTracker::Joypads); i++)
{
auto *pad = pads[i];
if (!pad)
continue;

static const struct
{
JoypadKey gkey;
SDL_GamepadButton sdl;
} buttons[] = {
{ JoypadKey::Left, SDL_GAMEPAD_BUTTON_DPAD_LEFT },
{ JoypadKey::Right, SDL_GAMEPAD_BUTTON_DPAD_RIGHT },
{ JoypadKey::Up, SDL_GAMEPAD_BUTTON_DPAD_UP },
{ JoypadKey::Down, SDL_GAMEPAD_BUTTON_DPAD_DOWN },
{ JoypadKey::Start, SDL_GAMEPAD_BUTTON_START },
{ JoypadKey::Select, SDL_GAMEPAD_BUTTON_BACK },
{ JoypadKey::East, SDL_GAMEPAD_BUTTON_EAST },
{ JoypadKey::West, SDL_GAMEPAD_BUTTON_WEST },
{ JoypadKey::North, SDL_GAMEPAD_BUTTON_NORTH },
{ JoypadKey::South, SDL_GAMEPAD_BUTTON_SOUTH },
{ JoypadKey::LeftShoulder, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER },
{ JoypadKey::RightShoulder, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER },
{ JoypadKey::LeftThumb, SDL_GAMEPAD_BUTTON_LEFT_STICK },
{ JoypadKey::RightThumb, SDL_GAMEPAD_BUTTON_RIGHT_STICK },
};

for (auto &b : buttons)
{
tracker.joypad_key_state(i, b.gkey,
SDL_GetGamepadButton(pad, b.sdl) ?
JoypadKeyState::Pressed : JoypadKeyState::Released);
}

static const struct
{
JoypadAxis gaxis;
SDL_GamepadAxis sdl;
} axes[] = {
{ JoypadAxis::LeftX, SDL_GAMEPAD_AXIS_LEFTX },
{ JoypadAxis::LeftY, SDL_GAMEPAD_AXIS_LEFTY },
{ JoypadAxis::RightX, SDL_GAMEPAD_AXIS_RIGHTX },
{ JoypadAxis::RightY, SDL_GAMEPAD_AXIS_RIGHTY },
};

for (auto &a : axes)
{
float value = float(SDL_GetGamepadAxis(pad, a.sdl) - SDL_JOYSTICK_AXIS_MIN) /
float(SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN);
value = 2.0f * value - 1.0f;
tracker.joyaxis_state(i, a.gaxis, value);
}

tracker.joyaxis_state(i, JoypadAxis::LeftTrigger,
float(SDL_GetGamepadAxis(pad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER)) /
float(SDL_JOYSTICK_AXIS_MAX));

tracker.joyaxis_state(i, JoypadAxis::RightTrigger,
float(SDL_GetGamepadAxis(pad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) /
float(SDL_JOYSTICK_AXIS_MAX));
}
}

void InputTrackerSDL::close()
{
for (auto *pad : pads)
Expand All @@ -121,53 +133,6 @@ bool InputTrackerSDL::process_sdl_event(const SDL_Event &e, InputTracker &tracke
return true;
}

case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
{
int player = SDL_GetJoystickInstancePlayerIndex(e.gbutton.which);
if (player < 0 || player >= int(InputTracker::Joypads) || !pads[player])
break;

JoypadKey key = sdl_gamepad_button_to_granite(SDL_GamepadButton(e.gbutton.button));
if (key == JoypadKey::Unknown)
break;

auto state = e.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ?
JoypadKeyState::Pressed : JoypadKeyState::Released;

dispatcher([player, key, state, &tracker]() {
tracker.joypad_key_state(player, key, state);
});
return true;
}

case SDL_EVENT_GAMEPAD_AXIS_MOTION:
{
int player = SDL_GetJoystickInstancePlayerIndex(e.gaxis.which);
if (player < 0 || player >= int(InputTracker::Joypads) || !pads[player])
break;

JoypadAxis axis = sdl_gamepad_axis_to_granite(SDL_GamepadAxis(e.gaxis.axis));
bool is_trigger = axis == JoypadAxis::LeftTrigger || axis == JoypadAxis::RightTrigger;

float value;
if (is_trigger)
{
value = float(e.gaxis.value) / float(SDL_JOYSTICK_AXIS_MAX);
}
else
{
value = (float(e.gaxis.value) - SDL_JOYSTICK_AXIS_MIN) /
float(SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN);
value = 2.0f * value - 1.0f;
}

dispatcher([player, axis, value, &tracker]() {
tracker.joyaxis_state(player, axis, value);
});
return true;
}

default:
break;
}
Expand Down
1 change: 1 addition & 0 deletions application/input/input_sdl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class InputTrackerSDL
bool init(InputTracker &tracker, const Dispatcher &dispatcher);
void close();
bool process_sdl_event(const SDL_Event &event, InputTracker &tracker, const Dispatcher &dispatcher);
void update(InputTracker &tracker);

private:
SDL_Gamepad *pads[InputTracker::Joypads] = {};
Expand Down
8 changes: 7 additions & 1 deletion application/platforms/application_sdl3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ struct WSIPlatformSDL : GraniteWSIPlatform
}

SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
// Adding gamepad events will make main loop spin without waiting.
SDL_SetHint(SDL_HINT_AUTO_UPDATE_JOYSTICKS, "0");

if (SDL_Vulkan_LoadLibrary(nullptr) < 0)
{
Expand Down Expand Up @@ -203,14 +205,18 @@ struct WSIPlatformSDL : GraniteWSIPlatform
std::lock_guard<std::mutex> holder{get_input_tracker().get_lock()};
flush_deferred_input_events();
process_events_async_thread();
pad.update(get_input_tracker());
get_input_tracker().dispatch_current_state(get_frame_timer().get_frame_time());
}

void poll_input_async(Granite::InputTrackerHandler *override_handler) override
{
std::lock_guard<std::mutex> holder{get_input_tracker().get_lock()};
begin_async_input_handling();
process_events_async_thread();
{
process_events_async_thread();
pad.update(get_input_tracker());
}
end_async_input_handling();
get_input_tracker().dispatch_current_state(0.0, override_handler);
}
Expand Down

0 comments on commit a422999

Please sign in to comment.