Fixes for Savestates

* Savestate Performance Fix - no longer does giant memory allocations and copies
* Loadstate Performance Fix - no longer does N^2 string comparisons
* Fast Savestates (excludes text labels from the savestates, 20% speedup)
* Minor Savestate Performance Fix, no longer uses snprintf when strncpy will do.  Only applies to the text labels, no effect on fast savestates.
This commit is contained in:
Dwedit 2018-04-23 20:16:53 -05:00
parent ea2d49b63d
commit 32fc5d370b
2 changed files with 141 additions and 52 deletions

View File

@ -27,6 +27,10 @@
#include <vector> #include <vector>
#define ISHEXDEC ((codeLine[cursor]>='0') && (codeLine[cursor]<='9')) || ((codeLine[cursor]>='a') && (codeLine[cursor]<='f')) || ((codeLine[cursor]>='A') && (codeLine[cursor]<='F')) #define ISHEXDEC ((codeLine[cursor]>='0') && (codeLine[cursor]<='9')) || ((codeLine[cursor]>='a') && (codeLine[cursor]<='f')) || ((codeLine[cursor]>='A') && (codeLine[cursor]<='F'))
//Fast Save States exclude string labels from variables in the savestate, and are at least 20% faster.
extern bool FastSaveStates;
const int DEFAULT_STATE_SIZE = 16 * 1024 * 1024;
struct retro_perf_callback perf_cb; struct retro_perf_callback perf_cb;
retro_get_cpu_features_t perf_get_cpu_features_cb = NULL; retro_get_cpu_features_t perf_get_cpu_features_cb = NULL;
retro_log_printf_t log_cb; retro_log_printf_t log_cb;
@ -3432,6 +3436,17 @@ static uint64_t video_frames, audio_frames;
void retro_run(void) void retro_run(void)
{ {
bool updated = false; bool updated = false;
//code to implement audio and video disable is not yet implemented
//bool disableVideo = false;
//bool disableAudio = false;
//bool hardDisableAudio = false;
//int flags = 3;
//if (environ_cb(RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE, &flags))
//{
// disableVideo = !(flags & 1);
// disableAudio = !(flags & 2);
// hardDisableAudio = !!(flags & 8);
//}
rsx_intf_prepare_frame(); rsx_intf_prepare_frame();
@ -3539,6 +3554,11 @@ void retro_run(void)
spec.VideoFormatChanged = false; spec.VideoFormatChanged = false;
spec.SoundFormatChanged = false; spec.SoundFormatChanged = false;
//if (disableVideo)
//{
// spec.skip = true;
//}
EmulateSpecStruct *espec = (EmulateSpecStruct*)&spec; EmulateSpecStruct *espec = (EmulateSpecStruct*)&spec;
/* start of Emulate */ /* start of Emulate */
int32_t timestamp = 0; int32_t timestamp = 0;
@ -3901,41 +3921,72 @@ size_t retro_serialize_size(void)
return serialize_size = st.len; return serialize_size = st.len;
} }
return serialize_size = 16777216; // 16MB return serialize_size = DEFAULT_STATE_SIZE; // 16MB
}
bool UsingFastSavestates()
{
int flags;
if (environ_cb(RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE, &flags))
{
return flags & 4;
}
return false;
} }
bool retro_serialize(void *data, size_t size) bool retro_serialize(void *data, size_t size)
{ {
/* it seems that mednafen can realloc pointers sent to it? if (size == DEFAULT_STATE_SIZE) //16MB buffer reserved
since we don't know the disposition of void* data (is it safe to realloc?) we have to manage a new buffer here */
static bool logged;
StateMem st;
bool ret = false;
uint8_t *_dat = (uint8_t*)malloc(size);
if (!_dat)
return false;
st.data = _dat;
st.loc = 0;
st.len = 0;
st.malloced = size;
st.initial_malloc = 0;
/* there are still some errors with the save states,
* the size seems to change on some games for now
* just log when this happens */
if (!logged && st.len != size)
{ {
log_cb(RETRO_LOG_WARN, "warning, save state size has changed\n"); //actual size is around 3.75MB (3.67MB for fast savestates) rather than 16MB, so 16MB will hold a savestate without worrying about realloc
logged = true;
//save state in place
StateMem st;
st.data = (uint8_t*)data;
st.len = 0;
st.loc = 0;
st.malloced = size;
st.initial_malloc = 0;
//fast save states are at least 20% faster
FastSaveStates = UsingFastSavestates();
bool ret = MDFNSS_SaveSM(&st, 0, 0, NULL, NULL, NULL);
FastSaveStates = false;
return ret;
} }
else
{
/* it seems that mednafen can realloc pointers sent to it?
since we don't know the disposition of void* data (is it safe to realloc?) we have to manage a new buffer here */
static bool logged;
StateMem st;
bool ret = false;
uint8_t *_dat = (uint8_t*)malloc(size);
ret = MDFNSS_SaveSM(&st, 0, 0, NULL, NULL, NULL); if (!_dat)
return false;
memcpy(data,st.data,size); st.data = _dat;
free(st.data); st.loc = 0;
return ret; st.len = 0;
st.malloced = size;
st.initial_malloc = 0;
/* there are still some errors with the save states,
* the size seems to change on some games for now
* just log when this happens */
if (!logged && st.len != size)
{
log_cb(RETRO_LOG_WARN, "warning, save state size has changed\n");
logged = true;
}
ret = MDFNSS_SaveSM(&st, 0, 0, NULL, NULL, NULL);
memcpy(data, st.data, size);
free(st.data);
return ret;
}
} }
bool retro_unserialize(const void *data, size_t size) bool retro_unserialize(const void *data, size_t size)
@ -3948,7 +3999,11 @@ bool retro_unserialize(const void *data, size_t size)
st.malloced = 0; st.malloced = 0;
st.initial_malloc = 0; st.initial_malloc = 0;
return MDFNSS_LoadSM(&st, 0, 0); //fast save states are at least 20% faster
FastSaveStates = UsingFastSavestates();
bool okay = MDFNSS_LoadSM(&st, 0, 0);
FastSaveStates = false;
return okay;
} }
void *retro_get_memory_data(unsigned type) void *retro_get_memory_data(unsigned type)

View File

@ -28,6 +28,10 @@
#define RLSB MDFNSTATE_RLSB //0x80000000 #define RLSB MDFNSTATE_RLSB //0x80000000
//Fast Save States exclude string labels from variables in the savestate, and are at least 20% faster.
//Only used for internal savestates which will not be written to a file.
bool FastSaveStates = false;
int32_t smem_read(StateMem *st, void *buffer, uint32_t len) int32_t smem_read(StateMem *st, void *buffer, uint32_t len)
{ {
if ((len + st->loc) > st->len) if ((len + st->loc) > st->len)
@ -134,19 +138,33 @@ static bool SubWrite(StateMem *st, SFORMAT *sf, const char *name_prefix = NULL)
int32_t bytesize = sf->size; int32_t bytesize = sf->size;
char nameo[1 + 256]; //exclude text labels from fast savestates
int slen; if (!FastSaveStates)
slen = snprintf(nameo + 1, 256, "%s%s", name_prefix ? name_prefix : "", sf->name);
nameo[0] = slen;
if(slen >= 255)
{ {
printf("Warning: state variable name possibly too long: %s %s %s %d\n", sf->name, name_prefix, nameo, slen); char nameo[1 + 256];
slen = 255; int slen;
}
smem_write(st, nameo, 1 + nameo[0]); //Replace snprintf with strncpy for most cases
if (name_prefix != NULL)
{
slen = snprintf(nameo + 1, 256, "%s%s", name_prefix ? name_prefix : "", sf->name);
}
else
{
slen = strlen(sf->name);
strncpy(nameo + 1, sf->name, 255);
nameo[256] = 0;
}
nameo[0] = slen;
if (slen >= 255)
{
printf("Warning: state variable name possibly too long: %s %s %s %d\n", sf->name, name_prefix, nameo, slen);
slen = 255;
}
smem_write(st, nameo, 1 + nameo[0]);
}
smem_write32le(st, bytesize); smem_write32le(st, bytesize);
#ifdef MSB_FIRST #ifdef MSB_FIRST
@ -250,6 +268,11 @@ static SFORMAT *FindSF(const char *name, SFORMAT *sf)
} }
else else
{ {
//for fast savestates, we no longer have the text label in the state, and need to assume that it is the correct one.
if (FastSaveStates)
{
return sf;
}
assert(sf->name); assert(sf->name);
if (!strcmp(sf->name, name)) if (!strcmp(sf->name, name))
return sf; return sf;
@ -297,28 +320,39 @@ static int ReadStateChunk(StateMem *st, SFORMAT *sf, int size)
{ {
int temp = st->loc; int temp = st->loc;
uint32_t recorded_size; // In bytes
uint8_t toa[1 + 256]; // Don't change to char unless cast toa[0] to unsigned to smem_read() and other places.
toa[0] = 0;
toa[1] = 0;
while (st->loc < (temp + size)) while (st->loc < (temp + size))
{ {
uint32_t recorded_size; // In bytes //exclude text labels from fast savestates
uint8_t toa[1 + 256]; // Don't change to char unless cast toa[0] to unsigned to smem_read() and other places. if (!FastSaveStates)
if(smem_read(st, toa, 1) != 1)
{ {
puts("Unexpected EOF"); if (smem_read(st, toa, 1) != 1)
return(0); {
} puts("Unexpected EOF");
return(0);
}
if(smem_read(st, toa + 1, toa[0]) != toa[0]) if (smem_read(st, toa + 1, toa[0]) != toa[0])
{ {
puts("Unexpected EOF?"); puts("Unexpected EOF?");
return 0; return 0;
} }
toa[1 + toa[0]] = 0; toa[1 + toa[0]] = 0;
}
smem_read32le(st, &recorded_size); smem_read32le(st, &recorded_size);
SFORMAT *tmp = FindSF((char*)toa + 1, sf); SFORMAT *tmp = FindSF((char*)toa + 1, sf);
//Fix for unnecessary name checks, when we find it in the first slot, don't recheck that slot again. Also necessary for fast savestates to work.
if (tmp == sf)
{
sf++;
}
if(tmp) if(tmp)
{ {