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

Dongwon Kim dongwon.kim at intel.com
Fri Apr 1 23:46:36 UTC 2016


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

Signed-off-by: Dongwon Kim <dongwon.kim at intel.com>
---
 src/egl/drivers/dri2/egl_dri2.c | 210 ++++++++++++++++++++++++++++++++++++++--
 src/egl/drivers/dri2/egl_dri2.h |   2 +
 src/egl/main/eglapi.c           |   8 ++
 src/egl/main/eglsync.c          |   3 +-
 4 files changed, 213 insertions(+), 10 deletions(-)

diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
index 8f50f0c..843cd53 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) {
@@ -2389,14 +2393,22 @@ 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)
 {
    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);
    }
+
+   return EGL_TRUE;
 }
 
 static _EGLSync *
@@ -2408,6 +2420,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 +2464,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 +2506,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 */
+      err = cnd_broadcast(&dri2_sync->cond);
+
+      if (err) {
+         _eglError(EGL_BAD_ACCESS, "eglDestroySyncKHR");
+         ret = EGL_FALSE;
+      }
+   }
+
+   err = dri2_egl_unref_sync(dri2_dpy, dri2_sync);
+
+   if (err == EGL_FALSE) {
+      _eglError(EGL_BAD_ACCESS, "eglDestroySyncKHR");
+      ret = EGL_FALSE;
+   }
+
+   return ret;
 }
 
 static EGLint
@@ -2471,11 +2540,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 cnd_timedwait */
+   struct timespec current;
+   xtime expire;
+
    EGLint ret = EGL_CONDITION_SATISFIED_KHR;
+   EGLint err;
 
    /* The EGL_KHR_fence_sync spec states:
     *
@@ -2488,17 +2564,132 @@ 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 should be smaller than 2000000000L */
+            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:
+   err = dri2_egl_unref_sync(dri2_dpy, dri2_sync);
+
+   /* fail to unreference dri2_sync */
+   if (ret == EGL_FALSE || err == EGL_FALSE) {
+      _eglError(EGL_BAD_ACCESS, "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 = 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 +2811,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..6d7fd88 100644
--- a/src/egl/main/eglapi.c
+++ b/src/egl/main/eglapi.c
@@ -1467,6 +1467,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



More information about the mesa-dev mailing list