synchronizing window frame/client repaint



Hi,

Some of us have been discussing/experimenting with ways to make opaque
resize look better. Soeren Sandmann has a really nice analysis of the
problem (Soeren maybe you could post that here?). 

Basically opaque resize looks a little funny, Soeren tracks it to two
kinds of lag:

 a) lag between the window frame and the application window. 
    especially with the current XFree86 scheduler, the app tends
    to get starved so the frame is redrawn a lot more often.

 b) lag between the mouse cursor (user's motion events) 
    and the frame/application composite. i.e. stuff just not being
    fast enough.

I have an experimental feature in metacity now called
_METACITY_UPDATE_COUNTER intended to partially address a). This works
as follows:

  - an application window may set a property on itself called
    _METACITY_UPDATE_COUNTER containing the resource ID of a sync
    extension "Counter" object

  - the counter is incremented whenever the application reaches 
    the end of a resize/redraw cycle and is in a "stable" state.

    I'm not sure how to define when to increment the counter, 
    here is roughly how it works:

     - a series of events (Expose and ConfigureNotify) are received by
       the application indicating that some repainting is required

     - the application "compresses" some number of these events
       and repaints everything in response to them, leaving a
       fully-updated window

     - at this point the application increments the update counter

     - the application MUST increment the counter anytime it receives
       a ConfigureNotify, even if no repainting is done

    Of course more exposes or configure notifies may be en route 
    when the counter is updated. But the point is that the application 
    increments the counter when it has just finished a batch of update
    work.

  - the window manager uses the sync extension's "alarm" feature to
    create an alarm event when the counter is incremented

  - the window manager resizes the application window and frame, waits
    for an alarm, resizes the application window and frame again,
    waits for another alarm, etc. i.e. the window manager only resizes
    the window as quickly as we can keep up with repainting it. Thus
    we never get lag between frame and app window.

This almost but not 100% addresses issue a). It isn't 100% because we
don't double-buffer the frame-window composite. i.e. you can still see
the frame and app window redraw in separate steps. However, they are
at least "in sync," we don't get 10 frame redraws for 1 app redraw or
anything like that. Achieving 100% here probably requires an X
extension. But the extension would probably be used in conjunction
with the sync counter:
 - WM pushes the double buffer and then resizes the frame and app
 - WM waits for the counter increment
 - WM pops the double buffer
or something like that.

You can sort of imagine doing this without an X extension (WM provides
a Pixmap to the client to be used for a double buffer, or something),
but I'm not sure that sounds like a great idea.

We don't get the full benefits of the sync extension because it seems
like a poor idea to block the WM. i.e. the WM could say "block all my
requests until the app counter increments" and then send a "pop double
buffer" request; but then your WM is locked up until the app
increments its counter.

Anyhow, once we fix this then lag b) is just a matter of making things
go fast, doesn't seem to be any magic solution other than optimizing
the code. But optimization has very little user-visible benefit before
we fix a).

If anyone wants to play with the metacity feature, I'll attach the GTK
patch, the metacity part of it is in CVS.

If other WMs want to adopt this feature we should probably put it in
the spec.

Havoc


Index: configure.in
===================================================================
RCS file: /cvs/gnome/gtk+/configure.in,v
retrieving revision 1.307
diff -u -p -u -r1.307 configure.in
--- configure.in	3 Dec 2002 03:11:41 -0000	1.307
+++ configure.in	9 Dec 2002 16:44:48 -0000
@@ -1149,6 +1149,16 @@ if test "x$gdktarget" = "xx11"; then
       ,
       $x_libs_for_checks)
 
