mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 06:44:57 +01:00
several changes, major and minor, to improve threaded waveview rendering
This commit is contained in:
parent
2cb511e25a
commit
e9224a58d1
3 changed files with 68 additions and 47 deletions
|
|
@ -311,7 +311,6 @@ public:
|
|||
double _amplitude_above_axis;
|
||||
float _region_amplitude;
|
||||
double _start_shift;
|
||||
mutable bool idle_queued;
|
||||
|
||||
/** The `start' value to use for the region; we can't use the region's
|
||||
* value as the crossfade editor needs to alter it.
|
||||
|
|
@ -378,6 +377,8 @@ public:
|
|||
ArdourCanvas::Coord y_extent (double) const;
|
||||
void compute_tips (ARDOUR::PeakData const & peak, LineTips& tips) const;
|
||||
|
||||
ARDOUR::framecnt_t desired_image_width () const;
|
||||
|
||||
void draw_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int n_peaks, boost::shared_ptr<WaveViewThreadRequest>) const;
|
||||
void draw_absent_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int) const;
|
||||
|
||||
|
|
@ -392,7 +393,6 @@ public:
|
|||
mutable boost::shared_ptr<WaveViewCache::Entry> _current_image;
|
||||
|
||||
mutable boost::shared_ptr<WaveViewThreadRequest> current_request;
|
||||
bool idle_send_request () const;
|
||||
|
||||
static WaveViewCache* images;
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "ardour/lmath.h"
|
||||
#include "ardour/audioregion.h"
|
||||
#include "ardour/audiosource.h"
|
||||
#include "ardour/session.h"
|
||||
|
||||
#include "canvas/canvas.h"
|
||||
#include "canvas/colors.h"
|
||||
|
|
@ -44,6 +45,8 @@
|
|||
#include "canvas/utils.h"
|
||||
#include "canvas/wave_view.h"
|
||||
|
||||
#include "evoral/Range.hpp"
|
||||
|
||||
#include <gdkmm/general.h>
|
||||
|
||||
#include "gtkmm2ext/gui_thread.h"
|
||||
|
|
@ -87,7 +90,6 @@ WaveView::WaveView (Canvas* c, boost::shared_ptr<ARDOUR::AudioRegion> region)
|
|||
, _amplitude_above_axis (1.0)
|
||||
, _region_amplitude (region->scale_amplitude ())
|
||||
, _start_shift (0.0)
|
||||
, idle_queued (false)
|
||||
, _region_start (region->start())
|
||||
, get_image_in_thread (false)
|
||||
, always_get_image_in_thread (false)
|
||||
|
|
@ -121,7 +123,6 @@ WaveView::WaveView (Item* parent, boost::shared_ptr<ARDOUR::AudioRegion> region)
|
|||
, _amplitude_above_axis (1.0)
|
||||
, _region_amplitude (region->scale_amplitude ())
|
||||
, _start_shift (0.0)
|
||||
, idle_queued (false)
|
||||
, _region_start (region->start())
|
||||
, get_image_in_thread (false)
|
||||
, always_get_image_in_thread (false)
|
||||
|
|
@ -151,6 +152,7 @@ WaveView::debug_name() const
|
|||
void
|
||||
WaveView::image_ready ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::WaveView, string_compose ("queue draw for %1 at %2 (vis = %3 bbox = %4 CR %5)\n", this, g_get_monotonic_time(), visible(), _bounding_box, current_request));
|
||||
redraw ();
|
||||
}
|
||||
|
||||
|
|
@ -256,6 +258,7 @@ WaveView::set_clip_level (double dB)
|
|||
void
|
||||
WaveView::invalidate_image_cache ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 invalidates image cache and cancels current request\n", this));
|
||||
cancel_my_render_request ();
|
||||
_current_image.reset ();
|
||||
}
|
||||
|
|
@ -766,6 +769,10 @@ WaveView::get_image (framepos_t start, framepos_t end, bool& full_image) const
|
|||
* have an image there. if so, use it (and put it in the cache
|
||||
* while we're here.
|
||||
*/
|
||||
|
||||
DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 CR %2 stop? %3 image %4\n", this, current_request,
|
||||
(current_request ? current_request->should_stop() : false),
|
||||
(current_request ? current_request->image : 0)));
|
||||
|
||||
if (current_request && !current_request->should_stop() && current_request->image) {
|
||||
|
||||
|
|
@ -809,7 +816,7 @@ WaveView::get_image (framepos_t start, framepos_t end, bool& full_image) const
|
|||
if (!ret || !full_image) {
|
||||
|
||||
if ((rendered && get_image_in_thread) || always_get_image_in_thread) {
|
||||
|
||||
|
||||
DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1: generating image in caller thread\n", name));
|
||||
|
||||
boost::shared_ptr<WaveViewThreadRequest> req (new WaveViewThreadRequest);
|
||||
|
|
@ -820,10 +827,10 @@ WaveView::get_image (framepos_t start, framepos_t end, bool& full_image) const
|
|||
req->samples_per_pixel = _samples_per_pixel;
|
||||
req->region = _region; /* weak ptr, to avoid storing a reference in the request queue */
|
||||
req->channel = _channel;
|
||||
req->width = _canvas->visible_area().width();
|
||||
req->height = _height;
|
||||
req->fill_color = _fill_color;
|
||||
req->amplitude = _region_amplitude * _amplitude_above_axis;
|
||||
req->width = desired_image_width ();
|
||||
|
||||
/* draw image in this (the GUI thread) */
|
||||
|
||||
|
|
@ -864,6 +871,26 @@ WaveView::get_image_from_cache (framepos_t start, framepos_t end, bool& full) co
|
|||
_height, _region_amplitude * _amplitude_above_axis, _fill_color, _samples_per_pixel, full);
|
||||
}
|
||||
|
||||
framecnt_t
|
||||
WaveView::desired_image_width () const
|
||||
{
|
||||
/* compute how wide the image should be, in samples.
|
||||
*
|
||||
* We want at least 1 canvas width's worth, but if that
|
||||
* represents less than 1/10th of a second, use 1/10th of
|
||||
* a second instead.
|
||||
*/
|
||||
|
||||
framecnt_t canvas_width_samples = _canvas->visible_area().width() * _samples_per_pixel;
|
||||
const framecnt_t one_tenth_of_second = _region->session().frame_rate() / 10;
|
||||
|
||||
if (canvas_width_samples > one_tenth_of_second) {
|
||||
return canvas_width_samples;
|
||||
}
|
||||
|
||||
return one_tenth_of_second;
|
||||
}
|
||||
|
||||
void
|
||||
WaveView::queue_get_image (boost::shared_ptr<const ARDOUR::Region> region, framepos_t start, framepos_t end) const
|
||||
{
|
||||
|
|
@ -875,10 +902,10 @@ WaveView::queue_get_image (boost::shared_ptr<const ARDOUR::Region> region, frame
|
|||
req->samples_per_pixel = _samples_per_pixel;
|
||||
req->region = _region; /* weak ptr, to avoid storing a reference in the request queue */
|
||||
req->channel = _channel;
|
||||
req->width = _canvas->visible_area().width();
|
||||
req->height = _height;
|
||||
req->fill_color = _fill_color;
|
||||
req->amplitude = _region_amplitude * _amplitude_above_axis;
|
||||
req->width = desired_image_width ();
|
||||
|
||||
if (current_request) {
|
||||
/* this will stop rendering in progress (which might otherwise
|
||||
|
|
@ -894,44 +921,31 @@ WaveView::queue_get_image (boost::shared_ptr<const ARDOUR::Region> region, frame
|
|||
{
|
||||
Glib::Threads::Mutex::Lock lm (request_queue_lock);
|
||||
current_request = req;
|
||||
}
|
||||
|
||||
/* this is always called from the GUI thread (there is only one), and
|
||||
* the same thread runs the idle callback chain. thus we do not need
|
||||
* any locks to protect idle_queued - it is only ever set or read in
|
||||
* the unitary GUI thread.
|
||||
*/
|
||||
|
||||
if (!idle_queued) {
|
||||
Glib::signal_idle().connect (sigc::mem_fun (*this, &WaveView::idle_send_request));
|
||||
idle_queued = true;
|
||||
DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 now has current request %2\n", this, req));
|
||||
|
||||
if (request_queue.insert (this).second) {
|
||||
/* this waveview was not already in the request queue, make sure we wake
|
||||
the rendering thread in case it is asleep.
|
||||
*/
|
||||
request_cond.signal ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WaveView::idle_send_request () const
|
||||
{
|
||||
Glib::Threads::Mutex::Lock lm (request_queue_lock);
|
||||
|
||||
request_queue.insert (this);
|
||||
request_cond.signal (); /* wake thread - must be done while holding lock */
|
||||
idle_queued = false;
|
||||
|
||||
return false; /* do not call from idle again */
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WaveView::generate_image (boost::shared_ptr<WaveViewThreadRequest> req, bool in_render_thread) const
|
||||
{
|
||||
if (!req->should_stop()) {
|
||||
|
||||
/* sample position is canonical here, and we want to generate
|
||||
* an image that spans about twice the canvas width
|
||||
* an image that spans about 3x the canvas width. We get to that
|
||||
* width by using an image sample count of the screen width added
|
||||
* on each side of the desired image center.
|
||||
*/
|
||||
|
||||
const framepos_t center = req->start + ((req->end - req->start) / 2);
|
||||
const framecnt_t image_samples = req->width * req->samples_per_pixel; /* one canvas width */
|
||||
const framecnt_t image_samples = req->width;
|
||||
|
||||
/* we can request data from anywhere in the Source, between 0 and its length
|
||||
*/
|
||||
|
|
@ -973,6 +987,7 @@ WaveView::generate_image (boost::shared_ptr<WaveViewThreadRequest> req, bool in_
|
|||
}
|
||||
|
||||
if (in_render_thread && !req->should_stop()) {
|
||||
DEBUG_TRACE (DEBUG::WaveView, string_compose ("done with request for %1 at %2 CR %3 req %4 range %5 .. %6\n", this, g_get_monotonic_time(), current_request, req, req->start, req->end));
|
||||
const_cast<WaveView*>(this)->ImageReady (); /* emit signal */
|
||||
}
|
||||
|
||||
|
|
@ -1002,6 +1017,8 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons
|
|||
return;
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::WaveView, string_compose ("render %1 at %2\n", this, g_get_monotonic_time()));
|
||||
|
||||
/* a WaveView is intimately connected to an AudioRegion. It will
|
||||
* display the waveform within the region, anywhere from the start of
|
||||
* the region to its end.
|
||||
|
|
@ -1428,6 +1445,8 @@ WaveView::cancel_my_render_request () const
|
|||
|
||||
request_queue.erase (this);
|
||||
current_request.reset ();
|
||||
DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 now has no request %2\n", this));
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1488,6 +1507,8 @@ WaveView::drawing_thread ()
|
|||
requestor = *(request_queue.begin());
|
||||
request_queue.erase (request_queue.begin());
|
||||
|
||||
DEBUG_TRACE (DEBUG::WaveView, string_compose ("start request for %1 at %2\n", requestor, g_get_monotonic_time()));
|
||||
|
||||
boost::shared_ptr<WaveViewThreadRequest> req = requestor->current_request;
|
||||
|
||||
if (!req) {
|
||||
|
|
@ -1565,25 +1586,25 @@ WaveViewCache::lookup_image (boost::shared_ptr<ARDOUR::AudioSource> src,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (end <= e->end && start >= e->start) {
|
||||
/* found an image that covers the range we need */
|
||||
switch (Evoral::coverage (start, end, e->start, e->end)) {
|
||||
case Evoral::OverlapExternal: /* required range is inside image range */
|
||||
DEBUG_TRACE (DEBUG::WaveView, string_compose ("found image spanning %1..%2 covers %3..%4\n",
|
||||
e->start, e->end, start, end));
|
||||
use (src, e);
|
||||
full_coverage = true;
|
||||
return e;
|
||||
}
|
||||
full_coverage = true;
|
||||
return e;
|
||||
|
||||
if (start >= e->start) {
|
||||
/* found an image that covers the start, but not the
|
||||
* end. See if it is longer than any other similar
|
||||
* partial image that we've found so far.
|
||||
*/
|
||||
case Evoral::OverlapStart: /* required range start is covered by image range */
|
||||
if ((e->end - start) > max_coverage) {
|
||||
best_partial = e;
|
||||
max_coverage = e->end - start;
|
||||
}
|
||||
break;
|
||||
|
||||
if ((e->end - e->start) > max_coverage) {
|
||||
best_partial = e;
|
||||
max_coverage = e->end - e->start;
|
||||
}
|
||||
case Evoral::OverlapNone:
|
||||
case Evoral::OverlapEnd:
|
||||
case Evoral::OverlapInternal:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ def build(bld):
|
|||
obj.export_includes = ['.']
|
||||
obj.includes = ['.']
|
||||
obj.uselib = 'SIGCPP CAIROMM GTKMM BOOST'
|
||||
obj.use = [ 'libpbd', 'libevoral', 'libardour', 'libgtkmm2ext' ]
|
||||
obj.use = [ 'libpbd', 'libevoral', 'libardour', 'libgtkmm2ext', 'libevoral' ]
|
||||
obj.name = 'libcanvas'
|
||||
obj.target = 'canvas'
|
||||
obj.vnum = CANVAS_LIB_VERSION
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue