egl: implement eglQueryDmaBufModifiersEXT
[mesa.git] / src / egl / drivers / dri2 / egl_dri2.c
index 203517fbc9d0e1197b78a449f13669f934282fa7..bcafdf9f4c5a8989696698881ceadcb4f1ebcfb9 100644 (file)
 #include "wayland-drm-client-protocol.h"
 #endif
 
+#ifdef HAVE_X11_PLATFORM
+#include "X11/Xlibint.h"
+#endif
+
 #include "egl_dri2.h"
+#include "loader/loader.h"
 #include "util/u_atomic.h"
 
 /* The kernel header drm_fourcc.h defines the DRM formats below.  We duplicate
 #define DRM_FORMAT_GR88          fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
 #endif
 
+#ifndef DRM_FORMAT_R16
+#define DRM_FORMAT_R16           fourcc_code('R', '1', '6', ' ') /* [15:0] R 16 little endian */
+#endif
+
+#ifndef DRM_FORMAT_GR1616
+#define DRM_FORMAT_GR1616        fourcc_code('G', 'R', '3', '2') /* [31:0] R:G 16:16 little endian */
+#endif
+
+#ifndef DRM_FORMAT_MOD_INVALID
+#define DRM_FORMAT_MOD_INVALID ((1ULL<<56) - 1)
+#endif
+
+static void
+dri_set_background_context(void *loaderPrivate)
+{
+   _EGLContext *ctx = _eglGetCurrentContext();
+   _EGLThreadInfo *t = _eglGetCurrentThread();
+
+   _eglBindContextToThread(ctx, t);
+}
+
+static GLboolean
+dri_is_thread_safe(void *loaderPrivate)
+{
+   struct dri2_egl_surface *dri2_surf = loaderPrivate;
+   _EGLDisplay *display =  dri2_surf->base.Resource.Display;
+
+#ifdef HAVE_X11_PLATFORM
+   Display *xdpy = (Display*)display->PlatformDisplay;
+
+   /* Check Xlib is running in thread safe mode when running on EGL/X11-xlib
+    * platform
+    *
+    * 'lock_fns' is the XLockDisplay function pointer of the X11 display 'dpy'.
+    * It wll be NULL if XInitThreads wasn't called.
+    */
+   if (display->Platform == _EGL_PLATFORM_X11 && xdpy && !xdpy->lock_fns)
+      return false;
+#endif
+
+#ifdef HAVE_WAYLAND_PLATFORM
+   if (display->Platform == _EGL_PLATFORM_WAYLAND)
+      return true;
+#endif
+
+   return true;
+}
+
+const __DRIbackgroundCallableExtension background_callable_extension = {
+   .base = { __DRI_BACKGROUND_CALLABLE, 2 },
+
+   .setBackgroundContext = dri_set_background_context,
+   .isThreadSafe         = dri_is_thread_safe,
+};
+
 const __DRIuseInvalidateExtension use_invalidate = {
    .base = { __DRI_USE_INVALIDATE, 1 }
 };
 
