Changed the save state system to load/save only after the screen has been drawn. This should help stabilise the save states.
This commit is contained in:
parent
da9bd95a68
commit
5d14bb5e70
|
@ -28,6 +28,7 @@
|
||||||
#include "StringUtil.h"
|
#include "StringUtil.h"
|
||||||
|
|
||||||
#include "VideoBackendBase.h"
|
#include "VideoBackendBase.h"
|
||||||
|
#include "State.h"
|
||||||
|
|
||||||
namespace VideoInterface
|
namespace VideoInterface
|
||||||
{
|
{
|
||||||
|
@ -698,6 +699,9 @@ void UpdateInterrupts()
|
||||||
{
|
{
|
||||||
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_VI, false);
|
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_VI, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_InterruptRegister[1].IR_INT && m_InterruptRegister[1].IR_MASK)
|
||||||
|
State::ProcessRequestedStates(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetXFBAddressTop()
|
u32 GetXFBAddressTop()
|
||||||
|
|
|
@ -81,6 +81,9 @@ void DoState(PointerWrap &p)
|
||||||
|
|
||||||
SystemTimers::DecrementerSet();
|
SystemTimers::DecrementerSet();
|
||||||
SystemTimers::TimeBaseSet();
|
SystemTimers::TimeBaseSet();
|
||||||
|
|
||||||
|
if (jit && p.GetMode() == PointerWrap::MODE_READ)
|
||||||
|
jit->GetBlockCache()->ClearSafe();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetRegisters()
|
void ResetRegisters()
|
||||||
|
|
|
@ -31,6 +31,9 @@
|
||||||
#include "VideoBackendBase.h"
|
#include "VideoBackendBase.h"
|
||||||
|
|
||||||
#include <lzo/lzo1x.h>
|
#include <lzo/lzo1x.h>
|
||||||
|
#include "HW/Memmap.h"
|
||||||
|
#include "HW/VideoInterface.h"
|
||||||
|
#include "HW/SystemTimers.h"
|
||||||
|
|
||||||
namespace State
|
namespace State
|
||||||
{
|
{
|
||||||
|
@ -64,6 +67,12 @@ static std::vector<u8> g_current_buffer;
|
||||||
|
|
||||||
static std::thread g_save_thread;
|
static std::thread g_save_thread;
|
||||||
|
|
||||||
|
static const u8 NUM_HOOKS = 2;
|
||||||
|
static u8 waiting;
|
||||||
|
static u8 waitingslot;
|
||||||
|
static u64 lastCheckedStates[NUM_HOOKS];
|
||||||
|
static u8 hook;
|
||||||
|
|
||||||
// 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 int STATE_VERSION = 5;
|
static const int STATE_VERSION = 5;
|
||||||
|
|
||||||
|
@ -73,6 +82,13 @@ struct StateHeader
|
||||||
size_t size;
|
size_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STATE_NONE = 0,
|
||||||
|
STATE_SAVE = 1,
|
||||||
|
STATE_LOAD = 2,
|
||||||
|
};
|
||||||
|
|
||||||
static bool g_use_compression = true;
|
static bool g_use_compression = true;
|
||||||
|
|
||||||
void EnableCompression(bool compression)
|
void EnableCompression(bool compression)
|
||||||
|
@ -105,6 +121,12 @@ void DoState(PointerWrap &p)
|
||||||
g_video_backend->RunLoop(true);
|
g_video_backend->RunLoop(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResetCounters()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NUM_HOOKS; ++i)
|
||||||
|
lastCheckedStates[i] = CoreTiming::GetTicks();
|
||||||
|
}
|
||||||
|
|
||||||
void LoadBufferStateCallback(u64 userdata, int cyclesLate)
|
void LoadBufferStateCallback(u64 userdata, int cyclesLate)
|
||||||
{
|
{
|
||||||
u8* ptr = &g_current_buffer[0];
|
u8* ptr = &g_current_buffer[0];
|
||||||
|
@ -217,10 +239,6 @@ void SaveFileStateCallback(u64 userdata, int cyclesLate)
|
||||||
// Pause the core while we save the state
|
// Pause the core while we save the state
|
||||||
CCPU::EnableStepping(true);
|
CCPU::EnableStepping(true);
|
||||||
|
|
||||||
// Wait for the other threaded sub-systems to stop too
|
|
||||||
// TODO: this is ugly
|
|
||||||
SLEEP(100);
|
|
||||||
|
|
||||||
Flush();
|
Flush();
|
||||||
|
|
||||||
// Measure the size of the buffer.
|
// Measure the size of the buffer.
|
||||||
|
@ -318,10 +336,6 @@ void LoadFileStateCallback(u64 userdata, int cyclesLate)
|
||||||
// Stop the core while we load the state
|
// Stop the core while we load the state
|
||||||
CCPU::EnableStepping(true);
|
CCPU::EnableStepping(true);
|
||||||
|
|
||||||
// Wait for the other threaded sub-systems to stop too
|
|
||||||
// TODO: uglyyy
|
|
||||||
SLEEP(100);
|
|
||||||
|
|
||||||
Flush();
|
Flush();
|
||||||
|
|
||||||
// Save temp buffer for undo load state
|
// Save temp buffer for undo load state
|
||||||
|
@ -350,6 +364,8 @@ void LoadFileStateCallback(u64 userdata, int cyclesLate)
|
||||||
Movie::EndPlayInput(false);
|
Movie::EndPlayInput(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResetCounters();
|
||||||
|
|
||||||
g_op_in_progress = false;
|
g_op_in_progress = false;
|
||||||
|
|
||||||
// resume dat core
|
// resume dat core
|
||||||
|
@ -386,6 +402,11 @@ void Init()
|
||||||
ev_BufferSave = CoreTiming::RegisterEvent("SaveBufferState", &SaveBufferStateCallback);
|
ev_BufferSave = CoreTiming::RegisterEvent("SaveBufferState", &SaveBufferStateCallback);
|
||||||
ev_BufferVerify = CoreTiming::RegisterEvent("VerifyBufferState", &VerifyBufferStateCallback);
|
ev_BufferVerify = CoreTiming::RegisterEvent("VerifyBufferState", &VerifyBufferStateCallback);
|
||||||
|
|
||||||
|
waiting = STATE_NONE;
|
||||||
|
waitingslot = 0;
|
||||||
|
hook = 0;
|
||||||
|
ResetCounters();
|
||||||
|
|
||||||
if (lzo_init() != LZO_E_OK)
|
if (lzo_init() != LZO_E_OK)
|
||||||
PanicAlertT("Internal LZO Error - lzo_init() failed");
|
PanicAlertT("Internal LZO Error - lzo_init() failed");
|
||||||
}
|
}
|
||||||
|
@ -413,40 +434,100 @@ static std::string MakeStateFilename(int number)
|
||||||
SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), number);
|
SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), number);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduleFileEvent(const std::string &filename, int ev)
|
void ScheduleFileEvent(const std::string &filename, int ev, bool immediate)
|
||||||
{
|
{
|
||||||
if (g_op_in_progress)
|
if (g_op_in_progress)
|
||||||
return;
|
return;
|
||||||
g_op_in_progress = true;
|
g_op_in_progress = true;
|
||||||
|
|
||||||
g_current_filename = filename;
|
g_current_filename = filename;
|
||||||
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev);
|
|
||||||
|
if (immediate)
|
||||||
|
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev);
|
||||||
|
else
|
||||||
|
CoreTiming::ScheduleEvent_Threadsafe(0, ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveAs(const std::string &filename, bool immediate)
|
||||||
|
{
|
||||||
|
g_last_filename = filename;
|
||||||
|
ScheduleFileEvent(filename, ev_FileSave, immediate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveAs(const std::string &filename)
|
void SaveAs(const std::string &filename)
|
||||||
{
|
{
|
||||||
g_last_filename = filename;
|
SaveAs(filename, true);
|
||||||
ScheduleFileEvent(filename, ev_FileSave);
|
}
|
||||||
|
|
||||||
|
void LoadAs(const std::string &filename, bool immediate)
|
||||||
|
{
|
||||||
|
ScheduleFileEvent(filename, ev_FileLoad, immediate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadAs(const std::string &filename)
|
void LoadAs(const std::string &filename)
|
||||||
{
|
{
|
||||||
ScheduleFileEvent(filename, ev_FileLoad);
|
LoadAs(filename, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerifyAt(const std::string &filename)
|
void VerifyAt(const std::string &filename)
|
||||||
{
|
{
|
||||||
ScheduleFileEvent(filename, ev_FileVerify);
|
ScheduleFileEvent(filename, ev_FileVerify, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcessRequestedStates(int priority)
|
||||||
|
{
|
||||||
|
bool save = true;
|
||||||
|
|
||||||
|
if (hook == priority)
|
||||||
|
{
|
||||||
|
if (waiting == STATE_SAVE)
|
||||||
|
{
|
||||||
|
SaveAs(MakeStateFilename(waitingslot), false);
|
||||||
|
waitingslot = 0;
|
||||||
|
waiting = STATE_NONE;
|
||||||
|
}
|
||||||
|
else if (waiting == STATE_LOAD)
|
||||||
|
{
|
||||||
|
LoadAs(MakeStateFilename(waitingslot), false);
|
||||||
|
waitingslot = 0;
|
||||||
|
waiting = STATE_NONE;
|
||||||
|
save = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change hooks if the new hook gets called frequently (at least once a frame) and if the old
|
||||||
|
// hook has not been called for the last 5 seconds
|
||||||
|
if ((CoreTiming::GetTicks() - lastCheckedStates[priority]) < (VideoInterface::GetTicksPerFrame()))
|
||||||
|
{
|
||||||
|
lastCheckedStates[priority] = CoreTiming::GetTicks();
|
||||||
|
if (hook < NUM_HOOKS && priority >= (hook + 1) &&
|
||||||
|
(lastCheckedStates[priority] - lastCheckedStates[hook]) > (SystemTimers::GetTicksPerSecond() * 5))
|
||||||
|
{
|
||||||
|
hook++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
lastCheckedStates[priority] = CoreTiming::GetTicks();
|
||||||
|
|
||||||
|
return save;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Save(int slot)
|
void Save(int slot)
|
||||||
{
|
{
|
||||||
SaveAs(MakeStateFilename(slot));
|
if (waiting == STATE_NONE)
|
||||||
|
{
|
||||||
|
waiting = STATE_SAVE;
|
||||||
|
waitingslot = slot;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Load(int slot)
|
void Load(int slot)
|
||||||
{
|
{
|
||||||
LoadAs(MakeStateFilename(slot));
|
if (waiting == STATE_NONE)
|
||||||
|
{
|
||||||
|
waiting = STATE_LOAD;
|
||||||
|
waitingslot = slot;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Verify(int slot)
|
void Verify(int slot)
|
||||||
|
|
|
@ -26,6 +26,7 @@ namespace State
|
||||||
{
|
{
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
void EnableCompression(bool compression);
|
void EnableCompression(bool compression);
|
||||||
|
@ -39,6 +40,8 @@ void Save(int slot);
|
||||||
void Load(int slot);
|
void Load(int slot);
|
||||||
void Verify(int slot);
|
void Verify(int slot);
|
||||||
|
|
||||||
|
bool ProcessRequestedStates(int priority);
|
||||||
|
|
||||||
void SaveAs(const std::string &filename);
|
void SaveAs(const std::string &filename);
|
||||||
void LoadAs(const std::string &filename);
|
void LoadAs(const std::string &filename);
|
||||||
void VerifyAt(const std::string &filename);
|
void VerifyAt(const std::string &filename);
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "CommandProcessor.h"
|
#include "CommandProcessor.h"
|
||||||
#include "HW/ProcessorInterface.h"
|
#include "HW/ProcessorInterface.h"
|
||||||
#include "DLCache.h"
|
#include "DLCache.h"
|
||||||
|
#include "State.h"
|
||||||
namespace PixelEngine
|
namespace PixelEngine
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -329,6 +330,8 @@ void UpdateFinishInterrupt(bool active)
|
||||||
{
|
{
|
||||||
ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active);
|
ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active);
|
||||||
interruptSetFinish = active;
|
interruptSetFinish = active;
|
||||||
|
if (active)
|
||||||
|
State::ProcessRequestedStates(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue