From b6d0f8f6612cac4a3aedeb6f3b63181a2a5a42cb Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 29 Mar 2022 08:52:17 -0600 Subject: [PATCH] canvas: add a drawing-request-freeze/thaw API If queue_draw is "frozen", we simply accumulate drawing requests in a (union) rectangle, and when finally "thawed" the canvas submits a single redraw request for the entire accumulated rect. Although in theory this is all that GTK/GDK does for draw requests, callgrind reveals significant costs associated with the actual calltree for GtkWidget::queue_draw_area(). One potential cost is that GDK also maintains a list of invalidated rectangles in addition to the union, and for MIDI regions with thousands of notes, this can represent real overhead. This approach dispenses with the rect list, since our Canvas drawing model only uses the union rectangle anyway. --- libs/canvas/canvas.cc | 48 ++++++++++++++++++++++++++++++------- libs/canvas/canvas/canvas.h | 6 +++++ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/libs/canvas/canvas.cc b/libs/canvas/canvas.cc index 29c8753e15..71972a6616 100644 --- a/libs/canvas/canvas.cc +++ b/libs/canvas/canvas.cc @@ -55,6 +55,7 @@ uint32_t Canvas::tooltip_timeout_msecs = 750; /** Construct a new Canvas */ Canvas::Canvas () : _root (this) + , _queue_draw_frozen (0) , _bg_color (Gtkmm2ext::rgba_to_color (0, 1.0, 0.0, 1.0)) , _last_render_start_timestamp(0) , _use_intermediate_surface (false) @@ -230,6 +231,25 @@ Canvas::dump (ostream& o) const _root.dump (o); } +void +Canvas::freeze_queue_draw () +{ + _queue_draw_frozen++; +} + +void +Canvas::thaw_queue_draw () +{ + if (_queue_draw_frozen) { + _queue_draw_frozen--; + + if (_queue_draw_frozen == 0 && !frozen_area.empty()) { + request_redraw (frozen_area); + frozen_area = Rect(); + } + } +} + /** Called when an item has been shown or hidden. * @param item Item that has been shown or hidden. */ @@ -238,6 +258,11 @@ Canvas::item_shown_or_hidden (Item* item) { Rect bbox = item->bounding_box (); if (bbox) { + if (_queue_draw_frozen) { + frozen_area = frozen_area.extend (compute_draw_item_area (item, bbox)); + return; + } + if (item->item_to_window (bbox).intersection (visible_area ())) { queue_draw_item_area (item, bbox); } @@ -416,41 +441,48 @@ Canvas::item_moved (Item* item, Rect pre_change_parent_bounding_box) void Canvas::queue_draw_item_area (Item* item, Rect area) { + request_redraw (compute_draw_item_area (item, area)); +} + +Rect +Canvas::compute_draw_item_area (Item* item, Rect area) +{ + Rect r; + if ((area.width()) > 1.0 && (area.height() > 1.0)) { /* item has a rectangular bounding box, which may fall * on non-integer locations. Expand it appropriately. */ - Rect r = item->item_to_window (area, false); + r = item->item_to_window (area, false); r.x0 = floor (r.x0); r.y0 = floor (r.y0); r.x1 = ceil (r.x1); r.y1 = ceil (r.y1); //std::cerr << "redraw box, adjust from " << area << " to " << r << std::endl; - request_redraw (r); - return; } else if (area.width() > 1.0 && area.height() == 1.0) { /* horizontal line, which may fall on non-integer * coordinates. */ - Rect r = item->item_to_window (area, false); + r = item->item_to_window (area, false); r.y0 = floor (r.y0); r.y1 = ceil (r.y1); //std::cerr << "redraw HLine, adjust from " << area << " to " << r << std::endl; - request_redraw (r); } else if (area.width() == 1.0 && area.height() > 1.0) { /* vertical single pixel line, which may fall on non-integer * coordinates */ - Rect r = item->item_to_window (area, false); + r = item->item_to_window (area, false); r.x0 = floor (r.x0); r.x1 = ceil (r.x1); //std::cerr << "redraw VLine, adjust from " << area << " to " << r << std::endl; - request_redraw (r); + } else { /* impossible? one of width or height must be zero ... */ //std::cerr << "redraw IMPOSSIBLE of " << area << std::endl; - request_redraw (item->item_to_window (area, false)); + r = item->item_to_window (area, false); } + + return r; } void diff --git a/libs/canvas/canvas/canvas.h b/libs/canvas/canvas/canvas.h index db39005d4b..32d12c3eb2 100644 --- a/libs/canvas/canvas/canvas.h +++ b/libs/canvas/canvas/canvas.h @@ -107,6 +107,9 @@ public: return &_root; } + void freeze_queue_draw (); + void thaw_queue_draw (); + void set_background_color (Gtkmm2ext::Color); Gtkmm2ext::Color background_color() const { return _bg_color; } @@ -185,6 +188,8 @@ public: protected: Root _root; + uint32_t _queue_draw_frozen; + Rect frozen_area; Gtkmm2ext::Color _bg_color; mutable gint64 _last_render_start_timestamp; @@ -192,6 +197,7 @@ protected: static uint32_t tooltip_timeout_msecs; void queue_draw_item_area (Item *, Rect); + Rect compute_draw_item_area (Item *, Rect); virtual void pick_current_item (int state) = 0; virtual void pick_current_item (Duple const &, int state) = 0;