This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch wl/x11-output
in repository enlightenment.
View the commit online.
commit d6bf10addaec3c49f91b7ca73136885889ec942d
Author: Cedric BAIL <[email protected]>
AuthorDate: Thu Aug 13 21:06:13 2026 -0600
wayland - only offer dmabuf formats we can actually put on screen
What we advertised, what we accepted, and whether we thought a buffer had
alpha were three separate answers, and they did not agree.
The advertised list came straight from eglQueryDmaBufFormatsEXT - 52 entries
on panfrost here, including NV12, P010, AYUV, YUYV, the 16-bit ones. But
"EGL can import it" is not "we can show it". A modifier flagged external_only
yields an EGLImage that has to be sampled through GL_TEXTURE_EXTERNAL_OES,
and evas binds a native surface to GL_TEXTURE_2D. E never asked for the
flag - it passed NULL where the query returns it - so it offered clients
layouts it would then render as garbage, with nothing logged.
Nothing checked the format on the way back in either, so a client could send
anything at all; and a third list, the switch in _e_pixmap_refresh(), decided
alpha, so a format could be advertised, accepted, and then called opaque.
All three now read one table, built once from the driver and filtered twice:
- modifiers the driver marks external_only are dropped, and a format left
with none of its modifiers usable is dropped with them. That is what
prunes the planar and YUV end of the list, by construction, rather than
by us guessing which fourccs are planar.
- a format we cannot answer alpha for is not offered, because calling a
buffer opaque costs the client its alpha channel. If we cannot say, we
should not have offered it.
params_create_common() then holds clients to exactly that list and answers
INVALID_FORMAT otherwise - the protocol has no separate modifier error - so
a client that believed the advertisement cannot be refused, and one that
invented a layout cannot get through.
52 formats advertised before, 13 after. Brave is unaffected: it asks for
AB24, which survives the filter, and still runs with no rejections, no
import failures and no wl_shm fallback.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01FtoiXoSKUmZb6Aix6U3GZS
---
src/bin/e_comp_wl_dmabuf.c | 19 +++
src/bin/e_pixmap.c | 326 ++++++++++++++++++++++++++++++++++++---------
src/bin/e_pixmap.h | 2 +
3 files changed, 287 insertions(+), 60 deletions(-)
diff --git a/src/bin/e_comp_wl_dmabuf.c b/src/bin/e_comp_wl_dmabuf.c
index 95bb10ed7..c983848df 100644
--- a/src/bin/e_comp_wl_dmabuf.c
+++ b/src/bin/e_comp_wl_dmabuf.c
@@ -288,6 +288,25 @@ params_create_common(struct wl_client *client, struct wl_resource *params_resour
}
}
+ /* Hold the client to what we advertised. Same table the format and
+ * modifier events were sent from, so a client that believed us cannot be
+ * turned away here - and one that picked a layout we never offered is
+ * told so plainly, rather than having it imported into a texture we
+ * cannot sample and shown as garbage.
+ *
+ * INVALID_FORMAT is the protocol's own word for both halves of this;
+ * zwp_linux_buffer_params_v1 has no separate modifier error. */
+ if (!e_pixmap_dmabuf_format_supported(buffer->attributes.format,
+ buffer->attributes.modifier[0]))
+ {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
+ "format %.4s modifier %#" PRIx64 " was never "
+ "advertised", (const char *)&buffer->attributes.format,
+ buffer->attributes.modifier[0]);
+ goto err_out;
+ }
+
/* XXX: Some additional sanity checks could be done with respect
* to the fourcc format. A centralized collection (kernel or
* libdrm) would be useful to avoid code duplication for these
diff --git a/src/bin/e_pixmap.c b/src/bin/e_pixmap.c
index bd0787090..87f0a6799 100644
--- a/src/bin/e_pixmap.c
+++ b/src/bin/e_pixmap.c
@@ -12,6 +12,9 @@
# define DRM_FORMAT_ARGB8888 0x34325241
# define DRM_FORMAT_XRGB8888 0x34325258
# endif
+# ifndef DRM_FORMAT_MOD_LINEAR
+# define DRM_FORMAT_MOD_LINEAR 0ULL
+# endif
#endif
#ifndef HAVE_WAYLAND_ONLY
# include "e_comp_x.h"
@@ -617,6 +620,7 @@ e_pixmap_refresh(E_Pixmap *cp)
#ifdef HAVE_WAYLAND
{
E_Comp_Wl_Buffer *buffer = cp->buffer;
+ Eina_Bool argb, known = EINA_FALSE;
int format;
cp->w = cp->h = 0;
@@ -630,7 +634,14 @@ e_pixmap_refresh(E_Pixmap *cp)
if (buffer->shm_buffer)
format = wl_shm_buffer_get_format(buffer->shm_buffer);
else if (buffer->dmabuf_buffer)
- format = buffer->dmabuf_buffer->attributes.format;
+ {
+ /* Read off the same table the format was advertised from,
+ * so a buffer we agreed to take always has an alpha answer
+ * and the two cannot drift apart. */
+ format = buffer->dmabuf_buffer->attributes.format;
+ known = e_pixmap_dmabuf_format_is_argb(format, &argb);
+ if (known) cp->image_argb = argb;
+ }
else if (e_comp_wl->wl.glapi)
{
e_comp_wl->wl.glapi->evasglQueryWaylandBuffer
@@ -649,7 +660,11 @@ e_pixmap_refresh(E_Pixmap *cp)
* this list is called opaque, and evas then allocates an RGB
* texture and drops the alpha channel on the floor, so a client's
* transparent shadow margin composites as solid black - a thick
- * dark border around the window and around every menu it pops. */
+ * dark border around the window and around every menu it pops.
+ *
+ * A dmabuf whose format the table already answered for skips
+ * this; only shm and the EGL query land here. */
+ if (!known)
switch (format)
{
case DRM_FORMAT_ARGB8888:
@@ -1399,75 +1414,266 @@ if (weston_check_egl_extension(extensions,
extern Eina_Bool e_comp_wl_query_dmabuf_formats(int max_formats, int *formats, int *num_formats);
extern Eina_Bool e_comp_wl_query_dmabuf_modifiers(int format, int max_modifiers, uint64_t *modifiers, Eina_Bool *external_only, int *num_modifiers);
+/* What we tell clients we take, what we agree to take, and whether we think
+ * a buffer has alpha all have to be the same answer, so they are all read
+ * off one table built once from the driver.
+ *
+ * Two things get filtered out of what EGL says it can import, because
+ * importing is not the same as being able to show it:
+ *
+ * - modifiers flagged external_only. Those hand back an EGLImage that has
+ * to be sampled through GL_TEXTURE_EXTERNAL_OES, and evas binds a native
+ * surface to GL_TEXTURE_2D. Advertising them is how a client ends up
+ * told yes and then shown garbage, with nothing logged anywhere. This is
+ * what prunes the NV12/P010/AYUV/YUYV end of the list on its own, rather
+ * than by us keeping a list of formats we hope are planar.
+ *
+ * - formats we cannot answer e_pixmap_image_is_argb() for. Calling a
+ * buffer opaque when it is not costs the client its alpha channel, so a
+ * format whose alpha we do not know is a format we should not have
+ * offered.
+ */
+typedef struct
+{
+ int format;
+ uint64_t *modifiers;
+ int num_modifiers;
+ Eina_Bool argb E_BITFIELD;
+} E_Dmabuf_Format;
+
+static E_Dmabuf_Format *_dmabuf_formats = NULL;
+static int _dmabuf_formats_num = -1;
+
+static Eina_Bool
+_e_pixmap_dmabuf_format_argb_get(int format, Eina_Bool *argb)
+{
+ switch (format)
+ {
+ /* Past its first two entries wl_shm reuses the DRM fourccs verbatim,
+ * so one name covers both spellings. */
+ case WL_SHM_FORMAT_ABGR8888:
+ case WL_SHM_FORMAT_RGBA8888:
+ case WL_SHM_FORMAT_BGRA8888:
+ case WL_SHM_FORMAT_ARGB2101010:
+ case WL_SHM_FORMAT_ABGR2101010:
+ case WL_SHM_FORMAT_RGBA1010102:
+ case WL_SHM_FORMAT_BGRA1010102:
+ case WL_SHM_FORMAT_ARGB4444:
+ case WL_SHM_FORMAT_ABGR4444:
+ case WL_SHM_FORMAT_ARGB1555:
+ case WL_SHM_FORMAT_ABGR1555:
+ case DRM_FORMAT_ARGB8888:
+ *argb = EINA_TRUE;
+ return EINA_TRUE;
+ case WL_SHM_FORMAT_XBGR8888:
+ case WL_SHM_FORMAT_RGBX8888:
+ case WL_SHM_FORMAT_BGRX8888:
+ case WL_SHM_FORMAT_XRGB2101010:
+ case WL_SHM_FORMAT_XBGR2101010:
+ case WL_SHM_FORMAT_RGBX1010102:
+ case WL_SHM_FORMAT_BGRX1010102:
+ case WL_SHM_FORMAT_XRGB4444:
+ case WL_SHM_FORMAT_XBGR4444:
+ case WL_SHM_FORMAT_XRGB1555:
+ case WL_SHM_FORMAT_XBGR1555:
+ case WL_SHM_FORMAT_RGB565:
+ case WL_SHM_FORMAT_BGR565:
+ case WL_SHM_FORMAT_RGB888:
+ case WL_SHM_FORMAT_BGR888:
+ case DRM_FORMAT_XRGB8888:
+ *argb = EINA_FALSE;
+ return EINA_TRUE;
+ default:
+ /* Planar, YUV, anything we have not thought about. */
+ return EINA_FALSE;
+ }
+}
+
+/* Modifiers for one format, minus the external_only ones. Answers how many
+ * usable modifiers there are; ok is set false only if the driver refused to
+ * answer at all, which is different from answering "none". */
+static int
+_e_pixmap_dmabuf_modifiers_usable(int format, uint64_t **out, Eina_Bool *ok)
+{
+ uint64_t *mods;
+ Eina_Bool *external;
+ int num = 0, i, keep = 0;
+
+ *out = NULL;
+ *ok = EINA_FALSE;
+
+ if (!e_comp_wl_query_dmabuf_modifiers(format, 0, NULL, NULL, &num))
+ return 0;
+ *ok = EINA_TRUE;
+ if (num <= 0) return 0;
+
+ mods = calloc(num, sizeof(uint64_t));
+ external = calloc(num, sizeof(Eina_Bool));
+ if ((!mods) || (!external))
+ {
+ free(mods);
+ free(external);
+ *ok = EINA_FALSE;
+ return 0;
+ }
+ if (!e_comp_wl_query_dmabuf_modifiers(format, num, mods, external, &num))
+ {
+ free(mods);
+ free(external);
+ *ok = EINA_FALSE;
+ return 0;
+ }
+ for (i = 0; i < num; i++)
+ {
+ if (external[i]) continue;
+ mods[keep++] = mods[i];
+ }
+ free(external);
+ if (!keep)
+ {
+ free(mods);
+ return 0;
+ }
+ *out = mods;
+ return keep;
+}
+
+static void
+_e_pixmap_dmabuf_formats_build(void)
+{
+ static const int fallback_formats[] =
+ { DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888 };
+ int *formats = NULL;
+ int num = 0, i, keep = 0;
+
+ if (_dmabuf_formats_num >= 0) return;
+ _dmabuf_formats_num = 0;
+
+ if ((!e_comp_wl_query_dmabuf_formats(0, NULL, &num)) || (num <= 0))
+ {
+ /* No EGL_EXT_image_dma_buf_import_modifiers to ask. The two packed
+ * formats every driver that can import at all can import. */
+ num = EINA_C_ARRAY_LENGTH(fallback_formats);
+ formats = calloc(num, sizeof(int));
+ if (!formats) return;
+ memcpy(formats, fallback_formats, num * sizeof(int));
+ }
+ else
+ {
+ formats = calloc(num, sizeof(int));
+ if (!formats) return;
+ if (!e_comp_wl_query_dmabuf_formats(num, formats, &num))
+ {
+ free(formats);
+ return;
+ }
+ }
+
+ _dmabuf_formats = calloc(num, sizeof(E_Dmabuf_Format));
+ if (!_dmabuf_formats)
+ {
+ free(formats);
+ return;
+ }
+
+ for (i = 0; i < num; i++)
+ {
+ E_Dmabuf_Format *f = &_dmabuf_formats[keep];
+ Eina_Bool argb, ok;
+ uint64_t *mods = NULL;
+ int nmods;
+
+ if (!_e_pixmap_dmabuf_format_argb_get(formats[i], &argb)) continue;
+
+ nmods = _e_pixmap_dmabuf_modifiers_usable(formats[i], &mods, &ok);
+ /* Answered, and every modifier it has needs external sampling. */
+ if (ok && (!nmods)) continue;
+
+ f->format = formats[i];
+ f->modifiers = mods;
+ f->num_modifiers = nmods;
+ f->argb = argb;
+ keep++;
+ }
+ free(formats);
+ _dmabuf_formats_num = keep;
+}
+
+static const E_Dmabuf_Format *
+_e_pixmap_dmabuf_format_find(int format)
+{
+ int i;
+
+ _e_pixmap_dmabuf_formats_build();
+ for (i = 0; i < _dmabuf_formats_num; i++)
+ if (_dmabuf_formats[i].format == format) return &_dmabuf_formats[i];
+ return NULL;
+}
+
+/* Would we accept a buffer in this layout? Same table we advertised from,
+ * so a client that believed us cannot be refused here, and one that made a
+ * format up cannot get through. */
+E_API Eina_Bool
+e_pixmap_dmabuf_format_supported(int format, uint64_t modifier)
+{
+ const E_Dmabuf_Format *f;
+ int i;
+
+ f = _e_pixmap_dmabuf_format_find(format);
+ if (!f) return EINA_FALSE;
+ /* Nothing known about its modifiers, so only the implicit layout was
+ * ever offered for it. */
+ if (!f->num_modifiers)
+ return (modifier == DRM_FORMAT_MOD_INVALID) ||
+ (modifier == DRM_FORMAT_MOD_LINEAR);
+ for (i = 0; i < f->num_modifiers; i++)
+ if (f->modifiers[i] == modifier) return EINA_TRUE;
+ /* Advertised with explicit modifiers; the implicit layout stays legal,
+ * a client may not know what the buffer it was handed actually uses. */
+ return (modifier == DRM_FORMAT_MOD_INVALID);
+}
+
+E_API Eina_Bool
+e_pixmap_dmabuf_format_is_argb(int format, Eina_Bool *argb)
+{
+ const E_Dmabuf_Format *f;
+
+ f = _e_pixmap_dmabuf_format_find(format);
+ if (!f) return EINA_FALSE;
+ *argb = f->argb;
+ return EINA_TRUE;
+}
+
E_API Eina_Bool
e_pixmap_dmabuf_formats_query(int **formats, int *num_formats)
{
- static const int fallback_formats[] =
- {
- DRM_FORMAT_ARGB8888,
- DRM_FORMAT_XRGB8888,
-// support later ...
-// DRM_FORMAT_YUYV,
-// DRM_FORMAT_NV12,
-// DRM_FORMAT_YUV420,
-// DRM_FORMAT_YUV444,
-// DRM_FORMAT_XYUV8888,
- };
- Eina_Bool fallback = EINA_FALSE;
- int num = 0;
+ int i;
- if (!e_comp_wl_query_dmabuf_formats(0, NULL, &num))
- {
- num = 2;
- fallback = EINA_TRUE;
- }
- *formats = calloc(num, sizeof(int));
- if (!(*formats))
- {
- *num_formats = 0;
- return EINA_FALSE;
- }
- if (fallback)
- {
- memcpy(*formats, fallback_formats, num * sizeof(int));
- *num_formats = num;
- return EINA_TRUE;
- }
- if (!e_comp_wl_query_dmabuf_formats(num, *formats, &num))
- {
- *num_formats = 0;
- free(*formats);
- return EINA_FALSE;
- }
- *num_formats = num;
+ _e_pixmap_dmabuf_formats_build();
+ *num_formats = 0;
+ if (!_dmabuf_formats_num) return EINA_FALSE;
+
+ *formats = calloc(_dmabuf_formats_num, sizeof(int));
+ if (!(*formats)) return EINA_FALSE;
+ for (i = 0; i < _dmabuf_formats_num; i++)
+ (*formats)[i] = _dmabuf_formats[i].format;
+ *num_formats = _dmabuf_formats_num;
return EINA_TRUE;
}
E_API Eina_Bool
e_pixmap_dmabuf_modifiers_query(int format, uint64_t **modifiers, int *num_modifiers)
{
- int num;
+ const E_Dmabuf_Format *f;
- if (!e_comp_wl_query_dmabuf_modifiers(format, 0, NULL, NULL, &num) ||
- (num == 0))
- {
- *num_modifiers = 0;
- return EINA_FALSE;
- }
- *modifiers = calloc(num, sizeof(uint64_t));
- if (!(*modifiers))
- {
- *num_modifiers = 0;
- return EINA_FALSE;
- }
- if (!e_comp_wl_query_dmabuf_modifiers
- (format, num, *modifiers, NULL, &num))
- {
- *num_modifiers = 0;
- free(*modifiers);
- return EINA_FALSE;
- }
- *num_modifiers = num;
+ *num_modifiers = 0;
+ f = _e_pixmap_dmabuf_format_find(format);
+ if ((!f) || (!f->num_modifiers)) return EINA_FALSE;
+
+ *modifiers = calloc(f->num_modifiers, sizeof(uint64_t));
+ if (!(*modifiers)) return EINA_FALSE;
+ memcpy(*modifiers, f->modifiers, f->num_modifiers * sizeof(uint64_t));
+ *num_modifiers = f->num_modifiers;
return EINA_TRUE;
}
diff --git a/src/bin/e_pixmap.h b/src/bin/e_pixmap.h
index afd5065a3..bc401f30d 100644
--- a/src/bin/e_pixmap.h
+++ b/src/bin/e_pixmap.h
@@ -59,6 +59,8 @@ E_API void e_pixmap_pre_render(void);
E_API Eina_Bool e_pixmap_dmabuf_test(struct linux_dmabuf_buffer *);
E_API Eina_Bool e_pixmap_dmabuf_formats_query(int **formats, int *num_formats);
E_API Eina_Bool e_pixmap_dmabuf_modifiers_query(int format, uint64_t **modifiers, int *num_modifiers);
+E_API Eina_Bool e_pixmap_dmabuf_format_supported(int format, uint64_t modifier);
+E_API Eina_Bool e_pixmap_dmabuf_format_is_argb(int format, Eina_Bool *argb);
#endif
static inline Eina_Bool
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.