mirror of
https://github.com/brl/mutter.git
synced 2024-12-24 20:12:06 +00:00
Add clutter_stage_set_sync_delay()
New experimental API is added to allow changing the way that redraws are timed for a stage to include a "sync delay" - a period after the vertical blanking period where Clutter simply waits for updates. In detail, the algorithm is that when the master clock is restarted after drawing a frame (in the case where there are timelines running) or started fresh in response to a queued redraw or relayout, the start is scheduled at the next sync point (sync_delay ms after the predicted vblank period) rather than done immediately. https://bugzilla.gnome.org/show_bug.cgi?id=692901
This commit is contained in:
parent
d0d1b562bd
commit
b248941af5
@ -139,24 +139,9 @@ master_clock_is_running (ClutterMasterClock *master_clock)
|
|||||||
{
|
{
|
||||||
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
||||||
const GSList *stages, *l;
|
const GSList *stages, *l;
|
||||||
gboolean stage_free = FALSE;
|
|
||||||
|
|
||||||
stages = clutter_stage_manager_peek_stages (stage_manager);
|
stages = clutter_stage_manager_peek_stages (stage_manager);
|
||||||
|
|
||||||
/* If all of the stages are busy waiting for a swap-buffers to complete
|
|
||||||
* then we stop the master clock... */
|
|
||||||
for (l = stages; l != NULL; l = l->next)
|
|
||||||
{
|
|
||||||
if (_clutter_stage_get_pending_swaps (l->data) == 0)
|
|
||||||
{
|
|
||||||
stage_free = TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stage_free)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (master_clock->timelines)
|
if (master_clock->timelines)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
@ -176,6 +161,103 @@ master_clock_is_running (ClutterMasterClock *master_clock)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
master_clock_get_swap_wait_time (ClutterMasterClock *master_clock)
|
||||||
|
{
|
||||||
|
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
||||||
|
const GSList *stages, *l;
|
||||||
|
gint64 min_update_time = -1;
|
||||||
|
|
||||||
|
stages = clutter_stage_manager_peek_stages (stage_manager);
|
||||||
|
|
||||||
|
for (l = stages; l != NULL; l = l->next)
|
||||||
|
{
|
||||||
|
gint64 update_time = _clutter_stage_get_update_time (l->data);
|
||||||
|
if (min_update_time == -1 ||
|
||||||
|
(update_time != -1 && update_time < min_update_time))
|
||||||
|
min_update_time = update_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min_update_time == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gint64 now = g_source_get_time (master_clock->source);
|
||||||
|
if (min_update_time < now)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gint64 delay_us = min_update_time - now;
|
||||||
|
return (delay_us + 999) / 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
master_clock_schedule_stage_updates (ClutterMasterClock *master_clock)
|
||||||
|
{
|
||||||
|
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
||||||
|
const GSList *stages, *l;
|
||||||
|
|
||||||
|
stages = clutter_stage_manager_peek_stages (stage_manager);
|
||||||
|
|
||||||
|
for (l = stages; l != NULL; l = l->next)
|
||||||
|
_clutter_stage_schedule_update (l->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *
|
||||||
|
master_clock_list_ready_stages (ClutterMasterClock *master_clock)
|
||||||
|
{
|
||||||
|
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
||||||
|
const GSList *stages, *l;
|
||||||
|
GSList *result;
|
||||||
|
|
||||||
|
stages = clutter_stage_manager_peek_stages (stage_manager);
|
||||||
|
|
||||||
|
result = NULL;
|
||||||
|
for (l = stages; l != NULL; l = l->next)
|
||||||
|
{
|
||||||
|
gint64 update_time = _clutter_stage_get_update_time (l->data);
|
||||||
|
|
||||||
|
/* If a stage has a swap-buffers pending we don't want to draw to it
|
||||||
|
* in case the driver may block the CPU while it waits for the next
|
||||||
|
* backbuffer to become available.
|
||||||
|
*
|
||||||
|
* TODO: We should be able to identify if we are running triple or N
|
||||||
|
* buffered and in these cases we can still draw if there is 1 swap
|
||||||
|
* pending so we can hopefully always be ready to swap for the next
|
||||||
|
* vblank and really match the vsync frequency.
|
||||||
|
*/
|
||||||
|
if (update_time != -1 && update_time <= master_clock->cur_tick)
|
||||||
|
result = g_slist_prepend (result, g_object_ref (l->data));
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_slist_reverse (result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
master_clock_reschedule_stage_updates (ClutterMasterClock *master_clock,
|
||||||
|
GSList *stages)
|
||||||
|
{
|
||||||
|
const GSList *l;
|
||||||
|
|
||||||
|
for (l = stages; l != NULL; l = l->next)
|
||||||
|
{
|
||||||
|
/* Clear the old update time */
|
||||||
|
_clutter_stage_clear_update_time (l->data);
|
||||||
|
|
||||||
|
/* And if there is still work to be done, schedule a new one */
|
||||||
|
if (master_clock->timelines ||
|
||||||
|
_clutter_stage_has_queued_events (l->data) ||
|
||||||
|
_clutter_stage_needs_update (l->data))
|
||||||
|
_clutter_stage_schedule_update (l->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* master_clock_next_frame_delay:
|
* master_clock_next_frame_delay:
|
||||||
* @master_clock: a #ClutterMasterClock
|
* @master_clock: a #ClutterMasterClock
|
||||||
@ -189,10 +271,17 @@ static gint
|
|||||||
master_clock_next_frame_delay (ClutterMasterClock *master_clock)
|
master_clock_next_frame_delay (ClutterMasterClock *master_clock)
|
||||||
{
|
{
|
||||||
gint64 now, next;
|
gint64 now, next;
|
||||||
|
gint swap_delay;
|
||||||
|
|
||||||
if (!master_clock_is_running (master_clock))
|
if (!master_clock_is_running (master_clock))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
/* If all of the stages are busy waiting for a swap-buffers to complete
|
||||||
|
* then we wait for one to be ready.. */
|
||||||
|
swap_delay = master_clock_get_swap_wait_time (master_clock);
|
||||||
|
if (swap_delay != 0)
|
||||||
|
return swap_delay;
|
||||||
|
|
||||||
/* When we have sync-to-vblank, we count on swap-buffer requests (or
|
/* When we have sync-to-vblank, we count on swap-buffer requests (or
|
||||||
* swap-buffer-complete events if supported in the backend) to throttle our
|
* swap-buffer-complete events if supported in the backend) to throttle our
|
||||||
* frame rate so no additional delay is needed to start the next frame.
|
* frame rate so no additional delay is needed to start the next frame.
|
||||||
@ -274,14 +363,7 @@ master_clock_process_events (ClutterMasterClock *master_clock,
|
|||||||
|
|
||||||
/* Process queued events */
|
/* Process queued events */
|
||||||
for (l = stages; l != NULL; l = l->next)
|
for (l = stages; l != NULL; l = l->next)
|
||||||
{
|
|
||||||
/* NB: If a stage is busy waiting for a swap-buffers completion then
|
|
||||||
* we don't process its events so we can maximize the benefits of
|
|
||||||
* motion compression, and avoid multiple picks per frame.
|
|
||||||
*/
|
|
||||||
if (_clutter_stage_get_pending_swaps (l->data) == 0)
|
|
||||||
_clutter_stage_process_queued_events (l->data);
|
_clutter_stage_process_queued_events (l->data);
|
||||||
}
|
|
||||||
|
|
||||||
CLUTTER_TIMER_STOP (_clutter_uprof_context, master_event_process);
|
CLUTTER_TIMER_STOP (_clutter_uprof_context, master_event_process);
|
||||||
|
|
||||||
@ -372,19 +454,7 @@ master_clock_update_stages (ClutterMasterClock *master_clock,
|
|||||||
* is advanced.
|
* is advanced.
|
||||||
*/
|
*/
|
||||||
for (l = stages; l != NULL; l = l->next)
|
for (l = stages; l != NULL; l = l->next)
|
||||||
{
|
|
||||||
/* If a stage has a swap-buffers pending we don't want to draw to it
|
|
||||||
* in case the driver may block the CPU while it waits for the next
|
|
||||||
* backbuffer to become available.
|
|
||||||
*
|
|
||||||
* TODO: We should be able to identify if we are running triple or N
|
|
||||||
* buffered and in these cases we can still draw if there is 1 swap
|
|
||||||
* pending so we can hopefully always be ready to swap for the next
|
|
||||||
* vblank and really match the vsync frequency.
|
|
||||||
*/
|
|
||||||
if (_clutter_stage_get_pending_swaps (l->data) == 0)
|
|
||||||
stages_updated |= _clutter_stage_do_update (l->data);
|
stages_updated |= _clutter_stage_do_update (l->data);
|
||||||
}
|
|
||||||
|
|
||||||
_clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_POST_PAINT);
|
_clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_POST_PAINT);
|
||||||
|
|
||||||
@ -474,7 +544,6 @@ clutter_clock_dispatch (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;
|
||||||
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
|
|
||||||
gboolean stages_updated = FALSE;
|
gboolean stages_updated = FALSE;
|
||||||
GSList *stages;
|
GSList *stages;
|
||||||
|
|
||||||
@ -498,10 +567,10 @@ clutter_clock_dispatch (GSource *source,
|
|||||||
#endif
|
#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 - master_clock_list_ready_stages() returns a
|
||||||
|
* list of referenced that we'll unref afterwards.
|
||||||
*/
|
*/
|
||||||
stages = clutter_stage_manager_list_stages (stage_manager);
|
stages = master_clock_list_ready_stages (master_clock);
|
||||||
g_slist_foreach (stages, (GFunc) g_object_ref, NULL);
|
|
||||||
|
|
||||||
master_clock->idle = FALSE;
|
master_clock->idle = FALSE;
|
||||||
|
|
||||||
@ -524,6 +593,8 @@ clutter_clock_dispatch (GSource *source,
|
|||||||
if (!stages_updated)
|
if (!stages_updated)
|
||||||
master_clock->idle = TRUE;
|
master_clock->idle = TRUE;
|
||||||
|
|
||||||
|
master_clock_reschedule_stage_updates (master_clock, stages);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@ -617,7 +688,10 @@ _clutter_master_clock_add_timeline (ClutterMasterClock *master_clock,
|
|||||||
timeline);
|
timeline);
|
||||||
|
|
||||||
if (is_first)
|
if (is_first)
|
||||||
|
{
|
||||||
|
master_clock_schedule_stage_updates (master_clock);
|
||||||
_clutter_master_clock_start_running (master_clock);
|
_clutter_master_clock_start_running (master_clock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -66,7 +66,9 @@ void _clutter_stage_queue_event (ClutterStage *stage,
|
|||||||
gboolean _clutter_stage_has_queued_events (ClutterStage *stage);
|
gboolean _clutter_stage_has_queued_events (ClutterStage *stage);
|
||||||
void _clutter_stage_process_queued_events (ClutterStage *stage);
|
void _clutter_stage_process_queued_events (ClutterStage *stage);
|
||||||
void _clutter_stage_update_input_devices (ClutterStage *stage);
|
void _clutter_stage_update_input_devices (ClutterStage *stage);
|
||||||
int _clutter_stage_get_pending_swaps (ClutterStage *stage);
|
void _clutter_stage_schedule_update (ClutterStage *stage);
|
||||||
|
gint64 _clutter_stage_get_update_time (ClutterStage *stage);
|
||||||
|
void _clutter_stage_clear_update_time (ClutterStage *stage);
|
||||||
gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage);
|
gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage);
|
||||||
|
|
||||||
ClutterActor *_clutter_stage_do_pick (ClutterStage *stage,
|
ClutterActor *_clutter_stage_do_pick (ClutterStage *stage,
|
||||||
|
@ -122,21 +122,56 @@ _clutter_stage_window_get_geometry (ClutterStageWindow *window,
|
|||||||
CLUTTER_STAGE_WINDOW_GET_IFACE (window)->get_geometry (window, geometry);
|
CLUTTER_STAGE_WINDOW_GET_IFACE (window)->get_geometry (window, geometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
void
|
||||||
_clutter_stage_window_get_pending_swaps (ClutterStageWindow *window)
|
_clutter_stage_window_schedule_update (ClutterStageWindow *window,
|
||||||
|
int sync_delay)
|
||||||
|
{
|
||||||
|
ClutterStageWindowIface *iface;
|
||||||
|
|
||||||
|
g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
|
||||||
|
|
||||||
|
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
|
||||||
|
if (iface->schedule_update == NULL)
|
||||||
|
{
|
||||||
|
g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->schedule_update (window, sync_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
gint64
|
||||||
|
_clutter_stage_window_get_update_time (ClutterStageWindow *window)
|
||||||
{
|
{
|
||||||
ClutterStageWindowIface *iface;
|
ClutterStageWindowIface *iface;
|
||||||
|
|
||||||
g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), 0);
|
g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), 0);
|
||||||
|
|
||||||
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
|
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
|
||||||
if (iface->get_pending_swaps == NULL)
|
if (iface->get_update_time == NULL)
|
||||||
{
|
{
|
||||||
g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS));
|
g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return iface->get_pending_swaps (window);
|
return iface->get_update_time (window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_clutter_stage_window_clear_update_time (ClutterStageWindow *window)
|
||||||
|
{
|
||||||
|
ClutterStageWindowIface *iface;
|
||||||
|
|
||||||
|
g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
|
||||||
|
|
||||||
|
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
|
||||||
|
if (iface->clear_update_time == NULL)
|
||||||
|
{
|
||||||
|
g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->clear_update_time (window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -58,7 +58,10 @@ struct _ClutterStageWindowIface
|
|||||||
void (* get_geometry) (ClutterStageWindow *stage_window,
|
void (* get_geometry) (ClutterStageWindow *stage_window,
|
||||||
cairo_rectangle_int_t *geometry);
|
cairo_rectangle_int_t *geometry);
|
||||||
|
|
||||||
int (* get_pending_swaps) (ClutterStageWindow *stage_window);
|
void (* schedule_update) (ClutterStageWindow *stage_window,
|
||||||
|
int sync_delay);
|
||||||
|
gint64 (* get_update_time) (ClutterStageWindow *stage_window);
|
||||||
|
void (* clear_update_time) (ClutterStageWindow *stage_window);
|
||||||
|
|
||||||
void (* add_redraw_clip) (ClutterStageWindow *stage_window,
|
void (* add_redraw_clip) (ClutterStageWindow *stage_window,
|
||||||
cairo_rectangle_int_t *stage_rectangle);
|
cairo_rectangle_int_t *stage_rectangle);
|
||||||
@ -108,7 +111,10 @@ void _clutter_stage_window_resize (ClutterStageWin
|
|||||||
gint height);
|
gint height);
|
||||||
void _clutter_stage_window_get_geometry (ClutterStageWindow *window,
|
void _clutter_stage_window_get_geometry (ClutterStageWindow *window,
|
||||||
cairo_rectangle_int_t *geometry);
|
cairo_rectangle_int_t *geometry);
|
||||||
int _clutter_stage_window_get_pending_swaps (ClutterStageWindow *window);
|
void _clutter_stage_window_schedule_update (ClutterStageWindow *window,
|
||||||
|
int sync_delay);
|
||||||
|
gint64 _clutter_stage_window_get_update_time (ClutterStageWindow *window);
|
||||||
|
void _clutter_stage_window_clear_update_time (ClutterStageWindow *window);
|
||||||
|
|
||||||
void _clutter_stage_window_add_redraw_clip (ClutterStageWindow *window,
|
void _clutter_stage_window_add_redraw_clip (ClutterStageWindow *window,
|
||||||
cairo_rectangle_int_t *stage_clip);
|
cairo_rectangle_int_t *stage_clip);
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
#include <cairo.h>
|
#include <cairo.h>
|
||||||
|
|
||||||
#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
|
#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
|
||||||
|
#define CLUTTER_ENABLE_EXPERIMENTAL_API
|
||||||
|
|
||||||
#include "clutter-stage.h"
|
#include "clutter-stage.h"
|
||||||
#include "deprecated/clutter-stage.h"
|
#include "deprecated/clutter-stage.h"
|
||||||
@ -145,6 +146,8 @@ struct _ClutterStagePrivate
|
|||||||
|
|
||||||
CoglFramebuffer *active_framebuffer;
|
CoglFramebuffer *active_framebuffer;
|
||||||
|
|
||||||
|
gint sync_delay;
|
||||||
|
|
||||||
GTimer *fps_timer;
|
GTimer *fps_timer;
|
||||||
gint32 timer_n_frames;
|
gint32 timer_n_frames;
|
||||||
|
|
||||||
@ -930,6 +933,7 @@ _clutter_stage_queue_event (ClutterStage *stage,
|
|||||||
{
|
{
|
||||||
ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
|
ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
|
||||||
_clutter_master_clock_start_running (master_clock);
|
_clutter_master_clock_start_running (master_clock);
|
||||||
|
_clutter_stage_schedule_update (stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if needed, update the state of the input device of the event.
|
/* if needed, update the state of the input device of the event.
|
||||||
@ -1250,7 +1254,11 @@ clutter_stage_real_queue_relayout (ClutterActor *self)
|
|||||||
ClutterStagePrivate *priv = stage->priv;
|
ClutterStagePrivate *priv = stage->priv;
|
||||||
ClutterActorClass *parent_class;
|
ClutterActorClass *parent_class;
|
||||||
|
|
||||||
|
if (!priv->relayout_pending)
|
||||||
|
{
|
||||||
|
_clutter_stage_schedule_update (stage);
|
||||||
priv->relayout_pending = TRUE;
|
priv->relayout_pending = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/* chain up */
|
/* chain up */
|
||||||
parent_class = CLUTTER_ACTOR_CLASS (clutter_stage_parent_class);
|
parent_class = CLUTTER_ACTOR_CLASS (clutter_stage_parent_class);
|
||||||
@ -2273,6 +2281,7 @@ clutter_stage_init (ClutterStage *self)
|
|||||||
priv->use_fog = FALSE;
|
priv->use_fog = FALSE;
|
||||||
priv->throttle_motion_events = TRUE;
|
priv->throttle_motion_events = TRUE;
|
||||||
priv->min_size_changed = FALSE;
|
priv->min_size_changed = FALSE;
|
||||||
|
priv->sync_delay = -1;
|
||||||
|
|
||||||
/* XXX - we need to keep the invariant that calling
|
/* XXX - we need to keep the invariant that calling
|
||||||
* clutter_set_motion_event_enabled() before the stage creation
|
* clutter_set_motion_event_enabled() before the stage creation
|
||||||
@ -3584,6 +3593,10 @@ clutter_stage_ensure_redraw (ClutterStage *stage)
|
|||||||
g_return_if_fail (CLUTTER_IS_STAGE (stage));
|
g_return_if_fail (CLUTTER_IS_STAGE (stage));
|
||||||
|
|
||||||
priv = stage->priv;
|
priv = stage->priv;
|
||||||
|
|
||||||
|
if (!priv->relayout_pending && !priv->redraw_pending)
|
||||||
|
_clutter_stage_schedule_update (stage);
|
||||||
|
|
||||||
priv->relayout_pending = TRUE;
|
priv->relayout_pending = TRUE;
|
||||||
priv->redraw_pending = TRUE;
|
priv->redraw_pending = TRUE;
|
||||||
|
|
||||||
@ -3851,9 +3864,25 @@ clutter_stage_get_minimum_size (ClutterStage *stage,
|
|||||||
*height_p = (guint) height;
|
*height_p = (guint) height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the number of swap buffers pending completion for the stage */
|
void
|
||||||
int
|
_clutter_stage_schedule_update (ClutterStage *stage)
|
||||||
_clutter_stage_get_pending_swaps (ClutterStage *stage)
|
{
|
||||||
|
ClutterStageWindow *stage_window;
|
||||||
|
|
||||||
|
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
|
||||||
|
return;
|
||||||
|
|
||||||
|
stage_window = _clutter_stage_get_window (stage);
|
||||||
|
if (stage_window == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
return _clutter_stage_window_schedule_update (stage_window,
|
||||||
|
stage->priv->sync_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the earliest time the stage is ready to update */
|
||||||
|
gint64
|
||||||
|
_clutter_stage_get_update_time (ClutterStage *stage)
|
||||||
{
|
{
|
||||||
ClutterStageWindow *stage_window;
|
ClutterStageWindow *stage_window;
|
||||||
|
|
||||||
@ -3864,7 +3893,17 @@ _clutter_stage_get_pending_swaps (ClutterStage *stage)
|
|||||||
if (stage_window == NULL)
|
if (stage_window == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return _clutter_stage_window_get_pending_swaps (stage_window);
|
return _clutter_stage_window_get_update_time (stage_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_clutter_stage_clear_update_time (ClutterStage *stage)
|
||||||
|
{
|
||||||
|
ClutterStageWindow *stage_window;
|
||||||
|
|
||||||
|
stage_window = _clutter_stage_get_window (stage);
|
||||||
|
if (stage_window)
|
||||||
|
_clutter_stage_window_clear_update_time (stage_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3998,6 +4037,7 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage,
|
|||||||
|
|
||||||
CLUTTER_NOTE (PAINT, "First redraw request");
|
CLUTTER_NOTE (PAINT, "First redraw request");
|
||||||
|
|
||||||
|
_clutter_stage_schedule_update (stage);
|
||||||
priv->redraw_pending = TRUE;
|
priv->redraw_pending = TRUE;
|
||||||
|
|
||||||
master_clock = _clutter_master_clock_get_default ();
|
master_clock = _clutter_master_clock_get_default ();
|
||||||
@ -4487,3 +4527,58 @@ _clutter_stage_update_state (ClutterStage *stage,
|
|||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clutter_stage_set_sync_delay:
|
||||||
|
* @stage: a #ClutterStage
|
||||||
|
* @sync_delay: number of milliseconds after frame presentation to wait
|
||||||
|
* before painting the next frame. If less than zero, restores the
|
||||||
|
* default behavior where redraw is throttled to the refresh rate but
|
||||||
|
* not synchronized to it.
|
||||||
|
*
|
||||||
|
* This function enables an alternate behavior where Clutter draws at
|
||||||
|
* a fixed point in time after the frame presentation time (also known
|
||||||
|
* as the VBlank time). This is most useful when the application
|
||||||
|
* wants to show incoming data with predictable latency. (The primary
|
||||||
|
* example of this would be a window system compositor.) By synchronizing
|
||||||
|
* to provide new data before Clutter redraws, an external source of
|
||||||
|
* updates (in the compositor, an application) can get a reliable latency.
|
||||||
|
*
|
||||||
|
* The appropriate value of @sync_delay depends on the complexity of
|
||||||
|
* drawing the stage's scene graph - in general a value of between 0
|
||||||
|
* and 8 ms (up to one-half of a typical 60hz frame rate) is appropriate.
|
||||||
|
* using a larger value will reduce latency but risks skipping a frame if
|
||||||
|
* drawing the stage takes too long.
|
||||||
|
*
|
||||||
|
* Since: 1.14
|
||||||
|
* Stability: unstable
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
clutter_stage_set_sync_delay (ClutterStage *stage,
|
||||||
|
gint sync_delay)
|
||||||
|
{
|
||||||
|
g_return_if_fail (CLUTTER_IS_STAGE (stage));
|
||||||
|
|
||||||
|
stage->priv->sync_delay = sync_delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clutter_stage_skip_sync_delay:
|
||||||
|
* @stage: a #ClutterStage
|
||||||
|
*
|
||||||
|
* Causes the next frame for the stage to be drawn as quickly as
|
||||||
|
* possible, ignoring any delay that clutter_stage_set_sync_delay()
|
||||||
|
* would normally cause.
|
||||||
|
*
|
||||||
|
* Since: 1.14
|
||||||
|
* Stability: unstable
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
clutter_stage_skip_sync_delay (ClutterStage *stage)
|
||||||
|
{
|
||||||
|
ClutterStageWindow *stage_window;
|
||||||
|
|
||||||
|
stage_window = _clutter_stage_get_window (stage);
|
||||||
|
if (stage_window)
|
||||||
|
_clutter_stage_window_schedule_update (stage_window, -1);
|
||||||
|
}
|
||||||
|
@ -202,6 +202,14 @@ void clutter_stage_ensure_current (ClutterStage
|
|||||||
void clutter_stage_ensure_viewport (ClutterStage *stage);
|
void clutter_stage_ensure_viewport (ClutterStage *stage);
|
||||||
void clutter_stage_ensure_redraw (ClutterStage *stage);
|
void clutter_stage_ensure_redraw (ClutterStage *stage);
|
||||||
|
|
||||||
|
#ifdef CLUTTER_ENABLE_EXPERIMENTAL_API
|
||||||
|
CLUTTER_AVAILABLE_IN_1_14
|
||||||
|
void clutter_stage_set_sync_delay (ClutterStage *stage,
|
||||||
|
gint sync_delay);
|
||||||
|
CLUTTER_AVAILABLE_IN_1_14
|
||||||
|
void clutter_stage_skip_sync_delay (ClutterStage *stage);
|
||||||
|
#endif
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __CLUTTER_STAGE_H__ */
|
#endif /* __CLUTTER_STAGE_H__ */
|
||||||
|
@ -71,17 +71,25 @@ clutter_stage_cogl_unrealize (ClutterStageWindow *stage_window)
|
|||||||
|
|
||||||
if (stage_cogl->onscreen != NULL)
|
if (stage_cogl->onscreen != NULL)
|
||||||
{
|
{
|
||||||
|
cogl_onscreen_remove_frame_callback (stage_cogl->onscreen,
|
||||||
|
stage_cogl->frame_closure);
|
||||||
|
stage_cogl->frame_closure = NULL;
|
||||||
|
|
||||||
cogl_object_unref (stage_cogl->onscreen);
|
cogl_object_unref (stage_cogl->onscreen);
|
||||||
stage_cogl->onscreen = NULL;
|
stage_cogl->onscreen = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_swap_complete_cb (CoglFramebuffer *framebuffer,
|
frame_cb (CoglOnscreen *onscreen,
|
||||||
|
CoglFrameEvent event,
|
||||||
|
CoglFrameInfo *info,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
ClutterStageCogl *stage_cogl = user_data;
|
ClutterStageCogl *stage_cogl = user_data;
|
||||||
|
|
||||||
|
if (event == COGL_FRAME_EVENT_SYNC)
|
||||||
|
{
|
||||||
/* Early versions of the swap_event implementation in Mesa
|
/* Early versions of the swap_event implementation in Mesa
|
||||||
* deliver BufferSwapComplete event when not selected for,
|
* deliver BufferSwapComplete event when not selected for,
|
||||||
* so if we get a swap event we aren't expecting, just ignore it.
|
* so if we get a swap event we aren't expecting, just ignore it.
|
||||||
@ -93,6 +101,23 @@ handle_swap_complete_cb (CoglFramebuffer *framebuffer,
|
|||||||
*/
|
*/
|
||||||
if (stage_cogl->pending_swaps > 0)
|
if (stage_cogl->pending_swaps > 0)
|
||||||
stage_cogl->pending_swaps--;
|
stage_cogl->pending_swaps--;
|
||||||
|
}
|
||||||
|
else if (event == COGL_FRAME_EVENT_COMPLETE)
|
||||||
|
{
|
||||||
|
gint64 presentation_time_cogl = cogl_frame_info_get_presentation_time (info);
|
||||||
|
|
||||||
|
if (presentation_time_cogl != 0)
|
||||||
|
{
|
||||||
|
CoglContext *context = cogl_framebuffer_get_context (COGL_FRAMEBUFFER (onscreen));
|
||||||
|
gint64 current_time_cogl = cogl_get_clock_time (context);
|
||||||
|
gint64 now = g_get_monotonic_time ();
|
||||||
|
|
||||||
|
stage_cogl->last_presentation_time =
|
||||||
|
now + (presentation_time_cogl - current_time_cogl) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
stage_cogl->refresh_rate = cogl_frame_info_get_refresh_rate (info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@ -134,23 +159,77 @@ clutter_stage_cogl_realize (ClutterStageWindow *stage_window)
|
|||||||
* will be ignored, so we need to make sure the stage size is
|
* will be ignored, so we need to make sure the stage size is
|
||||||
* updated to this size. */
|
* updated to this size. */
|
||||||
|
|
||||||
if (cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT))
|
stage_cogl->frame_closure =
|
||||||
{
|
cogl_onscreen_add_frame_callback (stage_cogl->onscreen,
|
||||||
stage_cogl->swap_callback_id =
|
frame_cb,
|
||||||
cogl_onscreen_add_swap_buffers_callback (stage_cogl->onscreen,
|
stage_cogl,
|
||||||
handle_swap_complete_cb,
|
NULL);
|
||||||
stage_cogl);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static void
|
||||||
clutter_stage_cogl_get_pending_swaps (ClutterStageWindow *stage_window)
|
clutter_stage_cogl_schedule_update (ClutterStageWindow *stage_window,
|
||||||
|
gint sync_delay)
|
||||||
|
{
|
||||||
|
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
|
||||||
|
gint64 now;
|
||||||
|
float refresh_rate;
|
||||||
|
gint64 refresh_interval;
|
||||||
|
|
||||||
|
if (stage_cogl->update_time != -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
now = g_get_monotonic_time ();
|
||||||
|
|
||||||
|
if (sync_delay < 0)
|
||||||
|
{
|
||||||
|
stage_cogl->update_time = now;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We only extrapolate presentation times for 150ms - this is somewhat
|
||||||
|
* arbitrary. The reasons it might not be accurate for larger times are
|
||||||
|
* that the refresh interval might be wrong or the vertical refresh
|
||||||
|
* might be downclocked if nothing is going on onscreen.
|
||||||
|
*/
|
||||||
|
if (stage_cogl->last_presentation_time == 0||
|
||||||
|
stage_cogl->last_presentation_time < now - 150000)
|
||||||
|
{
|
||||||
|
stage_cogl->update_time = now;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh_rate = stage_cogl->refresh_rate;
|
||||||
|
if (refresh_rate == 0.0)
|
||||||
|
refresh_rate = 60.0;
|
||||||
|
|
||||||
|
refresh_interval = (gint64) (0.5 + 1000000 / refresh_rate);
|
||||||
|
if (refresh_interval == 0)
|
||||||
|
refresh_interval = 16667; /* 1/60th second */
|
||||||
|
|
||||||
|
stage_cogl->update_time = stage_cogl->last_presentation_time + 1000 * sync_delay;
|
||||||
|
|
||||||
|
while (stage_cogl->update_time < now)
|
||||||
|
stage_cogl->update_time += refresh_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint64
|
||||||
|
clutter_stage_cogl_get_update_time (ClutterStageWindow *stage_window)
|
||||||
{
|
{
|
||||||
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
|
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
|
||||||
|
|
||||||
return stage_cogl->pending_swaps;
|
if (stage_cogl->pending_swaps)
|
||||||
|
return -1; /* in the future, indefinite */
|
||||||
|
|
||||||
|
return stage_cogl->update_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_stage_cogl_clear_update_time (ClutterStageWindow *stage_window)
|
||||||
|
{
|
||||||
|
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
|
||||||
|
|
||||||
|
stage_cogl->update_time = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ClutterActor *
|
static ClutterActor *
|
||||||
@ -620,7 +699,9 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
|
|||||||
iface->resize = clutter_stage_cogl_resize;
|
iface->resize = clutter_stage_cogl_resize;
|
||||||
iface->show = clutter_stage_cogl_show;
|
iface->show = clutter_stage_cogl_show;
|
||||||
iface->hide = clutter_stage_cogl_hide;
|
iface->hide = clutter_stage_cogl_hide;
|
||||||
iface->get_pending_swaps = clutter_stage_cogl_get_pending_swaps;
|
iface->schedule_update = clutter_stage_cogl_schedule_update;
|
||||||
|
iface->get_update_time = clutter_stage_cogl_get_update_time;
|
||||||
|
iface->clear_update_time = clutter_stage_cogl_clear_update_time;
|
||||||
iface->add_redraw_clip = clutter_stage_cogl_add_redraw_clip;
|
iface->add_redraw_clip = clutter_stage_cogl_add_redraw_clip;
|
||||||
iface->has_redraw_clips = clutter_stage_cogl_has_redraw_clips;
|
iface->has_redraw_clips = clutter_stage_cogl_has_redraw_clips;
|
||||||
iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips;
|
iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips;
|
||||||
@ -669,4 +750,8 @@ _clutter_stage_cogl_class_init (ClutterStageCoglClass *klass)
|
|||||||
static void
|
static void
|
||||||
_clutter_stage_cogl_init (ClutterStageCogl *stage)
|
_clutter_stage_cogl_init (ClutterStageCogl *stage)
|
||||||
{
|
{
|
||||||
|
stage->last_presentation_time = 0;
|
||||||
|
stage->refresh_rate = 0.0;
|
||||||
|
|
||||||
|
stage->update_time = -1;
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,12 @@ struct _ClutterStageCogl
|
|||||||
|
|
||||||
CoglOnscreen *onscreen;
|
CoglOnscreen *onscreen;
|
||||||
|
|
||||||
|
gint64 last_presentation_time;
|
||||||
|
float refresh_rate;
|
||||||
|
|
||||||
|
gint64 update_time;
|
||||||
gint pending_swaps;
|
gint pending_swaps;
|
||||||
unsigned int swap_callback_id;
|
CoglFrameClosure *frame_closure;
|
||||||
|
|
||||||
/* We only enable clipped redraws after 2 frames, since we've seen
|
/* We only enable clipped redraws after 2 frames, since we've seen
|
||||||
* a lot of drivers can struggle to get going and may output some
|
* a lot of drivers can struggle to get going and may output some
|
||||||
|
Loading…
Reference in New Issue
Block a user