+  # Check for XSync extension
+
+  AC_CHECK_LIB(Xext, XSyncQueryExtension,
+      if test -z "`echo $x_extra_libs $x_libs | grep "\-lXext" 2> /dev/null`"; then
+           x_extra_libs="-lXext $x_extra_libs"
+      fi
+      AC_DEFINE(HAVE_XSYNC),
+      ,
+      $x_libs_for_checks)
+
   # Check for XConvertCase (X11R6 specific)
 
   AC_CHECK_LIB(X11, XConvertCase,
Index: acconfig.h
===================================================================
RCS file: /cvs/gnome/gtk+/acconfig.h,v
retrieving revision 1.28
diff -u -p -u -r1.28 acconfig.h
--- acconfig.h	12 Jun 2002 18:48:09 -0000	1.28
+++ acconfig.h	9 Dec 2002 16:44:48 -0000
@@ -48,6 +48,9 @@
 #undef USE_GMODULE
 #undef USE_MMX
 
+/* Xsync extension */
+#undef HAVE_XSYNC
+
 /* Define to use XKB extension */
 #undef HAVE_XKB
 
Index: gdk/gdkgc.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkgc.c,v
retrieving revision 1.39
diff -u -p -u -r1.39 gdkgc.c
--- gdk/gdkgc.c	25 Sep 2002 07:23:52 -0000	1.39
+++ gdk/gdkgc.c	9 Dec 2002 16:44:48 -0000
@@ -112,7 +112,7 @@ gdk_gc_new_with_values (GdkDrawable	*dra
     {
       gc->colormap = gdk_drawable_get_colormap (drawable);
       if (gc->colormap)
-	g_object_ref (gc->colormap);
+        g_object_ref (gc->colormap);
     }
   
   return gc;
Index: gdk/gdkinternals.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkinternals.h,v
retrieving revision 1.27
diff -u -p -u -r1.27 gdkinternals.h
--- gdk/gdkinternals.h	31 Oct 2002 21:11:13 -0000	1.27
+++ gdk/gdkinternals.h	9 Dec 2002 16:44:48 -0000
@@ -308,6 +308,11 @@ void _gdk_windowing_display_set_sm_clien
 GType _gdk_window_impl_get_type (void) G_GNUC_CONST;
 GType _gdk_pixmap_impl_get_type (void) G_GNUC_CONST;
 
+/* Called after processing updates on a window */
+void _gdk_windowing_window_notify_updated (GdkWindow *window);
+/* Called when we get a configure notify on a window */
+void _gdk_window_must_notify_updated      (GdkWindow *window);
+
 /************************************
  * Initialization and exit routines *
  ************************************/
Index: gdk/gdkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.c,v
retrieving revision 1.146
diff -u -p -u -r1.146 gdkwindow.c
--- gdk/gdkwindow.c	28 Nov 2002 00:33:03 -0000	1.146
+++ gdk/gdkwindow.c	9 Dec 2002 16:44:48 -0000
@@ -2067,6 +2067,7 @@ gdk_window_copy_to_image (GdkDrawable   
  */
 
 static GSList *update_windows = NULL;
+static GSList *update_notify_windows = NULL;
 static guint update_idle = 0;
 static gboolean debug_updates = FALSE;
 
@@ -2133,6 +2134,18 @@ gdk_window_process_updates_internal (Gdk
 
 	  if (expose_region != update_area)
 	    gdk_region_destroy (expose_region);
+
+          /* Notify windowing system of updates, X11 uses this
+           * to notify the window manager
+           */
+          {
+            GdkWindow *top;
+
+            top = gdk_window_get_toplevel (window);
+            if (top != NULL)
+              _gdk_window_must_notify_updated (top);
+          }
+          
 	  g_object_unref (window);
 	}
       if (!save_region)
@@ -2140,6 +2153,51 @@ gdk_window_process_updates_internal (Gdk
     }
 }
 
+static void
+gdk_window_notify_updates (void)
+{
+  GSList *tmp_list = update_notify_windows;
+
+  while (tmp_list != NULL)
+    {
+      GSList *next = tmp_list->next;
+      GdkWindow *window = tmp_list->data;
+      
+      _gdk_windowing_window_notify_updated (window);
+      g_object_unref (G_OBJECT (window));
+      
+      tmp_list = next;
+    }
+
+  g_slist_free (update_notify_windows);
+  update_notify_windows = NULL;
+}
+
+/* FIXME move this */
+static gboolean gdk_window_update_idle (gpointer data);
+
+void
+_gdk_window_must_notify_updated (GdkWindow *window)
+{
+  if (GDK_WINDOW_OBJECT (window)->window_type == GDK_WINDOW_TOPLEVEL ||
+      GDK_WINDOW_OBJECT (window)->window_type == GDK_WINDOW_DIALOG)
+    {
+      if (g_slist_find (update_notify_windows, window) == NULL)
+        {
+          update_notify_windows = g_slist_prepend (update_notify_windows,
+                                                   window);
+          g_object_ref (window);
+        }
+
+      /* be sure we process updates, even if there's not actually
+       * any invalid region.
+       */
+      if (!GDK_WINDOW_OBJECT (window)->update_freeze_count && !update_idle)
+        update_idle = g_idle_add_full (GDK_PRIORITY_REDRAW,
+                                       gdk_window_update_idle, NULL, NULL);
+    }
+}
+
 /**
  * gdk_window_process_all_updates:
  *
@@ -2167,10 +2225,12 @@ gdk_window_process_all_updates (void)
       g_object_unref (tmp_list->data);
       tmp_list = tmp_list->next;
     }
-
+  
   g_slist_free (old_update_windows);
 
-  gdk_flush();
+  gdk_window_notify_updates ();
+  
+  gdk_flush ();
 }
 
 static gboolean
@@ -2222,6 +2282,8 @@ gdk_window_process_updates (GdkWindow *w
 	  tmp_list = tmp_list->next;
 	}
     }
+
+  gdk_window_notify_updates ();
 }
 
 /**
Index: gdk/x11/gdkdisplay-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkdisplay-x11.c,v
retrieving revision 1.24
diff -u -p -u -r1.24 gdkdisplay-x11.c
--- gdk/x11/gdkdisplay-x11.c	28 Nov 2002 00:33:05 -0000	1.24
+++ gdk/x11/gdkdisplay-x11.c	9 Dec 2002 16:44:48 -0000
@@ -213,6 +213,20 @@ gdk_display_open (const gchar *display_n
   }
 #endif
 
+  display_x11->use_xsync = FALSE;
+#ifdef HAVE_XSYNC
+  {
+    int major, minor;
+    int error_base, event_base;
+    
+    if (XSyncQueryExtension (display_x11->xdisplay,
+                              &event_base, &error_base) &&
+        XSyncInitialize (display_x11->xdisplay,
+                         &major, &minor))
+      display_x11->use_xsync = TRUE;
+  }
+#endif
+  
   _gdk_windowing_image_init (display);
   _gdk_events_init (display);
   _gdk_input_init (display);
Index: gdk/x11/gdkdisplay-x11.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkdisplay-x11.h,v
retrieving revision 1.8
diff -u -p -u -r1.8 gdkdisplay-x11.h
--- gdk/x11/gdkdisplay-x11.h	8 Nov 2002 22:29:32 -0000	1.8
+++ gdk/x11/gdkdisplay-x11.h	9 Dec 2002 16:44:48 -0000
@@ -24,8 +24,14 @@
 #ifndef __GDK_DISPLAY_X11__
 #define __GDK_DISPLAY_X11__
 
+#include <config.h>
+
 #include <X11/X.h>
 #include <X11/Xlib.h>
+#ifdef HAVE_XSYNC
+#include <X11/extensions/sync.h>
+#endif
+
 #include <glib.h>
 #include <gdk/gdkdisplay.h>
 #include <gdk/gdkkeys.h>
@@ -136,6 +142,9 @@ struct _GdkDisplayX11
 
   /* Startup notification */
   gchar *startup_notification_id;
+
+  /* XSync */
+  gboolean use_xsync;
 };
 
 struct _GdkDisplayX11Class
Index: gdk/x11/gdkevents-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkevents-x11.c,v
retrieving revision 1.104
diff -u -p -u -r1.104 gdkevents-x11.c
--- gdk/x11/gdkevents-x11.c	9 Dec 2002 02:41:51 -0000	1.104
+++ gdk/x11/gdkevents-x11.c	9 Dec 2002 16:44:49 -0000
@@ -1627,6 +1627,9 @@ gdk_event_translate (GdkDisplay *display
 	      if (window_private->resize_count == 0)
 		_gdk_moveresize_configure_done (display, window);
 	    }
+
+          /* be sure we increment our update counter */
+          _gdk_window_must_notify_updated (window);
 	}
       break;
       
Index: gdk/x11/gdkwindow-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkwindow-x11.c,v
retrieving revision 1.179
diff -u -p -u -r1.179 gdkwindow-x11.c
--- gdk/x11/gdkwindow-x11.c	3 Dec 2002 21:57:13 -0000	1.179
+++ gdk/x11/gdkwindow-x11.c	9 Dec 2002 16:44:50 -0000
@@ -365,6 +365,41 @@ check_leader_window_title (GdkDisplay *d
     }
 }
 
+static void
+create_update_counter (GdkDisplay       *display,
+                       GdkWindowImplX11 *impl)
+{
+  impl->update_counter = None;
+#ifdef HAVE_XSYNC
+  if (g_getenv ("GDK_DISABLE_UPDATE_COUNTER") == NULL)
+    {
+      XSyncValue value;
+      /* we init to 1, so that 0 can be "invalid" */
+      XSyncIntToValue (&value, 1);
+      impl->update_counter = XSyncCreateCounter (GDK_DISPLAY_XDISPLAY (display),
+                                                 value);
+      
+      XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+                       GDK_DRAWABLE_IMPL_X11 (impl)->xid, 
+                       
+                       gdk_x11_get_xatom_by_name_for_display (display, "_METACITY_UPDATE_COUNTER"),
+                       gdk_x11_get_xatom_by_name_for_display (display, "SYNC_COUNTER"),
+                       32, PropModeReplace,
+                       (guchar *) &impl->update_counter, 1);
+    }
+#endif /* HAVE_XSYNC */
+}
+
+static void
+destroy_update_counter (GdkDisplay       *display,
+                        GdkWindowImplX11 *impl)
+{
+#ifdef HAVE_XSYNC
+  if (impl->update_counter != None)
+    XSyncDestroyCounter (GDK_DISPLAY_XDISPLAY (display), impl->update_counter);
+#endif /* HAVE_XSYNC */
+}
+
 /**
  * gdk_window_new:
  * @parent: a #GdkWindow, or %NULL to create the window as a child of
@@ -616,7 +651,11 @@ gdk_window_new (GdkWindow     *parent,
     {
     case GDK_WINDOW_DIALOG:
       XSetTransientForHint (xdisplay, xid, xparent);
+      /* FALL THROUGH */
     case GDK_WINDOW_TOPLEVEL:
