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.
This commit is contained in:
Andrew Hamilton 2026-08-12 10:40:05 +10:00
parent cc2ddc63c0
commit b7e1c650dc

View file

@ -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();