summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Bragg <robert@linux.intel.com>2011-03-22 00:26:34 +0000
committerNeil Roberts <neil@linux.intel.com>2012-03-01 11:49:52 +0000
commitf495aa8c73b4df83fd92c92fb6ff4dda7b5c172d (patch)
tree6ca3d84206f5732df2235d88d0e908280a876799
parentd1b857bc073e55bb27e3ae9a1c14c17ef1af5640 (diff)
downloadclutter-wip/wayland.tar.gz
Support clipped redraws with flippingwip/wayland
This adds support for doing incremental, clipped redraws but also using flipping to present instead of blitting. The approach builds on Cogl's support for cogl_onscreen_start_frame(), cogl_onscreen_get_back_buffer_age() and cogl_onscreen_swap_buffers_with_damage(). At the start of each frame we determine how old the back buffer contents are and if it's a reused buffer we aim to repair the old buffer based on the history of regions that have changed since the old buffer was created. When we present we present the back buffer we use cogl_onscreen_swap_buffers_with_damage() which the driver can implement using a flip and the damage region can also be passed to a system compositor to ensure it only recomposes the region that changed.
-rw-r--r--clutter/cogl/clutter-stage-cogl.c180
-rw-r--r--clutter/cogl/clutter-stage-cogl.h5
2 files changed, 150 insertions, 35 deletions
diff --git a/clutter/cogl/clutter-stage-cogl.c b/clutter/cogl/clutter-stage-cogl.c
index a18dd7c45..cd1f83acc 100644
--- a/clutter/cogl/clutter-stage-cogl.c
+++ b/clutter/cogl/clutter-stage-cogl.c
@@ -110,6 +110,8 @@ clutter_stage_cogl_realize (ClutterStageWindow *stage_window)
G_OBJECT_TYPE_NAME (stage_cogl),
stage_cogl);
+ stage_cogl->n_old_redraw_clips = 0;
+
backend = clutter_get_default_backend ();
if (stage_cogl->onscreen == NULL)
@@ -312,15 +314,44 @@ clutter_stage_cogl_get_redraw_clip_bounds (ClutterStageWindow *stage_window,
return FALSE;
}
-/* XXX: This is basically identical to clutter_stage_glx_redraw */
+static gboolean
+clutter_stage_cogl_can_clip_redraws (ClutterStageWindow *stage_window)
+{
+ return TRUE;
+}
+
+static void
+_cairo_rectangle_int_union (const cairo_rectangle_int_t *rect_a,
+ const cairo_rectangle_int_t *rect_b,
+ cairo_rectangle_int_t *result)
+{
+ /* We don't try to handle rectangles that can't be represented
+ * as a signed integer box */
+ gint x_1 = MIN (rect_a->x, rect_b->x);
+ gint y_1 = MIN (rect_a->y, rect_b->y);
+ gint x_2 = MAX (rect_a->x + (gint)rect_a->width,
+ rect_b->x + (gint)rect_b->width);
+ gint y_2 = MAX (rect_a->y + (gint)rect_a->height,
+ rect_b->y + (gint)rect_b->height);
+ result->x = x_1;
+ result->y = y_1;
+ result->width = x_2 - x_1;
+ result->height = y_2 - y_1;
+}
+
static void
clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
gboolean may_use_clipped_redraw;
+ cairo_rectangle_int_t final_clip;
+ gboolean have_final_clip = FALSE;
+ gboolean must_blit;
gboolean use_clipped_redraw;
- gboolean can_blit_sub_buffer;
ClutterActor *wrapper;
+ /* NB: we may push/pop a 1x1 framebuffer for picking so we
+ * we can't assume we are dealing with stage_cogl->onscreen */
+ CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer ();
CLUTTER_STATIC_TIMER (painting_timer,
"Redrawing", /* parent */
@@ -345,12 +376,7 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
CLUTTER_TIMER_START (_clutter_uprof_context, painting_timer);
- can_blit_sub_buffer =
- cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION);
-
- may_use_clipped_redraw = FALSE;
if (_clutter_stage_window_can_clip_redraws (stage_window) &&
- can_blit_sub_buffer &&
/* NB: a zero width redraw clip == full stage redraw */
stage_cogl->bounding_redraw_clip.width != 0 &&
/* some drivers struggle to get going and produce some junk
@@ -359,8 +385,70 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
{
may_use_clipped_redraw = TRUE;
}
+ else
+ {
+ stage_cogl->n_old_redraw_clips = 0;
+ may_use_clipped_redraw = FALSE;
+ }
+
+ if (may_use_clipped_redraw)
+ {
+ int age;
+ int i;
+
+ /* shift old redraw clips along and record the latest... */
+ for (i = CLUTTER_STAGE_COGL_CLIP_HISTORY_LENGTH - 1; i > 0 ; i--)
+ stage_cogl->old_redraw_clips[i] = stage_cogl->old_redraw_clips[i - 1];
+ stage_cogl->old_redraw_clips[0] = stage_cogl->bounding_redraw_clip;
+
+ if (stage_cogl->n_old_redraw_clips <
+ CLUTTER_STAGE_COGL_CLIP_HISTORY_LENGTH)
+ stage_cogl->n_old_redraw_clips++;
+
+ /* XXX: we're missing a cogl_is_onscreen() api in Cogl */
+ if (!cogl_is_offscreen (framebuffer))
+ {
+ CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+ cogl_onscreen_start_frame (onscreen);
+ age = cogl_onscreen_get_back_buffer_age (onscreen);
+ }
+ else
+ age = 0;
+
+ /* Only if the history of old redraw clips is as old as our back
+ * buffer contents can we perform a clipped redraw... */
+ if (age >= 1 && age <= stage_cogl->n_old_redraw_clips)
+ {
+ final_clip = stage_cogl->old_redraw_clips[0];
+ for (i = 1; i < age; i++)
+ _cairo_rectangle_int_union (&stage_cogl->old_redraw_clips[i],
+ &final_clip,
+ &final_clip);
+
+ have_final_clip = TRUE;
+ must_blit = FALSE;
+ }
+ else
+ {
+ if (age)
+ CLUTTER_NOTE (CLIPPING, "Not enough damage history to repair buffer:"
+ " age=%d, history_len=%d\n",
+ age, stage_cogl->n_old_redraw_clips);
+ else
+ CLUTTER_NOTE (CLIPPING, "Unknown back buffer contents so we can't clip, "
+ "repair + flip");
+ }
+
+ if (!have_final_clip &&
+ cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION))
+ {
+ final_clip = stage_cogl->bounding_redraw_clip;
+ have_final_clip = TRUE;
+ must_blit = TRUE;
+ }
+ }
- if (may_use_clipped_redraw &&
+ if (have_final_clip &&
G_LIKELY (!(clutter_paint_debug_flags &
CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
use_clipped_redraw = TRUE;
@@ -371,19 +459,18 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
{
CLUTTER_NOTE (CLIPPING,
"Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n",
- stage_cogl->bounding_redraw_clip.x,
- stage_cogl->bounding_redraw_clip.y,
- stage_cogl->bounding_redraw_clip.width,
- stage_cogl->bounding_redraw_clip.height);
+ final_clip.x,
+ final_clip.y,
+ final_clip.width,
+ final_clip.height);
stage_cogl->using_clipped_redraw = TRUE;
- cogl_clip_push_window_rectangle (stage_cogl->bounding_redraw_clip.x,
- stage_cogl->bounding_redraw_clip.y,
- stage_cogl->bounding_redraw_clip.width,
- stage_cogl->bounding_redraw_clip.height);
- _clutter_stage_do_paint (CLUTTER_STAGE (wrapper),
- &stage_cogl->bounding_redraw_clip);
+ cogl_clip_push_window_rectangle (final_clip.x,
+ final_clip.y,
+ final_clip.width,
+ final_clip.height);
+ _clutter_stage_do_paint (CLUTTER_STAGE (wrapper), &final_clip);
cogl_clip_pop ();
stage_cogl->using_clipped_redraw = FALSE;
@@ -396,22 +483,20 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
* the bounding_redraw_clip so it can be visualized */
if (G_UNLIKELY (clutter_paint_debug_flags &
CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS) &&
- may_use_clipped_redraw)
+ have_final_clip)
{
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper),
- &stage_cogl->bounding_redraw_clip);
+ &final_clip);
}
else
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper), NULL);
}
- if (may_use_clipped_redraw &&
+ if (have_final_clip &&
G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS)))
{
- CoglContext *ctx =
- clutter_backend_get_cogl_context (clutter_get_default_backend ());
static CoglPipeline *outline = NULL;
- cairo_rectangle_int_t *clip = &stage_cogl->bounding_redraw_clip;
+ cairo_rectangle_int_t *clip = &final_clip;
ClutterActor *actor = CLUTTER_ACTOR (wrapper);
CoglHandle vbo;
float x_1 = clip->x;
@@ -428,6 +513,8 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
if (outline == NULL)
{
+ CoglContext *ctx =
+ clutter_backend_get_cogl_context (clutter_get_default_backend ());
outline = cogl_pipeline_new (ctx);
cogl_pipeline_set_color4ub (outline, 0xff, 0x00, 0x00, 0xff);
}
@@ -456,9 +543,9 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
CLUTTER_TIMER_STOP (_clutter_uprof_context, painting_timer);
/* push on the screen */
- if (use_clipped_redraw)
+ if (use_clipped_redraw && must_blit)
{
- cairo_rectangle_int_t *clip = &stage_cogl->bounding_redraw_clip;
+ cairo_rectangle_int_t *clip = &final_clip;
int copy_area[4];
/* XXX: It seems there will be a race here in that the stage
@@ -491,18 +578,40 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
}
else
{
- CLUTTER_NOTE (BACKEND, "cogl_onscreen_swap_buffers (onscreen: %p)",
- stage_cogl->onscreen);
-
- /* If we have swap buffer events then cogl_onscreen_swap_buffers
- * will return immediately and we need to track that there is a
- * swap in progress... */
+ /* If we have swap buffer events then
+ * cogl_framebuffer_swap_buffers will return immediately and we
+ * need to track that there is a swap in progress... */
if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS))
stage_cogl->pending_swaps++;
- CLUTTER_TIMER_START (_clutter_uprof_context, swapbuffers_timer);
- cogl_onscreen_swap_buffers (stage_cogl->onscreen);
- CLUTTER_TIMER_STOP (_clutter_uprof_context, swapbuffers_timer);
+ if (have_final_clip)
+ {
+ int damage[] = {
+ final_clip.x,
+ final_clip.y,
+ final_clip.width,
+ final_clip.height
+ };
+
+ CLUTTER_NOTE (BACKEND,
+ "cogl_onscreen_swap_buffers_with_damage (onscreen: %p, "
+ "damage: { %d, %d, %d, %d})",
+ stage_cogl->onscreen,
+ damage[0], damage[1], damage[2], damage[3]);
+
+ CLUTTER_TIMER_START (_clutter_uprof_context, swapbuffers_timer);
+ cogl_onscreen_swap_buffers_with_damage (stage_cogl->onscreen,
+ damage, 1);
+ CLUTTER_TIMER_STOP (_clutter_uprof_context, swapbuffers_timer);
+ }
+ else
+ {
+ CLUTTER_NOTE (BACKEND, "cogl_framebuffer_swap_buffers (onscreen: %p)",
+ stage_cogl->onscreen);
+ CLUTTER_TIMER_START (_clutter_uprof_context, swapbuffers_timer);
+ cogl_onscreen_swap_buffers (stage_cogl->onscreen);
+ CLUTTER_TIMER_STOP (_clutter_uprof_context, swapbuffers_timer);
+ }
}
/* reset the redraw clipping for the next paint... */
@@ -540,6 +649,7 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
iface->add_redraw_clip = clutter_stage_cogl_add_redraw_clip;
iface->has_redraw_clips = clutter_stage_cogl_has_redraw_clips;
iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips;
+ iface->can_clip_redraws = clutter_stage_cogl_can_clip_redraws;
iface->get_redraw_clip_bounds = clutter_stage_cogl_get_redraw_clip_bounds;
iface->redraw = clutter_stage_cogl_redraw;
iface->get_active_framebuffer = clutter_stage_cogl_get_active_framebuffer;
diff --git a/clutter/cogl/clutter-stage-cogl.h b/clutter/cogl/clutter-stage-cogl.h
index e1408246d..8522d3045 100644
--- a/clutter/cogl/clutter-stage-cogl.h
+++ b/clutter/cogl/clutter-stage-cogl.h
@@ -23,6 +23,8 @@ G_BEGIN_DECLS
typedef struct _ClutterStageCogl ClutterStageCogl;
typedef struct _ClutterStageCoglClass ClutterStageCoglClass;
+#define CLUTTER_STAGE_COGL_CLIP_HISTORY_LENGTH 3
+
struct _ClutterStageCogl
{
GObject parent_instance;
@@ -45,6 +47,9 @@ struct _ClutterStageCogl
cairo_rectangle_int_t bounding_redraw_clip;
+ cairo_rectangle_int_t old_redraw_clips[CLUTTER_STAGE_COGL_CLIP_HISTORY_LENGTH];
+ int n_old_redraw_clips;
+
guint initialized_redraw_clip : 1;
/* TRUE if the current paint cycle has a clipped redraw. In that