Draw overlays to dma-bufs imported as EGLImage textures if supported

This significantly reduces texture upload times (typically < 1 ms).

Change-Id: I4ea6c8a5d2d5baae8d85dbd0905dc2ee57b57e48
diff --git a/plugins/gstglsvgoverlay.c b/plugins/gstglsvgoverlay.c
index 7dceb68..f7dadb6 100644
--- a/plugins/gstglsvgoverlay.c
+++ b/plugins/gstglsvgoverlay.c
@@ -20,9 +20,12 @@
 #endif
 
 #include <cairo.h>
+#include <libdrm/drm_fourcc.h>
 #include <librsvg/rsvg.h>
 
+#include <gst/allocators/gstdmabuf.h>
 #include <gst/gl/gstglfuncs.h>
+
 #include "gstglsvgoverlay.h"
 
 #define GST_CAT_DEFAULT gst_gl_svg_overlay_debug
@@ -35,15 +38,26 @@
 G_DEFINE_TYPE_WITH_CODE (GstGLSvgOverlay, gst_gl_svg_overlay,
     GST_TYPE_GL_FILTER, DEBUG_INIT);
 
+#define USING_OPENGL3(context) (gst_gl_context_check_gl_version(context, \
+    GST_GL_API_OPENGL3, 3, 1))
+#define USING_GLES2(context) (gst_gl_context_check_gl_version (context, \
+    GST_GL_API_GLES2, 2, 0))
+#define USING_GLES3(context) (gst_gl_context_check_gl_version (context, \
+  GST_GL_API_GLES2, 3, 0))
+
 enum
 {
   PROP_0,
   PROP_DATA,
   PROP_SVG,
   PROP_SYNC,
+  PROP_ION,
 };
 
 #define DEFAULT_PROP_SYNC TRUE
+#define DEFAULT_PROP_ION TRUE
+
+#define QNAME "GstGLSvgOverlayImage"
 
 static void gst_gl_svg_overlay_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
@@ -97,6 +111,11 @@
           "available.",
           DEFAULT_PROP_SYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ION,
+      g_param_spec_boolean ("ion", "ion",
+          "Use the ION allocator for overlays if available.",
+          DEFAULT_PROP_ION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gst_element_class_set_metadata (GST_ELEMENT_CLASS (klass),
       "OpenGL SVG overlay",
       "Filter/Effect/Video",
@@ -120,7 +139,7 @@
   GST_GL_BASE_FILTER_CLASS (klass)->gl_set_caps =
       gst_gl_svg_overlay_gl_set_caps;
   GST_GL_BASE_FILTER_CLASS (klass)->supported_gl_api =
-      GST_GL_API_OPENGL | GST_GL_API_OPENGL3 | GST_GL_API_GLES2;
+      GST_GL_API_GLES2 | GST_GL_API_OPENGL3;
   GST_GL_FILTER_CLASS (klass)->filter_texture =
       gst_gl_svg_overlay_filter_texture;
 }
@@ -130,16 +149,27 @@
 {
   GST_DEBUG_OBJECT (overlay, "init");
   overlay->sync = DEFAULT_PROP_SYNC;
+  overlay->use_ion = DEFAULT_PROP_ION;
   overlay->num_tasks = 0;
   overlay->started = FALSE;
   overlay->shader = NULL;
   overlay->thread_pool = NULL;
+  overlay->buffer_pool = NULL;
   overlay->svg_queue = NULL;
   overlay->gl_queue = NULL;
   overlay->next_pts = 0;
   overlay->current = NULL;
   g_mutex_init (&overlay->mutex);
   g_cond_init (&overlay->cond);
+
+  /* ION allocator is in the "coral" plugin, load it. */
+  gst_object_unref (gst_plugin_load_by_name ("coral"));
+  overlay->ion_allocator = gst_allocator_find ("ion");
+  if (overlay->ion_allocator) {
+    GST_INFO_OBJECT (overlay, "Have ION allocator");
+  } else {
+    GST_INFO_OBJECT (overlay, "ION allocator not present/usable");
+  }
 }
 
 static void
@@ -149,6 +179,16 @@
 
   GST_DEBUG_OBJECT (overlay, "finalize");
 
+  if (overlay->ion_allocator) {
+    gst_object_unref (overlay->ion_allocator);
+    overlay->ion_allocator = NULL;
+  }
+  if (overlay->buffer_pool) {
+    gst_buffer_pool_set_active (overlay->buffer_pool, FALSE);
+    gst_object_unref (overlay->buffer_pool);
+    overlay->buffer_pool = NULL;
+  }
+
   g_mutex_clear (&overlay->mutex);
   g_cond_clear (&overlay->cond);
 
@@ -172,6 +212,9 @@
       overlay->sync = g_value_get_boolean (value);
       g_mutex_unlock (&overlay->mutex);
       break;
+    case PROP_ION:
+      overlay->use_ion = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -189,13 +232,169 @@
       g_mutex_lock (&overlay->mutex);
       g_value_set_boolean (value, overlay->sync);
       g_mutex_unlock (&overlay->mutex);
-    break;
+      break;
+    case PROP_ION:
+      g_value_set_boolean (value, overlay->use_ion);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
 }
 
