diff --git a/gst-libs/gst/vaapi/gstvaapitexture_egl.c b/gst-libs/gst/vaapi/gstvaapitexture_egl.c
index 260ffaa2..1bfff5b0 100644
--- a/gst-libs/gst/vaapi/gstvaapitexture_egl.c
+++ b/gst-libs/gst/vaapi/gstvaapitexture_egl.c
@@ -34,7 +34,7 @@
 #include "gstvaapiutils_egl.h"
 #include "gstvaapidisplay_egl.h"
 #include "gstvaapidisplay_egl_priv.h"
-#include "gstvaapisurface_egl.h"
+#include "gstvaapisurface_drm.h"
 #include "gstvaapifilter.h"
 
 #define DEBUG 1
@@ -95,6 +95,8 @@ create_objects (GstVaapiTextureEGL * texture, GLuint texture_id)
   EglContext *const ctx = texture->egl_context;
   EglVTable *const vtable = egl_context_get_vtable (ctx, FALSE);
   GLint attribs[3], *attrib;
+  gsize size, offset[GST_VIDEO_MAX_PLANES];
+  gint name, stride[GST_VIDEO_MAX_PLANES];
 
   attrib = attribs;
   *attrib++ = EGL_IMAGE_PRESERVED_KHR;
@@ -106,10 +108,39 @@ create_objects (GstVaapiTextureEGL * texture, GLuint texture_id)
   if (!texture->egl_image)
     goto error_create_image;
 
+  /*
+   * The below dozen or so lines of code replaces all of gstvaapisurface_egl.c,
+   * which didn't work (deadlocks and incorrect GL context usage) as well as
+   * being around 20x bigger than this. Maybe consider deleting
+   * gstvaapisurface_egl.c if it doesn't yet work and nothing calls it...
+   */
+  if (!vtable->has_EGL_MESA_drm_image) {
+    GST_ERROR ("missing EGL_MESA_drm_image");
+    goto error_missing_extensions;
+  }
+
+  memset (offset, 0, sizeof (offset));
+  memset (stride, 0, sizeof (stride));
+
+  if (!vtable->eglExportDRMImageMESA (ctx->display->base.handle.p,
+          texture->egl_image, &name, NULL, &stride[0]))
+    goto error_export_image_gem_buf;
+
+  size = base_texture->height * stride[0];
+
+  /*
+   * XXX The below surface creation may fail on Intel due to:
+   *   https://github.com/01org/intel-vaapi-driver/issues/222
+   * A permanent fix for that problem will be released in intel-vaapi-driver
+   * version 1.8.4 and later, and also in 1.8.3-1ubuntu1.
+   * However if you don't have that fix then a simple workaround is to
+   * uncomment this line of code:
+   *   size = GST_ROUND_UP_32 (base_texture->height) * stride[0];
+   */
   texture->surface =
-      gst_vaapi_surface_new_with_egl_image (GST_VAAPI_OBJECT_DISPLAY (texture),
-      texture->egl_image, GST_VIDEO_FORMAT_RGBA, base_texture->width,
-      base_texture->height);
+      gst_vaapi_surface_new_with_gem_buf_handle (GST_VAAPI_OBJECT_DISPLAY
+      (texture), name, size, GST_VIDEO_FORMAT_RGBA, base_texture->width,
+      base_texture->height, offset, stride);
   if (!texture->surface)
     goto error_create_surface;
 
@@ -122,12 +153,18 @@ create_objects (GstVaapiTextureEGL * texture, GLuint texture_id)
 error_create_image:
   GST_ERROR ("failed to create EGL image from 2D texture %u", texture_id);
   return FALSE;
+error_missing_extensions:
+  GST_ERROR ("missing extension: EGL_MESA_drm_image");
+  return FALSE;
 error_create_surface:
   GST_ERROR ("failed to create VA surface from 2D texture %u", texture_id);
   return FALSE;
 error_create_filter:
   GST_ERROR ("failed to create VPP filter for color conversion");
   return FALSE;
+error_export_image_gem_buf:
+  GST_ERROR ("failed to export EGL image to GEM buffer");
+  return FALSE;
 }
 
 static gboolean
@@ -247,8 +284,26 @@ gst_vaapi_texture_egl_create (GstVaapiTextureEGL * texture)
 {
   CreateTextureArgs args = { texture };
 
-  egl_object_replace (&texture->egl_context,
-      GST_VAAPI_DISPLAY_EGL_CONTEXT (GST_VAAPI_OBJECT_DISPLAY (texture)));
+  if (GST_VAAPI_TEXTURE (texture)->is_wrapped) {
+    /*
+     * We must use the context of the calling thread when wrapping a foreign
+     * texture. This is confirmed by the documentation of
+     * GstVideoGLTextureUploadMeta:
+     *  "The caller of gst_video_gl_texture_upload_meta_upload() must
+     *   have OpenGL set up and call this from a thread where it is valid
+     *   to upload something to an OpenGL texture."
+     * But it's also obvious; if the caller only provided an int texture_id
+     * then the only context in which that is valid is the calling thread.
+     */
+    EglDisplay *current_egl_display =
+        egl_display_new_wrapped (eglGetCurrentDisplay ());
+    EglContext *current_egl_context =
+        egl_context_new_wrapped (current_egl_display, eglGetCurrentContext ());
+    egl_object_replace (&texture->egl_context, current_egl_context);
+  } else {
+    egl_object_replace (&texture->egl_context,
+        GST_VAAPI_DISPLAY_EGL_CONTEXT (GST_VAAPI_OBJECT_DISPLAY (texture)));
+  }
 
   return egl_context_run (texture->egl_context,
       (EglContextRunFunc) do_create_texture, &args) && args.success;
diff --git a/gst/vaapi/gstvaapidecode.c b/gst/vaapi/gstvaapidecode.c
index 1b312598..e7dfbcba 100644
--- a/gst/vaapi/gstvaapidecode.c
+++ b/gst/vaapi/gstvaapidecode.c
@@ -223,8 +223,7 @@ gst_vaapidecode_ensure_allowed_srcpad_caps (GstVaapiDecode * decode)
     return FALSE;
   }
 #if (USE_GLX || USE_EGL)
-  if (!GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAN_DMABUF (decode) &&
-      gst_vaapi_display_has_opengl (GST_VAAPI_PLUGIN_BASE_DISPLAY (decode))) {
+  if (gst_vaapi_display_has_opengl (GST_VAAPI_PLUGIN_BASE_DISPLAY (decode))) {
     out_caps = gst_caps_make_writable (out_caps);
     gst_caps_append (out_caps,
         gst_caps_from_string (GST_VAAPI_MAKE_GLTEXUPLOAD_CAPS));
diff --git a/gst/vaapi/gstvaapipostproc.c b/gst/vaapi/gstvaapipostproc.c
index d45f7ee9..11960aa9 100644
--- a/gst/vaapi/gstvaapipostproc.c
+++ b/gst/vaapi/gstvaapipostproc.c
@@ -1069,10 +1069,9 @@ expand_allowed_srcpad_caps (GstVaapiPostproc * postproc, GstCaps * caps)
   }
   g_value_unset (&value);
 
-  if ((GST_VAAPI_PLUGIN_BASE_SRC_PAD_CAN_DMABUF (postproc)
-          || !gst_vaapi_display_has_opengl (GST_VAAPI_PLUGIN_BASE_DISPLAY
-              (postproc)))
-      && gl_upload_meta_idx > -1) {
+  if (gl_upload_meta_idx >= 0 &&
+      !gst_vaapi_display_has_opengl (GST_VAAPI_PLUGIN_BASE_DISPLAY
+          (postproc))) {
     gst_caps_remove_structure (caps, gl_upload_meta_idx);
   }
 
