diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index 2734dca40..4a65ce285 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -133,6 +133,7 @@ cogl_experimental_h = \
$(srcdir)/cogl2-experimental.h \
$(srcdir)/cogl2-compatibility.h \
$(srcdir)/cogl-macros.h \
+ $(srcdir)/cogl-fence.h \
$(srcdir)/cogl-version.h \
$(srcdir)/cogl-error.h \
$(NULL)
@@ -437,6 +438,8 @@ cogl_sources_c = \
$(srcdir)/cogl-error.c \
$(srcdir)/cogl-closure-list-private.h \
$(srcdir)/cogl-closure-list.c \
+ $(srcdir)/cogl-fence.c \
+ $(srcdir)/cogl-fence-private.h \
$(NULL)
if USE_GLIB
diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h
index 21d841cd1..1444cb739 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -51,6 +51,8 @@
#include "cogl-gl-header.h"
#include "cogl-framebuffer-private.h"
#include "cogl-onscreen-private.h"
+#include "cogl-fence-private.h"
+#include "cogl-poll-private.h"
typedef struct
{
@@ -306,6 +308,9 @@ struct _CoglContext
GHashTable *uniform_name_hash;
int n_uniform_names;
+ CoglPollSource *fences_poll_source;
+ CoglFenceList fences;
+
/* This defines a list of function pointers that Cogl uses from
either GL or GLES. All functions are accessed indirectly through
these pointers rather than linking to them directly */
diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c
index 4e5b45148..a5dde52c4 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -477,6 +477,8 @@ cogl_context_new (CoglDisplay *display,
cogl_has_feature (context, COGL_FEATURE_ID_POINT_SPRITE))
GE (context, glEnable (GL_POINT_SPRITE));
+ COGL_TAILQ_INIT (&context->fences);
+
return context;
}
diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h
index f7eba11ae..0dfb5e576 100644
--- a/cogl/cogl-context.h
+++ b/cogl/cogl-context.h
@@ -259,6 +259,7 @@ typedef enum _CoglFeatureID
COGL_FEATURE_ID_GLES2_CONTEXT,
COGL_FEATURE_ID_DEPTH_TEXTURE,
COGL_FEATURE_ID_PRESENTATION_TIME,
+ COGL_FEATURE_ID_FENCE,
/*< private >*/
_COGL_N_FEATURE_IDS /*< skip >*/
diff --git a/cogl/cogl-fence-private.h b/cogl/cogl-fence-private.h
new file mode 100644
index 000000000..0817b7d7a
--- /dev/null
+++ b/cogl/cogl-fence-private.h
@@ -0,0 +1,61 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2012 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ *
+ */
+
+#ifndef __COGL_FENCE_PRIVATE_H__
+#define __COGL_FENCE_PRIVATE_H__
+
+#include "cogl-fence.h"
+#include "cogl-queue.h"
+#include "cogl-winsys-private.h"
+
+COGL_TAILQ_HEAD (CoglFenceList, CoglFenceClosure);
+
+typedef enum
+{
+ FENCE_TYPE_PENDING,
+#ifdef GL_ARB_sync
+ FENCE_TYPE_GL_ARB,
+#endif
+ FENCE_TYPE_WINSYS,
+ FENCE_TYPE_ERROR
+} CoglFenceType;
+
+struct _CoglFenceClosure
+{
+ COGL_TAILQ_ENTRY (CoglFenceClosure) list;
+ CoglFramebuffer *framebuffer;
+
+ CoglFenceType type;
+ void *fence_obj;
+
+ CoglFenceCallback callback;
+ void *user_data;
+};
+
+void
+_cogl_fence_submit (CoglFenceClosure *fence);
+
+void
+_cogl_fence_cancel_fences_for_framebuffer (CoglFramebuffer *framebuffer);
+
+#endif /* __COGL_FENCE_PRIVATE_H__ */
diff --git a/cogl/cogl-fence.c b/cogl/cogl-fence.c
new file mode 100644
index 000000000..2055ac46a
--- /dev/null
+++ b/cogl/cogl-fence.c
@@ -0,0 +1,228 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2012 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-fence.h"
+#include "cogl-fence-private.h"
+#include "cogl-context-private.h"
+#include "cogl-winsys-private.h"
+
+#define FENCE_CHECK_TIMEOUT 5000 /* microseconds */
+
+void *
+cogl_fence_closure_get_user_data (CoglFenceClosure *closure)
+{
+ return closure->user_data;
+}
+
+static void
+_cogl_fence_check (CoglFenceClosure *fence)
+{
+ CoglContext *context = fence->framebuffer->context;
+
+ if (fence->type == FENCE_TYPE_WINSYS)
+ {
+ const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context);
+ CoglBool ret;
+
+ ret = winsys->fence_is_complete (context, fence->fence_obj);
+ if (!ret)
+ return;
+ }
+#ifdef GL_ARB_sync
+ else if (fence->type == FENCE_TYPE_GL_ARB)
+ {
+ GLenum arb;
+
+ arb = context->glClientWaitSync (fence->fence_obj,
+ GL_SYNC_FLUSH_COMMANDS_BIT,
+ 0);
+ if (arb != GL_ALREADY_SIGNALED && arb != GL_CONDITION_SATISFIED)
+ return;
+ }
+#endif
+
+ fence->callback (NULL, /* dummy CoglFence object */
+ fence->user_data);
+ cogl_framebuffer_cancel_fence_callback (fence->framebuffer, fence);
+}
+
+static void
+_cogl_fence_poll_dispatch (void *source, int revents)
+{
+ CoglContext *context = source;
+ CoglFenceClosure *fence, *next;
+
+ COGL_TAILQ_FOREACH_SAFE (fence, &context->fences, list, next)
+ _cogl_fence_check (fence);
+}
+
+static int64_t
+_cogl_fence_poll_prepare (void *source)
+{
+ CoglContext *context = source;
+ GList *l;
+
+ /* If there are any pending fences in any of the journals then we
+ * need to flush the journal otherwise the fence will never be
+ * hit and the main loop might block forever */
+ for (l = context->framebuffers; l; l = l->next)
+ {
+ CoglFramebuffer *fb = l->data;
+
+ if (!COGL_TAILQ_EMPTY (&fb->journal->pending_fences))
+ _cogl_framebuffer_flush_journal (fb);
+ }
+
+ if (!COGL_TAILQ_EMPTY (&context->fences))
+ return FENCE_CHECK_TIMEOUT;
+ else
+ return -1;
+}
+
+void
+_cogl_fence_submit (CoglFenceClosure *fence)
+{
+ CoglContext *context = fence->framebuffer->context;
+ const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context);
+
+ fence->type = FENCE_TYPE_ERROR;
+
+ if (winsys->fence_add)
+ {
+ fence->fence_obj = winsys->fence_add (context);
+ if (fence->fence_obj)
+ {
+ fence->type = FENCE_TYPE_WINSYS;
+ goto done;
+ }
+ }
+
+#ifdef GL_ARB_sync
+ if (context->glFenceSync)
+ {
+ fence->fence_obj = context->glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE,
+ 0);
+ if (fence->fence_obj)
+ {
+ fence->type = FENCE_TYPE_GL_ARB;
+ goto done;
+ }
+ }
+#endif
+
+ done:
+ COGL_TAILQ_INSERT_TAIL (&context->fences, fence, list);
+
+ if (!context->fences_poll_source)
+ {
+ context->fences_poll_source =
+ _cogl_poll_renderer_add_source (context->display->renderer,
+ _cogl_fence_poll_prepare,
+ _cogl_fence_poll_dispatch,
+ context);
+ }
+}
+
+CoglFenceClosure *
+cogl_framebuffer_add_fence_callback (CoglFramebuffer *framebuffer,
+ CoglFenceCallback callback,
+ void *user_data)
+{
+ CoglContext *context = framebuffer->context;
+ CoglJournal *journal = framebuffer->journal;
+ CoglFenceClosure *fence;
+
+ if (!COGL_FLAGS_GET (context->features, COGL_FEATURE_ID_FENCE))
+ return NULL;
+
+ fence = g_slice_new (CoglFenceClosure);
+ fence->framebuffer = framebuffer;
+ fence->callback = callback;
+ fence->user_data = user_data;
+ fence->fence_obj = NULL;
+
+ if (journal->entries->len)
+ {
+ COGL_TAILQ_INSERT_TAIL (&journal->pending_fences, fence, list);
+ fence->type = FENCE_TYPE_PENDING;
+ }
+ else
+ _cogl_fence_submit (fence);
+
+ return fence;
+}
+
+void
+cogl_framebuffer_cancel_fence_callback (CoglFramebuffer *framebuffer,
+ CoglFenceClosure *fence)
+{
+ CoglJournal *journal = framebuffer->journal;
+ CoglContext *context = framebuffer->context;
+
+ if (fence->type == FENCE_TYPE_PENDING)
+ {
+ COGL_TAILQ_REMOVE (&journal->pending_fences, fence, list);
+ }
+ else
+ {
+ COGL_TAILQ_REMOVE (&context->fences, fence, list);
+
+ if (fence->type == FENCE_TYPE_WINSYS)
+ {
+ const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context);
+
+ winsys->fence_destroy (context, fence->fence_obj);
+ }
+#ifdef GL_ARB_sync
+ else if (fence->type == FENCE_TYPE_GL_ARB)
+ {
+ context->glDeleteSync (fence->fence_obj);
+ }
+#endif
+ }
+
+ g_slice_free (CoglFenceClosure, fence);
+}
+
+void
+_cogl_fence_cancel_fences_for_framebuffer (CoglFramebuffer *framebuffer)
+{
+ CoglJournal *journal = framebuffer->journal;
+ CoglContext *context = framebuffer->context;
+ CoglFenceClosure *fence, *next;
+
+ while (!COGL_TAILQ_EMPTY (&journal->pending_fences))
+ {
+ fence = COGL_TAILQ_FIRST (&journal->pending_fences);
+ cogl_framebuffer_cancel_fence_callback (framebuffer, fence);
+ }
+
+ COGL_TAILQ_FOREACH_SAFE (fence, &context->fences, list, next)
+ {
+ if (fence->framebuffer == framebuffer)
+ cogl_framebuffer_cancel_fence_callback (framebuffer, fence);
+ }
+}
diff --git a/cogl/cogl-fence.h b/cogl/cogl-fence.h
new file mode 100644
index 000000000..23ac7bbc6
--- /dev/null
+++ b/cogl/cogl-fence.h
@@ -0,0 +1,136 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2012 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ *
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __COGL_FENCE_H__
+#define __COGL_FENCE_H__
+
+#include
+#include
+
+/**
+ * SECTION:cogl-fence
+ * @short_description: Functions for notification of command completion
+ *
+ * Cogl allows notification of GPU command completion; users may mark
+ * points in the GPU command stream and receive notification when the GPU
+ * has executed to that point.
+ */
+
+/**
+ * CoglFence:
+ *
+ * An opaque object representing a fence. This type is currently
+ * unused but in the future may be used to pass extra information
+ * about the fence completion.
+ *
+ * Since: 2.0
+ * Stability: Unstable
+ */
+typedef struct _CoglFence CoglFence;
+
+/**
+ * CoglFenceCallback:
+ * @fence: Unused. In the future this parameter may be used to pass
+ * extra information about the fence completion but for now it
+ * should be ignored.
+ * @user_data: The private data passed to cogl_framebuffer_add_fence_callback()
+ *
+ * The callback prototype used with
+ * cogl_framebuffer_add_fence_callback() for notification of GPU
+ * command completion.
+ *
+ * Since: 2.0
+ * Stability: Unstable
+ */
+typedef void (* CoglFenceCallback) (CoglFence *fence,
+ void *user_data);
+
+/**
+ * CoglFenceClosure:
+ *
+ * An opaque type representing one future callback to be made when the
+ * GPU command stream has passed a certain point.
+ *
+ * Since: 2.0
+ * Stability: Unstable
+ */
+typedef struct _CoglFenceClosure CoglFenceClosure;
+
+/**
+ * cogl_frame_closure_get_user_data:
+ * @closure: A #CoglFenceClosure returned from cogl_framebuffer_add_fence()
+ *
+ * Returns the user_data submitted to cogl_framebuffer_add_fence() which
+ * returned a given #CoglFenceClosure.
+ *
+ * Since: 2.0
+ * Stability: Unstable
+ */
+void *
+cogl_fence_closure_get_user_data (CoglFenceClosure *closure);
+
+/**
+ * cogl_framebuffer_add_fence_callback:
+ * @framebuffer: The #CoglFramebuffer the commands have been submitted to
+ * @callback: A #CoglFenceCallback to be called when all commands submitted
+ * to Cogl have been executed
+ * @user_data: Private data that will be passed to the callback
+ *
+ * Calls the provided callback when all previously-submitted commands have
+ * been executed by the GPU.
+ *
+ * Returns non-NULL if the fence succeeded, or %NULL if it was unable to
+ * be inserted and the callback will never be called. The user does not
+ * need to free the closure; it will be freed automatically when the
+ * callback is called, or cancelled.
+ *
+ * Since: 2.0
+ * Stability: Unstable
+ */
+CoglFenceClosure *
+cogl_framebuffer_add_fence_callback (CoglFramebuffer *framebuffer,
+ CoglFenceCallback callback,
+ void *user_data);
+
+/**
+ * cogl_framebuffer_cancel_fence_callback:
+ * @framebuffer: The #CoglFramebuffer the commands were submitted to
+ * @closure: The #CoglFenceClosure returned from
+ * cogl_framebuffer_add_fence_callback()
+ *
+ * Removes a fence previously submitted with
+ * cogl_framebuffer_add_fence_callback(); the callback will not be
+ * called.
+ *
+ * Since: 2.0
+ * Stability: Unstable
+ */
+void
+cogl_framebuffer_cancel_fence_callback (CoglFramebuffer *framebuffer,
+ CoglFenceClosure *closure);
+
+#endif /* __COGL_FENCE_H__ */
diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c
index 136ae860d..4fc8e330a 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -171,6 +171,8 @@ _cogl_framebuffer_free (CoglFramebuffer *framebuffer)
{
CoglContext *ctx = framebuffer->context;
+ _cogl_fence_cancel_fences_for_framebuffer (framebuffer);
+
_cogl_clip_state_destroy (&framebuffer->clip_state);
cogl_object_unref (framebuffer->modelview_stack);
diff --git a/cogl/cogl-journal-private.h b/cogl/cogl-journal-private.h
index 6f839d383..b73fbd1fc 100644
--- a/cogl/cogl-journal-private.h
+++ b/cogl/cogl-journal-private.h
@@ -27,6 +27,7 @@
#include "cogl-texture.h"
#include "cogl-object-private.h"
#include "cogl-clip-stack.h"
+#include "cogl-fence-private.h"
#define COGL_JOURNAL_VBO_POOL_SIZE 8
@@ -58,6 +59,8 @@ typedef struct _CoglJournal
int fast_read_pixel_count;
+ CoglFenceList pending_fences;
+
} CoglJournal;
/* To improve batching of geometry when submitting vertices to OpenGL we
diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c
index 57322fb92..5adba2fd8 100644
--- a/cogl/cogl-journal.c
+++ b/cogl/cogl-journal.c
@@ -154,6 +154,8 @@ _cogl_journal_new (CoglFramebuffer *framebuffer)
journal->entries = g_array_new (FALSE, FALSE, sizeof (CoglJournalEntry));
journal->vertices = g_array_new (FALSE, FALSE, sizeof (float));
+ COGL_TAILQ_INIT (&journal->pending_fences);
+
return _cogl_journal_object_new (journal);
}
@@ -1267,6 +1269,18 @@ _cogl_journal_all_entries_within_bounds (CoglJournal *journal,
return TRUE;
}
+static void
+post_fences (CoglJournal *journal)
+{
+ CoglFenceClosure *fence, *next;
+
+ COGL_TAILQ_FOREACH_SAFE (fence, &journal->pending_fences, list, next)
+ {
+ COGL_TAILQ_REMOVE (&journal->pending_fences, fence, list);
+ _cogl_fence_submit (fence);
+ }
+}
+
/* XXX NB: When _cogl_journal_flush() returns all state relating
* to pipelines, all glEnable flags and current matrix state
* is undefined.
@@ -1290,7 +1304,10 @@ _cogl_journal_flush (CoglJournal *journal)
0 /* no application private data */);
if (journal->entries->len == 0)
- return;
+ {
+ post_fences (journal);
+ return;
+ }
framebuffer = journal->framebuffer;
ctx = framebuffer->context;
@@ -1388,6 +1405,8 @@ _cogl_journal_flush (CoglJournal *journal)
_cogl_journal_discard (journal);
COGL_TIMER_STOP (_cogl_uprof_context, discard_timer);
+ post_fences (journal);
+
COGL_TIMER_STOP (_cogl_uprof_context, flush_timer);
}
diff --git a/cogl/cogl-private.h b/cogl/cogl-private.h
index 6e2d301cc..b62be3354 100644
--- a/cogl/cogl-private.h
+++ b/cogl/cogl-private.h
@@ -56,7 +56,8 @@ typedef enum
COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS = 1L<<20,
COGL_PRIVATE_FEATURE_ALPHA_TEXTURES = 1L<<21,
COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE = 1L<<22,
- COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL = 1L<<23
+ COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL = 1L<<23,
+ COGL_PRIVATE_FEATURE_OES_EGL_SYNC = 1L<<24
} CoglPrivateFeatureFlags;
/* Sometimes when evaluating pipelines, either during comparisons or
diff --git a/cogl/cogl.h b/cogl/cogl.h
index 7791ad87f..2d1571cbd 100644
--- a/cogl/cogl.h
+++ b/cogl/cogl.h
@@ -109,6 +109,7 @@
#include
#include
#include
+#include
#if defined (COGL_HAS_EGL_PLATFORM_KMS_SUPPORT)
#include
#include
diff --git a/cogl/cogl.symbols b/cogl/cogl.symbols
index dc2daabfb..c74f667e7 100644
--- a/cogl/cogl.symbols
+++ b/cogl/cogl.symbols
@@ -981,3 +981,7 @@ _cogl_atlas_texture_new_with_size
_cogl_atlas_texture_remove_reorganize_callback
_cogl_context_get_default
#endif
+
+cogl_fence_closure_get_user_data
+cogl_framebuffer_add_fence_callback
+cogl_framebuffer_cancel_fence_callback
diff --git a/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/driver/gl/gl/cogl-driver-gl.c
index 713390cc5..25a550c43 100644
--- a/cogl/driver/gl/gl/cogl-driver-gl.c
+++ b/cogl/driver/gl/gl/cogl-driver-gl.c
@@ -597,6 +597,9 @@ _cogl_driver_update_features (CoglContext *ctx,
COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS |
COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL);
+ if (ctx->glFenceSync)
+ COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_FENCE, TRUE);
+
/* Cache features */
ctx->private_feature_flags |= private_flags;
ctx->feature_flags |= flags;
diff --git a/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/driver/gl/gles/cogl-driver-gles.c
index 19202b4ae..f34f0b8a2 100644
--- a/cogl/driver/gl/gles/cogl-driver-gles.c
+++ b/cogl/driver/gl/gles/cogl-driver-gles.c
@@ -346,6 +346,12 @@ _cogl_driver_update_features (CoglContext *context,
if (_cogl_check_extension ("GL_EXT_unpack_subimage", gl_extensions))
private_flags |= COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE;
+ /* A nameless vendor implemented the extension, but got the case wrong
+ * per the spec. */
+ if (_cogl_check_extension ("GL_OES_EGL_sync", gl_extensions) ||
+ _cogl_check_extension ("GL_OES_egl_sync", gl_extensions))
+ private_flags |= COGL_PRIVATE_FEATURE_OES_EGL_SYNC;
+
/* Cache features */
context->private_feature_flags |= private_flags;
context->feature_flags |= flags;
diff --git a/cogl/gl-prototypes/cogl-all-functions.h b/cogl/gl-prototypes/cogl-all-functions.h
index 7d0ed20d1..d774e4502 100644
--- a/cogl/gl-prototypes/cogl-all-functions.h
+++ b/cogl/gl-prototypes/cogl-all-functions.h
@@ -297,3 +297,15 @@ COGL_EXT_FUNCTION (GLvoid *, glMapBufferRange,
GLsizeiptr length,
GLbitfield access))
COGL_EXT_END ()
+
+COGL_EXT_BEGIN (sync, 3, 2,
+ 0, /* not in either GLES */
+ "ARB:\0",
+ "sync\0")
+COGL_EXT_FUNCTION (GLsync, glFenceSync,
+ (GLenum condition, GLbitfield flags))
+COGL_EXT_FUNCTION (GLenum, glClientWaitSync,
+ (GLsync sync, GLbitfield flags, GLuint64 timeout))
+COGL_EXT_FUNCTION (void, glDeleteSync,
+ (GLsync sync))
+COGL_EXT_END ()
diff --git a/cogl/winsys/cogl-winsys-egl-feature-functions.h b/cogl/winsys/cogl-winsys-egl-feature-functions.h
index 24cc80522..a26a6c586 100644
--- a/cogl/winsys/cogl-winsys-egl-feature-functions.h
+++ b/cogl/winsys/cogl-winsys-egl-feature-functions.h
@@ -112,3 +112,23 @@ COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglSwapBuffersWithDamage,
const EGLint *rects,
EGLint n_rects))
COGL_WINSYS_FEATURE_END ()
+
+#if defined(EGL_KHR_fence_sync) || defined(EGL_KHR_reusable_sync)
+COGL_WINSYS_FEATURE_BEGIN (fence_sync,
+ "KHR\0",
+ "fence_sync\0",
+ COGL_EGL_WINSYS_FEATURE_FENCE_SYNC)
+COGL_WINSYS_FEATURE_FUNCTION (EGLSyncKHR, eglCreateSync,
+ (EGLDisplay dpy,
+ EGLenum type,
+ const EGLint *attrib_list))
+COGL_WINSYS_FEATURE_FUNCTION (EGLint, eglClientWaitSync,
+ (EGLDisplay dpy,
+ EGLSyncKHR sync,
+ EGLint flags,
+ EGLTimeKHR timeout))
+COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglDestroySync,
+ (EGLDisplay dpy,
+ EGLSyncKHR sync))
+COGL_WINSYS_FEATURE_END ()
+#endif
diff --git a/cogl/winsys/cogl-winsys-egl-private.h b/cogl/winsys/cogl-winsys-egl-private.h
index 40c4747ed..fe186da9a 100644
--- a/cogl/winsys/cogl-winsys-egl-private.h
+++ b/cogl/winsys/cogl-winsys-egl-private.h
@@ -70,7 +70,8 @@ typedef enum _CoglEGLWinsysFeature
COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_X11_PIXMAP =1L<<1,
COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_WAYLAND_BUFFER =1L<<2,
COGL_EGL_WINSYS_FEATURE_CREATE_CONTEXT =1L<<3,
- COGL_EGL_WINSYS_FEATURE_BUFFER_AGE =1L<<4
+ COGL_EGL_WINSYS_FEATURE_BUFFER_AGE =1L<<4,
+ COGL_EGL_WINSYS_FEATURE_FENCE_SYNC =1L<<5
} CoglEGLWinsysFeature;
typedef struct _CoglRendererEGL
diff --git a/cogl/winsys/cogl-winsys-egl.c b/cogl/winsys/cogl-winsys-egl.c
index c81790ca7..84d735dc4 100644
--- a/cogl/winsys/cogl-winsys-egl.c
+++ b/cogl/winsys/cogl-winsys-egl.c
@@ -504,6 +504,10 @@ _cogl_winsys_context_init (CoglContext *context, CoglError **error)
COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE);
}
+ if ((egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_FENCE_SYNC) &&
+ (context->private_feature_flags & COGL_PRIVATE_FEATURE_OES_EGL_SYNC))
+ COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_FENCE, TRUE);
+
/* NB: We currently only support creating standalone GLES2 contexts
* for offscreen rendering and so we need a dummy (non-visible)
* surface to be able to bind those contexts */
@@ -902,6 +906,45 @@ _cogl_winsys_restore_context (CoglContext *ctx)
egl_display->egl_context);
}
+#if defined(EGL_KHR_fence_sync) || defined(EGL_KHR_reusable_sync)
+static void *
+_cogl_winsys_fence_add (CoglContext *context)
+{
+ CoglRendererEGL *renderer = context->display->renderer->winsys;
+ void *ret;
+
+ if (renderer->pf_eglCreateSync)
+ ret = renderer->pf_eglCreateSync (renderer->edpy,
+ EGL_SYNC_FENCE_KHR,
+ NULL);
+ else
+ ret = NULL;
+
+ return ret;
+}
+
+static CoglBool
+_cogl_winsys_fence_is_complete (CoglContext *context, void *fence)
+{
+ CoglRendererEGL *renderer = context->display->renderer->winsys;
+ EGLint ret;
+
+ ret = renderer->pf_eglClientWaitSync (renderer->edpy,
+ fence,
+ EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
+ 0);
+ return (ret == EGL_CONDITION_SATISFIED_KHR);
+}
+
+static void
+_cogl_winsys_fence_destroy (CoglContext *context, void *fence)
+{
+ CoglRendererEGL *renderer = context->display->renderer->winsys;
+
+ renderer->pf_eglDestroySync (renderer->edpy, fence);
+}
+#endif
+
static CoglWinsysVtable _cogl_winsys_vtable =
{
.constraints = COGL_RENDERER_CONSTRAINT_USES_EGL |
@@ -936,6 +979,12 @@ static CoglWinsysVtable _cogl_winsys_vtable =
.save_context = _cogl_winsys_save_context,
.set_gles2_context = _cogl_winsys_set_gles2_context,
.restore_context = _cogl_winsys_restore_context,
+
+#if defined(EGL_KHR_fence_sync) || defined(EGL_KHR_reusable_sync)
+ .fence_add = _cogl_winsys_fence_add,
+ .fence_is_complete = _cogl_winsys_fence_is_complete,
+ .fence_destroy = _cogl_winsys_fence_destroy,
+#endif
};
/* XXX: we use a function because no doubt someone will complain
diff --git a/cogl/winsys/cogl-winsys-private.h b/cogl/winsys/cogl-winsys-private.h
index ac67deea6..645017508 100644
--- a/cogl/winsys/cogl-winsys-private.h
+++ b/cogl/winsys/cogl-winsys-private.h
@@ -185,6 +185,15 @@ typedef struct _CoglWinsysVtable
void
(*destroy_gles2_context) (CoglGLES2Context *gles2_ctx);
+ void *
+ (*fence_add) (CoglContext *ctx);
+
+ CoglBool
+ (*fence_is_complete) (CoglContext *ctx, void *fence);
+
+ void
+ (*fence_destroy) (CoglContext *ctx, void *fence);
+
} CoglWinsysVtable;
CoglBool
diff --git a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-docs.xml.in b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-docs.xml.in
index 9fca1bda7..d379ad635 100644
--- a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-docs.xml.in
+++ b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-docs.xml.in
@@ -143,6 +143,7 @@
+
diff --git a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt
index 247516095..7a9469595 100644
--- a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt
+++ b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt
@@ -961,6 +961,17 @@ cogl_vector3_dot_product
cogl_vector3_distance
+
+cogl-fence
+GPU synchronisation fences
+CoglFence
+CoglFenceCallback
+CoglFenceClosure
+cogl_fence_closure_get_user_data
+cogl_framebuffer_add_fence_callback
+cogl_framebuffer_cancel_fence_callback
+
+
cogl-version
Versioning utility macros
diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am
index 9782755ae..aa4a5706a 100644
--- a/tests/conform/Makefile.am
+++ b/tests/conform/Makefile.am
@@ -66,6 +66,7 @@ test_sources = \
test-primitive-and-journal.c \
test-copy-replace-texture.c \
test-pipeline-cache-unrefs-texture.c \
+ test-fence.c \
$(NULL)
test_conformance_SOURCES = $(common_sources) $(test_sources)
diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c
index 1d1447e1b..79f017a57 100644
--- a/tests/conform/test-conform-main.c
+++ b/tests/conform/test-conform-main.c
@@ -132,6 +132,8 @@ main (int argc, char **argv)
ADD_TEST (test_euler_quaternion, 0, 0);
+ ADD_TEST (test_fence, TEST_REQUIREMENT_FENCE, 0);
+
g_printerr ("Unknown test name \"%s\"\n", argv[1]);
return 1;
diff --git a/tests/conform/test-fence.c b/tests/conform/test-fence.c
new file mode 100644
index 000000000..2390b710b
--- /dev/null
+++ b/tests/conform/test-fence.c
@@ -0,0 +1,64 @@
+#include
+
+/* These will be redefined in config.h */
+#undef COGL_ENABLE_EXPERIMENTAL_2_0_API
+#undef COGL_ENABLE_EXPERIMENTAL_API
+
+#include "test-utils.h"
+#include "config.h"
+#include
+
+/* I'm writing this on the train after having dinner at a churrascuria. */
+#define MAGIC_CHUNK_O_DATA ((void *) 0xdeadbeef)
+
+static GMainLoop *loop;
+
+gboolean
+timeout (void *user_data)
+{
+ g_assert (!"timeout not reached");
+
+ return FALSE;
+}
+
+void
+callback (CoglFence *fence,
+ void *user_data)
+{
+ int fb_width = cogl_framebuffer_get_width (test_fb);
+ int fb_height = cogl_framebuffer_get_height (test_fb);
+
+ test_utils_check_pixel (test_fb, fb_width - 1, fb_height - 1, 0x00ff0000);
+ g_assert (user_data == MAGIC_CHUNK_O_DATA && "callback data not mangled");
+
+ g_main_loop_quit (loop);
+}
+
+void
+test_fence (void)
+{
+ GSource *cogl_source;
+ int fb_width = cogl_framebuffer_get_width (test_fb);
+ int fb_height = cogl_framebuffer_get_height (test_fb);
+ CoglFenceClosure *closure;
+
+ cogl_source = cogl_glib_source_new (test_ctx, G_PRIORITY_DEFAULT);
+ g_source_attach (cogl_source, NULL);
+ loop = g_main_loop_new (NULL, TRUE);
+
+ cogl_framebuffer_orthographic (test_fb, 0, 0, fb_width, fb_height, -1, 100);
+ cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR,
+ 0.0f, 1.0f, 0.0f, 0.0f);
+
+ closure = cogl_framebuffer_add_fence_callback (test_fb,
+ callback,
+ MAGIC_CHUNK_O_DATA);
+ g_assert (closure != NULL);
+
+ g_timeout_add_seconds (5, timeout, NULL);
+
+ g_main_loop_run (loop);
+
+ if (cogl_test_verbose ())
+ g_print ("OK\n");
+}
diff --git a/tests/conform/test-utils.c b/tests/conform/test-utils.c
index aea359d2d..e2a089ded 100644
--- a/tests/conform/test-utils.c
+++ b/tests/conform/test-utils.c
@@ -71,6 +71,12 @@ check_flags (TestFlags flags,
return FALSE;
}
+ if (flags & TEST_REQUIREMENT_FENCE &&
+ !cogl_has_feature (test_ctx, COGL_FEATURE_ID_FENCE))
+ {
+ return FALSE;
+ }
+
if (flags & TEST_KNOWN_FAILURE)
{
return FALSE;
diff --git a/tests/conform/test-utils.h b/tests/conform/test-utils.h
index 3f9cdc6ae..ae2fb64f6 100644
--- a/tests/conform/test-utils.h
+++ b/tests/conform/test-utils.h
@@ -20,7 +20,8 @@ typedef enum _TestFlags
TEST_REQUIREMENT_GLES2_CONTEXT = 1<<6,
TEST_REQUIREMENT_MAP_WRITE = 1<<7,
TEST_REQUIREMENT_GLSL = 1<<8,
- TEST_REQUIREMENT_OFFSCREEN = 1<<9
+ TEST_REQUIREMENT_OFFSCREEN = 1<<9,
+ TEST_REQUIREMENT_FENCE = 1<<10
} TestFlags;
extern CoglContext *test_ctx;