[sysprof/wip/visualizers] line-visualizer: remove legacy drawing code



commit 0a9193ab700af05066d883e9ffd54ecc1f5d0569
Author: Christian Hergert <chergert redhat com>
Date:   Sun Sep 25 15:37:00 2016 -0700

    line-visualizer: remove legacy drawing code
    
    This provides the plumbing to do the threaded drawing, we just
    need to write the capture cursor and draw operations from the
    pixman/cairo worker thread (and do so safely).

 lib/sp-line-visualizer-row.c |  324 ++++++++++++++++++++----------------------
 1 files changed, 155 insertions(+), 169 deletions(-)
---
diff --git a/lib/sp-line-visualizer-row.c b/lib/sp-line-visualizer-row.c
index d359472..bd53601 100644
--- a/lib/sp-line-visualizer-row.c
+++ b/lib/sp-line-visualizer-row.c
@@ -24,30 +24,24 @@
 
 typedef struct
 {
-  gint64                time;
-  SpCaptureCounterValue value;
-} LineDataItem;
-
-typedef struct
-{
-  guint   id;
-  guint   type;
-  GArray *values;
-} LineData;
-
-typedef struct
-{
   SpCaptureReader *reader;
 
   GHashTable      *counters;
   GtkLabel        *label;
 
-  gint64           start_time;
-  gint64           duration;
-
-  guint            needs_recalc : 1;
+  cairo_surface_t *surface;
+  guint            draw_sequence;
 } SpLineVisualizerRowPrivate;
 
+typedef struct
+{
+  gint64 begin_time;
+  gint64 end_time;
+  gint width;
+  gint height;
+  guint sequence;
+} DrawRequest;
+
 G_DEFINE_TYPE_WITH_PRIVATE (SpLineVisualizerRow, sp_line_visualizer_row, SP_TYPE_VISUALIZER_ROW)
 
 enum {
@@ -59,139 +53,115 @@ enum {
 static GParamSpec *properties [N_PROPS];
 
 static void
-line_data_free (gpointer data)
+sp_line_visualizer_row_draw_worker (GTask        *task,
+                                    gpointer      source_object,
+                                    gpointer      task_data,
+                                    GCancellable *cancellable)
 {
-  LineData *ld = data;
-
-  if (ld != NULL)
-    {
-      g_array_unref (ld->values);
-      g_free (ld);
-    }
-}
-
-static void
-sp_line_visualizer_row_recalc (SpLineVisualizerRow *self)
-{
-  SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
-  GHashTable *new_data;
-  SpCaptureFrame frame;
+  SpLineVisualizerRow *self = source_object;
+  DrawRequest *draw = task_data;
+  cairo_surface_t *surface = NULL;
+  cairo_t *cr = NULL;
 
   g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
+  g_assert (G_IS_TASK (task));
+  g_assert (draw != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   /*
-   * TODO:
+   * Create our image surface. We do this in the local thread and use
+   * an image surface so that we can work around the lack of thread
+   * safety in cairo and pixman. They are mostly thread unaware though,
+   * so as long as we are really careful, we are okay.
    *
-   * We want to create an index that gives us quick access to this
-   * data without quite so much overhead that we have now. The 16
-   * bytes per data-point is pretty high.
+   * We draw using ARGB32 because the GtkListBoxRow will be doing it's
+   * own pixel caching. This means we can concentrate on the line data
+   * and ignore having to deal with backgrounds and such.
    */
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, draw->width, draw->height);
 
-  priv->needs_recalc = FALSE;
-
-  if (priv->reader == NULL)
-    return;
-
-  sp_capture_reader_reset (priv->reader);
-
-  priv->start_time = sp_capture_reader_get_start_time (priv->reader);
-  priv->duration = 0;
-
-  new_data = g_hash_table_new_full (NULL, NULL, NULL, line_data_free);
-
-  while (sp_capture_reader_peek_frame (priv->reader, &frame))
+  if (surface == NULL)
     {
-      gint64 relative;
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               "Failed to create image surface of size %dx%d",
+                               draw->width, draw->height);
+      goto cleanup;
+    }
 
-      if (frame.time < priv->start_time)
-        frame.time = priv->start_time;
+  cr = cairo_create (surface);
 
-      relative = frame.time - priv->start_time;
-      if (relative > priv->duration)
-        priv->duration = relative;
+  if (cr == NULL)
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               "Failed to create cairo context");
+      goto cleanup;
+    }
 
