mirror of
https://github.com/libretro/beetle-psx-libretro.git
synced 2024-11-23 16:59:49 +00:00
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:
parent
ea2d49b63d
commit
32fc5d370b
111
libretro.cpp
111
libretro.cpp
@ -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)
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user