+static void
+_free_egl_image_texture_gl (GstGLContext * context, gpointer data)
+{
+  EglImageTexture *tex = (EglImageTexture *) data;
+  const GstGLFuncs *gl = context->gl_vtable;
+
+  if (!tex || !tex->tex) {
+    return;
+  }
+
+  gl->DeleteTextures (1, &tex->tex);
+  tex->tex = 0;
+}
+
+static void
+_free_egl_image_texture (gpointer data)
+{
+  EglImageTexture *tex = (EglImageTexture *) data;
+  EGLBoolean (*_eglDestroyImage) (EGLDisplay dpy, EGLImageKHR image);
+  GstGLDisplayEGL *gst_egl_display;
+  EGLDisplay egl_display;
+  GstGLSvgOverlay *overlay = tex->overlay;
+  GstGLContext *context = overlay->context;
+
+  gst_gl_context_thread_add (context, _free_egl_image_texture_gl, tex);
+
+  gst_egl_display = gst_gl_display_egl_from_gl_display (context->display);
+  egl_display = (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (
+      gst_egl_display));
+  gst_object_unref (gst_egl_display);
+
+  _eglDestroyImage = gst_gl_context_get_proc_address (context,
+      "eglDestroyImage");
+  if (!_eglDestroyImage) {
+    _eglDestroyImage = gst_gl_context_get_proc_address (context,
+        "eglDestroyImageKHR");
+    if (!_eglDestroyImage) {
+      g_free (tex);
+      GST_ERROR_OBJECT (overlay, "eglDestroyImage not exposed");
+      return;
+    }
+  }
+
+  if (!_eglDestroyImage (egl_display, tex->image)) {
+    GST_ERROR_OBJECT (overlay, "free %p failed", tex);
+  }
+
+  g_free (tex);
+}
+
+static EglImageTexture *
+_get_cached_egl_image_texture (GstGLSvgOverlay * overlay, GstBuffer * buf) {
+  EglImageTexture *tex = (EglImageTexture *) gst_mini_object_get_qdata (
+      GST_MINI_OBJECT (buf), g_quark_from_static_string (QNAME));
+  return tex;
+}
+
+static void
+_create_egl_image_texture_gl (GstGLContext * context, gpointer data)
+{
+  EglImageTexture *tex = (EglImageTexture *) data;
+  GstGLSvgOverlay *overlay = tex->overlay;
+  const GstGLFuncs *gl = context->gl_vtable;
+
+  g_assert (!tex->tex);
+  gl->GenTextures (1, &tex->tex);
+  gl->BindTexture (GL_TEXTURE_2D, tex->tex);
+  gl->TexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  gl->TexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+  gl->EGLImageTargetTexture2D (GL_TEXTURE_2D, tex->image);
+}
+
+static EglImageTexture *
+_create_egl_image_texture (GstGLSvgOverlay * overlay, GstBuffer * buf) {
+  /* EglImageTexture is cached in GstBuffer qdata. */
+  EglImageTexture *tex;
+  EGLImageKHR *image;
+  EGLDisplay egl_display;
+  GstGLDisplayEGL *gst_egl_display;
+  GstMemory *mem;
+  gint atti = 0, fourcc, i, fd;
+  guintptr attribs[16];
+  EGLImageKHR (*_eglCreateImage) (EGLDisplay dpy, EGLContext ctx,
+      EGLenum target, EGLClientBuffer buffer, const EGLint * attribs);
+  GstGLContext *context = overlay->context;
+
+  tex = _get_cached_egl_image_texture (overlay, buf);
+  if (tex) {
+    return tex;
+  }
+
+  mem = gst_buffer_peek_memory (buf, 0);
+  g_assert (gst_is_dmabuf_memory (mem));
+  fd = gst_dmabuf_memory_get_fd (mem);
+
+  gst_egl_display = gst_gl_display_egl_from_gl_display (context->display);
+  egl_display = (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (
+      gst_egl_display));
+  gst_object_unref (gst_egl_display);
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+  fourcc = DRM_FORMAT_ARGB8888;
+#else
+  fourcc = DRM_FORMAT_ABGR8888;
+#endif
+
+  _eglCreateImage = gst_gl_context_get_proc_address (context,
+      "eglCreateImage");
+  if (!_eglCreateImage) {
+    _eglCreateImage = gst_gl_context_get_proc_address (context,
+        "eglCreateImageKHR");
+  }
+  g_assert (_eglCreateImage);
+
+  GST_DEBUG_OBJECT (overlay, "create image fd=%d w=%d h=%d s=%d", fd,
+      GST_VIDEO_INFO_WIDTH (&overlay->info),
+      GST_VIDEO_INFO_HEIGHT (&overlay->info),
+      GST_VIDEO_INFO_PLANE_STRIDE (&overlay->info, 0));
+
+  attribs[atti++] = EGL_WIDTH;
+  attribs[atti++] = GST_VIDEO_INFO_WIDTH (&overlay->info);
+  attribs[atti++] = EGL_HEIGHT;
+  attribs[atti++] = GST_VIDEO_INFO_HEIGHT (&overlay->info);
+  attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
+  attribs[atti++] = fourcc;
+  attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
+  attribs[atti++] = fd;
+  attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
+  attribs[atti++] = 0;
+  attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
+  attribs[atti++] = GST_VIDEO_INFO_PLANE_STRIDE (&overlay->info, 0);
+  attribs[atti] = EGL_NONE;
+
+  image = _eglCreateImage (egl_display, EGL_NO_CONTEXT,
+      EGL_LINUX_DMA_BUF_EXT, NULL, (const EGLint *) attribs);
+  GST_DEBUG_OBJECT (overlay, "eglCreateImage %p", image);
+  g_assert (image != EGL_NO_IMAGE_KHR);
+
+  tex = g_malloc0 (sizeof (EglImageTexture));
+  tex->overlay = overlay;
+  tex->image = image;
+
+  GST_DEBUG_OBJECT (overlay, "set buf %p tex %p", buf, tex);
+  gst_mini_object_set_qdata (GST_MINI_OBJECT (buf),
+      g_quark_from_static_string (QNAME), tex, _free_egl_image_texture);
+
+  gst_gl_context_thread_add (context, _create_egl_image_texture_gl, tex);
+
+  return tex;
+}
+
 static gint _task_data_compare (gconstpointer a, gconstpointer b,
     gpointer user_data)
 {
@@ -258,9 +457,10 @@
   }
 
   _task_data_unmap (tdata);
