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.
This commit is contained in:
Gregor Richards 2016-12-15 22:34:18 -05:00
parent 6890456ac0
commit 45d732a014
5 changed files with 67 additions and 15 deletions

View File

@ -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
};

View File

@ -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;

View File

@ -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? */

View File

@ -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) */

View File

@ -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();