diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h index 97db7b6d4f..3d87d620da 100644 --- a/network/netplay/netplay.h +++ b/network/netplay/netplay.h @@ -43,7 +43,6 @@ enum rarch_netplay_ctl_state RARCH_NETPLAY_CTL_IS_DATA_INITED, RARCH_NETPLAY_CTL_PAUSE, RARCH_NETPLAY_CTL_UNPAUSE, - RARCH_NETPLAY_CTL_CATCH_UP, RARCH_NETPLAY_CTL_LOAD_SAVESTATE, RARCH_NETPLAY_CTL_DISCONNECT }; diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index ab75f96735..5ab01f89e6 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -216,8 +216,23 @@ static bool netplay_poll(void) break; default: /* not stalling */ + { + retro_time_t max_ahead; + + /* Figure out how many frames we're allowed to be ahead: Ideally we need to be + * able to run our entire stall worth of frames in one real frame. In + * practice, we'll allow a couple jitter frames. (FIXME: hard coded + * as three 60FPS frame) */ + if (netplay_data->frame_run_time_avg) + max_ahead = 50000 / netplay_data->frame_run_time_avg; + else + max_ahead = netplay_data->delay_frames; + if (max_ahead > netplay_data->delay_frames) + max_ahead = netplay_data->delay_frames; + + /* Are we too far ahead? */ netplay_update_unread_ptr(netplay_data); - if (netplay_data->unread_frame_count + netplay_data->delay_frames + if (netplay_data->unread_frame_count + max_ahead <= netplay_data->self_frame_count) { netplay_data->stall = NETPLAY_STALL_RUNNING_FAST; @@ -246,6 +261,7 @@ static bool netplay_poll(void) } } + } } /* If we're stalling, consider disconnection */ @@ -922,9 +938,6 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data) case RARCH_NETPLAY_CTL_UNPAUSE: netplay_frontend_paused(netplay_data, false); break; - case RARCH_NETPLAY_CTL_CATCH_UP: - ret = netplay_data->catch_up; - break; case RARCH_NETPLAY_CTL_LOAD_SAVESTATE: netplay_load_savestate(netplay_data, (retro_ctx_serialize_info_t*)data, true); break; diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index f404c3b480..aba8cf1179 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -45,6 +45,8 @@ #define MAX_RETRIES 16 #define RETRY_MS 500 +#define NETPLAY_FRAME_RUN_TIME_WINDOW 128 + #define PREV_PTR(x) ((x) == 0 ? netplay->buffer_size - 1 : (x) - 1) #define NEXT_PTR(x) ((x + 1) % netplay->buffer_size) @@ -415,14 +417,25 @@ struct netplay bool flip; uint32_t flip_frame; - /* Netplay pausing - */ + /* Netplay pausing */ bool local_paused; bool remote_paused; - /* And stalling */ + /* Old-style stalling (to be removed) */ uint32_t delay_frames; + + /* We stall if we're far enough ahead that we couldn't transparently rewind. + * To know if we could transparently rewind, we need to know how long + * running a frame takes. We record that every frame and get a running + * (window) average */ + retro_time_t frame_run_time[NETPLAY_FRAME_RUN_TIME_WINDOW]; + int frame_run_time_ptr; + retro_time_t frame_run_time_sum, frame_run_time_avg; + + /* Are we stalled? */ enum rarch_netplay_stall_reason stall; + + /* How long have we been stalled? */ retro_time_t stall_time; /* Opposite of stalling, should we be catching up? */ diff --git a/network/netplay/netplay_sync.c b/network/netplay/netplay_sync.c index 5307651528..7601b6abc4 100644 --- a/network/netplay/netplay_sync.c +++ b/network/netplay/netplay_sync.c @@ -23,6 +23,8 @@ #include "netplay_private.h" #include "../../autosave.h" +#include "../../driver.h" +#include "../../input/input_driver.h" #if 0 #define DEBUG_NONDETERMINISTIC_CORES @@ -341,6 +343,8 @@ process: */ void netplay_sync_post_frame(netplay_t *netplay) { + int catch_up_ct; + netplay->self_ptr = NEXT_PTR(netplay->self_ptr); netplay->self_frame_count++; @@ -407,11 +411,15 @@ void netplay_sync_post_frame(netplay_t *netplay) while (netplay->replay_frame_count < netplay->self_frame_count) { + retro_time_t start, tm; + struct delta_frame *ptr = &netplay->buffer[netplay->replay_ptr]; serial_info.data = ptr->state; serial_info.size = netplay->state_size; serial_info.data_const = NULL; + start = cpu_features_get_time_usec(); + /* Remember the current state */ memset(serial_info.data, 0, serial_info.size); core_serialize(&serial_info); @@ -442,8 +450,20 @@ void netplay_sync_post_frame(netplay_t *netplay) RARCH_LOG("POST %u: %X\n", netplay->replay_frame_count-1, netplay_delta_frame_crc(netplay, ptr)); } #endif + + /* Get our time window */ + tm = cpu_features_get_time_usec() - start; + netplay->frame_run_time_sum -= netplay->frame_run_time[netplay->frame_run_time_ptr]; + netplay->frame_run_time[netplay->frame_run_time_ptr] = tm; + netplay->frame_run_time_sum += tm; + netplay->frame_run_time_ptr++; + if (netplay->frame_run_time_ptr >= NETPLAY_FRAME_RUN_TIME_WINDOW) + netplay->frame_run_time_ptr = 0; } + /* Average our time */ + netplay->frame_run_time_avg = netplay->frame_run_time_sum / NETPLAY_FRAME_RUN_TIME_WINDOW; + if (netplay->unread_frame_count < netplay->self_frame_count) { netplay->other_ptr = netplay->unread_ptr; @@ -459,10 +479,22 @@ void netplay_sync_post_frame(netplay_t *netplay) } /* If we're behind, try to catch up */ - if (netplay->self_frame_count < netplay->unread_frame_count - 2) - netplay->catch_up = true; + /* FIXME: Any use in interacting with the real fast forwarding? */ + if (netplay->catch_up) + catch_up_ct = 0; else + catch_up_ct = 2; + if (netplay->self_frame_count < netplay->unread_frame_count - catch_up_ct) + { + netplay->catch_up = true; + input_driver_set_nonblock_state(); + } + else + { netplay->catch_up = false; + input_driver_unset_nonblock_state(); + } + driver_ctl(RARCH_DRIVER_CTL_SET_NONBLOCK_STATE, NULL); /* If we're supposed to stall, rewind (we shouldn't get this far if we're * stalled, so this is a last resort) */ diff --git a/runloop.c b/runloop.c index 028e1f9391..c05f34aa65 100644 --- a/runloop.c +++ b/runloop.c @@ -1226,11 +1226,6 @@ int runloop_iterate(unsigned *sleep_ms) if (!settings->fastforward_ratio) return 0; -#ifdef HAVE_NETWORKING - if (netplay_driver_ctl(RARCH_NETPLAY_CTL_CATCH_UP, NULL)) - return 0; -#endif - end: current = cpu_features_get_time_usec();