-  gst_buffer_replace (&tdata->buf, NULL);
   gst_gl_context_thread_add (tdata->overlay->context, _task_data_free_gl,
       tdata);
+
+  gst_buffer_replace (&tdata->buf, NULL);
   g_free (tdata->svg);
   g_free (tdata);
 }
@@ -270,16 +470,20 @@
 {
   TaskData * tdata = data;
   GstGLSvgOverlay *overlay = tdata->overlay;
-  GstGLFilter *filter = GST_GL_FILTER (overlay);
   const GstGLFuncs *gl = context->gl_vtable;
 
   /* TODO: handle non packed stride */
   /* TODO: TexImage2D in a separate derived gl context */
 
+  g_assert (!tdata->egl_tex);
+
+  /* Copy pixels to GPU. */
   gl->GenTextures (1, &tdata->tex);
   gl->BindTexture (GL_TEXTURE_2D, tdata->tex);
-  gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, overlay->width, overlay->height, 0,
-      GL_RGBA, GL_UNSIGNED_BYTE, tdata->map.data);
+  gl->TexImage2D(GL_TEXTURE_2D, 0, overlay->internal_format,
+      GST_VIDEO_INFO_WIDTH (&overlay->info),
+      GST_VIDEO_INFO_HEIGHT (&overlay->info),
+      0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, tdata->map.data);
   gl->GenerateMipmap (GL_TEXTURE_2D);
 }
 
