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
This commit is contained in:
parent
aa5d8ac68c
commit
9ab338d7b6
1 changed files with 155 additions and 53 deletions
|
@ -51,7 +51,9 @@
|
||||||
|
|
||||||
typedef struct _ClutterStageViewCoglPrivate
|
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_MAX 16
|
||||||
#define DAMAGE_HISTORY(x) ((x) & (DAMAGE_HISTORY_MAX - 1))
|
#define DAMAGE_HISTORY(x) ((x) & (DAMAGE_HISTORY_MAX - 1))
|
||||||
cairo_rectangle_int_t damage_history[DAMAGE_HISTORY_MAX];
|
cairo_rectangle_int_t damage_history[DAMAGE_HISTORY_MAX];
|
||||||
|
@ -435,13 +437,20 @@ fill_current_damage_history_and_step (ClutterStageView *view)
|
||||||
ClutterStageViewCoglPrivate *view_priv =
|
ClutterStageViewCoglPrivate *view_priv =
|
||||||
clutter_stage_view_cogl_get_instance_private (view_cogl);
|
clutter_stage_view_cogl_get_instance_private (view_cogl);
|
||||||
cairo_rectangle_int_t view_rect;
|
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)];
|
&view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index)];
|
||||||
clutter_stage_view_get_layout (view, &view_rect);
|
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++;
|
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
|
static gboolean
|
||||||
clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||||
ClutterStageView *view)
|
ClutterStageView *view)
|
||||||
|
@ -501,13 +544,18 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||||
ClutterActor *wrapper;
|
ClutterActor *wrapper;
|
||||||
cairo_rectangle_int_t redraw_clip;
|
cairo_rectangle_int_t redraw_clip;
|
||||||
cairo_rectangle_int_t swap_region;
|
cairo_rectangle_int_t swap_region;
|
||||||
cairo_rectangle_int_t clip_region;
|
cairo_rectangle_int_t fb_clip_region;
|
||||||
gboolean clip_region_empty;
|
gboolean clip_region_empty;
|
||||||
float fb_scale;
|
float fb_scale;
|
||||||
|
int subpixel_compensation = 0;
|
||||||
|
int fb_width, fb_height;
|
||||||
|
|
||||||
wrapper = CLUTTER_ACTOR (stage_cogl->wrapper);
|
wrapper = CLUTTER_ACTOR (stage_cogl->wrapper);
|
||||||
|
|
||||||
clutter_stage_view_get_layout (view, &view_rect);
|
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 =
|
can_blit_sub_buffer =
|
||||||
cogl_is_onscreen (fb) &&
|
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)
|
cogl_onscreen_get_frame_counter (COGL_ONSCREEN (fb)) > 3)
|
||||||
{
|
{
|
||||||
may_use_clipped_redraw = TRUE;
|
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
|
else
|
||||||
{
|
{
|
||||||
clip_region = (cairo_rectangle_int_t){ 0 };
|
fb_clip_region = (cairo_rectangle_int_t) { 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (may_use_clipped_redraw &&
|
if (may_use_clipped_redraw &&
|
||||||
|
@ -555,9 +616,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||||
else
|
else
|
||||||
use_clipped_redraw = FALSE;
|
use_clipped_redraw = FALSE;
|
||||||
|
|
||||||
clip_region_empty = may_use_clipped_redraw && clip_region.width == 0;
|
clip_region_empty = may_use_clipped_redraw && fb_clip_region.width == 0;
|
||||||
|
|
||||||
fb_scale = clutter_stage_view_get_scale (view);
|
|
||||||
|
|
||||||
swap_with_damage = FALSE;
|
swap_with_damage = FALSE;
|
||||||
if (has_buffer_age)
|
if (has_buffer_age)
|
||||||
|
@ -565,34 +624,44 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||||
if (use_clipped_redraw && !clip_region_empty)
|
if (use_clipped_redraw && !clip_region_empty)
|
||||||
{
|
{
|
||||||
int age, i;
|
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++)];
|
&view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index++)];
|
||||||
|
|
||||||
age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (fb));
|
age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (fb));
|
||||||
|
|
||||||
if (valid_buffer_age (view_cogl, age))
|
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++)
|
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)];
|
&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. */
|
/* 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,
|
_clutter_util_rectangle_union (&stage_cogl->bounding_redraw_clip,
|
||||||
&clip_region,
|
&damage_region,
|
||||||
&stage_cogl->bounding_redraw_clip);
|
&stage_cogl->bounding_redraw_clip);
|
||||||
|
|
||||||
CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: x=%d, y=%d, width=%d, height=%d\n",
|
CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: x=%d, y=%d, width=%d, height=%d\n",
|
||||||
age,
|
age,
|
||||||
clip_region.x,
|
fb_clip_region.x,
|
||||||
clip_region.y,
|
fb_clip_region.y,
|
||||||
clip_region.width,
|
fb_clip_region.width,
|
||||||
clip_region.height);
|
fb_clip_region.height);
|
||||||
|
|
||||||
swap_with_damage = TRUE;
|
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);
|
CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", age);
|
||||||
use_clipped_redraw = FALSE;
|
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)
|
else if (!use_clipped_redraw)
|
||||||
|
@ -616,26 +690,34 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||||
}
|
}
|
||||||
else if (use_clipped_redraw)
|
else if (use_clipped_redraw)
|
||||||
{
|
{
|
||||||
int scissor_x;
|
cairo_rectangle_int_t scissor_rect;
|
||||||
int scissor_y;
|
|
||||||
|
calculate_scissor_region (&fb_clip_region,
|
||||||
|
subpixel_compensation,
|
||||||
|
fb_width, fb_height,
|
||||||
|
&scissor_rect);
|
||||||
|
|
||||||
CLUTTER_NOTE (CLIPPING,
|
CLUTTER_NOTE (CLIPPING,
|
||||||
"Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n",
|
"Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n",
|
||||||
clip_region.x,
|
scissor_rect.x,
|
||||||
clip_region.y,
|
scissor_rect.y,
|
||||||
clip_region.width,
|
scissor_rect.width,
|
||||||
clip_region.height);
|
scissor_rect.height);
|
||||||
|
|
||||||
stage_cogl->using_clipped_redraw = TRUE;
|
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,
|
cogl_framebuffer_push_scissor_clip (fb,
|
||||||
scissor_x,
|
scissor_rect.x,
|
||||||
scissor_y,
|
scissor_rect.y,
|
||||||
clip_region.width * fb_scale,
|
scissor_rect.width,
|
||||||
clip_region.height * fb_scale);
|
scissor_rect.height);
|
||||||
paint_stage (stage_cogl, view, &clip_region);
|
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);
|
cogl_framebuffer_pop_clip (fb);
|
||||||
|
|
||||||
stage_cogl->using_clipped_redraw = FALSE;
|
stage_cogl->using_clipped_redraw = FALSE;
|
||||||
|
@ -650,17 +732,25 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||||
may_use_clipped_redraw &&
|
may_use_clipped_redraw &&
|
||||||
!clip_region_empty)
|
!clip_region_empty)
|
||||||
{
|
{
|
||||||
int scissor_x;
|
cairo_rectangle_int_t scissor_rect;
|
||||||
int scissor_y;
|
|
||||||
|
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,
|
cogl_framebuffer_push_scissor_clip (fb,
|
||||||
scissor_x,
|
scissor_rect.x,
|
||||||
scissor_y,
|
scissor_rect.y,
|
||||||
clip_region.width * fb_scale,
|
scissor_rect.width,
|
||||||
clip_region.height * fb_scale);
|
scissor_rect.height);
|
||||||
paint_stage (stage_cogl, view, &clip_region);
|
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);
|
cogl_framebuffer_pop_clip (fb);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -723,12 +813,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||||
}
|
}
|
||||||
else if (use_clipped_redraw)
|
else if (use_clipped_redraw)
|
||||||
{
|
{
|
||||||
swap_region = (cairo_rectangle_int_t) {
|
swap_region = fb_clip_region;
|
||||||
.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,
|
|
||||||
};
|
|
||||||
g_assert (swap_region.width > 0);
|
g_assert (swap_region.width > 0);
|
||||||
do_swap_buffer = TRUE;
|
do_swap_buffer = TRUE;
|
||||||
}
|
}
|
||||||
|
@ -810,9 +895,25 @@ clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window,
|
||||||
gboolean has_buffer_age =
|
gboolean has_buffer_age =
|
||||||
cogl_is_onscreen (framebuffer) &&
|
cogl_is_onscreen (framebuffer) &&
|
||||||
cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
|
cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
|
||||||
cairo_rectangle_int_t *rect;
|
float fb_scale;
|
||||||
|
gboolean scale_is_fractional;
|
||||||
|
|
||||||
if (!has_buffer_age)
|
fb_scale = clutter_stage_view_get_scale (view);
|
||||||
|
if (fb_scale != floorf (fb_scale))
|
||||||
|
scale_is_fractional = TRUE;
|
||||||
|
else
|
||||||
|
scale_is_fractional = FALSE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
*x = 0;
|
||||||
*y = 0;
|
*y = 0;
|
||||||
|
@ -823,12 +924,13 @@ clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window,
|
||||||
ClutterStageViewCoglPrivate *view_priv =
|
ClutterStageViewCoglPrivate *view_priv =
|
||||||
clutter_stage_view_cogl_get_instance_private (view_cogl);
|
clutter_stage_view_cogl_get_instance_private (view_cogl);
|
||||||
cairo_rectangle_int_t view_layout;
|
cairo_rectangle_int_t view_layout;
|
||||||
|
cairo_rectangle_int_t *fb_damage;
|
||||||
|
|
||||||
clutter_stage_view_get_layout (view, &view_layout);
|
clutter_stage_view_get_layout (view, &view_layout);
|
||||||
|
|
||||||
rect = &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - 1)];
|
fb_damage = &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - 1)];
|
||||||
*x = rect->x - view_layout.x;
|
*x = fb_damage->x / fb_scale;
|
||||||
*y = rect->y - view_layout.y;
|
*y = fb_damage->y / fb_scale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue