From a50c2498ae3c85fd466a8f1085a07b9857f5d79d Mon Sep 17 00:00:00 2001 From: negativeExponent Date: Mon, 16 Sep 2024 07:10:24 +0800 Subject: [PATCH] Fixes for SaveStates Backported from beetle-psx, using new api callback instead of RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE https://github.com/libretro/beetle-psx-libretro/commit/32fc5d370babaa4d24b08449bd49174485db8b48 RE: * 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. --- libretro.c | 17 ++++++++++++- libretro/state.c | 63 +++++++++++++++++++++++++++++++++++------------- libretro/state.h | 5 ++++ 3 files changed, 67 insertions(+), 18 deletions(-) diff --git a/libretro.c b/libretro.c index 7c8e48ab..8070acb4 100644 --- a/libretro.c +++ b/libretro.c @@ -1656,6 +1656,15 @@ int StateAction(StateMem *sm, int load, int data_only) return ret; } +static bool UsingFastSavestates(void) +{ + int flags; + if (environ_cb(RETRO_ENVIRONMENT_GET_SAVESTATE_CONTEXT, &flags)) + return ((flags == RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_INSTANCE) || + ((flags == RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_BINARY))); + return false; +} + size_t retro_serialize_size(void) { StateMem st; @@ -1665,6 +1674,7 @@ size_t retro_serialize_size(void) st.len = 0; st.malloced = 0; st.initial_malloc = 0; + st.fastsavestates = 0; if (!PX68KSS_SaveSM(&st, 0, 0, NULL, NULL, NULL)) return 0; @@ -1688,6 +1698,7 @@ bool retro_serialize(void *data, size_t size) st.len = 0; st.malloced = size; st.initial_malloc = 0; + st.fastsavestates = UsingFastSavestates(); ret = PX68KSS_SaveSM(&st, 0, 0, NULL, NULL, NULL); @@ -1700,14 +1711,18 @@ bool retro_serialize(void *data, size_t size) bool retro_unserialize(const void *data, size_t size) { StateMem st; + bool ret = false; st.data = (uint8_t*)data; st.loc = 0; st.len = size; st.malloced = 0; st.initial_malloc = 0; + st.fastsavestates = UsingFastSavestates(); + + ret = PX68KSS_LoadSM(&st, 0, 0); - return PX68KSS_LoadSM(&st, 0, 0); + return ret; } /* TODO/FIXME - implement cheats */ diff --git a/libretro/state.c b/libretro/state.c index e86f209b..2aefd352 100644 --- a/libretro/state.c +++ b/libretro/state.c @@ -209,7 +209,6 @@ static bool SubWrite(StateMem *st, SFORMAT *sf) while(sf->size || sf->name) { int32_t bytesize; - char nameo[1 + 256]; if(!sf->size || !sf->v) { @@ -228,9 +227,18 @@ static bool SubWrite(StateMem *st, SFORMAT *sf) } bytesize = sf->size; - nameo[0] = strlcpy(nameo + 1, sf->name, 256); - smem_write(st, nameo, 1 + nameo[0]); + /* exclude text labels from fast savestates */ + if (!st->fastsavestates) + { + char nameo[1 + 256]; + int slen = strlcpy(nameo + 1, sf->name, 255); + nameo[256] = 0; + nameo[0] = slen; + + smem_write(st, nameo, 1 + nameo[0]); + } + smem_write32le(st, bytesize); #ifdef MSB_FIRST @@ -295,7 +303,8 @@ static int WriteStateChunk(StateMem *st, const char *sname, SFORMAT *sf) smem_write(st, sname_tmp, 32); - smem_write32le(st, 0); // We'll come back and write this later. + /* We'll come back and write this later. */ + smem_write32le(st, 0); data_start_pos = st->loc; @@ -311,7 +320,7 @@ static int WriteStateChunk(StateMem *st, const char *sname, SFORMAT *sf) return(end_pos - data_start_pos); } -static SFORMAT *FindSF(const char *name, SFORMAT *sf) +static SFORMAT *FindSF(const char *name, SFORMAT *sf, bool FastSaveStates) { /* Size can sometimes be zero, so also check for the text name. * These two should both be zero only at the end of a struct. */ @@ -326,12 +335,18 @@ static SFORMAT *FindSF(const char *name, SFORMAT *sf) /* Link to another SFORMAT structure. */ if (sf->size == (uint32_t)~0) { - SFORMAT *temp_sf = FindSF(name, (SFORMAT*)sf->v); + SFORMAT *temp_sf = FindSF(name, (SFORMAT*)sf->v, FastSaveStates); if (temp_sf) return temp_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; + if (!strcmp(sf->name, name)) return sf; } @@ -346,25 +361,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)) { - /* Don't change to char unless cast - * toa[0] to unsigned to smem_read() - * and other places. */ - uint8_t toa[1 + 256]; - uint32_t recorded_size = 0; /* In bytes */ SFORMAT *tmp = NULL; - if(smem_read(st, toa, 1) != 1) - return(0); + /* exclude text labels from fast savestates */ + if (!st->fastsavestates) + { + + if(smem_read(st, toa, 1) != 1) + return(0); - if(smem_read(st, toa + 1, toa[0]) != toa[0]) - return 0; + if(smem_read(st, toa + 1, toa[0]) != toa[0]) + return 0; - toa[1 + toa[0]] = 0; + toa[1 + toa[0]] = 0; + } smem_read32le(st, &recorded_size); - tmp = FindSF((char*)toa + 1, sf); + + tmp = FindSF((char*)toa + 1, sf, st->fastsavestates); + + /* 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) { diff --git a/libretro/state.h b/libretro/state.h index d7f259e0..6701b235 100644 --- a/libretro/state.h +++ b/libretro/state.h @@ -19,6 +19,11 @@ typedef struct uint32_t len; uint32_t malloced; uint32_t initial_malloc; /* A setting! */ + + /* 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; } StateMem; #ifdef __cplusplus