mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 10:00:27 +02:00
Fix for SDL stalling the pacing thread
This commit is contained in:
1 parent
1d66ae87bf
commit
b2353e1410
7 files changed
+141
-25
No files matched your search
@@ -4,6 +4,7 @@
|
||||
#include "input_bindings.h"
|
||||
#include "physical_wheel.h"
|
||||
#include "vr/mkw_vr_policy.h"
|
||||
#include "vr/openxr_integration.h"
|
||||
#include "wii_remote_input.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -118,6 +119,9 @@ extern "C" uint32_t PAD__Read_HLE(uint32_t statusPtr)
|
||||
PADStatus statuses[PAD_CHANMAX]{};
|
||||
// Keep looking for a Bluetooth Wii Remote that dropped out (or was turned on late).
|
||||
WiiRemoteInput::Poll();
|
||||
// The VR controllers the pacing thread published reach SDL here, on the
|
||||
// thread that polls controllers, rather than from the pacing thread itself.
|
||||
mkw::vr::OpenXRApplyControllerState();
|
||||
uint32_t rumbleMask = PADRead(statuses);
|
||||
// Wii Remotes reach the game through KPAD, not as GameCube pads. This also
|
||||
// applies while input is blocked (overlay open) so the port does not flip
|
||||
|
||||
@@ -2407,6 +2407,9 @@ void Draw() noexcept {
|
||||
// its "communications interrupted" prompt without polling pads). Same guest
|
||||
// thread as PADRead, so no concurrent access to the scanner's state.
|
||||
WiiRemoteInput::Poll();
|
||||
// Likewise for the VR controllers' gamepad, so it keeps being written even
|
||||
// while the game is not reading pads.
|
||||
mkw::vr::OpenXRApplyControllerState();
|
||||
if (g_firstPersonToggleRequested.exchange(false, std::memory_order_acq_rel)) {
|
||||
ToggleFirstPersonCamera();
|
||||
}
|
||||
|
||||
+102
-23
@@ -23,9 +23,11 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -38,6 +40,81 @@
|
||||
namespace mkw::vr {
|
||||
namespace {
|
||||
|
||||
// SDL holds its joystick lock for as long as an enumeration takes, and the
|
||||
// Bluetooth Wii Remote rescan (F10 > Controller settings > Keep scanning)
|
||||
// closes and reopens every HID device twice per scan: 15 ms on a plain desk,
|
||||
// over 200 ms on a machine carrying several HID devices, such as a Lighthouse
|
||||
// setup's base-station dongles. The pacing thread must never wait on that,
|
||||
// because the OpenXR frame it holds open costs the compositor every display
|
||||
// slot that passes. So it leaves the gamepad here, and the game thread writes
|
||||
// it to SDL where it already polls controllers.
|
||||
//
|
||||
// m_sdl is the lock the SDL work runs under; the pacing thread only ever takes
|
||||
// m_state, and only for the copy. Both are taken in that order.
|
||||
class VirtualGamepadRelay {
|
||||
public:
|
||||
struct Pad {
|
||||
std::array<int16_t, SDL_GAMEPAD_AXIS_COUNT> axes{};
|
||||
std::array<bool, SDL_GAMEPAD_BUTTON_COUNT> buttons{};
|
||||
};
|
||||
|
||||
void Attach(SDL_Joystick* joystick) {
|
||||
std::scoped_lock lock(m_sdl, m_state);
|
||||
m_joystick = joystick;
|
||||
m_pending = false;
|
||||
}
|
||||
|
||||
void Detach() {
|
||||
std::scoped_lock lock(m_sdl, m_state);
|
||||
m_joystick = nullptr;
|
||||
m_pending = false;
|
||||
}
|
||||
|
||||
// Pacing thread.
|
||||
void Publish(const Pad& pad) {
|
||||
std::lock_guard lock(m_state);
|
||||
m_pad = pad;
|
||||
m_pending = true;
|
||||
}
|
||||
|
||||
// Game thread. Holding m_sdl here is what keeps Detach from closing the
|
||||
// joystick underneath the writes.
|
||||
void Apply() {
|
||||
std::lock_guard sdl(m_sdl);
|
||||
Pad pad;
|
||||
SDL_Joystick* joystick = nullptr;
|
||||
{
|
||||
std::lock_guard lock(m_state);
|
||||
if (!m_pending || m_joystick == nullptr) {
|
||||
return;
|
||||
}
|
||||
pad = m_pad;
|
||||
joystick = m_joystick;
|
||||
m_pending = false;
|
||||
}
|
||||
for (int axis = 0; axis < SDL_GAMEPAD_AXIS_COUNT; ++axis) {
|
||||
SDL_SetJoystickVirtualAxis(joystick, static_cast<SDL_GamepadAxis>(axis),
|
||||
pad.axes[static_cast<size_t>(axis)]);
|
||||
}
|
||||
for (int button = 0; button < SDL_GAMEPAD_BUTTON_COUNT; ++button) {
|
||||
SDL_SetJoystickVirtualButton(joystick, static_cast<SDL_GamepadButton>(button),
|
||||
pad.buttons[static_cast<size_t>(button)]);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex m_sdl;
|
||||
std::mutex m_state;
|
||||
SDL_Joystick* m_joystick = nullptr;
|
||||
Pad m_pad;
|
||||
bool m_pending = false;
|
||||
};
|
||||
|
||||
VirtualGamepadRelay& Relay() {
|
||||
static VirtualGamepadRelay relay;
|
||||
return relay;
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
// Debug-only remote button presses for headset experiments driven over adb, so
|
||||
// a menu can be reached without someone wearing the headset:
|
||||
@@ -407,6 +484,10 @@ XrTime OpenXRInput::InputSampleTime(XrTime predicted_display_time) const {
|
||||
return now > 0 ? (std::min)(predicted_display_time, now) : predicted_display_time;
|
||||
}
|
||||
|
||||
void OpenXRApplyVirtualGamepad() noexcept {
|
||||
Relay().Apply();
|
||||
}
|
||||
|
||||
bool OpenXRInput::AttachVirtualGamepad() {
|
||||
SDL_VirtualJoystickDesc desc;
|
||||
SDL_INIT_INTERFACE(&desc);
|
||||
@@ -438,10 +519,13 @@ bool OpenXRInput::AttachVirtualGamepad() {
|
||||
}
|
||||
m_joystick_id = id;
|
||||
m_joystick = joystick;
|
||||
Relay().Attach(joystick);
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenXRInput::DetachVirtualGamepad() {
|
||||
// Before the handle goes: the game thread may be writing through it.
|
||||
Relay().Detach();
|
||||
if (m_joystick != nullptr) {
|
||||
SDL_CloseJoystick(static_cast<SDL_Joystick*>(m_joystick));
|
||||
m_joystick = nullptr;
|
||||
@@ -503,13 +587,7 @@ void OpenXRInput::Idle() {
|
||||
StopRumble();
|
||||
// Nothing stays held on the gamepad either while input is away.
|
||||
if (m_joystick != nullptr) {
|
||||
auto* joystick = static_cast<SDL_Joystick*>(m_joystick);
|
||||
for (int axis = 0; axis < SDL_GAMEPAD_AXIS_COUNT; ++axis) {
|
||||
SDL_SetJoystickVirtualAxis(joystick, axis, 0);
|
||||
}
|
||||
for (int button = 0; button < SDL_GAMEPAD_BUTTON_COUNT; ++button) {
|
||||
SDL_SetJoystickVirtualButton(joystick, button, false);
|
||||
}
|
||||
Relay().Publish({});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -648,24 +726,25 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
||||
const auto injected = [&panel](const char* button) { return !panel.withheld && Injected(button); };
|
||||
|
||||
if (m_joystick != nullptr) {
|
||||
auto* joystick = static_cast<SDL_Joystick*>(m_joystick);
|
||||
VirtualGamepadRelay::Pad pad;
|
||||
// OpenXR thumbsticks report +Y up; SDL gamepads report +Y down.
|
||||
SDL_SetJoystickVirtualAxis(joystick, SDL_GAMEPAD_AXIS_LEFTX, ToAxis(left.stick_x));
|
||||
SDL_SetJoystickVirtualAxis(joystick, SDL_GAMEPAD_AXIS_LEFTY, ToAxis(-left.stick_y));
|
||||
SDL_SetJoystickVirtualAxis(joystick, SDL_GAMEPAD_AXIS_RIGHTX, ToAxis(right.stick_x));
|
||||
SDL_SetJoystickVirtualAxis(joystick, SDL_GAMEPAD_AXIS_RIGHTY, ToAxis(-right.stick_y));
|
||||
SDL_SetJoystickVirtualAxis(joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, ToTrigger(left.trigger));
|
||||
SDL_SetJoystickVirtualAxis(joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, ToTrigger(right.trigger));
|
||||
pad.axes[SDL_GAMEPAD_AXIS_LEFTX] = ToAxis(left.stick_x);
|
||||
pad.axes[SDL_GAMEPAD_AXIS_LEFTY] = ToAxis(-left.stick_y);
|
||||
pad.axes[SDL_GAMEPAD_AXIS_RIGHTX] = ToAxis(right.stick_x);
|
||||
pad.axes[SDL_GAMEPAD_AXIS_RIGHTY] = ToAxis(-right.stick_y);
|
||||
pad.axes[SDL_GAMEPAD_AXIS_LEFT_TRIGGER] = ToTrigger(left.trigger);
|
||||
pad.axes[SDL_GAMEPAD_AXIS_RIGHT_TRIGGER] = ToTrigger(right.trigger);
|
||||
|
||||
SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_SOUTH, right.primary || injected("a"));
|
||||
SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_EAST, right.secondary || injected("b"));
|
||||
SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_WEST, left.primary || injected("x"));
|
||||
SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_NORTH, left.secondary || injected("y"));
|
||||
SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_START, left.menu || injected("start"));
|
||||
SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, left.thumbstick_click);
|
||||
SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, right.thumbstick_click);
|
||||
SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, left.squeeze > 0.5f);
|
||||
SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, right.squeeze > 0.5f);
|
||||
pad.buttons[SDL_GAMEPAD_BUTTON_SOUTH] = right.primary || injected("a");
|
||||
pad.buttons[SDL_GAMEPAD_BUTTON_EAST] = right.secondary || injected("b");
|
||||
pad.buttons[SDL_GAMEPAD_BUTTON_WEST] = left.primary || injected("x");
|
||||
pad.buttons[SDL_GAMEPAD_BUTTON_NORTH] = left.secondary || injected("y");
|
||||
pad.buttons[SDL_GAMEPAD_BUTTON_START] = left.menu || injected("start");
|
||||
pad.buttons[SDL_GAMEPAD_BUTTON_LEFT_STICK] = left.thumbstick_click;
|
||||
pad.buttons[SDL_GAMEPAD_BUTTON_RIGHT_STICK] = right.thumbstick_click;
|
||||
pad.buttons[SDL_GAMEPAD_BUTTON_LEFT_SHOULDER] = left.squeeze > 0.5f;
|
||||
pad.buttons[SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER] = right.squeeze > 0.5f;
|
||||
Relay().Publish(pad);
|
||||
}
|
||||
|
||||
PublishWiiRemote(input_time, screen, game_hands, panel.withheld ? 0u : InjectedWiiRemoteButtons(),
|
||||
|
||||
@@ -1768,6 +1768,12 @@ bool OpenXRIsRunning() noexcept {
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenXRApplyControllerState() noexcept {
|
||||
#if MKW_OPENXR_GRAPHICS_BACKEND
|
||||
OpenXRApplyVirtualGamepad();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenXRRequestRecenter() noexcept {
|
||||
#if MKW_OPENXR_GRAPHICS_BACKEND
|
||||
OpenXRIntegration::Get().RequestRecenter();
|
||||
|
||||
Reference in new issue
Block a user