@@ -298,12 +502,11 @@
 
   switch (transition) {
     case GST_STATE_CHANGE_READY_TO_PAUSED:
-    g_mutex_lock (&overlay->mutex);
+      g_mutex_lock (&overlay->mutex);
       overlay->svg_queue = g_queue_new ();
       overlay->gl_queue = g_queue_new ();
       overlay->thread_pool = g_thread_pool_new (gst_gl_svg_overlay_task,
           overlay, g_get_num_processors (), FALSE, NULL);
-      overlay->started = TRUE;
       g_mutex_unlock (&overlay->mutex);
       break;
     case GST_STATE_CHANGE_PAUSED_TO_READY:
@@ -321,6 +524,11 @@
       overlay->gl_queue = NULL;
       _task_data_free (overlay->current);
       overlay->current = NULL;
+      if (overlay->buffer_pool) {
+        gst_buffer_pool_set_active (overlay->buffer_pool, FALSE);
+        gst_object_unref (overlay->buffer_pool);
+        overlay->buffer_pool = NULL;
+      }
       g_mutex_unlock (&overlay->mutex);
       break;
     default:
@@ -347,6 +555,30 @@
   filter->draw_attr_texture_loc =
       gst_gl_shader_get_attribute_location (overlay->shader, "a_texcoord");
 
+  if (USING_GLES2 (overlay->context) || USING_GLES3 (overlay->context)) {
+    gboolean bgra = gst_gl_context_check_feature (overlay->context,
+        "GL_EXT_texture_format_BGRA8888");
+    if (bgra) {
+      GST_DEBUG_OBJECT (overlay, "GL_EXT_texture_format_BGRA8888 supported");
+    } else {
+      GST_ERROR_OBJECT (overlay, "GL_EXT_texture_format_BGRA8888 unsupported");
+      return FALSE;
+    }
+
+    overlay->internal_format = GL_BGRA_EXT;
+  } else if (USING_OPENGL3 (overlay->context)) {
+    /* BGRA8888 supported */
+    overlay->internal_format = GL_RGBA;
+  } else {
+    g_assert_not_reached ();
+  }
+
+  if (overlay->use_ion && overlay->ion_allocator) {
+    /* TODO: Init check the dma-buf import being supported. */
+  } else {
+    overlay->use_ion = FALSE;
+  }
+
   return GST_GL_BASE_FILTER_CLASS (parent_class)->gl_start (base_filter);
 }
 
@@ -376,24 +608,69 @@
 {
   GstGLFilter *filter = GST_GL_FILTER (base_filter);
   GstGLSvgOverlay *overlay = GST_GL_SVG_OVERLAY (base_filter);
+  GstStructure *config;
+  GstCaps *caps;
+  gsize size;
   gboolean ret;
 
   ret = GST_GL_BASE_FILTER_CLASS (parent_class)->gl_set_caps (base_filter,
       incaps, outcaps);
 
-  overlay->width = GST_VIDEO_INFO_WIDTH (&filter->out_info);
-  overlay->height = GST_VIDEO_INFO_HEIGHT (&filter->out_info);
-  overlay->stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32,
-      overlay->width);
+  if (!ret) {
+    GST_ERROR_OBJECT (filter, "GstGLBaseFilter failed to start");
+    return ret;
+  }
 
-  GST_DEBUG_OBJECT (filter, "%ux%u px, stride %u bytes", overlay->width,
-      overlay->height, overlay->stride);
+  caps = gst_caps_new_simple("video/x-raw",
+      "format", G_TYPE_STRING, "BGRA",
+      "width", G_TYPE_INT, GST_VIDEO_INFO_WIDTH (&filter->out_info),
+      "height", G_TYPE_INT, GST_VIDEO_INFO_HEIGHT (&filter->out_info),
+      "framerate", GST_TYPE_FRACTION, 0, 1,
+      NULL);
+  gst_video_info_from_caps (&overlay->info, caps);
+  GST_VIDEO_INFO_PLANE_STRIDE (&overlay->info, 0) =
+      cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32,
+          GST_VIDEO_INFO_WIDTH (&overlay->info));
 
