From: Akihiko Odaki <[email protected]>

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 <[email protected]>
Acked-by: Marc-André Lureau <[email protected]>
Message-ID: <[email protected]>
---
 ui/gtk-egl.c     |  6 ++----
 ui/gtk-gl-area.c | 23 +----------------------
 2 files changed, 3 insertions(+), 26 deletions(-)

diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 7c5c9b2428c..adc81f34b1c 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);
             }
+            qemu_console_hw_gl_block(vc->gfx.dcl.con, true);
         }
 #endif
         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,
 
     if (vc->gfx.guest_fb.dmabuf &&
         !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
-        qemu_console_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);
-        return;
     }
 
-    gd_egl_scanout_flush(&vc->gfx.dcl, x, y, w, h);
+    gtk_widget_queue_draw_area(area, x, y, w, h);
 }
 
 void gtk_egl_init(DisplayGLMode mode)
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index 23806b9d01b..29497019ee4 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);
             }
+            qemu_console_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;
@@ -347,7 +327,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)) {
-        qemu_console_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);
     }

-- 
2.55.0


Reply via email to