[mutter/wip/xtoglsync-3-10: 3/4] compositor: Add support for GL_EXT_x11_sync_object
- From: Rui Matos <rtcm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter/wip/xtoglsync-3-10: 3/4] compositor: Add support for GL_EXT_x11_sync_object
- Date: Wed, 4 Jun 2014 08:58:43 +0000 (UTC)
commit e6f100b6d46021f67473e8f59279309a77469f69
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.
https://bugzilla.gnome.org/show_bug.cgi?id=728464
configure.ac | 5 +
src/Makefile.am | 2 +
src/compositor/compositor-private.h | 6 +-
src/compositor/compositor.c | 66 ++++-
src/compositor/meta-sync-ring.c | 482 +++++++++++++++++++++++++++++++++++
src/compositor/meta-sync-ring.h | 17 ++
6 files changed, 561 insertions(+), 17 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index ef67b20..c894c1f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -363,6 +363,11 @@ fi
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
+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 6f91327..ee46a30 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -64,6 +64,8 @@ libmutter_la_SOURCES = \
compositor/meta-shadow-factory.c \
compositor/meta-shadow-factory-private.h \
compositor/meta-shaped-texture.c \
+ 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/compositor/compositor-private.h b/src/compositor/compositor-private.h
index 622dd0b..5cdb31f 100644
--- a/src/compositor/compositor-private.h
+++ b/src/compositor/compositor-private.h
@@ -19,7 +19,8 @@ struct _MetaCompositor
Atom atom_x_root_pixmap;
Atom atom_net_wm_window_opacity;
- guint repaint_func_id;
+ guint pre_paint_func_id;
+ guint post_paint_func_id;
ClutterActor *shadow_src;
@@ -33,7 +34,8 @@ struct _MetaCompositor
guint debug : 1;
guint no_mipmaps : 1;
- gboolean need_sync_drawing;
+ gboolean frame_has_updated_xsurfaces;
+ gboolean have_x11_sync_object;
};
struct _MetaCompScreen
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index 5eaca0c..add1ed6 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -86,6 +86,7 @@
#include "display-private.h" /* for meta_display_lookup_x_window() */
#include <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
+#include "meta-sync-ring.h"
/* #define DEBUG_TRACE g_print */
#define DEBUG_TRACE(X)
@@ -142,7 +143,11 @@ meta_switch_workspace_completed (MetaScreen *screen)
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
@@ -174,7 +179,7 @@ process_damage (MetaCompositor *compositor,
meta_window_actor_process_damage (window_actor, event);
- compositor->need_sync_drawing = TRUE;
+ compositor->frame_has_updated_xsurfaces = TRUE;
}
static void
@@ -721,6 +726,8 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
*/
XMapWindow (xdisplay, info->output);
+ compositor->have_x11_sync_object = meta_sync_ring_init (display);
+
redirect_windows (compositor, screen);
}
@@ -1021,6 +1028,10 @@ meta_compositor_process_event (MetaCompositor *compositor,
if (event->type == MapNotify)
clutter_x11_handle_event (event);
+ if (compositor->have_x11_sync_object &&
+ event->type == (compositor->display->xsync_event_base + XSyncAlarmNotify))
+ meta_sync_ring_handle_event ((XSyncAlarmNotifyEvent *) event);
+
/* The above handling is basically just "observing" the events, so we return
* FALSE to indicate that the event should not be filtered out; if we have
* GTK+ windows in the same process, GTK+ needs the ConfigureNotify event, for example.
@@ -1470,7 +1481,7 @@ pre_paint_windows (MetaCompScreen *info)
}
static gboolean
-meta_repaint_func (gpointer data)
+meta_pre_paint_func (gpointer data)
{
MetaCompositor *compositor = data;
GSList *screens = meta_display_get_screens (compositor->display);
@@ -1486,14 +1497,16 @@ meta_repaint_func (gpointer data)
pre_paint_windows (info);
}
- if (compositor->need_sync_drawing)
+ if (compositor->frame_has_updated_xsurfaces)
{
/* We need to make sure that any X drawing that happens before
- * the XDamageSubtract() for each MWA 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.
+ * the XDamageSubtract() for each window above is visible to
+ * 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:
*
@@ -1508,9 +1521,26 @@ meta_repaint_func (gpointer data)
* round trip request at this point is sufficient to flush the
* GLX buffers.
*/
- XSync (compositor->display->xdisplay, False);
+ if (compositor->have_x11_sync_object)
+ meta_sync_ring_insert_wait ();
+ else
+ XSync (compositor->display->xdisplay, False);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+meta_post_paint_func (gpointer data)
+{
+ MetaCompositor *compositor = data;
+
+ if (compositor->frame_has_updated_xsurfaces)
+ {
+ if (compositor->have_x11_sync_object)
+ meta_sync_ring_after_frame ();
- compositor->need_sync_drawing = FALSE;
+ compositor->frame_has_updated_xsurfaces = FALSE;
}
return TRUE;
@@ -1574,10 +1604,16 @@ meta_compositor_new (MetaDisplay *display)
compositor->atom_x_root_pixmap = atoms[0];
compositor->atom_net_wm_window_opacity = atoms[1];
- 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..c17c814
--- /dev/null
+++ b/src/compositor/meta-sync-ring.c
@@ -0,0 +1,482 @@
+/*
+ * 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 <cogl/cogl.h>
+
+#include <meta/display.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 */
+
+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
+{
+ MetaDisplay *display;
+
+ GHashTable *alarm_to_sync;
+
+ MetaSync *syncs_array[NUM_SYNCS];
+ guint current_sync_idx;
+ MetaSync *current_sync;
+ guint warmup_syncs;
+} MetaSyncRing;
+
+static MetaSyncRing meta_sync_ring = { 0 };
+
+static XSyncValue SYNC_VALUE_ZERO;
+static XSyncValue SYNC_VALUE_ONE;
+
+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)
+{
+ 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)
+{
+ int num_extensions, i;
+ gboolean 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)
+ sync = TRUE;
+ else if (g_strcmp0 ("GL_EXT_x11_sync_object", ext) == 0)
+ x11_sync_object = TRUE;
+ }
+
+ return sync && x11_sync_object;
+}
+
+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 ("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 ();
+ int xsync_event_base = meta_display_get_sync_event_base (ring->display);
+
+ if (event->type == 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 (MetaDisplay *display)
+{
+ guint i;
+ MetaSyncRing *ring = meta_sync_ring_get ();
+
+ g_return_val_if_fail (display != NULL, FALSE);
+ g_return_val_if_fail (ring->display == NULL, FALSE);
+
+ if (!load_required_symbols ())
+ return FALSE;
+
+ if (!meta_display_has_sync (display))
+ return FALSE;
+
+ XSyncIntToValue (&SYNC_VALUE_ZERO, 0);
+ XSyncIntToValue (&SYNC_VALUE_ONE, 1);
+
+ ring->display = display;
+
+ ring->alarm_to_sync = g_hash_table_new (NULL, NULL);
+
+ for (i = 0; i < NUM_SYNCS; ++i)
+ {
+ MetaSync *sync = meta_sync_new (meta_display_get_xdisplay (display));
+ 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 ();
+
+ g_return_if_fail (ring->display != 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->display = NULL;
+}
+
+static void
+meta_sync_ring_reboot (MetaDisplay *display)
+{
+ meta_sync_ring_destroy ();
+ meta_sync_ring_init (display);
+}
+
+void
+meta_sync_ring_after_frame (void)
+{
+ MetaSyncRing *ring = meta_sync_ring_get ();
+
+ g_return_if_fail (ring->display != 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");
+ meta_sync_ring_reboot (ring->display);
+ return;
+ }
+
+ 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];
+}
+
+void
+meta_sync_ring_insert_wait (void)
+{
+ MetaSyncRing *ring = meta_sync_ring_get ();
+
+ g_return_if_fail (ring->display != NULL);
+
+ if (ring->current_sync->state != META_SYNC_STATE_READY)
+ {
+ meta_warning ("MetaSyncRing: Sync object is not ready -- were events handled properly?\n");
+ meta_sync_ring_reboot (ring->display);
+ }
+
+ meta_sync_insert (ring->current_sync);
+}
+
+void
+meta_sync_ring_handle_event (XSyncAlarmNotifyEvent *event)
+{
+ MetaSync *sync;
+ MetaSyncRing *ring = meta_sync_ring_get ();
+
+ g_return_if_fail (ring->display != NULL);
+
+ 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..b0d2275
--- /dev/null
+++ b/src/compositor/meta-sync-ring.h
@@ -0,0 +1,17 @@
+#ifndef _META_SYNC_RING_H_
+#define _META_SYNC_RING_H_
+
+#include <glib.h>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/sync.h>
+
+#include <meta/display.h>
+
+gboolean meta_sync_ring_init (MetaDisplay *dpy);
+void meta_sync_ring_destroy (void);
+void meta_sync_ring_after_frame (void);
+void meta_sync_ring_insert_wait (void);
+void meta_sync_ring_handle_event (XSyncAlarmNotifyEvent *event);
+
+#endif /* _META_SYNC_RING_H_ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]