ea7d3b8476
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)
229 lines
6.0 KiB
C
229 lines
6.0 KiB
C
/*
|
|
* 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);
|
|
}
|
|
}
|