+static void
+dri2_egl_ref_sync(struct dri2_egl_sync *sync)
+{
+ p_atomic_inc(&sync->refcount);
+}
+
+static void
+dri2_egl_unref_sync(struct dri2_egl_display *dri2_dpy,
+ struct dri2_egl_sync *dri2_sync)
+{
+ if (p_atomic_dec_zero(&dri2_sync->refcount)) {
+ 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);
+ }
+}
+
+static _EGLSync *
+dri2_create_sync(_EGLDriver *drv, _EGLDisplay *dpy,
+ EGLenum type, const EGLint *attrib_list,
+ const EGLAttrib *attrib_list64)
+{
+ _EGLContext *ctx = _eglGetCurrentContext();
+ 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) {
+ _eglError(EGL_BAD_ALLOC, "eglCreateSyncKHR");
+ return NULL;
+ }
+
+ if (!_eglInitSync(&dri2_sync->base, dpy, type, attrib_list,
+ attrib_list64)) {
+ free(dri2_sync);
+ return NULL;
+ }
+
+ switch (type) {
+ case EGL_SYNC_FENCE_KHR:
+ dri2_sync->fence = dri2_dpy->fence->create_fence(dri2_ctx->dri_context);
+ if (!dri2_sync->fence) {
+ /* Why did it fail? DRI doesn't return an error code, so we emit
+ * a generic EGL error that doesn't communicate user error.
+ */
+ _eglError(EGL_BAD_ALLOC, "eglCreateSyncKHR");
+ free(dri2_sync);
+ return NULL;
+ }
+ break;
+
+ case EGL_SYNC_CL_EVENT_KHR:
+ dri2_sync->fence = dri2_dpy->fence->get_fence_from_cl_event(
+ dri2_dpy->dri_screen,
+ dri2_sync->base.CLEvent);
+ /* this can only happen if the cl_event passed in is invalid. */
+ if (!dri2_sync->fence) {
+ _eglError(EGL_BAD_ATTRIBUTE, "eglCreateSyncKHR");
+ free(dri2_sync);
+ return NULL;
+ }
+
+ /* the initial status must be "signaled" if the cl_event is signaled */
+ if (dri2_dpy->fence->client_wait_sync(dri2_ctx->dri_context,
+ 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);
+ return &dri2_sync->base;
+}
+
+static EGLBoolean
+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 ret;
+}
+
+static EGLint
+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:
+ *
+ * "If no context is current for the bound API,
+ * the EGL_SYNC_FLUSH_COMMANDS_BIT_KHR bit is ignored.
+ */
+ if (dri2_ctx && flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR)
+ wait_flags |= __DRI2_FENCE_FLAG_FLUSH_COMMANDS;
+
+ /* the sync object should take a reference while waiting */
+ dri2_egl_ref_sync(dri2_sync);
+
+ 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;
+ 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) {
+ mtx_lock(&dri2_sync->mutex);
+ cnd_wait(&dri2_sync->cond, &dri2_sync->mutex);
+ mtx_unlock(&dri2_sync->mutex);
+ } 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;
+ }
+
+ mtx_lock(&dri2_sync->mutex);
+ ret = cnd_timedwait(&dri2_sync->cond, &dri2_sync->mutex, &expire);
+ mtx_unlock(&dri2_sync->mutex);
+
+ 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;
+ }
+ 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)
+{
+ _EGLContext *ctx = _eglGetCurrentContext();
+ 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);
+
+ dri2_dpy->fence->server_wait_sync(dri2_ctx->dri_context,
+ dri2_sync->fence, 0);
+ return EGL_TRUE;
+}
+
+static int
+dri2_interop_query_device_info(_EGLDisplay *dpy, _EGLContext *ctx,
+ struct mesa_glinterop_device_info *out)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
+ struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
+
+ if (!dri2_dpy->interop)
+ return MESA_GLINTEROP_UNSUPPORTED;
+
+ return dri2_dpy->interop->query_device_info(dri2_ctx->dri_context, out);
+}
+
+static int
+dri2_interop_export_object(_EGLDisplay *dpy, _EGLContext *ctx,
+ struct mesa_glinterop_export_in *in,
+ struct mesa_glinterop_export_out *out)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
+ struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
+
+ if (!dri2_dpy->interop)
+ return MESA_GLINTEROP_UNSUPPORTED;
+
+ return dri2_dpy->interop->export_object(dri2_ctx->dri_context, in, out);
+}
+