[mutter] compositor: Add support for GL_EXT_x11_sync_object



commit 39763d4addd7fab1b6ab02622577413fa0228254
Author: Rui Matos <tiagomatos gmail com>
Date:   Fri Apr 18 20:21:20 2014 +0200

    compositor: Add support for GL_EXT_x11_sync_object
    
    If GL advertises this extension we'll use it to synchronize X with GL
    rendering instead of relying on the XSync() behavior with open source
    drivers.
    
    Some driver bugs were uncovered while working on this so if we have
    had to reboot the ring a few times, something is probably wrong and
    we're likely to just make things worse by continuing to try.  Let's
    err on the side of caution, disable ourselves and fallback to the
    XSync() path in the compositor.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=728464

 configure.ac                        |    5 +
 src/Makefile.am                     |    2 +
 src/backends/x11/meta-backend-x11.c |    3 +
 src/compositor/compositor-private.h |    4 +-
 src/compositor/compositor.c         |   70 +++--
 src/compositor/meta-sync-ring.c     |  566 +++++++++++++++++++++++++++++++++++
 src/compositor/meta-sync-ring.h     |   14 +
 7 files changed, 641 insertions(+), 23 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 6edc0da..382c0e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -319,6 +319,11 @@ if test "x$enable_debug" = "xyes"; then
        CFLAGS="$CFLAGS -g -O"
 fi
 
+AC_CHECK_DECL([GL_EXT_x11_sync_object],
+              [],
+              [AC_MSG_ERROR([GL_EXT_x11_sync_object definition not found, please update your GL headers])],
+              [#include <GL/glx.h>])
+
 #### Warnings (last since -Werror can disturb other tests)
 
 # Stay command-line compatible with the gnome-common configure option. Here
diff --git a/src/Makefile.am b/src/Makefile.am
index 0718c6b..c02fe6a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -138,6 +138,8 @@ libmutter_la_SOURCES =                              \
        compositor/meta-surface-actor.h         \
        compositor/meta-surface-actor-x11.c     \
        compositor/meta-surface-actor-x11.h     \
+       compositor/meta-sync-ring.c             \
+       compositor/meta-sync-ring.h             \
        compositor/meta-texture-rectangle.c     \
        compositor/meta-texture-rectangle.h     \
        compositor/meta-texture-tower.c         \
diff --git a/src/backends/x11/meta-backend-x11.c b/src/backends/x11/meta-backend-x11.c
index 604281d..99727fd 100644
--- a/src/backends/x11/meta-backend-x11.c
+++ b/src/backends/x11/meta-backend-x11.c
@@ -45,6 +45,7 @@
 #include <meta/util.h>
 #include "display-private.h"
 #include "compositor/compositor-private.h"
+#include "compositor/meta-sync-ring.h"
 
 typedef enum {
   /* We're a traditional CM running under the host. */
@@ -267,6 +268,8 @@ handle_host_xevent (MetaBackend *backend,
         MetaCompositor *compositor = display->compositor;
         if (meta_plugin_manager_xevent_filter (compositor->plugin_mgr, event))
           bypass_clutter = TRUE;
+        if (compositor->have_x11_sync_object)
+          meta_sync_ring_handle_event (event);
       }
   }
 
diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h
index 80fb4e2..9e3e73d 100644
--- a/src/compositor/compositor-private.h
+++ b/src/compositor/compositor-private.h
@@ -15,7 +15,8 @@ struct _MetaCompositor
 {
   MetaDisplay    *display;
 
-  guint           repaint_func_id;
+  guint           pre_paint_func_id;
+  guint           post_paint_func_id;
 
   gint64          server_time_query_time;
   gint64          server_time_offset;
@@ -40,6 +41,7 @@ struct _MetaCompositor
   MetaPluginManager *plugin_mgr;
 
   gboolean frame_has_updated_xsurfaces;
+  gboolean have_x11_sync_object;
 };
 
 /* Wait 2ms after vblank before starting to draw next frame */
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index d1e9561..1282279 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -74,6 +74,7 @@
 #include "frame.h"
 #include <X11/extensions/shape.h>
 #include <X11/extensions/Xcomposite.h>
+#include "meta-sync-ring.h"
 
 #include "backends/x11/meta-backend-x11.h"
 
@@ -120,7 +121,11 @@ meta_switch_workspace_completed (MetaCompositor *compositor)
 void
 meta_compositor_destroy (MetaCompositor *compositor)
 {
-  clutter_threads_remove_repaint_func (compositor->repaint_func_id);
+  clutter_threads_remove_repaint_func (compositor->pre_paint_func_id);
+  clutter_threads_remove_repaint_func (compositor->post_paint_func_id);
+
+  if (compositor->have_x11_sync_object)
+    meta_sync_ring_destroy ();
 }
 
 static void
@@ -463,13 +468,11 @@ meta_compositor_manage (MetaCompositor *compositor)
   MetaDisplay *display = compositor->display;
   Display *xdisplay = display->xdisplay;
   MetaScreen *screen = display->screen;
+  MetaBackend *backend = meta_get_backend ();
 
   meta_screen_set_cm_selection (display->screen);
 
-  {
-    MetaBackend *backend = meta_get_backend ();
-    compositor->stage = meta_backend_get_stage (backend);
-  }
+  compositor->stage = meta_backend_get_stage (backend);
 
   /* We use connect_after() here to accomodate code in GNOME Shell that,
    * when benchmarking drawing performance, connects to ::after-paint
@@ -505,7 +508,7 @@ meta_compositor_manage (MetaCompositor *compositor)
 
       compositor->output = screen->composite_overlay_window;
 
-      xwin = meta_backend_x11_get_xwindow (META_BACKEND_X11 (meta_get_backend ()));
+      xwin = meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend));
 
       XReparentWindow (xdisplay, xwin, compositor->output, 0, 0);
 
@@ -525,6 +528,9 @@ meta_compositor_manage (MetaCompositor *compositor)
        * contents until we show the stage.
        */
       XMapWindow (xdisplay, compositor->output);
+
+      compositor->have_x11_sync_object =
+        meta_sync_ring_init (meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)));
     }
 
   redirect_windows (display->screen);
