From 83f778800f263fa56e9310837d196883f61e8a18 Mon Sep 17 00:00:00 2001 From: Nikhil Narayana Date: Mon, 30 May 2022 01:29:06 -0700 Subject: [PATCH] pull in project-slippi/Ishiiruka/commit/248c5f95cba06b6496d61cb2743ace915c54d5a0 --- Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp | 106 ++++++++++++++++++- Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h | 7 ++ Source/Core/Core/Slippi/SlippiNetplay.cpp | 10 +- Source/Core/VideoCommon/OnScreenDisplay.h | 1 + 4 files changed, 114 insertions(+), 10 deletions(-) diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp index 9d66888b9b..60c811b71c 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp @@ -32,6 +32,7 @@ #include "Core/Slippi/SlippiPremadeText.h" #include "Core/Slippi/SlippiReplayComm.h" #include "Core/State.h" +#include "VideoCommon/OnScreenDisplay.h" #define FRAME_INTERVAL 900 #define SLEEP_TIME_MS 8 @@ -1548,6 +1549,16 @@ void CEXISlippi::handleOnlineInputs(u8* payload) isConnectionStalled = false; stallFrameCount = 0; + // Reset skip variables + framesToSkip = 0; + isCurrentlySkipping = false; + + // Reset advance stuff + framesToAdvance = 0; + isCurrentlyAdvancing = false; + fallBehindCounter = 0; + fallFarBehindCounter = 0; + // Reset character selections as they are no longer needed localSelections.Reset(); if (slippi_netplay) @@ -1619,10 +1630,12 @@ bool CEXISlippi::shouldSkipOnlineFrame(s32 frame) if (isTimeSyncFrame == 0 && !isCurrentlySkipping) { auto offsetUs = slippi_netplay->CalcTimeOffsetUs(); - INFO_LOG(SLIPPI_ONLINE, "[Frame %d] Offset is: %d us", frame, offsetUs); + INFO_LOG_FMT(SLIPPI_ONLINE, "[Frame {}] Offset for skip is: {} us", frame, offsetUs); - // TODO: figure out a better solution here for doubles? - if (offsetUs > 10000) + // The decision to skip a frame only happens when we are already pretty far off ahead. The hope + // is that this won't really be used much because the frame advance of the slow client will pick + // up the difference most of the time. But at some point it's probably better to slow down... + if (offsetUs > 26000) { isCurrentlySkipping = true; @@ -1650,6 +1663,85 @@ bool CEXISlippi::shouldSkipOnlineFrame(s32 frame) return false; } +bool CEXISlippi::shouldAdvanceOnlineFrame(s32 frame) +{ + // Return true if we are over 60% of a frame behind our opponent. We limit how often this happens + // to get a reliable average to act on. We will allow advancing up to 5 frames (spread out) over + // the 30 frame period. This makes the game feel relatively smooth still + auto isTimeSyncFrame = + (frame % SLIPPI_ONLINE_LOCKSTEP_INTERVAL) == 0; // Only time sync every 30 frames + if (isTimeSyncFrame) + { + auto offsetUs = slippi_netplay->CalcTimeOffsetUs(); + + // Dynamically adjust emulation speed in order to fine-tune time sync to reduce one sided + // rollbacks even more + auto maxSpeedDeviation = 0.005f; + auto deviation = (offsetUs / 33366.0f) * maxSpeedDeviation; + if (deviation > maxSpeedDeviation) + deviation = maxSpeedDeviation; + else if (deviation < -maxSpeedDeviation) + deviation = -maxSpeedDeviation; + + // If we are behind (negative offset) we want to go above 100% run speed, so we need to subtract + // the deviation value + auto dynamicEmulationSpeed = 1.0f - deviation; + SConfig::GetInstance().m_EmulationSpeed = dynamicEmulationSpeed; + // SConfig::GetInstance().m_EmulationSpeed = 0.97f; // used for testing + + INFO_LOG(SLIPPI_ONLINE, "[Frame %d] Offset for advance is: %d us. New speed: %.2f%%", frame, + offsetUs, dynamicEmulationSpeed * 100.0f); + + // Count the number of times we're below a threshold we should easily be able to clear. This is + // checked twice per second. + fallBehindCounter += offsetUs < -10000 ? 1 : 0; + fallFarBehindCounter += offsetUs < -25000 ? 1 : 0; + + bool isSlow = (offsetUs < -10000 && fallBehindCounter > 50) || + (offsetUs < -25000 && fallFarBehindCounter > 15); + if (isSlow && lastSearch.mode != SlippiMatchmaking::OnlinePlayMode::TEAMS) + { + // We don't show this message for teams because it seems to false positive a lot there, maybe + // because the min offset is always selected? Idk I feel like doubles has some perf issues I + // don't understand atm. + OSD::AddTypedMessage( + OSD::MessageType::PerformanceWarning, + "Your computer is running slow and is impacting the performance of the match.", 10000, + OSD::Color::RED); + } + + if (offsetUs < -10000 && !isCurrentlyAdvancing) + { + isCurrentlyAdvancing = true; + + // On early frames, don't advance any frames. Let the stalling logic handle the initial sync + int maxAdvFrames = frame > 120 ? 5 : 0; + framesToAdvance = ((-offsetUs - 10000) / 16683) + 1; + framesToAdvance = framesToAdvance > maxAdvFrames ? maxAdvFrames : framesToAdvance; + + WARN_LOG(SLIPPI_ONLINE, + "Advancing on frame %d due to time sync. Offset: %d us. Frames: %d...", frame, + offsetUs, framesToAdvance); + } + } + + // Handle the skipped frames + if (framesToAdvance > 0) + { + // Only advance once every 5 frames in an attempt to make the speed up feel smoother + if (frame % 5 != 0) + { + return false; + } + + framesToAdvance = framesToAdvance - 1; + return true; + } + + isCurrentlyAdvancing = false; + return false; +} + void CEXISlippi::handleSendInputs(u8* payload) { if (isConnectionStalled) @@ -1678,6 +1770,8 @@ void CEXISlippi::prepareOpponentInputs(u8* payload) { m_read_queue.clear(); + int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3]; + u8 frameResult = 1; // Indicates to continue frame auto state = slippi_netplay->GetSlippiConnectStatus(); @@ -1686,14 +1780,16 @@ void CEXISlippi::prepareOpponentInputs(u8* payload) { frameResult = 3; // Indicates we have disconnected } + else if (shouldAdvanceOnlineFrame(frame)) + { + frameResult = 4; + } m_read_queue.push_back(frameResult); // Indicate a continue frame u8 remotePlayerCount = matchmaking->RemotePlayerCount(); m_read_queue.push_back(remotePlayerCount); // Indicate the number of remote players - int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3]; - std::unique_ptr results[SLIPPI_REMOTE_PLAYER_MAX]; int offset[SLIPPI_REMOTE_PLAYER_MAX]; INFO_LOG(SLIPPI_ONLINE, "Preparing pad data for frame %d", frame); diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h index a2ad08433d..d6e17c1d26 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h @@ -182,6 +182,7 @@ private: void prepareOnlineMatchState(); void setMatchSelections(u8* payload); bool shouldSkipOnlineFrame(s32 frame); + bool shouldAdvanceOnlineFrame(s32 frame); void handleLogInRequest(); void handleLogOutRequest(); void handleUpdateAppRequest(); @@ -254,6 +255,12 @@ private: int framesToSkip = 0; bool isCurrentlySkipping = false; + // Frame advancing variables + int framesToAdvance = 0; + bool isCurrentlyAdvancing = false; + int fallBehindCounter = 0; + int fallFarBehindCounter = 0; + protected: void TransferByte(u8& byte) override; diff --git a/Source/Core/Core/Slippi/SlippiNetplay.cpp b/Source/Core/Core/Slippi/SlippiNetplay.cpp index 5de524e298..6debbfdcc1 100644 --- a/Source/Core/Core/Slippi/SlippiNetplay.cpp +++ b/Source/Core/Core/Slippi/SlippiNetplay.cpp @@ -1200,7 +1200,7 @@ int32_t SlippiNetplayClient::GetSlippiLatestRemoteFrame() return lowestFrame; } -// return the largest time offset among all remote players +// return the smallest time offset among all remote players s32 SlippiNetplayClient::CalcTimeOffsetUs() { bool empty = true; @@ -1250,12 +1250,12 @@ s32 SlippiNetplayClient::CalcTimeOffsetUs() offsets.push_back(result); } - s32 maxOffset = offsets.front(); + s32 minOffset = offsets.front(); for (int i = 1; i < offsets.size(); i++) { - if (offsets[i] > maxOffset) - maxOffset = offsets[i]; + if (offsets[i] < minOffset) + minOffset = offsets[i]; } - return maxOffset; + return minOffset; } diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index fe2aaed406..1913ecbf31 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -15,6 +15,7 @@ enum class MessageType { NetPlayPing, NetPlayBuffer, + PerformanceWarning, // This entry must be kept last so that persistent typed messages are // displayed before other messages