From dd8c6e9f51b488783f5542a57a5d9ee3b02b0d55 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 20 Oct 2023 12:08:18 +0200 Subject: [PATCH 1/7] dmabuf: Add gdk_dmabuf_sanitize() Tries to sanitize the dmabuf to conform to the values expected by Vulkan/EGL which should also be the values expected by Wayland compositors We put these sanitized values into the GdkDmabufTexture, by sanitizing the input from GdkDmabufTextureBuilder, which are controlled by the callers. Things we do here: 1. Disallow any dmabuf format that we do not know. 1. Treat the INVALID modifier the same as LINEAR. 2. Ignore all other modifiers. 3. Try and fix various inconsistencies between V4L and Mesa, like NV12. *** WARNING *** This function is not absolutely perfect, you do not have a perfect dmabuf afterwards. In particular, it doesn't check sizes. --- gdk/gdkdmabuf.c | 73 ++++++++++++++++++++++++++++++++++++++++++ gdk/gdkdmabufprivate.h | 6 ++++ gdk/gdkdmabuftexture.c | 22 +++++++++---- 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/gdk/gdkdmabuf.c b/gdk/gdkdmabuf.c index d31eea3ca8..858599eb64 100644 --- a/gdk/gdkdmabuf.c +++ b/gdk/gdkdmabuf.c @@ -206,4 +206,77 @@ gdk_dmabuf_get_direct_downloader (void) return &downloader; } +/* + * Tries to sanitize the dmabuf to conform to the values expected + * by Vulkan/EGL which should also be the values expected by + * Wayland compositors + * + * We put these sanitized values into the GdkDmabufTexture, by + * sanitizing the input from GdkDmabufTextureBuilder, which are + * controlled by the callers. + * + * Things we do here: + * + * 1. Disallow any dmabuf format that we do not know. + * + * 1. Treat the INVALID modifier the same as LINEAR. + * + * 2. Ignore all other modifiers. + * + * 3. Try and fix various inconsistencies between V4L and Mesa, + * like NV12. + * + * *** WARNING *** + * + * This function is not absolutely perfect, you do not have a + * perfect dmabuf afterwards. + * + * In particular, it doesn't check sizes. + * + * *** WARNING *** + */ +gboolean +gdk_dmabuf_sanitize (GdkDmabuf *dest, + gsize width, + gsize height, + const GdkDmabuf *src, + GError **error) +{ + const GdkDrmFormatInfo *info; + + info = get_drm_format_info (src->fourcc); + + if (info == NULL) + { + g_set_error (error, + GDK_DMABUF_ERROR, GDK_DMABUF_ERROR_UNSUPPORTED_FORMAT, + "Unsupported dmabuf format %.4s", + (char *) &src->fourcc); + return FALSE; + } + + *dest = *src; + + if (src->modifier && src->modifier != DRM_FORMAT_MOD_INVALID) + return TRUE; + + switch (dest->fourcc) + { + case DRM_FORMAT_NV12: + if (dest->n_planes == 1) + { + dest->n_planes = 2; + dest->planes[1].fd = dest->planes[0].fd; + dest->planes[1].stride = dest->planes[0].stride; + dest->planes[1].offset = dest->planes[0].offset + dest->planes[0].stride * height; + } + break; + + default: + break; + } + + return TRUE; +} + #endif /* HAVE_LINUX_DMA_BUF_H */ diff --git a/gdk/gdkdmabufprivate.h b/gdk/gdkdmabufprivate.h index b1c90a9c79..f9de00d23f 100644 --- a/gdk/gdkdmabufprivate.h +++ b/gdk/gdkdmabufprivate.h @@ -41,4 +41,10 @@ struct _GdkDmabufDownloader #ifdef HAVE_LINUX_DMA_BUF_H const GdkDmabufDownloader * gdk_dmabuf_get_direct_downloader (void) G_GNUC_CONST; + +gboolean gdk_dmabuf_sanitize (GdkDmabuf *dest, + gsize width, + gsize height, + const GdkDmabuf *src, + GError **error); #endif diff --git a/gdk/gdkdmabuftexture.c b/gdk/gdkdmabuftexture.c index 74db20c36a..76c69825a6 100644 --- a/gdk/gdkdmabuftexture.c +++ b/gdk/gdkdmabuftexture.c @@ -22,6 +22,7 @@ #include "gdkdisplayprivate.h" #include "gdkdmabufformatsbuilderprivate.h" +#include "gdkdmabufprivate.h" #include "gdktextureprivate.h" #include #include @@ -132,13 +133,22 @@ gdk_dmabuf_texture_new_from_builder (GdkDmabufTextureBuilder *builder, GdkDmabufTexture *self; GdkTexture *update_texture; GdkDisplay *display; - const GdkDmabuf *dmabuf; + GdkDmabuf dmabuf; GdkMemoryFormat format; GError *local_error = NULL; + int width, height; gsize i; display = gdk_dmabuf_texture_builder_get_display (builder); - dmabuf = gdk_dmabuf_texture_builder_get_dmabuf (builder); + width = gdk_dmabuf_texture_builder_get_width (builder); + height = gdk_dmabuf_texture_builder_get_height (builder); + + if (!gdk_dmabuf_sanitize (&dmabuf, + width, + height, + gdk_dmabuf_texture_builder_get_dmabuf (builder), + error)) + return NULL; gdk_display_init_dmabuf (display); @@ -149,7 +159,7 @@ gdk_dmabuf_texture_new_from_builder (GdkDmabufTextureBuilder *builder, if (display->dmabuf_downloaders[i]->supports (display->dmabuf_downloaders[i], display, - dmabuf, + &dmabuf, gdk_dmabuf_texture_builder_get_premultiplied (builder), &format, local_error ? NULL : &local_error)) @@ -163,14 +173,14 @@ gdk_dmabuf_texture_new_from_builder (GdkDmabufTextureBuilder *builder, } self = g_object_new (GDK_TYPE_DMABUF_TEXTURE, - "width", gdk_dmabuf_texture_builder_get_width (builder), - "height", gdk_dmabuf_texture_builder_get_height (builder), + "width", width, + "height", height, NULL); GDK_TEXTURE (self)->format = format; g_set_object (&self->display, display); self->downloader = display->dmabuf_downloaders[i]; - self->dmabuf = *dmabuf; + self->dmabuf = dmabuf; self->destroy = destroy; self->data = data; From 4e47d0d71e44ded7c050d0584afaf774f3efb2a1 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 20 Oct 2023 12:10:28 +0200 Subject: [PATCH 2/7] dmabuf: Ensure the number of planes is sane Make it a programming error when setting planes to 0 and throw a GError if somebody uses more than GDK_DMABUF_MAX_PLANES. --- gdk/gdkdmabuf.c | 9 +++++++++ gdk/gdkdmabuftexturebuilder.c | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/gdk/gdkdmabuf.c b/gdk/gdkdmabuf.c index 858599eb64..a9e14a2c45 100644 --- a/gdk/gdkdmabuf.c +++ b/gdk/gdkdmabuf.c @@ -244,6 +244,15 @@ gdk_dmabuf_sanitize (GdkDmabuf *dest, { const GdkDrmFormatInfo *info; + if (src->n_planes > GDK_DMABUF_MAX_PLANES) + { + g_set_error (error, + GDK_DMABUF_ERROR, GDK_DMABUF_ERROR_UNSUPPORTED_FORMAT, + "GTK only support dmabufs with %u planes, not %u", + GDK_DMABUF_MAX_PLANES, src->n_planes); + return FALSE; + } + info = get_drm_format_info (src->fourcc); if (info == NULL) diff --git a/gdk/gdkdmabuftexturebuilder.c b/gdk/gdkdmabuftexturebuilder.c index 1a143f5d49..98699d5bb7 100644 --- a/gdk/gdkdmabuftexturebuilder.c +++ b/gdk/gdkdmabuftexturebuilder.c @@ -331,7 +331,7 @@ gdk_dmabuf_texture_builder_class_init (GdkDmabufTextureBuilderClass *klass) */ properties[PROP_N_PLANES] = g_param_spec_uint ("n-planes", NULL, NULL, - 0, 4, 0, + 1, GDK_DMABUF_MAX_PLANES, 1, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); /** @@ -366,6 +366,7 @@ gdk_dmabuf_texture_builder_init (GdkDmabufTextureBuilder *self) { self->premultiplied = TRUE; self->display = gdk_display_get_default (); + self->dmabuf.n_planes = 1; for (int i = 0; i < GDK_DMABUF_MAX_PLANES; i++) self->dmabuf.planes[i].fd = -1; @@ -682,6 +683,7 @@ gdk_dmabuf_texture_builder_set_n_planes (GdkDmabufTextureBuilder *self, unsigned int n_planes) { g_return_if_fail (GDK_IS_DMABUF_TEXTURE_BUILDER (self)); + g_return_if_fail (n_planes > 0 && n_planes <= GDK_DMABUF_MAX_PLANES); if (self->dmabuf.n_planes == n_planes) return; @@ -968,7 +970,6 @@ gdk_dmabuf_texture_builder_build (GdkDmabufTextureBuilder *self, g_return_val_if_fail (self->width > 0, NULL); g_return_val_if_fail (self->height > 0, NULL); g_return_val_if_fail (self->dmabuf.fourcc != 0, NULL); - g_return_val_if_fail (self->dmabuf.n_planes > 0, NULL); for (int i = 0; i < self->dmabuf.n_planes; i++) g_return_val_if_fail (self->dmabuf.planes[i].fd != -1 || self->dmabuf.planes[i].offset != 0, NULL); From f29303dea70e7bdd685aadcfb95e720760013ea7 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 20 Oct 2023 12:12:23 +0200 Subject: [PATCH 3/7] dmabuf: Allocate the intermediate buffer properly. We should use the stride from the buffer's format, not from the dmabuf. --- gdk/gdkdmabuf.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gdk/gdkdmabuf.c b/gdk/gdkdmabuf.c index a9e14a2c45..6407f179a5 100644 --- a/gdk/gdkdmabuf.c +++ b/gdk/gdkdmabuf.c @@ -172,16 +172,14 @@ gdk_dmabuf_direct_downloader_download (const GdkDmabufDownloader *downloader, gdk_dmabuf_direct_downloader_do_download (texture, data, stride); else { - const GdkDmabuf *dmabuf; unsigned int width, height; guchar *src_data; gsize src_stride; - dmabuf = gdk_dmabuf_texture_get_dmabuf (GDK_DMABUF_TEXTURE (texture)); width = gdk_texture_get_width (texture); height = gdk_texture_get_height (texture); - src_stride = dmabuf->planes[0].stride; + src_stride = width * gdk_memory_format_bytes_per_pixel (src_format); src_data = g_new (guchar, src_stride * height); gdk_dmabuf_direct_downloader_do_download (texture, src_data, src_stride); From 193d9cd31a3a06fbbde618b52d5ff8b35e209350 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 20 Oct 2023 12:21:34 +0200 Subject: [PATCH 4/7] dmabuf: Improve download 1. Split out the download function from the mmap'ing of the plane(s) 2. Make the code mmap() all the planes 3. Determine size using lseek() as documented by libdrm, instead of trying to guess it from the format. 4. Fix some bugs, like switcheroos of width and height --- gdk/gdkdmabuf.c | 135 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 103 insertions(+), 32 deletions(-) diff --git a/gdk/gdkdmabuf.c b/gdk/gdkdmabuf.c index 6407f179a5..f2e685a849 100644 --- a/gdk/gdkdmabuf.c +++ b/gdk/gdkdmabuf.c @@ -32,23 +32,62 @@ #include typedef struct _GdkDrmFormatInfo GdkDrmFormatInfo; + struct _GdkDrmFormatInfo { guint32 fourcc; GdkMemoryFormat premultiplied_memory_format; GdkMemoryFormat unpremultiplied_memory_format; + void (* download) (guchar *dst_data, + gsize dst_stride, + GdkMemoryFormat dst_format, + gsize width, + gsize height, + const GdkDmabuf *dmabuf, + const guchar *src_datas[GDK_DMABUF_MAX_PLANES], + gsize sizes[GDK_DMABUF_MAX_PLANES]); }; -static GdkDrmFormatInfo supported_formats[] = { - { DRM_FORMAT_ARGB8888, GDK_MEMORY_A8R8G8B8_PREMULTIPLIED, GDK_MEMORY_A8R8G8B8 }, - { DRM_FORMAT_RGBA8888, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, GDK_MEMORY_R8G8B8A8 }, - { DRM_FORMAT_BGRA8888, GDK_MEMORY_B8G8R8A8_PREMULTIPLIED, GDK_MEMORY_B8G8R8A8 }, - { DRM_FORMAT_ABGR16161616F, GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED, GDK_MEMORY_R16G16B16A16_FLOAT }, - { DRM_FORMAT_RGB888, GDK_MEMORY_R8G8B8, GDK_MEMORY_R8G8B8 }, - { DRM_FORMAT_BGR888, GDK_MEMORY_B8G8R8, GDK_MEMORY_B8G8R8 }, +static void +download_memcpy (guchar *dst_data, + gsize dst_stride, + GdkMemoryFormat dst_format, + gsize width, + gsize height, + const GdkDmabuf *dmabuf, + const guchar *src_datas[GDK_DMABUF_MAX_PLANES], + gsize sizes[GDK_DMABUF_MAX_PLANES]) +{ + const guchar *src_data; + gsize src_stride; + guint bpp; + + bpp = gdk_memory_format_bytes_per_pixel (dst_format); + src_stride = dmabuf->planes[0].stride; + src_data = src_datas[0] + dmabuf->planes[0].offset; + g_return_if_fail (sizes[0] >= dmabuf->planes[0].offset + (height - 1) * dst_stride + width * bpp); + + if (dst_stride == src_stride) + memcpy (dst_data, src_data, (height - 1) * dst_stride + width * bpp); + else + { + gsize i; + + for (i = 0; i < height; i++) + memcpy (dst_data + i * dst_stride, src_data + i * src_stride, width * bpp); + } +} + +static const GdkDrmFormatInfo supported_formats[] = { + { DRM_FORMAT_ARGB8888, GDK_MEMORY_A8R8G8B8_PREMULTIPLIED, GDK_MEMORY_A8R8G8B8, download_memcpy }, + { DRM_FORMAT_RGBA8888, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, GDK_MEMORY_R8G8B8A8, download_memcpy }, + { DRM_FORMAT_BGRA8888, GDK_MEMORY_B8G8R8A8_PREMULTIPLIED, GDK_MEMORY_B8G8R8A8, download_memcpy }, + { DRM_FORMAT_ABGR16161616F, GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED, GDK_MEMORY_R16G16B16A16_FLOAT, download_memcpy }, + { DRM_FORMAT_RGB888, GDK_MEMORY_R8G8B8, GDK_MEMORY_R8G8B8, download_memcpy }, + { DRM_FORMAT_BGR888, GDK_MEMORY_B8G8R8, GDK_MEMORY_B8G8R8, download_memcpy }, }; -static GdkDrmFormatInfo * +static const GdkDrmFormatInfo * get_drm_format_info (guint32 fourcc) { for (int i = 0; i < G_N_ELEMENTS (supported_formats); i++) @@ -83,7 +122,7 @@ gdk_dmabuf_direct_downloader_supports (const GdkDmabufDownloader *downloader, GdkMemoryFormat *out_format, GError **error) { - GdkDrmFormatInfo *info; + const GdkDrmFormatInfo *info; info = get_drm_format_info (dmabuf->fourcc); @@ -124,39 +163,71 @@ gdk_dmabuf_direct_downloader_do_download (GdkTexture *texture, guchar *data, gsize stride) { + const GdkDrmFormatInfo *info; const GdkDmabuf *dmabuf; - gsize size; - unsigned int height; - gsize src_stride; - guchar *src_data; - int bpp; + const guchar *src_data[GDK_DMABUF_MAX_PLANES]; + gsize sizes[GDK_DMABUF_MAX_PLANES]; + gsize needs_unmap[GDK_DMABUF_MAX_PLANES] = { FALSE, }; + gsize i, j; GDK_DEBUG (DMABUF, "Using mmap() and memcpy() for downloading a dmabuf"); dmabuf = gdk_dmabuf_texture_get_dmabuf (GDK_DMABUF_TEXTURE (texture)); - height = gdk_texture_get_height (texture); - bpp = gdk_memory_format_bytes_per_pixel (gdk_texture_get_format (texture)); + info = get_drm_format_info (dmabuf->fourcc); - src_stride = dmabuf->planes[0].stride; - size = dmabuf->planes[0].stride * height; - - if (ioctl (dmabuf->planes[0].fd, 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)); - - src_data = mmap (NULL, size, PROT_READ, MAP_SHARED, dmabuf->planes[0].fd, dmabuf->planes[0].offset); - - if (stride == src_stride) - memcpy (data, src_data, size); - else + for (i = 0; i < dmabuf->n_planes; i++) { - for (unsigned int i = 0; i < height; i++) - memcpy (data + i * stride, src_data + i * src_stride, height * bpp); + for (j = 0; j < i; j++) + { + if (dmabuf->planes[i].fd == dmabuf->planes[j].fd) + break; + } + if (j < i) + { + src_data[i] = src_data[j]; + sizes[i] = sizes[j]; + continue; + } + + sizes[i] = lseek (dmabuf->planes[0].fd, 0, SEEK_END); + if (sizes[i] == (off_t) -1) + { + g_warning ("Failed to seek dmabuf: %s", g_strerror (errno)); + goto out; + } + + if (ioctl (dmabuf->planes[i].fd, DMA_BUF_IOCTL_SYNC, &(struct dma_buf_sync) { DMA_BUF_SYNC_START|DMA_BUF_SYNC_READ }) < 0) + g_warning ("Failed to sync dmabuf: %s", g_strerror (errno)); + + src_data[i] = mmap (NULL, sizes[i], PROT_READ, MAP_SHARED, dmabuf->planes[i].fd, dmabuf->planes[i].offset); + if (src_data[i] == NULL) + { + g_warning ("Failed to mmap dmabuf: %s", g_strerror (errno)); + goto out; + } + needs_unmap[i] = TRUE; } - munmap (src_data, size); + info->download (data, + stride, + gdk_texture_get_format (texture), + gdk_texture_get_width (texture), + gdk_texture_get_height (texture), + dmabuf, + src_data, + sizes); - if (ioctl (dmabuf->planes[0].fd, 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)); +out: + for (i = 0; i < dmabuf->n_planes; i++) + { + if (!needs_unmap[i]) + continue; + + munmap (src_data, sizes[i]); + + if (ioctl (dmabuf->planes[i].fd, DMA_BUF_IOCTL_SYNC, &(struct dma_buf_sync) { DMA_BUF_SYNC_END|DMA_BUF_SYNC_READ }) < 0) + g_warning ("Failed to sync dmabuf: %s", g_strerror (errno)); + } } static void From 4586af5876024023640f01d1c928e73b972060cc Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 20 Oct 2023 13:30:52 +0200 Subject: [PATCH 5/7] dmabuf: Add support for NV12 and NV21 This is as-good-as-necessary and doesn't do any fancy colorspace magic or chroma whatevering. That's a task for the future. --- gdk/gdkdmabuf.c | 111 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 103 insertions(+), 8 deletions(-) diff --git a/gdk/gdkdmabuf.c b/gdk/gdkdmabuf.c index f2e685a849..c129ddeaca 100644 --- a/gdk/gdkdmabuf.c +++ b/gdk/gdkdmabuf.c @@ -78,6 +78,106 @@ download_memcpy (guchar *dst_data, } } +typedef struct _YUVCoefficients YUVCoefficients; + +struct _YUVCoefficients +{ + int v_to_r; + int u_to_g; + int v_to_g; + int u_to_b; +}; + +/* multiplied by 65536 */ +//static const YUVCoefficients itu601_narrow = { 104597, -25675, -53279, 132201 }; +static const YUVCoefficients itu601_wide = { 74711, -25864, -38050, 133176 }; + +static inline void +get_uv_values (const YUVCoefficients *coeffs, + guint8 u, + guint8 v, + int *out_r, + int *out_g, + int *out_b) +{ + int u2 = (int) u - 127; + int v2 = (int) v - 127; + *out_r = coeffs->v_to_r * v2; + *out_g = coeffs->u_to_g * u2 + coeffs->v_to_g * v2; + *out_b = coeffs->u_to_b * u2; +} + +static inline void +set_rgb_values (guint8 rgb[3], + guint8 y, + int r, + int g, + int b) +{ + int y2 = y * 65536; + + rgb[0] = CLAMP ((y2 + r) >> 16, 0, 255); + rgb[1] = CLAMP ((y2 + g) >> 16, 0, 255); + rgb[2] = CLAMP ((y2 + b) >> 16, 0, 255); +} + +static void +download_nv12 (guchar *dst_data, + gsize dst_stride, + GdkMemoryFormat dst_format, + gsize width, + gsize height, + const GdkDmabuf *dmabuf, + const guchar *src_data[GDK_DMABUF_MAX_PLANES], + gsize sizes[GDK_DMABUF_MAX_PLANES]) +{ + const guchar *y_data, *uv_data; + gsize x, y, y_stride, uv_stride; + gsize U, V; + + if (dmabuf->fourcc == DRM_FORMAT_NV21) + { + U = 1; V = 0; + } + else + { + U = 0; V = 1; + } + + y_stride = dmabuf->planes[0].stride; + y_data = src_data[0] + dmabuf->planes[0].offset; + g_return_if_fail (sizes[0] >= dmabuf->planes[0].offset + height * y_stride); + uv_stride = dmabuf->planes[1].stride; + uv_data = src_data[1] + dmabuf->planes[1].offset; + g_return_if_fail (sizes[1] >= dmabuf->planes[1].offset + (height + 1) / 2 * uv_stride); + + for (y = 0; y < height; y += 2) + { + guchar *dst2_data = dst_data + dst_stride; + const guchar *y2_data = y_data + y_stride; + + for (x = 0; x < width; x += 2) + { + int r, g, b; + + get_uv_values (&itu601_wide, uv_data[x + U], uv_data[x + V], &r, &g, &b); + + set_rgb_values (&dst_data[3 * x], y_data[x], r, g, b); + if (x + 1 < width) + set_rgb_values (&dst_data[3 * (x + 1)], y_data[x], r, g, b); + if (y + 1 < height) + { + set_rgb_values (&dst2_data[3 * x], y2_data[x], r, g, b); + if (x + 1 < width) + set_rgb_values (&dst2_data[3 * (x + 1)], y2_data[x], r, g, b); + } + } + dst_data += 2 * dst_stride; + y_data += 2 * y_stride; + uv_data += uv_stride; + } +} + static const GdkDrmFormatInfo supported_formats[] = { { DRM_FORMAT_ARGB8888, GDK_MEMORY_A8R8G8B8_PREMULTIPLIED, GDK_MEMORY_A8R8G8B8, download_memcpy }, { DRM_FORMAT_RGBA8888, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, GDK_MEMORY_R8G8B8A8, download_memcpy }, @@ -85,6 +185,9 @@ static const GdkDrmFormatInfo supported_formats[] = { { DRM_FORMAT_ABGR16161616F, GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED, GDK_MEMORY_R16G16B16A16_FLOAT, download_memcpy }, { DRM_FORMAT_RGB888, GDK_MEMORY_R8G8B8, GDK_MEMORY_R8G8B8, download_memcpy }, { DRM_FORMAT_BGR888, GDK_MEMORY_B8G8R8, GDK_MEMORY_B8G8R8, download_memcpy }, + /* YUV formats */ + { DRM_FORMAT_NV12, GDK_MEMORY_R8G8B8, GDK_MEMORY_R8G8B8, download_nv12 }, + { DRM_FORMAT_NV21, GDK_MEMORY_R8G8B8, GDK_MEMORY_R8G8B8, download_nv12 }, }; static const GdkDrmFormatInfo * @@ -144,14 +247,6 @@ gdk_dmabuf_direct_downloader_supports (const GdkDmabufDownloader *downloader, return FALSE; } - if (dmabuf->n_planes > 1) - { - g_set_error_literal (error, - GDK_DMABUF_ERROR, GDK_DMABUF_ERROR_CREATION_FAILED, - "Multiplanar dmabufs are not supported"); - return FALSE; - } - *out_format = premultiplied ? info->premultiplied_memory_format : info->unpremultiplied_memory_format; From 09723d79c6b55e8fc459f8de6e1c46bac6478529 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 20 Oct 2023 13:54:05 +0200 Subject: [PATCH 6/7] dmabuf: Support YUYV and friends Same as the NV12 support, but necessary for the poor people stuck on Intel laptops. --- gdk/gdkdmabuf.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/gdk/gdkdmabuf.c b/gdk/gdkdmabuf.c index c129ddeaca..dcc0912afb 100644 --- a/gdk/gdkdmabuf.c +++ b/gdk/gdkdmabuf.c @@ -178,6 +178,59 @@ download_nv12 (guchar *dst_data, } } +static void +download_yuyv (guchar *dst_data, + gsize dst_stride, + GdkMemoryFormat dst_format, + gsize width, + gsize height, + const GdkDmabuf *dmabuf, + const guchar *src_datas[GDK_DMABUF_MAX_PLANES], + gsize sizes[GDK_DMABUF_MAX_PLANES]) +{ + const guchar *src_data; + gsize x, y, src_stride; + gsize Y1, Y2, U, V; + + switch (dmabuf->fourcc) + { + case DRM_FORMAT_YUYV: + Y1 = 0; U = 1; Y2 = 2; V = 3; + break; + case DRM_FORMAT_YVYU: + Y1 = 0; V = 1; Y2 = 2; U = 3; + break; + case DRM_FORMAT_UYVY: + U = 0; Y1 = 1; V = 2; Y2 = 3; + break; + case DRM_FORMAT_VYUY: + V = 0; Y1 = 1; U = 2; Y2 = 3; + break; + default: + g_assert_not_reached (); + return; + } + + src_stride = dmabuf->planes[0].stride; + src_data = src_datas[0] + dmabuf->planes[0].offset; + g_return_if_fail (sizes[0] >= dmabuf->planes[0].offset + height * src_stride); + + for (y = 0; y < height; y ++) + { + for (x = 0; x < width; x += 2) + { + int r, g, b; + + get_uv_values (&itu601_wide, src_data[2 * x + U], src_data[2 * x + V], &r, &g, &b); + set_rgb_values (&dst_data[3 * x], src_data[2 * x + Y1], r, g, b); + if (x + 1 < width) + set_rgb_values (&dst_data[3 * (x + 1)], src_data[2 * x + Y2], r, g, b); + } + dst_data += dst_stride; + src_data += src_stride; + } +} + static const GdkDrmFormatInfo supported_formats[] = { { DRM_FORMAT_ARGB8888, GDK_MEMORY_A8R8G8B8_PREMULTIPLIED, GDK_MEMORY_A8R8G8B8, download_memcpy }, { DRM_FORMAT_RGBA8888, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, GDK_MEMORY_R8G8B8A8, download_memcpy }, @@ -188,6 +241,10 @@ static const GdkDrmFormatInfo supported_formats[] = { /* YUV formats */ { DRM_FORMAT_NV12, GDK_MEMORY_R8G8B8, GDK_MEMORY_R8G8B8, download_nv12 }, { DRM_FORMAT_NV21, GDK_MEMORY_R8G8B8, GDK_MEMORY_R8G8B8, download_nv12 }, + { DRM_FORMAT_YUYV, GDK_MEMORY_R8G8B8, GDK_MEMORY_R8G8B8, download_yuyv }, + { DRM_FORMAT_YVYU, GDK_MEMORY_R8G8B8, GDK_MEMORY_R8G8B8, download_yuyv }, + { DRM_FORMAT_VYUY, GDK_MEMORY_R8G8B8, GDK_MEMORY_R8G8B8, download_yuyv }, + { DRM_FORMAT_UYVY, GDK_MEMORY_R8G8B8, GDK_MEMORY_R8G8B8, download_yuyv }, }; static const GdkDrmFormatInfo * From 008d9e532724990d6fa0ff1a79dcd44c09c84fdc Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 20 Oct 2023 09:30:01 +0200 Subject: [PATCH 7/7] vulkan: Actually check for GDK_DEBUG=vulkan-disable The dmabuf code would happily init Vulkan even if it was told we don't want it. --- gdk/gdkvulkancontext.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gdk/gdkvulkancontext.c b/gdk/gdkvulkancontext.c index 22d1eb2332..28447deeb2 100644 --- a/gdk/gdkvulkancontext.c +++ b/gdk/gdkvulkancontext.c @@ -1475,6 +1475,13 @@ gdk_display_create_vulkan_instance (GdkDisplay *display, gboolean validate = FALSE, have_debug_report = FALSE; VkResult res; + if (gdk_display_get_debug_flags (display) & GDK_DEBUG_VULKAN_DISABLE) + { + g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE, + _("Vulkan support disabled via GDK_DEBUG")); + return FALSE; + } + if (GDK_DISPLAY_GET_CLASS (display)->vk_extension_name == NULL) { g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED,