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); 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 void
_clutter_stage_window_dirty_back_buffer (ClutterStageWindow *window) _clutter_stage_window_dirty_back_buffer (ClutterStageWindow *window)
{ {

View File

@ -75,6 +75,9 @@ struct _ClutterStageWindowIface
void (* dirty_back_buffer) (ClutterStageWindow *stage_window); 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); CoglFramebuffer *(* get_active_framebuffer) (ClutterStageWindow *stage_window);
gboolean (* can_clip_redraws) (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_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); CoglFramebuffer *_clutter_stage_window_get_active_framebuffer (ClutterStageWindow *window);
gboolean _clutter_stage_window_can_clip_redraws (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; CoglFramebuffer *fb;
ClutterActor *actor; ClutterActor *actor;
gboolean is_clipped; gboolean is_clipped;
gint read_x;
gint read_y;
CLUTTER_STATIC_COUNTER (do_pick_counter, CLUTTER_STATIC_COUNTER (do_pick_counter,
"_clutter_stage_do_pick counter", "_clutter_stage_do_pick counter",
"Increments for each full pick run", "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 */ * picks for the same static scene won't require additional renders */
if (priv->picks_per_frame < 2) if (priv->picks_per_frame < 2)
{ {
if (G_LIKELY (!(clutter_pick_debug_flags & gint dirty_x;
CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) gint dirty_y;
cogl_clip_push_window_rectangle (x, y, 1, 1);
_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; is_clipped = TRUE;
} }
else else
{
read_x = x;
read_y = y;
is_clipped = FALSE; is_clipped = FALSE;
}
CLUTTER_NOTE (PICK, "Performing %s pick at %i,%i", CLUTTER_NOTE (PICK, "Performing %s pick at %i,%i",
is_clipped ? "clippped" : "full", x, y); is_clipped ? "clippped" : "full", x, y);
@ -1522,21 +1541,6 @@ _clutter_stage_do_pick (ClutterStage *stage,
context->pick_mode = CLUTTER_PICK_NONE; context->pick_mode = CLUTTER_PICK_NONE;
CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_paint); 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 /* 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 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 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 assumes that all pixels in the framebuffer are premultiplied so
it avoids a conversion. */ it avoids a conversion. */
CLUTTER_TIMER_START (_clutter_uprof_context, pick_read); 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_READ_PIXELS_COLOR_BUFFER,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, COGL_PIXEL_FORMAT_RGBA_8888_PRE,
pixel); pixel);
@ -1568,6 +1572,24 @@ _clutter_stage_do_pick (ClutterStage *stage,
/* Restore whether GL_DITHER was enabled */ /* Restore whether GL_DITHER was enabled */
cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save); 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: check_pixel:
if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff) 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 may_use_clipped_redraw;
gboolean use_clipped_redraw; gboolean use_clipped_redraw;
gboolean can_blit_sub_buffer; gboolean can_blit_sub_buffer;
gboolean has_buffer_age;
ClutterActor *wrapper; ClutterActor *wrapper;
cairo_rectangle_int_t *clip_region;
gboolean force_swap;
CLUTTER_STATIC_TIMER (painting_timer, CLUTTER_STATIC_TIMER (painting_timer,
"Redrawing", /* parent */ "Redrawing", /* parent */
@ -347,6 +350,8 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
can_blit_sub_buffer = can_blit_sub_buffer =
cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION); 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; may_use_clipped_redraw = FALSE;
if (_clutter_stage_window_can_clip_redraws (stage_window) && if (_clutter_stage_window_can_clip_redraws (stage_window) &&
can_blit_sub_buffer && can_blit_sub_buffer &&
@ -357,6 +362,7 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
stage_cogl->frame_count > 3) stage_cogl->frame_count > 3)
{ {
may_use_clipped_redraw = TRUE; may_use_clipped_redraw = TRUE;
clip_region = &stage_cogl->bounding_redraw_clip;
} }
if (may_use_clipped_redraw && if (may_use_clipped_redraw &&
@ -366,23 +372,83 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
else else
use_clipped_redraw = FALSE; 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) if (use_clipped_redraw)
{ {
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",
stage_cogl->bounding_redraw_clip.x, clip_region->x,
stage_cogl->bounding_redraw_clip.y, clip_region->y,
stage_cogl->bounding_redraw_clip.width, clip_region->width,
stage_cogl->bounding_redraw_clip.height); clip_region->height);
stage_cogl->using_clipped_redraw = TRUE; stage_cogl->using_clipped_redraw = TRUE;
cogl_clip_push_window_rectangle (stage_cogl->bounding_redraw_clip.x, cogl_clip_push_window_rectangle (clip_region->x,
stage_cogl->bounding_redraw_clip.y, clip_region->y,
stage_cogl->bounding_redraw_clip.width, clip_region->width,
stage_cogl->bounding_redraw_clip.height); clip_region->height);
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper), _clutter_stage_do_paint (CLUTTER_STAGE (wrapper),
&stage_cogl->bounding_redraw_clip); clip_region);
cogl_clip_pop (); cogl_clip_pop ();
stage_cogl->using_clipped_redraw = FALSE; stage_cogl->using_clipped_redraw = FALSE;
@ -398,7 +464,7 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
may_use_clipped_redraw) may_use_clipped_redraw)
{ {
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper), _clutter_stage_do_paint (CLUTTER_STAGE (wrapper),
&stage_cogl->bounding_redraw_clip); clip_region);
} }
else else
_clutter_stage_do_paint (CLUTTER_STAGE (wrapper), NULL); _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); CLUTTER_TIMER_STOP (_clutter_uprof_context, painting_timer);
/* push on the screen */ /* 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]; int copy_area[4];
/* XXX: It seems there will be a race here in that the stage /* 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; 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 static void
clutter_stage_window_iface_init (ClutterStageWindowIface *iface) 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->redraw = clutter_stage_cogl_redraw;
iface->get_active_framebuffer = clutter_stage_cogl_get_active_framebuffer; iface->get_active_framebuffer = clutter_stage_cogl_get_active_framebuffer;
iface->dirty_back_buffer = clutter_stage_cogl_dirty_back_buffer; iface->dirty_back_buffer = clutter_stage_cogl_dirty_back_buffer;
iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel;
} }
static void static void

View File

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