From b7e1c650dc80c2c1cf5410612fac954be85bed5b Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Wed, 12 Aug 2026 10:40:05 +1000 Subject: [PATCH] Drop redundant manual popup compositing Popup/menu surfaces are regular wlr_scene_xdg_surface nodes under their parent's tree, so wlroots' scene renderer already composites them into the output buffer like any other surface. The host-side second draw (composite_popups/popup_draw_cb + popup_tex) was a redundant double-blend that predated the scene nodes; with it removed, menus and sub-menus render correctly through the scene path. Keep the popup scene-node wiring, unconstrain, and commit/reposition/destroy listeners. --- tabbed-wlroots.c | 82 +++--------------------------------------------- 1 file changed, 5 insertions(+), 77 deletions(-) diff --git a/tabbed-wlroots.c b/tabbed-wlroots.c index c4714ba..9916ebc 100644 --- a/tabbed-wlroots.c +++ b/tabbed-wlroots.c @@ -151,7 +151,6 @@ struct tabbed_host { GLuint gl_program; GLint gl_pos_loc, gl_uv_loc, gl_tex_loc; GLuint import_tex; - GLuint popup_tex; /* Present gating: at most one buffer aimed at the host compositor at a time, paced by the host surface's wl_callback. */ struct wl_callback *frame_cb; @@ -442,14 +441,6 @@ static bool gl_setup(void) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, 0); - - glGenTextures(1, &host.popup_tex); - glBindTexture(GL_TEXTURE_2D, host.popup_tex); - 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); - glBindTexture(GL_TEXTURE_2D, 0); return true; } @@ -907,17 +898,11 @@ static int app_index(struct tabbed_app *app) { struct tabbed_popup { struct wlr_xdg_surface *xdg_surface; struct wlr_scene_tree *scene; - struct wl_list link; struct wl_listener commit; struct wl_listener destroy; struct wl_listener reposition; }; -/* Popups are also drawn by us (host-side) for the presented frame, because - wlroots' own premultiplied-alpha rendering of these ARGB menus produces - nothing in this nested EGL pipeline. This list tracks the live ones. */ -static struct wl_list popups = { &popups, &popups }; - /* Enough of the host window for popups to be positioned within; open menus may exceed the toplevel but we keep them inside the visible area. */ static void popup_usable_box(struct wlr_box *box) { @@ -932,10 +917,11 @@ static void popup_usable_box(struct wlr_box *box) { } } -/* Menu popups (e.g. galculator's) submit ARGB buffers that wlroots renders - with premultiplied-alpha blending, which produces nothing in this nested - EGL pipeline. We therefore draw them ourselves into the presented frame - (see composite_popups in blit_scene_buffer). */ +/* Popups (menus, tooltips) are regular wlr_scene_xdg_surface nodes under + their parent's tree, so wlroots' scene renderer composites them into the + output buffer like any other surface. We only need to make sure the client + gets its initial configure (unconstrain from the output box) and to place + each popup under its parent's tree. */ static void popup_handle_commit(struct wl_listener *listener, void *data) { (void)data; struct tabbed_popup *tp = wl_container_of(listener, tp, commit); @@ -964,7 +950,6 @@ static void popup_handle_reposition(struct wl_listener *listener, void *data) { static void popup_handle_destroy(struct wl_listener *listener, void *data) { (void)data; struct tabbed_popup *tp = wl_container_of(listener, tp, destroy); - wl_list_remove(&tp->link); wl_list_remove(&tp->commit.link); wl_list_remove(&tp->destroy.link); wl_list_remove(&tp->reposition.link); @@ -978,7 +963,6 @@ static void create_popup(struct wlr_xdg_popup *popup, if (!tp) return; tp->xdg_surface = xdg_surface; tp->scene = wlr_scene_xdg_surface_create(parent_tree, xdg_surface); - wl_list_insert(&popups, &tp->link); /* wlr_scene_xdg_surface_create destroys the node when the surface is destroyed, so we only track positioning/reposition listeners here. */ tp->commit.notify = popup_handle_commit; @@ -2528,60 +2512,6 @@ static bool import_scene_buffer(struct wlr_buffer *buf, GLuint tex) { return true; } -/* Draw one popup surface buffer into the presented window at its on-screen - position. This bypasses wlroots' scene rendering for popups, whose - premultiplied-alpha blend paints nothing in our nested EGL pipeline. - - Each popup surface is composited exactly once per frame. A sub-menu is a - popup nested under its parent menu's scene tree, so it is reachable both - from its own tree and from the parent menu's tree; the surfaces-drawn array - prevents it being drawn twice. Position uses wlr_scene_node_coords (the - true absolute offset, including every ancestor), because the iterator's - sx/sy only sums offsets from its own tree root and would place a nested - popup too high/left by the missing ancestor offsets. */ -struct popup_draw_ctx { - int win_w, win_h; - struct wlr_surface **drawn; - int ndrawn; -}; - -static void popup_draw_cb(struct wlr_scene_buffer *buffer, - int sx, int sy, void *user_data) { - struct popup_draw_ctx *ctx = user_data; - if (!buffer->buffer) return; - struct wlr_scene_surface *ss = wlr_scene_surface_try_from_buffer(buffer); - if (!ss) return; - for (int i = 0; i < ctx->ndrawn; i++) { - if (ctx->drawn[i] == ss->surface) return; - } - ctx->drawn[ctx->ndrawn++] = ss->surface; - (void)sx; (void)sy; - int lx = 0, ly = 0; - wlr_scene_node_coords(&buffer->node, &lx, &ly); - if (!import_scene_buffer(buffer->buffer, host.popup_tex)) return; - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - draw_texture_rect(host.popup_tex, buffer->buffer->width, - buffer->buffer->height, - 0, 0, buffer->buffer->width, buffer->buffer->height, - lx, ly, ctx->win_w, ctx->win_h); - glDisable(GL_BLEND); -} - -static void composite_popups(void) { - struct wlr_surface *drawn[64]; - struct popup_draw_ctx ctx = { - .win_w = host.width, - .win_h = host.height, - .drawn = drawn, - .ndrawn = 0, - }; - struct tabbed_popup *tp; - wl_list_for_each(tp, &popups, link) - wlr_scene_node_for_each_buffer(&tp->scene->node, - popup_draw_cb, &ctx); -} - /* Pace presents to the host compositor: this fires after the host has shown the buffer we queued, so we know we may queue the next one. Any frame drawn while a present is still pending is coalesced into the back buffer and @@ -2653,8 +2583,6 @@ static void blit_scene_buffer(struct wlr_buffer *buf) { 0, 0, buf->width, buf->height, 0, 0, w, h); glDisable(GL_BLEND); } - composite_popups(); - if (dump_host_req) { dump_host_req = false; dump_host_back();