diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c
index 3c553e9a69..ad8df916e1 100644
--- a/gdk/gdkdisplay.c
+++ b/gdk/gdkdisplay.c
@@ -391,6 +391,9 @@ gdk_display_dispose (GObject *object)
g_queue_clear (&display->queued_events);
+ g_clear_object (&display->egl_gsk_renderer);
+ g_clear_pointer (&display->egl_external_formats, gdk_dmabuf_formats_unref);
+
g_clear_object (&priv->gl_context);
#ifdef HAVE_EGL
g_clear_pointer (&priv->egl_display, eglTerminate);
@@ -1887,11 +1890,16 @@ gdk_display_init_dmabuf (GdkDisplay *self)
gdk_display_prepare_gl (self, NULL);
gdk_display_add_dmabuf_downloader (self, gdk_dmabuf_get_direct_downloader (), builder);
+
+#ifdef HAVE_EGL
+ if (gdk_display_prepare_gl (self, NULL))
+ gdk_display_add_dmabuf_downloader (self, gdk_dmabuf_get_egl_downloader (), builder);
+#endif
}
#endif
self->dmabuf_formats = gdk_dmabuf_formats_builder_free_to_formats (builder);
-}
+}
/**
* gdk_display_get_dmabuf_formats:
diff --git a/gdk/gdkdisplayprivate.h b/gdk/gdkdisplayprivate.h
index 1d3aca89ff..bce2867999 100644
--- a/gdk/gdkdisplayprivate.h
+++ b/gdk/gdkdisplayprivate.h
@@ -119,6 +119,10 @@ struct _GdkDisplay
GdkDmabufFormats *dmabuf_formats;
const GdkDmabufDownloader *dmabuf_downloaders[4];
+
+ /* Cached data the EGL dmabuf downloader */
+ gpointer egl_gsk_renderer;
+ GdkDmabufFormats *egl_external_formats;
};
struct _GdkDisplayClass
diff --git a/gdk/gdkdmabufegl.c b/gdk/gdkdmabufegl.c
new file mode 100644
index 0000000000..9be8668232
--- /dev/null
+++ b/gdk/gdkdmabufegl.c
@@ -0,0 +1,405 @@
+/* gdkdmabufegl.c
+ *
+ * Copyright 2023 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ */
+
+#include "config.h"
+
+#if defined(HAVE_DMABUF) && defined (HAVE_EGL)
+#include "gdkdmabufprivate.h"
+
+#include "gdkdmabufformatsbuilderprivate.h"
+#include "gdkdebugprivate.h"
+#include "gdkdmabuftextureprivate.h"
+#include "gdkmemoryformatprivate.h"
+#include "gdkdisplayprivate.h"
+#include "gdkglcontextprivate.h"
+#include "gdktexturedownloader.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* A dmabuf downloader implementation that downloads buffers via
+ * gsk_renderer_render_texture + GL texture download.
+ */
+
+static gboolean
+gdk_dmabuf_egl_downloader_add_formats (const GdkDmabufDownloader *downloader,
+ GdkDisplay *display,
+ GdkDmabufFormatsBuilder *builder)
+{
+ GdkGLContext *context = gdk_display_get_gl_context (display);
+ EGLDisplay egl_display = gdk_display_get_egl_display (display);
+ GdkDmabufFormatsBuilder *external;
+ gboolean retval = FALSE;
+
+ g_assert (display->egl_external_formats == NULL);
+
+ external = gdk_dmabuf_formats_builder_new ();
+
+ gdk_gl_context_make_current (context);
+
+ if (egl_display != EGL_NO_DISPLAY &&
+ display->have_egl_dma_buf_import &&
+ gdk_gl_context_has_image_storage (context))
+ {
+ int num_fourccs;
+ int *fourccs;
+ guint64 *modifiers;
+ unsigned int *external_only;
+ int n_mods;
+
+ eglQueryDmaBufFormatsEXT (egl_display, 0, NULL, &num_fourccs);
+ fourccs = g_new (int, num_fourccs);
+ eglQueryDmaBufFormatsEXT (egl_display, num_fourccs, fourccs, &num_fourccs);
+
+ n_mods = 80;
+ modifiers = g_new (guint64, n_mods);
+ external_only = g_new (unsigned int, n_mods);
+
+ for (int i = 0; i < num_fourccs; i++)
+ {
+ int num_modifiers;
+
+ eglQueryDmaBufModifiersEXT (egl_display,
+ fourccs[i],
+ 0,
+ NULL,
+ NULL,
+ &num_modifiers);
+
+ if (num_modifiers > n_mods)
+ {
+ n_mods = num_modifiers;
+ modifiers = g_renew (guint64, modifiers, n_mods);
+ external_only = g_renew (unsigned int, external_only, n_mods);
+ }
+
+ eglQueryDmaBufModifiersEXT (egl_display,
+ fourccs[i],
+ num_modifiers,
+ modifiers,
+ external_only,
+ &num_modifiers);
+
+ for (int j = 0; j < num_modifiers; j++)
+ {
+ GDK_DEBUG (DMABUF, "supported %sEGL dmabuf format %.4s:%#" G_GINT64_MODIFIER "x",
+ external_only[j] ? "external " : "",
+ (char *) &fourccs[i],
+ modifiers[j]);
+
+ /* All linear formats we support are already added my the mmap downloader */
+ if (modifiers[j] != DRM_FORMAT_MOD_LINEAR)
+ gdk_dmabuf_formats_builder_add_format (builder, fourccs[i], modifiers[j]);
+ if (external_only[j])
+ gdk_dmabuf_formats_builder_add_format (external, fourccs[i], modifiers[j]);
+ }
+ }
+
+ g_free (modifiers);
+ g_free (external_only);
+ g_free (fourccs);
+
+ retval = TRUE;
+ }
+
+ display->egl_external_formats = gdk_dmabuf_formats_builder_free_to_formats (external);
+
+ return retval;
+}
+
+static GdkMemoryFormat
+get_memory_format (guint32 fourcc,
+ gboolean premultiplied)
+{
+ switch (fourcc)
+ {
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_XRGB8888_A8:
+ case DRM_FORMAT_XBGR8888_A8:
+ return premultiplied ? GDK_MEMORY_A8R8G8B8_PREMULTIPLIED : GDK_MEMORY_A8R8G8B8;
+
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_RGBX8888_A8:
+ return premultiplied ? GDK_MEMORY_R8G8B8A8_PREMULTIPLIED : GDK_MEMORY_R8G8B8A8;
+
+ case DRM_FORMAT_BGRA8888:
+ return premultiplied ? GDK_MEMORY_B8G8R8A8_PREMULTIPLIED : GDK_MEMORY_B8G8R8A8;
+
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ return GDK_MEMORY_R8G8B8;
+
+ case DRM_FORMAT_BGR888:
+ return GDK_MEMORY_B8G8R8;
+
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_XBGR2101010:
+ case DRM_FORMAT_RGBX1010102:
+ case DRM_FORMAT_BGRX1010102:
+ case DRM_FORMAT_XRGB16161616:
+ case DRM_FORMAT_XBGR16161616:
+ return GDK_MEMORY_R16G16B16;
+
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_ABGR2101010:
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_BGRA1010102:
+ case DRM_FORMAT_ARGB16161616:
+ case DRM_FORMAT_ABGR16161616:
+ return premultiplied ? GDK_MEMORY_R16G16B16A16_PREMULTIPLIED : GDK_MEMORY_R16G16B16A16;
+
+ case DRM_FORMAT_ARGB16161616F:
+ case DRM_FORMAT_ABGR16161616F:
+ return premultiplied ? GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED : GDK_MEMORY_R16G16B16A16_FLOAT;
+
+ case DRM_FORMAT_XRGB16161616F:
+ case DRM_FORMAT_XBGR16161616F:
+ return GDK_MEMORY_R16G16B16_FLOAT;
+
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_XYUV8888:
+ case DRM_FORMAT_XVUY8888:
+ case DRM_FORMAT_VUY888:
+ return GDK_MEMORY_R8G8B8;
+
+ /* Add more formats here */
+ default:
+ return premultiplied ? GDK_MEMORY_A8R8G8B8_PREMULTIPLIED : GDK_MEMORY_A8R8G8B8;
+ }
+}
+
+static gboolean
+gdk_dmabuf_egl_downloader_supports (const GdkDmabufDownloader *downloader,
+ GdkDisplay *display,
+ const GdkDmabuf *dmabuf,
+ gboolean premultiplied,
+ GdkMemoryFormat *out_format,
+ GError **error)
+{
+ EGLDisplay egl_display;
+ GdkGLContext *context;
+ int num_modifiers;
+ guint64 *modifiers;
+ unsigned int *external_only;
+
+ egl_display = gdk_display_get_egl_display (display);
+ if (egl_display == EGL_NO_DISPLAY)
+ {
+ g_set_error_literal (error,
+ GDK_DMABUF_ERROR, GDK_DMABUF_ERROR_UNSUPPORTED_FORMAT,
+ "EGL not available");
+ return FALSE;
+ }
+
+ context = gdk_display_get_gl_context (display);
+
+ gdk_gl_context_make_current (context);
+
+ eglQueryDmaBufModifiersEXT (egl_display,
+ dmabuf->fourcc,
+ 0,
+ NULL,
+ NULL,
+ &num_modifiers);
+
+ modifiers = g_newa (uint64_t, num_modifiers);
+ external_only = g_newa (unsigned int, num_modifiers);
+
+ eglQueryDmaBufModifiersEXT (egl_display,
+ dmabuf->fourcc,
+ num_modifiers,
+ modifiers,
+ external_only,
+ &num_modifiers);
+
+ for (int i = 0; i < num_modifiers; i++)
+ {
+ if (modifiers[i] == dmabuf->modifier)
+ {
+ *out_format = get_memory_format (dmabuf->fourcc, premultiplied);
+ return TRUE;
+ }
+ }
+
+ g_set_error (error,
+ GDK_DMABUF_ERROR, GDK_DMABUF_ERROR_UNSUPPORTED_FORMAT,
+ "Unsupported dmabuf format: %.4s:%#" G_GINT64_MODIFIER "x",
+ (char *) &dmabuf->fourcc, dmabuf->modifier);
+
+ return FALSE;
+}
+
+/* Hack. We don't include gsk/gsk.h here to avoid a build order problem
+ * with the generated header gskenumtypes.h, so we need to hack around
+ * a bit to access the gsk api we need.
+ */
+
+typedef gpointer GskRenderer;
+
+extern GskRenderer * gsk_gl_renderer_new (void);
+extern gboolean gsk_renderer_realize (GskRenderer *renderer,
+ GdkSurface *surface,
+ GError **error);
+extern GdkTexture * gsk_renderer_convert_texture (GskRenderer *renderer,
+ GdkTexture *texture);
+
+typedef void (* InvokeFunc) (gpointer data);
+
+typedef struct _InvokeData
+{
+ volatile int spinlock;
+ InvokeFunc func;
+ gpointer data;
+} InvokeData;
+
+static gboolean
+gdk_dmabuf_egl_downloader_invoke_callback (gpointer data)
+{
+ InvokeData *invoke = data;
+ GdkGLContext *previous;
+
+ previous = gdk_gl_context_get_current ();
+
+ invoke->func (invoke->data);
+
+ if (previous)
+ gdk_gl_context_make_current (previous);
+ else
+ gdk_gl_context_clear_current ();
+
+ g_atomic_int_set (&invoke->spinlock, 1);
+
+ return FALSE;
+}
+
+/* Run func in the main thread, taking care not to disturb
+ * the current GL context of the caller.
+ */
+static void
+gdk_dmabuf_egl_downloader_run (InvokeFunc func,
+ gpointer data)
+{
+ InvokeData invoke = { 0, func, data };
+
+ g_main_context_invoke (NULL, gdk_dmabuf_egl_downloader_invoke_callback, &invoke);
+
+ while (g_atomic_int_get (&invoke.spinlock) == 0) ;
+}
+
+typedef struct _Download Download;
+
+struct _Download
+{
+ GdkDmabufTexture *texture;
+ GdkMemoryFormat format;
+ guchar *data;
+ gsize stride;
+};
+
+static GskRenderer *
+get_gsk_renderer (GdkDisplay *display)
+{
+ if (!display->egl_gsk_renderer)
+ {
+ GskRenderer *renderer;
+ GError *error = NULL;
+
+ renderer = gsk_gl_renderer_new ();
+
+ if (!gsk_renderer_realize (renderer, NULL, &error))
+ {
+ g_warning ("Failed to realize GL renderer: %s", error->message);
+ g_error_free (error);
+ g_object_unref (renderer);
+
+ return NULL;
+ }
+
+ display->egl_gsk_renderer = renderer;
+ }
+
+ return display->egl_gsk_renderer;
+}
+
+static void
+gdk_dmabuf_egl_downloader_do_download (gpointer data)
+{
+ Download *download = data;
+ GdkDisplay *display;
+ GskRenderer *renderer;
+ GdkTexture *native;
+ GdkTextureDownloader *downloader;
+
+ display = gdk_dmabuf_texture_get_display (download->texture);
+
+ renderer = get_gsk_renderer (display);
+
+ native = gsk_renderer_convert_texture (renderer, GDK_TEXTURE (download->texture));
+
+ downloader = gdk_texture_downloader_new (native);
+ gdk_texture_downloader_set_format (downloader, download->format);
+ gdk_texture_downloader_download_into (downloader, download->data, download->stride);
+ gdk_texture_downloader_free (downloader);
+
+ g_object_unref (native);
+}
+
+static void
+gdk_dmabuf_egl_downloader_download (const GdkDmabufDownloader *downloader,
+ GdkTexture *texture,
+ GdkMemoryFormat format,
+ guchar *data,
+ gsize stride)
+{
+ Download download;
+
+ GDK_DEBUG (DMABUF, "Using %s for downloading a dmabuf", downloader->name);
+
+ download.texture = GDK_DMABUF_TEXTURE (texture);
+ download.format = format;
+ download.data = data;
+ download.stride = stride;
+
+ gdk_dmabuf_egl_downloader_run (gdk_dmabuf_egl_downloader_do_download, &download);
+}
+
+const GdkDmabufDownloader *
+gdk_dmabuf_get_egl_downloader (void)
+{
+ static const GdkDmabufDownloader downloader = {
+ "egl",
+ gdk_dmabuf_egl_downloader_add_formats,
+ gdk_dmabuf_egl_downloader_supports,
+ gdk_dmabuf_egl_downloader_download,
+ };
+
+ return &downloader;
+}
+
+#endif /* HAVE_DMABUF && HAVE_EGL */
diff --git a/gdk/gdkdmabufprivate.h b/gdk/gdkdmabufprivate.h
index 33ff1398c5..0c1e280e65 100644
--- a/gdk/gdkdmabufprivate.h
+++ b/gdk/gdkdmabufprivate.h
@@ -25,7 +25,6 @@ struct _GdkDmabufDownloader
gboolean (* add_formats) (const GdkDmabufDownloader *downloader,
GdkDisplay *display,
GdkDmabufFormatsBuilder *builder);
-
gboolean (* supports) (const GdkDmabufDownloader *downloader,
GdkDisplay *display,
const GdkDmabuf *dmabuf,
@@ -40,14 +39,16 @@ struct _GdkDmabufDownloader
};
#ifdef HAVE_DMABUF
-const GdkDmabufDownloader *
- gdk_dmabuf_get_direct_downloader (void) G_GNUC_CONST;
-gboolean gdk_dmabuf_sanitize (GdkDmabuf *dest,
+const GdkDmabufDownloader * gdk_dmabuf_get_direct_downloader (void) G_GNUC_CONST;
+const GdkDmabufDownloader * gdk_dmabuf_get_egl_downloader (void) G_GNUC_CONST;
+
+gboolean gdk_dmabuf_sanitize (GdkDmabuf *dest,
gsize width,
gsize height,
const GdkDmabuf *src,
GError **error);
-gboolean gdk_dmabuf_is_disjoint (const GdkDmabuf *dmabuf);
+
+gboolean gdk_dmabuf_is_disjoint (const GdkDmabuf *dmabuf);
#endif
diff --git a/gdk/meson.build b/gdk/meson.build
index 68a6102564..7a939a6783 100644
--- a/gdk/meson.build
+++ b/gdk/meson.build
@@ -18,6 +18,7 @@ gdk_public_sources = files([
'gdkdisplay.c',
'gdkdisplaymanager.c',
'gdkdmabuf.c',
+ 'gdkdmabufegl.c',
'gdkdmabufformats.c',
'gdkdmabufformatsbuilder.c',
'gdkdmabuftexture.c',