瀏覽代碼

WSCompositor: Allow a compose to bypass the timer when it first happens

d66fa60fcf23fa7217a065479af6b1f5a851a506 introduced the use of a timer
to coalesce screen updates. This is OK, but it does introduce update
latency.

To help mitigate the impact of this, we now have a second (immediate)
timer. When a compose pass is first triggered, the immediate timer will
allow the compose to happen on the next spin of the event loop (so, only
coalescing updates across a single event loop pass). Any updates that
trigger while the delayed timer is running, though, will be delayed to
that (~60fps) timer.

This fixes #103.
Robin Burchell 6 年之前
父節點
當前提交
9b86eb9fad
共有 3 個文件被更改,包括 31 次插入4 次删除
  1. 29 4
      Servers/WindowServer/WSCompositor.cpp
  2. 1 0
      Servers/WindowServer/WSCompositor.h
  3. 1 0
      SharedGraphics/DisjointRectSet.h

+ 29 - 4
Servers/WindowServer/WSCompositor.cpp

@@ -30,12 +30,20 @@ WSCompositor::WSCompositor()
 
     m_compose_timer.on_timeout = [=]() {
 #if defined(COMPOSITOR_DEBUG)
-        dbgprintf("WSCompositor: frame callback\n");
+        dbgprintf("WSCompositor: delayed frame callback: %d rects\n", m_dirty_rects.size());
 #endif
         compose();
     };
     m_compose_timer.set_single_shot(true);
     m_compose_timer.set_interval(1000 / 60);
+    m_immediate_compose_timer.on_timeout = [=]() {
+#if defined(COMPOSITOR_DEBUG)
+        dbgprintf("WSCompositor: immediate frame callback: %d rects\n", m_dirty_rects.size());
+#endif
+        compose();
+    };
+    m_immediate_compose_timer.set_single_shot(true);
+    m_immediate_compose_timer.set_interval(0);
 }
 
 void WSCompositor::compose()
@@ -43,6 +51,12 @@ void WSCompositor::compose()
     auto& wm = WSWindowManager::the();
 
     auto dirty_rects = move(m_dirty_rects);
+
+    if (dirty_rects.size() == 0) {
+        // nothing dirtied since the last compose pass.
+        return;
+    }
+
     dirty_rects.add(Rect::intersection(m_last_geometry_label_rect, WSScreen::the().rect()));
     dirty_rects.add(Rect::intersection(m_last_cursor_rect, WSScreen::the().rect()));
     dirty_rects.add(Rect::intersection(current_cursor_rect(), WSScreen::the().rect()));
@@ -161,11 +175,22 @@ void WSCompositor::invalidate(const Rect& a_rect)
     if (rect.is_empty())
         return;
 
+    m_dirty_rects.add(rect);
+
+    // We delay composition by a timer interval, but to not affect latency too
+    // much, if a pending compose is not already scheduled, we also schedule an
+    // immediate compose the next spin of the event loop.
+    if (!m_compose_timer.is_active()) {
 #if defined(COMPOSITOR_DEBUG)
-    dbgprintf("Invalidated: %dx%d %dx%d\n", a_rect.x(), a_rect.y(), a_rect.width(), a_rect.height());
+        dbgprintf("Invalidated (starting immediate frame): %dx%d %dx%d\n", a_rect.x(), a_rect.y(), a_rect.width(), a_rect.height());
 #endif
-    m_dirty_rects.add(rect);
-    m_compose_timer.start();
+        m_compose_timer.start();
+        m_immediate_compose_timer.start();
+    } else {
+#if defined(COMPOSITOR_DEBUG)
+        dbgprintf("Invalidated (frame callback pending): %dx%d %dx%d\n", a_rect.x(), a_rect.y(), a_rect.width(), a_rect.height());
+#endif
+    }
 }
 
 bool WSCompositor::set_wallpaper(const String& path, Function<void(bool)>&& callback)

+ 1 - 0
Servers/WindowServer/WSCompositor.h

@@ -40,6 +40,7 @@ private:
     unsigned m_compose_count { 0 };
     unsigned m_flush_count { 0 };
     CTimer m_compose_timer;
+    CTimer m_immediate_compose_timer;
     bool m_flash_flush { false };
     bool m_buffers_are_flipped { false };
 

+ 1 - 0
SharedGraphics/DisjointRectSet.h

@@ -12,6 +12,7 @@ public:
     void add(const Rect&);
 
     bool is_empty() const { return m_rects.is_empty(); }
+    int size() const { return m_rects.size(); }
 
     void clear() { m_rects.clear(); }
     void clear_with_capacity() { m_rects.clear_with_capacity(); }