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.

Reply via email to