diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 9c693e59e..59fa41f72 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -182,12 +182,12 @@ void clutter_redraw (ClutterStage *stage) { ClutterMainContext *ctx; + ClutterMasterClock *master_clock; static GTimer *timer = NULL; static guint timer_n_frames = 0; ctx = clutter_context_get_default (); - - CLUTTER_NOTE (PAINT, " Redraw enter for stage:%p", stage); + master_clock = _clutter_master_clock_get_default (); /* Before we can paint, we have to be sure we have the latest layout */ _clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage)); @@ -208,10 +208,15 @@ clutter_redraw (ClutterStage *stage) /* Call through to the actual backend to do the painting down from * the stage. It will likely need to swap buffers, vblank sync etc - * which will be windowing system dependant. + * which will be windowing system dependent */ _clutter_backend_redraw (ctx->backend, stage); + /* prepare for the next frame; if anything queues a redraw as the + * result of a timeline, this will end up redrawing the scene + */ + _clutter_master_clock_advance (master_clock); + /* Complete FPS info */ if (G_UNLIKELY (clutter_get_show_fps ())) { @@ -225,7 +230,6 @@ clutter_redraw (ClutterStage *stage) } } - CLUTTER_NOTE (PAINT, " Redraw leave for stage:%p", stage); CLUTTER_TIMESTAMP (SCHEDULER, "Redraw finish for stage:%p", stage); } @@ -1052,6 +1056,8 @@ clutter_context_get_default (void) ctx->is_initialized = FALSE; ctx->motion_events_per_actor = TRUE; + ctx->master_clock = _clutter_master_clock_get_default (); + #ifdef CLUTTER_ENABLE_DEBUG ctx->timer = g_timer_new (); g_timer_start (ctx->timer); diff --git a/clutter/clutter-master-clock.c b/clutter/clutter-master-clock.c index a417dbbca..8b10238f2 100644 --- a/clutter/clutter-master-clock.c +++ b/clutter/clutter-master-clock.c @@ -43,18 +43,31 @@ #define CLUTTER_IS_MASTER_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_MASTER_CLOCK)) #define CLUTTER_MASTER_CLASS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_MASTER_CLOCK, ClutterMasterClockClass)) +typedef struct _ClutterClockSource ClutterClockSource; typedef struct _ClutterMasterClockClass ClutterMasterClockClass; struct _ClutterMasterClock { GObject parent_instance; + /* the list of timelines handled by the clock */ GSList *timelines; + /* the previous state of the clock, used to compute + * the delta + */ GTimeVal prev_tick; gulong msecs_delta; - gulong tick_id; + /* an idle source, used by the Master Clock to queue + * a redraw on the stage and drive the animations + */ + GSource *source; + + /* a guard, so that we dispatch the last redraw even + * after the last timeline has been completed + */ + guint last_advance : 1; }; struct _ClutterMasterClockClass @@ -62,36 +75,135 @@ struct _ClutterMasterClockClass GObjectClass parent_class; }; -static ClutterMasterClock *default_clock = NULL; +struct _ClutterClockSource +{ + GSource source; -static void on_timeline_start (ClutterTimeline *timeline, + ClutterMasterClock *master_clock; +}; + +static void on_timeline_started (ClutterTimeline *timeline, ClutterMasterClock *master_clock); static void on_timeline_completed (ClutterTimeline *timeline, ClutterMasterClock *master_clock); static void on_timeline_paused (ClutterTimeline *timeline, ClutterMasterClock *master_clock); +static gboolean clutter_clock_prepare (GSource *source, + gint *timeout); +static gboolean clutter_clock_check (GSource *source); +static gboolean clutter_clock_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data); + +static ClutterMasterClock *default_clock = NULL; + +static GSourceFuncs clock_funcs = { + clutter_clock_prepare, + clutter_clock_check, + clutter_clock_dispatch, + NULL +}; + G_DEFINE_TYPE (ClutterMasterClock, clutter_master_clock, G_TYPE_OBJECT); +static gboolean +_clutter_master_clock_has_running_timeline (ClutterMasterClock *master_clock) +{ + GSList *l; + + if (master_clock->last_advance) + return TRUE; + + if (master_clock->timelines == NULL) + return FALSE; + + for (l = master_clock->timelines; l != NULL; l = l->next) + { + if (clutter_timeline_is_playing (l->data)) + return TRUE; + } + + return FALSE; +} + +static GSource * +clutter_clock_source_new (ClutterMasterClock *master_clock) +{ + GSource *source = g_source_new (&clock_funcs, sizeof (ClutterClockSource)); + ClutterClockSource *clock_source = (ClutterClockSource *) source; + + clock_source->master_clock = master_clock; + + return source; +} + +static gboolean +clutter_clock_prepare (GSource *source, + gint *timeout) +{ + ClutterClockSource *clock_source = (ClutterClockSource *) source; + ClutterMasterClock *master_clock = clock_source->master_clock; + gboolean retval; + + *timeout = -1; + + retval = _clutter_master_clock_has_running_timeline (master_clock); + + return retval; +} + +static gboolean +clutter_clock_check (GSource *source) +{ + ClutterClockSource *clock_source = (ClutterClockSource *) source; + ClutterMasterClock *master_clock = clock_source->master_clock; + gboolean retval; + + retval = _clutter_master_clock_has_running_timeline (master_clock); + + return retval; +} + +static gboolean +clutter_clock_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + ClutterClockSource *clock_source = (ClutterClockSource *) source; + ClutterMasterClock *master_clock = clock_source->master_clock; + ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); + GSList *stages, *l; + + CLUTTER_NOTE (SCHEDULER, "Master clock [tick]"); + + stages = clutter_stage_manager_list_stages (stage_manager); + + for (l = stages; l != NULL; l = l->next) + clutter_actor_queue_redraw (l->data); + + g_slist_free (stages); + + if (master_clock->last_advance) + { + master_clock->last_advance = FALSE; + master_clock->prev_tick.tv_sec = 0; + } + + return TRUE; +} + static void timeline_weak_ref (gpointer data, GObject *object_pointer) { ClutterMasterClock *master_clock = data; - master_clock->timelines = g_slist_remove (master_clock->timelines, - object_pointer); + master_clock->timelines = + g_slist_remove (master_clock->timelines, object_pointer); if (master_clock->timelines == NULL) - { - if (master_clock->tick_id != 0) - { - g_source_remove (master_clock->tick_id); - master_clock->tick_id = 0; - } - - master_clock->prev_tick.tv_sec = 0; - } + master_clock->prev_tick.tv_sec = 0; } static void @@ -100,12 +212,6 @@ clutter_master_clock_finalize (GObject *gobject) ClutterMasterClock *master_clock = CLUTTER_MASTER_CLOCK (gobject); GSList *l; - if (master_clock->tick_id != 0) - { - g_source_remove (master_clock->tick_id); - master_clock->tick_id = 0; - } - for (l = master_clock->timelines; l != NULL; l = l->next) { ClutterTimeline *timeline = l->data; @@ -115,7 +221,7 @@ clutter_master_clock_finalize (GObject *gobject) master_clock); g_signal_handlers_disconnect_by_func (timeline, - G_CALLBACK (on_timeline_start), + G_CALLBACK (on_timeline_started), master_clock); g_signal_handlers_disconnect_by_func (timeline, G_CALLBACK (on_timeline_completed), @@ -141,6 +247,14 @@ clutter_master_clock_class_init (ClutterMasterClockClass *klass) static void clutter_master_clock_init (ClutterMasterClock *self) { + GSource *source; + + source = clutter_clock_source_new (self); + self->source = source; + + g_source_set_priority (source, CLUTTER_PRIORITY_REDRAW); + g_source_set_can_recurse (source, FALSE); + g_source_attach (source, NULL); } /* @@ -163,80 +277,34 @@ _clutter_master_clock_get_default (void) return default_clock; } -static gboolean -master_clock_tick (gpointer data) -{ - ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); - GSList *stages, *l; - - CLUTTER_NOTE (SCHEDULER, "Master clock [tick]"); - - stages = clutter_stage_manager_list_stages (stage_manager); - - for (l = stages; l != NULL; l = l->next) - clutter_actor_queue_redraw (l->data); - - g_slist_free (stages); - - return TRUE; -} - -static gboolean -has_running_timeline (ClutterMasterClock *master_clock) -{ - GSList *l; - - for (l = master_clock->timelines; l != NULL; l = l->next) - { - if (clutter_timeline_is_playing (l->data)) - return TRUE; - } - - return FALSE; -} - static void -on_timeline_start (ClutterTimeline *timeline, - ClutterMasterClock *master_clock) +on_timeline_started (ClutterTimeline *timeline, + ClutterMasterClock *master_clock) { - if (has_running_timeline (master_clock) && - master_clock->tick_id == 0) - { - master_clock->prev_tick.tv_sec = 0; - - master_clock->tick_id = - clutter_threads_add_frame_source (clutter_get_default_frame_rate (), - master_clock_tick, - master_clock); - } + if (!_clutter_master_clock_has_running_timeline (master_clock)) + master_clock->prev_tick.tv_sec = 0; } static void on_timeline_completed (ClutterTimeline *timeline, ClutterMasterClock *master_clock) { - if (!has_running_timeline (master_clock) && - master_clock->tick_id != 0) - { - master_clock->prev_tick.tv_sec = 0; - - g_source_remove (master_clock->tick_id); - master_clock->tick_id = 0; - } + /* if this is the last timeline we need to turn :last-advance + * on in order to queue the redraw of the scene for the last + * frame; otherwise the ClockSource will fail the prepare and + * check phases and the last frame will not be painted + */ + if (!_clutter_master_clock_has_running_timeline (master_clock)) + master_clock->last_advance = TRUE; } static void on_timeline_paused (ClutterTimeline *timeline, ClutterMasterClock *master_clock) { - if (!has_running_timeline (master_clock) && - master_clock->tick_id != 0) - { - master_clock->prev_tick.tv_sec = 0; - - g_source_remove (master_clock->tick_id); - master_clock->tick_id = 0; - } + /* see the comment in on_timeline_completed */ + if (!_clutter_master_clock_has_running_timeline (master_clock)) + master_clock->last_advance = TRUE; } /* @@ -267,7 +335,7 @@ _clutter_master_clock_add_timeline (ClutterMasterClock *master_clock, master_clock); g_signal_connect (timeline, "started", - G_CALLBACK (on_timeline_start), + G_CALLBACK (on_timeline_started), master_clock); g_signal_connect (timeline, "completed", G_CALLBACK (on_timeline_completed), @@ -301,7 +369,7 @@ _clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock, master_clock); g_signal_handlers_disconnect_by_func (timeline, - G_CALLBACK (on_timeline_start), + G_CALLBACK (on_timeline_started), master_clock); g_signal_handlers_disconnect_by_func (timeline, G_CALLBACK (on_timeline_completed), @@ -311,15 +379,7 @@ _clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock, master_clock); if (master_clock->timelines == NULL) - { - if (master_clock->tick_id != 0) - { - g_source_remove (master_clock->tick_id); - master_clock->tick_id = 0; - } - - master_clock->prev_tick.tv_sec = 0; - } + master_clock->prev_tick.tv_sec = 0; } /* @@ -339,6 +399,9 @@ _clutter_master_clock_advance (ClutterMasterClock *master_clock) g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock)); + if (master_clock->timelines == NULL) + return; + g_get_current_time (&cur_tick); if (master_clock->prev_tick.tv_sec == 0) diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 768ab9dbc..30870ee62 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -44,6 +44,7 @@ #include "clutter-event.h" #include "clutter-feature.h" #include "clutter-id-pool.h" +#include "clutter-master-clock.h" #include "clutter-stage-manager.h" #include "clutter-stage-window.h" #include "clutter-stage.h" @@ -129,6 +130,7 @@ struct _ClutterMainContext guint32 last_event_time; + ClutterMasterClock *master_clock; gulong redraw_count; }; diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index d5235550c..d34f43f6a 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -379,20 +379,22 @@ redraw_update_idle (gpointer user_data) */ _clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage)); + /* redrawing will advance the master clock */ CLUTTER_NOTE (PAINT, "redrawing via idle for stage[%p]", stage); clutter_redraw (stage); + /* reset the guard, so that new redraws are possible */ + priv->update_idle = 0; + if (CLUTTER_CONTEXT ()->redraw_count > 0) { - CLUTTER_NOTE (PAINT, "Queued %lu redraws during the last cycle", + CLUTTER_NOTE (SCHEDULER, "Queued %lu redraws during the last cycle", CLUTTER_CONTEXT ()->redraw_count); CLUTTER_CONTEXT ()->redraw_count = 0; } - priv->update_idle = 0; - - return retval; + return FALSE; } static void