Wiimote: Separate the Input system state update from the emulated state update.

This commit is contained in:
Admiral H. Curtiss 2022-09-25 02:29:35 +02:00
parent bb5943ae77
commit a2dadbb2f0
No known key found for this signature in database
GPG Key ID: F051B4C4044F33FB
9 changed files with 109 additions and 44 deletions

View File

@ -7,6 +7,11 @@
#include "Core/HW/WiimoteCommon/WiimoteConstants.h" #include "Core/HW/WiimoteCommon/WiimoteConstants.h"
#include "Core/HW/WiimoteCommon/WiimoteReport.h" #include "Core/HW/WiimoteCommon/WiimoteReport.h"
namespace WiimoteEmu
{
struct DesiredWiimoteState;
}
namespace WiimoteCommon namespace WiimoteCommon
{ {
// Source: HID_010_SPC_PFL/1.0 (official HID specification) // Source: HID_010_SPC_PFL/1.0 (official HID specification)
@ -34,7 +39,8 @@ public:
virtual void SetWiimoteDeviceIndex(u8 index) = 0; virtual void SetWiimoteDeviceIndex(u8 index) = 0;
// Called every ~200hz after HID channels are established. // Called every ~200hz after HID channels are established.
virtual void Update() = 0; virtual void PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state) = 0;
virtual void Update(const WiimoteEmu::DesiredWiimoteState& target_state) = 0;
void SetInterruptCallback(InterruptCallbackType callback) { m_callback = std::move(callback); } void SetInterruptCallback(InterruptCallbackType callback) { m_callback = std::move(callback); }
@ -42,8 +48,10 @@ public:
// Does not include HID-type header. // Does not include HID-type header.
virtual void InterruptDataOutput(const u8* data, u32 size) = 0; virtual void InterruptDataOutput(const u8* data, u32 size) = 0;
// Used to connect a disconnected wii remote on button press. // Get a snapshot of the current state of the Wiimote's buttons.
virtual bool IsButtonPressed() = 0; // Note that only the button bits of the return value are meaningful, the rest should be ignored.
// This is used to query a disconnected Wiimote whether it wants to reconnect.
virtual ButtonData GetCurrentlyPressedButtons() = 0;
protected: protected:
void InterruptDataInputCallback(const u8* data, u32 size) void InterruptDataInputCallback(const u8* data, u32 size)

View File

@ -448,7 +448,7 @@ void Wiimote::UpdateButtonsStatus(const DesiredWiimoteState& target_state)
m_status.buttons.hex = target_state.buttons.hex & ButtonData::BUTTON_MASK; m_status.buttons.hex = target_state.buttons.hex & ButtonData::BUTTON_MASK;
} }
DesiredWiimoteState Wiimote::BuildDesiredWiimoteState() void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state)
{ {
// Hotkey / settings modifier // Hotkey / settings modifier
// Data is later accessed in IsSideways and IsUpright // Data is later accessed in IsSideways and IsUpright
@ -457,36 +457,35 @@ DesiredWiimoteState Wiimote::BuildDesiredWiimoteState()
// Update our motion simulations. // Update our motion simulations.
StepDynamics(); StepDynamics();
DesiredWiimoteState wiimote_state;
// Fetch pressed buttons from user input. // Fetch pressed buttons from user input.
m_buttons->GetState(&wiimote_state.buttons.hex, button_bitmasks); target_state->buttons.hex = 0;
m_dpad->GetState(&wiimote_state.buttons.hex, m_buttons->GetState(&target_state->buttons.hex, button_bitmasks);
m_dpad->GetState(&target_state->buttons.hex,
IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks); IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks);
// Calculate accelerometer state. // Calculate accelerometer state.
// Calibration values are 8-bit but we want 10-bit precision, so << 2. // Calibration values are 8-bit but we want 10-bit precision, so << 2.
wiimote_state.acceleration = target_state->acceleration =
ConvertAccelData(GetTotalAcceleration(), ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2); ConvertAccelData(GetTotalAcceleration(), ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
// Calculate IR camera state. // Calculate IR camera state.
wiimote_state.camera_points = CameraLogic::GetCameraPoints( target_state->camera_points = CameraLogic::GetCameraPoints(
GetTotalTransformation(), GetTotalTransformation(),
Common::Vec2(m_fov_x_setting.GetValue(), m_fov_y_setting.GetValue()) / 360 * Common::Vec2(m_fov_x_setting.GetValue(), m_fov_y_setting.GetValue()) / 360 *
float(MathUtil::TAU)); float(MathUtil::TAU));
// Calculate MotionPlus state. // Calculate MotionPlus state.
if (m_motion_plus_setting.GetValue()) if (m_motion_plus_setting.GetValue())
wiimote_state.motion_plus = MotionPlus::GetGyroscopeData(GetTotalAngularVelocity()); target_state->motion_plus = MotionPlus::GetGyroscopeData(GetTotalAngularVelocity());
else
target_state->motion_plus = std::nullopt;
// Build Extension state. // Build Extension state.
// This also allows the extension to perform any regular duties it may need. // This also allows the extension to perform any regular duties it may need.
// (e.g. Nunchuk motion simulation step) // (e.g. Nunchuk motion simulation step)
static_cast<Extension*>( static_cast<Extension*>(
m_attachments->GetAttachmentList()[m_attachments->GetSelectedAttachment()].get()) m_attachments->GetAttachmentList()[m_attachments->GetSelectedAttachment()].get())
->BuildDesiredExtensionState(&wiimote_state.extension); ->BuildDesiredExtensionState(&target_state->extension);
return wiimote_state;
} }
u8 Wiimote::GetWiimoteDeviceIndex() const u8 Wiimote::GetWiimoteDeviceIndex() const
@ -500,13 +499,14 @@ void Wiimote::SetWiimoteDeviceIndex(u8 index)
} }
// This is called every ::Wiimote::UPDATE_FREQ (200hz) // This is called every ::Wiimote::UPDATE_FREQ (200hz)
void Wiimote::Update() void Wiimote::PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state)
{ {
const auto lock = GetStateLock(); const auto lock = GetStateLock();
BuildDesiredWiimoteState(target_state);
}
// Build target state. void Wiimote::Update(const WiimoteEmu::DesiredWiimoteState& target_state)
auto target_state = BuildDesiredWiimoteState(); {
// Update buttons in the status struct which is sent in 99% of input reports. // Update buttons in the status struct which is sent in 99% of input reports.
UpdateButtonsStatus(target_state); UpdateButtonsStatus(target_state);
@ -656,14 +656,15 @@ void Wiimote::SendDataReport(const DesiredWiimoteState& target_state)
m_reporting_mode = InputReportID::ReportInterleave1; m_reporting_mode = InputReportID::ReportInterleave1;
} }
bool Wiimote::IsButtonPressed() ButtonData Wiimote::GetCurrentlyPressedButtons()
{ {
u16 buttons = 0;
const auto lock = GetStateLock(); const auto lock = GetStateLock();
m_buttons->GetState(&buttons, button_bitmasks);
m_dpad->GetState(&buttons, dpad_bitmasks);
return buttons != 0; ButtonData buttons{};
m_buttons->GetState(&buttons.hex, button_bitmasks);
m_dpad->GetState(&buttons.hex, IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks);
return buttons;
} }
void Wiimote::LoadDefaults(const ControllerInterface& ciface) void Wiimote::LoadDefaults(const ControllerInterface& ciface)

