several changes, major and minor, to improve threaded waveview rendering
authorPaul Davis <paul@linuxaudiosystems.com>
Tue, 23 Jun 2015 18:26:10 +0000 (14:26 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 23 Jun 2015 18:26:26 +0000 (14:26 -0400)
libs/canvas/canvas/wave_view.h
libs/canvas/wave_view.cc
libs/canvas/wscript

index dc3fe4e13ce160c57f391cef7c2006ae7320d7b8..c4e19ec7bb353df0dd035b1db5e1e7eab1f72ccf 100644 (file)
@@ -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;
 
index 3a04961d129bee9c91c5afd7bea0e411cb8f97de..7b00b0eae78367c9ffcf5ce5c3081999d012d254 100644 (file)
@@ -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));
 
-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 */
+                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 ();
+                }
+       }
 }
 
-
 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;
-               }
-
-               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.
-                        */
-
-                       if ((e->end - e->start) > max_coverage) {
-                               best_partial = e;
-                               max_coverage = e->end - e->start;
-                       }
+                        full_coverage = true;
+                        return e;
+
+                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;
+
+                case Evoral::OverlapNone:
+                case Evoral::OverlapEnd:
+                case Evoral::OverlapInternal:
+                        break;
                }
        }
 
index 2c4cca850e20e7e3b0d2bfa7ec7b9bb64b663dea..3d741ec9241afb517979ce0c5401e1f5bf3d4967 100644 (file)
@@ -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