rewind: the buffer a load asks for is set up on the frame after

The drivers stage marks the rewind buffer pending instead of setting it
up, and runloop_check_state sets it up on the next frame, before the
core runs, so the full serialize it takes is no longer part of the load.
Rewind init and deinit clear the pending mark.

The content_load harness checks no frame of a load sets up the buffer
and the first frame after does.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-10-04 12:18:28 +02:00
1 parent 3e8307ca8f
commit f451993965
5 files changed
+79 -5

No files matched your search

+3 -1
View File
@@ -9074,7 +9074,9 @@ bool retroarch_main_init_drivers(bool staged,
command_event(CMD_EVENT_CONTROLLER_INIT, NULL);
#ifdef HAVE_REWIND
command_event(CMD_EVENT_REWIND_INIT, NULL);
/* Set up on the frame after the load, before the core runs: the
* buffer is one full serialize of the core */
runloop_st->rewind_st.flags |= STATE_MGR_REWIND_ST_FLAG_INIT_PENDING;
#endif
if (*rec_st->path)
+14
View File
@@ -7029,6 +7029,17 @@ static void runloop_idle_wait(void)
retro_sleep(10);
}
#ifdef HAVE_REWIND
/* The rewind buffer a load asked for: one full serialize of the core,
* taken on the frame after the load, before the core runs. */
static VIDEO_NOINLINE void runloop_rewind_init_pending(
runloop_state_t *runloop_st)
{
runloop_st->rewind_st.flags &= ~STATE_MGR_REWIND_ST_FLAG_INIT_PENDING;
command_event(CMD_EVENT_REWIND_INIT, NULL);
}
#endif
static enum runloop_state_enum runloop_check_state(
input_driver_state_t *input_st,
audio_driver_state_t *audio_st,
@@ -8203,6 +8214,9 @@ static enum runloop_state_enum runloop_check_state(
return RUNLOOP_STATE_PAUSE;
}
if (runloop_st->rewind_st.flags & STATE_MGR_REWIND_ST_FLAG_INIT_PENDING)
runloop_rewind_init_pending(runloop_st);
rewinding = state_manager_check_rewind(
&runloop_st->rewind_st,
&runloop_st->current_core,
@@ -68,6 +68,7 @@
#include "../../../content.h"
#include "../../../input/input_driver.h"
#include "../../../runahead.h"
#include "../../../state_manager.h"
#ifdef HAVE_NETWORKING
#include "../../../network/netplay/netplay.h"
#endif
@@ -510,6 +511,54 @@ static void lane_contentless_names(void)
}
#endif
#ifdef HAVE_REWIND
/* The rewind buffer is one full serialize of the core: a load leaves
* it for the frame after, which takes it before the core runs. */
static void lane_rewind_after_load(void)
{
struct load_frame log[LOAD_FRAMES];
settings_t *settings = config_get_ptr();
runloop_state_t *runloop_st = runloop_state_get_ptr();
bool rewind_enable = settings->bools.rewind_enable;
unsigned n, i;
unsigned had = failures;
configuration_set_bool(settings, settings->bools.rewind_enable, true);
open_menu();
CHECK(task_push_load_contentless_core_from_menu(core_path),
"the load was not started");
for (n = 0; n < LOAD_FRAMES && runloop_is_content_switching(); n++)
{
runloop_iterate();
task_queue_check();
CHECK(!(runloop_st->rewind_st.flags
& STATE_MGR_REWIND_ST_FLAG_INIT_ATTEMPTED),
"frame %u of the load set up the rewind buffer", n);
}
CHECK(!runloop_is_content_switching() && core_is_up(),
"the load did not finish");
CHECK(runloop_st->rewind_st.flags & STATE_MGR_REWIND_ST_FLAG_INIT_PENDING,
"the load did not leave the rewind buffer for the frame after");
for (i = 0; i < 2 && !(runloop_st->rewind_st.flags
& STATE_MGR_REWIND_ST_FLAG_INIT_ATTEMPTED); i++)
{
runloop_iterate();
task_queue_check();
}
CHECK(i == 1,
"the rewind buffer was set up %u frames after the load, not on "
"the first", i);
CHECK(!(runloop_st->rewind_st.flags & STATE_MGR_REWIND_ST_FLAG_INIT_PENDING),
"the rewind buffer is still pending after it was set up");
command_event(CMD_EVENT_REWIND_DEINIT, NULL);
configuration_set_bool(settings, settings->bools.rewind_enable,
rewind_enable);
if (failures == had)
fprintf(stderr, "[pass] rewind-after-load lane\n");
}
#endif
static void lane_one_at_a_time(void)
{
struct load_frame log[LOAD_FRAMES];
@@ -2062,6 +2111,9 @@ int main(int argc, char *argv[])
lane_probe_cached();
#endif
lane_one_at_a_time();
#ifdef HAVE_REWIND
lane_rewind_after_load();
#endif
lane_reinit_deferred();
lane_fallback();
lane_fallback_threaded();
+6 -3
View File
@@ -767,9 +767,11 @@ void state_manager_event_init(
core_info_t *core_info = NULL;
void *state = NULL;
if ( !rewind_st
|| (rewind_st->flags & STATE_MGR_REWIND_ST_FLAG_INIT_ATTEMPTED)
|| rewind_st->state)
if (!rewind_st)
return;
rewind_st->flags &= ~STATE_MGR_REWIND_ST_FLAG_INIT_PENDING;
if ( (rewind_st->flags & STATE_MGR_REWIND_ST_FLAG_INIT_ATTEMPTED)
|| rewind_st->state)
return;
rewind_st->size = 0;
@@ -862,6 +864,7 @@ void state_manager_event_deinit(
| STATE_MGR_REWIND_ST_FLAG_HOTKEY_WAS_CHECKED
| STATE_MGR_REWIND_ST_FLAG_HOTKEY_WAS_PRESSED
| STATE_MGR_REWIND_ST_FLAG_INIT_ATTEMPTED
| STATE_MGR_REWIND_ST_FLAG_INIT_PENDING
);
/* Restore regular (non-rewind) core audio
+4 -1
View File
@@ -34,7 +34,10 @@ enum state_manager_rewind_st_flags
STATE_MGR_REWIND_ST_FLAG_FRAME_IS_REVERSED = (1 << 0),
STATE_MGR_REWIND_ST_FLAG_INIT_ATTEMPTED = (1 << 1),
STATE_MGR_REWIND_ST_FLAG_HOTKEY_WAS_CHECKED = (1 << 2),
STATE_MGR_REWIND_ST_FLAG_HOTKEY_WAS_PRESSED = (1 << 3)
STATE_MGR_REWIND_ST_FLAG_HOTKEY_WAS_PRESSED = (1 << 3),
/* A load wants the buffer: it is set up on the frame after,
* before the core runs, rather than inside the load */
STATE_MGR_REWIND_ST_FLAG_INIT_PENDING = (1 << 4)
};
struct state_manager