[Mesa-dev] [PATCH v3] egl: add EGL_KHR_reusable_sync to egl_dri

Marek Olšák maraeo at gmail.com
Tue Apr 5 13:36:04 UTC 2016


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 <dongwon.kim at intel.com> 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 <dongwon.kim at intel.com>
> ---
>  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, &current);
> +
> +            /* 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
>


More information about the mesa-dev mailing list