WindowSerer+LibGUI: Send multiple rects in invalidation/flush messages.

This patch moves to sending up to 32 rects at a time when coordinating the
painting between WindowServer and its clients. Rects are also merged into
a minimal DisjointRectSet on the server side before painting.

Interactive resize looks a lot better after this change, since we can
usually do all the repainting needed in one go.
This commit is contained in:
Andreas Kling 2019-04-20 17:19:56 +02:00
parent ec365b82d5
commit 7efd61fcf5
Notes: sideshowbarker 2024-07-19 14:38:25 +09:00
10 changed files with 74 additions and 29 deletions

View file

@ -84,7 +84,8 @@ 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
post_event(window, make<GPaintEvent>(event.paint.rect, event.paint.window_size));
for (int i = 0; i < event.rect_count; ++i)
post_event(window, make<GPaintEvent>(event.rects[i], event.paint.window_size));
}
void GEventLoop::handle_resize_event(const WSAPI_ServerMessage& event, GWindow& window)

View file

@ -302,13 +302,15 @@ void GWindow::update(const Rect& a_rect)
if (m_pending_paint_event_rects.is_empty()) {
deferred_invoke([this] (auto&) {
for (auto& rect : m_pending_paint_event_rects) {
WSAPI_ClientMessage request;
request.type = WSAPI_ClientMessage::Type::InvalidateRect;
request.window_id = m_window_id;
request.window.rect = rect;
GEventLoop::current().post_message_to_server(request);
}
// FIXME: Break it into multiple batches if needed.
ASSERT(m_pending_paint_event_rects.size() <= 32);
WSAPI_ClientMessage request;
request.type = WSAPI_ClientMessage::Type::InvalidateRect;
request.window_id = m_window_id;
for (int i = 0; i < m_pending_paint_event_rects.size(); ++i)
request.rects[i] = m_pending_paint_event_rects[i];
request.rect_count = m_pending_paint_event_rects.size();
GEventLoop::current().post_message_to_server(request);
m_pending_paint_event_rects.clear_with_capacity();
});
}

View file

