Merge pull request #5803 from leoetlino/pause-and-lock
Replace balanced Core::PauseAndLock calls with RunAsCPUThread
This commit is contained in:
commit
57affaff22
|
@ -102,7 +102,6 @@ static StoppedCallbackFunc s_on_stopped_callback;
|
||||||
|
|
||||||
static std::thread s_cpu_thread;
|
static std::thread s_cpu_thread;
|
||||||
static bool s_request_refresh_info = false;
|
static bool s_request_refresh_info = false;
|
||||||
static int s_pause_and_lock_depth = 0;
|
|
||||||
static bool s_is_throttler_temp_disabled = false;
|
static bool s_is_throttler_temp_disabled = false;
|
||||||
|
|
||||||
struct HostJob
|
struct HostJob
|
||||||
|
@ -759,17 +758,12 @@ void RequestRefreshInfo()
|
||||||
s_request_refresh_info = true;
|
s_request_refresh_info = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PauseAndLock(bool do_lock, bool unpause_on_unlock)
|
static bool PauseAndLock(bool do_lock, bool unpause_on_unlock)
|
||||||
{
|
{
|
||||||
// WARNING: PauseAndLock is not fully threadsafe so is only valid on the Host Thread
|
// WARNING: PauseAndLock is not fully threadsafe so is only valid on the Host Thread
|
||||||
if (!IsRunning())
|
if (!IsRunning())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// let's support recursive locking to simplify things on the caller's side,
|
|
||||||
// and let's do it at this outer level in case the individual systems don't support it.
|
|
||||||
if (do_lock ? s_pause_and_lock_depth++ : --s_pause_and_lock_depth)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
bool was_unpaused = true;
|
bool was_unpaused = true;
|
||||||
if (do_lock)
|
if (do_lock)
|
||||||
{
|
{
|
||||||
|
@ -806,6 +800,19 @@ bool PauseAndLock(bool do_lock, bool unpause_on_unlock)
|
||||||
return was_unpaused;
|
return was_unpaused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RunAsCPUThread(std::function<void()> function)
|
||||||
|
{
|
||||||
|
const bool is_cpu_thread = IsCPUThread();
|
||||||
|
bool was_unpaused = false;
|
||||||
|
if (!is_cpu_thread)
|
||||||
|
was_unpaused = PauseAndLock(true, true);
|
||||||
|
|
||||||
|
function();
|
||||||
|
|
||||||
|
if (!is_cpu_thread)
|
||||||
|
PauseAndLock(false, was_unpaused);
|
||||||
|
}
|
||||||
|
|
||||||
// Display FPS info
|
// Display FPS info
|
||||||
// This should only be called from VI
|
// This should only be called from VI
|
||||||
void VideoThrottle()
|
void VideoThrottle()
|
||||||
|
@ -955,22 +962,20 @@ void UpdateWantDeterminism(bool initial)
|
||||||
{
|
{
|
||||||
NOTICE_LOG(COMMON, "Want determinism <- %s", new_want_determinism ? "true" : "false");
|
NOTICE_LOG(COMMON, "Want determinism <- %s", new_want_determinism ? "true" : "false");
|
||||||
|
|
||||||
bool was_unpaused = Core::PauseAndLock(true);
|
RunAsCPUThread([&] {
|
||||||
|
s_wants_determinism = new_want_determinism;
|
||||||
|
const auto ios = IOS::HLE::GetIOS();
|
||||||
|
if (ios)
|
||||||
|
ios->UpdateWantDeterminism(new_want_determinism);
|
||||||
|
Fifo::UpdateWantDeterminism(new_want_determinism);
|
||||||
|
// We need to clear the cache because some parts of the JIT depend on want_determinism,
|
||||||
|
// e.g. use of FMA.
|
||||||
|
JitInterface::ClearCache();
|
||||||
|
|
||||||
s_wants_determinism = new_want_determinism;
|
// Don't call InitializeWiiRoot during boot, because IOS already does it.
|
||||||
const auto ios = IOS::HLE::GetIOS();
|
if (!initial)
|
||||||
if (ios)
|
Core::InitializeWiiRoot(s_wants_determinism);
|
||||||
ios->UpdateWantDeterminism(new_want_determinism);
|
});
|
||||||
Fifo::UpdateWantDeterminism(new_want_determinism);
|
|
||||||
// We need to clear the cache because some parts of the JIT depend on want_determinism, e.g. use
|
|
||||||
// of FMA.
|
|
||||||
JitInterface::ClearCache();
|
|
||||||
|
|
||||||
// Don't call InitializeWiiRoot during boot, because IOS already does it.
|
|
||||||
if (!initial)
|
|
||||||
Core::InitializeWiiRoot(s_wants_determinism);
|
|
||||||
|
|
||||||
Core::PauseAndLock(false, was_unpaused);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,12 +74,14 @@ void RequestRefreshInfo();
|
||||||
|
|
||||||
void UpdateTitle();
|
void UpdateTitle();
|
||||||
|
|
||||||
// waits until all systems are paused and fully idle, and acquires a lock on that state.
|
// Run a function as the CPU thread.
|
||||||
// or, if doLock is false, releases a lock on that state and optionally unpauses.
|
//
|
||||||
// calls must be balanced (once with doLock true, then once with doLock false) but may be recursive.
|
// If called from the Host thread, the CPU thread is paused and the current thread temporarily
|
||||||
// the return value of the first call should be passed in as the second argument of the second call.
|
// becomes the CPU thread while running the function.
|
||||||
// [NOT THREADSAFE] Host only
|
// If called from the CPU thread, the function will be run directly.
|
||||||
bool PauseAndLock(bool doLock, bool unpauseOnUnlock = true);
|
//
|
||||||
|
// This should only be called from the CPU thread or the host thread.
|
||||||
|
void RunAsCPUThread(std::function<void()> function);
|
||||||
|
|
||||||
// for calling back into UI code without introducing a dependency on it in core
|
// for calling back into UI code without introducing a dependency on it in core
|
||||||
using StoppedCallbackFunc = std::function<void()>;
|
using StoppedCallbackFunc = std::function<void()>;
|
||||||
|
|
|
@ -476,12 +476,7 @@ static void InsertDiscCallback(u64 userdata, s64 cyclesLate)
|
||||||
// Can only be called by the host thread
|
// Can only be called by the host thread
|
||||||
void ChangeDiscAsHost(const std::string& new_path)
|
void ChangeDiscAsHost(const std::string& new_path)
|
||||||
{
|
{
|
||||||
bool was_unpaused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] { ChangeDiscAsCPU(new_path); });
|
||||||
|
|
||||||
// The host thread is now temporarily the CPU thread
|
|
||||||
ChangeDiscAsCPU(new_path);
|
|
||||||
|
|
||||||
Core::PauseAndLock(false, was_unpaused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can only be called by the CPU thread
|
// Can only be called by the CPU thread
|
||||||
|
|
|
@ -544,68 +544,66 @@ bool BeginRecordingInput(int controllers)
|
||||||
if (s_playMode != MODE_NONE || controllers == 0)
|
if (s_playMode != MODE_NONE || controllers == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool was_unpaused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([controllers] {
|
||||||
|
s_controllers = controllers;
|
||||||
|
s_currentFrame = s_totalFrames = 0;
|
||||||
|
s_currentLagCount = s_totalLagCount = 0;
|
||||||
|
s_currentInputCount = s_totalInputCount = 0;
|
||||||
|
s_totalTickCount = s_tickCountAtLastInput = 0;
|
||||||
|
s_bongos = 0;
|
||||||
|
s_memcards = 0;
|
||||||
|
if (NetPlay::IsNetPlayRunning())
|
||||||
|
{
|
||||||
|
s_bNetPlay = true;
|
||||||
|
s_recordingStartTime = ExpansionInterface::CEXIIPL::NetPlay_GetEmulatedTime();
|
||||||
|
}
|
||||||
|
else if (SConfig::GetInstance().bEnableCustomRTC)
|
||||||
|
{
|
||||||
|
s_recordingStartTime = SConfig::GetInstance().m_customRTCValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_recordingStartTime = Common::Timer::GetLocalTimeSinceJan1970();
|
||||||
|
}
|
||||||
|
|
||||||
s_controllers = controllers;
|
s_rerecords = 0;
|
||||||
s_currentFrame = s_totalFrames = 0;
|
|
||||||
s_currentLagCount = s_totalLagCount = 0;
|
|
||||||
s_currentInputCount = s_totalInputCount = 0;
|
|
||||||
s_totalTickCount = s_tickCountAtLastInput = 0;
|
|
||||||
s_bongos = 0;
|
|
||||||
s_memcards = 0;
|
|
||||||
if (NetPlay::IsNetPlayRunning())
|
|
||||||
{
|
|
||||||
s_bNetPlay = true;
|
|
||||||
s_recordingStartTime = ExpansionInterface::CEXIIPL::NetPlay_GetEmulatedTime();
|
|
||||||
}
|
|
||||||
else if (SConfig::GetInstance().bEnableCustomRTC)
|
|
||||||
{
|
|
||||||
s_recordingStartTime = SConfig::GetInstance().m_customRTCValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s_recordingStartTime = Common::Timer::GetLocalTimeSinceJan1970();
|
|
||||||
}
|
|
||||||
|
|
||||||
s_rerecords = 0;
|
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
|
||||||
|
{
|
||||||
|
if (SConfig::GetInstance().m_SIDevice[i] == SerialInterface::SIDEVICE_GC_TARUKONGA)
|
||||||
|
s_bongos |= (1 << i);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
|
if (Core::IsRunningAndStarted())
|
||||||
{
|
{
|
||||||
if (SConfig::GetInstance().m_SIDevice[i] == SerialInterface::SIDEVICE_GC_TARUKONGA)
|
const std::string save_path = File::GetUserPath(D_STATESAVES_IDX) + "dtm.sav";
|
||||||
s_bongos |= (1 << i);
|
if (File::Exists(save_path))
|
||||||
}
|
File::Delete(save_path);
|
||||||
|
|
||||||
if (Core::IsRunningAndStarted())
|
State::SaveAs(save_path);
|
||||||
{
|
s_bRecordingFromSaveState = true;
|
||||||
const std::string save_path = File::GetUserPath(D_STATESAVES_IDX) + "dtm.sav";
|
|
||||||
if (File::Exists(save_path))
|
|
||||||
File::Delete(save_path);
|
|
||||||
|
|
||||||
State::SaveAs(save_path);
|
std::thread md5thread(GetMD5);
|
||||||
s_bRecordingFromSaveState = true;
|
md5thread.detach();
|
||||||
|
GetSettings();
|
||||||
|
}
|
||||||
|
|
||||||
std::thread md5thread(GetMD5);
|
// Wiimotes cause desync issues if they're not reset before launching the game
|
||||||
md5thread.detach();
|
if (!Core::IsRunningAndStarted())
|
||||||
GetSettings();
|
{
|
||||||
}
|
// This will also reset the wiimotes for gamecube games, but that shouldn't do anything
|
||||||
|
Wiimote::ResetAllWiimotes();
|
||||||
|
}
|
||||||
|
|
||||||
// Wiimotes cause desync issues if they're not reset before launching the game
|
s_playMode = MODE_RECORDING;
|
||||||
if (!Core::IsRunningAndStarted())
|
s_author = SConfig::GetInstance().m_strMovieAuthor;
|
||||||
{
|
s_temp_input.clear();
|
||||||
// This will also reset the wiimotes for gamecube games, but that shouldn't do anything
|
|
||||||
Wiimote::ResetAllWiimotes();
|
|
||||||
}
|
|
||||||
|
|
||||||
s_playMode = MODE_RECORDING;
|
s_currentByte = 0;
|
||||||
s_author = SConfig::GetInstance().m_strMovieAuthor;
|
|
||||||
s_temp_input.clear();
|
|
||||||
|
|
||||||
s_currentByte = 0;
|
if (Core::IsRunning())
|
||||||
|
Core::UpdateWantDeterminism();
|
||||||
if (Core::IsRunning())
|
});
|
||||||
Core::UpdateWantDeterminism();
|
|
||||||
|
|
||||||
Core::PauseAndLock(false, was_unpaused);
|
|
||||||
|
|
||||||
Core::DisplayMessage("Starting movie recording", 2000);
|
Core::DisplayMessage("Starting movie recording", 2000);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -174,14 +174,14 @@ void MemChecks::Add(const TMemCheck& memory_check)
|
||||||
if (GetMemCheck(memory_check.start_address) == nullptr)
|
if (GetMemCheck(memory_check.start_address) == nullptr)
|
||||||
{
|
{
|
||||||
bool had_any = HasAny();
|
bool had_any = HasAny();
|
||||||
bool lock = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
m_mem_checks.push_back(memory_check);
|
m_mem_checks.push_back(memory_check);
|
||||||
// If this is the first one, clear the JIT cache so it can switch to
|
// If this is the first one, clear the JIT cache so it can switch to
|
||||||
// watchpoint-compatible code.
|
// watchpoint-compatible code.
|
||||||
if (!had_any && g_jit)
|
if (!had_any && g_jit)
|
||||||
g_jit->ClearCache();
|
g_jit->ClearCache();
|
||||||
PowerPC::DBATUpdated();
|
PowerPC::DBATUpdated();
|
||||||
Core::PauseAndLock(false, lock);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,12 +191,12 @@ void MemChecks::Remove(u32 address)
|
||||||
{
|
{
|
||||||
if (i->start_address == address)
|
if (i->start_address == address)
|
||||||
{
|
{
|
||||||
bool lock = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
m_mem_checks.erase(i);
|
m_mem_checks.erase(i);
|
||||||
if (!HasAny() && g_jit)
|
if (!HasAny() && g_jit)
|
||||||
g_jit->ClearCache();
|
g_jit->ClearCache();
|
||||||
PowerPC::DBATUpdated();
|
PowerPC::DBATUpdated();
|
||||||
Core::PauseAndLock(false, lock);
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,42 +207,36 @@ void LoadFromBuffer(std::vector<u8>& buffer)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wasUnpaused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
|
u8* ptr = &buffer[0];
|
||||||
u8* ptr = &buffer[0];
|
PointerWrap p(&ptr, PointerWrap::MODE_READ);
|
||||||
PointerWrap p(&ptr, PointerWrap::MODE_READ);
|
DoState(p);
|
||||||
DoState(p);
|
});
|
||||||
|
|
||||||
Core::PauseAndLock(false, wasUnpaused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveToBuffer(std::vector<u8>& buffer)
|
void SaveToBuffer(std::vector<u8>& buffer)
|
||||||
{
|
{
|
||||||
bool wasUnpaused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
|
u8* ptr = nullptr;
|
||||||
|
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
|
||||||
|
|
||||||
u8* ptr = nullptr;
|
DoState(p);
|
||||||
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
|
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
|
||||||
|
buffer.resize(buffer_size);
|
||||||
|
|
||||||
DoState(p);
|
ptr = &buffer[0];
|
||||||
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
|
p.SetMode(PointerWrap::MODE_WRITE);
|
||||||
buffer.resize(buffer_size);
|
DoState(p);
|
||||||
|
});
|
||||||
ptr = &buffer[0];
|
|
||||||
p.SetMode(PointerWrap::MODE_WRITE);
|
|
||||||
DoState(p);
|
|
||||||
|
|
||||||
Core::PauseAndLock(false, wasUnpaused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerifyBuffer(std::vector<u8>& buffer)
|
void VerifyBuffer(std::vector<u8>& buffer)
|
||||||
{
|
{
|
||||||
bool wasUnpaused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
|
u8* ptr = &buffer[0];
|
||||||
u8* ptr = &buffer[0];
|
PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
|
||||||
PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
|
DoState(p);
|
||||||
DoState(p);
|
});
|
||||||
|
|
||||||
Core::PauseAndLock(false, wasUnpaused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return state number not in map
|
// return state number not in map
|
||||||
|
@ -399,48 +393,44 @@ static void CompressAndDumpState(CompressAndDumpState_args save_args)
|
||||||
|
|
||||||
void SaveAs(const std::string& filename, bool wait)
|
void SaveAs(const std::string& filename, bool wait)
|
||||||
{
|
{
|
||||||
// Pause the core while we save the state
|
Core::RunAsCPUThread([&] {
|
||||||
bool wasUnpaused = Core::PauseAndLock(true);
|
// Measure the size of the buffer.
|
||||||
|
u8* ptr = nullptr;
|
||||||
// Measure the size of the buffer.
|
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
|
||||||
u8* ptr = nullptr;
|
|
||||||
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
|
|
||||||
DoState(p);
|
|
||||||
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
|
|
||||||
|
|
||||||
// Then actually do the write.
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_cs_current_buffer);
|
|
||||||
g_current_buffer.resize(buffer_size);
|
|
||||||
ptr = &g_current_buffer[0];
|
|
||||||
p.SetMode(PointerWrap::MODE_WRITE);
|
|
||||||
DoState(p);
|
DoState(p);
|
||||||
}
|
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
|
||||||
|
|
||||||
if (p.GetMode() == PointerWrap::MODE_WRITE)
|
// Then actually do the write.
|
||||||
{
|
{
|
||||||
Core::DisplayMessage("Saving State...", 1000);
|
std::lock_guard<std::mutex> lk(g_cs_current_buffer);
|
||||||
|
g_current_buffer.resize(buffer_size);
|
||||||
|
ptr = &g_current_buffer[0];
|
||||||
|
p.SetMode(PointerWrap::MODE_WRITE);
|
||||||
|
DoState(p);
|
||||||
|
}
|
||||||
|
|
||||||
CompressAndDumpState_args save_args;
|
if (p.GetMode() == PointerWrap::MODE_WRITE)
|
||||||
save_args.buffer_vector = &g_current_buffer;
|
{
|
||||||
save_args.buffer_mutex = &g_cs_current_buffer;
|
Core::DisplayMessage("Saving State...", 1000);
|
||||||
save_args.filename = filename;
|
|
||||||
save_args.wait = wait;
|
|
||||||
|
|
||||||
Flush();
|
CompressAndDumpState_args save_args;
|
||||||
g_save_thread = std::thread(CompressAndDumpState, save_args);
|
save_args.buffer_vector = &g_current_buffer;
|
||||||
g_compressAndDumpStateSyncEvent.Wait();
|
save_args.buffer_mutex = &g_cs_current_buffer;
|
||||||
|
save_args.filename = filename;
|
||||||
|
save_args.wait = wait;
|
||||||
|
|
||||||
g_last_filename = filename;
|
Flush();
|
||||||
}
|
g_save_thread = std::thread(CompressAndDumpState, save_args);
|
||||||
else
|
g_compressAndDumpStateSyncEvent.Wait();
|
||||||
{
|
|
||||||
// someone aborted the save by changing the mode?
|
|
||||||
Core::DisplayMessage("Unable to save: Internal DoState Error", 4000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resume the core and disable stepping
|
g_last_filename = filename;
|
||||||
Core::PauseAndLock(false, wasUnpaused);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// someone aborted the save by changing the mode?
|
||||||
|
Core::DisplayMessage("Unable to save: Internal DoState Error", 4000);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadHeader(const std::string& filename, StateHeader& header)
|
bool ReadHeader(const std::string& filename, StateHeader& header)
|
||||||
|
@ -549,72 +539,68 @@ void LoadAs(const std::string& filename)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop the core while we load the state
|
Core::RunAsCPUThread([&] {
|
||||||
bool wasUnpaused = Core::PauseAndLock(true);
|
g_loadDepth++;
|
||||||
|
|
||||||
g_loadDepth++;
|
// Save temp buffer for undo load state
|
||||||
|
if (!Movie::IsJustStartingRecordingInputFromSaveState())
|
||||||
// Save temp buffer for undo load state
|
|
||||||
if (!Movie::IsJustStartingRecordingInputFromSaveState())
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(g_cs_undo_load_buffer);
|
|
||||||
SaveToBuffer(g_undo_load_buffer);
|
|
||||||
if (Movie::IsMovieActive())
|
|
||||||
Movie::SaveRecording(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm");
|
|
||||||
else if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm"))
|
|
||||||
File::Delete(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool loaded = false;
|
|
||||||
bool loadedSuccessfully = false;
|
|
||||||
std::string version_created_by;
|
|
||||||
|
|
||||||
// brackets here are so buffer gets freed ASAP
|
|
||||||
{
|
|
||||||
std::vector<u8> buffer;
|
|
||||||
LoadFileStateData(filename, buffer);
|
|
||||||
|
|
||||||
if (!buffer.empty())
|
|
||||||
{
|
{
|
||||||
u8* ptr = &buffer[0];
|
std::lock_guard<std::mutex> lk(g_cs_undo_load_buffer);
|
||||||
PointerWrap p(&ptr, PointerWrap::MODE_READ);
|
SaveToBuffer(g_undo_load_buffer);
|
||||||
version_created_by = DoState(p);
|
if (Movie::IsMovieActive())
|
||||||
loaded = true;
|
Movie::SaveRecording(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm");
|
||||||
loadedSuccessfully = (p.GetMode() == PointerWrap::MODE_READ);
|
else if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm"))
|
||||||
|
File::Delete(File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (loaded)
|
bool loaded = false;
|
||||||
{
|
bool loadedSuccessfully = false;
|
||||||
if (loadedSuccessfully)
|
std::string version_created_by;
|
||||||
|
|
||||||
|
// brackets here are so buffer gets freed ASAP
|
||||||
{
|
{
|
||||||
Core::DisplayMessage(StringFromFormat("Loaded state from %s", filename.c_str()), 2000);
|
std::vector<u8> buffer;
|
||||||
if (File::Exists(filename + ".dtm"))
|
LoadFileStateData(filename, buffer);
|
||||||
Movie::LoadInput(filename + ".dtm");
|
|
||||||
else if (!Movie::IsJustStartingRecordingInputFromSaveState() &&
|
if (!buffer.empty())
|
||||||
!Movie::IsJustStartingPlayingInputFromSaveState())
|
{
|
||||||
Movie::EndPlayInput(false);
|
u8* ptr = &buffer[0];
|
||||||
|
PointerWrap p(&ptr, PointerWrap::MODE_READ);
|
||||||
|
version_created_by = DoState(p);
|
||||||
|
loaded = true;
|
||||||
|
loadedSuccessfully = (p.GetMode() == PointerWrap::MODE_READ);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (loaded)
|
||||||
{
|
{
|
||||||
// failed to load
|
if (loadedSuccessfully)
|
||||||
Core::DisplayMessage("Unable to load: Can't load state from other versions!", 4000);
|
{
|
||||||
if (!version_created_by.empty())
|
Core::DisplayMessage(StringFromFormat("Loaded state from %s", filename.c_str()), 2000);
|
||||||
Core::DisplayMessage("The savestate was created using " + version_created_by, 4000);
|
if (File::Exists(filename + ".dtm"))
|
||||||
|
Movie::LoadInput(filename + ".dtm");
|
||||||
|
else if (!Movie::IsJustStartingRecordingInputFromSaveState() &&
|
||||||
|
!Movie::IsJustStartingPlayingInputFromSaveState())
|
||||||
|
Movie::EndPlayInput(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// failed to load
|
||||||
|
Core::DisplayMessage("Unable to load: Can't load state from other versions!", 4000);
|
||||||
|
if (!version_created_by.empty())
|
||||||
|
Core::DisplayMessage("The savestate was created using " + version_created_by, 4000);
|
||||||
|
|
||||||
// since we could be in an inconsistent state now (and might crash or whatever), undo.
|
// since we could be in an inconsistent state now (and might crash or whatever), undo.
|
||||||
if (g_loadDepth < 2)
|
if (g_loadDepth < 2)
|
||||||
UndoLoadState();
|
UndoLoadState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (s_on_after_load_callback)
|
if (s_on_after_load_callback)
|
||||||
s_on_after_load_callback();
|
s_on_after_load_callback();
|
||||||
|
|
||||||
g_loadDepth--;
|
g_loadDepth--;
|
||||||
|
});
|
||||||
// resume dat core
|
|
||||||
Core::PauseAndLock(false, wasUnpaused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetOnAfterLoadCallback(AfterLoadCallbackFunc callback)
|
void SetOnAfterLoadCallback(AfterLoadCallbackFunc callback)
|
||||||
|
@ -624,24 +610,22 @@ void SetOnAfterLoadCallback(AfterLoadCallbackFunc callback)
|
||||||
|
|
||||||
void VerifyAt(const std::string& filename)
|
void VerifyAt(const std::string& filename)
|
||||||
{
|
{
|
||||||
bool wasUnpaused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
|
std::vector<u8> buffer;
|
||||||
|
LoadFileStateData(filename, buffer);
|
||||||
|
|
||||||
std::vector<u8> buffer;
|
if (!buffer.empty())
|
||||||
LoadFileStateData(filename, buffer);
|
{
|
||||||
|
u8* ptr = &buffer[0];
|
||||||
|
PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
|
||||||
|
DoState(p);
|
||||||
|
|
||||||
if (!buffer.empty())
|
if (p.GetMode() == PointerWrap::MODE_VERIFY)
|
||||||
{
|
Core::DisplayMessage(StringFromFormat("Verified state at %s", filename.c_str()), 2000);
|
||||||
u8* ptr = &buffer[0];
|
else
|
||||||
PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
|
Core::DisplayMessage("Unable to Verify : Can't verify state from other revisions !", 4000);
|
||||||
DoState(p);
|
}
|
||||||
|
});
|
||||||
if (p.GetMode() == PointerWrap::MODE_VERIFY)
|
|
||||||
Core::DisplayMessage(StringFromFormat("Verified state at %s", filename.c_str()), 2000);
|
|
||||||
else
|
|
||||||
Core::DisplayMessage("Unable to Verify : Can't verify state from other revisions !", 4000);
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::PauseAndLock(false, wasUnpaused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init()
|
void Init()
|
||||||
|
|
|
@ -137,13 +137,13 @@ void Host_ConnectWiimote(int wm_idx, bool connect)
|
||||||
const auto ios = IOS::HLE::GetIOS();
|
const auto ios = IOS::HLE::GetIOS();
|
||||||
if (!ios || SConfig::GetInstance().m_bt_passthrough_enabled)
|
if (!ios || SConfig::GetInstance().m_bt_passthrough_enabled)
|
||||||
return;
|
return;
|
||||||
bool was_unpaused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
|
const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
|
||||||
ios->GetDeviceByName("/dev/usb/oh1/57e/305"));
|
ios->GetDeviceByName("/dev/usb/oh1/57e/305"));
|
||||||
if (bt)
|
if (bt)
|
||||||
bt->AccessWiiMote(wm_idx | 0x100)->Activate(connect);
|
bt->AccessWiiMote(wm_idx | 0x100)->Activate(connect);
|
||||||
Host_UpdateMainFrame();
|
Host_UpdateMainFrame();
|
||||||
Core::PauseAndLock(false, was_unpaused);
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -237,25 +237,24 @@ void IOWindow::UpdateDeviceList()
|
||||||
m_block.Set(true);
|
m_block.Set(true);
|
||||||
m_devices_combo->clear();
|
m_devices_combo->clear();
|
||||||
|
|
||||||
const bool paused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
|
g_controller_interface.RefreshDevices();
|
||||||
|
m_controller->UpdateReferences(g_controller_interface);
|
||||||
|
m_controller->UpdateDefaultDevice();
|
||||||
|
|
||||||
g_controller_interface.RefreshDevices();
|
// Adding default device regardless if it's currently connected or not
|
||||||
m_controller->UpdateReferences(g_controller_interface);
|
const auto default_device = m_controller->default_device.ToString();
|
||||||
m_controller->UpdateDefaultDevice();
|
|
||||||
|
|
||||||
// Adding default device regardless if it's currently connected or not
|
m_devices_combo->addItem(QString::fromStdString(default_device));
|
||||||
const auto default_device = m_controller->default_device.ToString();
|
|
||||||
|
|
||||||
m_devices_combo->addItem(QString::fromStdString(default_device));
|
for (const auto& name : g_controller_interface.GetAllDeviceStrings())
|
||||||
|
{
|
||||||
|
if (name != default_device)
|
||||||
|
m_devices_combo->addItem(QString::fromStdString(name));
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& name : g_controller_interface.GetAllDeviceStrings())
|
m_devices_combo->setCurrentIndex(0);
|
||||||
{
|
});
|
||||||
if (name != default_device)
|
|
||||||
m_devices_combo->addItem(QString::fromStdString(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_devices_combo->setCurrentIndex(0);
|
|
||||||
|
|
||||||
Core::PauseAndLock(false, paused);
|
|
||||||
m_block.Set(false);
|
m_block.Set(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,25 +229,23 @@ void MappingWindow::RefreshDevices()
|
||||||
{
|
{
|
||||||
m_devices_combo->clear();
|
m_devices_combo->clear();
|
||||||
|
|
||||||
const bool paused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
|
g_controller_interface.RefreshDevices();
|
||||||
|
m_controller->UpdateReferences(g_controller_interface);
|
||||||
|
m_controller->UpdateDefaultDevice();
|
||||||
|
|
||||||
g_controller_interface.RefreshDevices();
|
const auto default_device = m_controller->default_device.ToString();
|
||||||
m_controller->UpdateReferences(g_controller_interface);
|
|
||||||
m_controller->UpdateDefaultDevice();
|
|
||||||
|
|
||||||
const auto default_device = m_controller->default_device.ToString();
|
m_devices_combo->addItem(QString::fromStdString(default_device));
|
||||||
|
|
||||||
m_devices_combo->addItem(QString::fromStdString(default_device));
|
for (const auto& name : g_controller_interface.GetAllDeviceStrings())
|
||||||
|
{
|
||||||
|
if (name != default_device)
|
||||||
|
m_devices_combo->addItem(QString::fromStdString(name));
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& name : g_controller_interface.GetAllDeviceStrings())
|
m_devices_combo->setCurrentIndex(0);
|
||||||
{
|
});
|
||||||
if (name != default_device)
|
|
||||||
m_devices_combo->addItem(QString::fromStdString(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_devices_combo->setCurrentIndex(0);
|
|
||||||
|
|
||||||
Core::PauseAndLock(false, paused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MappingWindow::ChangeMappingType(MappingWindow::Type type)
|
void MappingWindow::ChangeMappingType(MappingWindow::Type type)
|
||||||
|
|
|
@ -71,12 +71,12 @@ void GCAdapterConfigDiag::ScheduleAdapterUpdate()
|
||||||
|
|
||||||
void GCAdapterConfigDiag::OnUpdateAdapter(wxCommandEvent& WXUNUSED(event))
|
void GCAdapterConfigDiag::OnUpdateAdapter(wxCommandEvent& WXUNUSED(event))
|
||||||
{
|
{
|
||||||
bool unpause = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([this] {
|
||||||
if (GCAdapter::IsDetected())
|
if (GCAdapter::IsDetected())
|
||||||
m_adapter_status->SetLabelText(_("Adapter Detected"));
|
m_adapter_status->SetLabelText(_("Adapter Detected"));
|
||||||
else
|
else
|
||||||
m_adapter_status->SetLabelText(_("Adapter Not Detected"));
|
m_adapter_status->SetLabelText(_("Adapter Not Detected"));
|
||||||
Core::PauseAndLock(false, unpause);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCAdapterConfigDiag::OnAdapterRumble(wxCommandEvent& event)
|
void GCAdapterConfigDiag::OnAdapterRumble(wxCommandEvent& event)
|
||||||
|
|
|
@ -1249,9 +1249,7 @@ void CFrame::DoExclusiveFullscreen(bool enable_fullscreen)
|
||||||
if (!g_renderer || g_renderer->IsFullscreen() == enable_fullscreen)
|
if (!g_renderer || g_renderer->IsFullscreen() == enable_fullscreen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool was_unpaused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([enable_fullscreen] { g_renderer->SetFullscreen(enable_fullscreen); });
|
||||||
g_renderer->SetFullscreen(enable_fullscreen);
|
|
||||||
Core::PauseAndLock(false, was_unpaused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFrame::PollHotkeys(wxTimerEvent& event)
|
void CFrame::PollHotkeys(wxTimerEvent& event)
|
||||||
|
|
|
@ -1411,19 +1411,19 @@ void CFrame::ConnectWiimote(int wm_idx, bool connect)
|
||||||
if (Core::IsRunning() && SConfig::GetInstance().bWii &&
|
if (Core::IsRunning() && SConfig::GetInstance().bWii &&
|
||||||
!SConfig::GetInstance().m_bt_passthrough_enabled)
|
!SConfig::GetInstance().m_bt_passthrough_enabled)
|
||||||
{
|
{
|
||||||
bool was_unpaused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
const auto ios = IOS::HLE::GetIOS();
|
const auto ios = IOS::HLE::GetIOS();
|
||||||
if (!ios)
|
if (!ios)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
|
const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
|
||||||
ios->GetDeviceByName("/dev/usb/oh1/57e/305"));
|
ios->GetDeviceByName("/dev/usb/oh1/57e/305"));
|
||||||
if (bt)
|
if (bt)
|
||||||
bt->AccessWiiMote(wm_idx | 0x100)->Activate(connect);
|
bt->AccessWiiMote(wm_idx | 0x100)->Activate(connect);
|
||||||
const char* message = connect ? "Wii Remote %i connected" : "Wii Remote %i disconnected";
|
const char* message = connect ? "Wii Remote %i connected" : "Wii Remote %i disconnected";
|
||||||
Core::DisplayMessage(StringFromFormat(message, wm_idx + 1), 3000);
|
Core::DisplayMessage(StringFromFormat(message, wm_idx + 1), 3000);
|
||||||
Host_UpdateMainFrame();
|
Host_UpdateMainFrame();
|
||||||
Core::PauseAndLock(false, was_unpaused);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1432,13 +1432,13 @@ void CFrame::OnConnectWiimote(wxCommandEvent& event)
|
||||||
const auto ios = IOS::HLE::GetIOS();
|
const auto ios = IOS::HLE::GetIOS();
|
||||||
if (!ios || SConfig::GetInstance().m_bt_passthrough_enabled)
|
if (!ios || SConfig::GetInstance().m_bt_passthrough_enabled)
|
||||||
return;
|
return;
|
||||||
bool was_unpaused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
|
const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
|
||||||
ios->GetDeviceByName("/dev/usb/oh1/57e/305"));
|
ios->GetDeviceByName("/dev/usb/oh1/57e/305"));
|
||||||
const bool is_connected =
|
const bool is_connected =
|
||||||
bt && bt->AccessWiiMote((event.GetId() - IDM_CONNECT_WIIMOTE1) | 0x100)->IsConnected();
|
bt && bt->AccessWiiMote((event.GetId() - IDM_CONNECT_WIIMOTE1) | 0x100)->IsConnected();
|
||||||
ConnectWiimote(event.GetId() - IDM_CONNECT_WIIMOTE1, !is_connected);
|
ConnectWiimote(event.GetId() - IDM_CONNECT_WIIMOTE1, !is_connected);
|
||||||
Core::PauseAndLock(false, was_unpaused);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle fullscreen. In Windows the fullscreen mode is accomplished by expanding the m_panel to
|
// Toggle fullscreen. In Windows the fullscreen mode is accomplished by expanding the m_panel to
|
||||||
|
@ -1603,15 +1603,15 @@ void CFrame::UpdateGUI()
|
||||||
GetMenuBar()->FindItem(IDM_CONNECT_BALANCEBOARD)->Enable(ShouldEnableWiimotes);
|
GetMenuBar()->FindItem(IDM_CONNECT_BALANCEBOARD)->Enable(ShouldEnableWiimotes);
|
||||||
if (ShouldEnableWiimotes)
|
if (ShouldEnableWiimotes)
|
||||||
{
|
{
|
||||||
bool was_unpaused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
GetMenuBar()->FindItem(IDM_CONNECT_WIIMOTE1)->Check(bt->AccessWiiMote(0x0100)->IsConnected());
|
GetMenuBar()->FindItem(IDM_CONNECT_WIIMOTE1)->Check(bt->AccessWiiMote(0x0100)->IsConnected());
|
||||||
GetMenuBar()->FindItem(IDM_CONNECT_WIIMOTE2)->Check(bt->AccessWiiMote(0x0101)->IsConnected());
|
GetMenuBar()->FindItem(IDM_CONNECT_WIIMOTE2)->Check(bt->AccessWiiMote(0x0101)->IsConnected());
|
||||||
GetMenuBar()->FindItem(IDM_CONNECT_WIIMOTE3)->Check(bt->AccessWiiMote(0x0102)->IsConnected());
|
GetMenuBar()->FindItem(IDM_CONNECT_WIIMOTE3)->Check(bt->AccessWiiMote(0x0102)->IsConnected());
|
||||||
GetMenuBar()->FindItem(IDM_CONNECT_WIIMOTE4)->Check(bt->AccessWiiMote(0x0103)->IsConnected());
|
GetMenuBar()->FindItem(IDM_CONNECT_WIIMOTE4)->Check(bt->AccessWiiMote(0x0103)->IsConnected());
|
||||||
GetMenuBar()
|
GetMenuBar()
|
||||||
->FindItem(IDM_CONNECT_BALANCEBOARD)
|
->FindItem(IDM_CONNECT_BALANCEBOARD)
|
||||||
->Check(bt->AccessWiiMote(0x0104)->IsConnected());
|
->Check(bt->AccessWiiMote(0x0104)->IsConnected());
|
||||||
Core::PauseAndLock(false, was_unpaused);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
GetMenuBar()->FindItem(IDM_RECORD_READ_ONLY)->Enable(Running || Paused);
|
GetMenuBar()->FindItem(IDM_RECORD_READ_ONLY)->Enable(Running || Paused);
|
||||||
|
|
|
@ -915,25 +915,23 @@ void InputConfigDialog::UpdateDeviceComboBox()
|
||||||
|
|
||||||
void InputConfigDialog::RefreshDevices(wxCommandEvent&)
|
void InputConfigDialog::RefreshDevices(wxCommandEvent&)
|
||||||
{
|
{
|
||||||
bool was_unpaused = Core::PauseAndLock(true);
|
Core::RunAsCPUThread([&] {
|
||||||
|
// refresh devices
|
||||||
|
g_controller_interface.RefreshDevices();
|
||||||
|
|
||||||
// refresh devices
|
// update all control references
|
||||||
g_controller_interface.RefreshDevices();
|
UpdateControlReferences();
|
||||||
|
|
||||||
// update all control references
|
// update device cbox
|
||||||
UpdateControlReferences();
|
UpdateDeviceComboBox();
|
||||||
|
|
||||||
// update device cbox
|
Wiimote::LoadConfig();
|
||||||
UpdateDeviceComboBox();
|
Keyboard::LoadConfig();
|
||||||
|
Pad::LoadConfig();
|
||||||
|
HotkeyManagerEmu::LoadConfig();
|
||||||
|
|
||||||
Wiimote::LoadConfig();
|
UpdateGUI();
|
||||||
Keyboard::LoadConfig();
|
});
|
||||||
Pad::LoadConfig();
|
|
||||||
HotkeyManagerEmu::LoadConfig();
|
|
||||||
|
|
||||||
UpdateGUI();
|
|
||||||
|
|
||||||
Core::PauseAndLock(false, was_unpaused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlGroupBox::~ControlGroupBox()
|
ControlGroupBox::~ControlGroupBox()
|
||||||
|
|
Loading…
Reference in New Issue