This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch wl/browser-all
in repository enlightenment.
View the commit online.
commit 35592c4a2aa18ed0dbea5bba225ab570f09c2ab5
Author: Cedric BAIL <[email protected]>
AuthorDate: Tue Aug 18 10:05:07 2026 -0600
e_comp_wl - let clients ask for a buffer of one solid colour
wp_single_pixel_buffer_manager_v1. One request, returning a 1x1 wl_buffer
of a solid colour, which a client stretches over a rectangle with a
wp_viewport. Chromium uses it for the solid quads a page is full of -
backgrounds, letterbox bars, the scrim behind a dialog - instead of
allocating and uploading a real buffer for each. Measured binding it
under kwin and weston and unable to under E.
This is the first wl_buffer E hands out rather than receives, and that is
the whole of the work. Every other buffer arrives from a client with
either shm or dmabuf behind it, so the buffer path had exactly two kinds
and a habit of treating "not shm" as "the driver can sample it". A
single-pixel buffer is neither: four bytes of ours, no shared mapping, no
native surface. Four places had to learn the difference:
* e_comp_wl_buffer_get sizes it 1x1 rather than asking EGL about a
buffer that never came from EGL;
* e_pixmap_refresh knows the format without asking, because we packed
it;
* e_pixmap_image_refresh points cp->data straight at the pixel, taking
the busy count but no pool reference and registering no SIGBUS range
- there is no client mapping that can shrink underneath us;
* e_pixmap_native_surface_init and e_pixmap_is_pixels stop calling it a
native EGL surface, which would have handed the driver a resource it
has never seen.
Advertising the global without those would have been worse than not
having it at all: Chromium would have taken it and drawn nothing.
Verified on a screen rather than on the wire, because a 1x1 buffer is the
case where a clean commit proves least - the pixel can fail to arrive, or
arrive with its channels swapped, and both look like success from the
client. A 1x1 buffer scaled to 200x100 by a viewport, read back with the
pixel harness, all four corners the right colour, once for each of red,
green and blue. One colour would not have done: red alone catches an R/B
swap but says nothing about green.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01FtoiXoSKUmZb6Aix6U3GZS
---
src/bin/e_comp_wl.c | 12 +++++
src/bin/e_comp_wl.h | 14 ++++++
src/bin/e_comp_wl_extensions.c | 97 ++++++++++++++++++++++++++++++++++++++
src/bin/e_pixmap.c | 46 +++++++++++++++++-
src/bin/generated/meson.build | 1 +
src/tests/wayland/globals.expected | 1 +
6 files changed, 170 insertions(+), 1 deletion(-)
diff --git a/src/bin/e_comp_wl.c b/src/bin/e_comp_wl.c
index 006702ac5..86871632c 100644
--- a/src/bin/e_comp_wl.c
+++ b/src/bin/e_comp_wl.c
@@ -4805,6 +4805,7 @@ e_comp_wl_buffer_get(struct wl_resource *resource)
struct wl_listener *listener;
struct wl_shm_buffer *shmbuff;
struct linux_dmabuf_buffer *dmabuf;
+ uint32_t *single_pixel;
listener =
wl_resource_get_destroy_listener(resource, _e_comp_wl_buffer_cb_destroy);
@@ -4814,6 +4815,7 @@ e_comp_wl_buffer_get(struct wl_resource *resource)
if (!(buffer = E_NEW(E_Comp_Wl_Buffer, 1))) return NULL;
shmbuff = wl_shm_buffer_get(resource);
dmabuf = linux_dmabuf_buffer_get(resource);
+ single_pixel = e_comp_wl_single_pixel_buffer_get(resource);
if (shmbuff)
{
int32_t stride, bpp;
@@ -4872,6 +4874,15 @@ e_comp_wl_buffer_get(struct wl_resource *resource)
buffer->w = dmabuf->attributes.width;
buffer->h = dmabuf->attributes.height;
}
+ else if (single_pixel)
+ {
+ /* Fixed by the protocol, not read from anywhere - "the width and
+ * height of the buffer are 1". Checked before the EGL branch below,
+ * which would otherwise ask the driver about a buffer it has never
+ * seen and get zeros back. */
+ buffer->w = 1;
+ buffer->h = 1;
+ }
else if (e_comp->gl)
{
e_comp_wl->wl.glapi->evasglQueryWaylandBuffer(e_comp_wl->wl.gl, resource, EGL_WIDTH, &buffer->w);
@@ -4879,6 +4890,7 @@ e_comp_wl_buffer_get(struct wl_resource *resource)
}
buffer->shm_buffer = shmbuff;
buffer->dmabuf_buffer = dmabuf;
+ buffer->single_pixel = single_pixel;
buffer->resource = resource;
wl_signal_init(&buffer->destroy_signal);
diff --git a/src/bin/e_comp_wl.h b/src/bin/e_comp_wl.h
index 4bb6d1dca..0a453a69e 100644
--- a/src/bin/e_comp_wl.h
+++ b/src/bin/e_comp_wl.h
@@ -77,6 +77,12 @@ struct _E_Comp_Wl_Buffer
struct wl_shm_buffer *shm_buffer;
struct wl_shm_pool *pool;
struct linux_dmabuf_buffer *dmabuf_buffer;
+ /* wp_single_pixel_buffer_v1: a 1x1 buffer the *compositor* allocated, not
+ * the client. It is neither shm nor dmabuf and there is no shared mapping
+ * behind it - these four bytes are the whole image, and they belong to the
+ * wl_buffer resource rather than to us. Kept as its own field so every
+ * "is this shm?" test in the buffer path keeps meaning what it says. */
+ uint32_t *single_pixel;
E_Pixmap *discarding_pixmap;
int32_t w, h;
uint32_t busy;
@@ -186,6 +192,10 @@ typedef struct E_Comp_Wl_Extension_Data
{
struct wl_global *global;
} wp_viewporter;
+ struct
+ {
+ struct wl_global *global;
+ } wp_single_pixel_buffer_manager_v1;
struct
{
struct wl_global *global;
@@ -625,6 +635,10 @@ E_API void e_comp_wl_idle_inhibit_add(void);
E_API void e_comp_wl_idle_inhibit_del(void);
E_API Eina_Bool e_comp_wl_idle_inhibited_get(void);
E_API Eina_Bool e_comp_wl_dmabuf_importable(void);
+/* The four bytes behind a wp_single_pixel_buffer_v1, or NULL if this wl_buffer
+ * is somebody else's kind. Asked by e_comp_wl_buffer_get, which has to tell
+ * the compositor's own buffers from shm and dmabuf before it can size them. */
+EINTERN uint32_t *e_comp_wl_single_pixel_buffer_get(struct wl_resource *resource);
E_API Eina_Bool e_comp_wl_shortcuts_inhibited_get(void);
E_API void e_comp_wl_screensaver_activate(void);
E_API void e_comp_wl_screensaver_inhibit(Eina_Bool inhibit);
diff --git a/src/bin/e_comp_wl_extensions.c b/src/bin/e_comp_wl_extensions.c
index 64a3bbf99..a4679d02e 100644
--- a/src/bin/e_comp_wl_extensions.c
+++ b/src/bin/e_comp_wl_extensions.c
@@ -14,6 +14,7 @@
#include "idle-inhibit-unstable-v1-server-protocol.h"
#include "keyboard-shortcuts-inhibit-unstable-v1-server-protocol.h"
#include "presentation-time-server-protocol.h"
+#include "single-pixel-buffer-v1-server-protocol.h"
#include <time.h>
@@ -1945,6 +1946,100 @@ static const struct wp_presentation_interface _e_wp_presentation_interface =
_e_wp_presentation_cb_feedback,
};
+/* wp_single_pixel_buffer_manager_v1.
+ *
+ * One request, returning a 1x1 wl_buffer of a solid colour. Chromium uses it
+ * for the solid quads a page is full of - backgrounds, letterbox bars, the
+ * scrim behind a dialog - rather than allocating and uploading a real buffer
+ * for each, then stretches it with a wp_viewport. The protocol says as much:
+ * a compositor offering this should offer viewporter, which E does (E-18).
+ *
+ * It is the first buffer E hands out rather than receives. Every other
+ * wl_buffer arrives from a client with either shm or dmabuf behind it; this
+ * one has four bytes of ours behind it and no shared mapping at all, so the
+ * buffer path has to be able to say which kind it is holding. That is the
+ * whole of the work here, and it is why this could not be an add-a-global.
+ */
+
+static void
+_e_wp_single_pixel_buffer_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_buffer_interface _e_single_pixel_buffer_interface =
+{
+ _e_wp_single_pixel_buffer_cb_destroy,
+};
+
+static void
+_e_single_pixel_buffer_res_destroy(struct wl_resource *resource)
+{
+ uint32_t *pixel = wl_resource_get_user_data(resource);
+
+ free(pixel);
+}
+
+EINTERN uint32_t *
+e_comp_wl_single_pixel_buffer_get(struct wl_resource *resource)
+{
+ /* Identity by implementation, not by a flag we set somewhere else: this is
+ * the only way to be sure a wl_buffer is one of ours before dereferencing
+ * what its user_data points at. */
+ if (!wl_resource_instance_of(resource, &wl_buffer_interface,
+ &_e_single_pixel_buffer_interface))
+ return NULL;
+ return wl_resource_get_user_data(resource);
+}
+
+static void
+_e_wp_single_pixel_buffer_manager_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+_e_wp_single_pixel_buffer_manager_cb_create_u32_rgba_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id, uint32_t r, uint32_t g, uint32_t b, uint32_t a)
+{
+ struct wl_resource *res;
+ uint32_t *pixel;
+
+ pixel = malloc(sizeof(*pixel));
+ if (!pixel)
+ {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ /* The wire carries each channel as a 32-bit value spanning the full range,
+ * so the top byte is the 8-bit sample - a shift, not a division. Packed
+ * ARGB little-endian, which is what WL_SHM_FORMAT_ARGB8888 means and what
+ * every other buffer reaching evas through this path already is.
+ *
+ * The protocol says these are premultiplied unless another extension says
+ * otherwise, and evas expects premultiplied too, so the values pass
+ * through untouched. */
+ *pixel = ((a >> 24) << 24) | ((r >> 24) << 16) |
+ ((g >> 24) << 8) | (b >> 24);
+
+ res = wl_resource_create(client, &wl_buffer_interface, 1, id);
+ if (!res)
+ {
+ free(pixel);
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ wl_resource_set_implementation(res, &_e_single_pixel_buffer_interface, pixel,
+ _e_single_pixel_buffer_res_destroy);
+}
+
+static const struct wp_single_pixel_buffer_manager_v1_interface _e_wp_single_pixel_buffer_manager_v1_interface =
+{
+ _e_wp_single_pixel_buffer_manager_cb_destroy,
+ _e_wp_single_pixel_buffer_manager_cb_create_u32_rgba_buffer,
+};
+
#define GLOBAL_BIND_CB(NAME, IFACE, ...) \
static void \
_e_comp_wl_##NAME##_cb_bind(struct wl_client *client, void *data EINA_UNUSED, uint32_t version, uint32_t id) \
@@ -1980,6 +2075,7 @@ GLOBAL_BIND_CB(zwp_keyboard_shortcuts_inhibit_manager_v1, zwp_keyboard_shortcuts
GLOBAL_BIND_CB(wp_presentation, wp_presentation_interface,
wp_presentation_send_clock_id(res, CLOCK_MONOTONIC);
)
+GLOBAL_BIND_CB(wp_single_pixel_buffer_manager_v1, wp_single_pixel_buffer_manager_v1_interface)
GLOBAL_BIND_CB(action_route, action_route_interface,
e_binding_key_list_cb = _action_route_key_list_cb;
key_bindings = eina_hash_string_superfast_new(NULL);
@@ -2081,6 +2177,7 @@ e_comp_wl_extensions_init(void)
GLOBAL_CREATE_OR_RETURN(xdg_activation_v1, xdg_activation_v1_interface, 1);
GLOBAL_CREATE_OR_RETURN(wp_viewporter, wp_viewporter_interface, 1);
GLOBAL_CREATE_OR_RETURN(wp_presentation, wp_presentation_interface, 1);
+ GLOBAL_CREATE_OR_RETURN(wp_single_pixel_buffer_manager_v1, wp_single_pixel_buffer_manager_v1_interface, 1);
GLOBAL_CREATE_OR_RETURN(zxdg_output_manager_v1, zxdg_output_manager_v1_interface, 3);
GLOBAL_CREATE_OR_RETURN(wp_fractional_scale_manager_v1, wp_fractional_scale_manager_v1_interface, 1);
GLOBAL_CREATE_OR_RETURN(zwp_idle_inhibit_manager_v1, zwp_idle_inhibit_manager_v1_interface, 1);
diff --git a/src/bin/e_pixmap.c b/src/bin/e_pixmap.c
index 84e95f850..d7681e44c 100644
--- a/src/bin/e_pixmap.c
+++ b/src/bin/e_pixmap.c
@@ -739,6 +739,13 @@ e_pixmap_refresh(E_Pixmap *cp)
format = wl_shm_buffer_get_format(buffer->shm_buffer);
else if (buffer->dmabuf_buffer)
format = buffer->dmabuf_buffer->attributes.format;
+ else if (buffer->single_pixel)
+ /* We packed it, so we know it: premultiplied ARGB8888, which is
+ * what wp_single_pixel_buffer_v1 specifies and what this path
+ * already carries for every shm client. Ahead of the EGL branch,
+ * which would otherwise ask the driver about a buffer that never
+ * came from it. */
+ format = WL_SHM_FORMAT_ARGB8888;
else if (e_comp_wl->wl.glapi)
{
e_comp_wl->wl.glapi->evasglQueryWaylandBuffer
@@ -931,6 +938,11 @@ e_pixmap_is_pixels(E_Pixmap *cp)
case E_PIXMAP_TYPE_WL:
if (!cp->buffer) return EINA_TRUE;
if (cp->buffer->shm_buffer) return EINA_TRUE;
+ /* Four bytes in our own memory are as much "pixels" as a mapped
+ * shm region is. Both callers want the same answer for the same
+ * reason: no native surface to hand evas, and a buffer transform
+ * is evas's to apply rather than the driver's. */
+ if (cp->buffer->single_pixel) return EINA_TRUE;
return EINA_FALSE;
#endif
default:
@@ -1027,7 +1039,13 @@ e_pixmap_native_surface_init(E_Pixmap *cp, Evas_Native_Surface *ns)
}
ret = EINA_TRUE;
}
- else if (!cp->buffer->shm_buffer)
+ /* "not shm" has meant "the driver can sample it" everywhere in this
+ * file, and a single-pixel buffer is the first thing that is neither.
+ * Left to the branch below it would be handed to evas as a legacy
+ * EGL buffer and the driver asked to make an image from a resource it
+ * has never seen. It has no native surface at all: EINA_FALSE sends
+ * it down the same path as shm, which reads e_pixmap_image_data_get. */
+ else if ((!cp->buffer->shm_buffer) && (!cp->buffer->single_pixel))
{
ns->type = EVAS_NATIVE_SURFACE_WL;
ns->data.wl.legacy_buffer = cp->buffer->resource;
@@ -1164,6 +1182,32 @@ e_pixmap_image_refresh(E_Pixmap *cp)
*/
if (!cp->buffer) return EINA_FALSE;
+ /* A single-pixel buffer, before the shm test below sends it down
+ * the "not shm, so the renderer will sample it natively" path -
+ * which is true of dmabuf and EGL buffers and false of this one.
+ * Nothing samples it: the four bytes are the image, and evas
+ * reads them straight out of the wl_buffer's user data.
+ *
+ * No pool to reference and no SIGBUS range to register, because
+ * the memory is ours rather than a mapping a client can shrink
+ * under us - the two things the shm path below exists to do. The
+ * busy count is still taken, so the resource cannot be destroyed
+ * while evas is pointed at it. */
+ if (cp->buffer->single_pixel)
+ {
+ cp->held_buffer = cp->buffer;
+ cp->held_buffer->busy++;
+ cp->data = ""
+
+ cp->held_buffer_destroy_listener.notify =
+ _e_pixmap_cb_held_buffer_destroy;
+ if (cp->held_buffer_destroy_listener.link.next)
+ wl_list_remove(&cp->held_buffer_destroy_listener.link);
+ wl_signal_add(&cp->held_buffer->destroy_signal,
+ &cp->held_buffer_destroy_listener);
+ return EINA_TRUE;
+ }
+
if (!cp->buffer->shm_buffer) return EINA_TRUE;
cp->held_buffer = cp->buffer;
diff --git a/src/bin/generated/meson.build b/src/bin/generated/meson.build
index 256ffd61c..49af589c1 100644
--- a/src/bin/generated/meson.build
+++ b/src/bin/generated/meson.build
@@ -8,6 +8,7 @@ protos = [
'@0@/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'.format(dir_wayland_protocols),
'@0@/staging/xdg-activation/xdg-activation-v1.xml'.format(dir_wayland_protocols),
'@0@/stable/viewporter/viewporter.xml'.format(dir_wayland_protocols),
+ '@0@/staging/single-pixel-buffer/single-pixel-buffer-v1.xml'.format(dir_wayland_protocols),
'@0@/stable/presentation-time/presentation-time.xml'.format(dir_wayland_protocols),
'@0@/unstable/xdg-output/xdg-output-unstable-v1.xml'.format(dir_wayland_protocols),
'@0@/unstable/primary-selection/primary-selection-unstable-v1.xml'.format(dir_wayland_protocols),
diff --git a/src/tests/wayland/globals.expected b/src/tests/wayland/globals.expected
index 110ad0d2d..67008e907 100644
--- a/src/tests/wayland/globals.expected
+++ b/src/tests/wayland/globals.expected
@@ -9,6 +9,7 @@ wl_shm >=1
wl_subcompositor 1
wp_fractional_scale_manager_v1 1
wp_presentation 1
+wp_single_pixel_buffer_manager_v1 1
wp_viewporter 1
xdg_activation_v1 1
xdg_wm_base 6
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.