This patch enables an EGL extension, EGL_KHR_reusable_sync. This new extension basically provides a way for multiple APIs or threads to be excuted synchronously via a "reusable sync" primitive shared by those threads/API calls.
This was implemented based on the specification at https://www.khronos.org/registry/egl/extensions/KHR/EGL_KHR_reusable_sync.txt Signed-off-by: Dongwon Kim <dongwon....@intel.com> --- src/egl/drivers/dri2/egl_dri2.c | 197 ++++++++++++++++++++++++++++++++++++++-- src/egl/drivers/dri2/egl_dri2.h | 2 + src/egl/main/eglapi.c | 8 ++ src/egl/main/eglsync.c | 3 +- 4 files changed, 200 insertions(+), 10 deletions(-) diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c index 8f50f0c..78164e4 100644 --- a/src/egl/drivers/dri2/egl_dri2.c +++ b/src/egl/drivers/dri2/egl_dri2.c @@ -38,6 +38,8 @@ #include <fcntl.h> #include <errno.h> #include <unistd.h> +#include <pthread.h> +#include <time.h> #ifdef HAVE_LIBDRM #include <xf86drm.h> #include <drm_fourcc.h> @@ -623,6 +625,8 @@ dri2_setup_screen(_EGLDisplay *disp) disp->Extensions.KHR_cl_event2 = EGL_TRUE; } + disp->Extensions.KHR_reusable_sync = EGL_TRUE; + if (dri2_dpy->image) { if (dri2_dpy->image->base.version >= 10 && dri2_dpy->image->getCapabilities != NULL) { @@ -2389,14 +2393,33 @@ dri2_egl_ref_sync(struct dri2_egl_sync *sync) p_atomic_inc(&sync->refcount); } -static void +static EGLint dri2_egl_unref_sync(struct dri2_egl_display *dri2_dpy, struct dri2_egl_sync *dri2_sync) { + EGLint ret; + if (p_atomic_dec_zero(&dri2_sync->refcount)) { - dri2_dpy->fence->destroy_fence(dri2_dpy->dri_screen, dri2_sync->fence); + /* mutex and cond should be freed if not freed yet. */ + if (dri2_sync->mutex) + free(dri2_sync->mutex); + + if (dri2_sync->cond) { + ret = pthread_cond_destroy(dri2_sync->cond); + + if (ret) + return EGL_FALSE; + + free(dri2_sync->cond); + } + + if (dri2_sync->fence) + dri2_dpy->fence->destroy_fence(dri2_dpy->dri_screen, dri2_sync->fence); + free(dri2_sync); } + + return EGL_TRUE; } static _EGLSync * @@ -2408,6 +2431,7 @@ dri2_create_sync(_EGLDriver *drv, _EGLDisplay *dpy, struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); struct dri2_egl_sync *dri2_sync; + EGLint ret; dri2_sync = calloc(1, sizeof(struct dri2_egl_sync)); if (!dri2_sync) { @@ -2450,6 +2474,23 @@ dri2_create_sync(_EGLDriver *drv, _EGLDisplay *dpy, dri2_sync->fence, 0, 0)) dri2_sync->base.SyncStatus = EGL_SIGNALED_KHR; break; + + case EGL_SYNC_REUSABLE_KHR: + dri2_sync->cond = calloc(1, sizeof(pthread_cond_t)); + dri2_sync->mutex = calloc(1, sizeof(pthread_mutex_t)); + ret = pthread_cond_init(dri2_sync->cond, NULL); + + if (ret) { + _eglError(EGL_BAD_PARAMETER, "eglCreateSyncKHR"); + free(dri2_sync->cond); + free(dri2_sync->mutex); + free(dri2_sync); + return NULL; + } + + /* initial status of reusable sync must be "unsignaled" */ + dri2_sync->base.SyncStatus = EGL_UNSIGNALED_KHR; + break; } p_atomic_set(&dri2_sync->refcount, 1); @@ -2461,9 +2502,33 @@ dri2_destroy_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); + EGLint ret = EGL_TRUE; + EGLint err; - dri2_egl_unref_sync(dri2_dpy, dri2_sync); - return EGL_TRUE; + /* if type of sync is EGL_SYNC_REUSABLE_KHR and it is not signaled yet, + * then unlock all threads possibly blocked by the reusable sync before + * destroying it. + */ + if (dri2_sync->base.Type == EGL_SYNC_REUSABLE_KHR && + dri2_sync->base.SyncStatus == EGL_UNSIGNALED_KHR) { + dri2_sync->base.SyncStatus = EGL_SIGNALED_KHR; + /* unblock all threads currently blocked by sync */ + ret = pthread_cond_broadcast(dri2_sync->cond); + + if (ret) { + _eglError(EGL_BAD_PARAMETER, "eglDestroySyncKHR"); + ret = EGL_FALSE; + } + } + + err = dri2_egl_unref_sync(dri2_dpy, dri2_sync); + + if (err == EGL_FALSE) { + _eglError(EGL_BAD_PARAMETER, "eglDestroySyncKHR"); + ret = EGL_FALSE; + } + + return ret; } static EGLint @@ -2471,11 +2536,18 @@ dri2_client_wait_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync, EGLint flags, EGLTime timeout) { _EGLContext *ctx = _eglGetCurrentContext(); + struct dri2_egl_driver *dri2_drv = dri2_egl_driver(drv); struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); unsigned wait_flags = 0; + + /* timespecs for pthread_cond_timedwait */ + struct timespec current; + struct timespec expired; + EGLint ret = EGL_CONDITION_SATISFIED_KHR; + EGLint err; /* The EGL_KHR_fence_sync spec states: * @@ -2488,17 +2560,123 @@ dri2_client_wait_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync, /* the sync object should take a reference while waiting */ dri2_egl_ref_sync(dri2_sync); - if (dri2_dpy->fence->client_wait_sync(dri2_ctx ? dri2_ctx->dri_context : NULL, + switch (sync->Type) { + case EGL_SYNC_FENCE_KHR: + if (dri2_dpy->fence->client_wait_sync(dri2_ctx ? dri2_ctx->dri_context : NULL, dri2_sync->fence, wait_flags, timeout)) - dri2_sync->base.SyncStatus = EGL_SIGNALED_KHR; - else - ret = EGL_TIMEOUT_EXPIRED_KHR; + dri2_sync->base.SyncStatus = EGL_SIGNALED_KHR; + else + ret = EGL_TIMEOUT_EXPIRED_KHR; + + break; + + case EGL_SYNC_REUSABLE_KHR: + if (dri2_ctx && dri2_sync->base.SyncStatus==EGL_UNSIGNALED_KHR && + (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR)) { + /* flush context if EGL_SYNC_FLUSH_COMMANDS_BIT_KHR is set */ + if (dri2_drv->glFlush) + dri2_drv->glFlush(); + } + + /* if timeout is EGL_FOREVER_KHR, it should wait without any timeout.*/ + if (timeout == EGL_FOREVER_KHR) { + if (pthread_mutex_lock(dri2_sync->mutex)) { + ret = EGL_FALSE; + goto cleanup; + } + + ret = pthread_cond_wait(dri2_sync->cond, dri2_sync->mutex); + + if (pthread_mutex_unlock(dri2_sync->mutex)) { + ret = EGL_FALSE; + goto cleanup; + } + + if (ret) { + _eglError(EGL_BAD_PARAMETER, "eglClientWaitSyncKHR"); + ret = EGL_FALSE; + } + } else { + /* if reusable sync has not been yet signaled */ + if (dri2_sync->base.SyncStatus!=EGL_SIGNALED_KHR) { + + clock_gettime(CLOCK_REALTIME, ¤t); + + expired.tv_nsec = current.tv_nsec + timeout; + expired.tv_sec = current.tv_sec + expired.tv_nsec/1000000000L; + expired.tv_nsec = current.tv_nsec%1000000000L; + + if (pthread_mutex_lock(dri2_sync->mutex)) { + ret = EGL_FALSE; + goto cleanup; + } + + ret = pthread_cond_timedwait(dri2_sync->cond, dri2_sync->mutex, &expired); + + if (pthread_mutex_unlock(dri2_sync->mutex)) { + ret = EGL_FALSE; + goto cleanup; + } + + if (ret) + if (ret == ETIMEDOUT) { + if (dri2_sync->base.SyncStatus==EGL_UNSIGNALED_KHR) { + ret = EGL_TIMEOUT_EXPIRED_KHR; + } else { + _eglError(EGL_BAD_PARAMETER, "eglClientWaitSyncKHR"); + ret = EGL_FALSE; + } + } + } + } + break; + } + + cleanup: + err = dri2_egl_unref_sync(dri2_dpy, dri2_sync); + + /* fail to unreference dri2_sync */ + if (ret == EGL_FALSE || err == EGL_FALSE) { + _eglError(EGL_BAD_PARAMETER, "eglClientWaitSyncKHR"); + return EGL_FALSE; + } - dri2_egl_unref_sync(dri2_dpy, dri2_sync); return ret; } +static EGLBoolean +dri2_signal_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync, + EGLenum mode) +{ + struct dri2_egl_sync *dri2_sync = dri2_egl_sync(sync); + EGLint ret; + + if (sync->Type!=EGL_SYNC_REUSABLE_KHR) { + _eglError(EGL_BAD_MATCH, "eglSignalSyncKHR"); + return EGL_FALSE; + } + + if (mode != EGL_SIGNALED_KHR && mode != EGL_UNSIGNALED_KHR) { + _eglError(EGL_BAD_ATTRIBUTE, "eglSignalSyncKHR"); + return EGL_FALSE; + } + + dri2_sync->base.SyncStatus = mode; + + if (mode == EGL_SIGNALED_KHR) { + ret = pthread_cond_broadcast(dri2_sync->cond); + + /* fail to broadcast */ + if (ret) { + _eglError(EGL_BAD_PARAMETER, "eglSignalSyncKHR"); + return EGL_FALSE; + } + } + + return EGL_TRUE; +} + static EGLint dri2_server_wait_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync) { @@ -2620,6 +2798,7 @@ _eglBuiltInDriverDRI2(const char *args) dri2_drv->base.API.GetSyncValuesCHROMIUM = dri2_get_sync_values_chromium; dri2_drv->base.API.CreateSyncKHR = dri2_create_sync; dri2_drv->base.API.ClientWaitSyncKHR = dri2_client_wait_sync; + dri2_drv->base.API.SignalSyncKHR = dri2_signal_sync; dri2_drv->base.API.WaitSyncKHR = dri2_server_wait_sync; dri2_drv->base.API.DestroySyncKHR = dri2_destroy_sync; diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h index 52ad92b..f70f384 100644 --- a/src/egl/drivers/dri2/egl_dri2.h +++ b/src/egl/drivers/dri2/egl_dri2.h @@ -307,6 +307,8 @@ struct dri2_egl_image struct dri2_egl_sync { _EGLSync base; + pthread_mutex_t *mutex; + pthread_cond_t *cond; int refcount; void *fence; }; diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c index 32f6823..2ce2720 100644 --- a/src/egl/main/eglapi.c +++ b/src/egl/main/eglapi.c @@ -1476,6 +1476,14 @@ eglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout) if (s->SyncStatus == EGL_SIGNALED_KHR) RETURN_EGL_EVAL(disp, EGL_CONDITION_SATISFIED_KHR); + /* if sync type is EGL_SYNC_REUSABLE_KHR, dpy should be + * unlocked here to allow other threads also to be able to + * go into waiting state. + */ + + if (s->Type == EGL_SYNC_REUSABLE_KHR) + _eglUnlockDisplay(dpy); + ret = drv->API.ClientWaitSyncKHR(drv, disp, s, flags, timeout); RETURN_EGL_EVAL(disp, ret); diff --git a/src/egl/main/eglsync.c b/src/egl/main/eglsync.c index 999cb48..33625e9 100644 --- a/src/egl/main/eglsync.c +++ b/src/egl/main/eglsync.c @@ -152,7 +152,8 @@ _eglGetSyncAttrib(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync, /* update the sync status */ if (sync->SyncStatus != EGL_SIGNALED_KHR && (sync->Type == EGL_SYNC_FENCE_KHR || - sync->Type == EGL_SYNC_CL_EVENT_KHR)) + sync->Type == EGL_SYNC_CL_EVENT_KHR || + sync->Type == EGL_SYNC_REUSABLE_KHR)) drv->API.ClientWaitSyncKHR(drv, dpy, sync, 0, 0); *value = sync->SyncStatus; -- 1.9.1 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev