diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 2492df7efe..bb114aed35 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -1759,6 +1759,12 @@ enum retro_mod * the frontend is attempting to call retro_run(). */ +#define RETRO_ENVIRONMENT_GET_SAVESTATE_CONTEXT (72 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* int * -- + * Tells the core about the context the frontend is asking for savestate. + * (see enum retro_savestate_context) + */ + /* VFS functionality */ /* File paths: @@ -2996,6 +3002,35 @@ enum retro_pixel_format RETRO_PIXEL_FORMAT_UNKNOWN = INT_MAX }; +enum retro_savestate_context +{ + /* Standard savestate written to disk. */ + RETRO_SAVESTATE_CONTEXT_NORMAL = 0, + + /* Savestate where you are guaranteed that the same instance will load the save state. + * You can store internal pointers to code or data. + * It's still a full serialization and deserialization, and could be loaded or saved at any time. + * It won't be written to disk or sent over the network. + */ + RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_INSTANCE = 1, + + /* Savestate where you are guaranteed that the same emulator binary will load that savestate. + * You can skip anything that would slow down saving or loading state but you can not store internal pointers. + * It won't be written to disk or sent over the network. + * Example: "Second Instance" runahead + */ + RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_BINARY = 2, + + /* Savestate used within a rollback netplay feature. + * You should skip anything that would unnecessarily increase bandwidth usage. + * It won't be written to disk but it will be sent over the network. + */ + RETRO_SAVESTATE_CONTEXT_ROLLBACK_NETPLAY = 3, + + /* Ensure sizeof() == sizeof(int). */ + RETRO_SAVESTATE_CONTEXT_UNKNOWN = INT_MAX +}; + struct retro_message { const char *msg; /* Message to be displayed. */ diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index 27459d658e..985d95efe8 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -3422,12 +3422,17 @@ static bool netplay_sync_pre_frame(netplay_t *netplay, bool *disconnect) (NETPLAY_QUIRK_INITIALIZATION | NETPLAY_QUIRK_NO_SAVESTATES))) { retro_ctx_serialize_info_t serial_info = {0}; + bool okay = false; + runloop_state_t *runloop_st = runloop_state_get_ptr(); serial_info.data = netplay->buffer[netplay->run_ptr].state; serial_info.size = netplay->state_size; memset(serial_info.data, 0, serial_info.size); - if (core_serialize(&serial_info)) + runloop_st->request_fast_savestate = true; + okay = core_serialize(&serial_info); + runloop_st->request_fast_savestate = false; + if (okay) { if (netplay->force_send_savestate && !netplay->stall && !netplay->remote_paused) @@ -3638,6 +3643,8 @@ static void netplay_sync_post_frame(netplay_t *netplay, bool stalled) netplay->replay_frame_count < netplay->run_frame_count) { retro_ctx_serialize_info_t serial_info; + bool okay = false; + runloop_state_t *runloop_st = runloop_state_get_ptr(); /* Replay frames. */ netplay->is_replay = true; @@ -3668,7 +3675,10 @@ static void netplay_sync_post_frame(netplay_t *netplay, bool stalled) serial_info.data_const = netplay->buffer[netplay->replay_ptr].state; serial_info.size = netplay->state_size; - if (!core_unserialize(&serial_info)) + runloop_st->request_fast_savestate = true; + okay = core_unserialize(&serial_info); + runloop_st->request_fast_savestate = false; + if (!okay) { RARCH_ERR("[Netplay] Netplay savestate loading failed: Prepare for desync!\n"); } @@ -3686,7 +3696,9 @@ static void netplay_sync_post_frame(netplay_t *netplay, bool stalled) /* Remember the current state */ memset(serial_info.data, 0, serial_info.size); + runloop_st->request_fast_savestate = true; core_serialize(&serial_info); + runloop_st->request_fast_savestate = false; if (netplay->replay_frame_count < netplay->unread_frame_count) netplay_handle_frame_hash(netplay, ptr); @@ -3714,7 +3726,9 @@ static void netplay_sync_post_frame(netplay_t *netplay, bool stalled) ptr = &netplay->buffer[netplay->replay_ptr]; serial_info.data = ptr->state; memset(serial_info.data, 0, serial_info.size); + runloop_st->request_fast_savestate = true; core_serialize(&serial_info); + runloop_st->request_fast_savestate = false; RARCH_LOG("POST %u: %X\n", netplay->replay_frame_count-1, netplay->state_size ? netplay_delta_frame_crc(netplay, ptr) : 0); } #endif @@ -6688,12 +6702,15 @@ static bool netplay_init_socket_buffers(netplay_t *netplay) static bool netplay_init_serialization(netplay_t *netplay) { size_t i; - retro_ctx_size_info_t info = {0}; + retro_ctx_size_info_t info = {0}; + runloop_state_t *runloop_st = runloop_state_get_ptr(); if (netplay->state_size) return true; + runloop_st->request_fast_savestate = true; core_serialize_size(&info); + runloop_st->request_fast_savestate = false; if (!info.size) return false; @@ -6734,6 +6751,8 @@ static bool netplay_init_serialization(netplay_t *netplay) static bool netplay_try_init_serialization(netplay_t *netplay) { retro_ctx_serialize_info_t serial_info; + bool okay = false; + runloop_state_t *runloop_st = runloop_state_get_ptr(); if (netplay->state_size) return true; @@ -6746,7 +6765,10 @@ static bool netplay_try_init_serialization(netplay_t *netplay) serial_info.data = netplay->buffer[netplay->run_ptr].state; serial_info.size = netplay->state_size; - if (!core_serialize(&serial_info)) + runloop_st->request_fast_savestate = true; + okay = core_serialize(&serial_info); + runloop_st->request_fast_savestate = false; + if (!okay) return false; /* Once initialized, we no longer exhibit this quirk */ @@ -7210,6 +7232,8 @@ void netplay_load_savestate(netplay_t *netplay, retro_ctx_serialize_info_t *serial_info, bool save) { retro_ctx_serialize_info_t tmp_serial_info; + bool okay = false; + runloop_state_t *runloop_st = runloop_state_get_ptr(); netplay_force_future(netplay); @@ -7225,7 +7249,11 @@ void netplay_load_savestate(netplay_t *netplay, { tmp_serial_info.size = netplay->state_size; tmp_serial_info.data = netplay->buffer[netplay->run_ptr].state; - if (!core_serialize(&tmp_serial_info)) + + runloop_st->request_fast_savestate = true; + okay = core_serialize(&tmp_serial_info); + runloop_st->request_fast_savestate = false; + if (!okay) return; tmp_serial_info.data_const = tmp_serial_info.data; serial_info = &tmp_serial_info; diff --git a/runloop.c b/runloop.c index 07e34e4a07..97338c42a8 100644 --- a/runloop.c +++ b/runloop.c @@ -3017,15 +3017,15 @@ bool runloop_environment_cb(unsigned cmd, void *data) && !(video_st->current_video->frame == video_null.frame)) result |= 1; #ifdef HAVE_RUNAHEAD - if (runloop_st->request_fast_savestate) - result |= 4; if (audio_st->hard_disable) result |= 8; #endif #ifdef HAVE_NETWORKING if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_REPLAYING, NULL)) result &= ~(1|2); - if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) +#endif +#if defined(HAVE_RUNAHEAD) || defined(HAVE_NETWORKING) + if (runloop_st->request_fast_savestate) result |= 4; #endif if (data) @@ -3036,6 +3036,37 @@ bool runloop_environment_cb(unsigned cmd, void *data) break; } + case RETRO_ENVIRONMENT_GET_SAVESTATE_CONTEXT: + { + int result = RETRO_SAVESTATE_CONTEXT_NORMAL; + settings_t + *settings = config_get_ptr(); +#if defined(HAVE_RUNAHEAD) || defined(HAVE_NETWORKING) + if (runloop_st->request_fast_savestate) + { +#ifdef HAVE_RUNAHEAD +#if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB) + result = (settings->bools.run_ahead_secondary_instance + && runloop_st->runahead_secondary_core_available + && secondary_core_ensure_exists(settings) ? RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_BINARY : RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_INSTANCE); +#else + result = RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_INSTANCE; +#endif +#endif +#ifdef HAVE_NETWORKING + if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)) + result = RETRO_SAVESTATE_CONTEXT_ROLLBACK_NETPLAY; +#endif + } +#endif + if (data) + { + int* result_p = (int*)data; + *result_p = result; + } + break; + } + case RETRO_ENVIRONMENT_GET_MIDI_INTERFACE: { struct retro_midi_interface *midi_interface = diff --git a/runloop.h b/runloop.h index 76f57dd782..c5c088a72e 100644 --- a/runloop.h +++ b/runloop.h @@ -288,11 +288,13 @@ struct runloop bool has_variable_update; bool input_is_dirty; bool runahead_save_state_size_known; - bool request_fast_savestate; bool runahead_available; bool runahead_secondary_core_available; bool runahead_force_input_dirty; #endif +#if defined(HAVE_RUNAHEAD) || defined(HAVE_NETWORKING) + bool request_fast_savestate; +#endif #ifdef HAVE_PATCH bool patch_blocked; #endif