Repository navigation
Description
Summary
With the stable 3.47 Linux embedder on a native Wayland session with the NVIDIA proprietary driver, a long-running app's window stops updating after many hours of uptime: Flutter keeps rendering new frames, GTK keeps painting, but GTK paints the old frame forever. Input still works (the app reacts, just invisibly). A window resize revives it, because that is the only thing that recreates the compositor framebuffer and its EGLImage.
I debugged this in the live, stuck process with gdb (no symbols needed, all calls through the public GTK/GL/EGL API) and can show that the EGLImage that fl_framebuffer.cc creates from the compositor texture no longer refers to the same storage as that texture: writes on one side are invisible on the other, in both directions.
This is the FlCompositorOpenGL / fl_framebuffer_create_sibling path that master removed in #192150 in favour of FlViewRendererSubsurface, so master/beta are not affected by design. Filing because every 3.47 stable app on NVIDIA+Wayland can hit it, and because the trigger is not a one-off.
Environment
- App: BlueBubbles 2.1.5 (Flathub), engine
cf636b1dfb818dd6a3ff16e390e93b1a036969c8(flutter-3.47-candidate.0), Impeller, OpenGL renderer - Fedora 44, kernel 7.2.13, GNOME Shell 50.4 / Mutter, native Wayland session (
GDK_BACKEND=wayland), GTK 3.24.51 (Flatpak runtime org.gnome.Platform//51) - NVIDIA GeForce RTX 5090, driver 615.78.08 (libEGL_nvidia inside the sandbox too); display 3840x2160 at 1.25 fractional scale, window at scale factor 2 (frame 2004x3360)
- Seen 3 times in 3 days, each after many hours of uptime (the last one ~20 h after launch). No suspend/resume, no display mode change, no NVRM Xid in the journal; GPU memory was not under pressure.
What the stuck process showed
All numbers below are from one occurrence, read with glReadPixels through gdb while the window was stuck (scripts attached).
- Raster side is alive and correct. Breaking on
glBlitFramebufferonio.flutter.rastshowspresent_layersblitting layer FBO 4 into the compositor framebuffer (FBO 2, texture 3, 2004x3360) at every presented frame. Reading column 200 of texture 3 after the blit shows the new UI state (the chat the user clicked). - GTK side keeps painting, but stale.
fl_view_renderer_draw→fl_compositor_opengl_render→fl_framebuffer_create_sibling→gdk_cairo_draw_from_glruns on every redraw. Reading the sibling texture (a freshglEGLImageTargetTexture2DOESof the sameFlEGLImagehandle,0x294d0142bb1) at thegdk_cairo_draw_from_glentry returns the old frame, identical (same md5) on every paint, for over an hour. - Not a synchronization race: injecting
glFinish()on the raster thread after the blit, and forcing extra paints, changes nothing. Not an FBO/name problem: FBO 2'sGL_COLOR_ATTACHMENT0is still texture 3,glIsTexture(3)is true, size matches. Not a leak of sibling FBOs: after 20 h only 3 FBOs exist in GDK's context (so [Linux][Wayland] Stable 3.47 disposes sibling framebuffers in the wrong GL context #194840 is a separate issue). The "already a sibling" rule holds:eglCreateImageKHRon texture 3 returnsEGL_BAD_ACCESS, as the spec requires. - The image is decoupled in both directions. Clearing the GTK-side sibling to magenta (
glClearthrough an FBO on the sibling,glFinish) and then reading texture 3 on the raster thread shows no magenta at all; the raster texture still holds the new frame. So the texture and the EGLImage's siblings are backed by different memory. - Recreating the texture + EGLImage (what a resize does via
fl_framebuffer_new) fixes it immediately; a 1-pixel resize from the compositor side (org.gnome.Shell.Extensions.Windows.Resize) is the user-level workaround.
A standalone C program that mirrors this exact pattern (two non-shared EGL contexts, texture + EGLImage in A, per-paint sibling in B, unsynchronised full-surface blits at 60 Hz, pauses, extra GPU load, GL and GLES variants) ran 38 minutes / 165k frames on the same GPU without reproducing it, so the trigger is something rarer than the steady-state sharing; I could not identify it. I am happy to run more experiments on this machine.
Why I think the embedder should still care
The spec (EGL_KHR_image_base) makes the application responsible for synchronising sibling access across contexts; the 3.47 path writes the texture on the raster thread while GDK may still be sampling the previous sibling, with only a glFlush. Whether or not that is what makes the NVIDIA driver re-home the texture's storage, the stable branch has no way to detect or recover from a decoupled image: the framebuffer is only recreated on a size change.
Options for the stable branch (I can send a PR for whichever you prefer):
- cheapest: recreate the EGLImage (new texture) when the window is unmapped/mapped or on a periodic basis (hacky);
- better: give the raster thread something to wait on before it overwrites the texture (an
EGLSynctaken after GTK's draw), so there is never a write-after-read across contexts; - or document the Wayland subsurface renderer as the fix and cherry-pick it, if that is feasible for 3.47.x.
Steps to reproduce
Unfortunately only "run a Flutter 3.47 app on NVIDIA 615 + GNOME Wayland for many hours and interact occasionally". Three occurrences in 3 days on this machine.
Attachments
gdb scripts used for the readbacks (purge.gdb, wt.gdb, fresh.gdb), the trimmed outputs, and the standalone repro attempt (eglshare.c): https://gist.github.com/NelsonScott/b44f42154d84c003e7ed7665aa992664
For what it is worth, the beta (3.50) subsurface renderer, which replaces this path, does not render at all on this GNOME 50.6 + NVIDIA setup (black window with the default flutter create app); reported separately.
Expected results
The window shows the frame Flutter rendered.
Actual results
The window shows a frame from hours ago until it is resized.
Flutter doctor
(from the Flatpak build: flutter --version 3.47.8, engine cf636b1, Dart 3.12.x)
Activity
suryaKommana2662 commentedon Nov 3, 2026
@NelsonScott Thanks for the report. Routing to
team-linuxfor further investigation.linux: on Wayland the window could show the frame before the newest (…