An actual tempo line cache (not quite perfect when scrolling left, but miles ahead of the previous one didn't really help at all).

Tempo line updating done immediately/directly rather than in an idle handler.
Looking for feedback how this works for other people, performance wise...
Feel-wise, the obvious lag between scrolling and tempo lines being drawn is now gone.


git-svn-id: svn://localhost/ardour2/branches/3.0@3799 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2008-09-23 22:23:39 +00:00
parent 81c571f2f0
commit 95d82d7a16
7 changed files with 149 additions and 67 deletions

View file

@ -85,6 +85,7 @@
#include "simpleline.h"
#include "rhythm_ferret.h"
#include "actions.h"
#include "tempo_lines.h"
#ifdef FFT_ANALYSIS
#include "analysis_window.h"
@ -376,7 +377,6 @@ Editor::Editor ()
range_marker_drag_rect = 0;
marker_drag_line = 0;
tempo_map_change_idle_handler_id = -1;
set_midi_edit_mode (MidiEditPencil, true);
set_mouse_mode (MouseObject, true);
@ -3938,6 +3938,8 @@ Editor::set_show_measures (bool yn)
hide_measures ();
if ((_show_measures = yn) == true) {
if (tempo_lines)
tempo_lines->show();
draw_measures ();
}
instant_save ();
@ -4542,6 +4544,9 @@ Editor::set_frames_per_unit (double fpu)
if (max_frames / fpu < 800.0) {
return;
}
if (tempo_lines)
tempo_lines->tempo_map_changed();
frames_per_unit = fpu;
post_zoom ();

View file

@ -1597,7 +1597,6 @@ public:
void draw_metric_marks (const ARDOUR::Metrics& metrics);
void compute_current_bbt_points (nframes_t left, nframes_t right);
int tempo_map_change_idle_handler_id;
void tempo_map_changed (ARDOUR::Change);
void redisplay_tempo (bool immediate_redraw);

View file

@ -904,7 +904,8 @@ Editor::canvas_scroll_to (nframes64_t time_origin)
update_fixed_rulers ();
redisplay_tempo (!_dragging_hscrollbar);
//redisplay_tempo (!_dragging_hscrollbar);
redisplay_tempo (true);
}
void

View file

@ -102,12 +102,13 @@ Editor::tempo_map_changed (Change ignored)
ENSURE_GUI_THREAD(bind (mem_fun (*this, &Editor::tempo_map_changed), ignored));
if (tempo_lines)
tempo_lines->tempo_map_changed();
compute_current_bbt_points(leftmost_frame, leftmost_frame + (nframes_t)(edit_packer.get_width() * frames_per_unit));
session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
update_tempo_based_rulers ();
if (tempo_map_change_idle_handler_id < 0) {
tempo_map_change_idle_handler_id = Glib::signal_idle().connect (mem_fun (*this, &Editor::redraw_measures));
}
redraw_measures ();
}
void
@ -119,20 +120,7 @@ Editor::redisplay_tempo (bool immediate_redraw)
compute_current_bbt_points (leftmost_frame, leftmost_frame + (nframes_t)(edit_packer.get_width() * frames_per_unit)); // redraw rulers and measures
if (immediate_redraw) {
hide_measures ();
if (current_bbt_points) {
draw_measures ();
}
} else if (tempo_map_change_idle_handler_id < 0) {
tempo_map_change_idle_handler_id = Glib::signal_idle().connect (mem_fun (*this, &Editor::redraw_measures));
}
redraw_measures();
update_tempo_based_rulers ();
}
@ -182,9 +170,7 @@ Editor::hide_measures ()
bool
Editor::redraw_measures ()
{
hide_measures ();
draw_measures ();
tempo_map_change_idle_handler_id = -1;
return false;
}

View file

