Skip to content

Fix black tooltips and popups on Wayland - #192910

Merged
auto-submit[bot] merged 3 commits into
flutter:masterfrom
robert-ancell:linux-popup-black-frame
Sep 17, 2026
Merged

auto-submit[bot] merged 3 commits into
flutter:masterfrom
robert-ancell:linux-popup-black-frame

Conversation

@robert-ancell

Copy link
Copy Markdown
Contributor

Tooltips and popups were showing as black boxes, and opening a popup could crash the compositor. Two things were wrong. A tooltip only draws one frame, and that frame went into the buffer left over from before the surface was resized, which nothing had ever painted — so we now swap that stale buffer away unused before drawing. Separately, GTK destroys a window’s surface when the window is hidden and makes a new one when it is shown again, which it does when a popup is resized, leaving the subsurface Flutter draws into attached to a surface that no longer exists — so we now notice when the surface has been replaced, make a new subsurface on it, and ask the engine for another frame to fill it.

A window that is sized to its content, like a tooltip, is created before
its size is known, so its Wayland surface starts out a single pixel
across and is resized when the first frame arrives.

The buffer a frame is drawn into is acquired when the previous frame is
swapped, so resizing the surface doesn't reach the buffer the frame that
asked for the resize is drawn into. That frame went into the old, single
pixel buffer, and as a window sized to its content only produces one
frame at each size nothing ever replaced it. The window was left showing
a buffer that was never painted, which is what made tooltips appear as
black boxes.

Swap the stale buffer away unused after a resize so the frame is drawn
into a buffer of the size it was rendered at. eglQuerySurface() reports
the size of the buffer that was last swapped rather than the one being
drawn into, so the sizes are tracked here instead of being asked for.

Presenting a buffer as soon as it is resized also means the size given to
the surface has to be valid, where before an invalid size was always
replaced before any buffer of that size was used. A view that hasn't been
allocated yet produces a single pixel frame, which isn't a whole number
of logical pixels on a display with a scale of two, and Wayland requires
buffer sizes to be an integer multiple of the buffer scale. Round sizes
up to a whole logical pixel, and keep writing the frame at the top of the
buffer so the rounded up rows fall off the bottom, where Wayland clips.
GTK destroys a window's Wayland surface and makes a new one when the
window is hidden and shown again, which it does when a popup is resized.
The subsurface Flutter renders into belonged to the old surface, so
nothing was shown and moving it was a protocol error that could kill the
compositor.

Notice when the surface has been replaced, make a new subsurface on it
and ask the engine for another frame to fill it.
@robert-ancell
robert-ancell requested a review from a team as a code owner September 17, 2026 04:09
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Sep 17, 2026
@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. platform-linux Building on or for Linux specifically a: desktop Running on desktop team-linux Owned by the Linux platform team labels Sep 17, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the Linux platform shell to handle Wayland subsurface recreation when GTK replaces the parent surface, and ensures buffer sizes are rounded to integer multiples of the buffer scale. Feedback identifies a potential race condition in fl_view_renderer_subsurface.cc when calling fl_subsurface_egl_resize without holding self->frame_mutex, and a potential division-by-zero vulnerability in round_to_scale if scale is zero or negative.

The sizes tracked in FlSubsurfaceEGL are used by the raster thread when
it presents a frame, so the GTK thread has to hold the frame mutex while
it changes them.

@mattkae mattkae left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 🎉

@mattkae mattkae added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 17, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Sep 17, 2026
Merged via the queue into flutter:master with commit e44e7f7 Sep 17, 2026
23 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: desktop Running on desktop CICD Run CI/CD engine flutter/engine related. See also e: labels. platform-linux Building on or for Linux specifically team-linux Owned by the Linux platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants