mirror of
https://github.com/CTCaer/RetroArch.git
synced 2024-12-23 11:18:25 +00:00
Merge pull request #4547 from GregorR/netplay-input-latency-2
Netplay input latency
This commit is contained in:
commit
089ed2f215
@ -964,6 +964,8 @@ static struct config_int_setting *populate_settings_int(settings_t *settings, in
|
||||
#ifdef HAVE_NETWORKING
|
||||
SETTING_INT("netplay_ip_port", &settings->netplay.port, true, RARCH_DEFAULT_PORT, false);
|
||||
SETTING_INT("netplay_check_frames", (unsigned*)&settings->netplay.check_frames, true, netplay_check_frames, false);
|
||||
SETTING_INT("netplay_input_latency_frames_min",&settings->netplay.input_latency_frames_min, true, 0, false);
|
||||
SETTING_INT("netplay_input_latency_frames_range",&settings->netplay.input_latency_frames_range, true, 0, false);
|
||||
#endif
|
||||
#ifdef HAVE_LANGEXTRA
|
||||
SETTING_INT("user_language", &settings->user_language, true, RETRO_LANGUAGE_ENGLISH, false);
|
||||
|
@ -407,6 +407,8 @@ typedef struct settings
|
||||
unsigned port;
|
||||
bool stateless_mode;
|
||||
int check_frames;
|
||||
unsigned input_latency_frames_min;
|
||||
unsigned input_latency_frames_range;
|
||||
bool swap_input;
|
||||
bool nat_traversal;
|
||||
char password[128];
|
||||
|
@ -587,6 +587,10 @@ MSG_HASH(MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES,
|
||||
"netplay_check_frames")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT,
|
||||
"netplay_client_swap_input")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
|
||||
"netplay_input_latency_frames_min")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE,
|
||||
"netplay_input_latency_frames_range")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_DISCONNECT,
|
||||
"menu_netplay_disconnect")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_ENABLE,
|
||||
|
@ -1560,6 +1560,31 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) {
|
||||
"no checks. This value is only used on the \n"
|
||||
"netplay host. \n");
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN:
|
||||
snprintf(s, len,
|
||||
"The number of frames of input latency for \n"
|
||||
"netplay to use to hide network latency. \n"
|
||||
" \n"
|
||||
"When in netplay, this option delays local \n"
|
||||
"input, so that the frame being run is \n"
|
||||
"closer to the frames being received from \n"
|
||||
"the network. This reduces jitter and makes \n"
|
||||
"netplay less CPU-intensive, but at the \n"
|
||||
"price of noticeable input lag. \n");
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE:
|
||||
snprintf(s, len,
|
||||
"The range of frames of input latency that \n"
|
||||
"may be used by netplay to hide network \n"
|
||||
"latency. \n"
|
||||
"\n"
|
||||
"If set, netplay will adjust the number of \n"
|
||||
"frames of input latency dynamically to \n"
|
||||
"balance CPU time, input latency and \n"
|
||||
"network latency. This reduces jitter and \n"
|
||||
"makes netplay less CPU-intensive, but at \n"
|
||||
"the price of unpredictable input lag. \n");
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL:
|
||||
snprintf(s, len,
|
||||
"When hosting, attempt to listen for\n"
|
||||
|
@ -964,6 +964,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY,
|
||||
"Netplay")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES,
|
||||
"Netplay Check Frames")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
|
||||
"Input Latency Frames")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE,
|
||||
"Input Latency Frames Range")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT,
|
||||
"Netplay P2 Uses C1")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES,
|
||||
|
@ -4813,6 +4813,14 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
|
||||
MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES,
|
||||
PARSE_ONLY_INT, false) != -1)
|
||||
count++;
|
||||
if (menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
|
||||
PARSE_ONLY_INT, false) != -1)
|
||||
count++;
|
||||
if (menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE,
|
||||
PARSE_ONLY_INT, false) != -1)
|
||||
count++;
|
||||
if (menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL,
|
||||
PARSE_ONLY_BOOL, false) != -1)
|
||||
|
@ -5672,7 +5672,35 @@ static bool setting_append_list(
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler);
|
||||
menu_settings_list_current_add_range(list, list_info, -600, 600, 1, true, false);
|
||||
menu_settings_list_current_add_range(list, list_info, -600, 600, 1, false, false);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||
|
||||
CONFIG_INT(
|
||||
list, list_info,
|
||||
(int *) &settings->netplay.input_latency_frames_min,
|
||||
MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
|
||||
MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
|
||||
0,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler);
|
||||
menu_settings_list_current_add_range(list, list_info, 0, 15, 1, true, true);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||
|
||||
CONFIG_INT(
|
||||
list, list_info,
|
||||
(int *) &settings->netplay.input_latency_frames_range,
|
||||
MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE,
|
||||
MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE,
|
||||
0,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler);
|
||||
menu_settings_list_current_add_range(list, list_info, 0, 15, 1, true, true);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||
|
||||
CONFIG_BOOL(
|
||||
|
@ -1020,6 +1020,8 @@ enum msg_hash_enums
|
||||
MENU_LABEL(NETPLAY_DELAY_FRAMES),
|
||||
MENU_LABEL(NETPLAY_STATELESS_MODE),
|
||||
MENU_LABEL(NETPLAY_CHECK_FRAMES),
|
||||
MENU_LABEL(NETPLAY_INPUT_LATENCY_FRAMES_MIN),
|
||||
MENU_LABEL(NETPLAY_INPUT_LATENCY_FRAMES_RANGE),
|
||||
MENU_LABEL(NETPLAY_SPECTATOR_MODE_ENABLE),
|
||||
MENU_LABEL(NETPLAY_TCP_UDP_PORT),
|
||||
MENU_LABEL(NETPLAY_NAT_TRAVERSAL),
|
||||
|
@ -42,6 +42,10 @@ In general, other ≤ unread and other ≤ self. In all likelihood, unread ≤ s
|
||||
but it is both possible and supported for the remote host to get ahead of the
|
||||
local host.
|
||||
|
||||
There is an additional location, run, used when input latency is desired. In
|
||||
that case, self points to where input is being read, and run points to the
|
||||
frame actually being executed. Run is purely local.
|
||||
|
||||
The server has a slightly more complicated job as it can handle multiple
|
||||
clients, however it is not vastly more complicated: For each connection which
|
||||
is playing (i.e., has a controller), it maintains a per-player unread frame,
|
||||
|
@ -205,7 +205,7 @@ static bool netplay_poll(void)
|
||||
netplay_update_unread_ptr(netplay_data);
|
||||
if (netplay_data->stateless_mode &&
|
||||
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);
|
||||
else
|
||||
res = netplay_poll_net_input(netplay_data, false);
|
||||
@ -218,14 +218,73 @@ static bool netplay_poll(void)
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
netplay_update_unread_ptr(netplay_data);
|
||||
|
||||
/* Figure out how many frames of input latency we should be using to hide
|
||||
* network latency */
|
||||
if (netplay_data->frame_run_time_avg || netplay_data->stateless_mode)
|
||||
{
|
||||
/* FIXME: Using fixed 60fps for this calculation */
|
||||
unsigned frames_per_frame = netplay_data->frame_run_time_avg ?
|
||||
(16666/netplay_data->frame_run_time_avg) :
|
||||
0;
|
||||
unsigned frames_ahead = (netplay_data->run_frame_count > netplay_data->unread_frame_count) ?
|
||||
(netplay_data->run_frame_count - netplay_data->unread_frame_count) :
|
||||
0;
|
||||
settings_t *settings = config_get_ptr();
|
||||
unsigned input_latency_frames_min = settings->netplay.input_latency_frames_min;
|
||||
unsigned input_latency_frames_max = input_latency_frames_min + settings->netplay.input_latency_frames_range;
|
||||
|
||||
/* Assume we need a couple frames worth of time to actually run the
|
||||
* current frame */
|
||||
if (frames_per_frame > 2)
|
||||
frames_per_frame -= 2;
|
||||
else
|
||||
frames_per_frame = 0;
|
||||
|
||||
/* Shall we adjust our latency? */
|
||||
if (netplay_data->stateless_mode)
|
||||
{
|
||||
/* In stateless mode, we adjust up if we're "close" and down if we
|
||||
* have a lot of slack */
|
||||
if (netplay_data->input_latency_frames < input_latency_frames_min ||
|
||||
(netplay_data->unread_frame_count == netplay_data->run_frame_count + 1 &&
|
||||
netplay_data->input_latency_frames < input_latency_frames_max))
|
||||
{
|
||||
netplay_data->input_latency_frames++;
|
||||
}
|
||||
else if (netplay_data->input_latency_frames > input_latency_frames_max ||
|
||||
(netplay_data->unread_frame_count > netplay_data->run_frame_count + 2 &&
|
||||
netplay_data->input_latency_frames > input_latency_frames_min))
|
||||
{
|
||||
netplay_data->input_latency_frames--;
|
||||
}
|
||||
|
||||
}
|
||||
else if (netplay_data->input_latency_frames < input_latency_frames_min ||
|
||||
(frames_per_frame < frames_ahead &&
|
||||
netplay_data->input_latency_frames < input_latency_frames_max))
|
||||
{
|
||||
/* We can't hide this much network latency with replay, so hide some
|
||||
* with input latency */
|
||||
netplay_data->input_latency_frames++;
|
||||
}
|
||||
else if (netplay_data->input_latency_frames > input_latency_frames_max ||
|
||||
(frames_per_frame > frames_ahead + 2 &&
|
||||
netplay_data->input_latency_frames > input_latency_frames_min))
|
||||
{
|
||||
/* We don't need this much latency (any more) */
|
||||
netplay_data->input_latency_frames--;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're stalled, consider unstalling */
|
||||
switch (netplay_data->stall)
|
||||
{
|
||||
case NETPLAY_STALL_RUNNING_FAST:
|
||||
{
|
||||
netplay_update_unread_ptr(netplay_data);
|
||||
if (netplay_data->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2
|
||||
> netplay_data->self_frame_count)
|
||||
{
|
||||
@ -240,6 +299,11 @@ static bool netplay_poll(void)
|
||||
break;
|
||||
}
|
||||
|
||||
case NETPLAY_STALL_INPUT_LATENCY:
|
||||
/* Just let it recalculate momentarily */
|
||||
netplay_data->stall = NETPLAY_STALL_NONE;
|
||||
break;
|
||||
|
||||
case NETPLAY_STALL_SERVER_REQUESTED:
|
||||
{
|
||||
/* See if the stall is done */
|
||||
@ -261,38 +325,50 @@ static bool netplay_poll(void)
|
||||
break;
|
||||
|
||||
default: /* not stalling */
|
||||
{
|
||||
/* Are we too far ahead? */
|
||||
netplay_update_unread_ptr(netplay_data);
|
||||
if (netplay_data->unread_frame_count + NETPLAY_MAX_STALL_FRAMES
|
||||
<= netplay_data->self_frame_count)
|
||||
{
|
||||
netplay_data->stall = NETPLAY_STALL_RUNNING_FAST;
|
||||
netplay_data->stall_time = cpu_features_get_time_usec();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Figure out who to blame */
|
||||
if (netplay_data->is_server)
|
||||
/* If we're not stalled, consider stalling */
|
||||
if (!netplay_data->stall)
|
||||
{
|
||||
/* Have we not reat enough latency frames? */
|
||||
if (netplay_data->self_mode == NETPLAY_CONNECTION_PLAYING &&
|
||||
netplay_data->connected_players &&
|
||||
netplay_data->run_frame_count + netplay_data->input_latency_frames > netplay_data->self_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
|
||||
<= netplay_data->self_frame_count)
|
||||
{
|
||||
netplay_data->stall = NETPLAY_STALL_RUNNING_FAST;
|
||||
netplay_data->stall_time = cpu_features_get_time_usec();
|
||||
|
||||
/* Figure out who to blame */
|
||||
if (netplay_data->is_server)
|
||||
{
|
||||
for (player = 0; player < MAX_USERS; player++)
|
||||
{
|
||||
for (player = 0; player < MAX_USERS; player++)
|
||||
if (!(netplay_data->connected_players & (1<<player))) continue;
|
||||
if (netplay_data->read_frame_count[player] > netplay_data->unread_frame_count) continue;
|
||||
for (i = 0; i < netplay_data->connections_size; i++)
|
||||
{
|
||||
if (!(netplay_data->connected_players & (1<<player))) continue;
|
||||
if (netplay_data->read_frame_count[player] > netplay_data->unread_frame_count) continue;
|
||||
for (i = 0; i < netplay_data->connections_size; i++)
|
||||
struct netplay_connection *connection = &netplay_data->connections[i];
|
||||
if (connection->active &&
|
||||
connection->mode == NETPLAY_CONNECTION_PLAYING &&
|
||||
connection->player == player)
|
||||
{
|
||||
struct netplay_connection *connection = &netplay_data->connections[i];
|
||||
if (connection->active &&
|
||||
connection->mode == NETPLAY_CONNECTION_PLAYING &&
|
||||
connection->player == player)
|
||||
{
|
||||
connection->stall = NETPLAY_STALL_RUNNING_FAST;
|
||||
connection->stall_time = netplay_data->stall_time;
|
||||
break;
|
||||
}
|
||||
connection->stall = NETPLAY_STALL_RUNNING_FAST;
|
||||
connection->stall_time = netplay_data->stall_time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,7 +447,7 @@ static int16_t netplay_input_state(netplay_t *netplay,
|
||||
unsigned idx, unsigned id)
|
||||
{
|
||||
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;
|
||||
|
||||
@ -688,7 +764,7 @@ void netplay_send_savestate(netplay_t *netplay,
|
||||
/* Send it to relevant peers */
|
||||
header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE);
|
||||
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);
|
||||
|
||||
for (i = 0; i < netplay->connections_size; i++)
|
||||
@ -721,16 +797,21 @@ void netplay_load_savestate(netplay_t *netplay,
|
||||
{
|
||||
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->self_ptr;
|
||||
netplay->run_frame_count = netplay->self_frame_count;
|
||||
|
||||
/* Record it in our own buffer */
|
||||
if (save || !serial_info)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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))
|
||||
return;
|
||||
tmp_serial_info.data_const = tmp_serial_info.data;
|
||||
@ -740,7 +821,7 @@ void netplay_load_savestate(netplay_t *netplay,
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -755,29 +836,29 @@ void netplay_load_savestate(netplay_t *netplay,
|
||||
/* We need to ignore any intervening data from the other side,
|
||||
* and never rewind past this */
|
||||
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;
|
||||
for (player = 0; player < MAX_USERS; player++)
|
||||
{
|
||||
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_frame_count[player] = netplay->self_frame_count;
|
||||
netplay->read_ptr[player] = netplay->run_ptr;
|
||||
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_frame_count = netplay->self_frame_count;
|
||||
netplay->server_ptr = netplay->run_ptr;
|
||||
netplay->server_frame_count = netplay->run_frame_count;
|
||||
}
|
||||
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_frame_count = netplay->self_frame_count;
|
||||
netplay->other_ptr = netplay->run_ptr;
|
||||
netplay->other_frame_count = netplay->run_frame_count;
|
||||
}
|
||||
|
||||
/* If we can't send it to the peer, loading a state was a bad idea */
|
||||
|
@ -904,9 +904,9 @@ bool netplay_handshake_pre_sync(netplay_t *netplay,
|
||||
netplay->flip_frame = flip_frame;
|
||||
|
||||
/* Set our frame counters as requested */
|
||||
netplay->self_frame_count = netplay->other_frame_count =
|
||||
netplay->unread_frame_count = netplay->server_frame_count =
|
||||
new_frame_count;
|
||||
netplay->self_frame_count = netplay->run_frame_count =
|
||||
netplay->other_frame_count = netplay->unread_frame_count =
|
||||
netplay->server_frame_count = new_frame_count;
|
||||
for (i = 0; i < netplay->buffer_size; i++)
|
||||
{
|
||||
struct delta_frame *ptr = &netplay->buffer[i];
|
||||
@ -919,7 +919,8 @@ bool netplay_handshake_pre_sync(netplay_t *netplay,
|
||||
return false;
|
||||
ptr->frame = new_frame_count;
|
||||
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;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -321,7 +321,7 @@ bool netplay_try_init_serialization(netplay_t *netplay)
|
||||
|
||||
/* Check if we can actually save */
|
||||
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;
|
||||
|
||||
if (!core_serialize(&serial_info))
|
||||
|
@ -954,7 +954,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
case NETPLAY_CMD_CRC:
|
||||
{
|
||||
uint32_t buffer[2];
|
||||
size_t tmp_ptr = netplay->self_ptr;
|
||||
size_t tmp_ptr = netplay->run_ptr;
|
||||
bool found = false;
|
||||
|
||||
if (cmd_size != sizeof(buffer))
|
||||
@ -985,7 +985,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
}
|
||||
|
||||
tmp_ptr = PREV_PTR(tmp_ptr);
|
||||
} while (tmp_ptr != netplay->self_ptr);
|
||||
} while (tmp_ptr != netplay->run_ptr);
|
||||
|
||||
if (!found)
|
||||
{
|
||||
@ -1035,8 +1035,8 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
if (!netplay->is_replay)
|
||||
{
|
||||
netplay->is_replay = true;
|
||||
netplay->replay_ptr = netplay->self_ptr;
|
||||
netplay->replay_frame_count = netplay->self_frame_count;
|
||||
netplay->replay_ptr = netplay->run_ptr;
|
||||
netplay->replay_frame_count = netplay->run_frame_count;
|
||||
netplay_wait_and_init_serialization(netplay);
|
||||
netplay->is_replay = false;
|
||||
}
|
||||
@ -1134,15 +1134,20 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
true, &rd, &wn, NULL);
|
||||
|
||||
/* 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
|
||||
* 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
|
||||
* up never reading the input for read_frame_count itself, which
|
||||
* will make the other side unhappy. */
|
||||
netplay->self_ptr = PREV_PTR(netplay->read_ptr[connection->player]);
|
||||
netplay->self_frame_count = frame - 1;
|
||||
netplay->run_ptr = PREV_PTR(netplay->read_ptr[connection->player]);
|
||||
netplay->run_frame_count = frame - 1;
|
||||
if (frame > netplay->self_frame_count)
|
||||
{
|
||||
netplay->self_ptr = netplay->run_ptr;
|
||||
netplay->self_frame_count = netplay->run_frame_count;
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't expect earlier data from other clients */
|
||||
@ -1289,6 +1294,8 @@ int netplay_poll_net_input(netplay_t *netplay, bool block)
|
||||
if (max_fd == 0)
|
||||
return 0;
|
||||
|
||||
netplay->timeout_cnt = 0;
|
||||
|
||||
do
|
||||
{
|
||||
had_input = false;
|
||||
@ -1308,7 +1315,7 @@ int netplay_poll_net_input(netplay_t *netplay, bool block)
|
||||
netplay_update_unread_ptr(netplay);
|
||||
|
||||
/* 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;
|
||||
|
||||
/* If we're supposed to block but we didn't have enough input, wait for it */
|
||||
@ -1330,7 +1337,7 @@ int netplay_poll_net_input(netplay_t *netplay, bool block)
|
||||
return -1;
|
||||
|
||||
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)
|
||||
return -1;
|
||||
|
@ -210,6 +210,7 @@ enum rarch_netplay_stall_reason
|
||||
{
|
||||
NETPLAY_STALL_NONE = 0,
|
||||
NETPLAY_STALL_RUNNING_FAST,
|
||||
NETPLAY_STALL_INPUT_LATENCY,
|
||||
NETPLAY_STALL_SERVER_REQUESTED,
|
||||
NETPLAY_STALL_NO_CONNECTION
|
||||
};
|
||||
@ -357,10 +358,15 @@ struct netplay
|
||||
/* The size of our packet buffers */
|
||||
size_t packet_buffer_size;
|
||||
|
||||
/* The current frame seen by the frontend */
|
||||
/* The frame we're currently inputting */
|
||||
size_t self_ptr;
|
||||
uint32_t self_frame_count;
|
||||
|
||||
/* The frame we're currently running, which may be behind the frame we're
|
||||
* currently inputting if we're using input latency */
|
||||
size_t run_ptr;
|
||||
uint32_t run_frame_count;
|
||||
|
||||
/* The first frame at which some data might be unreliable */
|
||||
size_t other_ptr;
|
||||
uint32_t other_frame_count;
|
||||
@ -437,6 +443,9 @@ struct netplay
|
||||
int frame_run_time_ptr;
|
||||
retro_time_t frame_run_time_sum, frame_run_time_avg;
|
||||
|
||||
/* Latency frames and limits */
|
||||
unsigned input_latency_frames;
|
||||
|
||||
/* Are we stalled? */
|
||||
enum rarch_netplay_stall_reason stall;
|
||||
|
||||
|
@ -186,14 +186,14 @@ bool netplay_sync_pre_frame(netplay_t *netplay)
|
||||
{
|
||||
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 = netplay->buffer[netplay->self_ptr].state;
|
||||
serial_info.data = netplay->buffer[netplay->run_ptr].state;
|
||||
serial_info.size = netplay->state_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 */
|
||||
}
|
||||
@ -201,8 +201,19 @@ bool netplay_sync_pre_frame(netplay_t *netplay)
|
||||
{
|
||||
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->self_ptr)
|
||||
{
|
||||
memcpy(netplay->buffer[netplay->self_ptr].state,
|
||||
netplay->buffer[netplay->run_ptr].state,
|
||||
netplay->state_size);
|
||||
netplay->run_ptr = netplay->self_ptr;
|
||||
netplay->run_frame_count = netplay->self_frame_count;
|
||||
}
|
||||
|
||||
/* 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->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 (netplay->self_frame_count > 0 &&
|
||||
if (netplay->run_frame_count > 0 &&
|
||||
(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)) &&
|
||||
(netplay->connections_size == 0 || !netplay->connections[0].active ||
|
||||
netplay->connections[0].mode < NETPLAY_CONNECTION_CONNECTED))
|
||||
@ -356,6 +367,14 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
|
||||
/* Unless we're stalling, we've just finished running a frame */
|
||||
if (!stalled)
|
||||
{
|
||||
netplay->run_ptr = NEXT_PTR(netplay->run_ptr);
|
||||
netplay->run_frame_count++;
|
||||
}
|
||||
|
||||
/* We've finished an input frame even if we're stalling */
|
||||
if ((!stalled || netplay->stall == NETPLAY_STALL_INPUT_LATENCY) &&
|
||||
netplay->self_frame_count < netplay->run_frame_count + netplay->input_latency_frames)
|
||||
{
|
||||
netplay->self_ptr = NEXT_PTR(netplay->self_ptr);
|
||||
netplay->self_frame_count++;
|
||||
@ -383,7 +402,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
/* Skip ahead if we predicted correctly.
|
||||
* Skip until our simulation failed. */
|
||||
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];
|
||||
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 */
|
||||
if (netplay->force_rewind ||
|
||||
(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;
|
||||
|
||||
@ -428,7 +447,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
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;
|
||||
|
||||
@ -483,15 +502,15 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
/* 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)
|
||||
if (netplay->unread_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
netplay->other_ptr = netplay->unread_ptr;
|
||||
netplay->other_frame_count = netplay->unread_frame_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
netplay->other_ptr = netplay->self_ptr;
|
||||
netplay->other_frame_count = netplay->self_frame_count;
|
||||
netplay->other_ptr = netplay->run_ptr;
|
||||
netplay->other_frame_count = netplay->run_frame_count;
|
||||
}
|
||||
netplay->is_replay = false;
|
||||
netplay->force_rewind = false;
|
||||
|
Loading…
Reference in New Issue
Block a user