View File

@ -132,11 +132,12 @@ public:
u8 GetWiimoteDeviceIndex() const override; u8 GetWiimoteDeviceIndex() const override;
void SetWiimoteDeviceIndex(u8 index) override; void SetWiimoteDeviceIndex(u8 index) override;
void Update() override; void PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state) override;
void Update(const WiimoteEmu::DesiredWiimoteState& target_state) override;
void EventLinked() override; void EventLinked() override;
void EventUnlinked() override; void EventUnlinked() override;
void InterruptDataOutput(const u8* data, u32 size) override; void InterruptDataOutput(const u8* data, u32 size) override;
bool IsButtonPressed() override; WiimoteCommon::ButtonData GetCurrentlyPressedButtons() override;
void Reset(); void Reset();
@ -157,7 +158,7 @@ private:
void StepDynamics(); void StepDynamics();
void UpdateButtonsStatus(const DesiredWiimoteState& target_state); void UpdateButtonsStatus(const DesiredWiimoteState& target_state);
DesiredWiimoteState BuildDesiredWiimoteState(); void BuildDesiredWiimoteState(DesiredWiimoteState* target_state);
// Returns simulated accelerometer data in m/s^2. // Returns simulated accelerometer data in m/s^2.
Common::Vec3 GetAcceleration( Common::Vec3 GetAcceleration(

View File

@ -460,7 +460,12 @@ void Wiimote::SetWiimoteDeviceIndex(u8 index)
m_bt_device_index = index; m_bt_device_index = index;
} }
void Wiimote::Update() void Wiimote::PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state)
{
// Nothing to do here on real Wiimotes.
}
void Wiimote::Update(const WiimoteEmu::DesiredWiimoteState& target_state)
{ {
// Wii remotes send input at 200hz once a Wii enables "sniff mode" on the connection. // Wii remotes send input at 200hz once a Wii enables "sniff mode" on the connection.
// PC bluetooth stacks do not enable sniff mode causing remotes to send input at only 100hz. // PC bluetooth stacks do not enable sniff mode causing remotes to send input at only 100hz.
@ -485,7 +490,7 @@ void Wiimote::Update()
u32(rpt.size() - REPORT_HID_HEADER_SIZE)); u32(rpt.size() - REPORT_HID_HEADER_SIZE));
} }
bool Wiimote::IsButtonPressed() ButtonData Wiimote::GetCurrentlyPressedButtons()
{ {
Report& rpt = m_last_input_report; Report& rpt = m_last_input_report;
if (rpt.size() >= 4) if (rpt.size() >= 4)
@ -499,10 +504,10 @@ bool Wiimote::IsButtonPressed()
ButtonData buttons = {}; ButtonData buttons = {};
builder->GetCoreData(&buttons); builder->GetCoreData(&buttons);
return buttons.hex != 0; return buttons;
} }
} }
return false; return ButtonData{};
} }
void Wiimote::Prepare() void Wiimote::Prepare()

