@samitouri / QOSamiQemu / commits / 53f0d87db7

ui/gtk: Narrow DMA-BUF critical section

Scanout operations need to be properly ordered to avoid tearing. The virtio specification allows the guest to use pageflip. With pageflip, the guest only modifies the invisible framebuffer while the host scans out the visible framebuffer. The guest may choose not to use pageflip to avoid its overhead, accepting the risk of tearing. ui/gtk performs the following procedure to flush a scanout: 1) Queue a draw event. 2) The draw event gets triggered. 3) Blit the guest framebuffer to the host framebuffer. When flushing a DMA-BUF scanout, ui/gtk blocks the device before 2) if possible and unblocks it after 3) to enforce proper ordering. However, blocking the device before 2) has two problems. First, it can leave the device blocked indefinitely because GTK sometimes decides to cancel 2) when the window is not visible for example. ui/gtk regularly repeats 1) as a workaround, but it is not applicable to GtkGLArea because it causes display corruption. Second, the behavior is inconsistent with the other types of scanout that leave the device unblocked between 1) and 2). To fix these problems, let ui/gtk block the device only when the queued draw event runs, immediately before 3). Blocking before that is unnecessary since ui/gtk does not access the framebuffer yet. If the guest does not use pageflip but instead updates the visible framebuffer directly, ui/gtk should not add the overhead of a pre-draw block. ui/gtk still blocks the device during 3) for DMA-BUF. Unlike the other scanout types, 3) can happen asynchronously with the device for a DMA-BUF, so ui/gtk needs to keep the visible guest framebuffer stable for the blit. With the problems fixed, the workaround to repeat 1) is no longer necessary and is removed. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260628-gtk-v2-1-1e4839012f09@rsg.ci.i.u-tokyo.ac.jp>

Akihiko Odaki committed Jun 28, 2026 at 22:58 UTC 53f0d87db7b4bc4b8c906eedcce6fec17d39824f
2 files changed +3 -26
ui/gtk-egl.c
+2 -4
@@ -91,6 +91,7 @@ void gd_egl_draw(VirtualConsole *vc)
91 } else {
92 qemu_dmabuf_set_draw_submitted(dmabuf, false);
93 }
94 + qemu_console_hw_gl_block(vc->gfx.dcl.con, true);
95 }
96 #endif
97 gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h);
@@ -405,14 +406,11 @@ void gd_egl_flush(DisplayChangeListener *dcl,
406
407 if (vc->gfx.guest_fb.dmabuf &&
408 !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
408 - qemu_console_hw_gl_block(vc->gfx.dcl.con, true);
409 qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true);
410 gtk_egl_set_scanout_mode(vc, true);
411 - gtk_widget_queue_draw_area(area, x, y, w, h);
412 - return;
411 }
412
415 - gd_egl_scanout_flush(&vc->gfx.dcl, x, y, w, h);
413 + gtk_widget_queue_draw_area(area, x, y, w, h);
414 }
415
416 void gtk_egl_init(DisplayGLMode mode)
ui/gtk-gl-area.c
+1 -22
@@ -86,6 +86,7 @@ void gd_gl_area_draw(VirtualConsole *vc)
86 } else {
87 qemu_dmabuf_set_draw_submitted(dmabuf, false);
88 }
89 + qemu_console_hw_gl_block(vc->gfx.dcl.con, true);
90 }
91 #endif
92
@@ -163,27 +164,6 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl)
164
165 gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area);
166
166 - if (vc->gfx.guest_fb.dmabuf &&
167 - qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
168 - /*
169 - * gd_egl_refresh() calls gd_egl_draw() if a DMA-BUF draw has already
170 - * been submitted, but this function does not call gd_gl_area_draw() in
171 - * such a case due to display corruption.
172 - *
173 - * Calling gd_gl_area_draw() is necessary to prevent a situation where
174 - * there is a scheduled draw event but it won't happen bacause the window
175 - * is currently in inactive state (minimized or tabified). If draw is not
176 - * done for a long time, gl_block timeout and/or fence timeout (on the
177 - * guest) will happen eventually.
178 - *
179 - * However, it is found that calling gd_gl_area_draw() here causes guest
180 - * display corruption on a Wayland Compositor. The display corruption is
181 - * more serious than the possible fence timeout so gd_gl_area_draw() is
182 - * omitted for now.
183 - */
184 - return;
185 - }
186 -
167 if (!vc->gfx.gls) {
168 if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
169 return;
@@ -347,7 +327,6 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
327
328 if (vc->gfx.guest_fb.dmabuf &&
329 !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
350 - qemu_console_hw_gl_block(vc->gfx.dcl.con, true);
330 qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true);
331 gtk_gl_area_set_scanout_mode(vc, true);
332 }