0,0 → 1,278 |
/* |
* Mesa 3-D graphics library |
* |
* 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; |
} |