[PATCH] ui/gtk: Implement dpy_gl_ctx_destroy_texture for gtk-egl and gtk-gl-area

dongwon.kim@intel.com posted 1 patch 1 month, 1 week ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260303030714.1926907-1-dongwon.kim@intel.com
Maintainers: "Marc-André Lureau" <marcandre.lureau@redhat.com>
include/ui/gtk.h |  4 ++++
ui/gtk-egl.c     | 10 ++++++++++
ui/gtk-gl-area.c |  8 ++++++++
ui/gtk.c         |  2 ++
4 files changed, 24 insertions(+)
[PATCH] ui/gtk: Implement dpy_gl_ctx_destroy_texture for gtk-egl and gtk-gl-area
Posted by dongwon.kim@intel.com 1 month, 1 week ago
From: Dongwon Kim <dongwon.kim@intel.com>

Currently, the texture associated with a DisplaySurface is not deleted
when the surface is switched or freed, leading to a memory leak in the
GPU process. This occurs because the GTK backend lacks a mapping for the
'dpy_gl_ctx_destroy_texture' operation.

This patch implements the necessary cleanup functions for both EGL and
GL Area backends. Each implementation ensures the appropriate GL context
is made current before calling surface_gl_destroy_texture(), ensuring
that the underlying GL texture ID is safely released by the driver.

Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Marc-André Lureau <marcandre.lureau@redhat.com>
Cc: Vivek Kasireddy <vivek.kasireddy@intel.com>
Signed-off-by: Dongwon Kim <dongwon.kim@intel.com>
---
 include/ui/gtk.h |  4 ++++
 ui/gtk-egl.c     | 10 ++++++++++
 ui/gtk-gl-area.c |  8 ++++++++
 ui/gtk.c         |  2 ++
 4 files changed, 24 insertions(+)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 3e6ce3cb48..f1765ad5cb 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -170,6 +170,8 @@ void gd_egl_switch(DisplayChangeListener *dcl,
                    DisplaySurface *surface);
 QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc,
                                     QEMUGLParams *params);
+void gd_egl_destroy_texture(DisplayGLCtx *dgc,
+                            DisplaySurface *surface);
 void gd_egl_scanout_disable(DisplayChangeListener *dcl);
 void gd_egl_scanout_texture(DisplayChangeListener *dcl,
                             uint32_t backing_id,
@@ -206,6 +208,8 @@ QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc,
                                         QEMUGLParams *params);
 void gd_gl_area_destroy_context(DisplayGLCtx *dgc,
                                 QEMUGLContext ctx);
+void gd_gl_area_destroy_texture(DisplayGLCtx *dgc,
+                                DisplaySurface *surface);
 void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
                                QemuDmaBuf *dmabuf);
 void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index ae9239999c..c1b32bc41b 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -225,6 +225,16 @@ QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc,
     return qemu_egl_create_context(dgc, params);
 }
 
+void gd_egl_destroy_texture(DisplayGLCtx *dgc, DisplaySurface *surface)
+{
+    VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
+
+    eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+                   vc->gfx.esurface, vc->gfx.ectx);
+
+    surface_gl_destroy_texture(dgc->gls, surface);
+}
+
 void gd_egl_scanout_disable(DisplayChangeListener *dcl)
 {
     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index cd86022d26..84e41f4221 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -299,6 +299,14 @@ void gd_gl_area_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx)
     g_clear_object(&ctx);
 }
 
+void gd_gl_area_destroy_texture(DisplayGLCtx *dgc, DisplaySurface *surface)
+{
+    VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
+
+    gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+    surface_gl_destroy_texture(dgc->gls, surface);
+}
+
 void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
                                 uint32_t backing_id,
                                 bool backing_y_0_top,
diff --git a/ui/gtk.c b/ui/gtk.c
index 9ebe7e8df0..217f425075 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -636,6 +636,7 @@ static const DisplayGLCtxOps gl_area_ctx_ops = {
     .dpy_gl_ctx_is_compatible_dcl = gd_gl_area_is_compatible_dcl,
     .dpy_gl_ctx_create       = gd_gl_area_create_context,
     .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
+    .dpy_gl_ctx_destroy_texture = gd_gl_area_destroy_texture,
     .dpy_gl_ctx_make_current = gd_gl_area_make_current,
 };
 
@@ -670,6 +671,7 @@ static const DisplayGLCtxOps egl_ctx_ops = {
     .dpy_gl_ctx_is_compatible_dcl = gd_egl_is_compatible_dcl,
     .dpy_gl_ctx_create       = gd_egl_create_context,
     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
+    .dpy_gl_ctx_destroy_texture = gd_egl_destroy_texture,
     .dpy_gl_ctx_make_current = gd_egl_make_current,
 };
 #endif
-- 
2.43.0


Re: [PATCH] ui/gtk: Implement dpy_gl_ctx_destroy_texture for gtk-egl and gtk-gl-area
Posted by Marc-André Lureau 1 month, 1 week ago
Hi

On Tue, Mar 3, 2026 at 4:13 AM <dongwon.kim@intel.com> wrote:
>
> From: Dongwon Kim <dongwon.kim@intel.com>
>
> Currently, the texture associated with a DisplaySurface is not deleted
> when the surface is switched or freed, leading to a memory leak in the
> GPU process. This occurs because the GTK backend lacks a mapping for the
> 'dpy_gl_ctx_destroy_texture' operation.
>
> This patch implements the necessary cleanup functions for both EGL and
> GL Area backends. Each implementation ensures the appropriate GL context
> is made current before calling surface_gl_destroy_texture(), ensuring
> that the underlying GL texture ID is safely released by the driver.
>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Cc: Marc-André Lureau <marcandre.lureau@redhat.com>
> Cc: Vivek Kasireddy <vivek.kasireddy@intel.com>
> Signed-off-by: Dongwon Kim <dongwon.kim@intel.com>
> ---
>  include/ui/gtk.h |  4 ++++
>  ui/gtk-egl.c     | 10 ++++++++++
>  ui/gtk-gl-area.c |  8 ++++++++
>  ui/gtk.c         |  2 ++
>  4 files changed, 24 insertions(+)
>
> diff --git a/include/ui/gtk.h b/include/ui/gtk.h
> index 3e6ce3cb48..f1765ad5cb 100644
> --- a/include/ui/gtk.h
> +++ b/include/ui/gtk.h
> @@ -170,6 +170,8 @@ void gd_egl_switch(DisplayChangeListener *dcl,
>                     DisplaySurface *surface);
>  QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc,
>                                      QEMUGLParams *params);
> +void gd_egl_destroy_texture(DisplayGLCtx *dgc,
> +                            DisplaySurface *surface);
>  void gd_egl_scanout_disable(DisplayChangeListener *dcl);
>  void gd_egl_scanout_texture(DisplayChangeListener *dcl,
>                              uint32_t backing_id,
> @@ -206,6 +208,8 @@ QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc,
>                                          QEMUGLParams *params);
>  void gd_gl_area_destroy_context(DisplayGLCtx *dgc,
>                                  QEMUGLContext ctx);
> +void gd_gl_area_destroy_texture(DisplayGLCtx *dgc,
> +                                DisplaySurface *surface);
>  void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
>                                 QemuDmaBuf *dmabuf);
>  void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
> diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
> index ae9239999c..c1b32bc41b 100644
> --- a/ui/gtk-egl.c
> +++ b/ui/gtk-egl.c
> @@ -225,6 +225,16 @@ QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc,
>      return qemu_egl_create_context(dgc, params);
>  }
>
> +void gd_egl_destroy_texture(DisplayGLCtx *dgc, DisplaySurface *surface)
> +{
> +    VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
> +
> +    eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
> +                   vc->gfx.esurface, vc->gfx.ectx);
> +
> +    surface_gl_destroy_texture(dgc->gls, surface);
> +}
> +
>  void gd_egl_scanout_disable(DisplayChangeListener *dcl)
>  {
>      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
> diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
> index cd86022d26..84e41f4221 100644
> --- a/ui/gtk-gl-area.c
> +++ b/ui/gtk-gl-area.c
> @@ -299,6 +299,14 @@ void gd_gl_area_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx)
>      g_clear_object(&ctx);
>  }
>
> +void gd_gl_area_destroy_texture(DisplayGLCtx *dgc, DisplaySurface *surface)
> +{
> +    VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
> +
> +    gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> +    surface_gl_destroy_texture(dgc->gls, surface);
> +}
> +
>  void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
>                                  uint32_t backing_id,
>                                  bool backing_y_0_top,
> diff --git a/ui/gtk.c b/ui/gtk.c
> index 9ebe7e8df0..217f425075 100644
> --- a/ui/gtk.c
> +++ b/ui/gtk.c
> @@ -636,6 +636,7 @@ static const DisplayGLCtxOps gl_area_ctx_ops = {
>      .dpy_gl_ctx_is_compatible_dcl = gd_gl_area_is_compatible_dcl,
>      .dpy_gl_ctx_create       = gd_gl_area_create_context,
>      .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
> +    .dpy_gl_ctx_destroy_texture = gd_gl_area_destroy_texture,
>      .dpy_gl_ctx_make_current = gd_gl_area_make_current,
>  };
>
> @@ -670,6 +671,7 @@ static const DisplayGLCtxOps egl_ctx_ops = {
>      .dpy_gl_ctx_is_compatible_dcl = gd_egl_is_compatible_dcl,
>      .dpy_gl_ctx_create       = gd_egl_create_context,
>      .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
> +    .dpy_gl_ctx_destroy_texture = gd_egl_destroy_texture,
>      .dpy_gl_ctx_make_current = gd_egl_make_current,

But there is no dpy_gl_ctx_create_texture(), how can
dpy_gl_ctx_destroy_texture() get called?

>  };
>  #endif
> --
> 2.43.0
>
>


