use CFRunLoopTimer to check the effect of plugin redrawing, not a glib idle
authorPaul Davis <paul@linuxaudiosystems.com>
Sat, 30 Apr 2016 17:19:56 +0000 (13:19 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Sat, 30 Apr 2016 17:20:08 +0000 (13:20 -0400)
gtk2_ardour/au_pluginui.h
gtk2_ardour/au_pluginui.mm

index 807d6b751aa5044d21a13e48d82e565c856b2346..d0dfac1ba972aa035e7c5cd8c3cde210a42464a3 100644 (file)
@@ -96,7 +96,6 @@ class AUPluginUI : public PlugUIBase, public Gtk::VBox
        void lower_box_unmap ();
        void lower_box_size_request (GtkRequisition*);
        void lower_box_size_allocate (Gtk::Allocation&);
-       bool lower_box_expose (GdkEventExpose*);
 
        void cocoa_view_resized ();
        void on_realize ();
@@ -165,10 +164,17 @@ class AUPluginUI : public PlugUIBase, public Gtk::VBox
 
        bool plugin_class_valid (Class pluginClass);
 
-       static bool idle_meter();
-       static int64_t last_idle;
-       static bool idle_meter_needed;
-       int64_t expose_cnt;
+       friend void au_cf_timer_callback (CFRunLoopTimerRef timer, void* info);
+       static CFRunLoopTimerRef   cf_timer;
+       static void cf_timer_callback ();
+       static int64_t last_timer;
+       static bool timer_needed;
+       static uint64_t timer_callbacks;
+       static uint64_t timer_out_of_range;
+
+  public:
+       static void start_cf_timer ();
+       static void stop_cf_timer ();
 };
 
 #endif /* __gtk2_ardour_auplugin_ui_h__  */
index edf3ee5ffbca3074bf42839b8e82fb4bee6c035d..4398ec7a90e6c3e614676074454c31d0b14b2ede 100644 (file)
@@ -50,8 +50,11 @@ using namespace std;
 using namespace PBD;
 
 vector<string> AUPluginUI::automation_mode_strings;
-bool AUPluginUI::idle_meter_needed = true;
-int64_t AUPluginUI::last_idle = 0;
+int64_t AUPluginUI::last_timer = 0;
+bool    AUPluginUI::timer_needed = true;
+CFRunLoopTimerRef AUPluginUI::cf_timer;
+uint64_t AUPluginUI::timer_callbacks = 0;
+uint64_t AUPluginUI::timer_out_of_range = 0;
 
 static const gchar* _automation_mode_strings[] = {
        X_("Manual"),
@@ -198,11 +201,16 @@ static IMP original_nsview_drawIfNeeded;
 static std::vector<id> plugin_views;
 static uint32_t block_plugin_redraws = 0;
 static const uint32_t minimum_redraw_rate = 25; /* frames per second */
-static const uint32_t block_plugin_redraw_count = 100; /* number of combined plugin redraws to block, if blocking */
+static const uint32_t block_plugin_redraw_count = 10; /* number of combined plugin redraws to block, if blocking */
 
 static void add_plugin_view (id view)
 {
+       if (plugin_views.empty()) {
+               AUPluginUI::start_cf_timer ();
+       }
+
        plugin_views.push_back (view);
+
 }
 
 static void remove_plugin_view (id view)
@@ -211,6 +219,9 @@ static void remove_plugin_view (id view)
        if (x != plugin_views.end()) {
                plugin_views.erase (x);
        }
+       if (plugin_views.empty()) {
+               AUPluginUI::stop_cf_timer ();
+       }
 }
 
 static void interposed_drawIfNeeded (id receiver, SEL selector, NSRect rect)
@@ -345,7 +356,6 @@ AUPluginUI::AUPluginUI (boost::shared_ptr<PluginInsert> insert)
        , cocoa_parent (0)
        , _notify (0)
        , _resize_notify (0)
