summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdel Gadllah <adel.gadllah@gmail.com>2013-02-06 11:05:58 +0100
committerAdel Gadllah <adel.gadllah@gmail.com>2013-02-06 18:21:27 +0100
commitb9ad93ad8d33aa41c80de440818e5240c82c7c39 (patch)
treef9a59f9100e84a526cf9224bafb243257681944f
parent60f20e8a7e7e8662d8a76401b9d28ac2ec1d1882 (diff)
downloadclutter-b9ad93ad8d33aa41c80de440818e5240c82c7c39.tar.gz
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
-rw-r--r--clutter/clutter-stage-window.c17
-rw-r--r--clutter/clutter-stage-window.h6
-rw-r--r--clutter/clutter-stage.c62
-rw-r--r--clutter/cogl/clutter-stage-cogl.c111
-rw-r--r--clutter/cogl/clutter-stage-cogl.h3
5 files changed, 167 insertions, 32 deletions
diff --git a/clutter/clutter-stage-window.c b/clutter/clutter-stage-window.c
index 941a5b566..2790a32e0 100644
--- a/clutter/clutter-stage-window.c
+++ b/clutter/clutter-stage-window.c
@@ -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)
{
diff --git a/clutter/clutter-stage-window.h b/clutter/clutter-stage-window.h
index 389f6174b..8ff756c61 100644
--- a/clutter/clutter-stage-window.h
+++ b/clutter/clutter-stage-window.h
@@ -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);
diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c
index c658b6a97..a62f0a4c7 100644
--- a/clutter/clutter-stage.c
+++ b/clutter/clutter-stage.c
@@ -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)
{
diff --git a/clutter/cogl/clutter-stage-cogl.c b/clutter/cogl/clutter-stage-cogl.c
index 1d9029ff0..151d9da2b 100644
--- a/clutter/cogl/clutter-stage-cogl.c
+++ b/clutter/cogl/clutter-stage-cogl.c
@@ -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
@@ -525,6 +591,26 @@ clutter_stage_cogl_dirty_back_buffer (ClutterStageWindow *stage_window)
}
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)
{
iface->realize = clutter_stage_cogl_realize;
@@ -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
diff --git a/clutter/cogl/clutter-stage-cogl.h b/clutter/cogl/clutter-stage-cogl.h
index 41dde935e..28b0ad1f5 100644
--- a/clutter/cogl/clutter-stage-cogl.h
+++ b/clutter/cogl/clutter-stage-cogl.h
@@ -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