+      create_update_counter (gdk_drawable_get_display (window),
+                             impl);
+      /* FALL THROUGH */
     case GDK_WINDOW_TEMP:
       set_wm_protocols (window);
       break;
@@ -877,6 +916,9 @@ _gdk_windowing_window_destroy (GdkWindow
   }
 #endif /* HAVE_XFT */  
 
+  destroy_update_counter (gdk_drawable_get_display (window),
+                          GDK_WINDOW_IMPL_X11 (private->impl));
+  
   if (private->window_type == GDK_WINDOW_FOREIGN)
     {
       if (!foreign_destroy && (private->parent != NULL))
@@ -5013,4 +5055,26 @@ gdk_window_begin_move_drag (GdkWindow *w
 		       timestamp);
   else
     emulate_move_drag (window, button, root_x, root_y, timestamp);
+}
+
+void
+_gdk_windowing_window_notify_updated (GdkWindow *window)
+{
+#ifdef HAVE_XSYNC
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+  
+  if (impl->update_counter != None)
+    {
+      XSyncValue value;
+      
+      XSyncIntToValue (&value, 1);
+      
+      XSyncChangeCounter (GDK_DRAWABLE_XDISPLAY (window),
+                          impl->update_counter, value);
+    }
+#endif
 }
Index: gdk/x11/gdkwindow-x11.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkwindow-x11.h,v
retrieving revision 1.6
diff -u -p -u -r1.6 gdkwindow-x11.h
--- gdk/x11/gdkwindow-x11.h	9 Dec 2002 02:41:51 -0000	1.6
+++ gdk/x11/gdkwindow-x11.h	9 Dec 2002 16:44:50 -0000
@@ -96,6 +96,9 @@ struct _GdkWindowImplX11
    * that might not even be part of this app
    */
   Window focus_window;
+
+  /* XSyncCounter */
+  XID update_counter;
 };
  
 struct _GdkWindowImplX11Class 


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