mirror of
https://github.com/libretro/beetle-psx-libretro.git
synced 2024-11-23 08:49:47 +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>
|
||||
#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;
|
||||
retro_get_cpu_features_t perf_get_cpu_features_cb = NULL;
|
||||
retro_log_printf_t log_cb;
|
||||
@ -3432,6 +3436,17 @@ static uint64_t video_frames, audio_frames;
|
||||
void retro_run(void)
|
||||
{
|
||||
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();
|
||||
|
||||
@ -3539,6 +3554,11 @@ void retro_run(void)
|
||||
spec.VideoFormatChanged = false;
|
||||
spec.SoundFormatChanged = false;
|
||||
|
||||
//if (disableVideo)
|
||||
//{
|
||||
// spec.skip = true;
|
||||
//}
|
||||
|
||||
EmulateSpecStruct *espec = (EmulateSpecStruct*)&spec;
|
||||
/* start of Emulate */
|
||||
int32_t timestamp = 0;
|
||||
@ -3901,41 +3921,72 @@ size_t retro_serialize_size(void)
|
||||
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)
|
||||
{
|
||||
/* 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);
|
||||
|
||||
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)
|
||||
if (size == DEFAULT_STATE_SIZE) //16MB buffer reserved
|
||||
{
|
||||
log_cb(RETRO_LOG_WARN, "warning, save state size has changed\n");
|
||||
logged = true;
|
||||
//actual size is around 3.75MB (3.67MB for fast savestates) rather than 16MB, so 16MB will hold a savestate without worrying about realloc
|
||||
|
||||
//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);
|
||||
free(st.data);
|
||||
return ret;
|
||||
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");
|
||||
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)
|
||||
@ -3948,7 +3999,11 @@ bool retro_unserialize(const void *data, size_t size)
|
||||
st.malloced = 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)
|
||||
|
@ -28,6 +28,10 @@
|
||||
|
||||
#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)
|
||||
{
|
||||
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;
|
||||
|
||||
char nameo[1 + 256];
|
||||
int slen;
|
||||
|
||||
slen = snprintf(nameo + 1, 256, "%s%s", name_prefix ? name_prefix : "", sf->name);
|
||||
nameo[0] = slen;
|
||||
|
||||
if(slen >= 255)
|
||||
//exclude text labels from fast savestates
|
||||
if (!FastSaveStates)
|
||||
{
|
||||
printf("Warning: state variable name possibly too long: %s %s %s %d\n", sf->name, name_prefix, nameo, slen);
|
||||
slen = 255;
|
||||
}
|
||||
char nameo[1 + 256];
|
||||
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);
|
||||
|
||||
#ifdef MSB_FIRST
|
||||
@ -250,6 +268,11 @@ static SFORMAT *FindSF(const char *name, SFORMAT *sf)
|
||||
}
|
||||
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);
|
||||
if (!strcmp(sf->name, name))
|
||||
return sf;
|
||||
@ -297,28 +320,39 @@ static int ReadStateChunk(StateMem *st, SFORMAT *sf, int size)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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.
|
||||
|
||||
if(smem_read(st, toa, 1) != 1)
|
||||
//exclude text labels from fast savestates
|
||||
if (!FastSaveStates)
|
||||
{
|
||||
puts("Unexpected EOF");
|
||||
return(0);
|
||||
}
|
||||
if (smem_read(st, toa, 1) != 1)
|
||||
{
|
||||
puts("Unexpected EOF");
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(smem_read(st, toa + 1, toa[0]) != toa[0])
|
||||
{
|
||||
puts("Unexpected EOF?");
|
||||
return 0;
|
||||
}
|
||||
if (smem_read(st, toa + 1, toa[0]) != toa[0])
|
||||
{
|
||||
puts("Unexpected EOF?");
|
||||
return 0;
|
||||
}
|
||||
|
||||
toa[1 + toa[0]] = 0;
|
||||
toa[1 + toa[0]] = 0;
|
||||
}
|
||||
|
||||
smem_read32le(st, &recorded_size);
|
||||
|
||||
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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user