Commit 78809254f3 for qemu.org

commit 78809254f367cec04d68afd4c90115d9cc15a2e4
Author: Dongwon Kim <dongwon.kim@intel.com>
Date:   Wed Jul 29 06:47:59 2026 -0700

    ui/gtk: Clean up GL resources on tab detach, re-attach, and VC free

    When a VC is detached into an independent window or re-attached back
    to the main window via gd_tab_window_close(), its underlying EGL surface
    and context are destroyed and recreated.

    However, the associated FB objects (guest_fb, win_fb, cursor_fb),
    display surface textures, and shader instances were not being cleaned up
    during these transitions, leading to potential resource leaks.

    Introduce a helper function, gd_gl_release_resources(), to make the
    appropriate GL context current, delete the textures and framebuffers,
    release the shader instance, and reset state pointers.

    Use this helper in gd_tab_window_close(), gd_menu_untabify(), and refactor
    gd_vc_free() to use it as well.

    Cc: Daniel P. Berrangé <berrange@redhat.com>
    Cc: Marc-André Lureau <marcandre.lureau@redhat.com>
    Signed-off-by: Dongwon Kim <dongwon.kim@intel.com>
    Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
    Message-ID: <20260729134759.2877-1-dongwon.kim@intel.com>

diff --git a/ui/gtk.c b/ui/gtk.c
index a194488a0c..ed7ffc06b1 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1497,6 +1497,28 @@ static int gd_vc_notebook_pos(GtkDisplayState *s, VirtualConsole *target)
     g_assert_not_reached();
 }

+#if defined(CONFIG_OPENGL)
+static void gd_gl_release_resources(VirtualConsole *vc)
+{
+    if (vc->gfx.ectx) {
+        eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+                       vc->gfx.esurface, vc->gfx.ectx);
+    } else if (gtk_use_gl_area) {
+        gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+    }
+
+    if (vc->gfx.gls) {
+        surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
+        qemu_gl_fini_shader(vc->gfx.gls);
+        vc->gfx.gls = NULL;
+    }
+
+    egl_fb_destroy(&vc->gfx.guest_fb);
+    egl_fb_destroy(&vc->gfx.win_fb);
+    egl_fb_destroy(&vc->gfx.cursor_fb);
+}
+#endif
+
 static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
                                     void *opaque)
 {
@@ -1513,13 +1535,17 @@ static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
     gtk_widget_destroy(vc->window);
     vc->window = NULL;
 #if defined(CONFIG_OPENGL)
-    if (vc->gfx.esurface) {
-        eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
-        vc->gfx.esurface = NULL;
-    }
-    if (vc->gfx.ectx) {
-        eglDestroyContext(qemu_egl_display, vc->gfx.ectx);
-        vc->gfx.ectx = NULL;
+    if (vc->type == GD_VC_GFX) {
+        gd_gl_release_resources(vc);
+
+        if (vc->gfx.esurface) {
+            eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
+            vc->gfx.esurface = NULL;
+        }
+        if (vc->gfx.ectx) {
+            eglDestroyContext(qemu_egl_display, vc->gfx.ectx);
+            vc->gfx.ectx = NULL;
+        }
     }
 #endif

@@ -1556,12 +1582,9 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque)

     if (vc->type == GD_VC_GFX &&
         qemu_console_is_graphic(vc->gfx.dcl.con)) {
-        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
-                                       FALSE);
-    }
-    if (!vc->window) {
-        vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 #if defined(CONFIG_OPENGL)
+        gd_gl_release_resources(vc);
+
         if (vc->gfx.esurface) {
             eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
             vc->gfx.esurface = NULL;
@@ -1571,6 +1594,11 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
             vc->gfx.ectx = NULL;
         }
 #endif
+        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
+                                       FALSE);
+    }
+    if (!vc->window) {
+        vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
         gd_widget_reparent(s->notebook, vc->window, vc->tab_item);

         g_signal_connect(vc->window, "delete-event",
@@ -2707,19 +2735,8 @@ static void gd_vc_free(void *p)
         if (display_opengl) {
             qemu_console_set_display_gl_ctx(vc->gfx.dcl.con, NULL);
         }
-        if (vc->gfx.ectx) {
-            eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
-                           vc->gfx.esurface, vc->gfx.ectx);
-        } else if (gtk_use_gl_area) {
-            gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
-        }
-        if (vc->gfx.gls) {
-            surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
-            qemu_gl_fini_shader(vc->gfx.gls);
-        }
-        egl_fb_destroy(&vc->gfx.guest_fb);
-        egl_fb_destroy(&vc->gfx.win_fb);
-        egl_fb_destroy(&vc->gfx.cursor_fb);
+        gd_gl_release_resources(vc);
+
         if (vc->gfx.esurface) {
             eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
         }