From b0cb100958530969dea1a96bac94816592febf05 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 26 Jan 2019 10:29:06 -0600 Subject: [PATCH] SI: Device change logic fix. --- Source/Core/Core/HW/SI/SI.cpp | 108 +++++++++++++++-------------- Source/Core/Core/HW/SI/SI.h | 1 - Source/Core/Core/Movie.cpp | 11 ++- Source/Core/Core/Movie.h | 2 +- Source/Core/Core/NetPlayClient.cpp | 8 +-- Source/Core/Core/State.cpp | 2 +- 6 files changed, 67 insertions(+), 65 deletions(-) diff --git a/Source/Core/Core/HW/SI/SI.cpp b/Source/Core/Core/HW/SI/SI.cpp index 6cfe63ad4c..3438a4c950 100644 --- a/Source/Core/Core/HW/SI/SI.cpp +++ b/Source/Core/Core/HW/SI/SI.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -102,6 +103,8 @@ struct SSIChannel USIChannelIn_Hi in_hi; USIChannelIn_Lo in_lo; std::unique_ptr device; + + bool has_recent_device_change; }; // SI Poll: Controls how often a device is polled @@ -205,6 +208,9 @@ union USIEXIClockCount static CoreTiming::EventType* s_change_device_event; static CoreTiming::EventType* s_tranfer_pending_event; +// User-configured device type. possibly overridden by TAS/Netplay +static std::array, MAX_SI_CHANNELS> s_desired_device_types; + // STATE_TO_SAVE static std::array s_channel; static USIPoll s_poll; @@ -236,20 +242,8 @@ static void SetNoResponse(u32 channel) static void ChangeDeviceCallback(u64 user_data, s64 cycles_late) { - u8 channel = (u8)(user_data >> 32); - SIDevices device = (SIDevices)(u32)user_data; - - // Skip redundant (spammed) device changes - if (GetDeviceType(channel) != device) - { - s_channel[channel].out.hex = 0; - s_channel[channel].in_hi.hex = 0; - s_channel[channel].in_lo.hex = 0; - - SetNoResponse(channel); - - AddDevice(device, channel); - } + // The purpose of this callback is to simply re-enable device changes. + s_channel[user_data].has_recent_device_change = false; } static void UpdateInterrupts() @@ -329,26 +323,18 @@ void DoState(PointerWrap& p) p.Do(s_channel[i].in_hi.hex); p.Do(s_channel[i].in_lo.hex); p.Do(s_channel[i].out.hex); + p.Do(s_channel[i].has_recent_device_change); std::unique_ptr& device = s_channel[i].device; SIDevices type = device->GetDeviceType(); p.Do(type); - if (type == device->GetDeviceType()) + if (type != device->GetDeviceType()) { - device->DoState(p); - } - else - { - // If no movie is active, we'll assume the user wants to keep their current devices - // instead of the ones they had when the savestate was created. - // But we need to restore the current devices first just in case. - SIDevices original_device = device->GetDeviceType(); - std::unique_ptr save_device = SIDevice_Create(type, i); - save_device->DoState(p); - AddDevice(std::move(save_device)); - ChangeDeviceDeterministic(original_device, i); + AddDevice(SIDevice_Create(type, i)); } + + device->DoState(p); } p.Do(s_poll); @@ -365,27 +351,30 @@ void Init() s_channel[i].out.hex = 0; s_channel[i].in_hi.hex = 0; s_channel[i].in_lo.hex = 0; + s_channel[i].has_recent_device_change = false; if (Movie::IsMovieActive()) { + s_desired_device_types[i] = SIDEVICE_NONE; + if (Movie::IsUsingPad(i)) { SIDevices current = SConfig::GetInstance().m_SIDevice[i]; // GC pad-compatible devices can be used for both playing and recording - if (SIDevice_IsGCController(current)) - AddDevice(Movie::IsUsingBongo(i) ? SIDEVICE_GC_TARUKONGA : current, i); + if (Movie::IsUsingBongo(i)) + s_desired_device_types[i] = SIDEVICE_GC_TARUKONGA; + else if (SIDevice_IsGCController(current)) + s_desired_device_types[i] = current; else - AddDevice(Movie::IsUsingBongo(i) ? SIDEVICE_GC_TARUKONGA : SIDEVICE_GC_CONTROLLER, i); - } - else - { - AddDevice(SIDEVICE_NONE, i); + s_desired_device_types[i] = SIDEVICE_GC_CONTROLLER; } } else if (!NetPlay::IsNetPlayRunning()) { - AddDevice(SConfig::GetInstance().m_SIDevice[i], i); + s_desired_device_types[i] = SConfig::GetInstance().m_SIDevice[i]; } + + AddDevice(s_desired_device_types[i], i); } s_poll.hex = 0; @@ -594,31 +583,48 @@ void AddDevice(const SIDevices device, int device_number) void ChangeDevice(SIDevices device, int channel) { - // Called from GUI, so we need to use FromThread::NON_CPU. - // Let the hardware see no device for 1 second - // TODO: Calling GetDeviceType here isn't threadsafe. - if (GetDeviceType(channel) != device) - { - CoreTiming::ScheduleEvent(0, s_change_device_event, ((u64)channel << 32) | SIDEVICE_NONE, - CoreTiming::FromThread::NON_CPU); - CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), s_change_device_event, - ((u64)channel << 32) | device, CoreTiming::FromThread::NON_CPU); - } + // Actual device change will happen in UpdateDevices. + s_desired_device_types[channel] = device; } -void ChangeDeviceDeterministic(SIDevices device, int channel) +static void ChangeDeviceDeterministic(SIDevices device, int channel) { - // Called from savestates, so we don't use FromThread::NON_CPU. - if (GetDeviceType(channel) != device) + if (s_channel[channel].has_recent_device_change) + return; + + if (GetDeviceType(channel) != SIDEVICE_NONE) { - CoreTiming::ScheduleEvent(0, s_change_device_event, ((u64)channel << 32) | SIDEVICE_NONE); - CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), s_change_device_event, - ((u64)channel << 32) | device); + // Detach the current device before switching to the new one. + device = SIDEVICE_NONE; } + + s_channel[channel].out.hex = 0; + s_channel[channel].in_hi.hex = 0; + s_channel[channel].in_lo.hex = 0; + + SetNoResponse(channel); + + AddDevice(device, channel); + + // Prevent additional device changes on this channel for one second. + s_channel[channel].has_recent_device_change = true; + CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), s_change_device_event, channel); } void UpdateDevices() { + // Check for device change requests: + for (int i = 0; i != MAX_SI_CHANNELS; ++i) + { + const SIDevices current_type = GetDeviceType(i); + const SIDevices desired_type = s_desired_device_types[i]; + + if (current_type != desired_type) + { + ChangeDeviceDeterministic(desired_type, i); + } + } + // Hinting NetPlay that all controllers will be polled in // succession, in order to optimize networking NetPlay::SetSIPollBatching(true); diff --git a/Source/Core/Core/HW/SI/SI.h b/Source/Core/Core/HW/SI/SI.h index 5b1cce11dc..0e3094bc40 100644 --- a/Source/Core/Core/HW/SI/SI.h +++ b/Source/Core/Core/HW/SI/SI.h @@ -38,7 +38,6 @@ void AddDevice(SIDevices device, int device_number); void AddDevice(std::unique_ptr device); void ChangeDevice(SIDevices device, int channel); -void ChangeDeviceDeterministic(SIDevices device, int channel); SIDevices GetDeviceType(int channel); diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp index ae12021e47..85e4b47e52 100644 --- a/Source/Core/Core/Movie.cpp +++ b/Source/Core/Core/Movie.cpp @@ -409,7 +409,7 @@ bool IsNetPlayRecording() } // NOTE: Host Thread -void ChangePads(bool instantly) +void ChangePads() { if (!Core::IsRunning()) return; @@ -422,7 +422,7 @@ void ChangePads(bool instantly) controllers |= (1 << i); } - if (instantly && (s_controllers & 0x0F) == controllers) + if ((s_controllers & 0x0F) == controllers) return; for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i) @@ -441,10 +441,7 @@ void ChangePads(bool instantly) } } - if (instantly) // Changes from savestates need to be instantaneous - SerialInterface::AddDevice(device, i); - else - SerialInterface::ChangeDevice(device, i); + SerialInterface::ChangeDevice(device, i); } } @@ -961,7 +958,7 @@ void LoadInput(const std::string& movie_path) t_record.WriteArray(&tmpHeader, 1); } - ChangePads(true); + ChangePads(); if (SConfig::GetInstance().bWii) ChangeWiiPads(true); diff --git a/Source/Core/Core/Movie.h b/Source/Core/Core/Movie.h index 11a700f18b..b4e88cc47d 100644 --- a/Source/Core/Core/Movie.h +++ b/Source/Core/Core/Movie.h @@ -152,7 +152,7 @@ bool IsNetPlayRecording(); bool IsUsingPad(int controller); bool IsUsingWiimote(int wiimote); bool IsUsingBongo(int controller); -void ChangePads(bool instantly = false); +void ChangePads(); void ChangeWiiPads(bool instantly = false); void SetReadOnly(bool bEnabled); diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index c18b56ff13..f696b2ad27 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -1566,21 +1566,21 @@ void NetPlayClient::UpdateDevices() { if (SerialInterface::SIDevice_IsGCController(SConfig::GetInstance().m_SIDevice[local_pad])) { - SerialInterface::AddDevice(SConfig::GetInstance().m_SIDevice[local_pad], pad); + SerialInterface::ChangeDevice(SConfig::GetInstance().m_SIDevice[local_pad], pad); } else { - SerialInterface::AddDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad); + SerialInterface::ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad); } local_pad++; } else if (player_id > 0) { - SerialInterface::AddDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad); + SerialInterface::ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad); } else { - SerialInterface::AddDevice(SerialInterface::SIDEVICE_NONE, pad); + SerialInterface::ChangeDevice(SerialInterface::SIDEVICE_NONE, pad); } pad++; } diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 87d88d5e68..8e3020b0da 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent; static std::thread g_save_thread; // Don't forget to increase this after doing changes on the savestate system -static const u32 STATE_VERSION = 101; // Last changed in PR 7761 +static const u32 STATE_VERSION = 102; // Last changed in PR 7742 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list,