@@ -1030,11 +1036,12 @@ frame_callback (CoglOnscreen  *onscreen,
     }
 }
 
-static void
-pre_paint_windows (MetaCompositor *compositor)
+static gboolean
+meta_pre_paint_func (gpointer data)
 {
   GList *l;
   MetaWindowActor *top_window;
+  MetaCompositor *compositor = data;
 
   if (compositor->onscreen == NULL)
     {
@@ -1046,7 +1053,7 @@ pre_paint_windows (MetaCompositor *compositor)
     }
 
   if (compositor->windows == NULL)
-    return;
+    return TRUE;
 
   top_window = g_list_last (compositor->windows)->data;
 
@@ -1063,10 +1070,12 @@ pre_paint_windows (MetaCompositor *compositor)
     {
       /* We need to make sure that any X drawing that happens before
        * the XDamageSubtract() for each window above is visible to
-       * subsequent GL rendering; the only standardized way to do this
-       * is EXT_x11_sync_object, which isn't yet widely available. For
-       * now, we count on details of Xorg and the open source drivers,
-       * and hope for the best otherwise.
+       * subsequent GL rendering; the standardized way to do this is
+       * GL_EXT_X11_sync_object. Since this isn't implemented yet in
+       * mesa, we also have a path that relies on the implementation
+       * of the open source drivers.
+       *
+       * Anything else, we just hope for the best.
        *
        * Xorg and open source driver specifics:
        *
@@ -1081,17 +1090,28 @@ pre_paint_windows (MetaCompositor *compositor)
        * round trip request at this point is sufficient to flush the
        * GLX buffers.
        */
-      XSync (compositor->display->xdisplay, False);
-
-      compositor->frame_has_updated_xsurfaces = FALSE;
+      if (compositor->have_x11_sync_object)
+        compositor->have_x11_sync_object = meta_sync_ring_insert_wait ();
+      else
+        XSync (compositor->display->xdisplay, False);
     }
+
+  return TRUE;
 }
 
 static gboolean