-- 
Marc-André Lureau
RE: [PATCH] ui/gtk: Implement dpy_gl_ctx_destroy_texture for gtk-egl and gtk-gl-area
Posted by Kim, Dongwon 1 month, 1 week ago
Hi Marc-André,

> Subject: Re: [PATCH] ui/gtk: Implement dpy_gl_ctx_destroy_texture for gtk-
> egl and gtk-gl-area
> 
> Hi
> 
> On Tue, Mar 3, 2026 at 4:13 AM <dongwon.kim@intel.com> wrote:
> >
> > From: Dongwon Kim <dongwon.kim@intel.com>
> >
> > Currently, the texture associated with a DisplaySurface is not deleted
> > when the surface is switched or freed, leading to a memory leak in the
> > GPU process. This occurs because the GTK backend lacks a mapping for
> > the 'dpy_gl_ctx_destroy_texture' operation.
> >
> > This patch implements the necessary cleanup functions for both EGL and
> > GL Area backends. Each implementation ensures the appropriate GL
> > context is made current before calling surface_gl_destroy_texture(),
> > ensuring that the underlying GL texture ID is safely released by the driver.
> >
> > Cc: Gerd Hoffmann <kraxel@redhat.com>
> > Cc: Marc-André Lureau <marcandre.lureau@redhat.com>
> > Cc: Vivek Kasireddy <vivek.kasireddy@intel.com>
> > Signed-off-by: Dongwon Kim <dongwon.kim@intel.com>
> > ---
> >  include/ui/gtk.h |  4 ++++
> >  ui/gtk-egl.c     | 10 ++++++++++
> >  ui/gtk-gl-area.c |  8 ++++++++
> >  ui/gtk.c         |  2 ++
> >  4 files changed, 24 insertions(+)
> >
> > diff --git a/include/ui/gtk.h b/include/ui/gtk.h index
> > 3e6ce3cb48..f1765ad5cb 100644
> > --- a/include/ui/gtk.h
> > +++ b/include/ui/gtk.h
> > @@ -170,6 +170,8 @@ void gd_egl_switch(DisplayChangeListener *dcl,
> >                     DisplaySurface *surface);  QEMUGLContext
> > gd_egl_create_context(DisplayGLCtx *dgc,
> >                                      QEMUGLParams *params);
> > +void gd_egl_destroy_texture(DisplayGLCtx *dgc,
> > +                            DisplaySurface *surface);
> >  void gd_egl_scanout_disable(DisplayChangeListener *dcl);  void
> > gd_egl_scanout_texture(DisplayChangeListener *dcl,
> >                              uint32_t backing_id, @@ -206,6 +208,8 @@
> > QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc,
> >                                          QEMUGLParams *params);  void
> > gd_gl_area_destroy_context(DisplayGLCtx *dgc,
> >                                  QEMUGLContext ctx);
> > +void gd_gl_area_destroy_texture(DisplayGLCtx *dgc,
> > +                                DisplaySurface *surface);
> >  void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
> >                                 QemuDmaBuf *dmabuf);  void
> > gd_gl_area_scanout_texture(DisplayChangeListener *dcl, diff --git
> > a/ui/gtk-egl.c b/ui/gtk-egl.c index ae9239999c..c1b32bc41b 100644
> > --- a/ui/gtk-egl.c
> > +++ b/ui/gtk-egl.c
> > @@ -225,6 +225,16 @@ QEMUGLContext
> gd_egl_create_context(DisplayGLCtx *dgc,
> >      return qemu_egl_create_context(dgc, params);  }
> >
> > +void gd_egl_destroy_texture(DisplayGLCtx *dgc, DisplaySurface
> > +*surface) {
> > +    VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
> > +
> > +    eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
> > +                   vc->gfx.esurface, vc->gfx.ectx);
> > +
> > +    surface_gl_destroy_texture(dgc->gls, surface); }
> > +
> >  void gd_egl_scanout_disable(DisplayChangeListener *dcl)  {
> >      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
> > diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index
> > cd86022d26..84e41f4221 100644
> > --- a/ui/gtk-gl-area.c
> > +++ b/ui/gtk-gl-area.c
> > @@ -299,6 +299,14 @@ void gd_gl_area_destroy_context(DisplayGLCtx
> *dgc, QEMUGLContext ctx)
> >      g_clear_object(&ctx);
> >  }
> >
> > +void gd_gl_area_destroy_texture(DisplayGLCtx *dgc, DisplaySurface
> > +*surface) {
> > +    VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
> > +
> > +    gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> > +    surface_gl_destroy_texture(dgc->gls, surface); }
> > +
> >  void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
> >                                  uint32_t backing_id,
> >                                  bool backing_y_0_top, diff --git
> > a/ui/gtk.c b/ui/gtk.c index 9ebe7e8df0..217f425075 100644
> > --- a/ui/gtk.c
> > +++ b/ui/gtk.c
> > @@ -636,6 +636,7 @@ static const DisplayGLCtxOps gl_area_ctx_ops = {
> >      .dpy_gl_ctx_is_compatible_dcl = gd_gl_area_is_compatible_dcl,
> >      .dpy_gl_ctx_create       = gd_gl_area_create_context,
> >      .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
> > +    .dpy_gl_ctx_destroy_texture = gd_gl_area_destroy_texture,
> >      .dpy_gl_ctx_make_current = gd_gl_area_make_current,  };
> >
> > @@ -670,6 +671,7 @@ static const DisplayGLCtxOps egl_ctx_ops = {
> >      .dpy_gl_ctx_is_compatible_dcl = gd_egl_is_compatible_dcl,
> >      .dpy_gl_ctx_create       = gd_egl_create_context,
> >      .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
> > +    .dpy_gl_ctx_destroy_texture = gd_egl_destroy_texture,
> >      .dpy_gl_ctx_make_current = gd_egl_make_current,
> 
> But there is no dpy_gl_ctx_create_texture(), how can
> dpy_gl_ctx_destroy_texture() get called?

Textures are also created via surface_gl_create_texture in the places like,

gtk-egl.c
static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout)
{
 .....
       egl_fb_destroy(&vc->gfx.guest_fb);
        if (vc->gfx.surface) {
            surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
            surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
        }

surface_gl_create_texture always come after surface_gl_destroy_texture
so there is no leak while the guest is running but texture would remain unremoved
if we do none in dpy_gl_ctx_destroy_texture() when the guest is rebooting. 

> 
> >  };
> >  #endif
> > --
> > 2.43.0
> >
> >
> 
> 
> --
> Marc-André Lureau

