From 98784ad3cb9525729bec266225b4a1eb0178b9a6 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 19 Feb 2019 14:49:23 +0100 Subject: [PATCH] WindowServer: Avoid overdraw by shattering dirty rects into unique shards. The algorithm I came up with is O(n^2) but given the small numbers of rects we're typically working with, it doesn't really matter. May need to revisit this in the future if we find ourselves with a huge number of rects. --- SharedGraphics/DisjointRectSet.cpp | 46 ++++++++++++++++++++++++++++++ SharedGraphics/DisjointRectSet.h | 23 +++++++++++++++ SharedGraphics/Rect.cpp | 44 ++++++++++++++++++++++++++++ SharedGraphics/Rect.h | 2 ++ WindowServer/Makefile | 1 + WindowServer/WSWindowManager.cpp | 29 ++++++------------- WindowServer/WSWindowManager.h | 3 +- 7 files changed, 127 insertions(+), 21 deletions(-) create mode 100644 SharedGraphics/DisjointRectSet.cpp create mode 100644 SharedGraphics/DisjointRectSet.h diff --git a/SharedGraphics/DisjointRectSet.cpp b/SharedGraphics/DisjointRectSet.cpp new file mode 100644 index 0000000000..76ba128e0f --- /dev/null +++ b/SharedGraphics/DisjointRectSet.cpp @@ -0,0 +1,46 @@ +#include + +void DisjointRectSet::add(const Rect& new_rect) +{ + for (auto& rect : m_rects) { + if (rect.contains(new_rect)) + return; + } + + m_rects.append(new_rect); + if (m_rects.size() > 1) + shatter(); +} + +void DisjointRectSet::shatter() +{ + Vector output; + bool pass_had_intersections = false; + do { + pass_had_intersections = false; + output.clear_with_capacity(); + for (size_t i = 0; i < m_rects.size(); ++i) { + auto& r1 = m_rects[i]; + bool r1_had_intersections = false; + for (size_t j = 0; j < m_rects.size(); ++j) { + if (i == j) + continue; + auto& r2 = m_rects[j]; + if (!r1.intersects(r2)) + continue; + pass_had_intersections = true; + auto pieces = r1.shatter(r2); + for (auto& piece : pieces) + output.append(piece); + m_rects.remove(i); + for (; i < m_rects.size(); ++i) + output.append(m_rects[i]); + goto next_pass; + } + if (!r1_had_intersections) + output.append(r1); + } +next_pass: + swap(output, m_rects); + } while(pass_had_intersections); +} diff --git a/SharedGraphics/DisjointRectSet.h b/SharedGraphics/DisjointRectSet.h new file mode 100644 index 0000000000..5b3f26d20b --- /dev/null +++ b/SharedGraphics/DisjointRectSet.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +class DisjointRectSet { +public: + DisjointRectSet() { } + ~DisjointRectSet() { } + DisjointRectSet(DisjointRectSet&& other) : m_rects(move(other.m_rects)) { } + + void add(const Rect&); + + void clear() { m_rects.clear(); } + void clear_with_capacity() { m_rects.clear_with_capacity(); } + const Vector& rects() const { return m_rects; } + +private: + void shatter(); + + Vector m_rects; +}; + diff --git a/SharedGraphics/Rect.cpp b/SharedGraphics/Rect.cpp index ffbf59f1ef..aad87432d8 100644 --- a/SharedGraphics/Rect.cpp +++ b/SharedGraphics/Rect.cpp @@ -33,3 +33,47 @@ Rect Rect::united(const Rect& other) const rect.set_bottom(max(bottom(), other.bottom())); return rect; } + +Vector Rect::shatter(const Rect& hammer) const +{ + Vector pieces; + if (!intersects(hammer)) { + pieces.append(*this); + pieces.append(hammer); + return pieces; + } + Rect top_shard { + x(), + y(), + width(), + hammer.y() - y() + }; + Rect bottom_shard { + x(), + hammer.y() + hammer.height(), + width(), + (y() + height()) - (hammer.y() + hammer.height()) + }; + Rect left_shard { + x(), + max(hammer.y(), y()), + hammer.x() - x(), + min((hammer.y() + hammer.height()), (y() + height())) - max(hammer.y(), y()) + }; + Rect right_shard { + hammer.x() + hammer.width(), + max(hammer.y(), y()), + (x() + width() - 1) - (hammer.x() + hammer.width() - 1), + min((hammer.y() + hammer.height()), (y() + height())) - max(hammer.y(), y()) + }; + if (intersects(top_shard)) + pieces.append(top_shard); + if (intersects(bottom_shard)) + pieces.append(bottom_shard); + if (intersects(left_shard)) + pieces.append(left_shard); + if (intersects(right_shard)) + pieces.append(right_shard); + + return pieces; +} diff --git a/SharedGraphics/Rect.h b/SharedGraphics/Rect.h index 931f230b6e..3b5d7538c8 100644 --- a/SharedGraphics/Rect.h +++ b/SharedGraphics/Rect.h @@ -170,6 +170,8 @@ public: Point location() const { return m_location; } Size size() const { return m_size; } + Vector shatter(const Rect& hammer) const; + operator WSAPI_Rect() const; bool operator==(const Rect& other) const diff --git a/WindowServer/Makefile b/WindowServer/Makefile index 422dfeccc8..5f582e6c27 100644 --- a/WindowServer/Makefile +++ b/WindowServer/Makefile @@ -4,6 +4,7 @@ SHAREDGRAPHICS_OBJS = \ ../SharedGraphics/Rect.o \ ../SharedGraphics/GraphicsBitmap.o \ ../SharedGraphics/CharacterBitmap.o \ + ../SharedGraphics/DisjointRectSet.o \ ../SharedGraphics/Color.o WINDOWSERVER_OBJS = \ diff --git a/WindowServer/WSWindowManager.cpp b/WindowServer/WSWindowManager.cpp index 02154287ef..5a38005575 100644 --- a/WindowServer/WSWindowManager.cpp +++ b/WindowServer/WSWindowManager.cpp @@ -614,10 +614,10 @@ void WSWindowManager::compose() { auto dirty_rects = move(m_dirty_rects); auto cursor_location = m_screen.cursor_location(); - dirty_rects.append(m_last_cursor_rect); - dirty_rects.append({ cursor_location.x(), cursor_location.y(), (int)m_cursor_bitmap_inner->width(), (int)m_cursor_bitmap_inner->height() }); + dirty_rects.add(m_last_cursor_rect); + dirty_rects.add({ cursor_location.x(), cursor_location.y(), (int)m_cursor_bitmap_inner->width(), (int)m_cursor_bitmap_inner->height() }); #ifdef DEBUG_COUNTERS - dbgprintf("[WM] compose #%u (%u rects)\n", ++m_compose_count, dirty_rects.size()); + dbgprintf("[WM] compose #%u (%u rects)\n", ++m_compose_count, dirty_rects.rects().size()); #endif auto any_opaque_window_contains_rect = [this] (const Rect& r) { @@ -639,14 +639,14 @@ void WSWindowManager::compose() auto any_dirty_rect_intersects_window = [&dirty_rects] (const WSWindow& window) { auto window_rect = outer_window_rect(window.rect()); - for (auto& dirty_rect : dirty_rects) { + for (auto& dirty_rect : dirty_rects.rects()) { if (dirty_rect.intersects(window_rect)) return true; } return false; }; - for (auto& dirty_rect : dirty_rects) { + for (auto& dirty_rect : dirty_rects.rects()) { if (any_opaque_window_contains_rect(dirty_rect)) continue; if (!m_wallpaper) @@ -661,7 +661,7 @@ void WSWindowManager::compose() return IterationDecision::Continue; if (!any_dirty_rect_intersects_window(window)) return IterationDecision::Continue; - for (auto& dirty_rect : dirty_rects) { + for (auto& dirty_rect : dirty_rects.rects()) { m_back_painter->set_clip_rect(dirty_rect); paint_window_frame(window); Rect dirty_rect_in_window_coordinates = Rect::intersection(dirty_rect, window.rect()); @@ -685,12 +685,12 @@ void WSWindowManager::compose() draw_cursor(); if (m_flash_flush) { - for (auto& rect : dirty_rects) + for (auto& rect : dirty_rects.rects()) m_front_painter->fill_rect(rect, Color::Yellow); } flip_buffers(); - for (auto& r : dirty_rects) + for (auto& r : dirty_rects.rects()) flush(r); } @@ -808,18 +808,7 @@ void WSWindowManager::invalidate(const Rect& a_rect, bool should_schedule_compos if (rect.is_empty()) return; - for (auto& r : m_dirty_rects) { - if (r.contains(rect)) - return; - if (r.intersects(rect)) { - // Unite with the existing dirty rect. - // FIXME: It would be much nicer to compute the exact rects needing repaint. - r = r.united(rect); - return; - } - } - - m_dirty_rects.append(rect); + m_dirty_rects.add(rect); if (should_schedule_compose_event && !m_pending_compose_event) { WSMessageLoop::the().post_message(this, make(WSMessage::WM_DeferredCompose)); diff --git a/WindowServer/WSWindowManager.h b/WindowServer/WSWindowManager.h index f2ee1c9c47..40841648a3 100644 --- a/WindowServer/WSWindowManager.h +++ b/WindowServer/WSWindowManager.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -127,7 +128,7 @@ private: RetainPtr m_front_bitmap; RetainPtr m_back_bitmap; - Vector m_dirty_rects; + DisjointRectSet m_dirty_rects; bool m_pending_compose_event { false };