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/WiimoteReport.h"
namespace WiimoteEmu
{
struct DesiredWiimoteState;
}
namespace WiimoteCommon
{
// Source: HID_010_SPC_PFL/1.0 (official HID specification)
@ -34,7 +39,8 @@ public:
virtual void SetWiimoteDeviceIndex(u8 index) = 0;
// 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); }
@ -42,8 +48,10 @@ public:
// Does not include HID-type header.
virtual void InterruptDataOutput(const u8* data, u32 size) = 0;
// Used to connect a disconnected wii remote on button press.
virtual bool IsButtonPressed() = 0;
// Get a snapshot of the current state of the Wiimote's buttons.
// 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:
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;
}
DesiredWiimoteState Wiimote::BuildDesiredWiimoteState()
void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state)
{
// Hotkey / settings modifier
// Data is later accessed in IsSideways and IsUpright
@ -457,36 +457,35 @@ DesiredWiimoteState Wiimote::BuildDesiredWiimoteState()
// Update our motion simulations.
StepDynamics();
DesiredWiimoteState wiimote_state;
// Fetch pressed buttons from user input.
m_buttons->GetState(&wiimote_state.buttons.hex, button_bitmasks);
m_dpad->GetState(&wiimote_state.buttons.hex,
target_state->buttons.hex = 0;
m_buttons->GetState(&target_state->buttons.hex, button_bitmasks);
m_dpad->GetState(&target_state->buttons.hex,
IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks);
// Calculate accelerometer state.
// 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);
// Calculate IR camera state.
wiimote_state.camera_points = CameraLogic::GetCameraPoints(
target_state->camera_points = CameraLogic::GetCameraPoints(
GetTotalTransformation(),
Common::Vec2(m_fov_x_setting.GetValue(), m_fov_y_setting.GetValue()) / 360 *
float(MathUtil::TAU));
// Calculate MotionPlus state.
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.
// This also allows the extension to perform any regular duties it may need.
// (e.g. Nunchuk motion simulation step)
static_cast<Extension*>(
m_attachments->GetAttachmentList()[m_attachments->GetSelectedAttachment()].get())
->BuildDesiredExtensionState(&wiimote_state.extension);
return wiimote_state;
->BuildDesiredExtensionState(&target_state->extension);
}
u8 Wiimote::GetWiimoteDeviceIndex() const
@ -500,13 +499,14 @@ void Wiimote::SetWiimoteDeviceIndex(u8 index)
}
// This is called every ::Wiimote::UPDATE_FREQ (200hz)
void Wiimote::Update()
void Wiimote::PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state)
{
const auto lock = GetStateLock();
BuildDesiredWiimoteState(target_state);
}
// Build target state.
auto target_state = BuildDesiredWiimoteState();
void Wiimote::Update(const WiimoteEmu::DesiredWiimoteState& target_state)
{
// Update buttons in the status struct which is sent in 99% of input reports.
UpdateButtonsStatus(target_state);
@ -656,14 +656,15 @@ void Wiimote::SendDataReport(const DesiredWiimoteState& target_state)
m_reporting_mode = InputReportID::ReportInterleave1;
}
bool Wiimote::IsButtonPressed()
ButtonData Wiimote::GetCurrentlyPressedButtons()
{
u16 buttons = 0;
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)

View file

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

View file

@ -460,7 +460,12 @@ void Wiimote::SetWiimoteDeviceIndex(u8 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.
// 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));
}
bool Wiimote::IsButtonPressed()
ButtonData Wiimote::GetCurrentlyPressedButtons()
{
Report& rpt = m_last_input_report;
if (rpt.size() >= 4)
@ -499,10 +504,10 @@ bool Wiimote::IsButtonPressed()
ButtonData buttons = {};
builder->GetCoreData(&buttons);
return buttons.hex != 0;
return buttons;
}
}
return false;
return ButtonData{};
}
void Wiimote::Prepare()

View file

@ -66,10 +66,11 @@ public:
u8 GetWiimoteDeviceIndex() const 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 EventUnlinked() override;
bool IsButtonPressed() override;
WiimoteCommon::ButtonData GetCurrentlyPressedButtons() override;
void EmuStop();

View file

@ -19,6 +19,7 @@
#include "Core/HW/Memmap.h"
#include "Core/HW/SystemTimers.h"
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h"
#include "Core/IOS/Device.h"
#include "Core/IOS/IOS.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],
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;
@ -340,8 +341,16 @@ void BluetoothEmuDevice::Update()
{
g_controller_interface.SetCurrentInputChannel(ciface::InputChannel::Bluetooth);
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;
}

View file

@ -60,7 +60,7 @@ public:
void DoState(PointerWrap& p) override;
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}};

View file

@ -21,6 +21,7 @@
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h"
#include "Core/Host.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.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)
--m_connection_request_counter;
if (!IsSourceValid())
return;
return NextUpdateInputCall::None;
// Allow button press to trigger activation after a second of no connection activity.
if (!m_connection_request_counter && m_baseband_state == BasebandState::Inactive)
if (m_baseband_state == BasebandState::Inactive)
{
if (Wiimote::NetPlay_GetButtonPress(GetNumber(), m_hid_source->IsButtonPressed()))
Activate(true);
// Allow button press to trigger activation after a second of no connection activity.
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.
const auto* channel = FindChannelWithPSM(L2CAP_PSM_HID_INTR);
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

View file

@ -13,6 +13,11 @@
class PointerWrap;
namespace WiimoteEmu
{
struct DesiredWiimoteState;
}
namespace IOS::HLE
{
class BluetoothEmuDevice;
@ -38,7 +43,15 @@ public:
void Update();
// 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);