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',