-meta_repaint_func (gpointer data)
+meta_post_paint_func (gpointer data)
 {
   MetaCompositor *compositor = data;
-  pre_paint_windows (compositor);
+
+  if (compositor->frame_has_updated_xsurfaces)
+    {
+      if (compositor->have_x11_sync_object)
+        compositor->have_x11_sync_object = meta_sync_ring_after_frame ();
+
+      compositor->frame_has_updated_xsurfaces = FALSE;
+    }
+
   return TRUE;
 }
 
@@ -1126,10 +1146,16 @@ meta_compositor_new (MetaDisplay *display)
                     G_CALLBACK (on_shadow_factory_changed),
                     compositor);
 
-  compositor->repaint_func_id = clutter_threads_add_repaint_func (meta_repaint_func,
-                                                                  compositor,
-                                                                  NULL);
-
+  compositor->pre_paint_func_id =
+    clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_PRE_PAINT,
+                                           meta_pre_paint_func,
+                                           compositor,
+                                           NULL);
+  compositor->post_paint_func_id =
+    clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT,
+                                           meta_post_paint_func,
+                                           compositor,
+                                           NULL);
   return compositor;
 }
 
diff --git a/src/compositor/meta-sync-ring.c b/src/compositor/meta-sync-ring.c
new file mode 100644
index 0000000..4ee61f8
--- /dev/null
+++ b/src/compositor/meta-sync-ring.c
@@ -0,0 +1,566 @@
+/*
+ * This is based on an original C++ implementation for compiz that
+ * carries the following copyright notice:
+ *
+ *
+ * Copyright © 2011 NVIDIA Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of NVIDIA
+ * Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission.  NVIDIA Corporation makes no representations about the
+ * suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * NVIDIA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: James Jones <jajones nvidia com>
+ */
+
+#include <string.h>
+
+#include <GL/gl.h>
+#include <GL/glx.h>
+#include <X11/extensions/sync.h>
+
+#include <cogl/cogl.h>
+#include <clutter/clutter.h>
+
+#include <meta/util.h>
+
+#include "meta-sync-ring.h"
+
+/* Theory of operation:
+ *
+ * We use a ring of NUM_SYNCS fence objects. On each frame we advance
+ * to the next fence in the ring. For each fence we do:
+ *
+ * 1. fence is XSyncTriggerFence()'d and glWaitSync()'d
+ * 2. NUM_SYNCS / 2 frames later, fence should be triggered
+ * 3. fence is XSyncResetFence()'d
+ * 4. NUM_SYNCS / 2 frames later, fence should be reset
+ * 5. go back to 1 and re-use fence
+ *
+ * glClientWaitSync() and XAlarms are used in steps 2 and 4,
+ * respectively, to double-check the expectections.
+ */
+
+#define NUM_SYNCS 10
+#define MAX_SYNC_WAIT_TIME (1 * 1000 * 1000 * 1000) /* one sec */
+#define MAX_REBOOT_ATTEMPTS 2
+
+typedef enum
+{
+  META_SYNC_STATE_READY,
+  META_SYNC_STATE_WAITING,
+  META_SYNC_STATE_DONE,
+  META_SYNC_STATE_RESET_PENDING,
+} MetaSyncState;
+
+typedef struct
+{
+  Display *xdisplay;
+
+  XSyncFence xfence;
+  GLsync glsync;
+
+  XSyncCounter xcounter;
+  XSyncAlarm xalarm;
+  XSyncValue next_counter_value;
+
+  MetaSyncState state;
+} MetaSync;
+
+typedef struct
+{
+  Display *xdisplay;
+  int xsync_event_base;
+  int xsync_error_base;
+
+  GHashTable *alarm_to_sync;
+
+  MetaSync *syncs_array[NUM_SYNCS];
+  guint current_sync_idx;
+  MetaSync *current_sync;
+  guint warmup_syncs;
+
+  guint reboots;
+} MetaSyncRing;
+
+static MetaSyncRing meta_sync_ring = { 0 };
+
+static XSyncValue SYNC_VALUE_ZERO;
+static XSyncValue SYNC_VALUE_ONE;
+
+static const char*      (*meta_gl_get_string) (GLenum name);
+static void             (*meta_gl_get_integerv) (GLenum  pname,
+                                                 GLint  *params);
+static const char*      (*meta_gl_get_stringi) (GLenum name,
+                                                GLuint index);
+static void             (*meta_gl_delete_sync) (GLsync sync);
+static GLenum           (*meta_gl_client_wait_sync) (GLsync sync,
+                                                     GLbitfield flags,
+                                                     GLuint64 timeout);
+static void             (*meta_gl_wait_sync) (GLsync sync,
+                                              GLbitfield flags,
+                                              GLuint64 timeout);
+static GLsync           (*meta_gl_import_sync) (GLenum external_sync_type,
+                                                GLintptr external_sync,
+                                                GLbitfield flags);
+
+static MetaSyncRing *
+meta_sync_ring_get (void)
+{
+  if (meta_sync_ring.reboots > MAX_REBOOT_ATTEMPTS)
+    return NULL;
+
+  return &meta_sync_ring;
+}
+
+static gboolean
+load_gl_symbol (const char  *name,
+                void       **func)
+{
+  *func = cogl_get_proc_address (name);
+  if (!*func)
+    {
+      meta_verbose ("MetaSyncRing: failed to resolve required GL symbol \"%s\"\n", name);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+static gboolean
+check_gl_extensions (void)
+{
+  ClutterBackend *backend;
+  CoglContext *cogl_context;
+  CoglDisplay *cogl_display;
+  CoglRenderer *cogl_renderer;
+
+  backend = clutter_get_default_backend ();
+  cogl_context = clutter_backend_get_cogl_context (backend);
+  cogl_display = cogl_context_get_display (cogl_context);
+  cogl_renderer = cogl_display_get_renderer (cogl_display);
+
+  switch (cogl_renderer_get_driver (cogl_renderer))
+    {
+    case COGL_DRIVER_GL3:
+      {
+        int num_extensions, i;
+        gboolean arb_sync = FALSE;
+        gboolean x11_sync_object = FALSE;
+
+        meta_gl_get_integerv (GL_NUM_EXTENSIONS, &num_extensions);
+
+        for (i = 0; i < num_extensions; ++i)
+          {
+            const char *ext = meta_gl_get_stringi (GL_EXTENSIONS, i);
+
+            if (g_strcmp0 ("GL_ARB_sync", ext) == 0)
+              arb_sync = TRUE;
+            else if (g_strcmp0 ("GL_EXT_x11_sync_object", ext) == 0)
+              x11_sync_object = TRUE;
+          }
+
+        return arb_sync && x11_sync_object;
+      }
+    case COGL_DRIVER_GL:
+      {
+        const char *extensions = meta_gl_get_string (GL_EXTENSIONS);
+        return (extensions != NULL &&
+                strstr (extensions, "GL_ARB_sync") != NULL &&
+                strstr (extensions, "GL_EXT_x11_sync_object") != NULL);
+      }
+    default:
+      break;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+load_required_symbols (void)
+{
+  static gboolean success = FALSE;
+
+  if (success)
+    return TRUE;
+
+  /* We don't link against libGL directly because cogl may want to
+   * use something else. This assumes that cogl has been initialized
+   * and dynamically loaded libGL at this point.
+   */
+
+  if (!load_gl_symbol ("glGetString", (void **) &meta_gl_get_string))
+    goto out;
+  if (!load_gl_symbol ("glGetIntegerv", (void **) &meta_gl_get_integerv))
+    goto out;
+  if (!load_gl_symbol ("glGetStringi", (void **) &meta_gl_get_stringi))
+    goto out;
+
+  if (!check_gl_extensions ())
+    {
+      meta_verbose ("MetaSyncRing: couldn't find required GL extensions\n");
+      goto out;
+    }
+
+  if (!load_gl_symbol ("glDeleteSync", (void **) &meta_gl_delete_sync))
+    goto out;
+  if (!load_gl_symbol ("glClientWaitSync", (void **) &meta_gl_client_wait_sync))
+    goto out;
+  if (!load_gl_symbol ("glWaitSync", (void **) &meta_gl_wait_sync))
+    goto out;
+  if (!load_gl_symbol ("glImportSyncEXT", (void **) &meta_gl_import_sync))
+    goto out;
+
+  success = TRUE;
+ out:
+  return success;
+}
+
+static void
+meta_sync_insert (MetaSync *self)
+{
+  g_return_if_fail (self->state == META_SYNC_STATE_READY);
+
+  XSyncTriggerFence (self->xdisplay, self->xfence);
+  XFlush (self->xdisplay);
+
+  meta_gl_wait_sync (self->glsync, 0, GL_TIMEOUT_IGNORED);
+
+  self->state = META_SYNC_STATE_WAITING;
+}
+
+static GLenum
+meta_sync_check_update_finished (MetaSync *self,
+                                 GLuint64  timeout)
+{
+  GLenum status = GL_WAIT_FAILED;
+
+  switch (self->state)
+    {
+    case META_SYNC_STATE_DONE:
+      status = GL_ALREADY_SIGNALED;
+      break;
+    case META_SYNC_STATE_WAITING:
+      status = meta_gl_client_wait_sync (self->glsync, 0, timeout);
+      if (status == GL_ALREADY_SIGNALED || status == GL_CONDITION_SATISFIED)
+        self->state = META_SYNC_STATE_DONE;
+      break;
+    default:
+      break;
+    }
+
+  g_warn_if_fail (status != GL_WAIT_FAILED);
+
+  return status;
+}
+
+static void
+meta_sync_reset (MetaSync *self)
+{
+  XSyncAlarmAttributes attrs;
+  int overflow;
+
+  g_return_if_fail (self->state == META_SYNC_STATE_DONE);
+
+  XSyncResetFence (self->xdisplay, self->xfence);
+
+  attrs.trigger.wait_value = self->next_counter_value;
+
+  XSyncChangeAlarm (self->xdisplay, self->xalarm, XSyncCAValue, &attrs);
+  XSyncSetCounter (self->xdisplay, self->xcounter, self->next_counter_value);
+
+  XSyncValueAdd (&self->next_counter_value,
+                 self->next_counter_value,
+                 SYNC_VALUE_ONE,
+                 &overflow);
+
+  self->state = META_SYNC_STATE_RESET_PENDING;
+}
+
+static void
+meta_sync_handle_event (MetaSync              *self,
+                        XSyncAlarmNotifyEvent *event)
+{
+  g_return_if_fail (event->alarm == self->xalarm);
+  g_return_if_fail (self->state == META_SYNC_STATE_RESET_PENDING);
+
+  self->state = META_SYNC_STATE_READY;
+}
+
+static MetaSync *
+meta_sync_new (Display *xdisplay)
+{
+  MetaSync *self;
+  XSyncAlarmAttributes attrs;
+
+  self = g_malloc0 (sizeof (MetaSync));
+
+  self->xdisplay = xdisplay;
+
+  self->xfence = XSyncCreateFence (xdisplay, DefaultRootWindow (xdisplay), FALSE);
+  self->glsync = meta_gl_import_sync (GL_SYNC_X11_FENCE_EXT, self->xfence, 0);
+
+  self->xcounter = XSyncCreateCounter (xdisplay, SYNC_VALUE_ZERO);
+
+  attrs.trigger.counter = self->xcounter;
+  attrs.trigger.value_type = XSyncAbsolute;
+  attrs.trigger.wait_value = SYNC_VALUE_ONE;
+  attrs.trigger.test_type = XSyncPositiveTransition;
+  attrs.events = TRUE;
+  self->xalarm = XSyncCreateAlarm (xdisplay,
+                                   XSyncCACounter |
+                                   XSyncCAValueType |
+                                   XSyncCAValue |
+                                   XSyncCATestType |
+                                   XSyncCAEvents,
+                                   &attrs);
+
+  XSyncIntToValue (&self->next_counter_value, 1);
+
+  self->state = META_SYNC_STATE_READY;
+
+  return self;
+}
+
+static Bool
+alarm_event_predicate (Display  *dpy,
+                       XEvent   *event,
+                       XPointer  data)
+{
+  MetaSyncRing *ring = meta_sync_ring_get ();
+
+  if (!ring)
+    return False;
+
+  if (event->type == ring->xsync_event_base + XSyncAlarmNotify)
+    {
+      if (((MetaSync *) data)->xalarm == ((XSyncAlarmNotifyEvent *) event)->alarm)
+        return True;
+    }
+  return False;
+}
+
+static void
+meta_sync_free (MetaSync *self)
+{
+  /* When our assumptions don't hold, something has gone wrong but we
+   * don't know what, so we reboot the ring. While doing that, we
+   * trigger fences before deleting them to try to get ourselves out
+   * of a potentially stuck GPU state.
+   */
+  switch (self->state)
+    {
+    case META_SYNC_STATE_WAITING:
+    case META_SYNC_STATE_DONE:
+      /* nothing to do */
+      break;
+    case META_SYNC_STATE_RESET_PENDING:
+      {
+        XEvent event;
+        XIfEvent (self->xdisplay, &event, alarm_event_predicate, (XPointer) self);
+        meta_sync_handle_event (self, (XSyncAlarmNotifyEvent *) &event);
+      }
+      /* fall through */
+    case META_SYNC_STATE_READY:
+      XSyncTriggerFence (self->xdisplay, self->xfence);
+      XFlush (self->xdisplay);
+      break;
+    default:
+      break;
+    }
+
+  meta_gl_delete_sync (self->glsync);
+  XSyncDestroyFence (self->xdisplay, self->xfence);
+  XSyncDestroyCounter (self->xdisplay, self->xcounter);
+  XSyncDestroyAlarm (self->xdisplay, self->xalarm);
+
+  g_free (self);
+}
+
+gboolean
+meta_sync_ring_init (Display *xdisplay)
+{
+  gint major, minor;
+  guint i;
+  MetaSyncRing *ring = meta_sync_ring_get ();
+
+  if (!ring)
+    return FALSE;
+
+  g_return_val_if_fail (xdisplay != NULL, FALSE);
+  g_return_val_if_fail (ring->xdisplay == NULL, FALSE);
+
+  if (!load_required_symbols ())
+    return FALSE;
+
+  if (!XSyncQueryExtension (xdisplay, &ring->xsync_event_base, &ring->xsync_error_base) ||
+      !XSyncInitialize (xdisplay, &major, &minor))
+    return FALSE;
+
+  XSyncIntToValue (&SYNC_VALUE_ZERO, 0);
+  XSyncIntToValue (&SYNC_VALUE_ONE, 1);
+
+  ring->xdisplay = xdisplay;
+
+  ring->alarm_to_sync = g_hash_table_new (NULL, NULL);
+
+  for (i = 0; i < NUM_SYNCS; ++i)
+    {
+      MetaSync *sync = meta_sync_new (ring->xdisplay);
+      ring->syncs_array[i] = sync;
+      g_hash_table_replace (ring->alarm_to_sync, (gpointer) sync->xalarm, sync);
+    }
+
+  ring->current_sync_idx = 0;
+  ring->current_sync = ring->syncs_array[0];
+  ring->warmup_syncs = 0;
+
+  return TRUE;
+}
+
+void
+meta_sync_ring_destroy (void)
+{
+  guint i;
+  MetaSyncRing *ring = meta_sync_ring_get ();
+
+  if (!ring)
+    return;
+
+  g_return_if_fail (ring->xdisplay != NULL);
+
+  ring->current_sync_idx = 0;
+  ring->current_sync = NULL;
+  ring->warmup_syncs = 0;
+
+  for (i = 0; i < NUM_SYNCS; ++i)
+    meta_sync_free (ring->syncs_array[i]);
+
+  g_hash_table_destroy (ring->alarm_to_sync);
+
+  ring->xsync_event_base = 0;
+  ring->xsync_error_base = 0;
+  ring->xdisplay = NULL;
+}
+
+static gboolean
+meta_sync_ring_reboot (Display *xdisplay)
+{
+  MetaSyncRing *ring = meta_sync_ring_get ();
+
+  if (!ring)
+    return FALSE;
+
+  meta_sync_ring_destroy ();
+
+  ring->reboots += 1;
+
+  if (!meta_sync_ring_get ())
+    {
+      meta_warning ("MetaSyncRing: Too many reboots -- disabling\n");
+      return FALSE;
+    }
+
+  return meta_sync_ring_init (xdisplay);
+}
+
+gboolean
+meta_sync_ring_after_frame (void)
+{
+  MetaSyncRing *ring = meta_sync_ring_get ();
+
+  if (!ring)
+    return FALSE;
+
+  g_return_if_fail (ring->xdisplay != NULL);
+
+  if (ring->warmup_syncs >= NUM_SYNCS / 2)
+    {
+      guint reset_sync_idx = (ring->current_sync_idx + NUM_SYNCS - (NUM_SYNCS / 2)) % NUM_SYNCS;
+      MetaSync *sync_to_reset = ring->syncs_array[reset_sync_idx];
+
+      GLenum status = meta_sync_check_update_finished (sync_to_reset, 0);
+      if (status == GL_TIMEOUT_EXPIRED)
+        {
+          meta_warning ("MetaSyncRing: We should never wait for a sync -- add more syncs?\n");
+          status = meta_sync_check_update_finished (sync_to_reset, MAX_SYNC_WAIT_TIME);
+        }
+
+      if (status != GL_ALREADY_SIGNALED && status != GL_CONDITION_SATISFIED)
+        {
+          meta_warning ("MetaSyncRing: Timed out waiting for sync object.\n");
+          return meta_sync_ring_reboot (ring->xdisplay);
+        }
+
+      meta_sync_reset (sync_to_reset);
+    }
+  else
+    {
+      ring->warmup_syncs += 1;
+    }
+
+  ring->current_sync_idx += 1;
+  ring->current_sync_idx %= NUM_SYNCS;
+
+  ring->current_sync = ring->syncs_array[ring->current_sync_idx];
+
+  return TRUE;
+}
+
+gboolean
+meta_sync_ring_insert_wait (void)
+{
+  MetaSyncRing *ring = meta_sync_ring_get ();
+
+  if (!ring)
+    return FALSE;
+
+  g_return_if_fail (ring->xdisplay != NULL);
+
+  if (ring->current_sync->state != META_SYNC_STATE_READY)
+    {
+      meta_warning ("MetaSyncRing: Sync object is not ready -- were events handled properly?\n");
+      if (!meta_sync_ring_reboot (ring->xdisplay))
+        return FALSE;
+    }
+
+  meta_sync_insert (ring->current_sync);
+
+  return TRUE;
+}
+
+void
+meta_sync_ring_handle_event (XEvent *xevent)
+{
+  XSyncAlarmNotifyEvent *event;
+  MetaSync *sync;
+  MetaSyncRing *ring = meta_sync_ring_get ();
+
+  if (!ring)
+    return;
+
+  g_return_if_fail (ring->xdisplay != NULL);
+
+  if (xevent->type != (ring->xsync_event_base + XSyncAlarmNotify))
+    return;
+
+  event = (XSyncAlarmNotifyEvent *) xevent;
+
+  sync = g_hash_table_lookup (ring->alarm_to_sync, (gpointer) event->alarm);
+  if (sync)
+    meta_sync_handle_event (sync, event);
+}
diff --git a/src/compositor/meta-sync-ring.h b/src/compositor/meta-sync-ring.h
new file mode 100644
index 0000000..6dca8ef
--- /dev/null
+++ b/src/compositor/meta-sync-ring.h
@@ -0,0 +1,14 @@
+#ifndef _META_SYNC_RING_H_
+#define _META_SYNC_RING_H_
+
+#include <glib.h>
+
+#include <X11/Xlib.h>
+
+gboolean meta_sync_ring_init (Display *dpy);
+void meta_sync_ring_destroy (void);
+gboolean meta_sync_ring_after_frame (void);
+gboolean meta_sync_ring_insert_wait (void);
+void meta_sync_ring_handle_event (XEvent *event);
+
+#endif  /* _META_SYNC_RING_H_ */


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