Thanks. I've pushed this patch along with an update to Mesa release notes. Marek
On Tue, Apr 5, 2016 at 2:14 AM, Dongwon Kim <[email protected]> wrote: > 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 <[email protected]> > --- > 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 8f50f0c..490b040 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 <c11/threads.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) { > @@ -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 52ad92b..ef79939 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 dd145a1..9ef785e 100644 > --- a/src/egl/main/eglapi.c > +++ b/src/egl/main/eglapi.c > @@ -1467,9 +1467,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 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 [email protected] https://lists.freedesktop.org/mailman/listinfo/mesa-dev
