[clutter/wip/wayland: 3/3] Support clipped redraws with flipping



commit f495aa8c73b4df83fd92c92fb6ff4dda7b5c172d
Author: Robert Bragg <robert linux intel com>
Date:   Tue Mar 22 00:26:34 2011 +0000

    Support clipped redraws with flipping
    
    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.

 clutter/cogl/clutter-stage-cogl.c |  180 +++++++++++++++++++++++++++++-------
 clutter/cogl/clutter-stage-cogl.h |    5 +
 2 files changed, 150 insertions(+), 35 deletions(-)
---
diff --git a/clutter/cogl/clutter-stage-cogl.c b/clutter/cogl/clutter-stage-cogl.c
index a18dd7c..cd1f83a 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 e140824..8522d30 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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]