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;