mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-23 04:40:09 +01:00
egl: add EGL_KHR_reusable_sync to egl_dri
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@intel.com> Signed-off-by: Marek Olšák <marek.olsak@amd.com>
This commit is contained in:
parent
3e13572826
commit
70299474f5
4 changed files with 206 additions and 8 deletions
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -307,6 +307,8 @@ struct dri2_egl_image
|
|||
|
||||
struct dri2_egl_sync {
|
||||
_EGLSync base;
|
||||
mtx_t mutex;
|
||||
cnd_t cond;
|
||||
int refcount;
|
||||
void *fence;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue