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

(cherry picked from commit e6d37470da9294adc1554c0a8c91aa2af560ed9f)
This commit is contained in:
Daniel Stone 2013-01-10 17:13:34 -08:00 committed by Robert Bragg
parent 7b14b5e3da
commit ea7d3b8476
27 changed files with 656 additions and 4 deletions

View File

@ -133,6 +133,7 @@ cogl_experimental_h = \
$(srcdir)/cogl2-experimental.h \ $(srcdir)/cogl2-experimental.h \
$(srcdir)/cogl2-compatibility.h \ $(srcdir)/cogl2-compatibility.h \
$(srcdir)/cogl-macros.h \ $(srcdir)/cogl-macros.h \
$(srcdir)/cogl-fence.h \
$(srcdir)/cogl-version.h \ $(srcdir)/cogl-version.h \
$(srcdir)/cogl-error.h \ $(srcdir)/cogl-error.h \
$(NULL) $(NULL)
@ -437,6 +438,8 @@ cogl_sources_c = \
$(srcdir)/cogl-error.c \ $(srcdir)/cogl-error.c \
$(srcdir)/cogl-closure-list-private.h \ $(srcdir)/cogl-closure-list-private.h \
$(srcdir)/cogl-closure-list.c \ $(srcdir)/cogl-closure-list.c \
$(srcdir)/cogl-fence.c \
$(srcdir)/cogl-fence-private.h \
$(NULL) $(NULL)
if USE_GLIB if USE_GLIB

View File

@ -51,6 +51,8 @@
#include "cogl-gl-header.h" #include "cogl-gl-header.h"
#include "cogl-framebuffer-private.h" #include "cogl-framebuffer-private.h"
#include "cogl-onscreen-private.h" #include "cogl-onscreen-private.h"
#include "cogl-fence-private.h"
#include "cogl-poll-private.h"
typedef struct typedef struct
{ {
@ -306,6 +308,9 @@ struct _CoglContext
GHashTable *uniform_name_hash; GHashTable *uniform_name_hash;
int n_uniform_names; int n_uniform_names;
CoglPollSource *fences_poll_source;
CoglFenceList fences;
/* This defines a list of function pointers that Cogl uses from /* This defines a list of function pointers that Cogl uses from
either GL or GLES. All functions are accessed indirectly through either GL or GLES. All functions are accessed indirectly through
these pointers rather than linking to them directly */ these pointers rather than linking to them directly */

View File

@ -477,6 +477,8 @@ cogl_context_new (CoglDisplay *display,
cogl_has_feature (context, COGL_FEATURE_ID_POINT_SPRITE)) cogl_has_feature (context, COGL_FEATURE_ID_POINT_SPRITE))
GE (context, glEnable (GL_POINT_SPRITE)); GE (context, glEnable (GL_POINT_SPRITE));
COGL_TAILQ_INIT (&context->fences);
return context; return context;
} }

View File

@ -259,6 +259,7 @@ typedef enum _CoglFeatureID
COGL_FEATURE_ID_GLES2_CONTEXT, COGL_FEATURE_ID_GLES2_CONTEXT,
COGL_FEATURE_ID_DEPTH_TEXTURE, COGL_FEATURE_ID_DEPTH_TEXTURE,
COGL_FEATURE_ID_PRESENTATION_TIME, COGL_FEATURE_ID_PRESENTATION_TIME,
COGL_FEATURE_ID_FENCE,
/*< private >*/ /*< private >*/
_COGL_N_FEATURE_IDS /*< skip >*/ _COGL_N_FEATURE_IDS /*< skip >*/

61
cogl/cogl-fence-private.h Normal file
View File

@ -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__ */

228
cogl/cogl-fence.c Normal file
View File

@ -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);
}
}

136
cogl/cogl-fence.h Normal file
View File

@ -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__ */

View File

@ -171,6 +171,8 @@ _cogl_framebuffer_free (CoglFramebuffer *framebuffer)
{ {
CoglContext *ctx = framebuffer->context; CoglContext *ctx = framebuffer->context;
_cogl_fence_cancel_fences_for_framebuffer (framebuffer);
_cogl_clip_state_destroy (&framebuffer->clip_state); _cogl_clip_state_destroy (&framebuffer->clip_state);
cogl_object_unref (framebuffer->modelview_stack); cogl_object_unref (framebuffer->modelview_stack);

View File

@ -27,6 +27,7 @@
#include "cogl-texture.h" #include "cogl-texture.h"
#include "cogl-object-private.h" #include "cogl-object-private.h"
#include "cogl-clip-stack.h" #include "cogl-clip-stack.h"
#include "cogl-fence-private.h"
#define COGL_JOURNAL_VBO_POOL_SIZE 8 #define COGL_JOURNAL_VBO_POOL_SIZE 8
@ -58,6 +59,8 @@ typedef struct _CoglJournal
int fast_read_pixel_count; int fast_read_pixel_count;
CoglFenceList pending_fences;
} CoglJournal; } CoglJournal;
/* To improve batching of geometry when submitting vertices to OpenGL we /* To improve batching of geometry when submitting vertices to OpenGL we

View File

@ -154,6 +154,8 @@ _cogl_journal_new (CoglFramebuffer *framebuffer)
journal->entries = g_array_new (FALSE, FALSE, sizeof (CoglJournalEntry)); journal->entries = g_array_new (FALSE, FALSE, sizeof (CoglJournalEntry));
journal->vertices = g_array_new (FALSE, FALSE, sizeof (float)); journal->vertices = g_array_new (FALSE, FALSE, sizeof (float));
COGL_TAILQ_INIT (&journal->pending_fences);
return _cogl_journal_object_new (journal); return _cogl_journal_object_new (journal);
} }
@ -1267,6 +1269,18 @@ _cogl_journal_all_entries_within_bounds (CoglJournal *journal,
return TRUE; 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 /* XXX NB: When _cogl_journal_flush() returns all state relating
* to pipelines, all glEnable flags and current matrix state * to pipelines, all glEnable flags and current matrix state
* is undefined. * is undefined.
@ -1290,7 +1304,10 @@ _cogl_journal_flush (CoglJournal *journal)
0 /* no application private data */); 0 /* no application private data */);
if (journal->entries->len == 0) if (journal->entries->len == 0)
{
post_fences (journal);
return; return;
}
framebuffer = journal->framebuffer; framebuffer = journal->framebuffer;
ctx = framebuffer->context; ctx = framebuffer->context;
@ -1388,6 +1405,8 @@ _cogl_journal_flush (CoglJournal *journal)
_cogl_journal_discard (journal); _cogl_journal_discard (journal);
COGL_TIMER_STOP (_cogl_uprof_context, discard_timer); COGL_TIMER_STOP (_cogl_uprof_context, discard_timer);
post_fences (journal);
COGL_TIMER_STOP (_cogl_uprof_context, flush_timer); COGL_TIMER_STOP (_cogl_uprof_context, flush_timer);
} }

View File

@ -56,7 +56,8 @@ typedef enum
COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS = 1L<<20, COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS = 1L<<20,
COGL_PRIVATE_FEATURE_ALPHA_TEXTURES = 1L<<21, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES = 1L<<21,
COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE = 1L<<22, 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; } CoglPrivateFeatureFlags;
/* Sometimes when evaluating pipelines, either during comparisons or /* Sometimes when evaluating pipelines, either during comparisons or

View File

@ -109,6 +109,7 @@
#include <cogl/cogl-onscreen.h> #include <cogl/cogl-onscreen.h>
#include <cogl/cogl-frame-info.h> #include <cogl/cogl-frame-info.h>
#include <cogl/cogl-poll.h> #include <cogl/cogl-poll.h>
#include <cogl/cogl-fence.h>
#if defined (COGL_HAS_EGL_PLATFORM_KMS_SUPPORT) #if defined (COGL_HAS_EGL_PLATFORM_KMS_SUPPORT)
#include <cogl/cogl-kms-renderer.h> #include <cogl/cogl-kms-renderer.h>
#include <cogl/cogl-kms-display.h> #include <cogl/cogl-kms-display.h>

View File

@ -981,3 +981,7 @@ _cogl_atlas_texture_new_with_size
_cogl_atlas_texture_remove_reorganize_callback _cogl_atlas_texture_remove_reorganize_callback
_cogl_context_get_default _cogl_context_get_default
#endif #endif
cogl_fence_closure_get_user_data
cogl_framebuffer_add_fence_callback
cogl_framebuffer_cancel_fence_callback

View File

@ -597,6 +597,9 @@ _cogl_driver_update_features (CoglContext *ctx,
COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS | COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS |
COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL); COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL);
if (ctx->glFenceSync)
COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_FENCE, TRUE);
/* Cache features */ /* Cache features */
ctx->private_feature_flags |= private_flags; ctx->private_feature_flags |= private_flags;
ctx->feature_flags |= flags; ctx->feature_flags |= flags;

View File

@ -346,6 +346,12 @@ _cogl_driver_update_features (CoglContext *context,
if (_cogl_check_extension ("GL_EXT_unpack_subimage", gl_extensions)) if (_cogl_check_extension ("GL_EXT_unpack_subimage", gl_extensions))
private_flags |= COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE; 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 */ /* Cache features */
context->private_feature_flags |= private_flags; context->private_feature_flags |= private_flags;
context->feature_flags |= flags; context->feature_flags |= flags;

View File

@ -297,3 +297,15 @@ COGL_EXT_FUNCTION (GLvoid *, glMapBufferRange,
GLsizeiptr length, GLsizeiptr length,
GLbitfield access)) GLbitfield access))
COGL_EXT_END () 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 ()

View File

@ -112,3 +112,23 @@ COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglSwapBuffersWithDamage,
const EGLint *rects, const EGLint *rects,
EGLint n_rects)) EGLint n_rects))
COGL_WINSYS_FEATURE_END () 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

View File

@ -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_X11_PIXMAP =1L<<1,
COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_WAYLAND_BUFFER =1L<<2, COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_WAYLAND_BUFFER =1L<<2,
COGL_EGL_WINSYS_FEATURE_CREATE_CONTEXT =1L<<3, 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; } CoglEGLWinsysFeature;
typedef struct _CoglRendererEGL typedef struct _CoglRendererEGL

View File

@ -504,6 +504,10 @@ _cogl_winsys_context_init (CoglContext *context, CoglError **error)
COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE); 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 /* NB: We currently only support creating standalone GLES2 contexts
* for offscreen rendering and so we need a dummy (non-visible) * for offscreen rendering and so we need a dummy (non-visible)
* surface to be able to bind those contexts */ * surface to be able to bind those contexts */
@ -902,6 +906,45 @@ _cogl_winsys_restore_context (CoglContext *ctx)
egl_display->egl_context); 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 = static CoglWinsysVtable _cogl_winsys_vtable =
{ {
.constraints = COGL_RENDERER_CONSTRAINT_USES_EGL | .constraints = COGL_RENDERER_CONSTRAINT_USES_EGL |
@ -936,6 +979,12 @@ static CoglWinsysVtable _cogl_winsys_vtable =
.save_context = _cogl_winsys_save_context, .save_context = _cogl_winsys_save_context,
.set_gles2_context = _cogl_winsys_set_gles2_context, .set_gles2_context = _cogl_winsys_set_gles2_context,
.restore_context = _cogl_winsys_restore_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 /* XXX: we use a function because no doubt someone will complain

View File

@ -185,6 +185,15 @@ typedef struct _CoglWinsysVtable
void void
(*destroy_gles2_context) (CoglGLES2Context *gles2_ctx); (*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; } CoglWinsysVtable;
CoglBool CoglBool

View File

@ -143,6 +143,7 @@
<xi:include href="xml/cogl-vector.xml"/> <xi:include href="xml/cogl-vector.xml"/>
<xi:include href="xml/cogl-euler.xml"/> <xi:include href="xml/cogl-euler.xml"/>
<xi:include href="xml/cogl-quaternion.xml"/> <xi:include href="xml/cogl-quaternion.xml"/>
<xi:include href="xml/cogl-fence.xml"/>
<xi:include href="xml/cogl-version.xml"/> <xi:include href="xml/cogl-version.xml"/>
</section> </section>

View File

@ -961,6 +961,17 @@ cogl_vector3_dot_product
cogl_vector3_distance cogl_vector3_distance
</SECTION> </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> <SECTION>
<FILE>cogl-version</FILE> <FILE>cogl-version</FILE>
<TITLE>Versioning utility macros</TITLE> <TITLE>Versioning utility macros</TITLE>

View File

@ -66,6 +66,7 @@ test_sources = \
test-primitive-and-journal.c \ test-primitive-and-journal.c \
test-copy-replace-texture.c \ test-copy-replace-texture.c \
test-pipeline-cache-unrefs-texture.c \ test-pipeline-cache-unrefs-texture.c \
test-fence.c \
$(NULL) $(NULL)
test_conformance_SOURCES = $(common_sources) $(test_sources) test_conformance_SOURCES = $(common_sources) $(test_sources)

View File

@ -132,6 +132,8 @@ main (int argc, char **argv)
ADD_TEST (test_euler_quaternion, 0, 0); ADD_TEST (test_euler_quaternion, 0, 0);
ADD_TEST (test_fence, TEST_REQUIREMENT_FENCE, 0);
g_printerr ("Unknown test name \"%s\"\n", argv[1]); g_printerr ("Unknown test name \"%s\"\n", argv[1]);
return 1; return 1;

View File

@ -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");
}

View File

@ -71,6 +71,12 @@ check_flags (TestFlags flags,
return FALSE; return FALSE;
} }
if (flags & TEST_REQUIREMENT_FENCE &&
!cogl_has_feature (test_ctx, COGL_FEATURE_ID_FENCE))
{
return FALSE;
}
if (flags & TEST_KNOWN_FAILURE) if (flags & TEST_KNOWN_FAILURE)
{ {
return FALSE; return FALSE;

View File

@ -20,7 +20,8 @@ typedef enum _TestFlags
TEST_REQUIREMENT_GLES2_CONTEXT = 1<<6, TEST_REQUIREMENT_GLES2_CONTEXT = 1<<6,
TEST_REQUIREMENT_MAP_WRITE = 1<<7, TEST_REQUIREMENT_MAP_WRITE = 1<<7,
TEST_REQUIREMENT_GLSL = 1<<8, TEST_REQUIREMENT_GLSL = 1<<8,
TEST_REQUIREMENT_OFFSCREEN = 1<<9 TEST_REQUIREMENT_OFFSCREEN = 1<<9,
TEST_REQUIREMENT_FENCE = 1<<10
} TestFlags; } TestFlags;
extern CoglContext *test_ctx; extern CoglContext *test_ctx;