gdk: Fix mapping between frame clock and stages

While each stage has at most a GdkFrameClock, the same GdkFrameClock
instance may drive multiple stages per frame. This means that the
mapping between a GdkFrameClock and a ClutterStage is a 1:M one, not a
1:1.

We should store a list of stages associated to each frame clock
instance, so that we can iterate over it when we need to update the
stages.

This commit fixes redraws of applications using multiple stages,
especially when using clutter-gtk.
This commit is contained in:
Emmanuele Bassi 2015-03-23 10:56:32 +00:00
parent 0bb5993b2b
commit 4f8643cea3

View File

@ -66,9 +66,13 @@ struct _ClutterMasterClockGdk
/* the list of timelines handled by the clock */ /* the list of timelines handled by the clock */
GSList *timelines; GSList *timelines;
/* mapping between ClutterStages and GdkFrameClocks */ /* mapping between ClutterStages and GdkFrameClocks.
GHashTable *clock_to_stage; *
* @stage_to_clock: a direct mapping because each stage has at most one clock
* @clock_to_stage: each clock can have more than one stage
*/
GHashTable *stage_to_clock; GHashTable *stage_to_clock;
GHashTable *clock_to_stage;
/* the current state of the clock, in usecs */ /* the current state of the clock, in usecs */
gint64 cur_tick; gint64 cur_tick;
@ -234,39 +238,48 @@ static void
clutter_master_clock_gdk_update (GdkFrameClock *frame_clock, clutter_master_clock_gdk_update (GdkFrameClock *frame_clock,
ClutterMasterClockGdk *master_clock) ClutterMasterClockGdk *master_clock)
{ {
ClutterStage *stage; GList *stages, *l;
CLUTTER_NOTE (SCHEDULER, "Master clock [tick]");
_clutter_threads_acquire_lock (); _clutter_threads_acquire_lock ();
stage = g_hash_table_lookup (master_clock->clock_to_stage, frame_clock);
/* Get the time to use for this frame */ /* Get the time to use for this frame */
master_clock->cur_tick = g_get_monotonic_time (); master_clock->cur_tick = g_get_monotonic_time ();
#ifdef CLUTTER_ENABLE_DEBUG #ifdef CLUTTER_ENABLE_DEBUG
/* Update the remaining budget */
master_clock->remaining_budget = master_clock->frame_budget; master_clock->remaining_budget = master_clock->frame_budget;
#endif #endif
/* Each frame is split into three separate phases: */ stages = g_hash_table_lookup (master_clock->clock_to_stage, frame_clock);
CLUTTER_NOTE (SCHEDULER, "Updating %d stages tied to frame clock %p",
/* 1. process all the events; goes through the stage's event queue g_list_length (stages), frame_clock);
* and processes each event according to its type, then emits the for (l = stages; l != NULL; l = l->next)
* various signals that are associated with the event
*/
master_clock_process_stage_events (master_clock, stage);
/* 2. advance the timelines */
master_clock_advance_timelines (master_clock);
/* 3. relayout and redraw the stage; the stage might have been
* destroyed in 1. when processing events, check whether it's
* still alive. */
if (g_hash_table_lookup (master_clock->clock_to_stage, frame_clock) != NULL)
{ {
master_clock_update_stage (master_clock, stage); ClutterStage *stage = l->data;
master_clock_schedule_stage_update (master_clock, stage, frame_clock);
CLUTTER_NOTE (SCHEDULER, "Master clock (stage:%p, clock:%p) [tick]", stage, frame_clock);
/* Each frame is split into three separate phases: */
/* 1. process all the events; goes through the stage's event queue
* and processes each event according to its type, then emits the
* various signals that are associated with the event
*/
master_clock_process_stage_events (master_clock, stage);
/* 2. advance the timelines */
master_clock_advance_timelines (master_clock);
/* 3. relayout and redraw the stage; the stage might have been
* destroyed in 1. when processing events, check whether it's
* still alive.
*/
if (g_hash_table_lookup (master_clock->stage_to_clock, stage) != NULL)
{
master_clock_update_stage (master_clock, stage);
master_clock_schedule_stage_update (master_clock, stage, frame_clock);
}
} }
master_clock->prev_tick = master_clock->cur_tick; master_clock->prev_tick = master_clock->cur_tick;
@ -279,15 +292,27 @@ clutter_master_clock_gdk_remove_stage_clock (ClutterMasterClockGdk *master_clock
ClutterStage *stage) ClutterStage *stage)
{ {
gpointer frame_clock = g_hash_table_lookup (master_clock->stage_to_clock, stage); gpointer frame_clock = g_hash_table_lookup (master_clock->stage_to_clock, stage);
if (frame_clock == NULL) GList *stages;
return;
g_signal_handlers_disconnect_by_func (frame_clock, if (frame_clock == NULL)
clutter_master_clock_gdk_update, return;
master_clock);
CLUTTER_NOTE (SCHEDULER, "Removing stage %p with clock %p", stage, frame_clock);
g_hash_table_remove (master_clock->stage_to_clock, stage); g_hash_table_remove (master_clock->stage_to_clock, stage);
g_hash_table_remove (master_clock->clock_to_stage, frame_clock);
stages = g_hash_table_lookup (master_clock->clock_to_stage, frame_clock);
if (stages != NULL)
{
stages = g_list_remove (stages, stage);
if (stages == NULL)
{
g_signal_handlers_disconnect_by_func (frame_clock,
clutter_master_clock_gdk_update,
master_clock);
g_hash_table_remove (master_clock->clock_to_stage, frame_clock);
}
}
} }
static void static void
@ -295,14 +320,26 @@ clutter_master_clock_gdk_add_stage_clock (ClutterMasterClockGdk *master_clock,
ClutterStage *stage, ClutterStage *stage,
GdkFrameClock *frame_clock) GdkFrameClock *frame_clock)
{ {
GList *stages;
clutter_master_clock_gdk_remove_stage_clock (master_clock, stage); clutter_master_clock_gdk_remove_stage_clock (master_clock, stage);
g_hash_table_insert (master_clock->stage_to_clock, stage, g_object_ref (frame_clock)); CLUTTER_NOTE (SCHEDULER, "Adding stage %p with clock %p", stage, frame_clock);
g_hash_table_insert (master_clock->clock_to_stage, g_object_ref (frame_clock), stage);
g_signal_connect (frame_clock, "update", g_hash_table_insert (master_clock->stage_to_clock, stage, g_object_ref (frame_clock));
G_CALLBACK (clutter_master_clock_gdk_update),
master_clock); stages = g_hash_table_lookup (master_clock->clock_to_stage, frame_clock);
if (stages == NULL)
{
g_hash_table_insert (master_clock->clock_to_stage, g_object_ref (frame_clock),
g_list_append (NULL, stage));
g_signal_connect (frame_clock, "update",
G_CALLBACK (clutter_master_clock_gdk_update),
master_clock);
}
else
stages = g_list_append (stages, stage);
if (master_clock->timelines != NULL) if (master_clock->timelines != NULL)
_clutter_master_clock_start_running ((ClutterMasterClock *) clock); _clutter_master_clock_start_running ((ClutterMasterClock *) clock);
@ -418,7 +455,7 @@ clutter_master_clock_gdk_init (ClutterMasterClockGdk *self)
const GSList *stages, *l; const GSList *stages, *l;
self->clock_to_stage = g_hash_table_new_full (g_direct_hash, g_direct_equal, self->clock_to_stage = g_hash_table_new_full (g_direct_hash, g_direct_equal,
g_object_unref, NULL); g_object_unref, (GDestroyNotify) g_list_free);
self->stage_to_clock = g_hash_table_new_full (g_direct_hash, g_direct_equal, self->stage_to_clock = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, g_object_unref); NULL, g_object_unref);
@ -433,8 +470,7 @@ clutter_master_clock_gdk_init (ClutterMasterClockGdk *self)
clutter_master_clock_gdk_stage_added (manager, l->data, self); clutter_master_clock_gdk_stage_added (manager, l->data, self);
if (G_UNLIKELY (clutter_paint_debug_flags & if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_CONTINUOUS_REDRAW))
CLUTTER_DEBUG_CONTINUOUS_REDRAW))
g_warning ("Continuous redraw is not supported with the GDK backend."); g_warning ("Continuous redraw is not supported with the GDK backend.");
} }