[Mesa-dev] [PATCH] egl: adds EGL_KHR_reusable_sync to egl_dri
Dongwon Kim
dongwon.kim at intel.com
Wed Mar 9 01:28:25 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
Signed-off-by: Dongwon Kim <dongwon.kim at 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
More information about the mesa-dev
mailing list