-EGLint dri2_to_egl_attribute_map[] = {
-   0,
-   EGL_BUFFER_SIZE,                /* __DRI_ATTRIB_BUFFER_SIZE */
-   EGL_LEVEL,                        /* __DRI_ATTRIB_LEVEL */
-   EGL_RED_SIZE,                /* __DRI_ATTRIB_RED_SIZE */
-   EGL_GREEN_SIZE,                /* __DRI_ATTRIB_GREEN_SIZE */
-   EGL_BLUE_SIZE,                /* __DRI_ATTRIB_BLUE_SIZE */
-   EGL_LUMINANCE_SIZE,                /* __DRI_ATTRIB_LUMINANCE_SIZE */
-   EGL_ALPHA_SIZE,                /* __DRI_ATTRIB_ALPHA_SIZE */
-   0,                                /* __DRI_ATTRIB_ALPHA_MASK_SIZE */
-   EGL_DEPTH_SIZE,                /* __DRI_ATTRIB_DEPTH_SIZE */
-   EGL_STENCIL_SIZE,                /* __DRI_ATTRIB_STENCIL_SIZE */
-   0,                                /* __DRI_ATTRIB_ACCUM_RED_SIZE */
-   0,                                /* __DRI_ATTRIB_ACCUM_GREEN_SIZE */
-   0,                                /* __DRI_ATTRIB_ACCUM_BLUE_SIZE */
-   0,                                /* __DRI_ATTRIB_ACCUM_ALPHA_SIZE */
-   EGL_SAMPLE_BUFFERS,                /* __DRI_ATTRIB_SAMPLE_BUFFERS */
-   EGL_SAMPLES,                        /* __DRI_ATTRIB_SAMPLES */
-   0,                                /* __DRI_ATTRIB_RENDER_TYPE, */
-   0,                                /* __DRI_ATTRIB_CONFIG_CAVEAT */
-   0,                                /* __DRI_ATTRIB_CONFORMANT */
-   0,                                /* __DRI_ATTRIB_DOUBLE_BUFFER */
-   0,                                /* __DRI_ATTRIB_STEREO */
-   0,                                /* __DRI_ATTRIB_AUX_BUFFERS */
-   0,                                /* __DRI_ATTRIB_TRANSPARENT_TYPE */
-   0,                                /* __DRI_ATTRIB_TRANSPARENT_INDEX_VALUE */
-   0,                                /* __DRI_ATTRIB_TRANSPARENT_RED_VALUE */
-   0,                                /* __DRI_ATTRIB_TRANSPARENT_GREEN_VALUE */
-   0,                                /* __DRI_ATTRIB_TRANSPARENT_BLUE_VALUE */
-   0,                                /* __DRI_ATTRIB_TRANSPARENT_ALPHA_VALUE */
-   0,                                /* __DRI_ATTRIB_FLOAT_MODE (deprecated) */
-   0,                                /* __DRI_ATTRIB_RED_MASK */
-   0,                                /* __DRI_ATTRIB_GREEN_MASK */
-   0,                                /* __DRI_ATTRIB_BLUE_MASK */
-   0,                                /* __DRI_ATTRIB_ALPHA_MASK */
-   EGL_MAX_PBUFFER_WIDTH,        /* __DRI_ATTRIB_MAX_PBUFFER_WIDTH */
-   EGL_MAX_PBUFFER_HEIGHT,        /* __DRI_ATTRIB_MAX_PBUFFER_HEIGHT */
-   EGL_MAX_PBUFFER_PIXELS,        /* __DRI_ATTRIB_MAX_PBUFFER_PIXELS */
-   0,                                /* __DRI_ATTRIB_OPTIMAL_PBUFFER_WIDTH */
-   0,                                /* __DRI_ATTRIB_OPTIMAL_PBUFFER_HEIGHT */
-   0,                                /* __DRI_ATTRIB_VISUAL_SELECT_GROUP */
-   0,                                /* __DRI_ATTRIB_SWAP_METHOD */
-   EGL_MAX_SWAP_INTERVAL,        /* __DRI_ATTRIB_MAX_SWAP_INTERVAL */
-   EGL_MIN_SWAP_INTERVAL,        /* __DRI_ATTRIB_MIN_SWAP_INTERVAL */
-   0,                                /* __DRI_ATTRIB_BIND_TO_TEXTURE_RGB */
-   0,                                /* __DRI_ATTRIB_BIND_TO_TEXTURE_RGBA */
-   0,                                /* __DRI_ATTRIB_BIND_TO_MIPMAP_TEXTURE */
-   0,                                /* __DRI_ATTRIB_BIND_TO_TEXTURE_TARGETS */
-   EGL_Y_INVERTED_NOK,                /* __DRI_ATTRIB_YINVERTED */
-   0,                                /* __DRI_ATTRIB_FRAMEBUFFER_SRGB_CAPABLE */
+static const EGLint dri2_to_egl_attribute_map[__DRI_ATTRIB_MAX] = {
+   [__DRI_ATTRIB_BUFFER_SIZE ]          = EGL_BUFFER_SIZE,
+   [__DRI_ATTRIB_LEVEL]                 = EGL_LEVEL,
+   [__DRI_ATTRIB_RED_SIZE]              = EGL_RED_SIZE,
+   [__DRI_ATTRIB_GREEN_SIZE]            = EGL_GREEN_SIZE,
+   [__DRI_ATTRIB_BLUE_SIZE]             = EGL_BLUE_SIZE,
+   [__DRI_ATTRIB_LUMINANCE_SIZE]        = EGL_LUMINANCE_SIZE,
+   [__DRI_ATTRIB_ALPHA_SIZE]            = EGL_ALPHA_SIZE,
+   [__DRI_ATTRIB_DEPTH_SIZE]            = EGL_DEPTH_SIZE,
+   [__DRI_ATTRIB_STENCIL_SIZE]          = EGL_STENCIL_SIZE,
+   [__DRI_ATTRIB_SAMPLE_BUFFERS]        = EGL_SAMPLE_BUFFERS,
+   [__DRI_ATTRIB_SAMPLES]               = EGL_SAMPLES,
+   [__DRI_ATTRIB_MAX_PBUFFER_WIDTH]     = EGL_MAX_PBUFFER_WIDTH,
+   [__DRI_ATTRIB_MAX_PBUFFER_HEIGHT]    = EGL_MAX_PBUFFER_HEIGHT,
+   [__DRI_ATTRIB_MAX_PBUFFER_PIXELS]    = EGL_MAX_PBUFFER_PIXELS,
+   [__DRI_ATTRIB_MAX_SWAP_INTERVAL]     = EGL_MAX_SWAP_INTERVAL,
+   [__DRI_ATTRIB_MIN_SWAP_INTERVAL]     = EGL_MIN_SWAP_INTERVAL,
+   [__DRI_ATTRIB_YINVERTED]             = EGL_Y_INVERTED_NOK,
 };
 
 const __DRIconfig *
@@ -241,6 +269,15 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id,
             return NULL;
          break;
 
+      case __DRI_ATTRIB_MAX_PBUFFER_WIDTH:
+         _eglSetConfigKey(&base, EGL_MAX_PBUFFER_WIDTH,
+                          _EGL_MAX_PBUFFER_WIDTH);
+         break;
+      case __DRI_ATTRIB_MAX_PBUFFER_HEIGHT:
+         _eglSetConfigKey(&base, EGL_MAX_PBUFFER_HEIGHT,
+                          _EGL_MAX_PBUFFER_HEIGHT);
+         break;
+
       default:
          key = dri2_to_egl_attribute_map[attrib];
          if (key != 0)
@@ -365,37 +402,37 @@ struct dri2_extension_match {
    int offset;
 };
 
-static struct dri2_extension_match dri3_driver_extensions[] = {
+static const struct dri2_extension_match dri3_driver_extensions[] = {
    { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) },
    { __DRI_IMAGE_DRIVER, 1, offsetof(struct dri2_egl_display, image_driver) },
    { NULL, 0, 0 }
 };
 
-static struct dri2_extension_match dri2_driver_extensions[] = {
+static const struct dri2_extension_match dri2_driver_extensions[] = {
    { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) },
    { __DRI_DRI2, 2, offsetof(struct dri2_egl_display, dri2) },
    { NULL, 0, 0 }
 };
 
-static struct dri2_extension_match dri2_core_extensions[] = {
+static const struct dri2_extension_match dri2_core_extensions[] = {
    { __DRI2_FLUSH, 1, offsetof(struct dri2_egl_display, flush) },
    { __DRI_TEX_BUFFER, 2, offsetof(struct dri2_egl_display, tex_buffer) },
    { __DRI_IMAGE, 1, offsetof(struct dri2_egl_display, image) },
    { NULL, 0, 0 }
 };
 
-static struct dri2_extension_match swrast_driver_extensions[] = {
+static const struct dri2_extension_match swrast_driver_extensions[] = {
    { __DRI_CORE, 1, offsetof(struct dri2_egl_display, core) },
    { __DRI_SWRAST, 2, offsetof(struct dri2_egl_display, swrast) },
    { NULL, 0, 0 }
 };
 
-static struct dri2_extension_match swrast_core_extensions[] = {
+static const struct dri2_extension_match swrast_core_extensions[] = {
    { __DRI_TEX_BUFFER, 2, offsetof(struct dri2_egl_display, tex_buffer) },
    { NULL, 0, 0 }
 };
 
-static struct dri2_extension_match optional_core_extensions[] = {
+static const struct dri2_extension_match optional_core_extensions[] = {
    { __DRI2_ROBUSTNESS, 1, offsetof(struct dri2_egl_display, robustness) },
    { __DRI2_CONFIG_QUERY, 1, offsetof(struct dri2_egl_display, config) },
    { __DRI2_FENCE, 1, offsetof(struct dri2_egl_display, fence) },
@@ -406,7 +443,7 @@ static struct dri2_extension_match optional_core_extensions[] = {
 
 static EGLBoolean
 dri2_bind_extensions(struct dri2_egl_display *dri2_dpy,
-                     struct dri2_extension_match *matches,
+                     const struct dri2_extension_match *matches,
                      const __DRIextension **extensions,
                      bool optional)
 {
@@ -422,6 +459,7 @@ dri2_bind_extensions(struct dri2_egl_display *dri2_dpy,
             *(const __DRIextension **) field = extensions[i];
             _eglLog(_EGL_INFO, "found extension %s version %d",
                     extensions[i]->name, extensions[i]->version);
+            break;
          }
       }
    }
@@ -504,8 +542,8 @@ dri2_open_driver(_EGLDisplay *disp)
 
    _eglLog(_EGL_DEBUG, "DRI2: dlopen(%s)", path);
 
-   if (asprintf(&get_extensions_name, "%s_%s",
-                __DRI_DRIVER_GET_EXTENSIONS, dri2_dpy->driver_name) != -1) {
+   get_extensions_name = loader_get_extensions_name(dri2_dpy->driver_name);
+   if (get_extensions_name) {
       get_extensions = dlsym(dri2_dpy->driver, get_extensions_name);
       if (get_extensions) {
          extensions = get_extensions();
@@ -647,6 +685,12 @@ dri2_setup_screen(_EGLDisplay *disp)
       disp->Extensions.KHR_wait_sync = EGL_TRUE;
       if (dri2_dpy->fence->get_fence_from_cl_event)
          disp->Extensions.KHR_cl_event2 = EGL_TRUE;
+      if (dri2_dpy->fence->base.version >= 2) {
+         unsigned capabilities =
+            dri2_dpy->fence->get_capabilities(dri2_dpy->dri_screen);
+         disp->Extensions.ANDROID_native_fence_sync =
+            (capabilities & __DRI_FENCE_CAP_NATIVE_FD) != 0;
+      }
    }
 
    disp->Extensions.KHR_reusable_sync = EGL_TRUE;
@@ -686,14 +730,12 @@ dri2_setup_screen(_EGLDisplay *disp)
    }
 }
 
-/* All platforms but DRM call this function to create the screen, query the
- * dri extensions, setup the vtables and populate the driver_configs.
- * DRM inherits all that information from its display - GBM.
+/* All platforms but DRM call this function to create the screen and populate
+ * the driver_configs. DRM inherits that information from its display - GBM.
  */
 EGLBoolean
 dri2_create_screen(_EGLDisplay *disp)
 {
-   const __DRIextension **extensions;
    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
 
    if (dri2_dpy->image_driver) {
@@ -736,27 +778,28 @@ dri2_create_screen(_EGLDisplay *disp)
    }
 
    dri2_dpy->own_dri_screen = 1;
+   return EGL_TRUE;
+}
+
+EGLBoolean
+dri2_setup_extensions(_EGLDisplay *disp)
+{
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+   const struct dri2_extension_match *mandatory_core_extensions;
+   const __DRIextension **extensions;
 
    extensions = dri2_dpy->core->getExtensions(dri2_dpy->dri_screen);
 
-   if (dri2_dpy->image_driver || dri2_dpy->dri2) {
-      if (!dri2_bind_extensions(dri2_dpy, dri2_core_extensions, extensions, false))
-         goto cleanup_dri_screen;
-   } else {
-      assert(dri2_dpy->swrast);
-      if (!dri2_bind_extensions(dri2_dpy, swrast_core_extensions, extensions, false))
-         goto cleanup_dri_screen;
-   }
+   if (dri2_dpy->image_driver || dri2_dpy->dri2)
+      mandatory_core_extensions = dri2_core_extensions;
+   else
+      mandatory_core_extensions = swrast_core_extensions;
 
-   dri2_bind_extensions(dri2_dpy, optional_core_extensions, extensions, true);
-   dri2_setup_screen(disp);
+   if (!dri2_bind_extensions(dri2_dpy, mandatory_core_extensions, extensions, false))
+      return EGL_FALSE;
 
+   dri2_bind_extensions(dri2_dpy, optional_core_extensions, extensions, true);
    return EGL_TRUE;
-
- cleanup_dri_screen:
-   dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen);
-
-   return EGL_FALSE;
 }
 
 /**
@@ -847,7 +890,6 @@ static void
 dri2_display_release(_EGLDisplay *disp)
 {
    struct dri2_egl_display *dri2_dpy;
-   unsigned i;
 
    if (!disp)
       return;
@@ -861,6 +903,14 @@ dri2_display_release(_EGLDisplay *disp)
       return;
 
    _eglCleanupDisplay(disp);
+   dri2_display_destroy(disp);
+}
+
+void
+dri2_display_destroy(_EGLDisplay *disp)
+{
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+   unsigned i;
 
    if (dri2_dpy->own_dri_screen)
       dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen);
@@ -885,7 +935,7 @@ dri2_display_release(_EGLDisplay *disp)
 #ifdef HAVE_DRM_PLATFORM
    case _EGL_PLATFORM_DRM:
       if (dri2_dpy->own_device) {
-         gbm_device_destroy(&dri2_dpy->gbm_dri->base.base);
+         gbm_device_destroy(&dri2_dpy->gbm_dri->base);
       }
       break;
 #endif
@@ -897,6 +947,7 @@ dri2_display_release(_EGLDisplay *disp)
           wl_shm_destroy(dri2_dpy->wl_shm);
       wl_registry_destroy(dri2_dpy->wl_registry);
       wl_event_queue_destroy(dri2_dpy->wl_queue);
+      wl_proxy_wrapper_destroy(dri2_dpy->wl_dpy_wrapper);
       if (dri2_dpy->own_device) {
          wl_display_disconnect(dri2_dpy->wl_dpy);
       }
@@ -910,7 +961,7 @@ dri2_display_release(_EGLDisplay *disp)
     * the ones from the gbm device. As such the gbm itself is responsible
     * for the cleanup.
     */
-   if (disp->Platform != _EGL_PLATFORM_DRM) {
+   if (disp->Platform != _EGL_PLATFORM_DRM && dri2_dpy->driver_configs) {
       for (i = 0; dri2_dpy->driver_configs[i]; i++)
          free((__DRIconfig *) dri2_dpy->driver_configs[i]);
       free(dri2_dpy->driver_configs);
@@ -1076,6 +1127,20 @@ dri2_create_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf,
    if (!_eglInitContext(&dri2_ctx->base, disp, conf, attrib_list))
       goto cleanup;
 
+   /* The EGL_EXT_create_context_robustness spec says:
+    *
+    *    "Add to the eglCreateContext context creation errors: [...]
+    *
+    *     * If the reset notification behavior of <share_context> and the
+    *       newly created context are different then an EGL_BAD_MATCH error is
+    *       generated."
+    */
+   if (share_list && share_list->ResetNotificationStrategy !=
+                     dri2_ctx->base.ResetNotificationStrategy) {
+      _eglError(EGL_BAD_MATCH, "eglCreateContext");
+      goto cleanup;
+   }
+
    switch (dri2_ctx->base.ClientAPI) {
    case EGL_OPENGL_ES_API:
       switch (dri2_ctx->base.ClientMajorVersion) {
@@ -1258,11 +1323,11 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf,
    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
    struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
    _EGLContext *old_ctx;
-   _EGLDisplay *old_disp = NULL;
    _EGLSurface *old_dsurf, *old_rsurf;
    _EGLSurface *tmp_dsurf, *tmp_rsurf;
    __DRIdrawable *ddraw, *rdraw;
    __DRIcontext *cctx;
+   EGLBoolean unbind;
 
    if (!dri2_dpy)
       return _eglError(EGL_NOT_INITIALIZED, "eglMakeCurrent");
@@ -1273,55 +1338,55 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf,
       return EGL_FALSE;
    }
 
+   /* flush before context switch */
+   if (old_ctx)
+      dri2_drv->glFlush();
+
    ddraw = (dsurf) ? dri2_dpy->vtbl->get_dri_drawable(dsurf) : NULL;
    rdraw = (rsurf) ? dri2_dpy->vtbl->get_dri_drawable(rsurf) : NULL;
    cctx = (dri2_ctx) ? dri2_ctx->dri_context : NULL;
 
    if (old_ctx) {
       __DRIcontext *old_cctx = dri2_egl_context(old_ctx)->dri_context;
-
-      /* flush before context switch */
-      dri2_drv->glFlush();
       dri2_dpy->core->unbindContext(old_cctx);
-
-      /* Keep track of the old dpy as we'll need it for resource cleanup */
-      old_disp = old_ctx->Resource.Display;
    }
 
-   /* Bind if at least one of the primitives is valid ... */
-   if (cctx || ddraw || rdraw) {
-      if (dri2_dpy->core->bindContext(cctx, ddraw, rdraw)) {
-         /* undo the previous _eglBindContext */
-         _eglBindContext(old_ctx, old_dsurf, old_rsurf, &ctx, &tmp_dsurf, &tmp_rsurf);
-         assert(&dri2_ctx->base == ctx &&
-                tmp_dsurf == dsurf &&
-                tmp_rsurf == rsurf);
-
-         _eglPutSurface(dsurf);
-         _eglPutSurface(rsurf);
-         _eglPutContext(ctx);
+   unbind = (cctx == NULL && ddraw == NULL && rdraw == NULL);
 
-         _eglPutSurface(old_dsurf);
-         _eglPutSurface(old_rsurf);
-         _eglPutContext(old_ctx);
+   if (unbind || dri2_dpy->core->bindContext(cctx, ddraw, rdraw)) {
+      dri2_destroy_surface(drv, disp, old_dsurf);
+      dri2_destroy_surface(drv, disp, old_rsurf);
 
-         /* dri2_dpy->core->bindContext failed. We cannot tell for sure why, but
-          * setting the error to EGL_BAD_MATCH is surely better than leaving it
-          * as EGL_SUCCESS.
-          */
-         return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
+      if (!unbind)
+         dri2_dpy->ref_count++;
+      if (old_ctx) {
+         EGLDisplay old_disp = _eglGetDisplayHandle(old_ctx->Resource.Display);
+         dri2_destroy_context(drv, disp, old_ctx);
+         dri2_display_release(old_disp);
       }
 
-      /* ... and refcount the dpy when successful. */
-      dri2_dpy->ref_count++;
+      return EGL_TRUE;
+   } else {
+      /* undo the previous _eglBindContext */
+      _eglBindContext(old_ctx, old_dsurf, old_rsurf, &ctx, &tmp_dsurf, &tmp_rsurf);
+      assert(&dri2_ctx->base == ctx &&
+             tmp_dsurf == dsurf &&
+             tmp_rsurf == rsurf);
+
+      _eglPutSurface(dsurf);
+      _eglPutSurface(rsurf);
+      _eglPutContext(ctx);
+
+      _eglPutSurface(old_dsurf);
+      _eglPutSurface(old_rsurf);
+      _eglPutContext(old_ctx);
+
+      /* dri2_dpy->core->bindContext failed. We cannot tell for sure why, but
+       * setting the error to EGL_BAD_MATCH is surely better than leaving it
+       * as EGL_SUCCESS.
+       */
+      return _eglError(EGL_BAD_MATCH, "eglMakeCurrent");
    }
-
-   dri2_destroy_surface(drv, disp, old_dsurf);
-   dri2_destroy_surface(drv, disp, old_rsurf);
-   dri2_destroy_context(drv, disp, old_ctx);
-   dri2_display_release(old_disp);
-
-   return EGL_TRUE;
 }
 
 __DRIdrawable *
@@ -1531,9 +1596,9 @@ dri2_bind_tex_image(_EGLDriver *drv,
       assert(!"Unexpected texture target in dri2_bind_tex_image()");
    }
 
-   (*dri2_dpy->tex_buffer->setTexBuffer2)(dri2_ctx->dri_context,
-                                          target, format,
-                                          dri_drawable);
+   dri2_dpy->tex_buffer->setTexBuffer2(dri2_ctx->dri_context,
+                                       target, format,
+                                       dri_drawable);
 
    return EGL_TRUE;
 }
@@ -1564,9 +1629,8 @@ dri2_release_tex_image(_EGLDriver *drv,
 
    if (dri2_dpy->tex_buffer->base.version >= 3 &&
        dri2_dpy->tex_buffer->releaseTexBuffer != NULL) {
-      (*dri2_dpy->tex_buffer->releaseTexBuffer)(dri2_ctx->dri_context,
-                                                target,
-                                                dri_drawable);
+      dri2_dpy->tex_buffer->releaseTexBuffer(dri2_ctx->dri_context,
+                                             target, dri_drawable);
    }
 
    return EGL_TRUE;
@@ -1927,6 +1991,37 @@ dri2_check_dma_buf_attribs(const _EGLImageAttribs *attrs)
       }
    }
 
+   /**
+    * If <target> is EGL_LINUX_DMA_BUF_EXT, both or neither of the following
+    * attribute values may be given.
+    *
+    * This is referring to EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT and
+    * EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, and the same for other planes.
+    */
+   for (i = 0; i < DMA_BUF_MAX_PLANES; ++i) {
+      if (attrs->DMABufPlaneModifiersLo[i].IsPresent !=
+          attrs->DMABufPlaneModifiersHi[i].IsPresent) {
+         _eglError(EGL_BAD_PARAMETER, "modifier attribute lo or hi missing");
+         return EGL_FALSE;
+      }
+   }
+
+   /* Although the EGL_EXT_image_dma_buf_import_modifiers spec doesn't
+    * mandate it, we only accept the same modifier across all planes. */
+   for (i = 1; i < DMA_BUF_MAX_PLANES; ++i) {
+      if (attrs->DMABufPlaneFds[i].IsPresent) {
+         if ((attrs->DMABufPlaneModifiersLo[0].IsPresent !=
+               attrs->DMABufPlaneModifiersLo[i].IsPresent) ||
+             (attrs->DMABufPlaneModifiersLo[0].Value !=
+               attrs->DMABufPlaneModifiersLo[i].Value) ||
+             (attrs->DMABufPlaneModifiersHi[0].Value !=
+               attrs->DMABufPlaneModifiersHi[i].Value)) {
+            _eglError(EGL_BAD_PARAMETER, "modifier attributes not equal");
+            return EGL_FALSE;
+         }
+      }
+   }
+
    return EGL_TRUE;
 }
 
@@ -1940,6 +2035,8 @@ dri2_check_dma_buf_format(const _EGLImageAttribs *attrs)
    case DRM_FORMAT_R8:
    case DRM_FORMAT_RG88:
    case DRM_FORMAT_GR88:
+   case DRM_FORMAT_R16:
+   case DRM_FORMAT_GR1616:
    case DRM_FORMAT_RGB332:
    case DRM_FORMAT_BGR233:
    case DRM_FORMAT_XRGB4444:
@@ -2028,12 +2125,30 @@ dri2_check_dma_buf_format(const _EGLImageAttribs *attrs)
     * "If <target> is EGL_LINUX_DMA_BUF_EXT, and the EGL_LINUX_DRM_FOURCC_EXT
     *  attribute indicates a single-plane format, EGL_BAD_ATTRIBUTE is
     *  generated if any of the EGL_DMA_BUF_PLANE1_* or EGL_DMA_BUF_PLANE2_*
-    *  attributes are specified."
+    *  or EGL_DMA_BUF_PLANE3_* attributes are specified."
     */
-   for (i = plane_n; i < 3; ++i) {
+   for (i = plane_n; i < DMA_BUF_MAX_PLANES; ++i) {
       if (attrs->DMABufPlaneFds[i].IsPresent ||
           attrs->DMABufPlaneOffsets[i].IsPresent ||
-          attrs->DMABufPlanePitches[i].IsPresent) {
+          attrs->DMABufPlanePitches[i].IsPresent ||
+          attrs->DMABufPlaneModifiersLo[i].IsPresent ||
+          attrs->DMABufPlaneModifiersHi[i].IsPresent) {
+
+         /**
+          * The modifiers extension spec says:
+          *
+          * "Modifiers may modify any attribute of a buffer import, including
+          *  but not limited to adding extra planes to a format which
+          *  otherwise does not have those planes. As an example, a modifier
+          *  may add a plane for an external compression buffer to a
+          *  single-plane format. The exact meaning and effect of any
+          *  modifier is canonically defined by drm_fourcc.h, not as part of
+          *  this extension."
+          */
+         if (attrs->DMABufPlaneModifiersLo[i].IsPresent &&
+             attrs->DMABufPlaneModifiersHi[i].IsPresent)
+            continue;
+
          _eglError(EGL_BAD_ATTRIBUTE, "too many plane attributes");
          return 0;
       }
@@ -2042,6 +2157,59 @@ dri2_check_dma_buf_format(const _EGLImageAttribs *attrs)
    return plane_n;
 }
 
+static EGLBoolean
+dri2_query_dma_buf_formats(_EGLDriver *drv, _EGLDisplay *disp,
+                            EGLint max, EGLint *formats, EGLint *count)
+{
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+   if (max < 0 || (max > 0 && formats == NULL)) {
+      _eglError(EGL_BAD_PARAMETER, "invalid value for max count of formats");
+      return EGL_FALSE;
+   }
+
+   if (dri2_dpy->image->base.version < 15 ||
+       dri2_dpy->image->queryDmaBufFormats == NULL)
+      return EGL_FALSE;
+
+   if (!dri2_dpy->image->queryDmaBufFormats(dri2_dpy->dri_screen, max,
+                                            formats, count))
+      return EGL_FALSE;
+
+   return EGL_TRUE;
+}
+
+static EGLBoolean
+dri2_query_dma_buf_modifiers(_EGLDriver *drv, _EGLDisplay *disp, EGLint format,
+                             EGLint max, EGLuint64KHR *modifiers,
+                             EGLBoolean *external_only, EGLint *count)
+{
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+
+   if (max < 0) {
+      _eglError(EGL_BAD_PARAMETER, "invalid value for max count of formats");
+      return EGL_FALSE;
+   }
+
+   if (max > 0 && modifiers == NULL) {
+      _eglError(EGL_BAD_PARAMETER, "invalid modifiers array");
+      return EGL_FALSE;
+   }
+
+   if (dri2_dpy->image->base.version < 15 ||
+       dri2_dpy->image->queryDmaBufModifiers == NULL)
+      return EGL_FALSE;
+
+   if (dri2_dpy->image->queryDmaBufModifiers(dri2_dpy->dri_screen, format,
+                                             max, modifiers,
+                                             (unsigned int *) external_only,
+                                             count) == false) {
+      _eglError(EGL_BAD_PARAMETER, "invalid format");
+      return EGL_FALSE;
+   }
+
+   return EGL_TRUE;
+}
+
 /**
  * The spec says:
  *
@@ -2063,9 +2231,11 @@ dri2_create_image_dma_buf(_EGLDisplay *disp, _EGLContext *ctx,
    __DRIimage *dri_image;
    unsigned num_fds;
    unsigned i;
-   int fds[3];
-   int pitches[3];
-   int offsets[3];
+   int fds[DMA_BUF_MAX_PLANES];
+   int pitches[DMA_BUF_MAX_PLANES];
+   int offsets[DMA_BUF_MAX_PLANES];
+   uint64_t modifier;
+   bool has_modifier = false;
    unsigned error;
 
    /**
@@ -2098,16 +2268,47 @@ dri2_create_image_dma_buf(_EGLDisplay *disp, _EGLContext *ctx,
       offsets[i] = attrs.DMABufPlaneOffsets[i].Value;
    }
 
-   dri_image =
-      dri2_dpy->image->createImageFromDmaBufs(dri2_dpy->dri_screen,
-         attrs.Width, attrs.Height, attrs.DMABufFourCC.Value,
-         fds, num_fds, pitches, offsets,
-         attrs.DMABufYuvColorSpaceHint.Value,
-         attrs.DMABufSampleRangeHint.Value,
-         attrs.DMABufChromaHorizontalSiting.Value,
-         attrs.DMABufChromaVerticalSiting.Value,
-         &error,
-         NULL);
+   /* dri2_check_dma_buf_attribs ensures that the modifier, if available,
+    * will be present in attrs.DMABufPlaneModifiersLo[0] and
+    * attrs.DMABufPlaneModifiersHi[0] */
+   if (attrs.DMABufPlaneModifiersLo[0].IsPresent) {
+      modifier =
+         ((uint64_t) attrs.DMABufPlaneModifiersHi[0].Value << 32) |
+         attrs.DMABufPlaneModifiersLo[0].Value;
+      has_modifier = true;
+   } else {
+      modifier = DRM_FORMAT_MOD_INVALID;
+   }
+
+   if (has_modifier) {
+      if (dri2_dpy->image->base.version < 15 ||
+          dri2_dpy->image->createImageFromDmaBufs2 == NULL) {
+         _eglError(EGL_BAD_MATCH, "unsupported dma_buf format modifier");
+         return EGL_NO_IMAGE_KHR;
+      }
+      dri_image =
+         dri2_dpy->image->createImageFromDmaBufs2(dri2_dpy->dri_screen,
+            attrs.Width, attrs.Height, attrs.DMABufFourCC.Value,
+            modifier, fds, num_fds, pitches, offsets,
+            attrs.DMABufYuvColorSpaceHint.Value,
+            attrs.DMABufSampleRangeHint.Value,
+            attrs.DMABufChromaHorizontalSiting.Value,
+            attrs.DMABufChromaVerticalSiting.Value,
+            &error,
+            NULL);
+   }
+   else {
+      dri_image =
+         dri2_dpy->image->createImageFromDmaBufs(dri2_dpy->dri_screen,
+            attrs.Width, attrs.Height, attrs.DMABufFourCC.Value,
+            fds, num_fds, pitches, offsets,
+            attrs.DMABufYuvColorSpaceHint.Value,
+            attrs.DMABufSampleRangeHint.Value,
+            attrs.DMABufChromaHorizontalSiting.Value,
+            attrs.DMABufChromaVerticalSiting.Value,
+            &error,
+            NULL);
+   }
    dri2_create_image_khr_texture_error(error);
 
    if (!dri_image)
@@ -2500,8 +2701,17 @@ 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)
+      switch (dri2_sync->base.Type) {
+      case EGL_SYNC_REUSABLE_KHR:
          cnd_destroy(&dri2_sync->cond);
+         break;
+      case EGL_SYNC_NATIVE_FENCE_ANDROID:
+         if (dri2_sync->base.SyncFd != EGL_NO_NATIVE_FENCE_FD_ANDROID)
+            close(dri2_sync->base.SyncFd);
+         break;
+      default:
+         break;
+      }
 
       if (dri2_sync->fence)
          dri2_dpy->fence->destroy_fence(dri2_dpy->dri_screen, dri2_sync->fence);
@@ -2592,6 +2802,19 @@ dri2_create_sync(_EGLDriver *drv, _EGLDisplay *dpy,
       /* initial status of reusable sync must be "unsignaled" */
       dri2_sync->base.SyncStatus = EGL_UNSIGNALED_KHR;
       break;
+
+   case EGL_SYNC_NATIVE_FENCE_ANDROID:
+      if (dri2_dpy->fence->create_fence_fd) {
+         dri2_sync->fence = dri2_dpy->fence->create_fence_fd(
+                                    dri2_ctx->dri_context,
+                                    dri2_sync->base.SyncFd);
+      }
+      if (!dri2_sync->fence) {
+         _eglError(EGL_BAD_ATTRIBUTE, "eglCreateSyncKHR");
+         free(dri2_sync);
+         return NULL;
+      }
+      break;
    }
 
    p_atomic_set(&dri2_sync->refcount, 1);
@@ -2621,11 +2844,37 @@ dri2_destroy_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync)
          ret = EGL_FALSE;
       }
    }
+
    dri2_egl_unref_sync(dri2_dpy, dri2_sync);
 
    return ret;
 }
 
+static EGLint
+dri2_dup_native_fence_fd(_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);
+
+   assert(sync->Type == EGL_SYNC_NATIVE_FENCE_ANDROID);
+
+   if (sync->SyncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+      /* try to retrieve the actual native fence fd.. if rendering is
+       * not flushed this will just return -1, aka NO_NATIVE_FENCE_FD:
+       */
+      sync->SyncFd = dri2_dpy->fence->get_fence_fd(dri2_dpy->dri_screen,
+                                                   dri2_sync->fence);
+   }
+
+   if (sync->SyncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+      /* if native fence fd still not created, return an error: */
+      _eglError(EGL_BAD_PARAMETER, "eglDupNativeFenceFDANDROID");
+      return EGL_NO_NATIVE_FENCE_FD_ANDROID;
+   }
+
+   return dup(sync->SyncFd);
+}
+
 static EGLint
 dri2_client_wait_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync,
                       EGLint flags, EGLTime timeout)
@@ -2656,6 +2905,7 @@ dri2_client_wait_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync,
 
    switch (sync->Type) {
    case EGL_SYNC_FENCE_KHR:
+   case EGL_SYNC_NATIVE_FENCE_ANDROID:
    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,
@@ -2807,10 +3057,8 @@ dri2_load(_EGLDriver *drv)
    const char *libname = "libglapi.0.dylib";
 #elif defined(__CYGWIN__)
    const char *libname = "cygglapi-0.dll";
-#elif defined(__linux__)
-   const char *libname = "libglapi.so.0";
 #else
-#error Unknown glapi provider for this platform
+   const char *libname = "libglapi.so.0";
 #endif
    void *handle;
 
@@ -2899,6 +3147,8 @@ _eglBuiltInDriverDRI2(const char *args)
    dri2_drv->base.API.ExportDRMImageMESA = dri2_export_drm_image_mesa;
    dri2_drv->base.API.ExportDMABUFImageQueryMESA = dri2_export_dma_buf_image_query_mesa;
    dri2_drv->base.API.ExportDMABUFImageMESA = dri2_export_dma_buf_image_mesa;
+   dri2_drv->base.API.QueryDmaBufFormatsEXT = dri2_query_dma_buf_formats;
+   dri2_drv->base.API.QueryDmaBufModifiersEXT = dri2_query_dma_buf_modifiers;
 #endif
 #ifdef HAVE_WAYLAND_PLATFORM
    dri2_drv->base.API.BindWaylandDisplayWL = dri2_bind_wayland_display_wl;
@@ -2913,6 +3163,7 @@ _eglBuiltInDriverDRI2(const char *args)
    dri2_drv->base.API.DestroySyncKHR = dri2_destroy_sync;
    dri2_drv->base.API.GLInteropQueryDeviceInfo = dri2_interop_query_device_info;
    dri2_drv->base.API.GLInteropExportObject = dri2_interop_export_object;
+   dri2_drv->base.API.DupNativeFenceFDANDROID = dri2_dup_native_fence_fd;
 
    dri2_drv->base.Name = "DRI2";
    dri2_drv->base.Unload = dri2_unload;