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 @@ -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 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