Compare commits
8 Commits
wip/matthi
...
dmabuf-tex
Author | SHA1 | Date | |
---|---|---|---|
|
994d64635d | ||
|
e17b23bdb8 | ||
|
26d33f9ced | ||
|
9bebb86e26 | ||
|
a1f48003d7 | ||
|
b1d701e44f | ||
|
9a580b3db1 | ||
|
3406599942 |
@@ -391,6 +391,8 @@ gdk_display_dispose (GObject *object)
|
|||||||
|
|
||||||
g_queue_clear (&display->queued_events);
|
g_queue_clear (&display->queued_events);
|
||||||
|
|
||||||
|
gdk_display_set_egl_downloader_data (display, NULL, NULL);
|
||||||
|
|
||||||
g_clear_object (&priv->gl_context);
|
g_clear_object (&priv->gl_context);
|
||||||
#ifdef HAVE_EGL
|
#ifdef HAVE_EGL
|
||||||
g_clear_pointer (&priv->egl_display, eglTerminate);
|
g_clear_pointer (&priv->egl_display, eglTerminate);
|
||||||
@@ -1770,6 +1772,10 @@ gdk_display_init_egl (GdkDisplay *self,
|
|||||||
epoxy_has_egl_extension (priv->egl_display, "EGL_KHR_no_config_context");
|
epoxy_has_egl_extension (priv->egl_display, "EGL_KHR_no_config_context");
|
||||||
self->have_egl_pixel_format_float =
|
self->have_egl_pixel_format_float =
|
||||||
epoxy_has_egl_extension (priv->egl_display, "EGL_EXT_pixel_format_float");
|
epoxy_has_egl_extension (priv->egl_display, "EGL_EXT_pixel_format_float");
|
||||||
|
self->have_egl_dma_buf_import =
|
||||||
|
epoxy_has_egl_extension (priv->egl_display, "EGL_EXT_image_dma_buf_import_modifiers");
|
||||||
|
self->have_egl_dma_buf_export =
|
||||||
|
epoxy_has_egl_extension (priv->egl_display, "EGL_MESA_image_dma_buf_export");
|
||||||
|
|
||||||
if (self->have_egl_no_config_context)
|
if (self->have_egl_no_config_context)
|
||||||
priv->egl_config_high_depth = gdk_display_create_egl_config (self,
|
priv->egl_config_high_depth = gdk_display_create_egl_config (self,
|
||||||
@@ -1879,9 +1885,12 @@ init_dmabuf_formats (GdkDisplay *display)
|
|||||||
#ifdef HAVE_LINUX_DMA_BUF_H
|
#ifdef HAVE_LINUX_DMA_BUF_H
|
||||||
if (!GDK_DEBUG_CHECK (DMABUF_DISABLE))
|
if (!GDK_DEBUG_CHECK (DMABUF_DISABLE))
|
||||||
{
|
{
|
||||||
gdk_display_prepare_gl (display, NULL);
|
|
||||||
|
|
||||||
gdk_display_add_dmabuf_downloader (display, gdk_dmabuf_get_direct_downloader (), builder);
|
gdk_display_add_dmabuf_downloader (display, gdk_dmabuf_get_direct_downloader (), builder);
|
||||||
|
|
||||||
|
#ifdef HAVE_EGL
|
||||||
|
if (gdk_display_prepare_gl (display, NULL))
|
||||||
|
gdk_display_add_dmabuf_downloader (display, gdk_dmabuf_get_egl_downloader (), builder);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -2368,3 +2377,21 @@ gdk_display_translate_key (GdkDisplay *display,
|
|||||||
level,
|
level,
|
||||||
consumed);
|
consumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpointer
|
||||||
|
gdk_display_get_egl_downloader_data (GdkDisplay *display)
|
||||||
|
{
|
||||||
|
return display->egl_downloader_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gdk_display_set_egl_downloader_data (GdkDisplay *display,
|
||||||
|
gpointer data,
|
||||||
|
GDestroyNotify destroy)
|
||||||
|
{
|
||||||
|
if (display->egl_downloader_data_destroy)
|
||||||
|
display->egl_downloader_data_destroy (display->egl_downloader_data);
|
||||||
|
|
||||||
|
display->egl_downloader_data = data;
|
||||||
|
display->egl_downloader_data_destroy = destroy;
|
||||||
|
}
|
||||||
|
@@ -114,9 +114,14 @@ struct _GdkDisplay
|
|||||||
guint have_egl_buffer_age : 1;
|
guint have_egl_buffer_age : 1;
|
||||||
guint have_egl_no_config_context : 1;
|
guint have_egl_no_config_context : 1;
|
||||||
guint have_egl_pixel_format_float : 1;
|
guint have_egl_pixel_format_float : 1;
|
||||||
|
guint have_egl_dma_buf_import : 1;
|
||||||
|
guint have_egl_dma_buf_export : 1;
|
||||||
|
|
||||||
GdkDmabufFormats *dmabuf_formats;
|
GdkDmabufFormats *dmabuf_formats;
|
||||||
const GdkDmabufDownloader *dmabuf_downloaders[4];
|
const GdkDmabufDownloader *dmabuf_downloaders[4];
|
||||||
|
/* Private data for the EGL dmabuf downloader */
|
||||||
|
gpointer egl_downloader_data;
|
||||||
|
GDestroyNotify egl_downloader_data_destroy;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GdkDisplayClass
|
struct _GdkDisplayClass
|
||||||
@@ -265,5 +270,11 @@ void gdk_display_set_cursor_theme (GdkDisplay *display,
|
|||||||
const char *theme,
|
const char *theme,
|
||||||
int size);
|
int size);
|
||||||
|
|
||||||
|
gpointer gdk_display_get_egl_downloader_data (GdkDisplay *display);
|
||||||
|
void gdk_display_set_egl_downloader_data (GdkDisplay *display,
|
||||||
|
gpointer data,
|
||||||
|
GDestroyNotify destroy);
|
||||||
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
325
gdk/gdkdmabufegl.c
Normal file
325
gdk/gdkdmabufegl.c
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
/* 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_LINUX_DMA_BUF_H) && defined (HAVE_EGL)
|
||||||
|
#include "gdkdmabufprivate.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/drm_fourcc.h>
|
||||||
|
#include <epoxy/egl.h>
|
||||||
|
|
||||||
|
static void
|
||||||
|
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);
|
||||||
|
|
||||||
|
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, "%ssupported EGL dmabuf format %.4s:%#lx %s",
|
||||||
|
external_only[j] ? "un" : "",
|
||||||
|
(char *) &fourccs[i],
|
||||||
|
modifiers[j],
|
||||||
|
external_only[j] ? "EXT" : "");
|
||||||
|
if (!external_only[j])
|
||||||
|
gdk_dmabuf_formats_builder_add_format (builder, fourccs[i], modifiers[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (modifiers);
|
||||||
|
g_free (external_only);
|
||||||
|
g_free (fourccs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (external_only[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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:%#lx",
|
||||||
|
(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;
|
||||||
|
typedef gpointer GskRenderNode;
|
||||||
|
|
||||||
|
extern GskRenderer * gsk_gl_renderer_new (void);
|
||||||
|
extern gboolean gsk_renderer_realize (GskRenderer *renderer,
|
||||||
|
GdkSurface *surface,
|
||||||
|
GError **error);
|
||||||
|
extern void gsk_renderer_unrealize (GskRenderer *renderer);
|
||||||
|
extern GdkTexture * gsk_renderer_render_texture (GskRenderer *renderer,
|
||||||
|
GskRenderNode *node,
|
||||||
|
const graphene_rect_t *bounds);
|
||||||
|
extern GskRenderNode * gsk_texture_node_new (GdkTexture *texture,
|
||||||
|
const graphene_rect_t *viewport);
|
||||||
|
extern void gsk_render_node_unref (GskRenderNode *node);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gdk_dmabuf_egl_downloader_download (const GdkDmabufDownloader *downloader,
|
||||||
|
GdkTexture *texture,
|
||||||
|
GdkMemoryFormat format,
|
||||||
|
guchar *data,
|
||||||
|
gsize stride)
|
||||||
|
{
|
||||||
|
GdkDmabufTexture *self = GDK_DMABUF_TEXTURE (texture);
|
||||||
|
GdkDisplay *display;
|
||||||
|
GskRenderer *renderer;
|
||||||
|
int width, height;
|
||||||
|
GskRenderNode *node;
|
||||||
|
GdkTexture *gl_texture;
|
||||||
|
GdkTextureDownloader *tex_downloader;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
GDK_DEBUG (DMABUF, "Using gsk for downloading a dmabuf");
|
||||||
|
|
||||||
|
display = gdk_dmabuf_texture_get_display (self);
|
||||||
|
|
||||||
|
renderer = gdk_display_get_egl_downloader_data (display);
|
||||||
|
if (!renderer)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
gdk_display_set_egl_downloader_data (display, renderer, g_object_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
width = gdk_texture_get_width (GDK_TEXTURE (self));
|
||||||
|
height = gdk_texture_get_height (GDK_TEXTURE (self));
|
||||||
|
|
||||||
|
node = gsk_texture_node_new (GDK_TEXTURE (self), &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||||
|
gl_texture = gsk_renderer_render_texture (renderer, node, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||||
|
gsk_render_node_unref (node);
|
||||||
|
|
||||||
|
tex_downloader = gdk_texture_downloader_new (gl_texture);
|
||||||
|
|
||||||
|
gdk_texture_downloader_set_format (tex_downloader, format);
|
||||||
|
|
||||||
|
gdk_texture_downloader_download_into (tex_downloader, data, stride);
|
||||||
|
|
||||||
|
gdk_texture_downloader_free (tex_downloader);
|
||||||
|
g_object_unref (gl_texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
const GdkDmabufDownloader *
|
||||||
|
gdk_dmabuf_get_egl_downloader (void)
|
||||||
|
{
|
||||||
|
static const GdkDmabufDownloader downloader = {
|
||||||
|
gdk_dmabuf_egl_downloader_add_formats,
|
||||||
|
gdk_dmabuf_egl_downloader_supports,
|
||||||
|
gdk_dmabuf_egl_downloader_download,
|
||||||
|
};
|
||||||
|
|
||||||
|
return &downloader;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_LINUX_DMA_BUF_H && HAVE_EGL */
|
@@ -39,6 +39,6 @@ struct _GdkDmabufDownloader
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef HAVE_LINUX_DMA_BUF_H
|
#ifdef HAVE_LINUX_DMA_BUF_H
|
||||||
const GdkDmabufDownloader *
|
const GdkDmabufDownloader * gdk_dmabuf_get_direct_downloader (void) G_GNUC_CONST;
|
||||||
gdk_dmabuf_get_direct_downloader (void) G_GNUC_CONST;
|
const GdkDmabufDownloader * gdk_dmabuf_get_egl_downloader (void) G_GNUC_CONST;
|
||||||
#endif
|
#endif
|
||||||
|
@@ -83,6 +83,7 @@
|
|||||||
#include "gdkmemorytextureprivate.h"
|
#include "gdkmemorytextureprivate.h"
|
||||||
#include "gdkprofilerprivate.h"
|
#include "gdkprofilerprivate.h"
|
||||||
#include "gdkglversionprivate.h"
|
#include "gdkglversionprivate.h"
|
||||||
|
#include "gdkdmabufformatsprivate.h"
|
||||||
|
|
||||||
#include "gdkprivate.h"
|
#include "gdkprivate.h"
|
||||||
|
|
||||||
@@ -95,6 +96,10 @@
|
|||||||
#include <epoxy/egl.h>
|
#include <epoxy/egl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_LINUX_DMA_BUF_H
|
||||||
|
#include <drm/drm_fourcc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#define DEFAULT_ALLOWED_APIS GDK_GL_API_GL | GDK_GL_API_GLES
|
#define DEFAULT_ALLOWED_APIS GDK_GL_API_GL | GDK_GL_API_GLES
|
||||||
@@ -109,6 +114,7 @@ typedef struct {
|
|||||||
guint has_sync : 1;
|
guint has_sync : 1;
|
||||||
guint has_unpack_subimage : 1;
|
guint has_unpack_subimage : 1;
|
||||||
guint has_debug_output : 1;
|
guint has_debug_output : 1;
|
||||||
|
guint has_image_storage : 1;
|
||||||
guint extensions_checked : 1;
|
guint extensions_checked : 1;
|
||||||
guint debug_enabled : 1;
|
guint debug_enabled : 1;
|
||||||
guint forward_compatible : 1;
|
guint forward_compatible : 1;
|
||||||
@@ -1555,6 +1561,8 @@ gdk_gl_context_check_extensions (GdkGLContext *context)
|
|||||||
epoxy_has_gl_extension ("GL_ARB_sync") ||
|
epoxy_has_gl_extension ("GL_ARB_sync") ||
|
||||||
epoxy_has_gl_extension ("GL_APPLE_sync");
|
epoxy_has_gl_extension ("GL_APPLE_sync");
|
||||||
|
|
||||||
|
priv->has_image_storage = epoxy_has_gl_extension ("GL_EXT_EGL_image_storage");
|
||||||
|
|
||||||
#ifdef G_ENABLE_DEBUG
|
#ifdef G_ENABLE_DEBUG
|
||||||
{
|
{
|
||||||
int max_texture_size;
|
int max_texture_size;
|
||||||
@@ -1862,6 +1870,14 @@ gdk_gl_context_has_sync (GdkGLContext *self)
|
|||||||
return priv->has_sync;
|
return priv->has_sync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gdk_gl_context_has_image_storage (GdkGLContext *self)
|
||||||
|
{
|
||||||
|
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
|
||||||
|
|
||||||
|
return priv->has_image_storage;
|
||||||
|
}
|
||||||
|
|
||||||
/* This is currently private! */
|
/* This is currently private! */
|
||||||
/* When using GL/ES, don't flip the 'R' and 'B' bits on Windows/ANGLE for glReadPixels() */
|
/* When using GL/ES, don't flip the 'R' and 'B' bits on Windows/ANGLE for glReadPixels() */
|
||||||
gboolean
|
gboolean
|
||||||
@@ -1942,3 +1958,173 @@ gdk_gl_backend_use (GdkGLBackend backend_type)
|
|||||||
|
|
||||||
g_assert (the_gl_backend_type == backend_type);
|
g_assert (the_gl_backend_type == backend_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guint
|
||||||
|
gdk_gl_context_import_dmabuf (GdkGLContext *self,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
const GdkDmabuf *dmabuf)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_EGL) && defined(HAVE_LINUX_DMA_BUF_H)
|
||||||
|
GdkDisplay *display = gdk_gl_context_get_display (self);
|
||||||
|
EGLDisplay egl_display = gdk_display_get_egl_display (display);
|
||||||
|
EGLint attribs[64];
|
||||||
|
int i;
|
||||||
|
EGLImage image;
|
||||||
|
guint texture_id;
|
||||||
|
|
||||||
|
g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), 0);
|
||||||
|
g_return_val_if_fail (width > 0, 0);
|
||||||
|
g_return_val_if_fail (height > 0, 0);
|
||||||
|
g_return_val_if_fail (1 <= dmabuf->n_planes && dmabuf->n_planes <= 4, 0);
|
||||||
|
|
||||||
|
if (egl_display == EGL_NO_DISPLAY || !display->have_egl_dma_buf_import)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
GDK_DEBUG (DMABUF,
|
||||||
|
"Importing dmabuf (format: %.4s:%#lx, planes: %u) into GL",
|
||||||
|
(char *) &dmabuf->fourcc, dmabuf->modifier, dmabuf->n_planes);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
attribs[i++] = EGL_WIDTH;
|
||||||
|
attribs[i++] = width;
|
||||||
|
attribs[i++] = EGL_HEIGHT;
|
||||||
|
attribs[i++] = height;
|
||||||
|
attribs[i++] = EGL_LINUX_DRM_FOURCC_EXT;
|
||||||
|
attribs[i++] = dmabuf->fourcc;
|
||||||
|
|
||||||
|
#define ADD_PLANE(plane) \
|
||||||
|
{ \
|
||||||
|
if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID) \
|
||||||
|
{ \
|
||||||
|
attribs[i++] = EGL_DMA_BUF_PLANE## plane ##_MODIFIER_LO_EXT; \
|
||||||
|
attribs[i++] = dmabuf->modifier & 0xFFFFFFFF; \
|
||||||
|
attribs[i++] = EGL_DMA_BUF_PLANE## plane ## _MODIFIER_HI_EXT; \
|
||||||
|
attribs[i++] = dmabuf->modifier >> 32; \
|
||||||
|
} \
|
||||||
|
attribs[i++] = EGL_DMA_BUF_PLANE## plane ##_FD_EXT; \
|
||||||
|
attribs[i++] = dmabuf->planes[plane].fd; \
|
||||||
|
attribs[i++] = EGL_DMA_BUF_PLANE## plane ##_PITCH_EXT; \
|
||||||
|
attribs[i++] = dmabuf->planes[plane].stride; \
|
||||||
|
attribs[i++] = EGL_DMA_BUF_PLANE## plane ##_OFFSET_EXT; \
|
||||||
|
attribs[i++] = dmabuf->planes[plane].offset; \
|
||||||
|
}
|
||||||
|
|
||||||
|
ADD_PLANE (0);
|
||||||
|
|
||||||
|
if (dmabuf->n_planes > 1) ADD_PLANE (1);
|
||||||
|
if (dmabuf->n_planes > 2) ADD_PLANE (2);
|
||||||
|
if (dmabuf->n_planes > 3) ADD_PLANE (3);
|
||||||
|
|
||||||
|
attribs[i++] = EGL_NONE;
|
||||||
|
|
||||||
|
image = eglCreateImageKHR (egl_display,
|
||||||
|
EGL_NO_CONTEXT,
|
||||||
|
EGL_LINUX_DMA_BUF_EXT,
|
||||||
|
(EGLClientBuffer)NULL,
|
||||||
|
attribs);
|
||||||
|
|
||||||
|
if (image == EGL_NO_IMAGE)
|
||||||
|
{
|
||||||
|
g_warning ("Creating EGLImage for dmabuf failed: %#x", eglGetError ());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
glGenTextures (1, &texture_id);
|
||||||
|
glBindTexture (GL_TEXTURE_2D, texture_id);
|
||||||
|
glEGLImageTargetTexture2DOES (GL_TEXTURE_2D, image);
|
||||||
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
eglDestroyImageKHR (egl_display, image);
|
||||||
|
|
||||||
|
return texture_id;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gdk_gl_context_export_dmabuf (GdkGLContext *self,
|
||||||
|
unsigned int texture_id,
|
||||||
|
GdkDmabuf *dmabuf)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_EGL) && defined(HAVE_LINUX_DMA_BUF_H)
|
||||||
|
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
|
||||||
|
GdkDisplay *display = gdk_gl_context_get_display (self);
|
||||||
|
EGLDisplay egl_display = gdk_display_get_egl_display (display);
|
||||||
|
EGLContext egl_context = priv->egl_context;
|
||||||
|
EGLint attribs[10];
|
||||||
|
int i;
|
||||||
|
EGLImage image;
|
||||||
|
gboolean result = FALSE;
|
||||||
|
int fourcc;
|
||||||
|
int n_planes;
|
||||||
|
guint64 modifier;
|
||||||
|
int fds[GDK_DMABUF_MAX_PLANES];
|
||||||
|
int strides[GDK_DMABUF_MAX_PLANES];
|
||||||
|
int offsets[GDK_DMABUF_MAX_PLANES];
|
||||||
|
|
||||||
|
g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), FALSE);
|
||||||
|
g_return_val_if_fail (texture_id > 0, FALSE);
|
||||||
|
g_return_val_if_fail (dmabuf != NULL, FALSE);
|
||||||
|
|
||||||
|
if (egl_display == EGL_NO_DISPLAY || !display->have_egl_dma_buf_export)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
GDK_DEBUG (DMABUF, "Exporting GL texture to dmabuf");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
attribs[i++] = EGL_IMAGE_PRESERVED_KHR;
|
||||||
|
attribs[i++] = EGL_TRUE;
|
||||||
|
|
||||||
|
attribs[i++] = EGL_NONE;
|
||||||
|
|
||||||
|
image = eglCreateImageKHR (egl_display,
|
||||||
|
egl_context,
|
||||||
|
EGL_GL_TEXTURE_2D_KHR,
|
||||||
|
(EGLClientBuffer)GUINT_TO_POINTER (texture_id),
|
||||||
|
attribs);
|
||||||
|
|
||||||
|
if (image == EGL_NO_IMAGE)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!eglExportDMABUFImageQueryMESA (egl_display,
|
||||||
|
image,
|
||||||
|
&fourcc,
|
||||||
|
&n_planes,
|
||||||
|
&modifier))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!eglExportDMABUFImageMESA (egl_display,
|
||||||
|
image,
|
||||||
|
fds,
|
||||||
|
strides,
|
||||||
|
offsets))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
dmabuf->fourcc = (guint32)fourcc;
|
||||||
|
dmabuf->modifier = modifier;
|
||||||
|
dmabuf->n_planes = n_planes;
|
||||||
|
|
||||||
|
for (i = 0; i < n_planes; i++)
|
||||||
|
{
|
||||||
|
dmabuf->planes[i].fd = fds[i];
|
||||||
|
dmabuf->planes[i].stride = (int) strides[i];
|
||||||
|
dmabuf->planes[i].offset = (int) offsets[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
GDK_DEBUG (DMABUF,
|
||||||
|
"Exported GL texture to dmabuf (format: %.4s:%#lx, planes: %d)",
|
||||||
|
(char *)&fourcc, modifier, n_planes);
|
||||||
|
|
||||||
|
result = TRUE;
|
||||||
|
|
||||||
|
out:
|
||||||
|
eglDestroyImageKHR (egl_display, image);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
return FALSE:
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
@@ -23,6 +23,7 @@
|
|||||||
#include "gdkglcontext.h"
|
#include "gdkglcontext.h"
|
||||||
#include "gdkdrawcontextprivate.h"
|
#include "gdkdrawcontextprivate.h"
|
||||||
#include "gdkglversionprivate.h"
|
#include "gdkglversionprivate.h"
|
||||||
|
#include "gdkdmabufprivate.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
@@ -157,7 +158,17 @@ gboolean gdk_gl_context_has_vertex_half_float (GdkGLContext
|
|||||||
|
|
||||||
gboolean gdk_gl_context_has_sync (GdkGLContext *self) G_GNUC_PURE;
|
gboolean gdk_gl_context_has_sync (GdkGLContext *self) G_GNUC_PURE;
|
||||||
|
|
||||||
|
gboolean gdk_gl_context_has_image_storage (GdkGLContext *self) G_GNUC_PURE;
|
||||||
|
|
||||||
double gdk_gl_context_get_scale (GdkGLContext *self);
|
double gdk_gl_context_get_scale (GdkGLContext *self);
|
||||||
|
|
||||||
G_END_DECLS
|
guint gdk_gl_context_import_dmabuf (GdkGLContext *self,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
const GdkDmabuf *dmabuf);
|
||||||
|
|
||||||
|
gboolean gdk_gl_context_export_dmabuf (GdkGLContext *self,
|
||||||
|
unsigned int texture_id,
|
||||||
|
GdkDmabuf *dmabuf);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
@@ -18,6 +18,7 @@ gdk_public_sources = files([
|
|||||||
'gdkdisplay.c',
|
'gdkdisplay.c',
|
||||||
'gdkdisplaymanager.c',
|
'gdkdisplaymanager.c',
|
||||||
'gdkdmabuf.c',
|
'gdkdmabuf.c',
|
||||||
|
'gdkdmabufegl.c',
|
||||||
'gdkdmabufformats.c',
|
'gdkdmabufformats.c',
|
||||||
'gdkdmabufformatsbuilder.c',
|
'gdkdmabufformatsbuilder.c',
|
||||||
'gdkdmabuftexture.c',
|
'gdkdmabuftexture.c',
|
||||||
|
@@ -44,6 +44,8 @@
|
|||||||
#include <gdk/gdktextureprivate.h>
|
#include <gdk/gdktextureprivate.h>
|
||||||
|
|
||||||
#include <gdk/gdkmemoryformatprivate.h>
|
#include <gdk/gdkmemoryformatprivate.h>
|
||||||
|
#include <gdk/gdkdmabuftextureprivate.h>
|
||||||
|
|
||||||
|
|
||||||
G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
|
G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
|
||||||
|
|
||||||
@@ -758,7 +760,14 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
|
|||||||
return t->texture_id;
|
return t->texture_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GDK_IS_GL_TEXTURE (texture))
|
if (GDK_IS_DMABUF_TEXTURE (texture))
|
||||||
|
{
|
||||||
|
texture_id = gdk_gl_context_import_dmabuf (context,
|
||||||
|
gdk_texture_get_width (texture),
|
||||||
|
gdk_texture_get_height (texture),
|
||||||
|
gdk_dmabuf_texture_get_dmabuf (GDK_DMABUF_TEXTURE (texture)));
|
||||||
|
}
|
||||||
|
else if (GDK_IS_GL_TEXTURE (texture))
|
||||||
{
|
{
|
||||||
GdkGLTexture *gl_texture = (GdkGLTexture *) texture;
|
GdkGLTexture *gl_texture = (GdkGLTexture *) texture;
|
||||||
GdkGLContext *texture_context = gdk_gl_texture_get_context (gl_texture);
|
GdkGLContext *texture_context = gdk_gl_texture_get_context (gl_texture);
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
#include <gsk/gskglshaderprivate.h>
|
#include <gsk/gskglshaderprivate.h>
|
||||||
#include <gdk/gdktextureprivate.h>
|
#include <gdk/gdktextureprivate.h>
|
||||||
#include <gdk/gdkmemorytextureprivate.h>
|
#include <gdk/gdkmemorytextureprivate.h>
|
||||||
|
#include <gdk/gdkdmabuftexture.h>
|
||||||
#include <gsk/gsktransformprivate.h>
|
#include <gsk/gsktransformprivate.h>
|
||||||
#include <gsk/gskroundedrectprivate.h>
|
#include <gsk/gskroundedrectprivate.h>
|
||||||
#include <gsk/gskrectprivate.h>
|
#include <gsk/gskrectprivate.h>
|
||||||
@@ -47,6 +48,7 @@
|
|||||||
#include "ninesliceprivate.h"
|
#include "ninesliceprivate.h"
|
||||||
#include "fp16private.h"
|
#include "fp16private.h"
|
||||||
|
|
||||||
|
|
||||||
#define ORTHO_NEAR_PLANE -10000
|
#define ORTHO_NEAR_PLANE -10000
|
||||||
#define ORTHO_FAR_PLANE 10000
|
#define ORTHO_FAR_PLANE 10000
|
||||||
#define MAX_GRADIENT_STOPS 6
|
#define MAX_GRADIENT_STOPS 6
|
||||||
@@ -3630,16 +3632,12 @@ gsk_gl_render_job_upload_texture (GskGLRenderJob *job,
|
|||||||
gboolean ensure_mipmap,
|
gboolean ensure_mipmap,
|
||||||
GskGLRenderOffscreen *offscreen)
|
GskGLRenderOffscreen *offscreen)
|
||||||
{
|
{
|
||||||
GdkGLTexture *gl_texture = NULL;
|
/* Don't put GL or dmabuf textures into icon caches, they are already on the GPU side */
|
||||||
|
|
||||||
if (GDK_IS_GL_TEXTURE (texture))
|
|
||||||
gl_texture = GDK_GL_TEXTURE (texture);
|
|
||||||
|
|
||||||
if (!ensure_mipmap &&
|
if (!ensure_mipmap &&
|
||||||
gsk_gl_texture_library_can_cache ((GskGLTextureLibrary *)job->driver->icons_library,
|
gsk_gl_texture_library_can_cache ((GskGLTextureLibrary *)job->driver->icons_library,
|
||||||
texture->width,
|
texture->width,
|
||||||
texture->height) &&
|
texture->height) &&
|
||||||
!gl_texture)
|
!(GDK_IS_GL_TEXTURE (texture) || GDK_IS_DMABUF_TEXTURE (texture)))
|
||||||
{
|
{
|
||||||
const GskGLIconData *icon_data;
|
const GskGLIconData *icon_data;
|
||||||
|
|
||||||
@@ -3653,16 +3651,18 @@ gsk_gl_render_job_upload_texture (GskGLRenderJob *job,
|
|||||||
/* Only generate a mipmap if it does not make use reupload
|
/* Only generate a mipmap if it does not make use reupload
|
||||||
* a GL texture which we could otherwise use directly.
|
* a GL texture which we could otherwise use directly.
|
||||||
*/
|
*/
|
||||||
if (gl_texture &&
|
if (GDK_IS_GL_TEXTURE (texture) &&
|
||||||
gdk_gl_context_is_shared (gdk_gl_texture_get_context (gl_texture), job->command_queue->context))
|
gdk_gl_context_is_shared (gdk_gl_texture_get_context (GDK_GL_TEXTURE (texture)),
|
||||||
ensure_mipmap = gdk_gl_texture_has_mipmap (gl_texture);
|
job->command_queue->context))
|
||||||
|
ensure_mipmap = gdk_gl_texture_has_mipmap (GDK_GL_TEXTURE (texture));
|
||||||
|
|
||||||
offscreen->texture_id = gsk_gl_driver_load_texture (job->driver, texture, ensure_mipmap);
|
offscreen->texture_id = gsk_gl_driver_load_texture (job->driver, texture, ensure_mipmap);
|
||||||
init_full_texture_region (offscreen);
|
init_full_texture_region (offscreen);
|
||||||
offscreen->has_mipmap = ensure_mipmap;
|
offscreen->has_mipmap = ensure_mipmap;
|
||||||
|
|
||||||
if (gl_texture && offscreen->texture_id == gdk_gl_texture_get_id (gl_texture))
|
if (GDK_IS_GL_TEXTURE (texture) &&
|
||||||
offscreen->sync = gdk_gl_texture_get_sync (gl_texture);
|
offscreen->texture_id == gdk_gl_texture_get_id (GDK_GL_TEXTURE (texture)))
|
||||||
|
offscreen->sync = gdk_gl_texture_get_sync (GDK_GL_TEXTURE (texture));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
gtk_tests = [
|
gtk_tests = [
|
||||||
# testname, optional extra sources
|
# testname, optional extra sources
|
||||||
|
['testdmabuf'],
|
||||||
['testsections'],
|
['testsections'],
|
||||||
['testfilelauncher'],
|
['testfilelauncher'],
|
||||||
['input'],
|
['input'],
|
||||||
|
443
tests/testdmabuf.c
Normal file
443
tests/testdmabuf.c
Normal file
@@ -0,0 +1,443 @@
|
|||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/dma-heap.h>
|
||||||
|
#include <drm/drm_fourcc.h>
|
||||||
|
|
||||||
|
/* For this to work, you may need to give /dev/dma_heap/system
|
||||||
|
* lax permissions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
allocate_dma_buf (gsize size)
|
||||||
|
{
|
||||||
|
static int fd = -1;
|
||||||
|
struct dma_heap_allocation_data heap_data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
fd = open ("/dev/dma_heap/system", O_RDONLY | O_CLOEXEC);
|
||||||
|
if (fd == -1)
|
||||||
|
g_error ("Failed to open /dev/dma_heap/system");
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_data.len = size;
|
||||||
|
heap_data.fd = 0;
|
||||||
|
heap_data.fd_flags = O_RDWR | O_CLOEXEC;
|
||||||
|
heap_data.heap_flags = 0;
|
||||||
|
|
||||||
|
ret = ioctl (fd, DMA_HEAP_IOCTL_ALLOC, &heap_data);
|
||||||
|
if (ret)
|
||||||
|
g_error ("dma-buf allocation failed");
|
||||||
|
|
||||||
|
return heap_data.fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
populate_dma_buf (int fd,
|
||||||
|
guchar *data,
|
||||||
|
gsize size)
|
||||||
|
{
|
||||||
|
guchar *buf;
|
||||||
|
|
||||||
|
buf = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
memcpy (buf, data, size);
|
||||||
|
munmap (buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* The YUV conversion code is adapted from weston/tests/yuv-buffer-test.c */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on Rec. ITU-R BT.601-7
|
||||||
|
*
|
||||||
|
* This is intended to be obvious and accurate, not fast.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
x8r8g8b8_to_ycbcr8_bt601 (guint32 xrgb,
|
||||||
|
guchar *y_out,
|
||||||
|
guchar *cb_out,
|
||||||
|
guchar *cr_out)
|
||||||
|
{
|
||||||
|
double y, cb, cr;
|
||||||
|
double r = (xrgb >> 16) & 0xff;
|
||||||
|
double g = (xrgb >> 8) & 0xff;
|
||||||
|
double b = (xrgb >> 0) & 0xff;
|
||||||
|
|
||||||
|
/* normalize to [0.0, 1.0] */
|
||||||
|
r /= 255.0;
|
||||||
|
g /= 255.0;
|
||||||
|
b /= 255.0;
|
||||||
|
|
||||||
|
/* Y normalized to [0.0, 1.0], Cb and Cr [-0.5, 0.5] */
|
||||||
|
y = 0.299 * r + 0.587 * g + 0.114 * b;
|
||||||
|
cr = (r - y) / 1.402;
|
||||||
|
cb = (b - y) / 1.772;
|
||||||
|
|
||||||
|
/* limited range quantization to 8 bit */
|
||||||
|
*y_out = round(219.0 * y + 16.0);
|
||||||
|
if (cr_out)
|
||||||
|
*cr_out = round(224.0 * cr + 128.0);
|
||||||
|
if (cb_out)
|
||||||
|
*cb_out = round(224.0 * cb + 128.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 3 plane YCbCr
|
||||||
|
* plane 0: Y plane, [7:0] Y
|
||||||
|
* plane 1: Cb plane, [7:0] Cb
|
||||||
|
* plane 2: Cr plane, [7:0] Cr
|
||||||
|
* YUV420: 2x2 subsampled Cb (1) and Cr (2) planes
|
||||||
|
* YUV444: no subsampling
|
||||||
|
*/
|
||||||
|
static guchar *
|
||||||
|
y_u_v_create_buffer (uint32_t drm_format,
|
||||||
|
guchar *rgb_data,
|
||||||
|
int rgb_width,
|
||||||
|
int rgb_height,
|
||||||
|
int *size,
|
||||||
|
int *u_offset,
|
||||||
|
int *v_offset)
|
||||||
|
{
|
||||||
|
gsize bytes;
|
||||||
|
int x, y;
|
||||||
|
guint32 *rgb_row;
|
||||||
|
guchar *y_base;
|
||||||
|
guchar *u_base;
|
||||||
|
guchar *v_base;
|
||||||
|
guchar *y_row;
|
||||||
|
guchar *u_row;
|
||||||
|
guchar *v_row;
|
||||||
|
guint32 argb;
|
||||||
|
int sub = (drm_format == DRM_FORMAT_YUV420) ? 2 : 1;
|
||||||
|
guchar *buf;
|
||||||
|
|
||||||
|
g_assert (drm_format == DRM_FORMAT_YUV420 ||
|
||||||
|
drm_format == DRM_FORMAT_YUV444);
|
||||||
|
|
||||||
|
/* Full size Y plus quarter U and V */
|
||||||
|
bytes = rgb_width * rgb_height +
|
||||||
|
(rgb_width / sub) * (rgb_height / sub) * 2;
|
||||||
|
|
||||||
|
buf = g_new (guchar, bytes);
|
||||||
|
|
||||||
|
*size = bytes;
|
||||||
|
*u_offset = rgb_width * rgb_height;
|
||||||
|
*v_offset = *u_offset + (rgb_width / sub) * (rgb_height / sub);
|
||||||
|
|
||||||
|
y_base = buf;
|
||||||
|
u_base = y_base + rgb_width * rgb_height;
|
||||||
|
v_base = u_base + (rgb_width / sub) * (rgb_height / sub);
|
||||||
|
|
||||||
|
for (y = 0; y < rgb_height; y++)
|
||||||
|
{
|
||||||
|
rgb_row = (guint32 *) (rgb_data + y * 4 * rgb_width);
|
||||||
|
y_row = y_base + y * rgb_width;
|
||||||
|
u_row = u_base + (y / sub) * (rgb_width / sub);
|
||||||
|
v_row = v_base + (y / sub) * (rgb_width / sub);
|
||||||
|
|
||||||
|
for (x = 0; x < rgb_width; x++)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Sub-sample the source image instead, so that U and V
|
||||||
|
* sub-sampling does not require proper
|
||||||
|
* filtering/averaging/siting.
|
||||||
|
*/
|
||||||
|
argb = *(rgb_row + x / 2 * 2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A stupid way of "sub-sampling" chroma. This does not
|
||||||
|
* do the necessary filtering/averaging/siting or
|
||||||
|
* alternate Cb/Cr rows.
|
||||||
|
*/
|
||||||
|
if ((y & (sub - 1)) == 0 && (x & (sub - 1)) == 0)
|
||||||
|
x8r8g8b8_to_ycbcr8_bt601 (argb, y_row + x, u_row + x / sub, v_row + x / sub);
|
||||||
|
else
|
||||||
|
x8r8g8b8_to_ycbcr8_bt601 (argb, y_row + x, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2 plane YCbCr
|
||||||
|
* plane 0 = Y plane, [7:0] Y
|
||||||
|
* plane 1 = Cr:Cb plane, [15:0] Cr:Cb little endian
|
||||||
|
* 2x2 subsampled Cr:Cb plane
|
||||||
|
*/
|
||||||
|
static guchar *
|
||||||
|
nv12_create_buffer (uint32_t drm_format,
|
||||||
|
guchar *rgb_data,
|
||||||
|
int rgb_width,
|
||||||
|
int rgb_height,
|
||||||
|
int *size,
|
||||||
|
int *uv_offset)
|
||||||
|
{
|
||||||
|
size_t bytes;
|
||||||
|
int x, y;
|
||||||
|
uint32_t *rgb_row;
|
||||||
|
uint8_t *y_base;
|
||||||
|
uint16_t *uv_base;
|
||||||
|
uint8_t *y_row;
|
||||||
|
uint16_t *uv_row;
|
||||||
|
uint32_t argb;
|
||||||
|
uint8_t cr;
|
||||||
|
uint8_t cb;
|
||||||
|
guchar *buf;
|
||||||
|
|
||||||
|
g_assert (drm_format == DRM_FORMAT_NV12);
|
||||||
|
|
||||||
|
/* Full size Y, quarter UV */
|
||||||
|
bytes = rgb_width * rgb_height +
|
||||||
|
(rgb_width / 2) * (rgb_height / 2) * sizeof (uint16_t);
|
||||||
|
|
||||||
|
buf = g_new0 (guchar, bytes);
|
||||||
|
|
||||||
|
*uv_offset = rgb_width * rgb_height;
|
||||||
|
y_base = buf;
|
||||||
|
uv_base = (uint16_t *)(y_base + rgb_width * rgb_height);
|
||||||
|
|
||||||
|
for (y = 0; y < rgb_height; y++)
|
||||||
|
{
|
||||||
|
rgb_row = (uint32_t *) (rgb_data + y * 4 * rgb_width);
|
||||||
|
y_row = y_base + y * rgb_width;
|
||||||
|
uv_row = (uint16_t *) (uv_base + (y / 2) * (rgb_width / 2));
|
||||||
|
|
||||||
|
for (x = 0; x < rgb_width; x++)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Sub-sample the source image instead, so that U and V
|
||||||
|
* sub-sampling does not require proper
|
||||||
|
* filtering/averaging/siting.
|
||||||
|
*/
|
||||||
|
argb = *(rgb_row + x / 2 * 2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A stupid way of "sub-sampling" chroma. This does not
|
||||||
|
* do the necessary filtering/averaging/siting.
|
||||||
|
*/
|
||||||
|
if ((y & 1) == 0 && (x & 1) == 0)
|
||||||
|
{
|
||||||
|
x8r8g8b8_to_ycbcr8_bt601(argb, y_row + x, &cb, &cr);
|
||||||
|
*(uv_row + x / 2) = ((uint16_t)cr << 8) | cb;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x8r8g8b8_to_ycbcr8_bt601(argb, y_row + x, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GdkTexture *
|
||||||
|
make_dmabuf_texture (const char *filename,
|
||||||
|
guint32 format)
|
||||||
|
{
|
||||||
|
GdkTexture *texture;
|
||||||
|
int width, height;
|
||||||
|
gsize rgb_stride, rgb_size;
|
||||||
|
guchar *rgb_data;
|
||||||
|
int fd;
|
||||||
|
GdkDmabufTextureBuilder *builder;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
texture = gdk_texture_new_from_filename (filename, NULL);
|
||||||
|
|
||||||
|
width = gdk_texture_get_width (texture);
|
||||||
|
height = gdk_texture_get_height (texture);
|
||||||
|
rgb_stride = 4 * width;
|
||||||
|
rgb_size = rgb_stride * height;
|
||||||
|
|
||||||
|
rgb_data = g_new0 (guchar, rgb_size);
|
||||||
|
|
||||||
|
gdk_texture_download (texture, rgb_data, rgb_stride);
|
||||||
|
|
||||||
|
g_object_unref (texture);
|
||||||
|
|
||||||
|
builder = gdk_dmabuf_texture_builder_new ();
|
||||||
|
|
||||||
|
gdk_dmabuf_texture_builder_set_display (builder, gdk_display_get_default ());
|
||||||
|
gdk_dmabuf_texture_builder_set_width (builder, width);
|
||||||
|
gdk_dmabuf_texture_builder_set_height (builder, height);
|
||||||
|
gdk_dmabuf_texture_builder_set_fourcc (builder, format);
|
||||||
|
gdk_dmabuf_texture_builder_set_modifier (builder, DRM_FORMAT_MOD_LINEAR);
|
||||||
|
|
||||||
|
if (format == DRM_FORMAT_XRGB8888)
|
||||||
|
{
|
||||||
|
gdk_dmabuf_texture_builder_set_n_planes (builder, 1);
|
||||||
|
|
||||||
|
fd = allocate_dma_buf (rgb_size);
|
||||||
|
populate_dma_buf (fd, rgb_data, rgb_size);
|
||||||
|
|
||||||
|
gdk_dmabuf_texture_builder_set_fd (builder, 0, fd);
|
||||||
|
gdk_dmabuf_texture_builder_set_stride (builder, 0, rgb_stride);
|
||||||
|
}
|
||||||
|
else if (format == DRM_FORMAT_YUV420)
|
||||||
|
{
|
||||||
|
guchar *buf;
|
||||||
|
int size, u_offset, v_offset;
|
||||||
|
|
||||||
|
gdk_dmabuf_texture_builder_set_n_planes (builder, 3);
|
||||||
|
|
||||||
|
buf = y_u_v_create_buffer (format, rgb_data, width, height, &size, &u_offset, &v_offset);
|
||||||
|
|
||||||
|
fd = allocate_dma_buf (width * height);
|
||||||
|
populate_dma_buf (fd, buf, width * height);
|
||||||
|
|
||||||
|
gdk_dmabuf_texture_builder_set_fd (builder, 0, fd);
|
||||||
|
gdk_dmabuf_texture_builder_set_stride (builder, 0, width);
|
||||||
|
gdk_dmabuf_texture_builder_set_offset (builder, 0, 0);
|
||||||
|
|
||||||
|
fd = allocate_dma_buf ((width / 2) * (height / 2));
|
||||||
|
populate_dma_buf (fd, buf + u_offset, (width / 2) * (height / 2));
|
||||||
|
|
||||||
|
gdk_dmabuf_texture_builder_set_fd (builder, 1, fd);
|
||||||
|
gdk_dmabuf_texture_builder_set_stride (builder, 1, width / 2);
|
||||||
|
gdk_dmabuf_texture_builder_set_offset (builder, 1, 0);
|
||||||
|
|
||||||
|
fd = allocate_dma_buf ((width / 2) * (height / 2));
|
||||||
|
populate_dma_buf (fd, buf + v_offset, (width / 2) * (height / 2));
|
||||||
|
|
||||||
|
gdk_dmabuf_texture_builder_set_fd (builder, 2, fd);
|
||||||
|
gdk_dmabuf_texture_builder_set_stride (builder, 2, width / 2);
|
||||||
|
gdk_dmabuf_texture_builder_set_offset (builder, 2, 0);
|
||||||
|
|
||||||
|
g_free (buf);
|
||||||
|
}
|
||||||
|
else if (format == DRM_FORMAT_NV12)
|
||||||
|
{
|
||||||
|
guchar *buf;
|
||||||
|
int size, uv_offset;
|
||||||
|
|
||||||
|
gdk_dmabuf_texture_builder_set_n_planes (builder, 2);
|
||||||
|
|
||||||
|
buf = nv12_create_buffer (format, rgb_data, width, height, &size, &uv_offset);
|
||||||
|
|
||||||
|
fd = allocate_dma_buf (width * height);
|
||||||
|
populate_dma_buf (fd, buf, width * height);
|
||||||
|
|
||||||
|
gdk_dmabuf_texture_builder_set_fd (builder, 0, fd);
|
||||||
|
gdk_dmabuf_texture_builder_set_stride (builder, 0, width);
|
||||||
|
gdk_dmabuf_texture_builder_set_offset (builder, 0, 0);
|
||||||
|
|
||||||
|
fd = allocate_dma_buf ((width / 2) * (height / 2) * sizeof (uint16_t));
|
||||||
|
populate_dma_buf (fd, buf + uv_offset, (width / 2) * (height / 2) * sizeof (uint16_t));
|
||||||
|
|
||||||
|
gdk_dmabuf_texture_builder_set_fd (builder, 1, fd);
|
||||||
|
gdk_dmabuf_texture_builder_set_stride (builder, 1, width);
|
||||||
|
gdk_dmabuf_texture_builder_set_offset (builder, 1, 0);
|
||||||
|
|
||||||
|
g_free (buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (rgb_data);
|
||||||
|
|
||||||
|
texture = gdk_dmabuf_texture_builder_build (builder, NULL, NULL, &error);
|
||||||
|
if (!texture)
|
||||||
|
g_error ("Failed to create dmabuf texture: %s", error->message);
|
||||||
|
|
||||||
|
g_object_unref (builder);
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint32 supported_formats[] = {
|
||||||
|
DRM_FORMAT_XRGB8888,
|
||||||
|
DRM_FORMAT_YUV420,
|
||||||
|
DRM_FORMAT_NV12,
|
||||||
|
};
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
format_is_supported (guint32 fmt)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < G_N_ELEMENTS (supported_formats); i++)
|
||||||
|
{
|
||||||
|
if (supported_formats[i] == fmt)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
supported_formats_to_string (void)
|
||||||
|
{
|
||||||
|
GString *s;
|
||||||
|
|
||||||
|
s = g_string_new ("");
|
||||||
|
for (int i = 0; i < G_N_ELEMENTS (supported_formats); i++)
|
||||||
|
{
|
||||||
|
if (s->len)
|
||||||
|
g_string_append (s, ", ");
|
||||||
|
g_string_append_printf (s, "%.4s", (char *)&supported_formats[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_string_free (s, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_GNUC_NORETURN
|
||||||
|
static void
|
||||||
|
usage (void)
|
||||||
|
{
|
||||||
|
char *formats = supported_formats_to_string ();
|
||||||
|
g_print ("Usage: testdmabuf FORMAT FILE\n"
|
||||||
|
"Supported formats: %s\n", formats);
|
||||||
|
g_free (formats);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint32
|
||||||
|
parse_format (const char *a)
|
||||||
|
{
|
||||||
|
if (strlen (a) == 4)
|
||||||
|
{
|
||||||
|
guint32 format = fourcc_code (a[0], a[1], a[2], a[3]);
|
||||||
|
if (format_is_supported (format))
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
usage ();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
GdkTexture *texture;
|
||||||
|
GtkWidget *window, *picture;
|
||||||
|
char *filename;
|
||||||
|
guint32 format;
|
||||||
|
|
||||||
|
if (argc != 3)
|
||||||
|
usage ();
|
||||||
|
|
||||||
|
format = parse_format (argv[1]);
|
||||||
|
filename = argv[2];
|
||||||
|
|
||||||
|
gtk_init ();
|
||||||
|
|
||||||
|
/* Get the list of supported formats with GDK_DEBUG=opengl */
|
||||||
|
gdk_display_get_dmabuf_formats (gdk_display_get_default ());
|
||||||
|
|
||||||
|
texture = make_dmabuf_texture (filename, format);
|
||||||
|
|
||||||
|
gdk_texture_save_to_png (texture, "testdmabuf.out.png");
|
||||||
|
|
||||||
|
window = gtk_window_new ();
|
||||||
|
|
||||||
|
picture = gtk_picture_new_for_paintable (GDK_PAINTABLE (texture));
|
||||||
|
|
||||||
|
gtk_window_set_child (GTK_WINDOW (window), picture);
|
||||||
|
|
||||||
|
gtk_window_present (GTK_WINDOW (window));
|
||||||
|
|
||||||
|
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
|
||||||
|
g_main_context_iteration (NULL, TRUE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Reference in New Issue
Block a user