View File

@ -66,10 +66,11 @@ public:
u8 GetWiimoteDeviceIndex() const override; u8 GetWiimoteDeviceIndex() const override;
void SetWiimoteDeviceIndex(u8 index) override; void SetWiimoteDeviceIndex(u8 index) override;
void Update() override; void PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state) override;
void Update(const WiimoteEmu::DesiredWiimoteState& target_state) override;
void EventLinked() override; void EventLinked() override;
void EventUnlinked() override; void EventUnlinked() override;
bool IsButtonPressed() override; WiimoteCommon::ButtonData GetCurrentlyPressedButtons() override;
void EmuStop(); void EmuStop();

View File

@ -19,6 +19,7 @@
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
#include "Core/HW/SystemTimers.h" #include "Core/HW/SystemTimers.h"
#include "Core/HW/Wiimote.h" #include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h"
#include "Core/IOS/Device.h" #include "Core/IOS/Device.h"
#include "Core/IOS/IOS.h" #include "Core/IOS/IOS.h"
#include "Core/SysConf.h" #include "Core/SysConf.h"
@ -58,7 +59,7 @@ BluetoothEmuDevice::BluetoothEmuDevice(Kernel& ios, const std::string& device_na
DEBUG_LOG_FMT(IOS_WIIMOTE, "Wii Remote {} BT ID {:x},{:x},{:x},{:x},{:x},{:x}", i, tmp_bd[0], DEBUG_LOG_FMT(IOS_WIIMOTE, "Wii Remote {} BT ID {:x},{:x},{:x},{:x},{:x},{:x}", i, tmp_bd[0],
tmp_bd[1], tmp_bd[2], tmp_bd[3], tmp_bd[4], tmp_bd[5]); tmp_bd[1], tmp_bd[2], tmp_bd[3], tmp_bd[4], tmp_bd[5]);
m_wiimotes.emplace_back(std::make_unique<WiimoteDevice>(this, tmp_bd, i)); m_wiimotes[i] = std::make_unique<WiimoteDevice>(this, tmp_bd, i);
} }
bt_dinf.num_registered = MAX_BBMOTES; bt_dinf.num_registered = MAX_BBMOTES;
@ -340,8 +341,16 @@ void BluetoothEmuDevice::Update()
{ {
g_controller_interface.SetCurrentInputChannel(ciface::InputChannel::Bluetooth); g_controller_interface.SetCurrentInputChannel(ciface::InputChannel::Bluetooth);
g_controller_interface.UpdateInput(); g_controller_interface.UpdateInput();
for (auto& wiimote : m_wiimotes)
wiimote->UpdateInput(); std::array<WiimoteEmu::DesiredWiimoteState, MAX_BBMOTES> wiimote_states;
std::array<WiimoteDevice::NextUpdateInputCall, MAX_BBMOTES> next_call;
for (size_t i = 0; i < m_wiimotes.size(); ++i)
next_call[i] = m_wiimotes[i]->PrepareInput(&wiimote_states[i]);
for (size_t i = 0; i < m_wiimotes.size(); ++i)
m_wiimotes[i]->UpdateInput(next_call[i], wiimote_states[i]);
m_last_ticks = now; m_last_ticks = now;
} }

