blob: 9a1002b29a77c22bf9249c37ec35072384b9426f [file] [log] [blame]
/*
* Mesa 3-D graphics library
* Version: 7.9
*
* Copyright (C) 2010 LunarG Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Authors:
* Chia-I Wu <olv@lunarg.com>
*/
#include "util/u_memory.h"
#include "util/u_atomic.h"
#include "os/os_thread.h"
#include "eglsync.h"
#include "eglcurrent.h"
#include "egl_g3d.h"
#include "egl_g3d_sync.h"
/**
* Wait for the conditional variable.
*/
static EGLint
egl_g3d_wait_sync_condvar(struct egl_g3d_sync *gsync, EGLTimeKHR timeout)
{
_EGLDisplay *dpy = gsync->base.Resource.Display;
pipe_mutex_lock(gsync->mutex);
/* unlock display lock just before waiting */
_eglUnlockMutex(&dpy->Mutex);
/* No timed wait. Always treat timeout as EGL_FOREVER_KHR */
pipe_condvar_wait(gsync->condvar, gsync->mutex);
_eglLockMutex(&dpy->Mutex);
pipe_mutex_unlock(gsync->mutex);
return EGL_CONDITION_SATISFIED_KHR;
}
/**
* Signal the conditional variable.
*/
static void
egl_g3d_signal_sync_condvar(struct egl_g3d_sync *gsync)
{
pipe_mutex_lock(gsync->mutex);
pipe_condvar_broadcast(gsync->condvar);
pipe_mutex_unlock(gsync->mutex);
}
/**
* Insert a fence command to the command stream of the current context.
*/
static EGLint
egl_g3d_insert_fence_sync(struct egl_g3d_sync *gsync)
{
_EGLContext *ctx = _eglGetCurrentContext();
struct egl_g3d_context *gctx = egl_g3d_context(ctx);
/* already checked in egl_g3d_create_sync */
assert(gctx);
/* insert the fence command */
gctx->stctxi->flush(gctx->stctxi, 0x0, &gsync->fence);
if (!gsync->fence)
gsync->base.SyncStatus = EGL_SIGNALED_KHR;
return EGL_SUCCESS;
}
/**
* Wait for the fence sync to be signaled.
*/
static EGLint
egl_g3d_wait_fence_sync(struct egl_g3d_sync *gsync, EGLTimeKHR timeout)
{
EGLint ret;
if (gsync->fence) {
_EGLDisplay *dpy = gsync->base.Resource.Display;
struct egl_g3d_display *gdpy = egl_g3d_display(dpy);
struct pipe_screen *screen = gdpy->native->screen;
struct pipe_fence_handle *fence = gsync->fence;
gsync->fence = NULL;
_eglUnlockMutex(&dpy->Mutex);
/* no timed finish? */
screen->fence_finish(screen, fence, PIPE_TIMEOUT_INFINITE);
ret = EGL_CONDITION_SATISFIED_KHR;
_eglLockMutex(&dpy->Mutex);
gsync->base.SyncStatus = EGL_SIGNALED_KHR;
screen->fence_reference(screen, &fence, NULL);
egl_g3d_signal_sync_condvar(gsync);
}
else {
ret = egl_g3d_wait_sync_condvar(gsync, timeout);
}
return ret;
}
static INLINE void
egl_g3d_ref_sync(struct egl_g3d_sync *gsync)
{
_eglGetSync(&gsync->base);
}
static INLINE void
egl_g3d_unref_sync(struct egl_g3d_sync *gsync)
{
if (_eglPutSync(&gsync->base)) {
pipe_condvar_destroy(gsync->condvar);
pipe_mutex_destroy(gsync->mutex);
if (gsync->fence) {
struct egl_g3d_display *gdpy =
egl_g3d_display(gsync->base.Resource.Display);
struct pipe_screen *screen = gdpy->native->screen;
screen->fence_reference(screen, &gsync->fence, NULL);
}
FREE(gsync);
}
}
_EGLSync *
egl_g3d_create_sync(_EGLDriver *drv, _EGLDisplay *dpy,
EGLenum type, const EGLint *attrib_list)
{
_EGLContext *ctx = _eglGetCurrentContext();
struct egl_g3d_sync *gsync;
EGLint err;
if (!ctx || ctx->Resource.Display != dpy) {
_eglError(EGL_BAD_MATCH, "eglCreateSyncKHR");
return NULL;
}
gsync = CALLOC_STRUCT(egl_g3d_sync);
if (!gsync) {
_eglError(EGL_BAD_ALLOC, "eglCreateSyncKHR");
return NULL;
}
if (!_eglInitSync(&gsync->base, dpy, type, attrib_list)) {
FREE(gsync);
return NULL;
}
switch (type) {
case EGL_SYNC_REUSABLE_KHR:
err = EGL_SUCCESS;
break;
case EGL_SYNC_FENCE_KHR:
err = egl_g3d_insert_fence_sync(gsync);
break;
default:
err = EGL_BAD_ATTRIBUTE;
break;
}
if (err != EGL_SUCCESS) {
_eglError(err, "eglCreateSyncKHR");
FREE(gsync);
return NULL;
}
pipe_mutex_init(gsync->mutex);
pipe_condvar_init(gsync->condvar);
return &gsync->base;
}
EGLBoolean
egl_g3d_destroy_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync)
{
struct egl_g3d_sync *gsync = egl_g3d_sync(sync);
switch (gsync->base.Type) {
case EGL_SYNC_REUSABLE_KHR:
/* signal the waiters */
if (gsync->base.SyncStatus != EGL_SIGNALED_KHR) {
gsync->base.SyncStatus = EGL_SIGNALED_KHR;
egl_g3d_signal_sync_condvar(gsync);
}
break;
default:
break;
}
egl_g3d_unref_sync(gsync);
return EGL_TRUE;
}
EGLint
egl_g3d_client_wait_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync,
EGLint flags, EGLTimeKHR timeout)
{
struct egl_g3d_sync *gsync = egl_g3d_sync(sync);
EGLint ret = EGL_CONDITION_SATISFIED_KHR;
if (gsync->base.SyncStatus != EGL_SIGNALED_KHR) {
/* flush if there is a current context */
if (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) {
_EGLContext *ctx = _eglGetCurrentContext();
struct egl_g3d_context *gctx = egl_g3d_context(ctx);
if (gctx)
gctx->stctxi->flush(gctx->stctxi, ST_FLUSH_FRONT, NULL);
}
if (timeout) {
/* reference the sync object in case it is destroyed while waiting */
egl_g3d_ref_sync(gsync);
switch (gsync->base.Type) {
case EGL_SYNC_REUSABLE_KHR:
ret = egl_g3d_wait_sync_condvar(gsync, timeout);
break;
case EGL_SYNC_FENCE_KHR:
ret = egl_g3d_wait_fence_sync(gsync, timeout);
default:
break;
}
egl_g3d_unref_sync(gsync);
}
else {
ret = EGL_TIMEOUT_EXPIRED_KHR;
}
}
return ret;
}
EGLBoolean
egl_g3d_signal_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync,
EGLenum mode)
{
struct egl_g3d_sync *gsync = egl_g3d_sync(sync);
/* only for reusable sync */
if (sync->Type != EGL_SYNC_REUSABLE_KHR)
return _eglError(EGL_BAD_MATCH, "eglSignalSyncKHR");
if (gsync->base.SyncStatus != mode) {
gsync->base.SyncStatus = mode;
if (mode == EGL_SIGNALED_KHR)
egl_g3d_signal_sync_condvar(gsync);
}
return EGL_TRUE;
}