-      if (frame.type == SP_CAPTURE_FRAME_CTRDEF)
-        {
-          const SpCaptureFrameCounterDefine *def;
-          guint i;
-
-          if (NULL == (def = sp_capture_reader_read_counter_define (priv->reader)))
-            return;
-
-          for (i = 0; i < def->n_counters; i++)
-            {
-              const SpCaptureCounter *ctr = &def->counters[i];
-
-              if (g_hash_table_contains (priv->counters, GSIZE_TO_POINTER (ctr->id)))
-                {
-                  LineData *ld = g_hash_table_lookup (new_data, GSIZE_TO_POINTER (ctr->id));
-                  LineDataItem item;
-
-                  if (ld == NULL)
-                    {
-                      ld = g_new (LineData, 1);
-                      ld->id = ctr->id;
-                      ld->type = ctr->type;
-                      ld->values = g_array_new (FALSE, FALSE, sizeof (LineDataItem));
-                      g_hash_table_insert (new_data, GSIZE_TO_POINTER (ctr->id), ld);
-                    }
-
-                  item.time = MAX (priv->start_time, def->frame.time) - priv->start_time;
-                  item.value = ctr->value;
-
-                  g_array_append_val (ld->values, item);
-                }
-            }
-        }
-      else if (frame.type == SP_CAPTURE_FRAME_CTRSET)
-        {
-          const SpCaptureFrameCounterSet *set;
-          LineDataItem item;
-          guint i;
+  g_task_return_pointer (task, g_steal_pointer (&surface), (GDestroyNotify)cairo_surface_destroy);
 
-          if (NULL == (set = sp_capture_reader_read_counter_set (priv->reader)))
-            return;
+cleanup:
+  if (surface != NULL)
+    cairo_surface_destroy (surface);
 
-          item.time = MAX (priv->start_time, set->frame.time) - priv->start_time;
+  if (cr != NULL)
+    cairo_destroy (cr);
+}
 
-          for (i = 0; i < set->n_values; i++)
-            {
-              const SpCaptureCounterValues *values = &set->values[i];
-              guint j;
+static void
+complete_draw (GObject      *object,
+               GAsyncResult *result,
+               gpointer      user_data)
+{
+  SpLineVisualizerRow *self = (SpLineVisualizerRow *)object;
+  SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
+  DrawRequest *draw;
+  GTask *task = (GTask *)result;
+  cairo_surface_t *surface;
 
-              for (j = 0; j < G_N_ELEMENTS (values->values); j++)
-                {
-                  LineData *ld;
+  g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
+  g_assert (G_IS_TASK (task));
 
-                  if (values->ids[j] == 0)
-                    break;
+  draw = g_task_get_task_data (task);
+  surface = g_task_propagate_pointer (task, NULL);
 
-                  ld = g_hash_table_lookup (new_data, GSIZE_TO_POINTER (values->ids[j]));
+  if (surface != NULL && draw->sequence == priv->draw_sequence)
+    {
+      g_clear_pointer (&priv->surface, cairo_surface_destroy);
+      priv->surface = surface;
+      gtk_widget_queue_draw (GTK_WIDGET (self));
+    }
+}
 
-                  /* possible there was no matching ctrdef */
-                  if (ld == NULL)
-                    continue;
+static void
+sp_line_visualizer_row_begin_draw (SpLineVisualizerRow *self)
+{
+  SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
+  g_autoptr(GTask) task = NULL;
+  DrawRequest *draw;
+  gint64 begin_time;
+  gint64 end_time;
+  GtkAllocation alloc;
 
-                  item.value = values->values[j];
+  g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
 
-                  g_array_append_val (ld->values, item);
-                }
-            }
-        }
-      else if (!sp_capture_reader_skip (priv->reader))
-        return;
-    }
+  sp_visualizer_row_get_time_range (SP_VISUALIZER_ROW (self), &begin_time, &end_time);
+  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
 
-  g_clear_pointer (&priv->counters, g_hash_table_unref);
-  priv->counters = new_data;
-}
+  draw = g_new (DrawRequest, 1);
+  draw->begin_time = begin_time;
+  draw->end_time = end_time;
+  draw->width = alloc.width;
+  draw->height = alloc.height;
+  draw->sequence = ++priv->draw_sequence;
 
-static inline gdouble
-calc_y (SpLineVisualizerRow   *self,
-        LineData              *ld,
-        SpCaptureCounterValue  value)
-{
-  return value.vdbl / 100.0;
+  task = g_task_new (self, NULL, complete_draw, NULL);
+  g_task_set_source_tag (task, sp_line_visualizer_row_begin_draw);
+  g_task_set_task_data (task, draw, g_free);
+  g_task_run_in_thread (task, sp_line_visualizer_row_draw_worker);
 }
 
 static gboolean