@ -109,8 +109,16 @@ struct WSAPI_ServerMessage {
};
Type type { Invalid };
int window_id { -1 };
int text_length { 0 };
char text[256];
union {
int text_length { 0 };
int rect_count;
};
union {
char text[512];
WSAPI_Rect rects[32];
};
int value { 0 };
union {
@ -134,7 +142,6 @@ struct WSAPI_ServerMessage {
WSAPI_Rect old_rect;
} window;
struct {
WSAPI_Rect rect;
WSAPI_Size window_size;
} paint;
struct {
@ -207,8 +214,14 @@ struct WSAPI_ClientMessage {
};
Type type { Invalid };
int window_id { -1 };
int text_length { 0 };
char text[256];
union {
int text_length { 0 };
int rect_count;
};
union {
char text[512];
WSAPI_Rect rects[32];
};
int value { 0 };
union {

View file

@ -383,7 +383,7 @@ void WSClientConnection::handle_request(const WSAPISetWindowRectRequest& request
}
auto& window = *(*it).value;
window.set_rect(request.rect());
post_paint_request(window, request.rect());
window.request_update(request.rect());
}
void WSClientConnection::handle_request(const WSAPIGetWindowRectRequest& request)
@ -477,14 +477,20 @@ void WSClientConnection::handle_request(const WSAPIDestroyWindowRequest& request
post_message(response);
}
void WSClientConnection::post_paint_request(const WSWindow& window, const Rect& rect)
void WSClientConnection::post_paint_message(WSWindow& window)
{
WSAPI_ServerMessage response;
response.type = WSAPI_ServerMessage::Type::Paint;
response.window_id = window.window_id();
response.paint.rect = rect;
response.paint.window_size = window.size();
post_message(response);
WSAPI_ServerMessage message;
message.type = WSAPI_ServerMessage::Type::Paint;
message.window_id = window.window_id();
auto rect_set = window.take_pending_paint_rects();
auto& rects = rect_set.rects();
// FIXME: Break it into multiple batches if needed.
ASSERT(rects.size() <= 32);
message.rect_count = rects.size();
for (int i = 0; i < rects.size(); ++i)
message.rects[i] = rects[i];
message.paint.window_size = window.size();
post_message(message);
}
void WSClientConnection::handle_request(const WSAPIInvalidateRectRequest& request)
@ -496,7 +502,8 @@ void WSClientConnection::handle_request(const WSAPIInvalidateRectRequest& reques
return;
}
auto& window = *(*it).value;
post_paint_request(window, request.rect());
for (int i = 0; i < request.rects().size(); ++i)
window.request_update(request.rects()[i]);
}
void WSClientConnection::handle_request(const WSAPIDidFinishPaintingNotification& request)

View file

@ -37,7 +37,7 @@ public:
template<typename Callback> void for_each_window(Callback);
void notify_about_new_screen_rect(const Rect&);
void post_paint_request(const WSWindow&, const Rect&);
void post_paint_message(WSWindow&);
private:
virtual void event(CEvent&) override;

View file

@ -564,19 +564,19 @@ private:
class WSAPIInvalidateRectRequest final : public WSAPIClientRequest {
public:
explicit WSAPIInvalidateRectRequest(int client_id, int window_id, const Rect& rect)
explicit WSAPIInvalidateRectRequest(int client_id, int window_id, const Vector<Rect, 32>& rects)
: WSAPIClientRequest(WSEvent::APIInvalidateRectRequest, 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;
};
class WSAPIGetWindowBackingStoreRequest final : public WSAPIClientRequest {

View file

@ -191,9 +191,14 @@ void WSEventLoop::on_receive_from_client(int client_id, const WSAPI_ClientMessag
case WSAPI_ClientMessage::Type::GetClipboardContents:
post_event(client, make<WSAPIGetClipboardContentsRequest>(client_id));
break;
case WSAPI_ClientMessage::Type::InvalidateRect:
post_event(client, make<WSAPIInvalidateRectRequest>(client_id, message.window_id, message.window.rect));
case WSAPI_ClientMessage::Type::InvalidateRect: {
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<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));
break;

View file

@ -271,3 +271,13 @@ void WSWindow::set_default_icon()
m_icon = default_window_icon();
m_icon_path = default_window_icon_path();
}
void WSWindow::request_update(const Rect& rect)
{
if (m_pending_paint_rects.is_empty()) {
deferred_invoke([this] (auto&) {
client()->post_paint_message(*this);
});
}
m_pending_paint_rects.add(rect);
}

View file

@ -7,6 +7,7 @@
#include <LibCore/CObject.h>
#include <WindowServer/WSWindowType.h>
#include <WindowServer/WSWindowFrame.h>
#include <SharedGraphics/DisjointRectSet.h>
class WSClientConnection;
class WSCursor;
@ -127,6 +128,9 @@ public:
const WSCursor* override_cursor() const { return m_override_cursor.ptr(); }
void set_override_cursor(RetainPtr<WSCursor>&& cursor) { m_override_cursor = move(cursor); }
void request_update(const Rect&);
DisjointRectSet take_pending_paint_rects() { return move(m_pending_paint_rects); }
// For InlineLinkedList.
// FIXME: Maybe make a ListHashSet and then WSWindowManager can just use that.
WSWindow* m_next { nullptr };
@ -160,4 +164,5 @@ private:
WSWindowFrame m_frame;
Color m_background_color { Color::LightGray };
unsigned m_wm_event_mask { 0 };
DisjointRectSet m_pending_paint_rects;
};

View file

@ -11,6 +11,8 @@ public:
void add(const Rect&);
bool is_empty() const { return m_rects.is_empty(); }
void clear() { m_rects.clear(); }
void clear_with_capacity() { m_rects.clear_with_capacity(); }
const Vector<Rect, 32>& rects() const { return m_rects; }