From 70299474f58ad7c0299ea5f997019880d1c4deef Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Mon, 4 Apr 2016 17:14:10 -0700 Subject: [PATCH] egl: add EGL_KHR_reusable_sync to egl_dri MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 v2 - use thread functions defined in C11/threads.h instead of using direct pthread calls - make the timeout set with reference to CLOCK_MONOTONIC - cleaned up the way expiration time is calculated - (bug fix) in dri2_client_wait_sync, case EGL_SYNC_CL_EVENT_KHR has been added. - (bug fix) in dri2_destroy_sync, return from cond_broadcast call is now stored in 'err' intead of 'ret' to prevent 'ret' from being reset to 'EGL_FALSE' even in successful case - corrected minor syntax problems v3 - dri2_egl_unref_sync now became 'void' type. No more error check is needed for this function call as a result. - (bug fix) resolved issue with duplicated unlocking of display in eglClientWaitSync when type of sync is "EGL_KHR_REUSABLE_SYNC" Signed-off-by: Dongwon Kim Signed-off-by: Marek Olšák --- src/egl/drivers/dri2/egl_dri2.c | 192 +++++++++++++++++++++++++++++++- src/egl/drivers/dri2/egl_dri2.h | 2 + src/egl/main/eglapi.c | 17 ++- src/egl/main/eglsync.c | 3 +- 4 files changed, 206 insertions(+), 8 deletions(-) diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c index 8f50f0ce573..490b0409c98 100644 --- a/src/egl/drivers/dri2/egl_dri2.c +++ b/src/egl/drivers/dri2/egl_dri2.c @@ -38,6 +38,8 @@ #include #include #include +#include +#include #ifdef HAVE_LIBDRM #include #include @@ -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) { @@ -2394,7 +2398,12 @@ dri2_egl_unref_sync(struct dri2_egl_display *dri2_dpy, struct dri2_egl_sync *dri2_sync) { if (p_atomic_dec_zero(&dri2_sync->refcount)) { - dri2_dpy->fence->destroy_fence(dri2_dpy->dri_screen, dri2_sync->fence); + if (dri2_sync->base.Type == EGL_SYNC_REUSABLE_KHR) + cnd_destroy(&dri2_sync->cond); + + if (dri2_sync->fence) + dri2_dpy->fence->destroy_fence(dri2_dpy->dri_screen, dri2_sync->fence); + free(dri2_sync); } } @@ -2408,6 +2417,8 @@ 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; + pthread_condattr_t attr; dri2_sync = calloc(1, sizeof(struct dri2_egl_sync)); if (!dri2_sync) { @@ -2450,6 +2461,37 @@ 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: + /* intialize attr */ + ret = pthread_condattr_init(&attr); + + if (ret) { + _eglError(EGL_BAD_ACCESS, "eglCreateSyncKHR"); + free(dri2_sync); + return NULL; + } + + /* change clock attribute to CLOCK_MONOTONIC */ + ret = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + + if (ret) { + _eglError(EGL_BAD_ACCESS, "eglCreateSyncKHR"); + free(dri2_sync); + return NULL; + } + + ret = pthread_cond_init(&dri2_sync->cond, &attr); + + if (ret) { + _eglError(EGL_BAD_ACCESS, "eglCreateSyncKHR"); + 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 +2503,27 @@ 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; + + /* 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 */ + err = cnd_broadcast(&dri2_sync->cond); + if (err) { + _eglError(EGL_BAD_ACCESS, "eglDestroySyncKHR"); + ret = EGL_FALSE; + } + } dri2_egl_unref_sync(dri2_dpy, dri2_sync); - return EGL_TRUE; + + return ret; } static EGLint @@ -2471,10 +2531,16 @@ 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 cnd_timedwait */ + struct timespec current; + xtime expire; + EGLint ret = EGL_CONDITION_SATISFIED_KHR; /* The EGL_KHR_fence_sync spec states: @@ -2488,17 +2554,130 @@ 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: + case EGL_SYNC_CL_EVENT_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 (mtx_lock(&dri2_sync->mutex)) { + ret = EGL_FALSE; + goto cleanup; + } + + ret = cnd_wait(&dri2_sync->cond, &dri2_sync->mutex); + if (mtx_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_MONOTONIC, ¤t); + + /* calculating when to expire */ + expire.nsec = timeout % 1000000000L; + expire.sec = timeout / 1000000000L; + + expire.nsec += current.tv_nsec; + expire.sec += current.tv_sec; + + /* expire.nsec now is a number between 0 and 1999999998 */ + if (expire.nsec > 999999999L) { + expire.sec++; + expire.nsec -= 1000000000L; + } + + if (mtx_lock(&dri2_sync->mutex)) { + ret = EGL_FALSE; + goto cleanup; + } + + ret = cnd_timedwait(&dri2_sync->cond, &dri2_sync->mutex, &expire); + + if (mtx_unlock(&dri2_sync->mutex)) { + ret = EGL_FALSE; + goto cleanup; + } + + if (ret) + if (ret == thrd_busy) { + if (dri2_sync->base.SyncStatus == EGL_UNSIGNALED_KHR) { + ret = EGL_TIMEOUT_EXPIRED_KHR; + } else { + _eglError(EGL_BAD_ACCESS, "eglClientWaitSyncKHR"); + ret = EGL_FALSE; + } + } + } + } + break; + } + + cleanup: dri2_egl_unref_sync(dri2_dpy, dri2_sync); + + if (ret == EGL_FALSE) { + _eglError(EGL_BAD_ACCESS, "eglClientWaitSyncKHR"); + return EGL_FALSE; + } + 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 = cnd_broadcast(&dri2_sync->cond); + + /* fail to broadcast */ + if (ret) { + _eglError(EGL_BAD_ACCESS, "eglSignalSyncKHR"); + return EGL_FALSE; + } + } + + return EGL_TRUE; +} + static EGLint dri2_server_wait_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync) { @@ -2620,6 +2799,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 52ad92b182d..ef799398474 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; + mtx_t mutex; + cnd_t cond; int refcount; void *fence; }; diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c index 8886759011a..64ffe92be43 100644 --- a/src/egl/main/eglapi.c +++ b/src/egl/main/eglapi.c @@ -1469,9 +1469,24 @@ 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); + /* + * 'disp' is already unlocked for reusable sync type, + * so passing 'NULL' to bypass unlocking display. + */ + if (s->Type == EGL_SYNC_REUSABLE_KHR) + RETURN_EGL_EVAL(NULL, ret); + else + RETURN_EGL_EVAL(disp, ret); } diff --git a/src/egl/main/eglsync.c b/src/egl/main/eglsync.c index 999cb480c4b..33625e97ae3 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; -- 2.30.2