Hi Akihiko On Thu, Jan 15, 2026 at 9:44 AM Akihiko Odaki <[email protected]> wrote: > > ui/gtk performs the following procedure to flush a scanout: > 1) Queue a draw event. > 2) The draw event gets triggered. > 3) Blit the scanout to the framebuffer. > > When flushing a DMA-BUF scanout, ui/gtk blocks the device before 2) if > possible and unblocks it after 3). > > 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 leaves the device unblocked between 1) and 2). > > To fix these problems, let ui/gtk block the device after 2) instead.
Wouldn't that let the device overwrite the buffer before it is displayed? I wish there would be a clear design for how virtio-gpu handles display buffering.. I suppose more demanding users, like crosvm may have documented this. > Note that the device is still blocked during 3) for DMA-BUF; this is > because, unlike the other scanout types, 3) can happen asynchronously > with the device for a DMA-BUF, and the device may simulatenously > overwrite it, resulting in tearing, if it is left unblocked. > > With the problems fixed, the workaround to repeat 1) is no longer > necessary and removed. > > Signed-off-by: Akihiko Odaki <[email protected]> > --- > ui/gtk-egl.c | 8 +------- > ui/gtk-gl-area.c | 23 +---------------------- > 2 files changed, 2 insertions(+), 29 deletions(-) > > diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c > index ae9239999cdb..61bb8d731ac5 100644 > --- a/ui/gtk-egl.c > +++ b/ui/gtk-egl.c > @@ -91,6 +91,7 @@ void gd_egl_draw(VirtualConsole *vc) > } else { > qemu_dmabuf_set_draw_submitted(dmabuf, false); > } > + graphic_hw_gl_block(vc->gfx.dcl.con, true); > } > #endif > gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); > @@ -152,12 +153,6 @@ void gd_egl_refresh(DisplayChangeListener *dcl) > gd_update_monitor_refresh_rate( > vc, vc->window ? vc->window : vc->gfx.drawing_area); > > - if (vc->gfx.guest_fb.dmabuf && > - qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { > - gd_egl_draw(vc); > - return; > - } > - > if (!vc->gfx.esurface) { > gd_egl_init(vc); > if (!vc->gfx.esurface) { > @@ -408,7 +403,6 @@ void gd_egl_flush(DisplayChangeListener *dcl, > > if (vc->gfx.guest_fb.dmabuf && > !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { > - graphic_hw_gl_block(vc->gfx.dcl.con, true); > qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); > gtk_egl_set_scanout_mode(vc, true); > gtk_widget_queue_draw_area(area, x, y, w, h); > diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c > index cd86022d264a..9e7ec7043037 100644 > --- a/ui/gtk-gl-area.c > +++ b/ui/gtk-gl-area.c > @@ -86,6 +86,7 @@ void gd_gl_area_draw(VirtualConsole *vc) > } else { > qemu_dmabuf_set_draw_submitted(dmabuf, false); > } > + graphic_hw_gl_block(vc->gfx.dcl.con, true); > } > #endif > > @@ -163,27 +164,6 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl) > > gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : > vc->gfx.drawing_area); > > - if (vc->gfx.guest_fb.dmabuf && > - qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { > - /* > - * gd_egl_refresh() calls gd_egl_draw() if a DMA-BUF draw has already > - * been submitted, but this function does not call gd_gl_area_draw() > in > - * such a case due to display corruption. > - * > - * Calling gd_gl_area_draw() is necessary to prevent a situation > where > - * there is a scheduled draw event but it won't happen bacause the > window > - * is currently in inactive state (minimized or tabified). If draw > is not > - * done for a long time, gl_block timeout and/or fence timeout (on > the > - * guest) will happen eventually. > - * > - * However, it is found that calling gd_gl_area_draw() here causes > guest > - * display corruption on a Wayland Compositor. The display > corruption is > - * more serious than the possible fence timeout so gd_gl_area_draw() > is > - * omitted for now. > - */ > - return; > - } > - > if (!vc->gfx.gls) { > if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { > return; > @@ -342,7 +322,6 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, > > if (vc->gfx.guest_fb.dmabuf && > !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { > - graphic_hw_gl_block(vc->gfx.dcl.con, true); > qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); > gtk_gl_area_set_scanout_mode(vc, true); > } > > --- > base-commit: 667e1fff878326c35c7f5146072e60a63a9a41c8 > change-id: 20260115-gtk-424c2b910e65 > > Best regards, > -- > Akihiko Odaki <[email protected]> > > -- Marc-André Lureau