View File

@ -60,7 +60,7 @@ public:
void DoState(PointerWrap& p) override; void DoState(PointerWrap& p) override;
private: private:
std::vector<std::unique_ptr<WiimoteDevice>> m_wiimotes; std::array<std::unique_ptr<WiimoteDevice>, MAX_BBMOTES> m_wiimotes;
bdaddr_t m_controller_bd{{0x11, 0x02, 0x19, 0x79, 0x00, 0xff}}; bdaddr_t m_controller_bd{{0x11, 0x02, 0x19, 0x79, 0x00, 0xff}};

View File

@ -21,6 +21,7 @@
#include "Core/HW/Wiimote.h" #include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteCommon/WiimoteConstants.h" #include "Core/HW/WiimoteCommon/WiimoteConstants.h"
#include "Core/HW/WiimoteCommon/WiimoteHid.h" #include "Core/HW/WiimoteCommon/WiimoteHid.h"
#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h"
#include "Core/Host.h" #include "Core/Host.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.h" #include "Core/IOS/USB/Bluetooth/BTEmu.h"
#include "Core/IOS/USB/Bluetooth/WiimoteHIDAttr.h" #include "Core/IOS/USB/Bluetooth/WiimoteHIDAttr.h"
@ -341,25 +342,51 @@ void WiimoteDevice::Update()
} }
} }
void WiimoteDevice::UpdateInput() WiimoteDevice::NextUpdateInputCall
WiimoteDevice::PrepareInput(WiimoteEmu::DesiredWiimoteState* wiimote_state)
{ {
if (m_connection_request_counter) if (m_connection_request_counter)
--m_connection_request_counter; --m_connection_request_counter;
if (!IsSourceValid()) if (!IsSourceValid())
return; return NextUpdateInputCall::None;
// Allow button press to trigger activation after a second of no connection activity. if (m_baseband_state == BasebandState::Inactive)
if (!m_connection_request_counter && m_baseband_state == BasebandState::Inactive)
{ {
if (Wiimote::NetPlay_GetButtonPress(GetNumber(), m_hid_source->IsButtonPressed())) // Allow button press to trigger activation after a second of no connection activity.
Activate(true); if (!m_connection_request_counter)
{
wiimote_state->buttons = m_hid_source->GetCurrentlyPressedButtons();
return NextUpdateInputCall::Activate;
}
return NextUpdateInputCall::None;
} }
// Verify interrupt channel is connected and configured. // Verify interrupt channel is connected and configured.
const auto* channel = FindChannelWithPSM(L2CAP_PSM_HID_INTR); const auto* channel = FindChannelWithPSM(L2CAP_PSM_HID_INTR);
if (channel && channel->IsComplete()) if (channel && channel->IsComplete())
m_hid_source->Update(); {
m_hid_source->PrepareInput(wiimote_state);
return NextUpdateInputCall::Update;
}
return NextUpdateInputCall::None;
}
void WiimoteDevice::UpdateInput(NextUpdateInputCall next_call,
const WiimoteEmu::DesiredWiimoteState& wiimote_state)
{
switch (next_call)
{
case NextUpdateInputCall::Activate:
if (wiimote_state.buttons.hex & WiimoteCommon::ButtonData::BUTTON_MASK)
Activate(true);
break;
case NextUpdateInputCall::Update:
m_hid_source->Update(wiimote_state);
break;
default:
break;
}
} }
// This function receives L2CAP commands from the CPU // This function receives L2CAP commands from the CPU

View File

@ -13,6 +13,11 @@
class PointerWrap; class PointerWrap;
namespace WiimoteEmu
{
struct DesiredWiimoteState;
}
namespace IOS::HLE namespace IOS::HLE
{ {
class BluetoothEmuDevice; class BluetoothEmuDevice;
@ -38,7 +43,15 @@ public:
void Update(); void Update();
// Called every ~200hz. // Called every ~200hz.
void UpdateInput(); enum class NextUpdateInputCall
{
None,
Activate,
Update
};
NextUpdateInputCall PrepareInput(WiimoteEmu::DesiredWiimoteState* wiimote_state);
void UpdateInput(NextUpdateInputCall next_call,
const WiimoteEmu::DesiredWiimoteState& wiimote_state);
void DoState(PointerWrap& p); void DoState(PointerWrap& p);