New approach to input latency

This commit is contained in:
Gregor Richards 2017-01-18 16:07:17 -05:00
parent 2b3343a687
commit c4cb94db19
6 changed files with 165 additions and 110 deletions

View File

@ -106,10 +106,10 @@ static bool netplay_can_poll(netplay_t *netplay)
static bool get_self_input_state(netplay_t *netplay) static bool get_self_input_state(netplay_t *netplay)
{ {
uint32_t state[WORDS_PER_INPUT] = {0, 0, 0}; uint32_t state[WORDS_PER_INPUT] = {0, 0, 0};
struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr]; struct delta_frame *ptr = &netplay->buffer[netplay->input_ptr];
size_t i; size_t i;
if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count)) if (!netplay_delta_frame_ready(netplay, ptr, netplay->input_frame_count))
return false; return false;
if (ptr->have_local) if (ptr->have_local)
@ -118,7 +118,7 @@ static bool get_self_input_state(netplay_t *netplay)
return true; return true;
} }
if (!input_driver_is_libretro_input_blocked() && netplay->self_frame_count > 0) if (!input_driver_is_libretro_input_blocked() && netplay->input_frame_count > 0)
{ {
/* First frame we always give zero input since relying on /* First frame we always give zero input since relying on
* input from first frame screws up when we use -F 0. */ * input from first frame screws up when we use -F 0. */
@ -205,7 +205,7 @@ static bool netplay_poll(void)
netplay_update_unread_ptr(netplay_data); netplay_update_unread_ptr(netplay_data);
if (netplay_data->stateless_mode && if (netplay_data->stateless_mode &&
netplay_data->connected_players && netplay_data->connected_players &&
netplay_data->unread_frame_count <= netplay_data->self_frame_count) netplay_data->unread_frame_count <= netplay_data->run_frame_count)
res = netplay_poll_net_input(netplay_data, true); res = netplay_poll_net_input(netplay_data, true);
else else
res = netplay_poll_net_input(netplay_data, false); res = netplay_poll_net_input(netplay_data, false);
@ -218,16 +218,16 @@ static bool netplay_poll(void)
} }
/* Simulate the input if we don't have real input */ /* Simulate the input if we don't have real input */
netplay_simulate_input(netplay_data, netplay_data->self_ptr, false); netplay_simulate_input(netplay_data, netplay_data->run_ptr, false);
/* Consider stalling */ /* If we're stalled, consider unstalling */
switch (netplay_data->stall) switch (netplay_data->stall)
{ {
case NETPLAY_STALL_RUNNING_FAST: case NETPLAY_STALL_RUNNING_FAST:
{ {
netplay_update_unread_ptr(netplay_data); netplay_update_unread_ptr(netplay_data);
if (netplay_data->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2 if (netplay_data->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2
> netplay_data->self_frame_count) > netplay_data->input_frame_count)
{ {
netplay_data->stall = NETPLAY_STALL_NONE; netplay_data->stall = NETPLAY_STALL_NONE;
for (i = 0; i < netplay_data->connections_size; i++) for (i = 0; i < netplay_data->connections_size; i++)
@ -240,6 +240,11 @@ static bool netplay_poll(void)
break; break;
} }
case NETPLAY_STALL_INPUT_LATENCY:
/* Just let it recalculate momentarily */
netplay_data->stall = NETPLAY_STALL_NONE;
break;
case NETPLAY_STALL_SERVER_REQUESTED: case NETPLAY_STALL_SERVER_REQUESTED:
{ {
/* See if the stall is done */ /* See if the stall is done */
@ -261,11 +266,24 @@ static bool netplay_poll(void)
break; break;
default: /* not stalling */ default: /* not stalling */
break;
}
/* If we're not stalled, consider stalling */
if (!netplay_data->stall)
{ {
/* Are we too far ahead? */
netplay_update_unread_ptr(netplay_data); netplay_update_unread_ptr(netplay_data);
/* Have we not reat enough latency frames? */
if (netplay_data->run_frame_count + NETPLAY_INPUT_LATENCY_FRAMES > netplay_data->input_frame_count)
{
netplay_data->stall = NETPLAY_STALL_INPUT_LATENCY;
netplay_data->stall_time = 0;
}
/* Are we too far ahead? */
if (netplay_data->unread_frame_count + NETPLAY_MAX_STALL_FRAMES if (netplay_data->unread_frame_count + NETPLAY_MAX_STALL_FRAMES
<= netplay_data->self_frame_count) <= netplay_data->input_frame_count)
{ {
netplay_data->stall = NETPLAY_STALL_RUNNING_FAST; netplay_data->stall = NETPLAY_STALL_RUNNING_FAST;
netplay_data->stall_time = cpu_features_get_time_usec(); netplay_data->stall_time = cpu_features_get_time_usec();
@ -294,7 +312,6 @@ static bool netplay_poll(void)
} }
} }
}
/* If we're stalling, consider disconnection */ /* If we're stalling, consider disconnection */
if (netplay_data->stall && netplay_data->stall_time) if (netplay_data->stall && netplay_data->stall_time)
@ -371,7 +388,7 @@ static int16_t netplay_input_state(netplay_t *netplay,
unsigned idx, unsigned id) unsigned idx, unsigned id)
{ {
size_t ptr = netplay->is_replay ? size_t ptr = netplay->is_replay ?
netplay->replay_ptr : netplay->self_ptr; netplay->replay_ptr : netplay->run_ptr;
const uint32_t *curr_input_state = NULL; const uint32_t *curr_input_state = NULL;
@ -492,7 +509,7 @@ static void netplay_flip_users(netplay_t *netplay)
{ {
/* Must be in the future because we may have /* Must be in the future because we may have
* already sent this frame's data */ * already sent this frame's data */
uint32_t flip_frame = netplay->self_frame_count + 1; uint32_t flip_frame = netplay->input_frame_count + 1;
uint32_t flip_frame_net = htonl(flip_frame); uint32_t flip_frame_net = htonl(flip_frame);
size_t i; size_t i;
@ -688,7 +705,7 @@ void netplay_send_savestate(netplay_t *netplay,
/* Send it to relevant peers */ /* Send it to relevant peers */
header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE); header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE);
header[1] = htonl(wn + 2*sizeof(uint32_t)); header[1] = htonl(wn + 2*sizeof(uint32_t));
header[2] = htonl(netplay->self_frame_count); header[2] = htonl(netplay->run_frame_count);
header[3] = htonl(serial_info->size); header[3] = htonl(serial_info->size);
for (i = 0; i < netplay->connections_size; i++) for (i = 0; i < netplay->connections_size; i++)
@ -721,16 +738,21 @@ void netplay_load_savestate(netplay_t *netplay,
{ {
retro_ctx_serialize_info_t tmp_serial_info; retro_ctx_serialize_info_t tmp_serial_info;
/* Wherever we're inputting, that's where we consider our state to be loaded
* (FIXME: Need to be more careful about saving it?) */
netplay->run_ptr = netplay->input_ptr;
netplay->run_frame_count = netplay->input_frame_count;
/* Record it in our own buffer */ /* Record it in our own buffer */
if (save || !serial_info) if (save || !serial_info)
{ {
if (netplay_delta_frame_ready(netplay, if (netplay_delta_frame_ready(netplay,
&netplay->buffer[netplay->self_ptr], netplay->self_frame_count)) &netplay->buffer[netplay->run_ptr], netplay->run_frame_count))
{ {
if (!serial_info) if (!serial_info)
{ {
tmp_serial_info.size = netplay->state_size; tmp_serial_info.size = netplay->state_size;
tmp_serial_info.data = netplay->buffer[netplay->self_ptr].state; tmp_serial_info.data = netplay->buffer[netplay->run_ptr].state;
if (!core_serialize(&tmp_serial_info)) if (!core_serialize(&tmp_serial_info))
return; return;
tmp_serial_info.data_const = tmp_serial_info.data; tmp_serial_info.data_const = tmp_serial_info.data;
@ -740,7 +762,7 @@ void netplay_load_savestate(netplay_t *netplay,
{ {
if (serial_info->size <= netplay->state_size) if (serial_info->size <= netplay->state_size)
{ {
memcpy(netplay->buffer[netplay->self_ptr].state, memcpy(netplay->buffer[netplay->run_ptr].state,
serial_info->data_const, serial_info->size); serial_info->data_const, serial_info->size);
} }
} }
@ -755,29 +777,29 @@ void netplay_load_savestate(netplay_t *netplay,
/* We need to ignore any intervening data from the other side, /* We need to ignore any intervening data from the other side,
* and never rewind past this */ * and never rewind past this */
netplay_update_unread_ptr(netplay); netplay_update_unread_ptr(netplay);
if (netplay->unread_frame_count < netplay->self_frame_count) if (netplay->unread_frame_count < netplay->run_frame_count)
{ {
uint32_t player; uint32_t player;
for (player = 0; player < MAX_USERS; player++) for (player = 0; player < MAX_USERS; player++)
{ {
if (!(netplay->connected_players & (1<<player))) continue; if (!(netplay->connected_players & (1<<player))) continue;
if (netplay->read_frame_count[player] < netplay->self_frame_count) if (netplay->read_frame_count[player] < netplay->run_frame_count)
{ {
netplay->read_ptr[player] = netplay->self_ptr; netplay->read_ptr[player] = netplay->run_ptr;
netplay->read_frame_count[player] = netplay->self_frame_count; netplay->read_frame_count[player] = netplay->run_frame_count;
} }
} }
if (netplay->server_frame_count < netplay->self_frame_count) if (netplay->server_frame_count < netplay->run_frame_count)
{ {
netplay->server_ptr = netplay->self_ptr; netplay->server_ptr = netplay->run_ptr;
netplay->server_frame_count = netplay->self_frame_count; netplay->server_frame_count = netplay->run_frame_count;
} }
netplay_update_unread_ptr(netplay); netplay_update_unread_ptr(netplay);
} }
if (netplay->other_frame_count < netplay->self_frame_count) if (netplay->other_frame_count < netplay->run_frame_count)
{ {
netplay->other_ptr = netplay->self_ptr; netplay->other_ptr = netplay->run_ptr;
netplay->other_frame_count = netplay->self_frame_count; netplay->other_frame_count = netplay->run_frame_count;
} }
/* If we can't send it to the peer, loading a state was a bad idea */ /* If we can't send it to the peer, loading a state was a bad idea */
@ -807,7 +829,7 @@ static void netplay_toggle_play_spectate(netplay_t *netplay)
uint32_t payload[2]; uint32_t payload[2];
char msg[512]; char msg[512];
const char *dmsg = NULL; const char *dmsg = NULL;
payload[0] = htonl(netplay->self_frame_count); payload[0] = htonl(netplay->input_frame_count);
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
{ {
/* Mark us as no longer playing */ /* Mark us as no longer playing */

View File

@ -518,7 +518,7 @@ bool netplay_handshake_sync(netplay_t *netplay, struct netplay_connection *conne
cmd[0] = htonl(NETPLAY_CMD_SYNC); cmd[0] = htonl(NETPLAY_CMD_SYNC);
cmd[1] = htonl(3*sizeof(uint32_t) + MAX_USERS*sizeof(uint32_t) + cmd[1] = htonl(3*sizeof(uint32_t) + MAX_USERS*sizeof(uint32_t) +
NETPLAY_NICK_LEN + mem_info.size); NETPLAY_NICK_LEN + mem_info.size);
cmd[2] = htonl(netplay->self_frame_count); cmd[2] = htonl(netplay->input_frame_count);
connected_players = netplay->connected_players; connected_players = netplay->connected_players;
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
connected_players |= 1<<netplay->self_player; connected_players |= 1<<netplay->self_player;
@ -904,22 +904,23 @@ bool netplay_handshake_pre_sync(netplay_t *netplay,
netplay->flip_frame = flip_frame; netplay->flip_frame = flip_frame;
/* Set our frame counters as requested */ /* Set our frame counters as requested */
netplay->self_frame_count = netplay->other_frame_count = netplay->input_frame_count = netplay->run_frame_count =
netplay->unread_frame_count = netplay->server_frame_count = netplay->other_frame_count = netplay->unread_frame_count =
new_frame_count; netplay->server_frame_count = new_frame_count;
for (i = 0; i < netplay->buffer_size; i++) for (i = 0; i < netplay->buffer_size; i++)
{ {
struct delta_frame *ptr = &netplay->buffer[i]; struct delta_frame *ptr = &netplay->buffer[i];
ptr->used = false; ptr->used = false;
if (i == netplay->self_ptr) if (i == netplay->input_ptr)
{ {
/* Clear out any current data but still use this frame */ /* Clear out any current data but still use this frame */
if (!netplay_delta_frame_ready(netplay, ptr, 0)) if (!netplay_delta_frame_ready(netplay, ptr, 0))
return false; return false;
ptr->frame = new_frame_count; ptr->frame = new_frame_count;
ptr->have_local = true; ptr->have_local = true;
netplay->other_ptr = netplay->unread_ptr = netplay->server_ptr = i; netplay->run_ptr = netplay->other_ptr = netplay->unread_ptr =
netplay->server_ptr = i;
} }
} }
@ -927,8 +928,8 @@ bool netplay_handshake_pre_sync(netplay_t *netplay,
{ {
if (connected_players & (1<<i)) if (connected_players & (1<<i))
{ {
netplay->read_ptr[i] = netplay->self_ptr; netplay->read_ptr[i] = netplay->input_ptr;
netplay->read_frame_count[i] = netplay->self_frame_count; netplay->read_frame_count[i] = netplay->input_frame_count;
} }
} }

View File

@ -321,7 +321,7 @@ bool netplay_try_init_serialization(netplay_t *netplay)
/* Check if we can actually save */ /* Check if we can actually save */
serial_info.data_const = NULL; serial_info.data_const = NULL;
serial_info.data = netplay->buffer[netplay->self_ptr].state; serial_info.data = netplay->buffer[netplay->run_ptr].state;
serial_info.size = netplay->state_size; serial_info.size = netplay->state_size;
if (!core_serialize(&serial_info)) if (!core_serialize(&serial_info))

View File

@ -38,7 +38,7 @@ static void print_state(netplay_t *netplay)
#define APPEND(out) cur += snprintf out #define APPEND(out) cur += snprintf out
#define M msg + cur, sizeof(msg) - cur #define M msg + cur, sizeof(msg) - cur
APPEND((M, "NETPLAY: S:%u U:%u O:%u", netplay->self_frame_count, netplay->unread_frame_count, netplay->other_frame_count)); APPEND((M, "NETPLAY: S:%u U:%u O:%u", netplay->input_frame_count, netplay->unread_frame_count, netplay->other_frame_count));
if (!netplay->is_server) if (!netplay->is_server)
APPEND((M, " H:%u", netplay->server_frame_count)); APPEND((M, " H:%u", netplay->server_frame_count));
for (player = 0; player < MAX_USERS; player++) for (player = 0; player < MAX_USERS; player++)
@ -204,7 +204,7 @@ static bool send_input_frame(netplay_t *netplay,
bool netplay_send_cur_input(netplay_t *netplay, bool netplay_send_cur_input(netplay_t *netplay,
struct netplay_connection *connection) struct netplay_connection *connection)
{ {
struct delta_frame *dframe = &netplay->buffer[netplay->self_ptr]; struct delta_frame *dframe = &netplay->buffer[netplay->input_ptr];
uint32_t player; uint32_t player;
if (netplay->is_server) if (netplay->is_server)
@ -220,7 +220,7 @@ bool netplay_send_cur_input(netplay_t *netplay,
if (dframe->have_real[player]) if (dframe->have_real[player])
{ {
if (!send_input_frame(netplay, connection, NULL, if (!send_input_frame(netplay, connection, NULL,
netplay->self_frame_count, player, netplay->input_frame_count, player,
dframe->real_input_state[player])) dframe->real_input_state[player]))
return false; return false;
} }
@ -230,7 +230,7 @@ bool netplay_send_cur_input(netplay_t *netplay,
/* If we're not playing, send a NOINPUT */ /* If we're not playing, send a NOINPUT */
if (netplay->self_mode != NETPLAY_CONNECTION_PLAYING) if (netplay->self_mode != NETPLAY_CONNECTION_PLAYING)
{ {
uint32_t payload = htonl(netplay->self_frame_count); uint32_t payload = htonl(netplay->input_frame_count);
if (!netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_NOINPUT, if (!netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_NOINPUT,
&payload, sizeof(payload))) &payload, sizeof(payload)))
return false; return false;
@ -242,7 +242,7 @@ bool netplay_send_cur_input(netplay_t *netplay,
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
{ {
if (!send_input_frame(netplay, connection, NULL, if (!send_input_frame(netplay, connection, NULL,
netplay->self_frame_count, netplay->input_frame_count,
(netplay->is_server ? NETPLAY_CMD_INPUT_BIT_SERVER : 0) | netplay->self_player, (netplay->is_server ? NETPLAY_CMD_INPUT_BIT_SERVER : 0) | netplay->self_player,
dframe->self_state)) dframe->self_state))
return false; return false;
@ -505,7 +505,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
if (netplay->is_server) if (netplay->is_server)
{ {
/* Forward it on if it's past data*/ /* Forward it on if it's past data*/
if (dframe->frame <= netplay->self_frame_count) if (dframe->frame <= netplay->input_frame_count)
send_input_frame(netplay, NULL, connection, buffer[0], send_input_frame(netplay, NULL, connection, buffer[0],
player, dframe->real_input_state[player]); player, dframe->real_input_state[player]);
} }
@ -585,7 +585,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
/* Force a rewind to assure the flip happens: This just prevents us /* Force a rewind to assure the flip happens: This just prevents us
* from skipping other past the flip because our prediction was * from skipping other past the flip because our prediction was
* correct */ * correct */
if (flip_frame < netplay->self_frame_count) if (flip_frame < netplay->input_frame_count)
netplay->force_rewind = true; netplay->force_rewind = true;
RARCH_LOG("%s.\n", msg_hash_to_str(MSG_NETPLAY_USERS_HAS_FLIPPED)); RARCH_LOG("%s.\n", msg_hash_to_str(MSG_NETPLAY_USERS_HAS_FLIPPED));
@ -638,7 +638,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
{ {
uint32_t payload[2]; uint32_t payload[2];
uint32_t player = 0; uint32_t player = 0;
payload[0] = htonl(netplay->self_frame_count + 1); payload[0] = htonl(netplay->input_frame_count + 1);
if (!netplay->is_server) if (!netplay->is_server)
{ {
@ -695,8 +695,8 @@ static bool netplay_get_cmd(netplay_t *netplay,
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
/* And expect their data */ /* And expect their data */
netplay->read_ptr[player] = NEXT_PTR(netplay->self_ptr); netplay->read_ptr[player] = NEXT_PTR(netplay->input_ptr);
netplay->read_frame_count[player] = netplay->self_frame_count + 1; netplay->read_frame_count[player] = netplay->input_frame_count + 1;
break; break;
} }
@ -734,7 +734,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
frame = ntohl(payload[0]); frame = ntohl(payload[0]);
/* We're changing past input, so must replay it */ /* We're changing past input, so must replay it */
if (frame < netplay->self_frame_count) if (frame < netplay->input_frame_count)
netplay->force_rewind = true; netplay->force_rewind = true;
mode = ntohl(payload[1]); mode = ntohl(payload[1]);
@ -767,16 +767,16 @@ static bool netplay_get_cmd(netplay_t *netplay,
netplay->self_player = player; netplay->self_player = player;
/* Fix up current frame info */ /* Fix up current frame info */
if (frame <= netplay->self_frame_count) if (frame <= netplay->input_frame_count)
{ {
/* It wanted past frames, better send 'em! */ /* It wanted past frames, better send 'em! */
START(netplay->server_ptr); START(netplay->server_ptr);
while (dframe->used && dframe->frame <= netplay->self_frame_count) while (dframe->used && dframe->frame <= netplay->input_frame_count)
{ {
memcpy(dframe->real_input_state[player], dframe->self_state, sizeof(dframe->self_state)); memcpy(dframe->real_input_state[player], dframe->self_state, sizeof(dframe->self_state));
dframe->have_real[player] = true; dframe->have_real[player] = true;
send_input_frame(netplay, connection, NULL, dframe->frame, player, dframe->self_state); send_input_frame(netplay, connection, NULL, dframe->frame, player, dframe->self_state);
if (dframe->frame == netplay->self_frame_count) break; if (dframe->frame == netplay->input_frame_count) break;
NEXT(); NEXT();
} }
@ -786,8 +786,8 @@ static bool netplay_get_cmd(netplay_t *netplay,
uint32_t frame_count; uint32_t frame_count;
/* It wants future frames, make sure we don't capture or send intermediate ones */ /* It wants future frames, make sure we don't capture or send intermediate ones */
START(netplay->self_ptr); START(netplay->input_ptr);
frame_count = netplay->self_frame_count; frame_count = netplay->input_frame_count;
while (true) while (true)
{ {
if (!dframe->used) if (!dframe->used)
@ -954,7 +954,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
case NETPLAY_CMD_CRC: case NETPLAY_CMD_CRC:
{ {
uint32_t buffer[2]; uint32_t buffer[2];
size_t tmp_ptr = netplay->self_ptr; size_t tmp_ptr = netplay->input_ptr;
bool found = false; bool found = false;
if (cmd_size != sizeof(buffer)) if (cmd_size != sizeof(buffer))
@ -985,7 +985,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
} }
tmp_ptr = PREV_PTR(tmp_ptr); tmp_ptr = PREV_PTR(tmp_ptr);
} while (tmp_ptr != netplay->self_ptr); } while (tmp_ptr != netplay->input_ptr);
if (!found) if (!found)
{ {
@ -1035,8 +1035,8 @@ static bool netplay_get_cmd(netplay_t *netplay,
if (!netplay->is_replay) if (!netplay->is_replay)
{ {
netplay->is_replay = true; netplay->is_replay = true;
netplay->replay_ptr = netplay->self_ptr; netplay->replay_ptr = netplay->run_ptr;
netplay->replay_frame_count = netplay->self_frame_count; netplay->replay_frame_count = netplay->run_frame_count;
netplay_wait_and_init_serialization(netplay); netplay_wait_and_init_serialization(netplay);
netplay->is_replay = false; netplay->is_replay = false;
} }
@ -1134,15 +1134,20 @@ static bool netplay_get_cmd(netplay_t *netplay,
true, &rd, &wn, NULL); true, &rd, &wn, NULL);
/* Skip ahead if it's past where we are */ /* Skip ahead if it's past where we are */
if (frame > netplay->self_frame_count) if (frame > netplay->run_frame_count)
{ {
/* This is squirrely: We need to assure that when we advance the /* This is squirrely: We need to assure that when we advance the
* frame in post_frame, THEN we're referring to the frame to * frame in post_frame, THEN we're referring to the frame to
* load into. If we refer directly to read_ptr, then we'll end * load into. If we refer directly to read_ptr, then we'll end
* up never reading the input for read_frame_count itself, which * up never reading the input for read_frame_count itself, which
* will make the other side unhappy. */ * will make the other side unhappy. */
netplay->self_ptr = PREV_PTR(netplay->read_ptr[connection->player]); netplay->run_ptr = PREV_PTR(netplay->read_ptr[connection->player]);
netplay->self_frame_count = frame - 1; netplay->run_frame_count = frame - 1;
if (frame > netplay->input_frame_count)
{
netplay->input_ptr = netplay->run_ptr;
netplay->input_frame_count = netplay->run_frame_count;
}
} }
/* Don't expect earlier data from other clients */ /* Don't expect earlier data from other clients */
@ -1308,7 +1313,7 @@ int netplay_poll_net_input(netplay_t *netplay, bool block)
netplay_update_unread_ptr(netplay); netplay_update_unread_ptr(netplay);
/* If we were blocked for input, pass if we have this frame's input */ /* If we were blocked for input, pass if we have this frame's input */
if (netplay->unread_frame_count > netplay->self_frame_count) if (netplay->unread_frame_count > netplay->run_frame_count)
break; break;
/* If we're supposed to block but we didn't have enough input, wait for it */ /* If we're supposed to block but we didn't have enough input, wait for it */
@ -1330,7 +1335,7 @@ int netplay_poll_net_input(netplay_t *netplay, bool block)
return -1; return -1;
RARCH_LOG("Network is stalling at frame %u, count %u of %d ...\n", RARCH_LOG("Network is stalling at frame %u, count %u of %d ...\n",
netplay->self_frame_count, netplay->timeout_cnt, MAX_RETRIES); netplay->run_frame_count, netplay->timeout_cnt, MAX_RETRIES);
if (netplay->timeout_cnt >= MAX_RETRIES && !netplay->remote_paused) if (netplay->timeout_cnt >= MAX_RETRIES && !netplay->remote_paused)
return -1; return -1;
@ -1348,7 +1353,7 @@ int netplay_poll_net_input(netplay_t *netplay, bool block)
*/ */
bool netplay_flip_port(netplay_t *netplay) bool netplay_flip_port(netplay_t *netplay)
{ {
size_t frame = netplay->self_frame_count; size_t frame = netplay->input_frame_count;
if (netplay->flip_frame == 0) if (netplay->flip_frame == 0)
return false; return false;

View File

@ -50,6 +50,9 @@
#define NETPLAY_MAX_REQ_STALL_TIME 60 #define NETPLAY_MAX_REQ_STALL_TIME 60
#define NETPLAY_MAX_REQ_STALL_FREQUENCY 120 #define NETPLAY_MAX_REQ_STALL_FREQUENCY 120
/* TEMPORARY */
#define NETPLAY_INPUT_LATENCY_FRAMES 5
#define PREV_PTR(x) ((x) == 0 ? netplay->buffer_size - 1 : (x) - 1) #define PREV_PTR(x) ((x) == 0 ? netplay->buffer_size - 1 : (x) - 1)
#define NEXT_PTR(x) ((x + 1) % netplay->buffer_size) #define NEXT_PTR(x) ((x + 1) % netplay->buffer_size)
@ -210,6 +213,7 @@ enum rarch_netplay_stall_reason
{ {
NETPLAY_STALL_NONE = 0, NETPLAY_STALL_NONE = 0,
NETPLAY_STALL_RUNNING_FAST, NETPLAY_STALL_RUNNING_FAST,
NETPLAY_STALL_INPUT_LATENCY,
NETPLAY_STALL_SERVER_REQUESTED, NETPLAY_STALL_SERVER_REQUESTED,
NETPLAY_STALL_NO_CONNECTION NETPLAY_STALL_NO_CONNECTION
}; };
@ -357,9 +361,13 @@ struct netplay
/* The size of our packet buffers */ /* The size of our packet buffers */
size_t packet_buffer_size; size_t packet_buffer_size;
/* The current frame seen by the frontend */ /* The frame we're currently inputting */
size_t self_ptr; size_t input_ptr;
uint32_t self_frame_count; uint32_t input_frame_count;
/* The frame we're currently running */
size_t run_ptr;
uint32_t run_frame_count;
/* The first frame at which some data might be unreliable */ /* The first frame at which some data might be unreliable */
size_t other_ptr; size_t other_ptr;

View File

@ -42,8 +42,8 @@ void netplay_update_unread_ptr(netplay_t *netplay)
if (netplay->is_server && !netplay->connected_players) if (netplay->is_server && !netplay->connected_players)
{ {
/* Nothing at all to read! */ /* Nothing at all to read! */
netplay->unread_ptr = netplay->self_ptr; netplay->unread_ptr = netplay->input_ptr;
netplay->unread_frame_count = netplay->self_frame_count; netplay->unread_frame_count = netplay->input_frame_count;
} }
else else
@ -186,14 +186,14 @@ bool netplay_sync_pre_frame(netplay_t *netplay)
{ {
retro_ctx_serialize_info_t serial_info; retro_ctx_serialize_info_t serial_info;
if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->self_ptr], netplay->self_frame_count)) if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->run_ptr], netplay->run_frame_count))
{ {
serial_info.data_const = NULL; serial_info.data_const = NULL;
serial_info.data = netplay->buffer[netplay->self_ptr].state; serial_info.data = netplay->buffer[netplay->run_ptr].state;
serial_info.size = netplay->state_size; serial_info.size = netplay->state_size;
memset(serial_info.data, 0, serial_info.size); memset(serial_info.data, 0, serial_info.size);
if ((netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) || netplay->self_frame_count == 0) if ((netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) || netplay->run_frame_count == 0)
{ {
/* Don't serialize until it's safe */ /* Don't serialize until it's safe */
} }
@ -201,8 +201,19 @@ bool netplay_sync_pre_frame(netplay_t *netplay)
{ {
if (netplay->force_send_savestate && !netplay->stall && !netplay->remote_paused) if (netplay->force_send_savestate && !netplay->stall && !netplay->remote_paused)
{ {
/* Bring our running frame and input frames into parity so we don't
* send old info */
if (netplay->run_ptr != netplay->input_ptr)
{
memcpy(netplay->buffer[netplay->input_ptr].state,
netplay->buffer[netplay->run_ptr].state,
netplay->state_size);
netplay->run_ptr = netplay->input_ptr;
netplay->run_frame_count = netplay->input_frame_count;
}
/* Send this along to the other side */ /* Send this along to the other side */
serial_info.data_const = netplay->buffer[netplay->self_ptr].state; serial_info.data_const = netplay->buffer[netplay->run_ptr].state;
netplay_load_savestate(netplay, &serial_info, false); netplay_load_savestate(netplay, &serial_info, false);
netplay->force_send_savestate = false; netplay->force_send_savestate = false;
} }
@ -216,7 +227,7 @@ bool netplay_sync_pre_frame(netplay_t *netplay)
} }
/* If we can't transmit savestates, we must stall until the client is ready */ /* If we can't transmit savestates, we must stall until the client is ready */
if (netplay->self_frame_count > 0 && if (netplay->run_frame_count > 0 &&
(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)) && (netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)) &&
(netplay->connections_size == 0 || !netplay->connections[0].active || (netplay->connections_size == 0 || !netplay->connections[0].active ||
netplay->connections[0].mode < NETPLAY_CONNECTION_CONNECTED)) netplay->connections[0].mode < NETPLAY_CONNECTION_CONNECTED))
@ -357,16 +368,24 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
/* Unless we're stalling, we've just finished running a frame */ /* Unless we're stalling, we've just finished running a frame */
if (!stalled) if (!stalled)
{ {
netplay->self_ptr = NEXT_PTR(netplay->self_ptr); netplay->run_ptr = NEXT_PTR(netplay->run_ptr);
netplay->self_frame_count++; netplay->run_frame_count++;
}
/* We've finished an input frame even if we're stalling, unless we're too
* far ahead of ourselves */
if (netplay->input_frame_count < netplay->run_frame_count + NETPLAY_INPUT_LATENCY_FRAMES)
{
netplay->input_ptr = NEXT_PTR(netplay->input_ptr);
netplay->input_frame_count++;
} }
/* Only relevant if we're connected */ /* Only relevant if we're connected */
if ((netplay->is_server && !netplay->connected_players) || if ((netplay->is_server && !netplay->connected_players) ||
(netplay->self_mode < NETPLAY_CONNECTION_CONNECTED)) (netplay->self_mode < NETPLAY_CONNECTION_CONNECTED))
{ {
netplay->other_frame_count = netplay->self_frame_count; netplay->other_frame_count = netplay->input_frame_count;
netplay->other_ptr = netplay->self_ptr; netplay->other_ptr = netplay->input_ptr;
/* FIXME: Duplication */ /* FIXME: Duplication */
if (netplay->catch_up) if (netplay->catch_up)
{ {
@ -383,7 +402,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
/* Skip ahead if we predicted correctly. /* Skip ahead if we predicted correctly.
* Skip until our simulation failed. */ * Skip until our simulation failed. */
while (netplay->other_frame_count < netplay->unread_frame_count && while (netplay->other_frame_count < netplay->unread_frame_count &&
netplay->other_frame_count < netplay->self_frame_count) netplay->other_frame_count < netplay->run_frame_count)
{ {
struct delta_frame *ptr = &netplay->buffer[netplay->other_ptr]; struct delta_frame *ptr = &netplay->buffer[netplay->other_ptr];
size_t i; size_t i;
@ -406,7 +425,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
/* Now replay the real input if we've gotten ahead of it */ /* Now replay the real input if we've gotten ahead of it */
if (netplay->force_rewind || if (netplay->force_rewind ||
(netplay->other_frame_count < netplay->unread_frame_count && (netplay->other_frame_count < netplay->unread_frame_count &&
netplay->other_frame_count < netplay->self_frame_count)) netplay->other_frame_count < netplay->run_frame_count))
{ {
retro_ctx_serialize_info_t serial_info; retro_ctx_serialize_info_t serial_info;
@ -428,7 +447,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
RARCH_ERR("Netplay savestate loading failed: Prepare for desync!\n"); RARCH_ERR("Netplay savestate loading failed: Prepare for desync!\n");
} }
while (netplay->replay_frame_count < netplay->self_frame_count) while (netplay->replay_frame_count < netplay->run_frame_count)
{ {
retro_time_t start, tm; retro_time_t start, tm;
@ -483,15 +502,15 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
/* Average our time */ /* Average our time */
netplay->frame_run_time_avg = netplay->frame_run_time_sum / NETPLAY_FRAME_RUN_TIME_WINDOW; netplay->frame_run_time_avg = netplay->frame_run_time_sum / NETPLAY_FRAME_RUN_TIME_WINDOW;
if (netplay->unread_frame_count < netplay->self_frame_count) if (netplay->unread_frame_count < netplay->run_frame_count)
{ {
netplay->other_ptr = netplay->unread_ptr; netplay->other_ptr = netplay->unread_ptr;
netplay->other_frame_count = netplay->unread_frame_count; netplay->other_frame_count = netplay->unread_frame_count;
} }
else else
{ {
netplay->other_ptr = netplay->self_ptr; netplay->other_ptr = netplay->run_ptr;
netplay->other_frame_count = netplay->self_frame_count; netplay->other_frame_count = netplay->run_frame_count;
} }
netplay->is_replay = false; netplay->is_replay = false;
netplay->force_rewind = false; netplay->force_rewind = false;
@ -520,7 +539,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
if (netplay->catch_up) if (netplay->catch_up)
{ {
/* Are we caught up? */ /* Are we caught up? */
if (netplay->self_frame_count >= lo_frame_count) if (netplay->input_frame_count >= lo_frame_count)
{ {
netplay->catch_up = false; netplay->catch_up = false;
input_driver_unset_nonblock_state(); input_driver_unset_nonblock_state();
@ -530,7 +549,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
} }
else if (!stalled) else if (!stalled)
{ {
if (netplay->self_frame_count + 2 < lo_frame_count) if (netplay->input_frame_count + 2 < lo_frame_count)
{ {
/* Are we falling behind? */ /* Are we falling behind? */
netplay->catch_up = true; netplay->catch_up = true;
@ -538,7 +557,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
driver_set_nonblock_state(); driver_set_nonblock_state();
} }
else if (netplay->self_frame_count + 2 < hi_frame_count) else if (netplay->input_frame_count + 2 < hi_frame_count)
{ {
size_t i; size_t i;
@ -554,16 +573,16 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
player = connection->player; player = connection->player;
/* Are they ahead? */ /* Are they ahead? */
if (netplay->self_frame_count + 2 < netplay->read_frame_count[player]) if (netplay->input_frame_count + 2 < netplay->read_frame_count[player])
{ {
/* Tell them to stall */ /* Tell them to stall */
if (connection->stall_frame + NETPLAY_MAX_REQ_STALL_FREQUENCY < if (connection->stall_frame + NETPLAY_MAX_REQ_STALL_FREQUENCY <
netplay->self_frame_count) netplay->input_frame_count)
{ {
connection->stall_frame = netplay->self_frame_count; connection->stall_frame = netplay->input_frame_count;
netplay_cmd_stall(netplay, connection, netplay_cmd_stall(netplay, connection,
netplay->read_frame_count[player] - netplay->read_frame_count[player] -
netplay->self_frame_count + 1); netplay->input_frame_count + 1);
} }
} }
} }