SI: Device change logic fix.

This commit is contained in:
Jordan Woyak 2019-01-26 10:29:06 -06:00
parent a129d60a57
commit b0cb100958
6 changed files with 67 additions and 65 deletions

View File

@ -6,6 +6,7 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <atomic>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
@ -102,6 +103,8 @@ struct SSIChannel
USIChannelIn_Hi in_hi; USIChannelIn_Hi in_hi;
USIChannelIn_Lo in_lo; USIChannelIn_Lo in_lo;
std::unique_ptr<ISIDevice> device; std::unique_ptr<ISIDevice> device;
bool has_recent_device_change;
}; };
// SI Poll: Controls how often a device is polled // 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_change_device_event;
static CoreTiming::EventType* s_tranfer_pending_event; static CoreTiming::EventType* s_tranfer_pending_event;
// User-configured device type. possibly overridden by TAS/Netplay
static std::array<std::atomic<SIDevices>, MAX_SI_CHANNELS> s_desired_device_types;
// STATE_TO_SAVE // STATE_TO_SAVE
static std::array<SSIChannel, MAX_SI_CHANNELS> s_channel; static std::array<SSIChannel, MAX_SI_CHANNELS> s_channel;
static USIPoll s_poll; static USIPoll s_poll;
@ -236,20 +242,8 @@ static void SetNoResponse(u32 channel)
static void ChangeDeviceCallback(u64 user_data, s64 cycles_late) static void ChangeDeviceCallback(u64 user_data, s64 cycles_late)
{ {
u8 channel = (u8)(user_data >> 32); // The purpose of this callback is to simply re-enable device changes.
SIDevices device = (SIDevices)(u32)user_data; s_channel[user_data].has_recent_device_change = false;
// 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);
}
} }
static void UpdateInterrupts() static void UpdateInterrupts()
@ -329,27 +323,19 @@ void DoState(PointerWrap& p)
p.Do(s_channel[i].in_hi.hex); p.Do(s_channel[i].in_hi.hex);
p.Do(s_channel[i].in_lo.hex); p.Do(s_channel[i].in_lo.hex);
p.Do(s_channel[i].out.hex); p.Do(s_channel[i].out.hex);
p.Do(s_channel[i].has_recent_device_change);
std::unique_ptr<ISIDevice>& device = s_channel[i].device; std::unique_ptr<ISIDevice>& device = s_channel[i].device;
SIDevices type = device->GetDeviceType(); SIDevices type = device->GetDeviceType();
p.Do(type); p.Do(type);
if (type == device->GetDeviceType()) if (type != device->GetDeviceType())
{ {
AddDevice(SIDevice_Create(type, i));
}
device->DoState(p); 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<ISIDevice> save_device = SIDevice_Create(type, i);
save_device->DoState(p);
AddDevice(std::move(save_device));
ChangeDeviceDeterministic(original_device, i);
}
}
p.Do(s_poll); p.Do(s_poll);
p.DoPOD(s_com_csr); p.DoPOD(s_com_csr);
@ -365,27 +351,30 @@ void Init()
s_channel[i].out.hex = 0; s_channel[i].out.hex = 0;
s_channel[i].in_hi.hex = 0; s_channel[i].in_hi.hex = 0;
s_channel[i].in_lo.hex = 0; s_channel[i].in_lo.hex = 0;
s_channel[i].has_recent_device_change = false;
if (Movie::IsMovieActive()) if (Movie::IsMovieActive())
{ {
s_desired_device_types[i] = SIDEVICE_NONE;
if (Movie::IsUsingPad(i)) if (Movie::IsUsingPad(i))
{ {
SIDevices current = SConfig::GetInstance().m_SIDevice[i]; SIDevices current = SConfig::GetInstance().m_SIDevice[i];
// GC pad-compatible devices can be used for both playing and recording // GC pad-compatible devices can be used for both playing and recording
if (SIDevice_IsGCController(current)) if (Movie::IsUsingBongo(i))
AddDevice(Movie::IsUsingBongo(i) ? SIDEVICE_GC_TARUKONGA : current, i); s_desired_device_types[i] = SIDEVICE_GC_TARUKONGA;
else if (SIDevice_IsGCController(current))
s_desired_device_types[i] = current;
else else
AddDevice(Movie::IsUsingBongo(i) ? SIDEVICE_GC_TARUKONGA : SIDEVICE_GC_CONTROLLER, i); s_desired_device_types[i] = SIDEVICE_GC_CONTROLLER;
}
else
{
AddDevice(SIDEVICE_NONE, i);
} }
} }
else if (!NetPlay::IsNetPlayRunning()) 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; s_poll.hex = 0;
@ -594,31 +583,48 @@ void AddDevice(const SIDevices device, int device_number)
void ChangeDevice(SIDevices device, int channel) void ChangeDevice(SIDevices device, int channel)
{ {
// Called from GUI, so we need to use FromThread::NON_CPU. // Actual device change will happen in UpdateDevices.
// Let the hardware see no device for 1 second s_desired_device_types[channel] = device;
// 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);
}
} }
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 (s_channel[channel].has_recent_device_change)
if (GetDeviceType(channel) != device) return;
if (GetDeviceType(channel) != SIDEVICE_NONE)
{ {
CoreTiming::ScheduleEvent(0, s_change_device_event, ((u64)channel << 32) | SIDEVICE_NONE); // Detach the current device before switching to the new one.
CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), s_change_device_event, device = SIDEVICE_NONE;
((u64)channel << 32) | 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);
// 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() 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 // Hinting NetPlay that all controllers will be polled in
// succession, in order to optimize networking // succession, in order to optimize networking
NetPlay::SetSIPollBatching(true); NetPlay::SetSIPollBatching(true);

