diff options
author | Alexander Larsson <alexl@redhat.com> | 2013-04-17 23:06:07 +0200 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2013-05-07 16:33:01 +0200 |
commit | fe0982818d6f646999187f95d473d1237f9bd9b2 (patch) | |
tree | f6f529c19ce7b009fbf9a77ae7458a74b10586b9 | |
parent | d22fd7223c75f4720ddb982c659efb0d8d7543c4 (diff) | |
download | gtk+-fe0982818d6f646999187f95d473d1237f9bd9b2.tar.gz |
gdkwindow: Remove implicit paints
We now only do one expose event per native window, so there will
only be one begin/end_paint() call. This means all the work with
implicit paints to combine the paints on a single double buffer
surface is unnecessary, so we can just delete it.
-rw-r--r-- | gdk/gdkinternals.h | 3 | ||||
-rw-r--r-- | gdk/gdkwindow.c | 428 |
2 files changed, 32 insertions, 399 deletions
diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index d4e867c3e9..d512220ed8 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -224,6 +224,7 @@ struct _GdkWindow guint native_visibility : 2; /* the native visibility of a impl windows */ guint viewable : 1; /* mapped and all parents mapped */ guint applied_shape : 1; + guint in_update : 1; GdkFullscreenMode fullscreen_mode; /* The GdkWindow that has the impl, ref:ed if another window. @@ -250,8 +251,6 @@ struct _GdkWindow GdkCursor *cursor; GHashTable *device_cursor; - GSList *implicit_paint; - cairo_region_t *shape; cairo_region_t *input_shape; diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index cdebf2ea76..336366958f 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -133,14 +133,6 @@ * For native windows we apply the calculated clip region as a window shape * so that eg. client side siblings that overlap the native child properly * draws over the native child window. - * - * We use something called a "implicit paint" during repaint handling. - * An implicit paint is similar to a regular paint for the paint stack, but it is - * not put on the stack. Instead, it is set on the impl window, and later when - * regular gdk_window_begin_paint_region() happen on a window of this impl window - * we reuse the surface from the implicit paint. During repaint we create and at the - * end flush an implicit paint, which means we can collect all the paints on - * multiple client side windows in the same backing store. */ /* This adds a local value to the GdkVisibilityState enum */ @@ -169,9 +161,6 @@ struct _GdkWindowPaint { cairo_region_t *region; cairo_surface_t *surface; - cairo_region_t *flushed; - guint8 alpha; - guint uses_implicit : 1; }; /* Global info */ @@ -196,7 +185,6 @@ static void gdk_window_clear_backing_region (GdkWindow *window, static void recompute_visible_regions (GdkWindow *private, gboolean recalculate_children); -static void gdk_window_flush_recursive (GdkWindow *window); static void gdk_window_invalidate_in_parent (GdkWindow *private); static void move_native_children (GdkWindow *private); static void update_cursor (GdkDisplay *display, @@ -2655,217 +2643,12 @@ gdk_window_get_content (GdkWindow *window) return content; } -/* This creates an empty "implicit" paint region for the impl window. - * By itself this does nothing, but real paints to this window - * or children of it can use this surface as backing to avoid allocating - * multiple surfaces for subwindow rendering. When doing so they - * add to the region of the implicit paint region, which will be - * pushed to the window when the implicit paint region is ended. - * Such paints should not copy anything to the window on paint end, but - * should rely on the implicit paint end. - * The implicit paint will be automatically ended if someone draws - * directly to the window or a child window. - */ -static gboolean -gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect, - gboolean with_alpha, guint8 alpha) -{ - GdkWindowPaint *paint; - - g_assert (gdk_window_has_impl (window)); - - if (GDK_IS_PAINTABLE (window->impl)) - return FALSE; /* Implementation does double buffering */ - - if (window->paint_stack != NULL) - return FALSE; /* Don't stack implicit paints */ - - /* Never do implicit paints for foreign windows, they don't need - * double buffer combination since they have no client side children, - * and creating surfaces for them is risky since they could disappear - * at any time - */ - if (window->window_type == GDK_WINDOW_FOREIGN) - return FALSE; - - paint = g_new (GdkWindowPaint, 1); - paint->region = cairo_region_create (); /* Empty */ - paint->uses_implicit = FALSE; - paint->flushed = NULL; - paint->alpha = alpha; - paint->surface = gdk_window_create_similar_surface (window, - with_alpha ? CAIRO_CONTENT_COLOR_ALPHA : gdk_window_get_content (window), - MAX (rect->width, 1), - MAX (rect->height, 1)); - cairo_surface_set_device_offset (paint->surface, -rect->x, -rect->y); - - window->implicit_paint = g_slist_prepend (window->implicit_paint, paint); - - return TRUE; -} - static cairo_surface_t * gdk_window_ref_impl_surface (GdkWindow *window) { return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->ref_cairo_surface (gdk_window_get_impl_window (window)); } -static cairo_t * -gdk_cairo_create_for_impl (GdkWindow *window) -{ - cairo_surface_t *surface; - cairo_t *cr; - - surface = gdk_window_ref_impl_surface (window); - cr = cairo_create (surface); - - cairo_surface_destroy (surface); - - return cr; -} - -/* This is called whenever something is drawing directly to the - * window, bypassing the double buffering. When this happens we - * need to mark any the currently drawn data in the double buffer - * as invalid to avoid later drawing it back over the directly - * rendered pixels. We also need to mark this region as "flushed" - * so that if we later try to paint on it double-buffered we need - * to read back the on-window pixels rather than relying on what - * is in the current double-buffer pixmap. - * - * Note that this doesn't correctly handle the case where the - * non-double buffered drawing uses transparency and relies on - * what the windows below it draws. A fix for that would require - * drawing the existing double-buffered background to the window, - * but that causes ugly flashes. Non-double buffered drawing is - * typically only used in old code or when the drawed widget - * already has a double-buffering layer, and in these cases the - * pixels are opaque anyway. If you need transparency, don't - * disable double buffering. - */ -static void -gdk_window_flush_implicit_paint (GdkWindow *window) -{ - GdkWindow *impl_window; - GdkWindowPaint *paint; - cairo_region_t *region; - GSList *l; - - impl_window = gdk_window_get_impl_window (window); - if (impl_window->implicit_paint == NULL) - return; - - /* There is no proper way to flush any non-toplevel implicit - paint, because direct rendering is not compatible with - opacity rendering, as it requires an opacity group. - - We warn and flush just the outermost paint in this case. - */ - l = g_slist_last (impl_window->implicit_paint); - if (l != impl_window->implicit_paint) - g_warning ("Non double buffered drawing not supported inside transparent windows"); - - paint = l->data; - - region = cairo_region_copy (window->clip_region); - cairo_region_translate (region, window->abs_x, window->abs_y); - - /* Anything in the whole flushed window that was drawn is now - considered unpainted, so that we don't push it back at the - end of the implicit paint overwriting the directly rendered - pixels. */ - cairo_region_subtract (paint->region, region); - - /* Save flushed area so we can read it back if we draw over it later */ - if (paint->flushed == NULL) - paint->flushed = region; - else - { - cairo_region_union (paint->flushed, region); - cairo_region_destroy (region); - } -} - -/* Ends an implicit paint, paired with gdk_window_begin_implicit_paint returning TRUE */ -static void -gdk_window_end_implicit_paint (GdkWindow *window) -{ - GdkWindowPaint *paint; - - g_assert (gdk_window_has_impl (window)); - - g_assert (window->implicit_paint != NULL); - - paint = window->implicit_paint->data; - - window->implicit_paint = g_slist_delete_link (window->implicit_paint, - window->implicit_paint); - - if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (paint->region) && paint->alpha > 0) - { - GdkWindowPaint *parent_paint = NULL; - cairo_t *cr; - - /* Some regions are valid, push these to window now */ - if (window->implicit_paint) - { - parent_paint = window->implicit_paint->data; - - /* If the toplevel implicit paint was flushed, restore it now - before we blend over it. */ - if (parent_paint->flushed != NULL && - !cairo_region_is_empty (parent_paint->flushed)) - { - cairo_surface_t *source_surface = gdk_window_ref_impl_surface (window); - cairo_region_t *flushed = cairo_region_copy (parent_paint->flushed); - - cr = cairo_create (parent_paint->surface); - cairo_set_source_surface (cr, source_surface, 0, 0); - cairo_surface_destroy (source_surface); - - cairo_region_intersect (flushed, paint->region); - gdk_cairo_region (cr, flushed); - cairo_clip (cr); - - cairo_region_subtract (parent_paint->flushed, flushed); - cairo_region_destroy (flushed); - - cairo_paint (cr); - cairo_destroy (cr); - } - - cr = cairo_create (parent_paint->surface); - } - else - cr = gdk_cairo_create_for_impl (window); - - gdk_cairo_region (cr, paint->region); - cairo_clip (cr); - cairo_set_source_surface (cr, paint->surface, 0, 0); - if (paint->alpha == 255) - { - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - } - else - { - cairo_set_operator (cr, CAIRO_OPERATOR_OVER); - cairo_paint_with_alpha (cr, paint->alpha / 255.0); - } - - cairo_destroy (cr); - - if (parent_paint) - cairo_region_union (parent_paint->region, paint->region); - } - - cairo_region_destroy (paint->region); - if (paint->flushed) - cairo_region_destroy (paint->flushed); - cairo_surface_destroy (paint->surface); - g_free (paint); -} - /** * gdk_window_begin_paint_rect: * @window: a #GdkWindow @@ -2939,8 +2722,7 @@ gdk_window_begin_paint_region (GdkWindow *window, const cairo_region_t *region) { GdkRectangle clip_box; - GdkWindowPaint *paint, *implicit_paint; - GdkWindow *impl_window; + GdkWindowPaint *paint; GSList *list; g_return_if_fail (GDK_IS_WINDOW (window)); @@ -2958,78 +2740,16 @@ gdk_window_begin_paint_region (GdkWindow *window, return; } - impl_window = gdk_window_get_impl_window (window); - implicit_paint = impl_window->implicit_paint != NULL ? impl_window->implicit_paint->data : NULL; - paint = g_new (GdkWindowPaint, 1); paint->region = cairo_region_copy (region); cairo_region_intersect (paint->region, window->clip_region); cairo_region_get_extents (paint->region, &clip_box); - cairo_region_translate (paint->region, window->abs_x, window->abs_y); - - /* Mark the region as valid on the implicit paint */ - - if (implicit_paint) - cairo_region_union (implicit_paint->region, paint->region); - - /* Convert back to normal coords */ - cairo_region_translate (paint->region, -window->abs_x, -window->abs_y); - - if (implicit_paint) - { - paint->uses_implicit = TRUE; - paint->surface = cairo_surface_create_for_rectangle (implicit_paint->surface, - window->abs_x + clip_box.x, - window->abs_y + clip_box.y, - MAX (clip_box.width, 1), - MAX (clip_box.height, 1)); - } - else - { - paint->uses_implicit = FALSE; - paint->surface = gdk_window_create_similar_surface (window, - gdk_window_get_content (window), - MAX (clip_box.width, 1), - MAX (clip_box.height, 1)); - } - - /* Normally alpha backgrounded client side windows are composited on the implicit paint - by being drawn in back to front order. However, if implicit paints are not used, for - instance if it was flushed due to a non-double-buffered paint in the middle of the - expose we need to copy in the existing data here. */ - if (!implicit_paint || - (implicit_paint && implicit_paint->flushed != NULL && !cairo_region_is_empty (implicit_paint->flushed))) - { - cairo_t *cr = cairo_create (paint->surface); - /* We can't use gdk_cairo_set_source_window here, as that might - flush the implicit paint at an unfortunate time, since this - would be detected as a draw during non-expose time */ - cairo_surface_t *source_surface = gdk_window_ref_impl_surface (impl_window); - cairo_set_source_surface (cr, source_surface, - - (window->abs_x + clip_box.x), - - (window->abs_y + clip_box.y)); - cairo_surface_destroy (source_surface); - - /* Only read back the flushed area if any */ - if (implicit_paint) - { - cairo_region_t *flushed = cairo_region_copy (implicit_paint->flushed); - /* Convert from impl coords */ - cairo_region_translate (flushed, -window->abs_x, -window->abs_y); - cairo_region_intersect (flushed, paint->region); - gdk_cairo_region (cr, flushed); - cairo_clip (cr); - - /* Convert to impl coords */ - cairo_region_translate (flushed, window->abs_x, window->abs_y); - cairo_region_subtract (implicit_paint->flushed, flushed); - cairo_region_destroy (flushed); - } - cairo_paint (cr); - cairo_destroy (cr); - } + paint->surface = gdk_window_create_similar_surface (window, + gdk_window_get_content (window), + MAX (clip_box.width, 1), + MAX (clip_box.height, 1)); cairo_surface_set_device_offset (paint->surface, -clip_box.x, -clip_box.y); @@ -3069,6 +2789,7 @@ gdk_window_end_paint (GdkWindow *window) GdkWindowPaint *paint; GdkRectangle clip_box; cairo_region_t *full_clip; + cairo_t *cr; g_return_if_fail (GDK_IS_WINDOW (window)); @@ -3096,24 +2817,28 @@ gdk_window_end_paint (GdkWindow *window) window->paint_stack); cairo_region_get_extents (paint->region, &clip_box); + full_clip = cairo_region_copy (window->clip_region); + cairo_region_intersect (full_clip, paint->region); - if (!paint->uses_implicit) + cr = gdk_cairo_create (window); + cairo_set_source_surface (cr, paint->surface, 0, 0); + gdk_cairo_region (cr, full_clip); + cairo_clip (cr); + if (gdk_window_has_impl (window) || + window->alpha == 255) { - cairo_t *cr; - - full_clip = cairo_region_copy (window->clip_region); - cairo_region_intersect (full_clip, paint->region); - - cr = gdk_cairo_create (window); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_surface (cr, paint->surface, 0, 0); - gdk_cairo_region (cr, full_clip); - cairo_fill (cr); - - cairo_destroy (cr); - cairo_region_destroy (full_clip); + cairo_paint (cr); + } + else + { + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_paint_with_alpha (cr, window->alpha / 255.0); } + cairo_destroy (cr); + cairo_region_destroy (full_clip); + cairo_surface_destroy (paint->surface); cairo_region_destroy (paint->region); g_free (paint); @@ -3190,33 +2915,6 @@ gdk_window_free_paint_stack (GdkWindow *window) void gdk_window_flush (GdkWindow *window) { - gdk_window_flush_implicit_paint (window); -} - -static void -gdk_window_flush_recursive_helper (GdkWindow *window, - GdkWindowImpl *impl) -{ - GdkWindow *child; - GList *l; - - for (l = window->children; l != NULL; l = l->next) - { - child = l->data; - - if (child->impl == impl) - /* Same impl, ignore */ - gdk_window_flush_recursive_helper (child, impl); - else - gdk_window_flush_recursive (child); - } -} - -static void -gdk_window_flush_recursive (GdkWindow *window) -{ - gdk_window_flush (window); - gdk_window_flush_recursive_helper (window, window->impl); } /** @@ -3395,10 +3093,6 @@ _gdk_window_ref_cairo_surface (GdkWindow *window) } else { - - /* This will be drawing directly to the window, so flush implicit paint */ - gdk_window_flush (window); - if (!window->cairo_surface) { window->cairo_surface = gdk_window_create_cairo_surface (window, @@ -3452,18 +3146,6 @@ gdk_cairo_create (GdkWindow *window) gdk_cairo_region (cr, window->clip_region); cairo_clip (cr); } - else - { - GdkWindowPaint *paint = window->paint_stack->data; - - /* Only needs to clip to region if piggybacking - on an implicit paint */ - if (paint->uses_implicit) - { - gdk_cairo_region (cr, paint->region); - cairo_clip (cr); - } - } cairo_surface_destroy (surface); @@ -3608,7 +3290,6 @@ _gdk_window_process_updates_recurse (GdkWindow *window, cairo_region_t *clipped_expose_region; GdkRectangle clip_box; GList *l, *children; - gboolean end_implicit; if (cairo_region_is_empty (expose_region)) return; @@ -3617,7 +3298,6 @@ _gdk_window_process_updates_recurse (GdkWindow *window, gdk_window_has_impl (window)) _gdk_window_add_damage ((GdkWindow *) window->impl_window, expose_region); - end_implicit = FALSE; if (window->alpha != 255 && !gdk_window_has_impl (window)) { if (window->alpha == 0) @@ -3626,7 +3306,7 @@ _gdk_window_process_updates_recurse (GdkWindow *window, cairo_region_get_extents (expose_region, &clip_box); clip_box.x += window->abs_x; clip_box.y += window->abs_y; - end_implicit = gdk_window_begin_implicit_paint (window->impl_window, &clip_box, TRUE, window->alpha); + /* TODO: How do we now do subwindow alphas */ } /* Paint the window before the children, clipped to the window region */ @@ -3681,9 +3361,6 @@ _gdk_window_process_updates_recurse (GdkWindow *window, } g_list_free_full (children, g_object_unref); - - if (end_implicit) - gdk_window_end_implicit_paint (window->impl_window); } /* Process and remove any invalid area on the native window by creating @@ -3699,6 +3376,8 @@ gdk_window_process_updates_internal (GdkWindow *window) /* Ensure the window lives while updating it */ g_object_ref (window); + window->in_update = TRUE; + /* If an update got queued during update processing, we can get a * window in the update queue that has an empty update_area. * just ignore it. @@ -3711,9 +3390,8 @@ gdk_window_process_updates_internal (GdkWindow *window) if (gdk_window_is_viewable (window)) { cairo_region_t *expose_region; - gboolean end_implicit; - /* Clip to part visible in toplevel */ + /* Clip to part visible in impl window */ cairo_region_intersect (update_area, window->clip_region); if (debug_updates) @@ -3723,63 +3401,19 @@ gdk_window_process_updates_internal (GdkWindow *window) g_usleep (70000); } - /* By now we an update region that should be repainted. However in order to - * get nicer drawing we do some tricks: - * - * First of all, each subwindow expose may be double buffered by - * itself (depending on widget setting) via - * gdk_window_begin/end_paint(). But we also do an "implicit" paint, - * creating a single surface the size of the invalid area on the - * native window which all the individual normal paints will draw - * into. This way in the normal case there will be only one surface - * allocated and only once surface draw done for all the windows - * in this native window. - * There are a couple of reasons this may fail, for instance, some - * backends (like quartz) do its own double buffering, so we disable - * gdk double buffering there. Secondly, some subwindow could be - * non-double buffered and draw directly to the window outside a - * begin/end_paint pair. That will be lead to a gdk_window_flush - * which immediately paints+removes the implicit paint (further - * paints will allocate their own surfaces). - * - * Secondly, we queue an "antiexpose" on the area that will be drawn by - * the expose, which means that any invalid region on the native window side - * before the first expose drawing operation will be discarded, as it - * has by then been overdrawn with valid data. This means we can - * avoid doing the unnecessary repaint any outstanding expose events. - */ - cairo_region_get_extents (update_area, &clip_box); - end_implicit = gdk_window_begin_implicit_paint (window, &clip_box, FALSE, 255); expose_region = cairo_region_copy (update_area); impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl); - if (!end_implicit) - { - save_region = impl_class->queue_antiexpose (window, update_area); - } - /* Render the invalid areas to the implicit paint, by sending exposes. - * May flush if non-double buffered widget draw. */ + save_region = impl_class->queue_antiexpose (window, update_area); impl_class->process_updates_recurse (window, expose_region); - - if (end_implicit) - { - /* By this time we know that any outstanding expose for this - * area is invalid and we can avoid it, so queue an antiexpose. - * If we flushed we have already started drawing to the window, so it would - * be to late to anti-expose now. Since this is merely an - * optimization we just avoid doing it at all in that case. - */ - if (window->implicit_paint != NULL && !((GdkWindowPaint *)window->implicit_paint->data)->flushed) - save_region = impl_class->queue_antiexpose (window, update_area); - - gdk_window_end_implicit_paint (window); - } cairo_region_destroy (expose_region); } if (!save_region) cairo_region_destroy (update_area); } + window->in_update = FALSE; + g_object_unref (window); } @@ -3926,7 +3560,7 @@ gdk_window_process_updates_with_mode (GdkWindow *window, /* Don't recurse into process_updates_internal, we'll * do the update later when idle instead. */ - impl_window->implicit_paint == NULL) + !impl_window->in_update) { gdk_window_process_updates_internal ((GdkWindow *)impl_window); gdk_window_remove_update_window ((GdkWindow *)impl_window); |