Thanks
Re: [PATCH] ui/gtk: Implement dpy_gl_ctx_destroy_texture for gtk-egl and gtk-gl-area
Posted by Marc-André Lureau 1 month, 1 week ago
Hi

On Tue, Mar 3, 2026 at 7:50 PM Kim, Dongwon <dongwon.kim@intel.com> wrote:
>
> Hi Marc-André,
>
> > Subject: Re: [PATCH] ui/gtk: Implement dpy_gl_ctx_destroy_texture for gtk-
> > egl and gtk-gl-area
> >
> > Hi
> >
> > On Tue, Mar 3, 2026 at 4:13 AM <dongwon.kim@intel.com> wrote:
> > >
> > > From: Dongwon Kim <dongwon.kim@intel.com>
> > >
> > > Currently, the texture associated with a DisplaySurface is not deleted
> > > when the surface is switched or freed, leading to a memory leak in the
> > > GPU process. This occurs because the GTK backend lacks a mapping for
> > > the 'dpy_gl_ctx_destroy_texture' operation.
> > >
> > > This patch implements the necessary cleanup functions for both EGL and
> > > GL Area backends. Each implementation ensures the appropriate GL
> > > context is made current before calling surface_gl_destroy_texture(),
> > > ensuring that the underlying GL texture ID is safely released by the driver.
> > >
> > > Cc: Gerd Hoffmann <kraxel@redhat.com>
> > > Cc: Marc-André Lureau <marcandre.lureau@redhat.com>
> > > Cc: Vivek Kasireddy <vivek.kasireddy@intel.com>
> > > Signed-off-by: Dongwon Kim <dongwon.kim@intel.com>
> > > ---
> > >  include/ui/gtk.h |  4 ++++
> > >  ui/gtk-egl.c     | 10 ++++++++++
> > >  ui/gtk-gl-area.c |  8 ++++++++
> > >  ui/gtk.c         |  2 ++
> > >  4 files changed, 24 insertions(+)
> > >
> > > diff --git a/include/ui/gtk.h b/include/ui/gtk.h index
> > > 3e6ce3cb48..f1765ad5cb 100644
> > > --- a/include/ui/gtk.h
> > > +++ b/include/ui/gtk.h
> > > @@ -170,6 +170,8 @@ void gd_egl_switch(DisplayChangeListener *dcl,
> > >                     DisplaySurface *surface);  QEMUGLContext
> > > gd_egl_create_context(DisplayGLCtx *dgc,
> > >                                      QEMUGLParams *params);
> > > +void gd_egl_destroy_texture(DisplayGLCtx *dgc,
> > > +                            DisplaySurface *surface);
> > >  void gd_egl_scanout_disable(DisplayChangeListener *dcl);  void
> > > gd_egl_scanout_texture(DisplayChangeListener *dcl,
> > >                              uint32_t backing_id, @@ -206,6 +208,8 @@
> > > QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc,
> > >                                          QEMUGLParams *params);  void
> > > gd_gl_area_destroy_context(DisplayGLCtx *dgc,
> > >                                  QEMUGLContext ctx);
> > > +void gd_gl_area_destroy_texture(DisplayGLCtx *dgc,
> > > +                                DisplaySurface *surface);
> > >  void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
> > >                                 QemuDmaBuf *dmabuf);  void
> > > gd_gl_area_scanout_texture(DisplayChangeListener *dcl, diff --git
> > > a/ui/gtk-egl.c b/ui/gtk-egl.c index ae9239999c..c1b32bc41b 100644
> > > --- a/ui/gtk-egl.c
> > > +++ b/ui/gtk-egl.c
> > > @@ -225,6 +225,16 @@ QEMUGLContext
> > gd_egl_create_context(DisplayGLCtx *dgc,
> > >      return qemu_egl_create_context(dgc, params);  }
> > >
> > > +void gd_egl_destroy_texture(DisplayGLCtx *dgc, DisplaySurface
> > > +*surface) {
> > > +    VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
> > > +
> > > +    eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
> > > +                   vc->gfx.esurface, vc->gfx.ectx);
> > > +
> > > +    surface_gl_destroy_texture(dgc->gls, surface); }
> > > +
> > >  void gd_egl_scanout_disable(DisplayChangeListener *dcl)  {
> > >      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
> > > diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index
> > > cd86022d26..84e41f4221 100644
> > > --- a/ui/gtk-gl-area.c
> > > +++ b/ui/gtk-gl-area.c
> > > @@ -299,6 +299,14 @@ void gd_gl_area_destroy_context(DisplayGLCtx
> > *dgc, QEMUGLContext ctx)
> > >      g_clear_object(&ctx);
> > >  }
> > >
> > > +void gd_gl_area_destroy_texture(DisplayGLCtx *dgc, DisplaySurface
> > > +*surface) {
> > > +    VirtualConsole *vc = container_of(dgc, VirtualConsole, gfx.dgc);
> > > +
> > > +    gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> > > +    surface_gl_destroy_texture(dgc->gls, surface); }
> > > +
> > >  void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
> > >                                  uint32_t backing_id,
> > >                                  bool backing_y_0_top, diff --git
> > > a/ui/gtk.c b/ui/gtk.c index 9ebe7e8df0..217f425075 100644
> > > --- a/ui/gtk.c
> > > +++ b/ui/gtk.c
> > > @@ -636,6 +636,7 @@ static const DisplayGLCtxOps gl_area_ctx_ops = {
> > >      .dpy_gl_ctx_is_compatible_dcl = gd_gl_area_is_compatible_dcl,
> > >      .dpy_gl_ctx_create       = gd_gl_area_create_context,
> > >      .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
> > > +    .dpy_gl_ctx_destroy_texture = gd_gl_area_destroy_texture,
> > >      .dpy_gl_ctx_make_current = gd_gl_area_make_current,  };
> > >
> > > @@ -670,6 +671,7 @@ static const DisplayGLCtxOps egl_ctx_ops = {
> > >      .dpy_gl_ctx_is_compatible_dcl = gd_egl_is_compatible_dcl,
> > >      .dpy_gl_ctx_create       = gd_egl_create_context,
> > >      .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
> > > +    .dpy_gl_ctx_destroy_texture = gd_egl_destroy_texture,
> > >      .dpy_gl_ctx_make_current = gd_egl_make_current,
> >
> > But there is no dpy_gl_ctx_create_texture(), how can
> > dpy_gl_ctx_destroy_texture() get called?
>
> Textures are also created via surface_gl_create_texture in the places like,
>

But surface_gl_{create,destroy}_texture doesn't make use of
dpy_gl_ctx_destroy_texture. The later is only used by dbus so far, see
commit 589089feee ("ui/dbus: fix texture sharing").

> gtk-egl.c
> static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout)
> {
>  .....
>        egl_fb_destroy(&vc->gfx.guest_fb);
>         if (vc->gfx.surface) {
>             surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
>             surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
>         }
>
> surface_gl_create_texture always come after surface_gl_destroy_texture
> so there is no leak while the guest is running but texture would remain unremoved
> if we do none in dpy_gl_ctx_destroy_texture() when the guest is rebooting.
>
> >
> > >  };
> > >  #endif
> > > --
> > > 2.43.0
> > >
> > >
> >
> >
> > --
> > Marc-André Lureau
>
> Thanks



