diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 83604b005..594e8662e 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -101,13 +101,14 @@ static constexpr u32 SETTINGS_SAVE_DELAY = 1000; static constexpr u32 NUM_ASYNC_WORKER_THREADS = 2; /// Interval at which the controllers are polled when the system is not active. -static constexpr u32 BACKGROUND_CONTROLLER_POLLING_INTERVAL = 100; +static constexpr int BACKGROUND_CONTROLLER_POLLING_INTERVAL_WITH_DEVICES = 100; +static constexpr int BACKGROUND_CONTROLLER_POLLING_INTERVAL_WITHOUT_DEVICES = 1000; /// Poll at half the vsync rate for FSUI to reduce the chance of getting a press+release in the same frame. -static constexpr u32 FULLSCREEN_UI_CONTROLLER_POLLING_INTERVAL = 8; +static constexpr int FULLSCREEN_UI_CONTROLLER_POLLING_INTERVAL = 8; /// Poll at 1ms when running GDB server. We can get rid of this once we move networking to its own thread. -static constexpr u32 GDB_SERVER_POLLING_INTERVAL = 1; +static constexpr int GDB_SERVER_POLLING_INTERVAL = 1; ////////////////////////////////////////////////////////////////////////// // Local function declarations @@ -1870,15 +1871,14 @@ void EmuThread::destroyBackgroundControllerPollTimer() void EmuThread::startBackgroundControllerPollTimer() { if (m_background_controller_polling_timer->isActive()) + { + updateBackgroundControllerPollInterval(); return; + } - u32 poll_interval = BACKGROUND_CONTROLLER_POLLING_INTERVAL; - if (m_gpu_thread_run_idle) - poll_interval = FULLSCREEN_UI_CONTROLLER_POLLING_INTERVAL; - if (GDBServer::HasAnyClients()) - poll_interval = GDB_SERVER_POLLING_INTERVAL; - - m_background_controller_polling_timer->start(poll_interval); + const int interval = getBackgroundControllerPollInterval(); + DEV_LOG("Starting background controller polling timer with interval {} ms", interval); + m_background_controller_polling_timer->start(interval); } void EmuThread::stopBackgroundControllerPollTimer() @@ -1886,9 +1886,42 @@ void EmuThread::stopBackgroundControllerPollTimer() if (!m_background_controller_polling_timer->isActive()) return; + DEV_LOG("Stopping background controller polling timer"); m_background_controller_polling_timer->stop(); } +void EmuThread::updateBackgroundControllerPollInterval() +{ + if (!isCurrentThread()) + { + QMetaObject::invokeMethod(this, &EmuThread::updateBackgroundControllerPollInterval, Qt::QueuedConnection); + return; + } + + if (!m_background_controller_polling_timer || !m_background_controller_polling_timer->isActive()) + return; + + const int current_interval = m_background_controller_polling_timer->interval(); + const int new_interval = getBackgroundControllerPollInterval(); + if (current_interval != new_interval) + { + WARNING_LOG("Changed background polling interval from {} ms to {} ms", current_interval, new_interval); + m_background_controller_polling_timer->setInterval(new_interval); + } +} + +int EmuThread::getBackgroundControllerPollInterval() const +{ + if (GDBServer::HasAnyClients()) + return GDB_SERVER_POLLING_INTERVAL; + else if (m_gpu_thread_run_idle) + return FULLSCREEN_UI_CONTROLLER_POLLING_INTERVAL; + else if (InputManager::GetPollableDeviceCount() > 0) + return BACKGROUND_CONTROLLER_POLLING_INTERVAL_WITH_DEVICES; + else + return BACKGROUND_CONTROLLER_POLLING_INTERVAL_WITHOUT_DEVICES; +} + void EmuThread::setGPUThreadRunIdle(bool active) { if (!isCurrentThread()) @@ -1907,8 +1940,7 @@ void EmuThread::setGPUThreadRunIdle(bool active) if (!m_background_controller_polling_timer->isActive()) return; - g_emu_thread->stopBackgroundControllerPollTimer(); - g_emu_thread->startBackgroundControllerPollTimer(); + g_emu_thread->updateBackgroundControllerPollInterval(); } void EmuThread::updateFullscreenUITheme() @@ -2695,6 +2727,7 @@ void Host::OnInputDeviceConnected(InputBindingKey key, std::string_view identifi QMetaObject::invokeMethod(g_emu_thread->getInputDeviceListModel(), &InputDeviceListModel::onDeviceConnected, Qt::QueuedConnection, key, QtUtils::StringViewToQString(identifier), QtUtils::StringViewToQString(device_name), qeffect_list); + g_emu_thread->updateBackgroundControllerPollInterval(); if (System::IsValid() || GPUThread::IsFullscreenUIRequested()) { @@ -2707,6 +2740,7 @@ void Host::OnInputDeviceDisconnected(InputBindingKey key, std::string_view ident { QMetaObject::invokeMethod(g_emu_thread->getInputDeviceListModel(), &InputDeviceListModel::onDeviceDisconnected, Qt::QueuedConnection, key, QtUtils::StringViewToQString(identifier)); + g_emu_thread->updateBackgroundControllerPollInterval(); if (g_settings.pause_on_controller_disconnection && System::GetState() == System::State::Running && InputManager::HasAnyBindingsForSource(key)) diff --git a/src/duckstation-qt/qthost.h b/src/duckstation-qt/qthost.h index 08955aef0..b39fea88b 100644 --- a/src/duckstation-qt/qthost.h +++ b/src/duckstation-qt/qthost.h @@ -83,6 +83,7 @@ public: void startBackgroundControllerPollTimer(); void stopBackgroundControllerPollTimer(); + void updateBackgroundControllerPollInterval(); void wakeThread(); void bootOrLoadState(std::string path); @@ -188,6 +189,7 @@ protected: void run() override; private: + int getBackgroundControllerPollInterval() const; void stopInThread(); void onDisplayWindowMouseButtonEvent(int button, bool pressed); void onDisplayWindowMouseWheelEvent(float dx, float dy); diff --git a/src/util/dinput_source.cpp b/src/util/dinput_source.cpp index 0362dd00b..eb8444868 100644 --- a/src/util/dinput_source.cpp +++ b/src/util/dinput_source.cpp @@ -322,6 +322,11 @@ InputManager::DeviceEffectList DInputSource::EnumerateEffects(std::optional(m_controllers.size()); +} + bool DInputSource::GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) { return {}; diff --git a/src/util/dinput_source.h b/src/util/dinput_source.h index cc95a3958..df596d1dc 100644 --- a/src/util/dinput_source.h +++ b/src/util/dinput_source.h @@ -43,6 +43,7 @@ public: InputManager::DeviceList EnumerateDevices() override; InputManager::DeviceEffectList EnumerateEffects(std::optional type, std::optional for_device) override; + u32 GetPollableDeviceCount() const override; bool GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) override; void UpdateMotorState(InputBindingKey key, float intensity) override; void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, diff --git a/src/util/input_manager.cpp b/src/util/input_manager.cpp index 6345edc04..7d49bd9de 100644 --- a/src/util/input_manager.cpp +++ b/src/util/input_manager.cpp @@ -2304,6 +2304,21 @@ InputManager::DeviceEffectList InputManager::EnumerateDeviceEffects(std::optiona return ret; } +u32 InputManager::GetPollableDeviceCount() +{ + std::unique_lock lock(s_state.mutex); + + u32 count = 0; + + for (u32 i = FIRST_EXTERNAL_INPUT_SOURCE; i < LAST_EXTERNAL_INPUT_SOURCE; i++) + { + if (s_state.input_sources[i]) + count += s_state.input_sources[i]->GetPollableDeviceCount(); + } + + return count; +} + static void GetKeyboardGenericBindingMapping(std::vector>* mapping) { mapping->emplace_back(GenericInputBinding::DPadUp, "Keyboard/UpArrow"); diff --git a/src/util/input_manager.h b/src/util/input_manager.h index b78bf136b..58e7400c6 100644 --- a/src/util/input_manager.h +++ b/src/util/input_manager.h @@ -281,6 +281,9 @@ using DeviceEffectList = std::vector type = std::nullopt, std::optional for_device = std::nullopt); +/// Returns the number of pollable devices across all input sources. +u32 GetPollableDeviceCount(); + /// Retrieves bindings that match the generic bindings for the specified device. GenericInputBindingMapping GetGenericBindingMapping(std::string_view device); diff --git a/src/util/input_source.h b/src/util/input_source.h index 78e034fed..af0e4c1f1 100644 --- a/src/util/input_source.h +++ b/src/util/input_source.h @@ -62,6 +62,9 @@ public: virtual InputManager::DeviceEffectList EnumerateEffects(std::optional type, std::optional for_device) = 0; + /// Returns the number of pollable devices managed by this source. + virtual u32 GetPollableDeviceCount() const = 0; + /// Retrieves bindings that match the generic bindings for the specified device. /// Returns false if it's not one of our devices. virtual bool GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) = 0; diff --git a/src/util/sdl_input_source.cpp b/src/util/sdl_input_source.cpp index 03c7c10e3..14cb16760 100644 --- a/src/util/sdl_input_source.cpp +++ b/src/util/sdl_input_source.cpp @@ -1118,9 +1118,6 @@ bool SDLInputSource::CloseDevice(SDL_JoystickID joystick_index) if (it == m_controllers.end()) return false; - InputManager::OnInputDeviceDisconnected(MakeGenericControllerDeviceKey(InputSourceType::SDL, it->player_id), - fmt::format("SDL-{}", it->player_id)); - if (it->haptic) SDL_CloseHaptic(it->haptic); @@ -1129,7 +1126,12 @@ bool SDLInputSource::CloseDevice(SDL_JoystickID joystick_index) else SDL_CloseJoystick(it->joystick); + const int player_id = it->player_id; m_controllers.erase(it); + + InputManager::OnInputDeviceDisconnected(MakeGenericControllerDeviceKey(InputSourceType::SDL, player_id), + fmt::format("SDL-{}", player_id)); + return true; } @@ -1372,6 +1374,11 @@ InputManager::DeviceEffectList SDLInputSource::EnumerateEffects(std::optional(m_controllers.size()); +} + bool SDLInputSource::GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) { if (!device.starts_with("SDL-")) diff --git a/src/util/sdl_input_source.h b/src/util/sdl_input_source.h index 47f6809cb..89749a734 100644 --- a/src/util/sdl_input_source.h +++ b/src/util/sdl_input_source.h @@ -34,6 +34,7 @@ public: InputManager::DeviceList EnumerateDevices() override; InputManager::DeviceEffectList EnumerateEffects(std::optional type, std::optional for_device) override; + u32 GetPollableDeviceCount() const override; bool GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) override; void UpdateMotorState(InputBindingKey key, float intensity) override; void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, diff --git a/src/util/win32_raw_input_source.cpp b/src/util/win32_raw_input_source.cpp index e90236c4e..f791a7b1c 100644 --- a/src/util/win32_raw_input_source.cpp +++ b/src/util/win32_raw_input_source.cpp @@ -140,6 +140,11 @@ InputManager::DeviceEffectList Win32RawInputSource::EnumerateEffects(std::option return {}; } +u32 Win32RawInputSource::GetPollableDeviceCount() const +{ + return static_cast(m_mice.size()); +} + bool Win32RawInputSource::GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) { return {}; diff --git a/src/util/win32_raw_input_source.h b/src/util/win32_raw_input_source.h index 82aac02eb..9cbb0a311 100644 --- a/src/util/win32_raw_input_source.h +++ b/src/util/win32_raw_input_source.h @@ -30,6 +30,7 @@ public: InputManager::DeviceList EnumerateDevices() override; InputManager::DeviceEffectList EnumerateEffects(std::optional type, std::optional for_device) override; + u32 GetPollableDeviceCount() const override; bool GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) override; void UpdateMotorState(InputBindingKey key, float intensity) override; void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, diff --git a/src/util/xinput_source.cpp b/src/util/xinput_source.cpp index f52e5c63b..fb8119560 100644 --- a/src/util/xinput_source.cpp +++ b/src/util/xinput_source.cpp @@ -399,6 +399,11 @@ InputManager::DeviceEffectList XInputSource::EnumerateEffects(std::optional(std::ranges::count_if(m_controllers, [](const ControllerData& cd) { return cd.connected; })); +} + bool XInputSource::GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) { if (!device.starts_with("XInput-")) diff --git a/src/util/xinput_source.h b/src/util/xinput_source.h index 45ea40170..45ea5a759 100644 --- a/src/util/xinput_source.h +++ b/src/util/xinput_source.h @@ -45,6 +45,7 @@ public: InputManager::DeviceList EnumerateDevices() override; InputManager::DeviceEffectList EnumerateEffects(std::optional type, std::optional for_device) override; + u32 GetPollableDeviceCount() const override; bool GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping) override; void UpdateMotorState(InputBindingKey key, float intensity) override; void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,