@ -22,35 +22,52 @@
#include "tempo_lines.h"
#include "ardour_ui.h"
ArdourCanvas::SimpleLine *
TempoLines::get_line ()
using namespace std;
#define MAX_CACHED_LINES 512
TempoLines::TempoLines(ArdourCanvas::Canvas& canvas, ArdourCanvas::Group* group)
: _canvas(canvas)
, _group(group)
, _clean_left(DBL_MAX)
, _clean_right(0.0)
{
ArdourCanvas::SimpleLine *line;
if (_free_lines.empty()) {
line = new ArdourCanvas::SimpleLine (*_group);
_used_lines.push_back (line);
} else {
line = _free_lines.front();
_free_lines.erase (_free_lines.begin());
_used_lines.push_back (line);
}
return line;
}
void
TempoLines::tempo_map_changed()
{
_clean_left = DBL_MAX;
_clean_right = 0.0;
// TODO: Dirty/slow, but 'needed' for zoom :(
for (Lines::iterator i = _lines.begin(); i != _lines.end(); ) {
Lines::iterator next = i;
++next;
i->second->property_x1() = DBL_MAX;
i->second->property_x2() = DBL_MAX;
_lines.erase(i);
_lines.insert(make_pair(DBL_MAX, i->second));
i = next;
}
}
void
TempoLines::show ()
{
for (Lines::iterator i = _lines.begin(); i != _lines.end(); ++i) {
i->second->show();
}
}
void
TempoLines::hide ()
{
for (Lines::iterator i = _used_lines.begin(); i != _used_lines.end(); ++i) {
(*i)->hide();
_free_lines.push_back (*i);
for (Lines::iterator i = _lines.begin(); i != _lines.end(); ++i) {
i->second->hide();
}
_used_lines.clear ();
}
void
TempoLines::draw (ARDOUR::TempoMap::BBTPointList& points, double frames_per_unit)
{
@ -63,6 +80,8 @@ TempoLines::draw (ARDOUR::TempoMap::BBTPointList& points, double frames_per_unit
uint32_t beats = 0;
uint32_t bars = 0;
uint32_t color;
const size_t needed = points.size();
_canvas.get_scroll_region (x1, y1, x2, who_cares);
_canvas.root()->get_bounds(who_cares, who_cares, who_cares, y2);
@ -74,14 +93,38 @@ TempoLines::draw (ARDOUR::TempoMap::BBTPointList& points, double frames_per_unit
bars = (*i).bar - (*points.begin()).bar;
beats = points.size() - bars;
beat_density = (beats * 10.0f) / _canvas.get_width ();
beat_density = (beats * 10.0f) / _canvas.get_width ();
if (beat_density > 4.0f) {
/* if the lines are too close together, they become useless
*/
/* if the lines are too close together, they become useless */
return;
}
xpos = rint(((nframes64_t)(*i).frame) / (double)frames_per_unit);
const double needed_right = xpos;
i = points.begin();
xpos = rint(((nframes64_t)(*i).frame) / (double)frames_per_unit);
const double needed_left = xpos;
Lines::iterator left = _lines.lower_bound(xpos); // first line >= xpos
bool exhausted = (left == _lines.end());
Lines::iterator li = left;
// Tempo map hasn't changed and we're entirely within a clean
// range, don't need to do anything. Yay.
if (needed_left >= _clean_left && needed_right <= _clean_right) {
//cout << "LINE CACHE PERFECT HIT!" << endl;
return;
}
//cout << "LINE CACHE MISS :/" << endl;
bool inserted_last_time = false;
bool invalidated = false;
for (i = points.begin(); i != points.end(); ++i) {
switch ((*i).type) {
@ -96,21 +139,63 @@ TempoLines::draw (ARDOUR::TempoMap::BBTPointList& points, double frames_per_unit
color = ARDOUR_UI::config()->canvasvar_MeasureLineBeat.get();
if (beat_density > 2.0) {
/* only draw beat lines if the gaps between beats are large.
*/
/* only draw beat lines if the gaps between beats are large. */
break;
}
}
xpos = rint(((nframes64_t)(*i).frame) / (double)frames_per_unit);
line = get_line ();
line->property_x1() = xpos;
line->property_x2() = xpos;
line->property_y2() = y2;
line->property_color_rgba() = color;
//line->raise_to_top();
line->show();
if (inserted_last_time) {
li = _lines.lower_bound(xpos); // first line >= xpos
}
if (!exhausted) {
line = li->second;
exhausted = ((++li) == _lines.end());
inserted_last_time = false;
} else if (_lines.size() < needed || _lines.size() < MAX_CACHED_LINES) {
line = new ArdourCanvas::SimpleLine (*_group);
line->property_x1() = xpos;
line->property_x2() = xpos;
line->property_y2() = y2;
line->property_color_rgba() = color;
_lines.insert(make_pair(xpos, line));
inserted_last_time = true;
} else {
assert(li != _lines.begin());
line = _lines.begin()->second; // steal leftmost line
_lines.erase(_lines.begin());
_lines.insert(make_pair(xpos, line));
inserted_last_time = true;
invalidated = true;
}
/* At this point, line's key is correct, but actual pos may not be */
if (line->property_x1() != xpos) {
// Turn this on to see the case where this isn't quite ideal yet
// (scrolling left, lots of lines are moved left when there is
// likely to be a huge chunk with equivalent coords)
//cout << "MOVE " << line->property_x1() << " -> " << xpos << endl;
double x1 = line->property_x1();
bool was_clean = x1 >= _clean_left && x1 <= _clean_right;
invalidated = invalidated || was_clean;
line->property_x1() = xpos;
line->property_x2() = xpos;
line->property_y2() = y2;
line->property_color_rgba() = color;
}
break;
}
}
if (invalidated) { // We messed things up, visible range is all we know is valid
_clean_left = needed_left;
_clean_right = needed_right;
} else { // Extend range to what we've 'fixed'
_clean_left = min(_clean_left, needed_left);
_clean_right = max(_clean_right, needed_right);
}
}

View file

@ -19,30 +19,39 @@
#ifndef __ardour_tempo_lines_h__
#define __ardour_tempo_lines_h__
#include <vector>
#include <map>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <ardour/tempo.h>
#include "canvas.h"
#include "simpleline.h"
typedef boost::fast_pool_allocator<
std::pair<double, ArdourCanvas::SimpleLine>,
boost::default_user_allocator_new_delete,
boost::details::pool::null_mutex,
8192>
MapAllocator;
class TempoLines {
public:
TempoLines(ArdourCanvas::Canvas& canvas, ArdourCanvas::Group* group)
: _canvas(canvas)
, _group(group)
{}
ArdourCanvas::SimpleLine* get_line();
TempoLines(ArdourCanvas::Canvas& canvas, ArdourCanvas::Group* group);
void tempo_map_changed();
void draw(ARDOUR::TempoMap::BBTPointList& points, double frames_per_unit);
void show();
void hide();
private:
typedef std::vector<ArdourCanvas::SimpleLine*> Lines;
Lines _free_lines;
Lines _used_lines;
typedef std::map<double, ArdourCanvas::SimpleLine*, std::less<double>, MapAllocator> Lines;
Lines _lines;
ArdourCanvas::Canvas& _canvas;
ArdourCanvas::Group* _group;
double _clean_left;
double _clean_right;
};
#endif /* __ardour_tempo_lines_h__ */

View file

@ -27,9 +27,6 @@
#include <sigc++/signal.h>
#include <glibmm/thread.h>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <pbd/undo.h>
#include <pbd/xml++.h>
#include <pbd/statefuldestructible.h>