Compare commits

...

10 Commits

Author SHA1 Message Date
Matthias Clasen
611e707022 wip: test download of dmabuf textures 2023-10-16 10:58:31 -04:00
Matthias Clasen
7bc67ff5cf untested: Do indirect download without gsk
Do the necessary GL gymnastics ourselves.

For testing this, there is currently an
env var to select:

DMABUF_DOWNLOAD_METHOD=gl
2023-10-16 10:58:31 -04:00
Matthias Clasen
8b9fb596b7 Make a test with dma heaps
This uses the dma-heap kernel api to create a dma-buf
and use it for a GdkDmabufTexture. It supports a few
formats to test how well GL conversion of YUV works.

The YUV code is adapted from weston tests.
2023-10-16 10:58:31 -04:00
Matthias Clasen
79cb80f783 dmabuf: Cache the renderer
Creating a GL renderer is relatively expensive
(since we create a new GL context). Therefore,
cache the renderer we use for indirect download
of dmabuf textures on the display.
2023-10-16 10:58:31 -04:00
Matthias Clasen
f0e5a0a41f dmabuf: Support indirect download
For dmabufs with formats that are supported by EGL,
but can't be handled with mmap+memcpy, use GL for
downloading.
2023-10-16 10:58:31 -04:00
Matthias Clasen
5ceb2ed671 gsk: Support dmabuf textures
Defer handling of dmabuf textures to the driver,
and add the necessary code in load_texture.
2023-10-16 10:58:31 -04:00
Matthias Clasen
7e185f39d2 display: Take gl into account for dmabuf formats
Construct the list of formats by appending
the basic formats to the gl formats.
2023-10-16 10:58:31 -04:00
Matthias Clasen
0b45acd2a7 gl context: Add private dmabuf API
Add API to import a dmabuf into a texture,
export a texture to a dmabuf and to get
the supported dma-buf formats.

All this is implemented using the relevant
EGL and GL extensions.
2023-10-16 10:58:31 -04:00
Matthias Clasen
0693c51870 gl context: Check another extension
This will be used in future commits.
2023-10-15 19:27:04 -04:00
Matthias Clasen
81c00901c1 display: Check for more EGL extensions
We are going to rely on this in future commits.
2023-10-15 19:22:02 -04:00
9 changed files with 1273 additions and 37 deletions

View File

@@ -1770,6 +1770,10 @@ gdk_display_init_egl (GdkDisplay *self,
epoxy_has_egl_extension (priv->egl_display, "EGL_KHR_no_config_context");
self->have_egl_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)
priv->egl_config_high_depth = gdk_display_create_egl_config (self,
@@ -1846,6 +1850,7 @@ gdk_display_get_egl_display (GdkDisplay *self)
static void
init_dmabuf_formats (GdkDisplay *display)
{
GdkDisplayPrivate *priv = gdk_display_get_instance_private (display);
GdkDmabufFormatsBuilder *builder;
if (display->dmabuf_formats != NULL)
@@ -1858,6 +1863,9 @@ init_dmabuf_formats (GdkDisplay *display)
gdk_display_prepare_gl (display, NULL);
gdk_dmabuf_texture_add_supported_formats (builder);
if (priv->gl_context)
gdk_gl_context_add_dmabuf_formats (priv->gl_context, builder);
}
display->dmabuf_formats = gdk_dmabuf_formats_builder_free_to_formats (builder);

View File

@@ -114,6 +114,8 @@ struct _GdkDisplay
guint have_egl_buffer_age : 1;
guint have_egl_no_config_context : 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;
};

View File

