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