-  if (overlay->stride != overlay->width * 4) {
+  if (GST_VIDEO_INFO_PLANE_STRIDE (&overlay->info, 0) != GST_VIDEO_INFO_WIDTH (
+      &overlay->info) * 4) {
     /* TODO: stride */
     GST_ERROR_OBJECT (overlay, "Unsupported stride");
+    gst_caps_unref (caps);
     return FALSE;
   }
+  size = GST_VIDEO_INFO_PLANE_STRIDE (&overlay->info, 0) *
+      GST_VIDEO_INFO_HEIGHT (&overlay->info);
+
+  g_mutex_lock (&overlay->mutex);
+
+  if (overlay->buffer_pool) {
+    gst_buffer_pool_set_active (overlay->buffer_pool, FALSE);
+    gst_object_unref (overlay->buffer_pool);
+  }
+  GST_INFO_OBJECT (overlay, "New pool with max %u buffers ion=%d",
+      g_get_num_processors (), overlay->use_ion);
+  overlay->buffer_pool = gst_buffer_pool_new ();
+  config = gst_buffer_pool_get_config (overlay->buffer_pool);
+  gst_buffer_pool_config_set_params (config, caps, size, 0,
+      g_get_num_processors ());
+  if (overlay->ion_allocator && overlay->use_ion) {
+    gst_buffer_pool_config_set_allocator (config, overlay->ion_allocator, NULL);
+  }
+  ret = gst_buffer_pool_set_config (overlay->buffer_pool, config);
+  g_assert (ret);
+  ret = gst_buffer_pool_set_active (overlay->buffer_pool, TRUE);
+  g_assert (ret);
+  gst_caps_unref (caps);
+
+  overlay->started = TRUE;
+
+  g_mutex_unlock (&overlay->mutex);
+
+  GST_DEBUG_OBJECT (filter, "%ux%u px, stride %u bytes",
+      GST_VIDEO_INFO_WIDTH (&overlay->info),
+      GST_VIDEO_INFO_HEIGHT (&overlay->info),
+      GST_VIDEO_INFO_PLANE_STRIDE (&overlay->info, 0));
 
   return ret;
 }
@@ -451,12 +728,19 @@
   tdata = data;
 
   if (tdata->op == OP_ALLOC) {
-    /* TODO: Buffer pool. */
-    const gsize size = GST_VIDEO_INFO_WIDTH (&filter->out_info) *
-        GST_VIDEO_INFO_HEIGHT (&filter->out_info) * 4; /* BGRA 4 bpp. */
-    tdata->buf = gst_buffer_new_allocate (NULL, size, NULL);
+    GstBufferPool *pool;
+    GstFlowReturn ret;
+
+    g_mutex_lock (&overlay->mutex);
+    pool = overlay->buffer_pool;
+    g_mutex_unlock (&overlay->mutex);
+
+    g_assert (gst_buffer_pool_is_active (pool));
+
+    ret = gst_buffer_pool_acquire_buffer (pool, &tdata->buf, NULL);
+    g_assert (ret == GST_FLOW_OK);
+
     tdata->op = OP_DRAW;
-    LOG_ELAPSED ("alloc");
     TASK_DONE(tdata);
   }
 
@@ -471,7 +755,9 @@
 
     memset (tdata->map.data, 0, tdata->map.size);
     surface = cairo_image_surface_create_for_data (tdata->map.data,
-        CAIRO_FORMAT_ARGB32, overlay->width, overlay->height, overlay->stride);
+        CAIRO_FORMAT_ARGB32, GST_VIDEO_INFO_WIDTH (&overlay->info),
+        GST_VIDEO_INFO_HEIGHT (&overlay->info),
+        GST_VIDEO_INFO_PLANE_STRIDE (&overlay->info, 0));
     g_assert (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS);
     cairo = cairo_create (surface);
     g_assert (cairo_status (cairo) == CAIRO_STATUS_SUCCESS);
