浏览代码

WindowServer: Only blit dirty rect of windows to back buffer.

Previously we'd blit every pixel in every window that intersected any dirty
rect to the back buffer. With this patch, we limit ourselves to blitting the
pixels inside the actual dirty rects.

There's still a lot of optimizations to make in this code.
Andreas Kling 6 年之前
父节点
当前提交
3271c115e2

+ 25 - 23
SharedGraphics/Painter.cpp

@@ -56,42 +56,44 @@ void Painter::fill_rect(const Rect& a_rect, Color color)
     }
 }
 
-void Painter::draw_rect(const Rect& rect, Color color)
+void Painter::draw_rect(const Rect& a_rect, Color color)
 {
-    Rect r = rect;
-    r.move_by(m_translation);
+    Rect rect = a_rect;
+    rect.move_by(m_translation);
+
+    auto clipped_rect = Rect::intersection(rect, m_clip_rect);
+    if (clipped_rect.is_empty())
+        return;
 
-    int min_y = max(r.top(), m_clip_rect.top());
-    int max_y = min(r.bottom(), m_clip_rect.bottom());
-    int min_x = max(r.left(), m_clip_rect.left());
-    int max_x = min(r.right(), m_clip_rect.right());
+    int min_y = clipped_rect.top();
+    int max_y = clipped_rect.bottom();
 
-    if (r.top() >= min_y && r.top() <= max_y) {
-        fast_dword_fill(m_target->scanline(r.top()) + min_x, color.value(), max_x - min_x + 1);
+    if (rect.top() >= clipped_rect.top() && rect.top() <= clipped_rect.bottom()) {
+        fast_dword_fill(m_target->scanline(rect.top()) + clipped_rect.left(), color.value(), clipped_rect.width());
         ++min_y;
     }
-    if (r.bottom() <= max_y && r.bottom() >= min_y) {
-        fast_dword_fill(m_target->scanline(r.bottom()) + min_x, color.value(), max_x - min_x + 1);
+    if (rect.bottom() >= clipped_rect.top() && rect.bottom() <= clipped_rect.bottom()) {
+        fast_dword_fill(m_target->scanline(rect.bottom()) + clipped_rect.left(), color.value(), clipped_rect.width());
         --max_y;
     }
 
-    bool draw_left_side = r.left() >= m_clip_rect.left() && r.left() <= m_clip_rect.right();
-    bool draw_right_side = r.right() >= m_clip_rect.left() && r.right() <= m_clip_rect.right();
+    bool draw_left_side = rect.left() >= clipped_rect.left();
+    bool draw_right_side = rect.right() == clipped_rect.right();
 
     if (draw_left_side && draw_right_side) {
         // Specialized loop when drawing both sides.
         for (int y = min_y; y <= max_y; ++y) {
             auto* bits = m_target->scanline(y);
-            bits[r.left()] = color.value();
-            bits[r.right()] = color.value();
+            bits[rect.left()] = color.value();
+            bits[rect.right()] = color.value();
         }
     } else {
         for (int y = min_y; y <= max_y; ++y) {
             auto* bits = m_target->scanline(y);
             if (draw_left_side)
-                bits[r.left()] = color.value();
+                bits[rect.left()] = color.value();
             if (draw_right_side)
-                bits[r.right()] = color.value();
+                bits[rect.right()] = color.value();
         }
     }
 }
@@ -103,12 +105,12 @@ void Painter::draw_bitmap(const Point& p, const CharacterBitmap& bitmap, Color c
     for (unsigned row = 0; row < bitmap.height(); ++row) {
         int y = point.y() + row;
         if (y < m_clip_rect.top() || y > m_clip_rect.bottom())
-            break;
+            continue;
         auto* bits = m_target->scanline(y);
         for (unsigned j = 0; j < bitmap.width(); ++j) {
             int x = point.x() + j;
             if (x < m_clip_rect.left() || x > m_clip_rect.right())
-                break;
+                continue;
             char fc = bitmap.bits()[row * bitmap.width() + j];
             if (fc == '#')
                 bits[x] = color.value();
@@ -244,13 +246,13 @@ void Painter::draw_focus_rect(const Rect& rect)
     draw_rect(focus_rect, Color(96, 96, 192));
 }
 
-void Painter::blit(const Point& position, const GraphicsBitmap& source)
+void Painter::blit(const Point& position, const GraphicsBitmap& source, const Rect& src_rect)
 {
-    Rect dst_rect(position, source.size());
+    Rect dst_rect(position, src_rect.size());
     dst_rect.intersect(m_clip_rect);
 
-    RGBA32* dst = m_target->scanline(position.y()) + dst_rect.x();
-    const RGBA32* src= source.scanline(0) + (dst_rect.x() - position.x());
+    RGBA32* dst = m_target->scanline(dst_rect.y()) + dst_rect.x();
+    const RGBA32* src = source.scanline(src_rect.top()) + src_rect.left();
 
     const unsigned dst_skip = m_target->width();
     const unsigned src_skip = source.width();

+ 5 - 1
SharedGraphics/Painter.h

@@ -28,7 +28,7 @@ public:
     void set_pixel(const Point&, Color);
     void draw_line(const Point&, const Point&, Color);
     void draw_focus_rect(const Rect&);
-    void blit(const Point&, const GraphicsBitmap&);
+    void blit(const Point&, const GraphicsBitmap&, const Rect& src_rect);
 
     enum class TextAlignment { TopLeft, CenterLeft, Center };
     void draw_text(const Rect&, const String&, TextAlignment = TextAlignment::TopLeft, Color = Color());
@@ -40,6 +40,10 @@ public:
     void set_draw_op(DrawOp op) { m_draw_op = op; }
     DrawOp draw_op() const { return m_draw_op; }
 
+    void set_clip_rect(const Rect& rect) { m_clip_rect = rect; }
+    void clear_clip_rect() { m_clip_rect = { 0, 0, 1024, 768 }; }
+    Rect clip_rect() const { return m_clip_rect; }
+
 private:
     void set_pixel_with_draw_op(dword& pixel, const Color&);
 

+ 1 - 1
SharedGraphics/Rect.cpp

@@ -8,7 +8,7 @@ void Rect::intersect(const Rect& other)
     int t = max(top(), other.top());
     int b = min(bottom(), other.bottom());
 
-    if (l >= r || t >= b) {
+    if (l > r || t > b) {
         m_location = { };
         m_size = { };
         return;

+ 6 - 0
SharedGraphics/Rect.h

@@ -18,6 +18,12 @@ public:
         , m_size(size)
     {
     }
+    Rect(const Rect& other)
+        : m_location(other.m_location)
+        , m_size(other.m_size)
+    {
+    }
+
     Rect(const GUI_Rect&);
 
     bool is_null() const

+ 4 - 4
Terminal/Terminal.cpp

@@ -426,7 +426,6 @@ void Terminal::paint()
     Rect rect { 0, 0, m_pixel_width, m_pixel_height };
     Painter painter(*m_backing);
 
-    bool need_full_invalidation = false;
     memset(m_row_needs_invalidation, 0, rows() * sizeof(bool));
 
 #ifdef FAST_SCROLL
@@ -440,7 +439,7 @@ void Terminal::paint()
             m_backing->scanline(second_scanline),
             scanlines_to_copy * m_pixel_width
         );
-        need_full_invalidation = true;
+        m_need_full_invalidation = true;
         attribute_at(m_cursor_row - m_rows_to_scroll_backing_store, m_cursor_column).dirty = true;
     }
     m_rows_to_scroll_backing_store = 0;
@@ -472,12 +471,13 @@ void Terminal::paint()
     m_row_needs_invalidation[m_cursor_row] = true;
 
     if (m_belling) {
-        need_full_invalidation = true;
+        m_need_full_invalidation = true;
         painter.draw_rect(rect, Color::Red);
     }
 
-    if (need_full_invalidation) {
+    if (m_need_full_invalidation) {
         invalidate_window();
+        m_need_full_invalidation = false;
         return;
     }
 

+ 1 - 0
Terminal/Terminal.h

@@ -101,6 +101,7 @@ private:
     int m_line_height { 0 };
 
     bool m_in_active_window { false };
+    bool m_need_full_invalidation { false };
 
     RetainPtr<Font> m_font;
 };

+ 19 - 6
WindowServer/WSWindowManager.cpp

@@ -321,19 +321,32 @@ void WSWindowManager::compose()
         return false;
     };
 
-    for (auto& r : dirty_rects) {
-        if (any_window_contains_rect(r))
+    for (auto& dirty_rect : dirty_rects) {
+        if (any_window_contains_rect(dirty_rect)) {
             continue;
-        //dbgprintf("Repaint root %d,%d %dx%d\n", r.x(), r.y(), r.width(), r.height());
-        m_back_painter->fill_rect(r, Color(0, 72, 96));
+        }
+        //dbgprintf("Repaint root %d,%d %dx%d\n", dirty_rect.x(), dirty_rect.y(), dirty_rect.width(), dirty_rect.height());
+        m_back_painter->fill_rect(dirty_rect, Color(0, 72, 96));
     }
     for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
         if (!window->backing())
             continue;
         if (!any_dirty_rect_intersects_window(*window))
             continue;
-        paint_window_frame(*window);
-        m_back_painter->blit(window->position(), *window->backing());
+        for (auto& dirty_rect : dirty_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());
+            if (dirty_rect_in_window_coordinates.is_empty())
+                continue;
+            dirty_rect_in_window_coordinates.set_x(dirty_rect_in_window_coordinates.x() - window->x());
+            dirty_rect_in_window_coordinates.set_y(dirty_rect_in_window_coordinates.y() - window->y());
+            auto dst = window->position();
+            dst.move_by(dirty_rect_in_window_coordinates.location());
+            m_back_painter->blit(dst, *window->backing(), dirty_rect_in_window_coordinates);
+            m_back_painter->clear_clip_rect();
+        }
+        m_back_painter->clear_clip_rect();
     }
     for (auto& r : dirty_rects)
         flush(r);