From c302f4d3792600c1b0628ef7757e832aeb7988fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 25 Mar 2020 18:16:39 +0100 Subject: [PATCH] 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 95b624955..88c07cc25 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 221c393f4..6f70546d2 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 da9a0da4c..bc2e744e4 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 d6a5dde7b..265c15318 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 0c6400d93..b98253275 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 */