@@ -497,10 +783,22 @@
   }
 
   if (tdata->op == OP_UPLOAD) {
-    gst_gl_context_thread_add (tdata->overlay->context, _task_data_upload_gl,
-        tdata);
-    _task_data_unmap (tdata);
-    gst_buffer_replace (&tdata->buf, NULL);
+    gboolean dma = overlay->use_ion && gst_is_dmabuf_memory (
+        gst_buffer_peek_memory (tdata->buf, 0));
+    if (dma) {
+      /* Unmap to flush CPU cache. */
+      _task_data_unmap (tdata);
+      g_assert (!tdata->egl_tex);
+      tdata->egl_tex = _create_egl_image_texture (overlay, tdata->buf);
+      g_assert (tdata->egl_tex);
+    } else {
+      /* Copy pixels to GPU, unmap and free buffer. */
+      gst_gl_context_thread_add (tdata->overlay->context, _task_data_upload_gl,
+          tdata);
+      _task_data_unmap (tdata);
+      gst_buffer_replace (&tdata->buf, NULL);
+    }
+
     tdata->op = OP_READY;
 
     g_mutex_lock (&overlay->mutex);
@@ -573,10 +871,10 @@
   if (!overlay->started) {
     GST_WARNING_OBJECT (overlay, "Wrong state, dropping SVG overlay");
     g_mutex_unlock (&overlay->mutex);
-
+    return FALSE;
   }
 
-  tdata = g_malloc0 (sizeof(TaskData));
+  tdata = g_malloc0 (sizeof (TaskData));
   tdata->overlay = overlay;
   tdata->op = OP_ALLOC;
   tdata->svg = g_strdup (svg);
@@ -648,7 +946,11 @@
 
   tex_id = gst_gl_memory_get_texture_id (in_tex);
   if (overlay->current) {
-    o_tex_id = overlay->current->tex;
+    if (overlay->current->egl_tex) {
+      o_tex_id = overlay->current->egl_tex->tex;
+    } else {
+      o_tex_id = overlay->current->tex;
+    }
   }
 
   g_return_val_if_fail (overlay->shader, FALSE);
diff --git a/plugins/gstglsvgoverlay.h b/plugins/gstglsvgoverlay.h
index 796107b..5e71d49 100644
--- a/plugins/gstglsvgoverlay.h
+++ b/plugins/gstglsvgoverlay.h
@@ -19,6 +19,8 @@
 
 #include <gst/gst.h>
 
+#include <gst/gl/egl/gstegl.h>
+#include "gst/gl/egl/gstgldisplay_egl.h"
 #include <gst/gl/gstglfilter.h>
 
 G_BEGIN_DECLS
@@ -33,6 +35,7 @@
 typedef struct _GstGLSvgOverlay GstGLSvgOverlay;
 typedef struct _GstGLSvgOverlayClass GstGLSvgOverlayClass;
 typedef struct _TaskData TaskData;
+typedef struct _EglImageTexture EglImageTexture;
 
 struct _GstGLSvgOverlay
 {
@@ -43,6 +46,9 @@
   GCond cond;
   GstGLShader *shader;
   GThreadPool *thread_pool;
+  GstBufferPool *buffer_pool;
+  GstAllocator *ion_allocator;
+  gboolean use_ion;
   GQueue *svg_queue;
   GQueue *gl_queue;
   GstClockTime next_pts;
@@ -50,9 +56,8 @@
   TaskData *current;
   gboolean sync;
   gboolean started;
-  guint width;
-  guint height;
-  guint stride;
+  gint internal_format;
+  GstVideoInfo info;
 };
 
 struct _GstGLSvgOverlayClass
@@ -69,6 +74,12 @@
   OP_READY,
 };
 
+struct _EglImageTexture {
+  GstGLSvgOverlay *overlay;
+  EGLImageKHR *image;
+  guint tex;
+};
+
 struct _TaskData {
   GstGLSvgOverlay *overlay;
   enum TaskOp op;
@@ -76,6 +87,7 @@
   GstClockTime pts;
   GstBuffer *buf;
   GstMapInfo map;
+  EglImageTexture *egl_tex;
   guint tex;
 };