From e5ac45e82291a8158ad4119c2026f19a8f920392 Mon Sep 17 00:00:00 2001 From: Riteo Date: Thu, 26 Sep 2024 10:07:53 +0200 Subject: [PATCH] Wayland: Unsuspend only for the same reason as suspension Before, we would check both methods together, leading to loops. Now we track the actual reason we suspended and only unsuspend when that same reason triggers. For example, if we suspend because of the suspended flag we'll unsuspend only because it got unset. Conversely, if we suspend because of a timeout we'll unsuspend only if we get a new frame event. We do this because, while some compositors properly report a "suspended" state (hinting us to stop repainting), most don't and we need a "safety net" anyways as we do not want to constantly stay at 1fps (the max time we'll wait before giving up) either. --- .../wayland/display_server_wayland.cpp | 45 +++++++++++++------ .../linuxbsd/wayland/display_server_wayland.h | 8 +++- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index 697cd8388c7..d5dc8c2a783 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -906,11 +906,11 @@ bool DisplayServerWayland::window_is_focused(WindowID p_window_id) const { } bool DisplayServerWayland::window_can_draw(DisplayServer::WindowID p_window_id) const { - return !suspended; + return suspend_state == SuspendState::NONE; } bool DisplayServerWayland::can_any_window_draw() const { - return !suspended; + return suspend_state == SuspendState::NONE; } void DisplayServerWayland::window_set_ime_active(const bool p_active, DisplayServer::WindowID p_window_id) { @@ -1132,16 +1132,21 @@ void DisplayServerWayland::try_suspend() { if (emulate_vsync) { bool frame = wayland_thread.wait_frame_suspend_ms(1000); if (!frame) { - suspended = true; - } - } else { - if (wayland_thread.is_suspended()) { - suspended = true; + suspend_state = SuspendState::TIMEOUT; } } - if (suspended) { - DEBUG_LOG_WAYLAND("Window suspended."); + // If we suspended by capability, we'll know with this check. We must do this + // after `wait_frame_suspend_ms` as it progressively dispatches the event queue + // during the "timeout". + if (wayland_thread.is_suspended()) { + suspend_state = SuspendState::CAPABILITY; + } + + if (suspend_state == SuspendState::TIMEOUT) { + DEBUG_LOG_WAYLAND("Suspending. Reason: timeout."); + } else if (suspend_state == SuspendState::CAPABILITY) { + DEBUG_LOG_WAYLAND("Suspending. Reason: capability."); } } @@ -1226,7 +1231,7 @@ void DisplayServerWayland::process_events() { wayland_thread.keyboard_echo_keys(); - if (!suspended) { + if (suspend_state == SuspendState::NONE) { // Due to the way legacy suspension works, we have to treat low processor // usage mode very differently than the regular one. if (OS::get_singleton()->is_in_low_processor_usage_mode()) { @@ -1250,9 +1255,23 @@ void DisplayServerWayland::process_events() { } else { try_suspend(); } - } else if (!wayland_thread.is_suspended() || wayland_thread.get_reset_frame()) { - // At last, a sign of life! We're no longer suspended. - suspended = false; + } else { + if (suspend_state == SuspendState::CAPABILITY) { + // If we suspended by capability we can assume that it will be reset when + // the compositor wants us to repaint. + if (!wayland_thread.is_suspended()) { + suspend_state = SuspendState::NONE; + DEBUG_LOG_WAYLAND("Unsuspending from capability."); + } + } else if (suspend_state == SuspendState::TIMEOUT) { + // Certain compositors might not report the "suspended" wm_capability flag. + // Because of this we'll wake up at the next frame event, indicating the + // desire for the compositor to let us repaint. + if (wayland_thread.get_reset_frame()) { + suspend_state = SuspendState::NONE; + DEBUG_LOG_WAYLAND("Unsuspending from timeout."); + } + } } #ifdef DBUS_ENABLED diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h index beab5a83128..8c3bac9c7e3 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.h +++ b/platform/linuxbsd/wayland/display_server_wayland.h @@ -105,6 +105,12 @@ class DisplayServerWayland : public DisplayServer { Point2i hotspot; }; + enum class SuspendState { + NONE, // Unsuspended. + TIMEOUT, // Legacy fallback. + CAPABILITY, // New "suspended" wm_capability flag. + }; + CursorShape cursor_shape = CURSOR_ARROW; DisplayServer::MouseMode mouse_mode = DisplayServer::MOUSE_MODE_VISIBLE; @@ -118,7 +124,7 @@ class DisplayServerWayland : public DisplayServer { String ime_text; Vector2i ime_selection; - bool suspended = false; + SuspendState suspend_state = SuspendState::NONE; bool emulate_vsync = false; String rendering_driver;