@@ -24,7 +24,7 @@
#include "gdkdmabufformatsbuilderprivate.h"
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
#include <gdk/gdkglcontext.h>
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdkgltexturebuilder.h>
#include <gdk/gdktexturedownloader.h>
@@ -36,6 +36,8 @@
#include <epoxy/egl.h>
#endif
#include <graphene.h>
/**
* GdkDmabufTexture:
*
@@ -155,6 +157,9 @@ struct _GdkDrmFormatInfo
GdkMemoryFormat unpremultiplied_memory_format;
};
/* These are the dma-buf formats that we can support just with memcpy,
* as long as they are combined with DRM_FORMAT_MOD_LINEAR
*/
static GdkDrmFormatInfo supported_formats[] = {
{ DRM_FORMAT_ARGB8888, GDK_MEMORY_A8R8G8B8_PREMULTIPLIED, GDK_MEMORY_A8R8G8B8 },
{ DRM_FORMAT_RGBA8888, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, GDK_MEMORY_R8G8B8A8 },
@@ -176,6 +181,410 @@ get_drm_format_info (guint32 fourcc)
return NULL;
}
/* 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 int
gl_format_for_memory_format (GdkMemoryFormat format)
{
switch (gdk_memory_format_get_depth (format))
{
case GDK_MEMORY_U8:
return GL_RGBA8;
case GDK_MEMORY_U16:
return GL_RGBA16;
case GDK_MEMORY_FLOAT16:
return GL_RGBA16F;
case GDK_MEMORY_FLOAT32:
return GL_RGBA32F;
default:
g_assert_not_reached ();
}
}
static int
gl_type_for_gl_format (int format)
{
switch (format)
{
case GL_RGBA8:
return GL_UNSIGNED_BYTE;
case GL_RGBA16:
return GL_UNSIGNED_SHORT;
case GL_RGBA16F:
return GL_HALF_FLOAT;
case GL_RGBA32F:
return GL_FLOAT;
default:
g_assert_not_reached ();
}
}
static void
create_render_target (int width,
int height,
int format,
unsigned int *fbo_id,
unsigned int *texture_id)
{
int type;
unsigned int texture, fbo;
type = gl_type_for_gl_format (format);
glGenTextures (1, &texture);
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, texture);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D (GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, type, NULL);
glGenFramebuffers (1, &fbo);
glBindFramebuffer (GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
g_assert (glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
*fbo_id = fbo;
*texture_id = texture;
}
static const char vertex_shader_source[] = ""
"#version 150\n"
"in vec2 in_position;\n"
"void main() {\n"
" gl_Position = vec4(in_position, 0.0, 1.0);\n"
"}";
static const char fragment_shader_source[] = ""
"#version 150\n"
"uniform sampler2D source;\n"
"uniform vec2 size;\n"
"out vec4 out_color;\n"
"void main() {\n"
" vec4 in_color = texture(source, gl_FragCoord.xy / size);\n"
" out_color = in_color;\n"
"}";
static unsigned int
create_shader (int type,
const char *src)
{
unsigned int shader;
int status;
shader = glCreateShader (type);
glShaderSource (shader, 1, &src, NULL);
glCompileShader (shader);
glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
int log_len;
char *buffer;
glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &log_len);
buffer = g_malloc (log_len + 1);
glGetShaderInfoLog (shader, log_len, NULL, buffer);
g_warning ("Compile failure in %s shader:\n%s",
type == GL_VERTEX_SHADER ? "vertex" : "fragment",
buffer);
g_free (buffer);
glDeleteShader (shader);
return 0;
}
return shader;
}
static unsigned int
compile_program (const char *vs,
const char *fs,
unsigned int *source_location,
unsigned int *size_location)
{
unsigned int vertex, fragment, program;
int status;
vertex = create_shader (GL_VERTEX_SHADER, vs);
if (vertex == 0)
return 0;
fragment = create_shader (GL_FRAGMENT_SHADER, fs);
if (fragment == 0)
{
glDeleteShader (vertex);
return 0;
}
program = glCreateProgram ();
glAttachShader (program, vertex);
glAttachShader (program, fragment);
glLinkProgram (program);
glGetProgramiv (program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
int log_len;
char *buffer;
glGetProgramiv (program, GL_INFO_LOG_LENGTH, &log_len);
buffer = g_malloc (log_len + 1);
glGetProgramInfoLog (program, log_len, NULL, buffer);
g_warning ("Linking failure:\n%s", buffer);
g_free (buffer);
glDeleteProgram (program);
program = 0;
goto out;
}
*source_location = glGetUniformLocation (program, "source");
*size_location = glGetUniformLocation (program, "size");
glDetachShader (program, vertex);
glDetachShader (program, fragment);
out:
glDeleteShader (vertex);
glDeleteShader (fragment);
return program;
}
typedef struct {
unsigned int program;
unsigned int source_location;
unsigned int size_location;
} ProgramData;
static unsigned int
get_blit_program (GdkGLContext *context,
unsigned int *source_location,
unsigned int *size_location)
{
ProgramData *data;
data = g_object_get_data (G_OBJECT (context), "dmabuf-blit-program-data");
if (!data)
{
data = g_new0 (ProgramData, 1);
data->program = compile_program (vertex_shader_source,
fragment_shader_source,
&data->source_location,
&data->size_location);
g_object_set_data (G_OBJECT (context), "dmabuf-blit-program-data", data);
}
*source_location = data->source_location;
*size_location = data->size_location;
return data->program;
}
static void
blit_texture (GdkGLContext *context,
int width,
int height,
unsigned int texture,
unsigned int fbo)
{
unsigned int program;
unsigned int source_location;
unsigned int size_location;
unsigned int vao;
unsigned int buffer;
const float vertices[] = {
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, -1.0f
};
program = get_blit_program (context, &source_location, &size_location);
glGenVertexArrays (1, &vao);
glBindVertexArray (vao);
glGenBuffers (1, &buffer);
glBindBuffer (GL_ARRAY_BUFFER, buffer);
glBufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer (0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray (0);
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, texture);
glBindFramebuffer (GL_FRAMEBUFFER, fbo);
glUseProgram (program);
glUniform1i (source_location, texture);
glUniform2f (size_location, width, height);
glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
glUseProgram (0);
glDisableVertexAttribArray (0);
glBindBuffer (GL_ARRAY_BUFFER, 0);
glDeleteBuffers (1, &buffer);
glDeleteVertexArrays (1, &vao);
}
static void
do_indirect_download_gl (GdkDmabufTexture *self,
GdkMemoryFormat format,
guchar *data,
gsize stride)
{
GdkGLContext *context;
unsigned int texture_id;
unsigned int texture_id2;
unsigned int fbo;
int width, height;
int gl_format;
GdkGLTextureBuilder *builder;
GdkTexture *gl_texture;
gpointer sync;
GdkTextureDownloader *downloader;
GDK_DEBUG (DMABUF, "Using gl for downloading a dmabuf");
context = gdk_display_get_gl_context (self->display);
gdk_gl_context_make_current (context);
gl_format = gl_format_for_memory_format (format),
width = gdk_texture_get_width (GDK_TEXTURE (self));
height = gdk_texture_get_height (GDK_TEXTURE (self));
/* 1. import the dmabuf as GL texture */
texture_id = gdk_gl_context_import_dmabuf (context, width, height,
self->fourcc,
self->modifier,
self->n_planes,
self->fds,
self->strides,
self->offsets);
/* 2. create a texture to render into */
create_render_target (width, height, gl_format, &fbo, &texture_id2);
/* 3. copy from texture_id1 to texture_id2, using a blit shader */
glClear (GL_COLOR_BUFFER_BIT);
blit_texture (context, width, height, texture_id, fbo);
sync = glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
/* 4. create a GdkGLTexture from the rendered texture */
builder = gdk_gl_texture_builder_new ();
gdk_gl_texture_builder_set_context (builder, context);
gdk_gl_texture_builder_set_id (builder, texture_id2);
gdk_gl_texture_builder_set_width (builder, width);
gdk_gl_texture_builder_set_height (builder, height);
gdk_gl_texture_builder_set_format (builder, format);
gdk_gl_texture_builder_set_sync (builder, sync);
gl_texture = gdk_gl_texture_builder_build (builder, NULL, NULL);
/* 5. download it */
downloader = gdk_texture_downloader_new (gl_texture);
gdk_texture_downloader_set_format (downloader, format);
gdk_texture_downloader_download_into (downloader, data, stride);
gdk_texture_downloader_free (downloader);
g_object_unref (gl_texture);
/* 6. cleanup */
glDeleteFramebuffers (1, &fbo);
glDeleteTextures (1, &texture_id);
glDeleteTextures (1, &texture_id2);
}
static void
do_indirect_download_gsk (GdkDmabufTexture *self,
GdkMemoryFormat format,
guchar *data,
gsize stride)
{
GskRenderer *renderer;
int width, height;
GskRenderNode *node;
GdkTexture *gl_texture;
GdkTextureDownloader *downloader;
GError *error = NULL;
GDK_DEBUG (DMABUF, "Using gsk for downloading a dmabuf");
g_assert (GDK_IS_DISPLAY (self->display));
g_assert (GDK_IS_GL_CONTEXT (gdk_display_get_gl_context (self->display)));
renderer = g_object_get_data (G_OBJECT (self->display), "dmabuf-gl-renderer");
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;
}
g_object_set_data_full (G_OBJECT (self->display), "dmabuf-gl-renderer",
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);
gsk_renderer_unrealize (renderer);
g_object_unref (renderer);
downloader = gdk_texture_downloader_new (gl_texture);
gdk_texture_downloader_set_format (downloader, format);
gdk_texture_downloader_download_into (downloader, data, stride);
gdk_texture_downloader_free (downloader);
g_object_unref (gl_texture);
}
static void
do_direct_download (GdkDmabufTexture *self,
guchar *data,
@@ -187,7 +596,7 @@ do_direct_download (GdkDmabufTexture *self,
guchar *src_data;
int bpp;
GDK_DEBUG (DMABUF, "Using mmap() and memcpy() for downloading a dmabuf");
GDK_DEBUG (DMABUF, "Using mmap for downloading a dmabuf");
height = gdk_texture_get_height (GDK_TEXTURE (self));
bpp = gdk_memory_format_bytes_per_pixel (gdk_texture_get_format (GDK_TEXTURE (self)));
@@ -196,7 +605,7 @@ do_direct_download (GdkDmabufTexture *self,
size = self->strides[0] * height;
if (ioctl (self->fds[0], DMA_BUF_IOCTL_SYNC, &(struct dma_buf_sync) { DMA_BUF_SYNC_START|DMA_BUF_SYNC_READ }) < 0)
g_warning ("Failed to sync dma-buf: %s", g_strerror (errno));
g_warning ("Failed to sync dmabuf: %s", g_strerror (errno));
src_data = mmap (NULL, size, PROT_READ, MAP_SHARED, self->fds[0], self->offsets[0]);
@@ -211,7 +620,76 @@ do_direct_download (GdkDmabufTexture *self,
munmap (src_data, size);
if (ioctl (self->fds[0], DMA_BUF_IOCTL_SYNC, &(struct dma_buf_sync) { DMA_BUF_SYNC_END|DMA_BUF_SYNC_READ }) < 0)
g_warning ("Failed to sync dma-buf: %s", g_strerror (errno));
g_warning ("Failed to sync dmabuf: %s", g_strerror (errno));
}
/* A helper to determine suitable download memory formats for drm formats */
static GdkMemoryFormat
compute_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;
}
}
#endif /* HAVE_LINUX_DMA_BUF_H */
@@ -231,6 +709,20 @@ gdk_dmabuf_texture_add_supported_formats (GdkDmabufFormatsBuilder *builder)
#endif
}
static void
do_indirect_download (GdkDmabufTexture *self,
GdkMemoryFormat format,
guchar *data,
gsize stride)
{
const char *method = g_getenv ("DMABUF_DOWNLOAD_METHOD");
if (g_strcmp0 (method, "gl") == 0)
do_indirect_download_gl (self, format, data, stride);
else
do_indirect_download_gsk (self, format, data, stride);
}
static void
gdk_dmabuf_texture_download (GdkTexture *texture,
GdkMemoryFormat format,
@@ -240,8 +732,14 @@ gdk_dmabuf_texture_download (GdkTexture *texture,
#ifdef HAVE_LINUX_DMA_BUF_H
GdkDmabufTexture *self = GDK_DMABUF_TEXTURE (texture);
GdkMemoryFormat src_format = gdk_texture_get_format (texture);
GdkDrmFormatInfo *info;
if (format == src_format)
info = get_drm_format_info (self->fourcc);
if (!info || self->n_planes > 1 || self->modifier != DRM_FORMAT_MOD_LINEAR ||
g_getenv ("DMABUF_DOWNLOAD_METHOD") != NULL)
do_indirect_download (self, format, data, stride);
else if (format == src_format)
do_direct_download (self, data, stride);
else
{
@@ -262,10 +760,25 @@ gdk_dmabuf_texture_download (GdkTexture *texture,
width, height);
g_free (src_data);
}
}
#endif /* HAVE_LINUX_DMA_BUF_H */
}
static gboolean
display_supports_format (GdkDisplay *display,
guint32 fourcc,
guint64 modifier)
{
GdkDmabufFormats *formats;
if (!display)
return FALSE;
formats = gdk_display_get_dmabuf_formats (display);
return gdk_dmabuf_formats_contains (formats, fourcc, modifier);
}
GdkTexture *
gdk_dmabuf_texture_new_from_builder (GdkDmabufTextureBuilder *builder,
GDestroyNotify destroy,
@@ -281,26 +794,25 @@ gdk_dmabuf_texture_new_from_builder (GdkDmabufTextureBuilder *builder,
gboolean premultiplied = gdk_dmabuf_texture_builder_get_premultiplied (builder);
unsigned int n_planes = gdk_dmabuf_texture_builder_get_n_planes (builder);
GdkDrmFormatInfo *info;
gboolean can_download_directly;
gboolean can_download_indirectly;
info = get_drm_format_info (fourcc);
if (!info || modifier != DRM_FORMAT_MOD_LINEAR)
can_download_directly = info != NULL && n_planes == 1 && modifier == DRM_FORMAT_MOD_LINEAR;
can_download_indirectly = display_supports_format (display, fourcc, modifier);
if (!can_download_directly && !can_download_indirectly)
{
g_set_error (error, GDK_DMABUF_ERROR, GDK_DMABUF_ERROR_UNSUPPORTED_FORMAT,
"Unsupported dmabuf format: %.4s:%#lx",
(char *) &fourcc, modifier);
return NULL;
}
if (n_planes > 1)
{
g_set_error (error, GDK_DMABUF_ERROR, GDK_DMABUF_ERROR_CREATION_FAILED,
"Cannot create multiplanar textures for dmabuf format: %.4s:%#lx",
(char *) &fourcc, modifier);
"Unsupported dmabuf format: %.4s:%#lx, planes: %u",
(char *) &fourcc, modifier, n_planes);
return NULL;
}
GDK_DEBUG (DMABUF, "Create dmabuf texture (format %.4s:%#lx)",
(char *) &fourcc, modifier);
GDK_DEBUG (DMABUF,
"Create dmabuf texture (format: %.4s:%#lx, planes: %u)",
(char *) &fourcc, modifier, n_planes);
self = g_object_new (GDK_TYPE_DMABUF_TEXTURE,
"width", gdk_dmabuf_texture_builder_get_width (builder),
@@ -310,8 +822,11 @@ gdk_dmabuf_texture_new_from_builder (GdkDmabufTextureBuilder *builder,
self->destroy = destroy;
self->data = data;
GDK_TEXTURE (self)->format = premultiplied ? info->premultiplied_memory_format
: info->unpremultiplied_memory_format;
if (can_download_directly)
GDK_TEXTURE (self)->format = premultiplied ? info->premultiplied_memory_format
: info->unpremultiplied_memory_format;
else
GDK_TEXTURE (self)->format = compute_memory_format (fourcc, premultiplied);
g_set_object (&self->display, display);

View File

@@ -83,6 +83,7 @@
#include "gdkmemorytextureprivate.h"
#include "gdkprofilerprivate.h"
#include "gdkglversionprivate.h"
#include "gdkdmabufformatsprivate.h"
#include "gdkprivate.h"
@@ -95,6 +96,10 @@
#include <epoxy/egl.h>
#endif
#ifdef HAVE_LINUX_DMA_BUF_H
#include <drm/drm_fourcc.h>
#endif
#include <math.h>
#define DEFAULT_ALLOWED_APIS GDK_GL_API_GL | GDK_GL_API_GLES
@@ -109,6 +114,7 @@ typedef struct {
guint has_sync : 1;
guint has_unpack_subimage : 1;
guint has_debug_output : 1;
guint has_image_storage : 1;
guint extensions_checked : 1;
guint debug_enabled : 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_APPLE_sync");
priv->has_image_storage = epoxy_has_gl_extension ("GL_EXT_EGL_image_storage");
#ifdef G_ENABLE_DEBUG
{
int max_texture_size;
@@ -1942,3 +1950,226 @@ gdk_gl_backend_use (GdkGLBackend backend_type)
g_assert (the_gl_backend_type == backend_type);
}
void
gdk_gl_context_add_dmabuf_formats (GdkGLContext *self,
GdkDmabufFormatsBuilder *builder)
{
#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);
gdk_gl_context_make_current (self);
if (egl_display != EGL_NO_DISPLAY &&
display->have_egl_dma_buf_import &&
priv->has_image_storage)
{
int num_fourccs;
int *fourccs;
eglQueryDmaBufFormatsEXT (egl_display, 0, NULL, &num_fourccs);
fourccs = g_new (int, num_fourccs);
eglQueryDmaBufFormatsEXT (egl_display, num_fourccs, fourccs, &num_fourccs);
for (int i = 0; i < num_fourccs; i++)
{
int num_modifiers;
uint64_t *modifiers;
unsigned int *external_only;
eglQueryDmaBufModifiersEXT (egl_display, fourccs[i], 0, NULL, NULL, &num_modifiers);
modifiers = g_new (uint64_t, num_modifiers);
external_only = g_new (unsigned int, num_modifiers);
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 (fourccs);
}
#endif
}
guint
gdk_gl_context_import_dmabuf (GdkGLContext *self,
int width,
int height,
unsigned int fourcc,
guint64 modifier,
unsigned int n_planes,
int *fds,
unsigned int *strides,
unsigned int *offsets)
{
#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 <= n_planes && n_planes <= 4, 0);
g_return_val_if_fail (fds != NULL, 0);
g_return_val_if_fail (strides != NULL, 0);
g_return_val_if_fail (offsets != NULL, 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 *) &fourcc, modifier, 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++] = fourcc;
#define ADD_PLANE(plane) \
{ \
if (modifier != DRM_FORMAT_MOD_INVALID) \
{ \
attribs[i++] = EGL_DMA_BUF_PLANE## plane ##_MODIFIER_LO_EXT; \
attribs[i++] = modifier & 0xFFFFFFFF; \
attribs[i++] = EGL_DMA_BUF_PLANE## plane ## _MODIFIER_HI_EXT; \
attribs[i++] = modifier >> 32; \
} \
attribs[i++] = EGL_DMA_BUF_PLANE## plane ##_FD_EXT; \
attribs[i++] = fds[plane]; \
attribs[i++] = EGL_DMA_BUF_PLANE## plane ##_PITCH_EXT; \
attribs[i++] = strides[plane]; \
attribs[i++] = EGL_DMA_BUF_PLANE## plane ##_OFFSET_EXT; \
attribs[i++] = offsets[plane]; \
}
ADD_PLANE (0);
if (n_planes > 1) ADD_PLANE (1);
if (n_planes > 2) ADD_PLANE (2);
if (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,
int *fourcc,
int *n_planes,
guint64 *modifier,
int *fds,
int *strides,
int *offsets)
{
#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;
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 (fourcc != NULL, FALSE);
g_return_val_if_fail (n_planes != NULL, FALSE);
g_return_val_if_fail (modifier != NULL, FALSE);
g_return_val_if_fail (fds != NULL, FALSE);
g_return_val_if_fail (strides != NULL, FALSE);
g_return_val_if_fail (offsets != 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;
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
}

View File

@@ -23,6 +23,7 @@
#include "gdkglcontext.h"
#include "gdkdrawcontextprivate.h"
#include "gdkglversionprivate.h"
#include "gdkdmabufformatsbuilderprivate.h"
G_BEGIN_DECLS
@@ -159,5 +160,28 @@ gboolean gdk_gl_context_has_sync (GdkGLContext
double gdk_gl_context_get_scale (GdkGLContext *self);
G_END_DECLS
void gdk_gl_context_add_dmabuf_formats (GdkGLContext *self,
GdkDmabufFormatsBuilder *builder);
guint gdk_gl_context_import_dmabuf (GdkGLContext *self,
int width,
int height,
unsigned int fourcc,
guint64 modifier,
unsigned int n_planes,
int *fds,
unsigned int *strides,
unsigned int *offsets);
gboolean gdk_gl_context_export_dmabuf (GdkGLContext *self,
unsigned int texture_id,
int *fourcc,
int *n_planes,
guint64 *modifier,
int *fds,
int *strides,
int *offsets);
G_END_DECLS

View File

@@ -44,6 +44,8 @@
#include <gdk/gdktextureprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gdk/gdkdmabuftextureprivate.h>
G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
@@ -399,7 +401,6 @@ gsk_gl_driver_load_programs (GskGLDriver *self,
g_steal_pointer (&program); \
} G_STMT_END;
# include "gskglprograms.defs"
#undef GSK_GL_DEFINE_PROGRAM_CLIP
#undef GSK_GL_DEFINE_PROGRAM
#undef GSK_GL_ADD_UNIFORM
#undef GSK_GL_SHADER_SINGLE
@@ -759,7 +760,19 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
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_fourcc (GDK_DMABUF_TEXTURE (texture)),
gdk_dmabuf_texture_get_modifier (GDK_DMABUF_TEXTURE (texture)),
gdk_dmabuf_texture_get_n_planes (GDK_DMABUF_TEXTURE (texture)),
gdk_dmabuf_texture_get_fds (GDK_DMABUF_TEXTURE (texture)),
gdk_dmabuf_texture_get_strides (GDK_DMABUF_TEXTURE (texture)),
gdk_dmabuf_texture_get_offsets (GDK_DMABUF_TEXTURE (texture)));
}
else if (GDK_IS_GL_TEXTURE (texture))
{
GdkGLTexture *gl_texture = (GdkGLTexture *) texture;
GdkGLContext *texture_context = gdk_gl_texture_get_context (gl_texture);
@@ -1004,9 +1017,9 @@ gsk_gl_driver_release_render_target (GskGLDriver *self,
texture_id = render_target->texture_id;
texture = gsk_gl_texture_new (render_target->texture_id,
render_target->width,
render_target->height,
self->current_frame_id);
render_target->width,
render_target->height,
self->current_frame_id);
g_hash_table_insert (self->textures,
GUINT_TO_POINTER (texture_id),
g_steal_pointer (&texture));

View File

@@ -30,6 +30,7 @@
#include <gsk/gskglshaderprivate.h>
#include <gdk/gdktextureprivate.h>
#include <gdk/gdkmemorytextureprivate.h>
#include <gdk/gdkdmabuftexture.h>
#include <gsk/gsktransformprivate.h>
#include <gsk/gskroundedrectprivate.h>
#include <gsk/gskrectprivate.h>
@@ -47,6 +48,7 @@
#include "ninesliceprivate.h"
#include "fp16private.h"
#define ORTHO_NEAR_PLANE -10000
#define ORTHO_FAR_PLANE 10000
#define MAX_GRADIENT_STOPS 6
@@ -3630,16 +3632,12 @@ gsk_gl_render_job_upload_texture (GskGLRenderJob *job,
gboolean ensure_mipmap,
GskGLRenderOffscreen *offscreen)
{
GdkGLTexture *gl_texture = NULL;
if (GDK_IS_GL_TEXTURE (texture))
gl_texture = GDK_GL_TEXTURE (texture);
/* Don't put GL or dma-buf textures into icon caches, they are already on the GPU side */
if (!ensure_mipmap &&
gsk_gl_texture_library_can_cache ((GskGLTextureLibrary *)job->driver->icons_library,
texture->width,
texture->height) &&
!gl_texture)
!(GDK_IS_GL_TEXTURE (texture) || GDK_IS_DMABUF_TEXTURE (texture)))
{
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
* a GL texture which we could otherwise use directly.
*/
if (gl_texture &&
gdk_gl_context_is_shared (gdk_gl_texture_get_context (gl_texture), job->command_queue->context))
ensure_mipmap = gdk_gl_texture_has_mipmap (gl_texture);
if (GDK_IS_GL_TEXTURE (texture) &&
gdk_gl_context_is_shared (gdk_gl_texture_get_context (GDK_GL_TEXTURE (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);
init_full_texture_region (offscreen);
offscreen->has_mipmap = ensure_mipmap;
if (gl_texture && offscreen->texture_id == gdk_gl_texture_get_id (gl_texture))
offscreen->sync = gdk_gl_texture_get_sync (gl_texture);
if (GDK_IS_GL_TEXTURE (texture) &&
offscreen->texture_id == gdk_gl_texture_get_id (GDK_GL_TEXTURE (texture)))
offscreen->sync = gdk_gl_texture_get_sync (GDK_GL_TEXTURE (texture));
}
}

View File

@@ -1,5 +1,6 @@
gtk_tests = [
# testname, optional extra sources
['testdmabuf'],
['testsections'],
['testfilelauncher'],
['input'],

442
tests/testdmabuf.c Normal file
View File

@@ -0,0 +1,442 @@
#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);
}
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)
g_assert (argc == 3);
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;
}