Skip to content

Commit f724cc3

Browse files
authored
Allow savestate undo disable for pcsx_rearmed on old3ds (#18755)
* Makefile.ctr: enable gc-sections This frees ~160k of RAM without any downsides that I know of. It's not much, but helps pcsx_rearmed on old3ds where it barely fits in RAM. * remove some dupe logic Regardless if `save_state_in_background` is enabled or not, it does exactly the same so the check can be removed. * optimize RAM state load content_load_state_from_ram() doesn't need the backup copy, content_save_state() never touches the ram_buf. It seems the whole logic was copy-pasted from content_undo_load_state() without thinking. * avoid useless memory use and copying Just take the source pointer instead of making a copy and then freeing it. This is a big deal on slow platforms having to deal with large states, like psx on old3ds. * allow the core to disable the savestate undo feature The undo feature may amount to at least 4 simultaneous buffers: save undo, load undo, the current save/load buffer, temporary buffer. When emulating a system with relatively large states on a system with little RAM (like PSX on old3ds) that amounts to something like ~18MB of ~42MB available to the core. Under memory pressure the libctru variation in RetroArch tries to salvage some memory from the main stack and linear memory, but since those are variable, they start to overlap creating instability. The solution is to just disable the undo feature.
1 parent a5d1741 commit f724cc3

9 files changed

Lines changed: 70 additions & 72 deletions

File tree

Makefile.ctr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ ASFLAGS := -g $(ARCH) -O3
200200
LDFLAGS += -specs=ctr/3dsx_custom.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
201201
CFLAGS += -std=gnu99
202202

203+
CFLAGS += -ffunction-sections -fdata-sections
204+
LDFLAGS += -Wl,--gc-sections
205+
203206
LIB_CORE :=
204207
LIB_CORE_FULL :=
205208

command.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,8 +1351,12 @@ static size_t command_event_save_config(const char *config_path,
13511351
static size_t command_event_undo_save_state(char *s, size_t len)
13521352
{
13531353
if (content_undo_save_buf_is_empty())
1354-
return strlcpy(s,
1355-
msg_hash_to_str(MSG_NO_SAVE_STATE_HAS_BEEN_OVERWRITTEN_YET), len);
1354+
{
1355+
enum msg_hash_enums msg = content_undo_save_disabled()
1356+
? MSG_CORE_DOES_NOT_SUPPORT_SAVESTATE_UNDO
1357+
: MSG_NO_SAVE_STATE_HAS_BEEN_OVERWRITTEN_YET;
1358+
return strlcpy(s, msg_hash_to_str(msg), len);
1359+
}
13561360
if (!content_undo_save_state())
13571361
return strlcpy(s,
13581362
msg_hash_to_str(MSG_FAILED_TO_UNDO_SAVE_STATE), len);
@@ -1363,9 +1367,12 @@ static size_t command_event_undo_save_state(char *s, size_t len)
13631367
static size_t command_event_undo_load_state(char *s, size_t len)
13641368
{
13651369
if (content_undo_load_buf_is_empty())
1366-
return strlcpy(s,
1367-
msg_hash_to_str(MSG_NO_STATE_HAS_BEEN_LOADED_YET),
1368-
len);
1370+
{
1371+
enum msg_hash_enums msg = content_undo_save_disabled()
1372+
? MSG_CORE_DOES_NOT_SUPPORT_SAVESTATE_UNDO
1373+
: MSG_NO_STATE_HAS_BEEN_LOADED_YET;
1374+
return strlcpy(s, msg_hash_to_str(msg), len);
1375+
}
13691376
if (!content_undo_load_state())
13701377
return snprintf(s, len, "%s \"%s\".",
13711378
msg_hash_to_str(MSG_FAILED_TO_UNDO_LOAD_STATE),

content.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,10 @@ bool content_init(void);
103103
/* Resets the state and savefile backup buffers */
104104
void content_reset_savestate_backups(void);
105105

106-
/* Checks if the buffers are empty */
106+
/* Checks if the buffers are empty, or undo feature is disabled */
107107
bool content_undo_load_buf_is_empty(void);
108108
bool content_undo_save_buf_is_empty(void);
109+
bool content_undo_save_disabled(void);
109110

110111
/* Clears the pending subsystem rom buffer */
111112
bool content_is_subsystem_pending_load(void);

intl/msg_hash_us.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14195,6 +14195,10 @@ MSG_HASH(
1419514195
MSG_CORE_DOES_NOT_SUPPORT_SAVESTATES,
1419614196
"Core does not support save states."
1419714197
)
14198+
MSG_HASH(
14199+
MSG_CORE_DOES_NOT_SUPPORT_SAVESTATE_UNDO,
14200+
"Core does not support save state undo."
14201+
)
1419814202
MSG_HASH(
1419914203
MSG_CORE_DOES_NOT_SUPPORT_DISK_OPTIONS,
1420014204
"Core does not support Disc Control."

msg_hash.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@ enum msg_hash_enums
513513
MSG_SAVED_STATE_TO_SLOT,
514514
MSG_SAVED_STATE_TO_SLOT_AUTO,
515515
MSG_CORE_DOES_NOT_SUPPORT_SAVESTATES,
516+
MSG_CORE_DOES_NOT_SUPPORT_SAVESTATE_UNDO,
516517
MSG_FAILED_TO_LOAD_STATE,
517518
MSG_FAILED_TO_UNDO_LOAD_STATE,
518519
MSG_FAILED_TO_UNDO_SAVE_STATE,

retroarch.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@
7171
* 3 - Late
7272
*/
7373

74+
#define RETRO_ENVIRONMENT_SET_SAVE_STATE_DISABLE_UNDO (5 | RETRO_ENVIRONMENT_RETROARCH_START_BLOCK)
75+
/* bool * --
76+
* If true, disables the save state save/load undo feature to conserve memory.
77+
*/
78+
7479
#define DRIVERS_CMD_ALL \
7580
( DRIVER_AUDIO_MASK \
7681
| DRIVER_MICROPHONE_MASK \

runloop.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3383,6 +3383,16 @@ bool runloop_environment_cb(unsigned cmd, void *data)
33833383
}
33843384
break;
33853385

3386+
case RETRO_ENVIRONMENT_SET_SAVE_STATE_DISABLE_UNDO:
3387+
{
3388+
bool state = *(const bool*)data;
3389+
3390+
RARCH_LOG("[Environ] RETRO_ENVIRONMENT_SET_SAVE_STATE_DISABLE_UNDO: %s.\n", state ? "yes" : "no");
3391+
3392+
set_save_state_disable_undo(state);
3393+
}
3394+
break;
3395+
33863396
case RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE:
33873397
{
33883398
const struct retro_system_content_info_override *overrides =

tasks/task_save.c

Lines changed: 32 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ static struct save_state_buf undo_load_buf;
134134
static struct ram_save_state_buf ram_buf;
135135

136136
static bool save_state_in_background = false;
137+
static bool save_state_disable_undo = false;
137138

138139
typedef struct rastate_size_info
139140
{
@@ -1012,19 +1013,12 @@ static void content_load_state_cb(retro_task_t *task,
10121013
{
10131014
/* If we were previously backing up a file, let go of it first */
10141015
if (undo_save_buf.data)
1015-
{
10161016
free(undo_save_buf.data);
1017-
undo_save_buf.data = NULL;
1018-
}
1019-
1020-
if (!(undo_save_buf.data = malloc(_len)))
1021-
goto error;
10221017

1023-
memcpy(undo_save_buf.data, buf, _len);
1018+
undo_save_buf.data = buf;
10241019
undo_save_buf.size = _len;
10251020
strlcpy(undo_save_buf.path, load_data->path, sizeof(undo_save_buf.path));
10261021

1027-
free(buf);
10281022
free(load_data);
10291023
return;
10301024
}
@@ -1403,6 +1397,9 @@ bool content_save_state(const char *path, bool save_to_disk)
14031397
size_t _len;
14041398
void *data = NULL;
14051399

1400+
if (!save_to_disk && save_state_disable_undo)
1401+
return false;
1402+
14061403
if (!core_info_current_supports_savestate())
14071404
{
14081405
RARCH_LOG("[State] %s\n",
@@ -1433,7 +1430,7 @@ bool content_save_state(const char *path, bool save_to_disk)
14331430

14341431
if (save_to_disk)
14351432
{
1436-
if (path_is_valid(path))
1433+
if (!save_state_disable_undo && path_is_valid(path))
14371434
{
14381435
/* Before overwriting the savestate file, load it into a buffer
14391436
to allow undo_save_state() to work */
@@ -1463,19 +1460,9 @@ bool content_save_state(const char *path, bool save_to_disk)
14631460

14641461
/* If we were holding onto an old state already, clean it up first */
14651462
if (undo_load_buf.data)
1466-
{
14671463
free(undo_load_buf.data);
1468-
undo_load_buf.data = NULL;
1469-
}
14701464

1471-
if (!(undo_load_buf.data = malloc(_len)))
1472-
{
1473-
free(data);
1474-
return false;
1475-
}
1476-
1477-
memcpy(undo_load_buf.data, data, _len);
1478-
free(data);
1465+
undo_load_buf.data = data;
14791466
undo_load_buf.size = _len;
14801467
strlcpy(undo_load_buf.path, path, sizeof(undo_load_buf.path));
14811468
}
@@ -1664,6 +1651,11 @@ bool content_undo_save_buf_is_empty(void)
16641651
return undo_save_buf.data == NULL || undo_save_buf.size == 0;
16651652
}
16661653

1654+
bool content_undo_save_disabled(void)
1655+
{
1656+
return save_state_disable_undo;
1657+
}
1658+
16671659
/**
16681660
* content_load_state_from_ram:
16691661
* Load a state from RAM.
@@ -1672,9 +1664,7 @@ bool content_undo_save_buf_is_empty(void)
16721664
**/
16731665
bool content_load_state_from_ram(void)
16741666
{
1675-
size_t temp_data_size;
16761667
bool ret = false;
1677-
void* temp_data = NULL;
16781668

16791669
if (!core_info_current_supports_savestate())
16801670
{
@@ -1691,21 +1681,10 @@ bool content_load_state_from_ram(void)
16911681
(unsigned)ram_buf.state_buf.size,
16921682
msg_hash_to_str(MSG_BYTES));
16931683

1694-
/* We need to make a temporary copy of the buffer, to allow the swap below */
1695-
temp_data = malloc(ram_buf.state_buf.size);
1696-
temp_data_size = ram_buf.state_buf.size;
1697-
memcpy(temp_data, ram_buf.state_buf.data, ram_buf.state_buf.size);
1698-
1699-
/* Swap the current state with the backup state. This way, we can undo
1700-
what we're undoing */
1684+
/* Backup the current state so we can undo this load */
17011685
content_save_state("RAM", false);
17021686

1703-
ret = content_deserialize_state(temp_data, temp_data_size);
1704-
1705-
/* Clean up the temporary copy */
1706-
free(temp_data);
1707-
temp_data = NULL;
1708-
1687+
ret = content_deserialize_state(ram_buf.state_buf.data, ram_buf.state_buf.size);
17091688
if (!ret)
17101689
{
17111690
RARCH_ERR("[State] %s.\n",
@@ -1735,50 +1714,32 @@ bool content_save_state_to_ram(void)
17351714
}
17361715

17371716
_len = core_serialize_size();
1738-
17391717
if (_len == 0)
17401718
return false;
17411719

1742-
if (!save_state_in_background)
1743-
{
1744-
if (!(data = content_get_serialized_data(&_len)))
1745-
{
1746-
RARCH_ERR("[State] %s.\n",
1747-
msg_hash_to_str(MSG_FAILED_TO_SAVE_SRAM));
1748-
return false;
1749-
}
1750-
1751-
RARCH_LOG("[State] %s, %u %s.\n",
1752-
msg_hash_to_str(MSG_SAVING_STATE),
1753-
(unsigned)_len,
1754-
msg_hash_to_str(MSG_BYTES));
1755-
}
1756-
1757-
if (!data)
1758-
{
1759-
if (!(data = content_get_serialized_data(&_len)))
1760-
{
1761-
RARCH_ERR("[State] %s.\n",
1762-
msg_hash_to_str(MSG_FAILED_TO_SAVE_SRAM));
1763-
return false;
1764-
}
1765-
}
1720+
RARCH_LOG("[State] %s, %u %s.\n",
1721+
msg_hash_to_str(MSG_SAVING_STATE),
1722+
(unsigned)_len,
1723+
msg_hash_to_str(MSG_BYTES));
17661724

1767-
/* If we were holding onto an old state already, clean it up first */
1768-
if (ram_buf.state_buf.data)
1725+
if (save_state_disable_undo && ram_buf.state_buf.data)
17691726
{
1727+
/* Undo off means lack of memory, free before we alloc the new one */
17701728
free(ram_buf.state_buf.data);
17711729
ram_buf.state_buf.data = NULL;
17721730
}
17731731

1774-
if (!(ram_buf.state_buf.data = malloc(_len)))
1732+
if (!(data = content_get_serialized_data(&_len)))
17751733
{
1776-
free(data);
1734+
RARCH_ERR("[State] %s.\n",
1735+
msg_hash_to_str(MSG_FAILED_TO_SAVE_SRAM));
17771736
return false;
17781737
}
17791738

1780-
memcpy(ram_buf.state_buf.data, data, _len);
1781-
free(data);
1739+
if (ram_buf.state_buf.data)
1740+
free(ram_buf.state_buf.data);
1741+
1742+
ram_buf.state_buf.data = data;
17821743
ram_buf.state_buf.size = _len;
17831744
ram_buf.to_write_file = true;
17841745

@@ -1827,3 +1788,8 @@ void set_save_state_in_background(bool state)
18271788
{
18281789
save_state_in_background = state;
18291790
}
1791+
1792+
void set_save_state_disable_undo(bool disable)
1793+
{
1794+
save_state_disable_undo = disable;
1795+
}

tasks/tasks_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ bool input_autoconfigure_disconnect(
261261
unsigned port, const char *name);
262262

263263
void set_save_state_in_background(bool state);
264+
void set_save_state_disable_undo(bool disable);
264265

265266
#ifdef HAVE_CDROM
266267
void task_push_cdrom_dump(const char *drive);

0 commit comments

Comments
 (0)