From 8369d1a0e11b999d4d1598f62ca5f6dea49602fb Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 27 Sep 2026 17:39:10 -0500 Subject: [PATCH] Reduce gamepad CPU usage on Steam Link - Gamepad sensors are now disabled by default - Added a small deadzone to limit excess gamepad input reports --- app/deploy/steamlink/moonlight.sh | 5 ++++ app/streaming/input/gamepad.cpp | 45 ++++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/app/deploy/steamlink/moonlight.sh b/app/deploy/steamlink/moonlight.sh index 91fa4dfe..1eb1e59b 100644 --- a/app/deploy/steamlink/moonlight.sh +++ b/app/deploy/steamlink/moonlight.sh @@ -9,6 +9,11 @@ HOME=/usr/local/moonlight # https://github.com/moonlight-stream/moonlight-qt/issues/697 export SDL_ENABLE_STEAM_CONTROLLERS=1 +# Disable gamepad sensors by default to avoid excessive CPU +# usage that can cause audio glitches. Remove the following +# line to enable gamepad sensors. +export SENSOR_REPORT_RATE_LIMIT_HZ=0 + # Renice PE_Single_CPU which seems to host A/V stuff renice -10 -p $(pidof PE_Single_CPU) diff --git a/app/streaming/input/gamepad.cpp b/app/streaming/input/gamepad.cpp index 3ff08527..b649971b 100644 --- a/app/streaming/input/gamepad.cpp +++ b/app/streaming/input/gamepad.cpp @@ -3,6 +3,7 @@ #include #include "SDL_compat.h" #include "settings/mappingmanager.h" +#include "utils.h" #include @@ -188,6 +189,26 @@ Uint32 SdlInputHandler::mouseEmulationTimerCallback(Uint32 interval, void *param return interval; } +static void updateAnalogStickAxis(short& value, short newValue, bool& dirty) +{ +#ifdef STEAM_LINK + // Use a deadzone on Steam Link to reduce CPU usage from idle joysticks + newValue = abs(newValue) < 1500 ? 0 : newValue; +#endif + dirty |= (newValue != value); + value = newValue; +} + +static void updateTriggerAxis(unsigned char& value, unsigned char newValue, bool& dirty) +{ +#ifdef STEAM_LINK + // Use a deadzone on Steam Link to reduce CPU usage from idle joysticks + newValue = newValue < 10 ? 0 : newValue; +#endif + dirty |= (newValue != value); + value = newValue; +} + void SdlInputHandler::handleControllerAxisEvent(SDL_ControllerAxisEvent* event) { SDL_JoystickID gameControllerId = event->which; @@ -198,11 +219,12 @@ void SdlInputHandler::handleControllerAxisEvent(SDL_ControllerAxisEvent* event) // Batch all pending axis motion events for this gamepad to save CPU time SDL_Event nextEvent; + bool dirty = false; for (;;) { switch (event->axis) { case SDL_CONTROLLER_AXIS_LEFTX: - state->lsX = event->value; + updateAnalogStickAxis(state->lsX, event->value, dirty); break; case SDL_CONTROLLER_AXIS_LEFTY: // Signed values have one more negative value than @@ -210,19 +232,19 @@ void SdlInputHandler::handleControllerAxisEvent(SDL_ControllerAxisEvent* event) // could actually cause the value to overflow and // wrap around to be negative again. Avoid that by // capping the value at 32767. - state->lsY = -qMax(event->value, (short)-32767); + updateAnalogStickAxis(state->lsY, -qMax(event->value, (short)-32767), dirty); break; case SDL_CONTROLLER_AXIS_RIGHTX: - state->rsX = event->value; + updateAnalogStickAxis(state->rsX, event->value, dirty); break; case SDL_CONTROLLER_AXIS_RIGHTY: - state->rsY = -qMax(event->value, (short)-32767); + updateAnalogStickAxis(state->rsY, -qMax(event->value, (short)-32767), dirty); break; case SDL_CONTROLLER_AXIS_TRIGGERLEFT: - state->lt = (unsigned char)(event->value * 255UL / 32767); + updateTriggerAxis(state->lt, (unsigned char)(event->value * 255UL / 32767), dirty); break; case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: - state->rt = (unsigned char)(event->value * 255UL / 32767); + updateTriggerAxis(state->rt, (unsigned char)(event->value * 255UL / 32767), dirty); break; default: SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, @@ -247,7 +269,7 @@ void SdlInputHandler::handleControllerAxisEvent(SDL_ControllerAxisEvent* event) } // Only send the gamepad state to the host if it's not in mouse emulation mode - if (state->mouseEmulationTimer == 0) { + if (state->mouseEmulationTimer == 0 && dirty) { sendGamepadState(state); } } @@ -897,6 +919,15 @@ void SdlInputHandler::setMotionEventState(uint16_t controllerNumber, uint8_t mot return; } + uint16_t reportRateHzLimit; + if (Utils::getEnvironmentVariableOverride("SENSOR_REPORT_RATE_LIMIT_HZ", &reportRateHzLimit) && + reportRateHz > reportRateHzLimit) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "Sensor report rate limited to %u Hz by environment variable", + reportRateHzLimit); + reportRateHz = reportRateHzLimit; + } + #if SDL_VERSION_ATLEAST(2, 0, 14) if (m_GamepadState[controllerNumber].controller != nullptr) { uint8_t reportPeriodMs = reportRateHz ? (1000 / reportRateHz) : 0;