* [PATCH] ui/gtk: Clean up GL resources on tab detach, re-attach, and VC free
@ 2026-07-29 13:47 dongwon.kim
2026-09-13 9:13 ` Marc-André Lureau
0 siblings, 1 reply; 2+ messages in thread
From: dongwon.kim @ 2026-07-29 13:47 UTC (permalink / raw)
To: qemu-devel
From: Dongwon Kim <dongwon.kim@intel.com>
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>
---
ui/gtk.c | 67 +++++++++++++++++++++++++++++++++++---------------------
1 file changed, 42 insertions(+), 25 deletions(-)
diff --git a/ui/gtk.c b/ui/gtk.c
index 0b5139ef31..c2324d273e 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1490,6 +1490,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)
{
@@ -1506,13 +1528,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
@@ -1549,12 +1575,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;
@@ -1564,6 +1587,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",
@@ -2696,19 +2724,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);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] ui/gtk: Clean up GL resources on tab detach, re-attach, and VC free
2026-07-29 13:47 [PATCH] ui/gtk: Clean up GL resources on tab detach, re-attach, and VC free dongwon.kim
@ 2026-09-13 9:13 ` Marc-André Lureau
0 siblings, 0 replies; 2+ messages in thread
From: Marc-André Lureau @ 2026-09-13 9:13 UTC (permalink / raw)
To: dongwon.kim; +Cc: qemu-devel
On Wed, Sep 9, 2026 at 3:30 AM <dongwon.kim@intel.com> wrote:
>
> From: Dongwon Kim <dongwon.kim@intel.com>
>
> 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>
> ---
> ui/gtk.c | 67 +++++++++++++++++++++++++++++++++++---------------------
> 1 file changed, 42 insertions(+), 25 deletions(-)
>
> diff --git a/ui/gtk.c b/ui/gtk.c
> index 0b5139ef31..c2324d273e 100644
> --- a/ui/gtk.c
> +++ b/ui/gtk.c
> @@ -1490,6 +1490,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)
> {
> @@ -1506,13 +1528,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
>
> @@ -1549,12 +1575,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;
> @@ -1564,6 +1587,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",
> @@ -2696,19 +2724,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);
> }
> --
> 2.43.0
>
>
--
Marc-André Lureau
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-13 9:13 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29 13:47 [PATCH] ui/gtk: Clean up GL resources on tab detach, re-attach, and VC free dongwon.kim
2026-09-13 9:13 ` Marc-André Lureau
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.