WindowServer+LibGUI: Coalesce multiple client paints into GMultiPaintEvents.

This allows GWindow to paint up to 32 separate rects before telling the
WindowServer to flip the buffers. Quite a bit smoother. :^)
This commit is contained in:
Andreas Kling 2019-04-20 17:37:28 +02:00
parent 7efd61fcf5
commit 7234900f61
6 changed files with 54 additions and 18 deletions

View file

@ -14,6 +14,7 @@ public:
Show = 1000,
Hide,
Paint,
MultiPaint,
Resize,
MouseMove,
MouseDown,
@ -127,6 +128,23 @@ private:
String m_icon_path;
};
class GMultiPaintEvent final : public GEvent {
public:
explicit GMultiPaintEvent(const Vector<Rect, 32>& rects, const Size& window_size)
: GEvent(GEvent::MultiPaint)
, m_rects(rects)
, m_window_size(window_size)
{
}
const Vector<Rect, 32>& rects() const { return m_rects; }
Size window_size() const { return m_window_size; }
private:
Vector<Rect, 32> m_rects;
Size m_window_size;
};
class GPaintEvent final : public GEvent {
public:
explicit GPaintEvent(const Rect& rect, const Size& window_size = Size())

View file

@ -84,8 +84,11 @@ void GEventLoop::handle_paint_event(const WSAPI_ServerMessage& event, GWindow& w
#ifdef GEVENTLOOP_DEBUG
dbgprintf("WID=%x Paint [%d,%d %dx%d]\n", event.window_id, event.paint.rect.location.x, event.paint.rect.location.y, event.paint.rect.size.width, event.paint.rect.size.height);
#endif
Vector<Rect, 32> rects;
ASSERT(event.rect_count <= 32);
for (int i = 0; i < event.rect_count; ++i)
post_event(window, make<GPaintEvent>(event.rects[i], event.paint.window_size));
rects.append(event.rects[i]);
post_event(window, make<GMultiPaintEvent>(rects, event.paint.window_size));
}
void GEventLoop::handle_resize_event(const WSAPI_ServerMessage& event, GWindow& window)

View file

@ -203,13 +203,14 @@ void GWindow::event(CEvent& event)
return;
}
if (event.type() == GEvent::Paint) {
if (event.type() == GEvent::MultiPaint) {
if (!m_window_id)
return;
if (!m_main_widget)
return;
auto& paint_event = static_cast<GPaintEvent&>(event);
auto rect = paint_event.rect();
auto& paint_event = static_cast<GMultiPaintEvent&>(event);
auto rects = paint_event.rects();
ASSERT(!rects.is_empty());
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
@ -219,21 +220,29 @@ void GWindow::event(CEvent& event)
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 = { { }, paint_event.window_size() };
m_main_widget->event(*make<GPaintEvent>(rect));
auto rect = rects.first();
if (rect.is_empty() || created_new_backing_store) {
rects.clear();
rects.append({ { }, paint_event.window_size() });
}
if (m_double_buffering_enabled)
flip(rect);
else if (created_new_backing_store)
for (auto& rect : rects) {
m_main_widget->event(*make<GPaintEvent>(rect));
if (m_double_buffering_enabled)
flip(rect);
}
if (!m_double_buffering_enabled && created_new_backing_store)
set_current_backing_bitmap(*m_back_bitmap, true);
if (m_window_id) {
WSAPI_ClientMessage message;
message.type = WSAPI_ClientMessage::Type::DidFinishPainting;
message.window_id = m_window_id;
message.window.rect = rect;
message.rect_count = paint_event.rects().size();
for (int i = 0; i < paint_event.rects().size(); ++i)
message.rects[i] = paint_event.rects()[i];
GEventLoop::current().post_message_to_server(message);
}
return;

View file

@ -515,7 +515,8 @@ void WSClientConnection::handle_request(const WSAPIDidFinishPaintingNotification
return;
}
auto& window = *(*it).value;
WSWindowManager::the().invalidate(window, request.rect());
for (auto& rect : request.rects())
WSWindowManager::the().invalidate(window, rect);
}
void WSClientConnection::handle_request(const WSAPIGetWindowBackingStoreRequest& request)

View file

@ -595,19 +595,19 @@ private:
class WSAPIDidFinishPaintingNotification final : public WSAPIClientRequest {
public:
explicit WSAPIDidFinishPaintingNotification(int client_id, int window_id, const Rect& rect)
explicit WSAPIDidFinishPaintingNotification(int client_id, int window_id, const Vector<Rect, 32>& rects)
: WSAPIClientRequest(WSEvent::APIDidFinishPaintingNotification, client_id)
, m_window_id(window_id)
, m_rect(rect)
, m_rects(rects)
{
}
int window_id() const { return m_window_id; }
Rect rect() const { return m_rect; }
const Vector<Rect, 32>& rects() const { return m_rects; }
private:
int m_window_id { 0 };
Rect m_rect;
Vector<Rect, 32> m_rects;
};
enum class MouseButton : byte {

View file

@ -199,9 +199,14 @@ void WSEventLoop::on_receive_from_client(int client_id, const WSAPI_ClientMessag
post_event(client, make<WSAPIInvalidateRectRequest>(client_id, message.window_id, rects));
break;
}
case WSAPI_ClientMessage::Type::DidFinishPainting:
post_event(client, make<WSAPIDidFinishPaintingNotification>(client_id, message.window_id, message.window.rect));
case WSAPI_ClientMessage::Type::DidFinishPainting: {
Vector<Rect, 32> rects;
ASSERT(message.rect_count <= 32);
for (int i = 0; i < message.rect_count; ++i)
rects.append(message.rects[i]);
post_event(client, make<WSAPIDidFinishPaintingNotification>(client_id, message.window_id, rects));
break;
}
case WSAPI_ClientMessage::Type::GetWindowBackingStore:
post_event(client, make<WSAPIGetWindowBackingStoreRequest>(client_id, message.window_id));
break;