-- 
Marc-André Lureau
RE: [PATCH] ui/gtk: Implement dpy_gl_ctx_destroy_texture for gtk-egl and gtk-gl-area
Posted by Kim, Dongwon 1 month, 1 week ago
Hi Marc-André,

> Subject: Re: [PATCH] ui/gtk: Implement dpy_gl_ctx_destroy_texture for gtk-
> egl and gtk-gl-area
> 
> Hi
> 
> On Tue, Mar 3, 2026 at 7:50 PM Kim, Dongwon <dongwon.kim@intel.com>
> wrote:
> >
> > Hi Marc-André,
> >
> > > Subject: Re: [PATCH] ui/gtk: Implement dpy_gl_ctx_destroy_texture
> > > for gtk- egl and gtk-gl-area
> > >
> > > Hi
> > >
> > > On Tue, Mar 3, 2026 at 4:13 AM <dongwon.kim@intel.com> wrote:
> > > >
> > > > From: Dongwon Kim <dongwon.kim@intel.com>
> > > >
> > > > Currently, the texture associated with a DisplaySurface is not
> > > > deleted when the surface is switched or freed, leading to a memory
> > > > leak in the GPU process. This occurs because the GTK backend lacks
> > > > a mapping for the 'dpy_gl_ctx_destroy_texture' operation.
> > > >
> > > > This patch implements the necessary cleanup functions for both EGL
> > > > and GL Area backends. Each implementation ensures the appropriate
> > > > GL context is made current before calling
> > > > surface_gl_destroy_texture(), ensuring that the underlying GL texture ID
> is safely released by the driver.
> > > >
> > > > Cc: Gerd Hoffmann <kraxel@redhat.com>
> > > > Cc: Marc-André Lureau <marcandre.lureau@redhat.com>
> > > > Cc: Vivek Kasireddy <vivek.kasireddy@intel.com>
> > > > Signed-off-by: Dongwon Kim <dongwon.kim@intel.com>
> > > > ---
> > > >  include/ui/gtk.h |  4 ++++
> > > >  ui/gtk-egl.c     | 10 ++++++++++
> > > >  ui/gtk-gl-area.c |  8 ++++++++
> > > >  ui/gtk.c         |  2 ++
> > > >  4 files changed, 24 insertions(+)
> > > >
> > > > diff --git a/include/ui/gtk.h b/include/ui/gtk.h index
> > > > 3e6ce3cb48..f1765ad5cb 100644
> > > > --- a/include/ui/gtk.h
> > > > +++ b/include/ui/gtk.h
> > > > @@ -170,6 +170,8 @@ void gd_egl_switch(DisplayChangeListener *dcl,
> > > >                     DisplaySurface *surface);  QEMUGLContext
> > > > gd_egl_create_context(DisplayGLCtx *dgc,
> > > >                                      QEMUGLParams *params);
> > > > +void gd_egl_destroy_texture(DisplayGLCtx *dgc,
> > > > +                            DisplaySurface *surface);
> > > >  void gd_egl_scanout_disable(DisplayChangeListener *dcl);  void
> > > > gd_egl_scanout_texture(DisplayChangeListener *dcl,
> > > >                              uint32_t backing_id, @@ -206,6 +208,8
> > > > @@ QEMUGLContext gd_gl_area_create_context(DisplayGLCtx *dgc,
> > > >                                          QEMUGLParams *params);
> > > > void gd_gl_area_destroy_context(DisplayGLCtx *dgc,
> > > >                                  QEMUGLContext ctx);
> > > > +void gd_gl_area_destroy_texture(DisplayGLCtx *dgc,
> > > > +                                DisplaySurface *surface);
> > > >  void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
> > > >                                 QemuDmaBuf *dmabuf);  void
> > > > gd_gl_area_scanout_texture(DisplayChangeListener *dcl, diff --git
> > > > a/ui/gtk-egl.c b/ui/gtk-egl.c index ae9239999c..c1b32bc41b 100644
> > > > --- a/ui/gtk-egl.c
> > > > +++ b/ui/gtk-egl.c
> > > > @@ -225,6 +225,16 @@ QEMUGLContext
> > > gd_egl_create_context(DisplayGLCtx *dgc,
> > > >      return qemu_egl_create_context(dgc, params);  }
> > > >
> > > > +void gd_egl_destroy_texture(DisplayGLCtx *dgc, DisplaySurface
> > > > +*surface) {
> > > > +    VirtualConsole *vc = container_of(dgc, VirtualConsole,
> > > > +gfx.dgc);
> > > > +
> > > > +    eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
> > > > +                   vc->gfx.esurface, vc->gfx.ectx);
> > > > +
> > > > +    surface_gl_destroy_texture(dgc->gls, surface); }
> > > > +
> > > >  void gd_egl_scanout_disable(DisplayChangeListener *dcl)  {
> > > >      VirtualConsole *vc = container_of(dcl, VirtualConsole,
> > > > gfx.dcl); diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index
> > > > cd86022d26..84e41f4221 100644
> > > > --- a/ui/gtk-gl-area.c
> > > > +++ b/ui/gtk-gl-area.c
> > > > @@ -299,6 +299,14 @@ void gd_gl_area_destroy_context(DisplayGLCtx
> > > *dgc, QEMUGLContext ctx)
> > > >      g_clear_object(&ctx);
> > > >  }
> > > >
> > > > +void gd_gl_area_destroy_texture(DisplayGLCtx *dgc, DisplaySurface
> > > > +*surface) {
> > > > +    VirtualConsole *vc = container_of(dgc, VirtualConsole,
> > > > +gfx.dgc);
> > > > +
> > > > +    gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> > > > +    surface_gl_destroy_texture(dgc->gls, surface); }
> > > > +
> > > >  void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
> > > >                                  uint32_t backing_id,
> > > >                                  bool backing_y_0_top, diff --git
> > > > a/ui/gtk.c b/ui/gtk.c index 9ebe7e8df0..217f425075 100644
> > > > --- a/ui/gtk.c
> > > > +++ b/ui/gtk.c
> > > > @@ -636,6 +636,7 @@ static const DisplayGLCtxOps gl_area_ctx_ops =
> {
> > > >      .dpy_gl_ctx_is_compatible_dcl = gd_gl_area_is_compatible_dcl,
> > > >      .dpy_gl_ctx_create       = gd_gl_area_create_context,
> > > >      .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
> > > > +    .dpy_gl_ctx_destroy_texture = gd_gl_area_destroy_texture,
> > > >      .dpy_gl_ctx_make_current = gd_gl_area_make_current,  };
> > > >
> > > > @@ -670,6 +671,7 @@ static const DisplayGLCtxOps egl_ctx_ops = {
> > > >      .dpy_gl_ctx_is_compatible_dcl = gd_egl_is_compatible_dcl,
> > > >      .dpy_gl_ctx_create       = gd_egl_create_context,
> > > >      .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
> > > > +    .dpy_gl_ctx_destroy_texture = gd_egl_destroy_texture,
> > > >      .dpy_gl_ctx_make_current = gd_egl_make_current,
> > >
> > > But there is no dpy_gl_ctx_create_texture(), how can
> > > dpy_gl_ctx_destroy_texture() get called?
> >
> > Textures are also created via surface_gl_create_texture in the places
> > like,
> >
> 
> But surface_gl_{create,destroy}_texture doesn't make use of
> dpy_gl_ctx_destroy_texture. The later is only used by dbus so far, see
> commit 589089feee ("ui/dbus: fix texture sharing").

I thought adding the call back was the simplest way to avoid the mem leak
we observed but now I understand this approach looks more like a WA.
I will take a look inside gtk-egl.c and check if there is better way to prevent
the corner case. Probably the root-cause is that the previous context was not
destroyed properly.

> 
> > gtk-egl.c
> > static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout)
> > {  .....
> >        egl_fb_destroy(&vc->gfx.guest_fb);
> >         if (vc->gfx.surface) {
> >             surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
> >             surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
> >         }
> >
> > surface_gl_create_texture always come after surface_gl_destroy_texture
> > so there is no leak while the guest is running but texture would
> > remain unremoved if we do none in dpy_gl_ctx_destroy_texture() when
> the guest is rebooting.
> >
> > >
> > > >  };
> > > >  #endif
> > > > --
> > > > 2.43.0
> > > >
> > > >
> > >
> > >
> > > --
> > > Marc-André Lureau
> >
> > Thanks
> 
> 
> 
> --
> Marc-André Lureau