[cogl] Add fence API



commit e6d37470da9294adc1554c0a8c91aa2af560ed9f
Author: Daniel Stone <daniel fooishbar org>
Date:   Thu Jan 10 17:13:34 2013 -0800

    Add fence API
    
    cogl_framebuffer_add_fence creates a synchronisation fence, which will
    invoke a user-specified callback when the GPU has finished executing all
    commands provided to it up to that point in time.
    
    Support is currently provided for GL 3.x's GL_ARB_sync extension, and
    EGL's EGL_KHR_fence_sync (when used with OpenGL ES).
    
    Signed-off-by: Daniel Stone <daniel fooishbar org>
    Reviewed-by: Neil Roberts <neil linux intel com>
    Reviewed-by: Robert Bragg <robert linux intel com>
    
    https://bugzilla.gnome.org/show_bug.cgi?id=691752

 cogl/Makefile.am                                |    3 +
 cogl/cogl-context-private.h                     |    5 +
 cogl/cogl-context.c                             |    2 +
 cogl/cogl-context.h                             |    1 +
 cogl/cogl-fence-private.h                       |   61 ++++++
 cogl/cogl-fence.c                               |  228 +++++++++++++++++++++++
 cogl/cogl-fence.h                               |  136 ++++++++++++++
 cogl/cogl-framebuffer.c                         |    2 +
 cogl/cogl-journal-private.h                     |    3 +
 cogl/cogl-journal.c                             |   21 ++-
 cogl/cogl-private.h                             |    3 +-
 cogl/cogl.h                                     |    1 +
 cogl/cogl.symbols                               |    4 +
 cogl/driver/gl/gl/cogl-driver-gl.c              |    3 +
 cogl/driver/gl/gles/cogl-driver-gles.c          |    6 +
 cogl/gl-prototypes/cogl-all-functions.h         |   12 ++
 cogl/winsys/cogl-winsys-egl-feature-functions.h |   20 ++
 cogl/winsys/cogl-winsys-egl-private.h           |    3 +-
 cogl/winsys/cogl-winsys-egl.c                   |   49 +++++
 cogl/winsys/cogl-winsys-private.h               |    9 +
 doc/reference/cogl2/cogl2-docs.xml.in           |    1 +
 doc/reference/cogl2/cogl2-sections.txt          |   11 +
 tests/conform/Makefile.am                       |    1 +
 tests/conform/test-conform-main.c               |    2 +
 tests/conform/test-fence.c                      |   64 +++++++
 tests/conform/test-utils.c                      |    6 +
 tests/conform/test-utils.h                      |    3 +-
 27 files changed, 656 insertions(+), 4 deletions(-)
---
diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index b261f11..d9a5056 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -107,6 +107,7 @@ cogl_public_h = \
        $(srcdir)/cogl-texture.h                \
        $(srcdir)/cogl-types.h                  \
        $(srcdir)/cogl-vector.h                 \
+       $(srcdir)/cogl-fence.h                  \
        $(srcdir)/cogl-version.h                \
        $(srcdir)/cogl.h                        \
        $(NULL)
