[mutter] clutter/stage-cogl: Track regions in buffer coordinate space



commit 9ab338d7b6529fa5df77caf3d3b2bca5759dcc98
Author: Jonas Ådahl <jadahl gmail com>
Date:   Thu Jun 8 10:08:16 2017 +0800

    clutter/stage-cogl: Track regions in buffer coordinate space
    
    When fractional scaling is used, damage and paint clip region is tracked
    in stage coordinate space using integers might end up missing some
    pixels when the border ends up on half pixels. Change the damage
    tracking and clip regions to be in buffer coordinates so we can align
    damage on physical pixel borders.
    
    However, just using rounding up to the next physical pixel results in
    glitches. To avoid this, extend the damage by one logical pixel in all
    directions, but still (scissor) clip the drawing to the non-extended
    region, as otherwise drawing the damaged regions will result in
    incorrect pixels on the right and bottom edges of the clip region. It is
    possible that there are better ways to do this, which can be explored in
    the future.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=765011

 clutter/clutter/cogl/clutter-stage-cogl.c |  208 +++++++++++++++++++++--------
 1 files changed, 155 insertions(+), 53 deletions(-)
---
diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c
index 436494a..84da1b1 100644
--- a/clutter/clutter/cogl/clutter-stage-cogl.c
+++ b/clutter/clutter/cogl/clutter-stage-cogl.c
@@ -51,7 +51,9 @@
 
 typedef struct _ClutterStageViewCoglPrivate
 {
-  /* Stores a list of previous damaged areas in the stage coordinate space */
+  /*
+   * List of previous damaged areas in stage view framebuffer coordinate space.
+   */
 #define DAMAGE_HISTORY_MAX 16
 #define DAMAGE_HISTORY(x) ((x) & (DAMAGE_HISTORY_MAX - 1))
   cairo_rectangle_int_t damage_history[DAMAGE_HISTORY_MAX];
@@ -435,13 +437,20 @@ fill_current_damage_history_and_step (ClutterStageView *view)
   ClutterStageViewCoglPrivate *view_priv =
     clutter_stage_view_cogl_get_instance_private (view_cogl);
   cairo_rectangle_int_t view_rect;
-  cairo_rectangle_int_t *current_damage;
+  float fb_scale;
+  cairo_rectangle_int_t *current_fb_damage;
 
-  current_damage =
+  current_fb_damage =
     &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index)];
   clutter_stage_view_get_layout (view, &view_rect);
+  fb_scale = clutter_stage_view_get_scale (view);
 
-  *current_damage = view_rect;
+  *current_fb_damage = (cairo_rectangle_int_t) {
+    .x = 0,
+    .y = 0,
+    .width = view_rect.width * fb_scale,
+    .height = view_rect.height * fb_scale
+  };
   view_priv->damage_index++;
 }
 
@@ -481,6 +490,40 @@ transform_swap_region_to_onscreen (ClutterStageView      *view,
   };
 }
 
+static void
+calculate_scissor_region (cairo_rectangle_int_t *fb_clip_region,
+                          int                    subpixel_compensation,
+                          int                    fb_width,
+                          int                    fb_height,
+                          cairo_rectangle_int_t *out_scissor_rect)
+{
+  int scissor_x;
+  int scissor_y;
+  int scissor_width;
+  int scissor_height;
+
+  scissor_x = fb_clip_region->x;
+  scissor_y = fb_clip_region->y;
+  scissor_width = fb_clip_region->width;
+  scissor_height = fb_clip_region->height;
+
+  if (fb_clip_region->x > 0)
+    scissor_x += subpixel_compensation;
+  if (fb_clip_region->y > 0)
+    scissor_y += subpixel_compensation;
+  if (fb_clip_region->x + fb_clip_region->width < fb_width)
+    scissor_width -= 2 * subpixel_compensation;
+  if (fb_clip_region->y + fb_clip_region->height < fb_height)
+    scissor_height -= 2 * subpixel_compensation;
+
+  *out_scissor_rect = (cairo_rectangle_int_t) {
+    .x = scissor_x,
+    .y = scissor_y,
+    .width = scissor_width,
+    .height = scissor_height
+  };
+}
+
 static gboolean
 clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
                                 ClutterStageView   *view)
@@ -501,13 +544,18 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
   ClutterActor *wrapper;
   cairo_rectangle_int_t redraw_clip;
   cairo_rectangle_int_t swap_region;
-  cairo_rectangle_int_t clip_region;
+  cairo_rectangle_int_t fb_clip_region;
   gboolean clip_region_empty;
   float fb_scale;
+  int subpixel_compensation = 0;
+  int fb_width, fb_height;
 
   wrapper = CLUTTER_ACTOR (stage_cogl->wrapper);
 
   clutter_stage_view_get_layout (view, &view_rect);
