Commit 02e8b5ecfe for qemu.org
commit 02e8b5ecfe9abdab88a156f6e9370d22b8436a47
Author: Marc-André Lureau <marcandre.lureau@redhat.com>
Date: Mon Aug 31 14:52:20 2026 +0400
ui: make GL context current in display backend callbacks
Display backend GL callbacks (scanout, flush, release, etc.) may be
invoked from a different GL context, like virgl. Since framebuffer
objects are per-context and textures require a valid context for
deletion, each callback must ensure the correct context is active before
performing GL operations.
Make each backend's own GL context current at the entry of every
callback that performs GL work.
For GTK, the previously shared gd_gl_release_dmabuf is split into
backend-specific gd_egl_release_dmabuf and gd_gl_area_release_dmabuf,
matching the existing pattern where each backend manages its own
context.
On the virgl side, restore ctx0 with virgl_renderer_force_ctx_0()
after SET_SCANOUT, RESOURCE_FLUSH, and SET_SCANOUT_BLOB command
handlers, since their display callbacks may have changed the current
GL context.
Note: restoring virgl ctx0 current is not necessarily correct,
fences may not work. We will follow-up with a complementary fix.
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c
index 7f1d1e0c32..0e43eacca4 100644
--- a/hw/display/virtio-gpu-virgl.c
+++ b/hw/display/virtio-gpu-virgl.c
@@ -1091,9 +1091,12 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
break;
case VIRTIO_GPU_CMD_SET_SCANOUT:
virgl_cmd_set_scanout(g, cmd);
+ /* Display callbacks may change the current GL context. */
+ virgl_renderer_force_ctx_0();
break;
case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
virgl_cmd_resource_flush(g, cmd);
+ virgl_renderer_force_ctx_0();
break;
case VIRTIO_GPU_CMD_RESOURCE_UNREF:
virgl_cmd_resource_unref(g, cmd, &cmd_suspended);
@@ -1130,6 +1133,7 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
break;
case VIRTIO_GPU_CMD_SET_SCANOUT_BLOB:
virgl_cmd_set_scanout_blob(g, cmd);
+ virgl_renderer_force_ctx_0();
break;
#endif
default:
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index b66b9ca5c5..6b62e97588 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -163,6 +163,7 @@ extern bool gtk_use_gl_area;
void gd_update_windowsize(VirtualConsole *vc);
void gd_update_monitor_refresh_rate(VirtualConsole *vc, GtkWidget *widget);
void gd_gl_wait_sync(VirtualConsole *vc, void *sync);
+void gd_release_dmabuf(VirtualConsole *vc, QemuDmaBuf *dmabuf);
/* ui/gtk-egl.c */
void gd_egl_init(VirtualConsole *vc);
@@ -190,6 +191,8 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
uint32_t hot_x, uint32_t hot_y);
void gd_egl_cursor_position(DisplayChangeListener *dcl,
uint32_t pos_x, uint32_t pos_y);
+void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf);
void gd_egl_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void *gd_egl_scanout_flush(DisplayChangeListener *dcl,
@@ -221,6 +224,8 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
uint32_t x, uint32_t y,
uint32_t w, uint32_t h,
void *d3d_tex2d);
+void gd_gl_area_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf);
void gd_gl_area_scanout_disable(DisplayChangeListener *dcl);
void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
diff --git a/ui/egl-headless.c b/ui/egl-headless.c
index ba27efcf4c..c3f319c096 100644
--- a/ui/egl-headless.c
+++ b/ui/egl-headless.c
@@ -52,6 +52,8 @@ static void egl_scanout_disable(DisplayChangeListener *dcl)
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, qemu_egl_rn_ctx);
egl_fb_destroy(&edpy->guest_fb);
egl_fb_destroy(&edpy->blit_fb);
}
@@ -69,6 +71,9 @@ static void egl_scanout_texture(DisplayChangeListener *dcl,
edpy->y_0_top = backing_y_0_top;
+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, qemu_egl_rn_ctx);
+
/* source framebuffer */
egl_fb_setup_for_tex(&edpy->guest_fb,
backing_width, backing_height, backing_id, false);
@@ -88,6 +93,8 @@ static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
{
uint32_t width, height, texture;
+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, qemu_egl_rn_ctx);
egl_dmabuf_import_texture(dmabuf);
texture = qemu_dmabuf_get_texture(dmabuf);
if (!texture) {
@@ -108,6 +115,8 @@ static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
uint32_t width, height, texture;
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, qemu_egl_rn_ctx);
if (dmabuf) {
egl_dmabuf_import_texture(dmabuf);
texture = qemu_dmabuf_get_texture(dmabuf);
@@ -126,6 +135,8 @@ static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
static void egl_release_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
{
+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, qemu_egl_rn_ctx);
egl_dmabuf_release_texture(dmabuf);
}
@@ -151,6 +162,8 @@ static void egl_scanout_flush(DisplayChangeListener *dcl,
}
assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8);
+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, qemu_egl_rn_ctx);
if (edpy->cursor_fb.texture) {
/* have cursor -> render using textures */
egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb,
@@ -225,6 +238,8 @@ static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
egl_dpys = g_ptr_array_new();
+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, qemu_egl_rn_ctx);
for (idx = 0;; idx++) {
DisplayGLCtx *ctx;
@@ -250,6 +265,8 @@ static void egl_headless_cleanup(void)
return;
}
+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, qemu_egl_rn_ctx);
for (guint i = 0; i < egl_dpys->len; i++) {
egl_dpy *edpy = g_ptr_array_index(egl_dpys, i);
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 8623181336..d595916476 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -426,6 +426,18 @@ void gtk_egl_init(DisplayGLMode mode)
display_opengl = 1;
}
+void gd_egl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_GBM
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, vc->gfx.ectx);
+ gd_release_dmabuf(vc, dmabuf);
+#endif
+}
+
int gd_egl_make_current(DisplayGLCtx *dgc,
QEMUGLContext ctx)
{
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index 45c0c8c81a..effd88f287 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -360,6 +360,17 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
#endif
}
+void gd_gl_area_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_GBM
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+ gd_release_dmabuf(vc, dmabuf);
+#endif
+}
+
void gtk_gl_area_init(void)
{
display_opengl = 1;
diff --git a/ui/gtk.c b/ui/gtk.c
index 55e45e8445..c615d35451 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -584,21 +584,16 @@ static bool gd_has_dmabuf(DisplayChangeListener *dcl)
return vc->gfx.has_dmabuf;
}
-static void gd_gl_release_dmabuf(DisplayChangeListener *dcl,
- QemuDmaBuf *dmabuf)
-{
#ifdef CONFIG_GBM
- VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
-
+void gd_release_dmabuf(VirtualConsole *vc, QemuDmaBuf *dmabuf)
+{
egl_dmabuf_release_texture(dmabuf);
if (vc->gfx.guest_fb.dmabuf == dmabuf) {
vc->gfx.guest_fb.dmabuf = NULL;
vc->gfx.draw_submitted = false;
}
-#endif
}
-#ifdef CONFIG_GBM
static void gd_gl_fence_cb(void *vcon)
{
VirtualConsole *vc = vcon;
@@ -639,7 +634,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
.dpy_gl_scanout_disable = gd_gl_area_scanout_disable,
.dpy_gl_update = gd_gl_area_scanout_flush,
.dpy_gl_scanout_dmabuf = gd_gl_area_scanout_dmabuf,
- .dpy_gl_release_dmabuf = gd_gl_release_dmabuf,
+ .dpy_gl_release_dmabuf = gd_gl_area_release_dmabuf,
.dpy_has_dmabuf = gd_has_dmabuf,
};
@@ -673,7 +668,7 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_gl_cursor_dmabuf = gd_egl_cursor_dmabuf,
.dpy_gl_cursor_position = gd_egl_cursor_position,
.dpy_gl_update = gd_egl_flush,
- .dpy_gl_release_dmabuf = gd_gl_release_dmabuf,
+ .dpy_gl_release_dmabuf = gd_egl_release_dmabuf,
.dpy_has_dmabuf = gd_has_dmabuf,
};
diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c
index 46eff768c9..f41fc8bb96 100644
--- a/ui/sdl2-gl.c
+++ b/ui/sdl2-gl.c
@@ -200,6 +200,7 @@ void sdl2_gl_scanout_disable(DisplayChangeListener *dcl)
struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
assert(scon->opengl);
+ SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
scon->w = 0;
scon->h = 0;
sdl2_set_scanout_mode(scon, false);
@@ -288,6 +289,7 @@ void sdl2_gl_release_dmabuf(DisplayChangeListener *dcl,
{
struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+ SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
egl_dmabuf_release_texture(dmabuf);
if (scon->guest_fb.dmabuf == dmabuf) {
scon->guest_fb.dmabuf = NULL;
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 75c7df7bb5..22cd1e266c 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -1176,6 +1176,8 @@ static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl,
ssd->guest_dmabuf = NULL;
ssd->guest_dmabuf_refresh = false;
}
+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, qemu_egl_rn_ctx);
egl_dmabuf_release_texture(dmabuf);
}