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 <[email protected]>
> 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 <[email protected]> wrote:
> > > >
> > > > From: Dongwon Kim <[email protected]>
> > > >
> > > > 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 <[email protected]>
> > > > Cc: Marc-André Lureau <[email protected]>
> > > > Cc: Vivek Kasireddy <[email protected]>
> > > > Signed-off-by: Dongwon Kim <[email protected]>
> > > > ---
> > > > 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