Skip to content

[Linux][Wayland] Stable 3.47 shared-framebuffer path: GTK keeps presenting a stale frame after the EGLImage decouples from the frame texture (NVIDIA 615, GNOME 50) #195821

Open

Description

@NelsonScott

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).

  1. Raster side is alive and correct. Breaking on glBlitFramebuffer on io.flutter.rast shows present_layers blitting 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).
  2. GTK side keeps painting, but stale. fl_view_renderer_draw → fl_compositor_opengl_render → fl_framebuffer_create_sibling → gdk_cairo_draw_from_gl runs on every redraw. Reading the sibling texture (a fresh glEGLImageTargetTexture2DOES of the same FlEGLImage handle, 0x294d0142bb1) at the gdk_cairo_draw_from_gl entry returns the old frame, identical (same md5) on every paint, for over an hour.
  3. 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's GL_COLOR_ATTACHMENT0 is 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: eglCreateImageKHR on texture 3 returns EGL_BAD_ACCESS, as the spec requires.
  4. The image is decoupled in both directions. Clearing the GTK-side sibling to magenta (glClear through 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.
  5. 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 EGLSync taken 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

  1. suryaKommana2662 commented on Nov 3, 2026

    @suryaKommana2662
    Contributor

    @NelsonScott Thanks for the report. Routing to team-linux for further investigation.

  2. added
    engineflutter/engine related. See also e: labels.
    platform-linuxBuilding on or for Linux specifically
    team-linuxOwned by the Linux platform team
    and removed
    in triagePresently being triaged by the triage team
    on Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    a: desktopRunning on desktopengineflutter/engine related. See also e: labels.platform-linuxBuilding on or for Linux specificallyteam-linuxOwned by the Linux platform team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions