1
0
Fork 0

stage-cogl: Reuse backbuffer contents

Use the buffer_age extension when available to recycle backbuffer contents
instead of blitting from the back to front buffer when doing clipped redraws.

The picking is now done in a pixel that is going to be repaired during the next
redraw cycle for non static scences.

This should improve performance and avoid tearing.

Reviewed-by: Robert Bragg <robert@linux.intel.com>

https://bugzilla.gnome.org/show_bug.cgi?id=669122
This commit is contained in:
Adel Gadllah 2013-02-06 11:05:58 +01:00
parent 60f20e8a7e
commit b9ad93ad8d
5 changed files with 167 additions and 32 deletions

View file

@ -236,6 +236,23 @@ _clutter_stage_window_redraw (ClutterStageWindow *window)
iface->redraw (window);
}
void
_clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window,
int *x, int *y)
{
ClutterStageWindowIface *iface;
*x = 0;
*y = 0;
g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->get_dirty_pixel)
iface->get_dirty_pixel (window, x, y);
}
void
_clutter_stage_window_dirty_back_buffer (ClutterStageWindow *window)
{

View file

@ -75,6 +75,9 @@ struct _ClutterStageWindowIface
void (* dirty_back_buffer) (ClutterStageWindow *stage_window);
void (* get_dirty_pixel) (ClutterStageWindow *stage_window,
int *x, int *y);
CoglFramebuffer *(* get_active_framebuffer) (ClutterStageWindow *stage_window);
gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window);
@ -121,6 +124,9 @@ void _clutter_stage_window_redraw (ClutterStageWin
void _clutter_stage_window_dirty_back_buffer (ClutterStageWindow *window);
void _clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window,
int *x, int *y);
CoglFramebuffer *_clutter_stage_window_get_active_framebuffer (ClutterStageWindow *window);
gboolean _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window);

View file