@@ -403,6 +404,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 cf41c88..2028094 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -50,6 +50,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
 {
@@ -269,6 +271,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 e4714be..461d0f3 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -425,6 +425,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 ff2ecfd..14f9f30 100644
--- a/cogl/cogl-context.h
+++ b/cogl/cogl-context.h
@@ -249,6 +249,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 0000000..0817b7d
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#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 0000000..2055ac4
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 0000000..23ac7bb
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_FENCE_H__
+#define __COGL_FENCE_H__
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-framebuffer.h>
+
+/**
+ * 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 ceb7916..883b37b 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -163,6 +163,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 6f839d3..b73fbd1 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 05cd646..f651a55 100644
--- a/cogl/cogl-journal.c
+++ b/cogl/cogl-journal.c
@@ -153,6 +153,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);
 }
 
@@ -1257,6 +1259,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.
@@ -1280,7 +1294,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;
@@ -1378,6 +1395,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 08ee775..15fafd7 100644
--- a/cogl/cogl-private.h
+++ b/cogl/cogl-private.h
@@ -57,7 +57,8 @@ typedef enum
   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_ARBFP = 1L<<24
+  COGL_PRIVATE_FEATURE_ARBFP = 1L<<24,
+  COGL_PRIVATE_FEATURE_OES_EGL_SYNC = 1L<<25
 } CoglPrivateFeatureFlags;
 
 /* Sometimes when evaluating pipelines, either during comparisons or
diff --git a/cogl/cogl.h b/cogl/cogl.h
index 367d45a..790dcd9 100644
--- a/cogl/cogl.h
+++ b/cogl/cogl.h
@@ -78,6 +78,7 @@
 #include <cogl/cogl-onscreen.h>
 #include <cogl/cogl-frame-info.h>
 #include <cogl/cogl-poll.h>
+#include <cogl/cogl-fence.h>
 #if defined (COGL_HAS_EGL_PLATFORM_KMS_SUPPORT)
 #include <cogl/cogl-kms-renderer.h>
 #include <cogl/cogl-kms-display.h>
diff --git a/cogl/cogl.symbols b/cogl/cogl.symbols
index cd176f7..21a0c81 100644
--- a/cogl/cogl.symbols
+++ b/cogl/cogl.symbols
@@ -675,3 +675,7 @@ _cogl_texture_gl_flush_legacy_texobj_wrap_modes
 _cogl_texture_transform_coords_to_gl
 _cogl_texture_transform_quad_coords_to_gl
 #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 74cc572..9d5f4cd 100644
--- a/cogl/driver/gl/gl/cogl-driver-gl.c
+++ b/cogl/driver/gl/gl/cogl-driver-gl.c
@@ -554,6 +554,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;
 
diff --git a/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/driver/gl/gles/cogl-driver-gles.c
index c212232..0438e34 100644
--- a/cogl/driver/gl/gles/cogl-driver-gles.c
+++ b/cogl/driver/gl/gles/cogl-driver-gles.c
@@ -314,6 +314,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;
 
diff --git a/cogl/gl-prototypes/cogl-all-functions.h b/cogl/gl-prototypes/cogl-all-functions.h
index 7d0ed20..d774e45 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 24cc805..a26a6c5 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 40c4747..fe186da 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 ae4f7e8..c87ae8b 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 170cf2f..123419e 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/cogl2/cogl2-docs.xml.in b/doc/reference/cogl2/cogl2-docs.xml.in
index e0840df..91acc53 100644
--- a/doc/reference/cogl2/cogl2-docs.xml.in
+++ b/doc/reference/cogl2/cogl2-docs.xml.in
@@ -142,6 +142,7 @@
       <xi:include href="xml/cogl-vector.xml"/>
       <xi:include href="xml/cogl-euler.xml"/>
       <xi:include href="xml/cogl-quaternion.xml"/>
+      <xi:include href="xml/cogl-fence.xml"/>
       <xi:include href="xml/cogl-version.xml"/>
     </section>
 
diff --git a/doc/reference/cogl2/cogl2-sections.txt b/doc/reference/cogl2/cogl2-sections.txt
index 59f40bd..670ca8c 100644
--- a/doc/reference/cogl2/cogl2-sections.txt
+++ b/doc/reference/cogl2/cogl2-sections.txt
@@ -890,6 +890,17 @@ cogl_vector3_distance
 </SECTION>
 
 <SECTION>
+<FILE>cogl-fence</FILE>
+<TITLE>GPU synchronisation fences</TITLE>
+CoglFence
+CoglFenceCallback
+CoglFenceClosure
+cogl_fence_closure_get_user_data
+cogl_framebuffer_add_fence_callback
+cogl_framebuffer_cancel_fence_callback
+</SECTION>
+
+<SECTION>
 <FILE>cogl-version</FILE>
 <TITLE>Versioning utility macros</TITLE>
 COGL_VERSION_MAJOR
diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am
index d5c25db..61c2e83 100644
--- a/tests/conform/Makefile.am
+++ b/tests/conform/Makefile.am
@@ -65,6 +65,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 9345065..569985f 100644
--- a/tests/conform/test-conform-main.c
+++ b/tests/conform/test-conform-main.c
@@ -134,6 +134,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 0000000..2390b71
--- /dev/null
+++ b/tests/conform/test-fence.c
@@ -0,0 +1,64 @@
+#include <cogl/cogl.h>
+
+/* 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 <cogl/cogl-util.h>
+
+/* 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 a3d1c14..a10019a 100644
--- a/tests/conform/test-utils.c
+++ b/tests/conform/test-utils.c
@@ -70,6 +70,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 3f9cdc6..ae2fb64 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;


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