mirror of
https://github.com/brl/mutter.git
synced 2024-12-25 04:22:05 +00:00
Limit the frame rate when not syncing to VBLANK
clutter-master-clock.c clutter-master-clock.h: When the SYNC_TO_VBLANK feature is not available, wait for 1/frame_rate seconds since the start of the last frame before drawing the next frame. Add _clutter_master_clock_start_running() to abstract the usage of g_main_context_wakeup() clutter-stage.c: Add _clutter_master_clock_start_running() clutter-main.c: Update docs for clutter_set_default_frame_rate() clutter_get_default_frame_rate() to no longer talk about timeline frame rates. test-text-perf.c test-text.c: Set a frame rate of 1000fps so that frame-rate limiting doesn't affect the result. http://bugzilla.openedhand.com/show_bug.cgi?id=1637 Signed-off-by: Emmanuele Bassi <ebassi@linux.intel.com>
This commit is contained in:
parent
64bb2e694f
commit
dcd8d28314
@ -2293,8 +2293,7 @@ clutter_base_init (void)
|
|||||||
/**
|
/**
|
||||||
* clutter_get_default_frame_rate:
|
* clutter_get_default_frame_rate:
|
||||||
*
|
*
|
||||||
* Retrieves the default frame rate used when creating #ClutterTimeline<!--
|
* Retrieves the default frame rate. See clutter_set_default_frame_rate().
|
||||||
* -->s.
|
|
||||||
*
|
*
|
||||||
* Return value: the default frame rate
|
* Return value: the default frame rate
|
||||||
*
|
*
|
||||||
@ -2314,8 +2313,10 @@ clutter_get_default_frame_rate (void)
|
|||||||
* clutter_set_default_frame_rate:
|
* clutter_set_default_frame_rate:
|
||||||
* @frames_per_sec: the new default frame rate
|
* @frames_per_sec: the new default frame rate
|
||||||
*
|
*
|
||||||
* Sets the default frame rate to be used when creating #ClutterTimeline<!--
|
* Sets the default frame rate. This frame rate will be used to limit
|
||||||
* -->s
|
* the number of frames drawn if Clutter is not able to synchronize
|
||||||
|
* with the vertical refresh rate of the display. When synchronization
|
||||||
|
* is possible, this value is ignored.
|
||||||
*
|
*
|
||||||
* Since: 0.6
|
* Since: 0.6
|
||||||
*/
|
*/
|
||||||
|
@ -53,6 +53,10 @@ struct _ClutterMasterClock
|
|||||||
/* the list of timelines handled by the clock */
|
/* the list of timelines handled by the clock */
|
||||||
GSList *timelines;
|
GSList *timelines;
|
||||||
|
|
||||||
|
/* the current state of the clock
|
||||||
|
*/
|
||||||
|
GTimeVal cur_tick;
|
||||||
|
|
||||||
/* the previous state of the clock, used to compute
|
/* the previous state of the clock, used to compute
|
||||||
* the delta
|
* the delta
|
||||||
*/
|
*/
|
||||||
@ -62,6 +66,8 @@ struct _ClutterMasterClock
|
|||||||
* a redraw on the stage and drive the animations
|
* a redraw on the stage and drive the animations
|
||||||
*/
|
*/
|
||||||
GSource *source;
|
GSource *source;
|
||||||
|
|
||||||
|
guint timelines_running : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _ClutterMasterClockClass
|
struct _ClutterMasterClockClass
|
||||||
@ -122,6 +128,59 @@ master_clock_is_running (ClutterMasterClock *master_clock)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* master_clock_next_frame_delay:
|
||||||
|
* @master_clock: a #ClutterMasterClock
|
||||||
|
*
|
||||||
|
* Computes the number of delay before we need to draw the next frame.
|
||||||
|
*
|
||||||
|
* Return value: -1 if there is no next frame pending, otherwise the
|
||||||
|
* number of millseconds before the we need to draw the next frame
|
||||||
|
*/
|
||||||
|
static gint
|
||||||
|
master_clock_next_frame_delay (ClutterMasterClock *master_clock)
|
||||||
|
{
|
||||||
|
GTimeVal now;
|
||||||
|
GTimeVal next;
|
||||||
|
|
||||||
|
if (!master_clock_is_running (master_clock))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (clutter_feature_available (CLUTTER_FEATURE_SYNC_TO_VBLANK))
|
||||||
|
{
|
||||||
|
/* When we have sync-to-vblank, we count on that to throttle
|
||||||
|
* our frame rate, and otherwise draw frames as fast as possible.
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (master_clock->prev_tick.tv_sec == 0)
|
||||||
|
{
|
||||||
|
/* If we weren't previously running, then draw the next frame
|
||||||
|
* immediately
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, wait at least 1/frame_rate seconds since we last started a frame */
|
||||||
|
|
||||||
|
g_source_get_current_time (master_clock->source, &now);
|
||||||
|
|
||||||
|
next = master_clock->prev_tick;
|
||||||
|
g_time_val_add (&next, 1000000 / clutter_get_default_frame_rate ());
|
||||||
|
|
||||||
|
if (next.tv_sec < now.tv_sec ||
|
||||||
|
(next.tv_sec == now.tv_sec && next.tv_usec < now.tv_usec))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ((next.tv_sec - now.tv_sec) * 1000 +
|
||||||
|
(next.tv_usec - now.tv_usec) / 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* clutter_clock_source_new:
|
* clutter_clock_source_new:
|
||||||
* @master_clock: a #ClutterMasterClock for the source
|
* @master_clock: a #ClutterMasterClock for the source
|
||||||
@ -150,14 +209,13 @@ clutter_clock_prepare (GSource *source,
|
|||||||
{
|
{
|
||||||
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
||||||
ClutterMasterClock *master_clock = clock_source->master_clock;
|
ClutterMasterClock *master_clock = clock_source->master_clock;
|
||||||
gboolean retval;
|
int delay;
|
||||||
|
|
||||||
/* just like an idle source, we are ready if nothing else is */
|
delay = master_clock_next_frame_delay (master_clock);
|
||||||
*timeout = -1;
|
|
||||||
|
|
||||||
retval = master_clock_is_running (master_clock);
|
*timeout = delay;
|
||||||
|
|
||||||
return retval;
|
return delay == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@ -165,11 +223,11 @@ clutter_clock_check (GSource *source)
|
|||||||
{
|
{
|
||||||
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
ClutterClockSource *clock_source = (ClutterClockSource *) source;
|
||||||
ClutterMasterClock *master_clock = clock_source->master_clock;
|
ClutterMasterClock *master_clock = clock_source->master_clock;
|
||||||
gboolean retval;
|
int delay;
|
||||||
|
|
||||||
retval = master_clock_is_running (master_clock);
|
delay = master_clock_next_frame_delay (master_clock);
|
||||||
|
|
||||||
return retval;
|
return delay == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@ -184,6 +242,10 @@ clutter_clock_dispatch (GSource *source,
|
|||||||
|
|
||||||
CLUTTER_NOTE (SCHEDULER, "Master clock [tick]");
|
CLUTTER_NOTE (SCHEDULER, "Master clock [tick]");
|
||||||
|
|
||||||
|
/* Get the time to use for this frame.
|
||||||
|
*/
|
||||||
|
g_source_get_current_time (source, &master_clock->cur_tick);
|
||||||
|
|
||||||
/* We need to protect ourselves against stages being destroyed during
|
/* We need to protect ourselves against stages being destroyed during
|
||||||
* event handling
|
* event handling
|
||||||
*/
|
*/
|
||||||
@ -206,6 +268,8 @@ clutter_clock_dispatch (GSource *source,
|
|||||||
g_slist_foreach (stages, (GFunc)g_object_unref, NULL);
|
g_slist_foreach (stages, (GFunc)g_object_unref, NULL);
|
||||||
g_slist_free (stages);
|
g_slist_free (stages);
|
||||||
|
|
||||||
|
master_clock->prev_tick = master_clock->cur_tick;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,15 +347,7 @@ _clutter_master_clock_add_timeline (ClutterMasterClock *master_clock,
|
|||||||
timeline);
|
timeline);
|
||||||
|
|
||||||
if (is_first)
|
if (is_first)
|
||||||
{
|
_clutter_master_clock_start_running (master_clock);
|
||||||
/* Start timing from scratch */
|
|
||||||
master_clock->prev_tick.tv_sec = 0;
|
|
||||||
|
|
||||||
/* If called from a different thread, we need to wake up the
|
|
||||||
* main loop to start running the timelines
|
|
||||||
*/
|
|
||||||
g_main_context_wakeup (NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -308,6 +364,24 @@ _clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock,
|
|||||||
{
|
{
|
||||||
master_clock->timelines = g_slist_remove (master_clock->timelines,
|
master_clock->timelines = g_slist_remove (master_clock->timelines,
|
||||||
timeline);
|
timeline);
|
||||||
|
if (master_clock->timelines == NULL)
|
||||||
|
master_clock->timelines_running = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _clutter_master_clock_start_running:
|
||||||
|
* @master_clock: a #ClutterMasterClock
|
||||||
|
*
|
||||||
|
* Called when we have events or redraws to process; if the clock
|
||||||
|
* is stopped, does the processing necessary to wake it up again.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_clutter_master_clock_start_running (ClutterMasterClock *master_clock)
|
||||||
|
{
|
||||||
|
/* If called from a different thread, we need to wake up the
|
||||||
|
* main loop to start running the timelines
|
||||||
|
*/
|
||||||
|
g_main_context_wakeup (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -321,7 +395,6 @@ _clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock,
|
|||||||
void
|
void
|
||||||
_clutter_master_clock_advance (ClutterMasterClock *master_clock)
|
_clutter_master_clock_advance (ClutterMasterClock *master_clock)
|
||||||
{
|
{
|
||||||
GTimeVal cur_tick = { 0, };
|
|
||||||
gulong msecs;
|
gulong msecs;
|
||||||
GSList *l;
|
GSList *l;
|
||||||
|
|
||||||
@ -330,13 +403,18 @@ _clutter_master_clock_advance (ClutterMasterClock *master_clock)
|
|||||||
if (master_clock->timelines == NULL)
|
if (master_clock->timelines == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
g_get_current_time (&cur_tick);
|
if (!master_clock->timelines_running)
|
||||||
|
{
|
||||||
|
/* When we start running, we count the first frame as time 0,
|
||||||
|
* so we don't need an advance. (And master_clock->prev_tick
|
||||||
|
* doesn't meaningfully relate to these timelines.)
|
||||||
|
*/
|
||||||
|
master_clock->timelines_running = TRUE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (master_clock->prev_tick.tv_sec == 0)
|
msecs = (master_clock->cur_tick.tv_sec - master_clock->prev_tick.tv_sec) * 1000
|
||||||
master_clock->prev_tick = cur_tick;
|
+ (master_clock->cur_tick.tv_usec - master_clock->prev_tick.tv_usec) / 1000;
|
||||||
|
|
||||||
msecs = (cur_tick.tv_sec - master_clock->prev_tick.tv_sec) * 1000
|
|
||||||
+ (cur_tick.tv_usec - master_clock->prev_tick.tv_usec) / 1000;
|
|
||||||
|
|
||||||
if (msecs == 0)
|
if (msecs == 0)
|
||||||
return;
|
return;
|
||||||
@ -352,9 +430,4 @@ _clutter_master_clock_advance (ClutterMasterClock *master_clock)
|
|||||||
if (clutter_timeline_is_playing (timeline))
|
if (clutter_timeline_is_playing (timeline))
|
||||||
clutter_timeline_advance_delta (timeline, msecs);
|
clutter_timeline_advance_delta (timeline, msecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* store the previous state so that we can use
|
|
||||||
* it for the next advancement
|
|
||||||
*/
|
|
||||||
master_clock->prev_tick = cur_tick;
|
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,8 @@ void _clutter_master_clock_add_timeline (ClutterMasterClock *m
|
|||||||
void _clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock,
|
void _clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock,
|
||||||
ClutterTimeline *timeline);
|
ClutterTimeline *timeline);
|
||||||
void _clutter_master_clock_advance (ClutterMasterClock *master_clock);
|
void _clutter_master_clock_advance (ClutterMasterClock *master_clock);
|
||||||
|
void _clutter_master_clock_start_running (ClutterMasterClock *master_clock);
|
||||||
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
@ -396,13 +396,22 @@ _clutter_stage_queue_event (ClutterStage *stage,
|
|||||||
ClutterEvent *event)
|
ClutterEvent *event)
|
||||||
{
|
{
|
||||||
ClutterStagePrivate *priv;
|
ClutterStagePrivate *priv;
|
||||||
|
gboolean first_event;
|
||||||
|
|
||||||
g_return_if_fail (CLUTTER_IS_STAGE (stage));
|
g_return_if_fail (CLUTTER_IS_STAGE (stage));
|
||||||
|
|
||||||
priv = stage->priv;
|
priv = stage->priv;
|
||||||
|
|
||||||
|
first_event = priv->event_queue->length == 0;
|
||||||
|
|
||||||
g_queue_push_tail (priv->event_queue,
|
g_queue_push_tail (priv->event_queue,
|
||||||
clutter_event_copy (event));
|
clutter_event_copy (event));
|
||||||
|
|
||||||
|
if (first_event)
|
||||||
|
{
|
||||||
|
ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
|
||||||
|
_clutter_master_clock_start_running (master_clock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
@ -544,13 +553,12 @@ clutter_stage_real_queue_redraw (ClutterActor *actor,
|
|||||||
|
|
||||||
if (!priv->redraw_pending)
|
if (!priv->redraw_pending)
|
||||||
{
|
{
|
||||||
|
ClutterMasterClock *master_clock;
|
||||||
|
|
||||||
priv->redraw_pending = TRUE;
|
priv->redraw_pending = TRUE;
|
||||||
|
|
||||||
/* If called from a thread, we need to wake up the main loop
|
master_clock = _clutter_master_clock_get_default ();
|
||||||
* out of its sleep so the clock source notices that we have
|
_clutter_master_clock_start_running (master_clock);
|
||||||
* a redraw pending
|
|
||||||
*/
|
|
||||||
g_main_context_wakeup (NULL);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
CLUTTER_CONTEXT ()->redraw_count += 1;
|
CLUTTER_CONTEXT ()->redraw_count += 1;
|
||||||
|
@ -77,6 +77,7 @@ main (int argc, char *argv[])
|
|||||||
int row, col;
|
int row, col;
|
||||||
|
|
||||||
g_setenv ("CLUTTER_VBLANK", "none", FALSE);
|
g_setenv ("CLUTTER_VBLANK", "none", FALSE);
|
||||||
|
g_setenv ("CLUTTER_DEFAULT_FPS", "1000", FALSE);
|
||||||
|
|
||||||
clutter_init (&argc, &argv);
|
clutter_init (&argc, &argv);
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ main (int argc, char *argv[])
|
|||||||
ClutterActor *group;
|
ClutterActor *group;
|
||||||
|
|
||||||
g_setenv ("CLUTTER_VBLANK", "none", FALSE);
|
g_setenv ("CLUTTER_VBLANK", "none", FALSE);
|
||||||
|
g_setenv ("CLUTTER_DEFAULT_FPS", "1000", FALSE);
|
||||||
|
|
||||||
clutter_init (&argc, &argv);
|
clutter_init (&argc, &argv);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user