master-clock: Add frame budget diagnostic messages

Let's keep a budget of 16.6 milliseconds per frame, and reduce it by the
amount of time spent in each phase of the frame processing. If any phase
goes over the allocated budget then we use the diagnostic mode
facilities to warn the app developer.
This commit is contained in:
Emmanuele Bassi 2012-03-06 13:50:10 +00:00
parent bdf9f49588
commit c2e7704151

View File

@ -46,6 +46,19 @@
#define CLUTTER_IS_MASTER_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_MASTER_CLOCK)) #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)) #define CLUTTER_MASTER_CLASS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_MASTER_CLOCK, ClutterMasterClockClass))
#ifdef CLUTTER_ENABLE_DEBUG
#define clutter_warn_if_over_budget(master_clock,start_time,section) G_STMT_START { \
gint64 __delta = g_get_monotonic_time () - start_time; \
gint64 __budget = master_clock->remaining_budget; \
if (__budget > 0 && __delta >= __budget) { \
_clutter_diagnostic_message ("%s took %" G_GINT64_FORMAT " microseconds " \
"over a budget of %" G_GINT64_FORMAT " microseconds", \
section, __delta, __budget); \
} } G_STMT_END
#else
#define clutter_warn_if_over_budget(master_clock,start_time,section)
#endif
typedef struct _ClutterClockSource ClutterClockSource; typedef struct _ClutterClockSource ClutterClockSource;
typedef struct _ClutterMasterClockClass ClutterMasterClockClass; typedef struct _ClutterMasterClockClass ClutterMasterClockClass;
@ -62,6 +75,11 @@ struct _ClutterMasterClock
/* the previous state of the clock, in usecs, used to compute the delta */ /* the previous state of the clock, in usecs, used to compute the delta */
gint64 prev_tick; gint64 prev_tick;
#ifdef CLUTTER_ENABLE_DEBUG
gint64 frame_budget;
gint64 remaining_budget;
#endif
/* an idle source, used by the Master Clock to queue /* an idle source, used by the Master Clock to queue
* a redraw on the stage and drive the animations * a redraw on the stage and drive the animations
*/ */
@ -242,6 +260,9 @@ master_clock_process_events (ClutterMasterClock *master_clock,
GSList *stages) GSList *stages)
{ {
GSList *l; GSList *l;
#ifdef CLUTTER_ENABLE_DEBUG
gint64 start = g_get_monotonic_time ();
#endif
CLUTTER_STATIC_TIMER (master_event_process, CLUTTER_STATIC_TIMER (master_event_process,
"Master Clock", "Master Clock",
@ -263,6 +284,13 @@ master_clock_process_events (ClutterMasterClock *master_clock,
} }
CLUTTER_TIMER_STOP (_clutter_uprof_context, master_event_process); CLUTTER_TIMER_STOP (_clutter_uprof_context, master_event_process);
#ifdef CLUTTER_ENABLE_DEBUG
if (_clutter_diagnostic_enabled ())
clutter_warn_if_over_budget (master_clock, start, "Event processing");
master_clock->remaining_budget -= (g_get_monotonic_time () - start);
#endif
} }
/* /*
@ -277,6 +305,9 @@ static void
master_clock_advance_timelines (ClutterMasterClock *master_clock) master_clock_advance_timelines (ClutterMasterClock *master_clock)
{ {
GSList *timelines, *l; GSList *timelines, *l;
#ifdef CLUTTER_ENABLE_DEBUG
gint64 start = g_get_monotonic_time ();
#endif
CLUTTER_STATIC_TIMER (master_timeline_advance, CLUTTER_STATIC_TIMER (master_timeline_advance,
"Master Clock", "Master Clock",
@ -316,6 +347,13 @@ master_clock_advance_timelines (ClutterMasterClock *master_clock)
g_slist_foreach (timelines, (GFunc) g_object_unref, NULL); g_slist_foreach (timelines, (GFunc) g_object_unref, NULL);
g_slist_free (timelines); g_slist_free (timelines);
#ifdef CLUTTER_ENABLE_DEBUG
if (_clutter_diagnostic_enabled ())
clutter_warn_if_over_budget (master_clock, start, "Animations");
master_clock->remaining_budget -= (g_get_monotonic_time () - start);
#endif
} }
static gboolean static gboolean
@ -324,6 +362,9 @@ master_clock_update_stages (ClutterMasterClock *master_clock,
{ {
gboolean stages_updated = FALSE; gboolean stages_updated = FALSE;
GSList *l; GSList *l;
#ifdef CLUTTER_ENABLE_DEBUG
gint64 start = g_get_monotonic_time ();
#endif
_clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_PRE_PAINT); _clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_PRE_PAINT);
@ -347,6 +388,13 @@ master_clock_update_stages (ClutterMasterClock *master_clock,
_clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_POST_PAINT); _clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_POST_PAINT);
#ifdef CLUTTER_ENABLE_DEBUG
if (_clutter_diagnostic_enabled ())
clutter_warn_if_over_budget (master_clock, start, "Updating the stage");
master_clock->remaining_budget -= (g_get_monotonic_time () - start);
#endif
return stages_updated; return stages_updated;
} }
@ -444,6 +492,10 @@ clutter_clock_dispatch (GSource *source,
/* Get the time to use for this frame */ /* Get the time to use for this frame */
master_clock->cur_tick = g_source_get_time (source); master_clock->cur_tick = g_source_get_time (source);
#ifdef CLUTTER_ENABLE_DEBUG
master_clock->remaining_budget = master_clock->frame_budget;
#endif
/* We need to protect ourselves against stages being destroyed during /* We need to protect ourselves against stages being destroyed during
* event handling * event handling
*/ */
@ -512,6 +564,8 @@ clutter_master_clock_init (ClutterMasterClock *self)
self->idle = FALSE; self->idle = FALSE;
self->ensure_next_iteration = FALSE; self->ensure_next_iteration = FALSE;
self->frame_budget = G_USEC_PER_SEC / 60;
g_source_set_priority (source, CLUTTER_PRIORITY_REDRAW); g_source_set_priority (source, CLUTTER_PRIORITY_REDRAW);
g_source_set_can_recurse (source, FALSE); g_source_set_can_recurse (source, FALSE);
g_source_attach (source, NULL); g_source_attach (source, NULL);
@ -608,4 +662,3 @@ _clutter_master_clock_ensure_next_iteration (ClutterMasterClock *master_clock)
master_clock->ensure_next_iteration = TRUE; master_clock->ensure_next_iteration = TRUE;
} }