[mutter] frame-clock: Make it possible to drive timelines



commit c302f4d3792600c1b0628ef7757e832aeb7988fa
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Wed Mar 25 18:16:39 2020 +0100

    frame-clock: Make it possible to drive timelines
    
    Add API to add and remove ClutterTimeline objects to the frame clock.
    Just as the legacy master clock, having a timeline added to the frame
    clock causes the frame clock to continuously reschedule updates until
    the timeline is removed.
    
    ClutterTimeline is adapted to be able to be driven by a
    ClutterFrameClock. This is done by adding a 'frame-clock' property, and
    if set, the timeline will add and remove itself to the frame clock
    instead of the master clock.
    
    https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285

 clutter/clutter/clutter-frame-clock.c |  75 ++++++++++++++++++++++-
 clutter/clutter/clutter-frame-clock.h |   6 ++
 clutter/clutter/clutter-timeline.c    | 111 +++++++++++++++++++++++++++++++---
 clutter/clutter/clutter-timeline.h    |  11 ++++
 clutter/clutter/clutter-types.h       |   1 +
 5 files changed, 196 insertions(+), 8 deletions(-)
---
diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c
index 95b6249550..88c07cc254 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
@@ -20,6 +20,7 @@
 #include "clutter/clutter-frame-clock.h"
 
 #include "clutter/clutter-main.h"
+#include "clutter/clutter-timeline-private.h"
 #include "cogl/cogl-trace.h"
 
 static inline uint64_t
@@ -80,15 +81,83 @@ struct _ClutterFrameClock
   gboolean pending_reschedule_now;
 
   int inhibit_count;
+
+  GList *timelines;
 };
 
 G_DEFINE_TYPE (ClutterFrameClock, clutter_frame_clock,
                G_TYPE_OBJECT)
 