+  fb_scale = clutter_stage_view_get_scale (view);
+  fb_width = cogl_framebuffer_get_width (fb);
+  fb_height = cogl_framebuffer_get_height (fb);
 
   can_blit_sub_buffer =
     cogl_is_onscreen (fb) &&
@@ -542,11 +590,24 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
       cogl_onscreen_get_frame_counter (COGL_ONSCREEN (fb)) > 3)
     {
       may_use_clipped_redraw = TRUE;
-      clip_region = redraw_clip;
+
+      if (fb_scale != floorf (fb_scale))
+        subpixel_compensation = ceilf (fb_scale);
+
+      fb_clip_region = (cairo_rectangle_int_t) {
+        .x = (floorf ((redraw_clip.x - view_rect.x) * fb_scale) -
+              subpixel_compensation),
+        .y = (floorf ((redraw_clip.y - view_rect.y) * fb_scale) -
+              subpixel_compensation),
+        .width = (ceilf (redraw_clip.width * fb_scale) +
+                  (2 * subpixel_compensation)),
+        .height = (ceilf (redraw_clip.height * fb_scale) +
+                   (2 * subpixel_compensation))
+      };
     }
   else
     {
-      clip_region = (cairo_rectangle_int_t){ 0 };
+      fb_clip_region = (cairo_rectangle_int_t) { 0 };
     }
 
   if (may_use_clipped_redraw &&
@@ -555,9 +616,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
   else
     use_clipped_redraw = FALSE;
 
-  clip_region_empty = may_use_clipped_redraw && clip_region.width == 0;
-
-  fb_scale = clutter_stage_view_get_scale (view);
+  clip_region_empty = may_use_clipped_redraw && fb_clip_region.width == 0;
 
   swap_with_damage = FALSE;
   if (has_buffer_age)
@@ -565,34 +624,44 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
       if (use_clipped_redraw && !clip_region_empty)
         {
           int age, i;
-          cairo_rectangle_int_t *current_damage =
+          cairo_rectangle_int_t *current_fb_damage =
             &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index++)];
 
           age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (fb));
 
           if (valid_buffer_age (view_cogl, age))
             {
-              *current_damage = clip_region;
+              cairo_rectangle_int_t damage_region;
+
+              *current_fb_damage = fb_clip_region;
 
               for (i = 1; i <= age; i++)
                 {
-                  cairo_rectangle_int_t *damage =
+                  cairo_rectangle_int_t *fb_damage =
                     &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - i - 1)];
 
-                  _clutter_util_rectangle_union (&clip_region, damage, &clip_region);
+                  _clutter_util_rectangle_union (&fb_clip_region,
+                                                 fb_damage,
+                                                 &fb_clip_region);
                 }
 
               /* Update the bounding redraw clip state with the extra damage. */
+              damage_region = (cairo_rectangle_int_t) {
+                .x = view_rect.x + floorf (fb_clip_region.x / fb_scale),
+                .y = view_rect.y + floorf (fb_clip_region.y / fb_scale),
+                .width = ceilf (fb_clip_region.width / fb_scale),
+                .height = ceilf (fb_clip_region.height / fb_scale)
+              };
               _clutter_util_rectangle_union (&stage_cogl->bounding_redraw_clip,
-                                             &clip_region,
+                                             &damage_region,
                                              &stage_cogl->bounding_redraw_clip);
 
               CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: x=%d, y=%d, width=%d, 
height=%d\n",
                             age,
-                            clip_region.x,
-                            clip_region.y,
-                            clip_region.width,
-                            clip_region.height);
+                            fb_clip_region.x,
+                            fb_clip_region.y,
+                            fb_clip_region.width,
+                            fb_clip_region.height);
 
               swap_with_damage = TRUE;
             }
@@ -600,7 +669,12 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
             {
               CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", age);
               use_clipped_redraw = FALSE;
-              *current_damage = view_rect;
+              *current_fb_damage = (cairo_rectangle_int_t) {
+                .x = 0,
+                .y = 0,
+                .width = view_rect.width * fb_scale,
+                .height = view_rect.height * fb_scale
+              };
             }
         }
       else if (!use_clipped_redraw)
@@ -616,26 +690,34 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
     }
   else if (use_clipped_redraw)
     {
-      int scissor_x;
-      int scissor_y;
+      cairo_rectangle_int_t scissor_rect;
+
+      calculate_scissor_region (&fb_clip_region,
+                                subpixel_compensation,
+                                fb_width, fb_height,
+                                &scissor_rect);
 
       CLUTTER_NOTE (CLIPPING,
                     "Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n",
-                    clip_region.x,
-                    clip_region.y,
-                    clip_region.width,
-                    clip_region.height);
+                    scissor_rect.x,
+                    scissor_rect.y,
+                    scissor_rect.width,
+                    scissor_rect.height);
 
       stage_cogl->using_clipped_redraw = TRUE;
 
