diff --git a/app/backend/nvhttp.cpp b/app/backend/nvhttp.cpp index 4dbc95c2..1e59e04a 100644 --- a/app/backend/nvhttp.cpp +++ b/app/backend/nvhttp.cpp @@ -29,9 +29,15 @@ NvHTTP::NvHTTP(QString address) : QVector NvHTTP::parseQuad(QString quad) { - QStringList parts = quad.split("."); QVector ret; + // Return an empty vector for old GFE versions + // that were missing GfeVersion. + if (quad.isEmpty()) { + return ret; + } + + QStringList parts = quad.split("."); for (int i = 0; i < 4; i++) { ret.append(parts.at(i).toInt()); diff --git a/app/streaming/input.cpp b/app/streaming/input.cpp index 40704be3..6b6d59f8 100644 --- a/app/streaming/input.cpp +++ b/app/streaming/input.cpp @@ -24,9 +24,10 @@ const int SdlInputHandler::k_ButtonMap[] = { UP_FLAG, DOWN_FLAG, LEFT_FLAG, RIGHT_FLAG }; -SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs) +SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer* computer) : m_LastMouseMotionTime(0), - m_MultiController(prefs.multiController) + m_MultiController(prefs.multiController), + m_NeedsInputDelay(false) { // Allow gamepad input when the app doesn't have focus SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); @@ -81,6 +82,19 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs) m_GamepadMask = 0; } + // Prior to GFE 3.14.1, sending too many mouse motion events can cause + // GFE to choke and input latency to increase significantly. We will + // artificially throttle them to avoid this situation. + QVector gfeVersion = NvHTTP::parseQuad(computer->gfeVersion); + if (gfeVersion.isEmpty() || // Very old versions don't have GfeVersion at all + gfeVersion[0] < 3 || + (gfeVersion[0] == 3 && gfeVersion[1] < 14) || + (gfeVersion[0] == 3 && gfeVersion[1] == 14 && gfeVersion[2] < 1)) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "This older version of GFE requires input delay hack"); + m_NeedsInputDelay = true; + } + SDL_zero(m_GamepadState); } @@ -430,9 +444,11 @@ void SdlInputHandler::handleMouseMotionEvent(SDL_MouseMotionEvent* event) short ydelta = (short)event->yrel; // If we're sending more than one motion event per millisecond, - // delay for 1 ms to allow batching of mouse move events. + // delay for 1 ms to allow batching of mouse move events. On older + // versions of GFE, we will unconditionally wait this 1 ms to + // work around an input processing issue that causes massive mouse latency. Uint32 currentTime = SDL_GetTicks(); - if (!SDL_TICKS_PASSED(currentTime, m_LastMouseMotionTime + 1)) { + if (m_NeedsInputDelay || !SDL_TICKS_PASSED(currentTime, m_LastMouseMotionTime + 1)) { SDL_Delay(1); currentTime = SDL_GetTicks(); } diff --git a/app/streaming/input.hpp b/app/streaming/input.hpp index 10383fd0..b0bd5987 100644 --- a/app/streaming/input.hpp +++ b/app/streaming/input.hpp @@ -1,6 +1,7 @@ #pragma once #include "settings/streamingpreferences.h" +#include "backend/computermanager.h" #include @@ -20,7 +21,7 @@ struct GamepadState { class SdlInputHandler { public: - explicit SdlInputHandler(StreamingPreferences& prefs); + explicit SdlInputHandler(StreamingPreferences& prefs, NvComputer* computer); ~SdlInputHandler(); @@ -48,6 +49,7 @@ private: Uint32 m_LastMouseMotionTime; bool m_MultiController; + bool m_NeedsInputDelay; int m_GamepadMask; GamepadState m_GamepadState[MAX_GAMEPADS]; diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index a6204650..f461b1a1 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -287,7 +287,8 @@ Session::Session(NvComputer* computer, NvApp& app) void Session::initialize() { - qDebug() << "Server GPU:" << m_Computer->gpuModel; + qInfo() << "Server GPU:" << m_Computer->gpuModel; + qInfo() << "Server GFE version:" << m_Computer->gfeVersion; LiInitializeVideoCallbacks(&m_VideoCallbacks); m_VideoCallbacks.setup = drSetup; @@ -731,7 +732,7 @@ void Session::exec(int displayOriginX, int displayOriginY) // Initialize the gamepad code with our preferences StreamingPreferences prefs; - SdlInputHandler inputHandler(prefs); + SdlInputHandler inputHandler(prefs, m_Computer); // The UI should have ensured the old game was already quit // if we decide to stream a different game.