View File

@ -38,7 +38,6 @@ void AddDevice(SIDevices device, int device_number);
void AddDevice(std::unique_ptr<ISIDevice> device); void AddDevice(std::unique_ptr<ISIDevice> device);
void ChangeDevice(SIDevices device, int channel); void ChangeDevice(SIDevices device, int channel);
void ChangeDeviceDeterministic(SIDevices device, int channel);
SIDevices GetDeviceType(int channel); SIDevices GetDeviceType(int channel);

View File

@ -409,7 +409,7 @@ bool IsNetPlayRecording()
} }
// NOTE: Host Thread // NOTE: Host Thread
void ChangePads(bool instantly) void ChangePads()
{ {
if (!Core::IsRunning()) if (!Core::IsRunning())
return; return;
@ -422,7 +422,7 @@ void ChangePads(bool instantly)
controllers |= (1 << i); controllers |= (1 << i);
} }
if (instantly && (s_controllers & 0x0F) == controllers) if ((s_controllers & 0x0F) == controllers)
return; return;
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i) for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
@ -441,9 +441,6 @@ 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); t_record.WriteArray(&tmpHeader, 1);
} }
ChangePads(true); ChangePads();
if (SConfig::GetInstance().bWii) if (SConfig::GetInstance().bWii)
ChangeWiiPads(true); ChangeWiiPads(true);

View File

@ -152,7 +152,7 @@ bool IsNetPlayRecording();
bool IsUsingPad(int controller); bool IsUsingPad(int controller);
bool IsUsingWiimote(int wiimote); bool IsUsingWiimote(int wiimote);
bool IsUsingBongo(int controller); bool IsUsingBongo(int controller);
void ChangePads(bool instantly = false); void ChangePads();
void ChangeWiiPads(bool instantly = false); void ChangeWiiPads(bool instantly = false);
void SetReadOnly(bool bEnabled); void SetReadOnly(bool bEnabled);

View File

@ -1566,21 +1566,21 @@ void NetPlayClient::UpdateDevices()
{ {
if (SerialInterface::SIDevice_IsGCController(SConfig::GetInstance().m_SIDevice[local_pad])) 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 else
{ {
SerialInterface::AddDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad); SerialInterface::ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad);
} }
local_pad++; local_pad++;
} }
else if (player_id > 0) else if (player_id > 0)
{ {
SerialInterface::AddDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad); SerialInterface::ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad);
} }
else else
{ {
SerialInterface::AddDevice(SerialInterface::SIDEVICE_NONE, pad); SerialInterface::ChangeDevice(SerialInterface::SIDEVICE_NONE, pad);
} }
pad++; pad++;
} }

View File

@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread; static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system // 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. // Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list, // Versions after 42 don't need to be added to this list,