@ -1417,6 +1417,9 @@ _clutter_stage_do_pick (ClutterStage *stage,
CoglFramebuffer *fb;
ClutterActor *actor;
gboolean is_clipped;
gint read_x;
gint read_y;
CLUTTER_STATIC_COUNTER (do_pick_counter,
"_clutter_stage_do_pick counter",
"Increments for each full pick run",
@ -1490,13 +1493,29 @@ _clutter_stage_do_pick (ClutterStage *stage,
* picks for the same static scene won't require additional renders */
if (priv->picks_per_frame < 2)
{
if (G_LIKELY (!(clutter_pick_debug_flags &
CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
cogl_clip_push_window_rectangle (x, y, 1, 1);
gint dirty_x;
gint dirty_y;
_clutter_stage_window_get_dirty_pixel (priv->impl, &dirty_x, &dirty_y);
if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
cogl_clip_push_window_rectangle (dirty_x, dirty_y, 1, 1);
cogl_set_viewport (priv->viewport[0] - x + dirty_x,
priv->viewport[1] - y + dirty_y,
priv->viewport[2],
priv->viewport[3]);
read_x = dirty_x;
read_y = dirty_y;
is_clipped = TRUE;
}
else
is_clipped = FALSE;
{
read_x = x;
read_y = y;
is_clipped = FALSE;
}
CLUTTER_NOTE (PICK, "Performing %s pick at %i,%i",
is_clipped ? "clippped" : "full", x, y);
@ -1522,21 +1541,6 @@ _clutter_stage_do_pick (ClutterStage *stage,
context->pick_mode = CLUTTER_PICK_NONE;
CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_paint);
/* Notify the backend that we have trashed the contents of
* the back buffer... */
_clutter_stage_window_dirty_back_buffer (priv->impl);
if (is_clipped)
{
if (G_LIKELY (!(clutter_pick_debug_flags &
CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
cogl_clip_pop ();
_clutter_stage_set_pick_buffer_valid (stage, FALSE, -1);
}
else
_clutter_stage_set_pick_buffer_valid (stage, TRUE, mode);
/* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used
even though we don't care about the alpha component because under
GLES this is the only format that is guaranteed to work so Cogl
@ -1545,7 +1549,7 @@ _clutter_stage_do_pick (ClutterStage *stage,
assumes that all pixels in the framebuffer are premultiplied so
it avoids a conversion. */
CLUTTER_TIMER_START (_clutter_uprof_context, pick_read);
cogl_read_pixels (x, y, 1, 1,
cogl_read_pixels (read_x, read_y, 1, 1,
COGL_READ_PIXELS_COLOR_BUFFER,
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
pixel);
@ -1568,6 +1572,24 @@ _clutter_stage_do_pick (ClutterStage *stage,
/* Restore whether GL_DITHER was enabled */
cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save);
if (is_clipped)
{
if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
cogl_clip_pop ();
_clutter_stage_dirty_viewport (stage);
_clutter_stage_set_pick_buffer_valid (stage, FALSE, -1);
}
else
{
/* Notify the backend that we have trashed the contents of
* the back buffer... */
_clutter_stage_window_dirty_back_buffer (priv->impl);
_clutter_stage_set_pick_buffer_valid (stage, TRUE, mode);
}
check_pixel:
if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff)
{

View file

@ -319,7 +319,10 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
gboolean may_use_clipped_redraw;
gboolean use_clipped_redraw;
gboolean can_blit_sub_buffer;
gboolean has_buffer_age;
ClutterActor *wrapper;
cairo_rectangle_int_t *clip_region;
gboolean force_swap;
CLUTTER_STATIC_TIMER (painting_timer,
"Redrawing", /* parent */
@ -347,6 +350,8 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
can_blit_sub_buffer =
cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION);
has_buffer_age = cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
may_use_clipped_redraw = FALSE;
if (_clutter_stage_window_can_clip_redraws (stage_window) &&
can_blit_sub_buffer &&
@ -357,6 +362,7 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
stage_cogl->frame_count > 3)
{
may_use_clipped_redraw = TRUE;
clip_region = &stage_cogl->bounding_redraw_clip;
}
if (may_use_clipped_redraw &&
@ -366,23 +372,83 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
else
use_clipped_redraw = FALSE;
force_swap = FALSE;
if (use_clipped_redraw)
{
if (has_buffer_age)
{
int age = cogl_onscreen_get_buffer_age (stage_cogl->onscreen);
cairo_rectangle_int_t *current_damage;
current_damage = g_new0 (cairo_rectangle_int_t, 1);
current_damage->x = clip_region->x;
current_damage->y = clip_region->y;
current_damage->width = clip_region->width;
current_damage->height = clip_region->height;
stage_cogl->damage_history = g_slist_prepend (stage_cogl->damage_history, current_damage);
if (age != 0 && !stage_cogl->dirty_backbuffer && g_slist_length (stage_cogl->damage_history) >= age)
{
int i = 0;
GSList *tmp = NULL;
for (tmp = stage_cogl->damage_history; tmp; tmp = tmp->next)
{
_clutter_util_rectangle_union (clip_region, tmp->data, clip_region);
i++;
if (i == age)
{
g_slist_free_full (tmp->next, g_free);
tmp->next = NULL;
}
}
force_swap = TRUE;
CLUTTER_NOTE (CLIPPING, "Reusing back buffer - repairing region: x=%d, y=%d, width=%d, height=%d\n",
clip_region->x,
clip_region->y,
clip_region->width,
clip_region->height);
}
else if (age == 0 || stage_cogl->dirty_backbuffer)
{
CLUTTER_NOTE (CLIPPING, "Invalid back buffer: Resetting damage history list.\n");
g_slist_free_full (stage_cogl->damage_history, g_free);
stage_cogl->damage_history = NULL;
}
}
}
else
{
CLUTTER_NOTE (CLIPPING, "Unclipped redraw: Resetting damage history list.\n");
g_slist_free_full (stage_cogl->damage_history, g_free);
stage_cogl->damage_history = NULL;
}
if (has_buffer_age && !force_swap)
use_clipped_redraw = FALSE;
if (use_clipped_redraw)
{
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);
clip_region->x,
clip_region->y,
clip_region->width,
clip_region->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);
cogl_clip_push_window_rectangle (clip_region->x,
clip_region->y,
clip_region->width,
clip_region->height);
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper),
&stage_cogl->bounding_redraw_clip);
clip_region);
cogl_clip_pop ();
stage_cogl->using_clipped_redraw = FALSE;
@ -398,7 +464,7 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
may_use_clipped_redraw)
{
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper),
&stage_cogl->bounding_redraw_clip);
clip_region);
}
else
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper), NULL);
@ -450,9 +516,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 && !force_swap)
{
cairo_rectangle_int_t *clip = &stage_cogl->bounding_redraw_clip;
cairo_rectangle_int_t *clip = clip_region;
int copy_area[4];
/* XXX: It seems there will be a race here in that the stage
@ -524,6 +590,26 @@ clutter_stage_cogl_dirty_back_buffer (ClutterStageWindow *stage_window)
stage_cogl->dirty_backbuffer = TRUE;
}
static void
clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window,
int *x, int *y)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
gboolean has_buffer_age = cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
if ((stage_cogl->damage_history == NULL && has_buffer_age) || !has_buffer_age)
{
*x = 0;
*y = 0;
}
else
{
cairo_rectangle_int_t *rect;
rect = (cairo_rectangle_int_t *) (stage_cogl->damage_history->data);
*x = rect->x;
*y = rect->y;
}
}
static void
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
{
@ -542,6 +628,7 @@ clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
iface->redraw = clutter_stage_cogl_redraw;
iface->get_active_framebuffer = clutter_stage_cogl_get_active_framebuffer;
iface->dirty_back_buffer = clutter_stage_cogl_dirty_back_buffer;
iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel;
}
static void

View file

@ -52,6 +52,9 @@ struct _ClutterStageCogl
guint using_clipped_redraw : 1;
guint dirty_backbuffer : 1;
/* Stores a list of previous damaged areas */
GSList *damage_history;
};
struct _ClutterStageCoglClass