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.
This commit is contained in:
parent
1769b20dd1
commit
b6d0f8f661
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user