mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-08 17:50:10 +00:00
a579e3bc6d
These weren't fixed in the merge request in https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6489 to the change from https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6514
411 lines
12 KiB
C
411 lines
12 KiB
C
/* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#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 <graphene.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/ioctl.h>
|
|
#include <linux/dma-buf.h>
|
|
#include <drm_fourcc.h>
|
|
#include <epoxy/egl.h>
|
|
|
|
/* 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.
|
|
* We don't add external formats, unless we can use them (via GLES)
|
|
*/
|
|
if (modifiers[j] != DRM_FORMAT_MOD_LINEAR &&
|
|
(!external_only[j] || gdk_gl_context_get_use_es (context)))
|
|
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:
|
|
#ifdef DRM_FORMAT_XVUY8888
|
|
case DRM_FORMAT_XVUY8888:
|
|
#endif
|
|
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 */
|