-      scissor_x = (clip_region.x - view_rect.x) * fb_scale;
-      scissor_y = (clip_region.y - view_rect.y) * fb_scale;
       cogl_framebuffer_push_scissor_clip (fb,
-                                          scissor_x,
-                                          scissor_y,
-                                          clip_region.width * fb_scale,
-                                          clip_region.height * fb_scale);
-      paint_stage (stage_cogl, view, &clip_region);
+                                          scissor_rect.x,
+                                          scissor_rect.y,
+                                          scissor_rect.width,
+                                          scissor_rect.height);
+      paint_stage (stage_cogl, view,
+                   &(cairo_rectangle_int_t) {
+                     .x = view_rect.x + floorf ((fb_clip_region.x - 0) / fb_scale),
+                     .y = view_rect.y + floorf ((fb_clip_region.y - 0) / fb_scale),
+                     .width = ceilf ((fb_clip_region.width + 0) / fb_scale),
+                     .height = ceilf ((fb_clip_region.height + 0) / fb_scale)
+                   });
       cogl_framebuffer_pop_clip (fb);
 
       stage_cogl->using_clipped_redraw = FALSE;
@@ -650,17 +732,25 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
           may_use_clipped_redraw &&
           !clip_region_empty)
         {
-          int scissor_x;
-          int scissor_y;
+          cairo_rectangle_int_t scissor_rect;
+
+          calculate_scissor_region (&fb_clip_region,
+                                    subpixel_compensation,
+                                    fb_width, fb_height,
+                                    &scissor_rect);
 
-          scissor_x = (clip_region.x - view_rect.x) * fb_scale;
-          scissor_y = (clip_region.y - view_rect.y) * fb_scale;
           cogl_framebuffer_push_scissor_clip (fb,
-                                              scissor_x,
-                                              scissor_y,
-                                              clip_region.width * fb_scale,
-                                              clip_region.height * fb_scale);
-          paint_stage (stage_cogl, view, &clip_region);
+                                              scissor_rect.x,
+                                              scissor_rect.y,
+                                              scissor_rect.width,
+                                              scissor_rect.height);
+          paint_stage (stage_cogl, view,
+                       &(cairo_rectangle_int_t) {
+                         .x = view_rect.x + floorf (fb_clip_region.x / fb_scale),
+                         .y = view_rect.y + floorf (fb_clip_region.y / fb_scale),
+                         .width = ceilf (fb_clip_region.width / fb_scale),
+                         .height = ceilf (fb_clip_region.height / fb_scale)
+                       });
           cogl_framebuffer_pop_clip (fb);
         }
       else
@@ -723,12 +813,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
         }
       else if (use_clipped_redraw)
         {
-          swap_region = (cairo_rectangle_int_t) {
-            .x = (clip_region.x - view_rect.x) * fb_scale,
-            .y = (clip_region.y - view_rect.y) * fb_scale,
-            .width = clip_region.width * fb_scale,
-            .height = clip_region.height * fb_scale,
-          };
+          swap_region = fb_clip_region;
           g_assert (swap_region.width > 0);
           do_swap_buffer = TRUE;
         }
@@ -810,9 +895,25 @@ clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window,
   gboolean has_buffer_age =
     cogl_is_onscreen (framebuffer) &&
     cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
-  cairo_rectangle_int_t *rect;
+  float fb_scale;
+  gboolean scale_is_fractional;
+
+  fb_scale = clutter_stage_view_get_scale (view);
+  if (fb_scale != floorf (fb_scale))
+    scale_is_fractional = TRUE;
+  else
+    scale_is_fractional = FALSE;
 
-  if (!has_buffer_age)
+  /*
+   * Buffer damage is tracked in the framebuffer coordinate space
+   * using the damage history. When fractional scaling is used, a
+   * coordinate on the stage might not correspond to the exact position of any
+   * physical pixel, which causes issues when painting using the pick mode.
+   *
+   * For now, always use the (0, 0) pixel for picking when using fractional
+   * framebuffer scaling.
+   */
+  if (!has_buffer_age || scale_is_fractional)
     {
       *x = 0;
       *y = 0;
@@ -823,12 +924,13 @@ clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window,
       ClutterStageViewCoglPrivate *view_priv =
         clutter_stage_view_cogl_get_instance_private (view_cogl);
       cairo_rectangle_int_t view_layout;
+      cairo_rectangle_int_t *fb_damage;
 
       clutter_stage_view_get_layout (view, &view_layout);
 
-      rect = &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - 1)];
-      *x = rect->x - view_layout.x;
-      *y = rect->y - view_layout.y;
+      fb_damage = &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - 1)];
+      *x = fb_damage->x / fb_scale;
+      *y = fb_damage->y / fb_scale;
     }
 }
 


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