+void
+clutter_frame_clock_add_timeline (ClutterFrameClock *frame_clock,
+                                  ClutterTimeline   *timeline)
+{
+  gboolean is_first;
+
+  if (g_list_find (frame_clock->timelines, timeline))
+    return;
+
+  is_first = !frame_clock->timelines;
+
+  frame_clock->timelines = g_list_prepend (frame_clock->timelines, timeline);
+
+  if (is_first)
+    clutter_frame_clock_schedule_update (frame_clock);
+}
+
+void
+clutter_frame_clock_remove_timeline (ClutterFrameClock *frame_clock,
+                                     ClutterTimeline   *timeline)
+{
+  frame_clock->timelines = g_list_remove (frame_clock->timelines, timeline);
+}
+
+static void
+advance_timelines (ClutterFrameClock *frame_clock,
+                   int64_t            time_us)
+{
+  GList *timelines;
+  GList *l;
+
+  /* we protect ourselves from timelines being removed during
+   * the advancement by other timelines by copying the list of
+   * timelines, taking a reference on them, iterating over the
+   * copied list and then releasing the reference.
+   *
+   * we cannot simply take a reference on the timelines and still
+   * use the list held by the master clock because the do_tick()
+   * might result in the creation of a new timeline, which gets
+   * added at the end of the list with no reference increase and
+   * thus gets disposed at the end of the iteration.
+   *
+   * this implies that a newly added timeline will not be advanced
+   * by this clock iteration, which is perfectly fine since we're
+   * in its first cycle.
+   *
+   * we also cannot steal the frame clock timelines list because
+   * a timeline might be removed as the direct result of do_tick()
+   * and remove_timeline() would not find the timeline, failing
+   * and leaving a dangling pointer behind.
+   */
+
+  timelines = g_list_copy (frame_clock->timelines);
+  g_list_foreach (timelines, (GFunc) g_object_ref, NULL);
+
+  for (l = timelines; l; l = l->next)
+    {
+      ClutterTimeline *timeline = l->data;
+
+      _clutter_timeline_do_tick (timeline, time_us / 1000);
+    }
+
+  g_list_free_full (timelines, g_object_unref);
+}
+
 static void
 maybe_reschedule_update (ClutterFrameClock *frame_clock)
 {
-  if (frame_clock->pending_reschedule)
+  if (frame_clock->pending_reschedule ||
+      frame_clock->timelines)
     {
       frame_clock->pending_reschedule = FALSE;
 
@@ -331,6 +400,10 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
     }
   COGL_TRACE_END (ClutterFrameClockEvents);
 
+  COGL_TRACE_BEGIN (ClutterFrameClockTimelines, "Frame Clock (timelines)");
+  advance_timelines (frame_clock, time_us);
+  COGL_TRACE_END (ClutterFrameClockTimelines);
+
   COGL_TRACE_BEGIN (ClutterFrameClockFrame, "Frame Clock (frame)");
   result = frame_clock->listener.iface->frame (frame_clock,
                                                frame_count,
diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h
index 221c393f4a..6f70546d2a 100644
--- a/clutter/clutter/clutter-frame-clock.h
+++ b/clutter/clutter/clutter-frame-clock.h
@@ -68,4 +68,10 @@ void clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock);
 CLUTTER_EXPORT
 void clutter_frame_clock_uninhibit (ClutterFrameClock *frame_clock);
 
+void clutter_frame_clock_add_timeline (ClutterFrameClock *frame_clock,
+                                       ClutterTimeline   *timeline);
+
+void clutter_frame_clock_remove_timeline (ClutterFrameClock *frame_clock,
+                                          ClutterTimeline   *timeline);
+
 #endif /* CLUTTER_FRAME_CLOCK_H */
diff --git a/clutter/clutter/clutter-timeline.c b/clutter/clutter/clutter-timeline.c
index da9a0da4c8..bc2e744e4b 100644
--- a/clutter/clutter/clutter-timeline.c
+++ b/clutter/clutter/clutter-timeline.c
@@ -99,6 +99,7 @@
 #include "clutter-debug.h"
 #include "clutter-easing.h"
 #include "clutter-enum-types.h"
+#include "clutter-frame-clock.h"
 #include "clutter-main.h"
 #include "clutter-marshal.h"
 #include "clutter-master-clock.h"
@@ -110,6 +111,8 @@ struct _ClutterTimelinePrivate
 {
   ClutterTimelineDirection direction;
 
+  ClutterFrameClock *frame_clock;
+
   guint delay_id;
 
   /* The total length in milliseconds of this timeline */
@@ -177,6 +180,7 @@ enum
   PROP_AUTO_REVERSE,
   PROP_REPEAT_COUNT,
   PROP_PROGRESS_MODE,
+  PROP_FRAME_CLOCK,
 
   PROP_LAST
 };
@@ -453,6 +457,10 @@ clutter_timeline_set_property (GObject      *object,
       clutter_timeline_set_progress_mode (timeline, g_value_get_enum (value));
       break;
 
+    case PROP_FRAME_CLOCK:
+      clutter_timeline_set_frame_clock (timeline, g_value_get_object (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -494,6 +502,10 @@ clutter_timeline_get_property (GObject    *object,
       g_value_set_enum (value, priv->progress_mode);
       break;
 
+    case PROP_FRAME_CLOCK:
+      g_value_set_object (value, priv->frame_clock);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -505,17 +517,27 @@ clutter_timeline_finalize (GObject *object)
 {
   ClutterTimeline *self = CLUTTER_TIMELINE (object);
   ClutterTimelinePrivate *priv = self->priv;
-  ClutterMasterClock *master_clock;
 
   if (priv->markers_by_name)
     g_hash_table_destroy (priv->markers_by_name);
 
   if (priv->is_playing)
     {
-      master_clock = _clutter_master_clock_get_default ();
-      _clutter_master_clock_remove_timeline (master_clock, self);
+      if (priv->frame_clock)
+        {
+          clutter_frame_clock_remove_timeline (priv->frame_clock, self);
+        }
+      else
+        {
+          ClutterMasterClock *master_clock;
+
+          master_clock = _clutter_master_clock_get_default ();
+          _clutter_master_clock_remove_timeline (master_clock, self);
+        }
     }
 
+  g_clear_object (&priv->frame_clock);
+
   G_OBJECT_CLASS (clutter_timeline_parent_class)->finalize (object);
 }
 
@@ -643,6 +665,18 @@ clutter_timeline_class_init (ClutterTimelineClass *klass)
                        CLUTTER_LINEAR,
                        CLUTTER_PARAM_READWRITE);
 
+  /**
+   * ClutterTimeline:frame-clock:
+   *
+   * The frame clock driving the timeline.
+   */
+  obj_props[PROP_FRAME_CLOCK] =
+    g_param_spec_object ("frame-clock",
+                         "Frame clock",
+                         "Frame clock driving the timeline",
+                         CLUTTER_TYPE_FRAME_CLOCK,
+                         G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE);
+
   object_class->dispose = clutter_timeline_dispose;
   object_class->finalize = clutter_timeline_finalize;
   object_class->set_property = clutter_timeline_set_property;
@@ -925,7 +959,6 @@ set_is_playing (ClutterTimeline *timeline,
                 gboolean         is_playing)
 {
   ClutterTimelinePrivate *priv = timeline->priv;
-  ClutterMasterClock *master_clock;
 
   is_playing = !!is_playing;
 
@@ -934,15 +967,29 @@ set_is_playing (ClutterTimeline *timeline,
 
   priv->is_playing = is_playing;
 
-  master_clock = _clutter_master_clock_get_default ();
   if (priv->is_playing)
     {
       priv->waiting_first_tick = TRUE;
       priv->current_repeat = 0;
-      _clutter_master_clock_add_timeline (master_clock, timeline);
+    }
+
+  if (priv->frame_clock)
+    {
+      if (priv->is_playing)
+        clutter_frame_clock_add_timeline (priv->frame_clock, timeline);
+      else
+        clutter_frame_clock_remove_timeline (priv->frame_clock, timeline);
     }
   else
-    _clutter_master_clock_remove_timeline (master_clock, timeline);
+    {
+      ClutterMasterClock *master_clock;
+
+      master_clock = _clutter_master_clock_get_default ();
+      if (priv->is_playing)
+        _clutter_master_clock_add_timeline (master_clock, timeline);
+      else
+        _clutter_master_clock_remove_timeline (master_clock, timeline);
+    }
 }
 
 static gboolean
@@ -1339,6 +1386,26 @@ clutter_timeline_new (guint duration_ms)
                        NULL);
 }
 
+/**
+ * clutter_timeline_new_for_frame_clock:
+ * @frame_clock: The #ClutterFrameClock the timeline is driven by
+ * @duration_ms: Duration of the timeline in milliseconds
+ *
+ * Creates a new #ClutterTimeline with a duration of @duration milli seconds.
+ *
+ * Return value: the newly created #ClutterTimeline instance. Use
+ *   g_object_unref() when done using it
+ */
+ClutterTimeline *
+clutter_timeline_new_for_frame_clock (ClutterFrameClock *frame_clock,
+                                      unsigned int       duration_ms)
+{
+  return g_object_new (CLUTTER_TYPE_TIMELINE,
+                       "duration", duration_ms,
+                       "frame-clock", frame_clock,
+                       NULL);
+}
+
 /**
  * clutter_timeline_get_delay:
  * @timeline: a #ClutterTimeline
@@ -2419,3 +2486,33 @@ clutter_timeline_get_cubic_bezier_progress (ClutterTimeline  *timeline,
 
   return TRUE;
 }
+
+/**
+ * clutter_timeline_get_frame_clock: (skip)
+ */
+ClutterFrameClock *
+clutter_timeline_get_frame_clock (ClutterTimeline *timeline)
+{
+  g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
+
+  return timeline->priv->frame_clock;
+}
+
+void
+clutter_timeline_set_frame_clock (ClutterTimeline   *timeline,
+                                  ClutterFrameClock *frame_clock)
+{
+  ClutterTimelinePrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
+
+  priv = timeline->priv;
+
+  if (priv->frame_clock == frame_clock)
+    return;
+
+  g_set_object (&priv->frame_clock, frame_clock);
+
+  g_object_notify_by_pspec (G_OBJECT (timeline),
+                            obj_props[PROP_FRAME_CLOCK]);
+}
diff --git a/clutter/clutter/clutter-timeline.h b/clutter/clutter/clutter-timeline.h
index d6a5dde7b2..265c15318e 100644
--- a/clutter/clutter/clutter-timeline.h
+++ b/clutter/clutter/clutter-timeline.h
@@ -121,6 +121,10 @@ GType clutter_timeline_get_type (void) G_GNUC_CONST;
 CLUTTER_EXPORT
 ClutterTimeline *               clutter_timeline_new                            (guint                     
duration_ms);
 
+CLUTTER_EXPORT
+ClutterTimeline *               clutter_timeline_new_for_frame_clock            (ClutterFrameClock        
*frame_clock,
+                                                                                 unsigned int              
duration_ms);
+
 CLUTTER_EXPORT
 guint                           clutter_timeline_get_duration                   (ClutterTimeline          
*timeline);
 CLUTTER_EXPORT
@@ -221,6 +225,13 @@ gint64                          clutter_timeline_get_duration_hint
 CLUTTER_EXPORT
 gint                            clutter_timeline_get_current_repeat             (ClutterTimeline          
*timeline);
 
+CLUTTER_EXPORT
+ClutterFrameClock *             clutter_timeline_get_frame_clock                (ClutterTimeline           
*timeline);
+
+CLUTTER_EXPORT
+void                            clutter_timeline_set_frame_clock                (ClutterTimeline           
*timeline,
+                                                                                 ClutterFrameClock         
*frame_clock);
+
 G_END_DECLS
 
 #endif /* _CLUTTER_TIMELINE_H__ */
diff --git a/clutter/clutter/clutter-types.h b/clutter/clutter/clutter-types.h
index 0c6400d93e..b982532755 100644
--- a/clutter/clutter/clutter-types.h
+++ b/clutter/clutter/clutter-types.h
@@ -57,6 +57,7 @@ typedef struct _ClutterActorIter                ClutterActorIter;
 typedef struct _ClutterPaintNode                ClutterPaintNode;
 typedef struct _ClutterContent                  ClutterContent; /* dummy */
 typedef struct _ClutterScrollActor             ClutterScrollActor;
+typedef struct _ClutterFrameClock               ClutterFrameClock;
 
 typedef struct _ClutterInterval                ClutterInterval;
 typedef struct _ClutterAnimatable              ClutterAnimatable; /* dummy */


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