From 55811f233fa0150f2d1de269b53f050b86e4c98f Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 10 Apr 2019 15:39:28 +0200 Subject: [PATCH] LibGUI+WindowServer: Coalesce paints and resizes on the client side. Only process paint and resize events on the GUI client side if those events have the latest up-to-date window size. This drastically reduces async overdraw during interactive resize. --- LibGUI/GEventLoop.cpp | 41 +++++++++++++++++++++ LibGUI/GWindow.cpp | 8 +++- Servers/WindowServer/WSClientConnection.cpp | 6 +-- Servers/WindowServer/WSWindow.h | 4 -- Servers/WindowServer/WSWindowManager.cpp | 4 -- 5 files changed, 50 insertions(+), 13 deletions(-) diff --git a/LibGUI/GEventLoop.cpp b/LibGUI/GEventLoop.cpp index b91f4f9f86..e60ac985d8 100644 --- a/LibGUI/GEventLoop.cpp +++ b/LibGUI/GEventLoop.cpp @@ -20,6 +20,7 @@ #include //#define GEVENTLOOP_DEBUG +//#define COALESCING_DEBUG static HashMap* g_actions; static GEventLoop* s_main_event_loop; @@ -352,7 +353,32 @@ void GEventLoop::wait_for_event() void GEventLoop::process_unprocessed_messages() { + int coalesced_paints = 0; + int coalesced_resizes = 0; auto unprocessed_events = move(m_unprocessed_messages); + + HashMap latest_size_for_window_id; + for (auto& event : unprocessed_events) { + if (event.type == WSAPI_ServerMessage::Type::WindowResized) { + latest_size_for_window_id.set(event.window_id, event.window.rect.size); + } + } + + int paint_count = 0; + HashMap latest_paint_size_for_window_id; + for (auto& event : unprocessed_events) { + if (event.type == WSAPI_ServerMessage::Type::Paint) { + ++paint_count; +#ifdef COALESCING_DEBUG + dbgprintf(" %s (window: %s)\n", Rect(event.paint.rect).to_string().characters(), Size(event.paint.window_size).to_string().characters()); +#endif + latest_paint_size_for_window_id.set(event.window_id, event.paint.window_size); + } + } +#ifdef COALESCING_DEBUG + dbgprintf("paint_count: %d\n", paint_count); +#endif + for (auto& event : unprocessed_events) { if (event.type == WSAPI_ServerMessage::Type::Greeting) { s_server_pid = event.greeting.server_pid; @@ -387,6 +413,10 @@ void GEventLoop::process_unprocessed_messages() } switch (event.type) { case WSAPI_ServerMessage::Type::Paint: + if (Size(event.paint.window_size) != latest_paint_size_for_window_id.get(event.window_id)) { + ++coalesced_paints; + break; + } handle_paint_event(event, *window); break; case WSAPI_ServerMessage::Type::MouseDown: @@ -410,6 +440,10 @@ void GEventLoop::process_unprocessed_messages() handle_window_entered_or_left_event(event, *window); break; case WSAPI_ServerMessage::Type::WindowResized: + if (Size(event.window.rect.size) != latest_size_for_window_id.get(event.window_id)) { + ++coalesced_resizes; + break; + } handle_resize_event(event, *window); break; case WSAPI_ServerMessage::Type::WM_WindowRemoved: @@ -421,6 +455,13 @@ void GEventLoop::process_unprocessed_messages() } } +#ifdef COALESCING_DEBUG + if (coalesced_paints) + dbgprintf("Coalesced %d paints\n", coalesced_paints); + if (coalesced_resizes) + dbgprintf("Coalesced %d resizes\n", coalesced_resizes); +#endif + if (!m_unprocessed_messages.is_empty()) process_unprocessed_messages(); } diff --git a/LibGUI/GWindow.cpp b/LibGUI/GWindow.cpp index d2f41176ef..1c47ec3c6c 100644 --- a/LibGUI/GWindow.cpp +++ b/LibGUI/GWindow.cpp @@ -211,11 +211,17 @@ void GWindow::event(GEvent& event) return; auto& paint_event = static_cast(event); auto rect = paint_event.rect(); + if (m_back_bitmap && m_back_bitmap->size() != paint_event.window_size()) { + // Eagerly discard the backing store if we learn from this paint event that it needs to be bigger. + // Otherwise we would have to wait for a resize event to tell us. This way we don't waste the + // effort on painting into an undersized bitmap that will be thrown away anyway. + m_back_bitmap = nullptr; + } bool created_new_backing_store = !m_back_bitmap; if (!m_back_bitmap) m_back_bitmap = create_backing_bitmap(paint_event.window_size()); if (rect.is_empty() || created_new_backing_store) - rect = m_main_widget->rect(); + rect = { { }, paint_event.window_size() }; m_main_widget->event(*make(rect)); diff --git a/Servers/WindowServer/WSClientConnection.cpp b/Servers/WindowServer/WSClientConnection.cpp index 5ac96e59d7..cfd3f4c99a 100644 --- a/Servers/WindowServer/WSClientConnection.cpp +++ b/Servers/WindowServer/WSClientConnection.cpp @@ -438,10 +438,8 @@ void WSClientConnection::handle_request(const WSAPIDidFinishPaintingNotification auto& window = *(*it).value; if (!window.has_painted_since_last_resize()) { - if (window.last_lazy_resize_rect().size() == request.rect().size()) { - window.set_has_painted_since_last_resize(true); - WSMessageLoop::the().post_message(window, make(window.last_lazy_resize_rect(), window.rect())); - } + window.set_has_painted_since_last_resize(true); + WSMessageLoop::the().post_message(window, make(window.rect(), window.rect())); } WSWindowManager::the().invalidate(window, request.rect()); } diff --git a/Servers/WindowServer/WSWindow.h b/Servers/WindowServer/WSWindow.h index fdcee8df6c..60faf978f0 100644 --- a/Servers/WindowServer/WSWindow.h +++ b/Servers/WindowServer/WSWindow.h @@ -106,9 +106,6 @@ public: bool has_alpha_channel() const { return m_has_alpha_channel; } void set_has_alpha_channel(bool value) { m_has_alpha_channel = value; } - void set_last_lazy_resize_rect(const Rect& rect) { m_last_lazy_resize_rect = rect; } - Rect last_lazy_resize_rect() const { return m_last_lazy_resize_rect; } - bool has_painted_since_last_resize() const { return m_has_painted_since_last_resize; } void set_has_painted_since_last_resize(bool b) { m_has_painted_since_last_resize = b; } @@ -150,7 +147,6 @@ private: RetainPtr m_last_backing_store; int m_window_id { -1 }; float m_opacity { 1 }; - Rect m_last_lazy_resize_rect; Size m_size_increment; Size m_base_size; Retained m_icon; diff --git a/Servers/WindowServer/WSWindowManager.cpp b/Servers/WindowServer/WSWindowManager.cpp index b8daee1c6a..bb3533d916 100644 --- a/Servers/WindowServer/WSWindowManager.cpp +++ b/Servers/WindowServer/WSWindowManager.cpp @@ -613,10 +613,6 @@ bool WSWindowManager::process_ongoing_window_resize(const WSMouseEvent& event, W m_resize_window->set_rect(new_rect); if (m_resize_window->has_painted_since_last_resize()) { m_resize_window->set_has_painted_since_last_resize(false); -#ifdef RESIZE_DEBUG - dbgprintf("[WM] I'm gonna wait for %s\n", new_rect.to_string().characters()); -#endif - m_resize_window->set_last_lazy_resize_rect(new_rect); WSMessageLoop::the().post_message(*m_resize_window, make(old_rect, new_rect)); } return true;