/* * 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 */ #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" #ifdef EGL_KHR_reusable_sync /** * 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; } #endif /* EGL_KHR_reusable_sync */