-       , expose_cnt (0)
 {
        if (automation_mode_strings.empty()) {
                automation_mode_strings = I18N (_automation_mode_strings);
@@ -439,7 +449,6 @@ AUPluginUI::AUPluginUI (boost::shared_ptr<PluginInsert> insert)
                low_box.signal_size_allocate ().connect (mem_fun (this, &AUPluginUI::lower_box_size_allocate));
                low_box.signal_map ().connect (mem_fun (this, &AUPluginUI::lower_box_map));
                low_box.signal_unmap ().connect (mem_fun (this, &AUPluginUI::lower_box_unmap));
-               low_box.signal_expose_event ().connect (mem_fun (this, &AUPluginUI::lower_box_expose));
        }
 }
 
@@ -672,29 +681,78 @@ AUPluginUI::update_view_size ()
        last_au_frame = [au_view frame];
 }
 
-bool
-AUPluginUI::idle_meter ()
+
+void
+au_cf_timer_callback (CFRunLoopTimerRef timer, void* info)
+{
+       reinterpret_cast<AUPluginUI*> (info)->cf_timer_callback ();
+}
+
+void
+AUPluginUI::cf_timer_callback ()
 {
        int64_t now = ARDOUR::get_microseconds ();
+       timer_callbacks++;
 
-       if (!last_idle) {
-               last_idle = now;
-               return true; /* call me again */
+       if (!last_timer) {
+               last_timer = now;
+               return;
        }
 
-       if ((now - last_idle) > (1000000/minimum_redraw_rate)) {
-               block_plugin_redraws = block_plugin_redraw_count;
-               std::cerr << "Idle too slow (" << (now - last_idle) << " usecs), block plugin redraws for the next 10 plugin exposes\n";
-       } else {
-               std::cerr << "idle all good: elapsed was " << (now - last_idle) << endl;
+       const int64_t usecs_slop = 7500; /* 7.5 msec */
+
+       std::cerr << "Timer elapsed : " << now - last_timer << std::endl;
+
+       if ((now - last_timer) > (usecs_slop + (1000000/minimum_redraw_rate))) {
+               timer_out_of_range++;
        }
 
-       /* We've been called twice. Cancel everything for now. */
+       /* check timing roughly every second */
 
-       idle_meter_needed = true;
-       last_idle = 0;
+       if ((timer_callbacks % minimum_redraw_rate) == 0) {
+               std::cerr << "OOR check: " << timer_out_of_range << std::endl;
+               if (timer_out_of_range > (minimum_redraw_rate / 4)) {
+                       /* more than 25 % of the last second's worth of timers
+                          have been late. Take action.
+                       */
+                       block_plugin_redraws = block_plugin_redraw_count;
+                       std::cerr << "Timer too slow, block plugin redraws\n";
+               }
+               timer_out_of_range = 0;
+       }
 
-       return false;
+       last_timer = now;
+}
+
+void
+AUPluginUI::start_cf_timer ()
+{
+       if (!timer_needed) {
+               return;
+       }
+
+       CFTimeInterval interval = 1.0/25.0; /* secs => 40msec or 25fps */
+
+       cf_timer = CFRunLoopTimerCreate (kCFAllocatorDefault,
+                                        CFAbsoluteTimeGetCurrent() + interval,
+                                        interval, 0, 0,
+                                        au_cf_timer_callback,
+                                        0);
+
+       CFRunLoopAddTimer (CFRunLoopGetCurrent(), cf_timer, kCFRunLoopCommonModes);
+       timer_needed = false;
+}
+
+void
+AUPluginUI::stop_cf_timer ()
+{
+       if (timer_needed) {
+               return;
+       }
+
+       CFRunLoopRemoveTimer (CFRunLoopGetCurrent(), cf_timer, kCFRunLoopCommonModes);
+       timer_needed = true;
+       last_timer = 0;
 }
 
 void
@@ -1136,22 +1194,6 @@ AUPluginUI::lower_box_size_allocate (Gtk::Allocation& allocation)
        update_view_size ();
 }
 
-bool
-AUPluginUI::lower_box_expose (GdkEventExpose* event)
-{
-       ++expose_cnt;
-
-       if (!(expose_cnt % 10)) {
-               /* every 10 exposes, check how frequently idle is being called */
-               if (idle_meter_needed) {
-                       Glib::signal_idle().connect (sigc::ptr_fun (AUPluginUI::idle_meter));
-                       idle_meter_needed = false;
-               }
-       }
-
-       return false;
-}
-
 void
 AUPluginUI::on_window_hide ()
 {