From 45d732a014d4611c2cf5f743c281dd3aa7025798 Mon Sep 17 00:00:00 2001 From: Gregor Richards Date: Thu, 15 Dec 2016 22:34:18 -0500 Subject: [PATCH] New sync system The idea: * Use a fixed number of delay_frames (eventually to be fixed at 120, currently still uses the config variable, 0 will still be an option) * Determine how long it takes to simulate a frame. * Stall only if resimulating the intervening frames would be sufficiently annoying (currently fixed at three frames worth of time) Because clients always try to catch up, the actual frame delay works out automatically to be minimally zero and maximally the latency. If one client is underpowered but the other is fine, the powerful one will automatically take up the slack. Seems like the most reasonable system. --- network/netplay/netplay.h | 1 - network/netplay/netplay_frontend.c | 21 +++++++++++++---- network/netplay/netplay_private.h | 19 +++++++++++++--- network/netplay/netplay_sync.c | 36 ++++++++++++++++++++++++++++-- runloop.c | 5 ----- 5 files changed, 67 insertions(+), 15 deletions(-) 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();