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.
This commit is contained in:
Andreas Kling 2019-04-10 15:39:28 +02:00
parent 0ac55f2c38
commit 55811f233f
Notes: sideshowbarker 2024-07-19 14:46:28 +09:00
5 changed files with 50 additions and 13 deletions

View File

@ -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();
}

View File

@ -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));

View File

@ -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());
}

View File

@ -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;

View File

@ -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;