From dd894d1024f2969d8a3c248ed58212aba30d4f28 Mon Sep 17 00:00:00 2001 From: hippoz <10706925-hippoz@users.noreply.gitlab.com> Date: Sat, 15 Oct 2022 21:35:00 +0300 Subject: [PATCH] much improved paint and reflow scheduling --- src/Box.cpp | 24 ++++++++++++++++++++++++ src/Box.hpp | 13 +++++++++++++ src/Widget.cpp | 10 +++++----- src/Window.cpp | 30 +++++++++++++++++------------- src/Window.hpp | 4 ++-- src/main.cpp | 2 ++ 6 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/Box.cpp b/src/Box.cpp index ee6d1df..a1c581e 100644 --- a/src/Box.cpp +++ b/src/Box.cpp @@ -95,4 +95,28 @@ double Box::clamp_for_dimension(Direction direction, double value) { } } +Box Box::united(const Box &other) { + if (is_null()) { + return other; + } + if (other.is_null()) { + return *this; + } + + Box rect; + rect.set_left(std::min(left(), other.left())); + rect.set_top(std::min(top(), other.top())); + rect.set_right(std::max(right(), other.right())); + rect.set_bottom(std::max(bottom(), other.bottom())); + return rect; +} + +bool Box::is_null() const { + return !(m_x && m_y && m_width && m_height); +} + +std::string Box::debug() { + return "Box(" + std::to_string(m_x) + ", " + std::to_string(m_y) + ", " + std::to_string(m_width) + ", " + std::to_string(m_height) + ")"; +} + } diff --git a/src/Box.hpp b/src/Box.hpp index 08d22ab..12b6ecd 100644 --- a/src/Box.hpp +++ b/src/Box.hpp @@ -1,6 +1,7 @@ #pragma once #include "Point.hpp" +#include namespace Raven { @@ -51,6 +52,11 @@ public: double min_height() const { return m_min_height; } double max_width() const { return m_max_width; } double max_height() const { return m_max_height; } + double top() const { return y(); } + double bottom() const { return y() + height() - 1; } + double left() const { return x(); } + double right() const { return x() + width() - 1; } + bool is_null() const; double dimension_at(Direction direction) { return direction == Direction::Horizontal ? width() : height(); } double max_dimension_at(Direction direction) { return direction == Direction::Horizontal ? max_width() : max_height(); } double min_dimension_at(Direction direction) { return direction == Direction::Horizontal ? min_width() : min_height(); } @@ -60,6 +66,10 @@ public: void set_y(double y) { m_y = y; } void set_width(double width); void set_height(double height); + void set_top(double top) { set_y(top); } + void set_bottom(double bottom) { set_height(bottom - y() + 1); } + void set_right(double right) { set_width(right - x() + 1); } + void set_left(double left) { set_x(left); } void set_max_width(double max_width) { m_max_width = max_width; set_width(m_width); } void set_max_height(double max_height) { m_max_height = max_height; set_height(m_height); } @@ -73,6 +83,9 @@ public: bool contains_box(const Box &other) const; Box offset(double x, double y); + Box united(const Box &other); + + std::string debug(); }; } diff --git a/src/Widget.cpp b/src/Widget.cpp index b55f4a1..af165dd 100644 --- a/src/Widget.cpp +++ b/src/Widget.cpp @@ -192,18 +192,18 @@ void Widget::handle_repaint_rect(RepaintRectEvent &event) { } void Widget::handle_relayout_subtree(RelayoutSubtreeEvent &event) { - on_layout(); // hack + on_layout(); m_window_relative = compute_window_relative(); - for (auto child : m_children) { - child->dispatch_event(event); - } - if (m_layout) { m_layout->run(); } + for (auto child : m_children) { + child->dispatch_event(event); + } + on_after_layout(); } diff --git a/src/Window.cpp b/src/Window.cpp index 06807b8..0bbec01 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -76,9 +76,10 @@ bool Window::dispatch_to_main_widget(Event &event) { } void Window::repaint(Box geometry) { - m_did_repaint_during_batch = true; - if (m_batches) + if (m_batches) { + m_invalidated_area = m_invalidated_area.united(geometry); return; + } auto event = RepaintRectEvent(true, geometry); dispatch_to_main_widget(event); @@ -116,7 +117,6 @@ void Window::reflow() { } void Window::start_batch() { - //INFO << "New batch started. Current count: " << m_batches << std::endl; m_batches++; } @@ -127,7 +127,6 @@ void Window::end_batch() { // we are only interested in performing batch operations for the last batch if (m_batches > 1) { m_batches--; - //INFO << "Consumed batch. Current count: " << m_batches << std::endl; return; } @@ -135,17 +134,17 @@ void Window::end_batch() { if (m_did_relayout_during_batch) { reflow(); - } else if (m_did_repaint_during_batch) { - repaint(); + } else if (!m_invalidated_area.is_null()) { + repaint(m_invalidated_area); } m_did_relayout_during_batch = false; - m_did_repaint_during_batch = false; - INFO << "Performed all batch actions." << std::endl; + m_invalidated_area = {0, 0, 0, 0}; } void Window::run(bool block) { XEvent e; + bool is_loop_batch_started = false; for (;;) { if (block || XPending(m_x_display)) @@ -153,10 +152,13 @@ void Window::run(bool block) { else break; + if (!is_loop_batch_started) { + start_batch(); + is_loop_batch_started = true; + } switch (e.type) { case MapNotify: { - INFO << "[X11 EVENT] MapNotify" << std::endl; - end_batch(); + INFO << "[X11 EVENT] MapNotify" << std::endl; break; } case ConfigureNotify: { @@ -210,14 +212,16 @@ void Window::run(bool block) { } } - if (XPending(m_x_display) == 0 && !m_microtasks.empty()) { - start_batch(); + if (XPending(m_x_display) == 0) { while (!m_microtasks.empty()) { auto callback = m_microtasks.front(); callback(); m_microtasks.pop(); } - end_batch(); + if (is_loop_batch_started) { + end_batch(); + is_loop_batch_started = false; + } } } } diff --git a/src/Window.hpp b/src/Window.hpp index 397376c..d5c053c 100644 --- a/src/Window.hpp +++ b/src/Window.hpp @@ -59,8 +59,8 @@ private: Box m_rect { 0, 0, 800, 600 }; Painter m_painter {}; Cairo::RefPtr m_xlib_surface { nullptr }; - int m_batches { 1 }; - bool m_did_repaint_during_batch { false }; + int m_batches { 0 }; + Box m_invalidated_area {0, 0, 0, 0}; bool m_did_relayout_during_batch { false }; std::queue> m_microtasks; diff --git a/src/main.cpp b/src/main.cpp index da79380..537805a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,7 @@ int main() { Raven::Window window {}; window.spawn_window(); + window.start_batch(); auto main_widget = window.set_main_widget(); auto main_widget_layout = main_widget->set_layout(Raven::Direction::Horizontal); @@ -66,6 +67,7 @@ int main() { } list_view->elements_updated(); + window.end_batch(); window.run(true); return 0; }