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

Reply via email to