Quellcode durchsuchen

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.
Andreas Kling vor 6 Jahren
Ursprung
Commit
55811f233f

+ 41 - 0
LibGUI/GEventLoop.cpp

@@ -20,6 +20,7 @@
 #include <LibC/stdlib.h>
 
 //#define GEVENTLOOP_DEBUG
+//#define COALESCING_DEBUG
 
 static HashMap<GShortcut, GAction*>* 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<int, Size> 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<int, Size> 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();
 }

+ 7 - 1
LibGUI/GWindow.cpp

@@ -211,11 +211,17 @@ void GWindow::event(GEvent& event)
             return;
         auto& paint_event = static_cast<GPaintEvent&>(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<GPaintEvent>(rect));
 

+ 2 - 4
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<WSResizeEvent>(window.last_lazy_resize_rect(), window.rect()));
-        }
+        window.set_has_painted_since_last_resize(true);
+        WSMessageLoop::the().post_message(window, make<WSResizeEvent>(window.rect(), window.rect()));
     }
     WSWindowManager::the().invalidate(window, request.rect());
 }

+ 0 - 4
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<GraphicsBitmap> 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<GraphicsBitmap> m_icon;

+ 0 - 4
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<WSResizeEvent>(old_rect, new_rect));
     }
     return true;