[gtk+] Always queue antiexposes after flushing outstanding moves



commit bd0755753a02a0898961be757e2b8bc2d243688d
Author: Alexander Larsson <alexl redhat com>
Date:   Fri Aug 14 12:09:02 2009 +0200

    Always queue antiexposes after flushing outstanding moves
    
    If antiexposures are queued and then we do the outstanding moves
    we will queue translations that will affect incomming exposes
    wrongly wrt the antiexposure. So, make sure we process the outstanding
    moves before doing the antiexposure.
    
    Additionally this commit adds a bunch of comments about how the
    expose handling works and fixes a small bug that made us always
    flush outstanding moves a little to early than necessary.

 gdk/gdkwindow.c |  105 +++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 79 insertions(+), 26 deletions(-)
---
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index ea7d27d..f42cc75 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -4839,6 +4839,13 @@ _gdk_window_process_updates_recurse (GdkWindow *window,
     }
 }
 
+/* Process and remove any invalid area on the native window by creating
+ * expose events for the window and all non-native descendants.
+ * Also processes any outstanding moves on the window before doing
+ * any drawing. Note that its possible to have outstanding moves without
+ * any invalid area as we use the update idle mechanism to coalesce
+ * multiple moves as well as multiple invalidations.
+ */
 static void
 gdk_window_process_updates_internal (GdkWindow *window)
 {
@@ -4860,7 +4867,6 @@ gdk_window_process_updates_internal (GdkWindow *window)
 	{
 	  GdkRectangle window_rect;
 	  GdkRegion *expose_region;
-	  GdkRegion *window_region;
 	  gboolean end_implicit;
 
 	  /* Clip to part visible in toplevel */
@@ -4873,24 +4879,12 @@ gdk_window_process_updates_internal (GdkWindow *window)
 	      g_usleep (70000);
 	    }
 
-	  save_region = GDK_WINDOW_IMPL_GET_IFACE (private->impl)->queue_antiexpose (window, update_area);
-	  if (save_region)
-	    expose_region = gdk_region_copy (update_area);
-	  else
-	    expose_region = update_area;
-
-	  window_rect.x = 0;
-	  window_rect.y = 0;
-	  window_rect.width = private->width;
-	  window_rect.height = private->height;
-
-	  window_region = gdk_region_rectangle (&window_rect);
-	  gdk_region_intersect (expose_region,
-				window_region);
-	  gdk_region_destroy (window_region);
-
-
-	  /* No need to do any moves that will end up over the update area */
+	  /* At this point we will be completely redrawing all of update_area.
+	   * If we have any outstanding moves that end up moving stuff inside
+	   * this area we don't actually need to move that as that part would
+	   * be overdrawn by the expose anyway. So, in order to copy less data
+	   * we remove these areas from the outstanding moves.
+	   */
 	  if (private->outstanding_moves)
 	    {
 	      GdkWindowRegionMove *move;
@@ -4898,10 +4892,13 @@ gdk_window_process_updates_internal (GdkWindow *window)
 	      GList *l, *prev;
 
 	      remove = gdk_region_copy (update_area);
+	      /* We iterate backwards, starting from the state that would be
+		 if we had applied all the moves. */
 	      for (l = g_list_last (private->outstanding_moves); l != NULL; l = prev)
 		{
 		  prev = l->prev;
 		  move = l->data;
+
 		  /* Don't need this area */
 		  gdk_region_subtract (move->dest_region, remove);
 
@@ -4917,26 +4914,82 @@ gdk_window_process_updates_internal (GdkWindow *window)
 		      private->outstanding_moves =
 			g_list_delete_link (private->outstanding_moves, l);
 		    }
-		  else
+		  else /* move back */
 		    gdk_region_offset (move->dest_region, move->dx, move->dy);
 		}
 	      gdk_region_destroy (remove);
 	    }
 
-	  gdk_region_get_clipbox (expose_region, &clip_box);
+	  /* By now we a set of window moves that should be applied, and then
+	   * an update region that should be repainted. A trivial implementation
+	   * would just do that in order, 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 pixmap 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 pixmap
+	   * allocated and only once pixmap 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 executes all outstanding moves and paints+removes
+	   * the implicit paint (further paints will allocate their own pixmap).
+	   *
+	   * Secondly, in the case of implicit double buffering we expose all
+	   * the child windows into the implicit pixmap before we execute
+	   * the outstanding moves. This way we minimize the time between
+	   * doing the moves and rendering the new update area, thus minimizing
+	   * flashing. Of course, if any subwindow is non-double buffered we
+	   * well flush earlier than that.
+	   *
+	   * Thirdly, after having done the outstanding moves 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.
+	   */
+
+	  gdk_region_get_clipbox (update_area, &clip_box);
 	  end_implicit = gdk_window_begin_implicit_paint (window, &clip_box);
-	  if (end_implicit) /* rendering is not double buffered, do moves now */
+	  expose_region = gdk_region_copy (update_area);
+	  if (!end_implicit)
+	    {
+	      /* Rendering is not double buffered by gdk, do outstanding
+	       * moves and queue antiexposure immediately. No need to do
+	       * any tricks */
 	      gdk_window_flush_outstanding_moves (window);
+	      save_region = GDK_WINDOW_IMPL_GET_IFACE (private->impl)->queue_antiexpose (window, update_area);
+	    }
+
+	  /* Render the invalid areas to the implicit paint, by sending exposes.
+	   * May flush if non-double buffered widget draw. */
 	  _gdk_windowing_window_process_updates_recurse (window, expose_region);
+
 	  if (end_implicit)
 	    {
-	      /* Do moves right before exposes are rendered */
+	      /* Do moves right before exposes are rendered to the window */
 	      gdk_window_flush_outstanding_moves (window);
+
+	      /* By this time we know that any outstanding expose for this
+	       * area is invalid and we can avoid it, so queue an antiexpose.
+	       * However, it may be that due to an non-double buffered expose
+	       * 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 (private->implicit_paint != NULL) /* didn't flush implicit paint */
+		save_region = GDK_WINDOW_IMPL_GET_IFACE (private->impl)->queue_antiexpose (window, update_area);
+
 	      gdk_window_end_implicit_paint (window);
 	    }
-
-	  if (expose_region != update_area)
-	    gdk_region_destroy (expose_region);
+	  gdk_region_destroy (expose_region);
 	}
       if (!save_region)
 	gdk_region_destroy (update_area);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]