@@ -201,52 +171,42 @@ sp_line_visualizer_row_draw (GtkWidget *widget,
   SpLineVisualizerRow *self = (SpLineVisualizerRow *)widget;
   SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
   GtkAllocation alloc;
-  GHashTableIter iter;
-  gdouble duration;
   gboolean ret;
-  gpointer key, val;
 
   g_assert (SP_IS_LINE_VISUALIZER_ROW (widget));
   g_assert (cr != NULL);
 
-  if (priv->needs_recalc)
-    sp_line_visualizer_row_recalc (self);
-
   gtk_widget_get_allocation (widget, &alloc);
 
   ret = GTK_WIDGET_CLASS (sp_line_visualizer_row_parent_class)->draw (widget, cr);
 
-  duration = (gdouble)priv->duration;
-
-  g_hash_table_iter_init (&iter, priv->counters);
-
-  while (g_hash_table_iter_next (&iter, &key, &val))
+  if (ret == GDK_EVENT_PROPAGATE && priv->surface != NULL)
     {
-      LineData *ld = val;
-
-      cairo_save (cr);
-      cairo_move_to (cr, 0, alloc.height);
-
-      for (guint i = 0; i < ld->values->len; i++)
+      gint width;
+      gint height;
+
+      width = cairo_image_surface_get_width (priv->surface);
+      height = cairo_image_surface_get_height (priv->surface);
+
+      /*
+       * We must have another threaded draw queued, so to give the impression
+       * that we are quickly growing with the widget, we will scale the draw
+       * and then paint our wrongly sized surface (which will likely be a bit
+       * blurred, but that's okay).
+       */
+      if (width != alloc.width || height != alloc.height)
         {
-          LineDataItem *item = &g_array_index (ld->values, LineDataItem, i);
-          gdouble x;
-          gdouble y;
-
-          x = (item->time / duration) * alloc.width;
-          y = alloc.height - ((item->value.vdbl / 100.0) * alloc.height);
 
-          cairo_line_to (cr, x, y);
+          return ret;
         }
 
-      cairo_line_to (cr, alloc.width, alloc.height);
-      cairo_line_to (cr, 0, alloc.height);
-
-      SP_LINE_VISUALIZER_ROW_GET_CLASS (self)->prepare (self, cr, ld->id);
-
-      cairo_stroke_preserve (cr);
+      /*
+       * This is our ideal path, where we have a 1-to-1 match of our backing
+       * surface matching the widgets current allocation.
+       */
+      cairo_rectangle (cr, 0, 0, alloc.width, alloc.height);
+      cairo_set_source_surface (cr, priv->surface, 0, 0);
       cairo_fill (cr);
-      cairo_restore (cr);
     }
 
   return ret;
@@ -267,17 +227,10 @@ sp_line_visualizer_row_set_reader (SpVisualizerRow *row,
         {
           sp_capture_reader_unref (priv->reader);
           priv->reader = NULL;
-          priv->start_time = 0;
-          priv->duration = 0;
         }
 
       if (reader != NULL)
-        {
-          priv->reader = sp_capture_reader_ref (reader);
-          priv->start_time = 0;
-          priv->duration = 0;
-          priv->needs_recalc = TRUE;
-        }
+        priv->reader = sp_capture_reader_ref (reader);
 
       gtk_widget_queue_draw (GTK_WIDGET (self));
     }
@@ -308,6 +261,33 @@ sp_line_visualizer_row_finalize (GObject *object)
 }
 
 static void
+sp_line_visualizer_row_size_allocate (GtkWidget     *widget,
+                                      GtkAllocation *alloc)
+{
+  SpLineVisualizerRow *self = (SpLineVisualizerRow *)widget;
+
+  g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
+  g_assert (alloc != NULL);
+
+  GTK_WIDGET_CLASS (sp_line_visualizer_row_parent_class)->size_allocate (widget, alloc);
+
+  sp_line_visualizer_row_begin_draw (self);
+}
+
+static void
+sp_line_visualizer_row_destroy (GtkWidget *widget)
+{
+  SpLineVisualizerRow *self = (SpLineVisualizerRow *)widget;
+  SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
+
+  g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
+
+  g_clear_pointer (&priv->surface, cairo_surface_destroy);
+
+  GTK_WIDGET_CLASS (sp_line_visualizer_row_parent_class)->destroy (widget);
+}
+
+static void
 sp_line_visualizer_row_get_property (GObject    *object,
                                      guint       prop_id,
                                      GValue     *value,
@@ -361,6 +341,8 @@ sp_line_visualizer_row_class_init (SpLineVisualizerRowClass *klass)
   object_class->set_property = sp_line_visualizer_row_set_property;
 
   widget_class->draw = sp_line_visualizer_row_draw;
+  widget_class->destroy = sp_line_visualizer_row_destroy;
+  widget_class->size_allocate = sp_line_visualizer_row_size_allocate;
 
   visualizer_class->set_reader = sp_line_visualizer_row_set_reader;
 
@@ -409,9 +391,13 @@ sp_line_visualizer_row_add_counter (SpLineVisualizerRow *self,
   if (SP_LINE_VISUALIZER_ROW_GET_CLASS (self)->counter_added)
     SP_LINE_VISUALIZER_ROW_GET_CLASS (self)->counter_added (self, counter_id);
 
-  priv->needs_recalc = TRUE;
-
-  gtk_widget_queue_draw (GTK_WIDGET (self));
+  /*
+   * We use queue_resize so that a size_allocate() is called
+   * to kick off the new threaded draw of the widget. Otherwise,
+   * we'd have to do our own queuing, so this simplifies things
+   * a small bit.
+   */
+  gtk_widget_queue_resize (GTK_WIDGET (self));
 }
 
 void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]