From ada6f6f4a34351c1320d30e4fc495c1d536bd19c Mon Sep 17 00:00:00 2001 From: Alex Chen Date: Thu, 19 Nov 2020 02:58:51 +0000 Subject: [PATCH 01/24] hw/display/qxl: Fix bad printf format specifiers We should use printf format specifier "%u" instead of "%d" for argument of type "unsigned int". Reported-by: Euler Robot Signed-off-by: Alex Chen Message-id: 20201119025851.56487-1-alex.chen@huawei.com Signed-off-by: Gerd Hoffmann --- hw/display/qxl-logger.c | 4 ++-- hw/display/qxl.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/display/qxl-logger.c b/hw/display/qxl-logger.c index c15175bce3..68bfa47568 100644 --- a/hw/display/qxl-logger.c +++ b/hw/display/qxl-logger.c @@ -189,7 +189,7 @@ static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd) qxl_name(qxl_surface_cmd, cmd->type), cmd->surface_id); if (cmd->type == QXL_SURFACE_CMD_CREATE) { - fprintf(stderr, " size %dx%d stride %d format %s (count %d, max %d)", + fprintf(stderr, " size %dx%d stride %d format %s (count %u, max %u)", cmd->u.surface_create.width, cmd->u.surface_create.height, cmd->u.surface_create.stride, @@ -197,7 +197,7 @@ static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd) qxl->guest_surfaces.count, qxl->guest_surfaces.max); } if (cmd->type == QXL_SURFACE_CMD_DESTROY) { - fprintf(stderr, " (count %d)", qxl->guest_surfaces.count); + fprintf(stderr, " (count %u)", qxl->guest_surfaces.count); } } diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 431c107096..874bb8ad9e 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -944,7 +944,7 @@ static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie) qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id); break; default: - fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__, + fprintf(stderr, "qxl: %s: unexpected current_async %u\n", __func__, current_async); } qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD); From c502758670432195d61ff848b1b47b0f78918ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 2 Feb 2021 00:14:21 +0400 Subject: [PATCH 02/24] qxl: set qxl.ssd.dcl.con on secondary devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On secondary QXL devices, the console is only set on qxl.vga.con. But graphic_hw_update_done() is called with qxl.ssd.dcl.con. Like for primary QXL devices, set qxl.sdd.dcl.con = qxl.vga.con. Signed-off-by: Marc-André Lureau Message-Id: <20210201201422.446552-2-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/display/qxl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 874bb8ad9e..6784d32920 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2266,6 +2266,7 @@ static void qxl_realize_secondary(PCIDevice *dev, Error **errp) qxl->vga.vram_size, &error_fatal); qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram); qxl->vga.con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl); + qxl->ssd.dcl.con = qxl->vga.con; qxl->id = qemu_console_get_index(qxl->vga.con); /* == channel_id */ qxl_realize_common(qxl, errp); From b577ab2dda3afc7d6a7befabcf226507ff06c17c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 2 Feb 2021 00:14:22 +0400 Subject: [PATCH 03/24] qxl: also notify the rendering is done when skipping it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Asynchronous handlers may be waiting for the graphic_hw_update_done() to be called in this case too. Fixes: 4d6316218 ("console: add graphic_hw_update_done()") Signed-off-by: Marc-André Lureau Message-Id: <20210201201422.446552-3-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/display/qxl-render.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index 3ce2e57b8f..d28849b121 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -181,6 +181,7 @@ void qxl_render_update(PCIQXLDevice *qxl) qxl->mode == QXL_MODE_UNDEFINED) { qxl_render_update_area_unlocked(qxl); qemu_mutex_unlock(&qxl->ssd.lock); + graphic_hw_update_done(qxl->ssd.dcl.con); return; } From a652b120130e89850edb18cebd222310b77218f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 29 Jan 2021 19:23:51 +0400 Subject: [PATCH 04/24] spice: delay starting until display are initialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU used to run qemu_spice.display_init() before vm_start(), and QXL/display interfaces where started then. Now, vm_start() happens before QXL/display interfaces are added and Spice server doesn't automatically start them in this case (fixed in spice git) Fixes Spice regression introduced after 5.2, with refactoring commits b4e1a34211 ("vl: remove separate preconfig main_loop") and facf7c60ee ("vl: initialize displays _after_ exiting preconfiguration"), probably others. Signed-off-by: Marc-André Lureau Message-Id: <20210129152351.161971-1-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- include/ui/qemu-spice.h | 1 + ui/spice-core.c | 9 ++++++++- ui/spice-display.c | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/ui/qemu-spice.h b/include/ui/qemu-spice.h index 2beb792972..71ecd6cfd1 100644 --- a/include/ui/qemu-spice.h +++ b/include/ui/qemu-spice.h @@ -28,6 +28,7 @@ void qemu_spice_input_init(void); void qemu_spice_display_init(void); +void qemu_spice_display_init_done(void); bool qemu_spice_have_display_interface(QemuConsole *con); int qemu_spice_add_display_interface(QXLInstance *qxlin, QemuConsole *con); int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, diff --git a/ui/spice-core.c b/ui/spice-core.c index 514c0f9754..beee932f55 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -625,6 +625,14 @@ static void vm_change_state_handler(void *opaque, int running, } } +void qemu_spice_display_init_done(void) +{ + if (runstate_is_running()) { + qemu_spice_display_start(); + } + qemu_add_vm_change_state_handler(vm_change_state_handler, NULL); +} + static void qemu_spice_init(void) { QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); @@ -796,7 +804,6 @@ static void qemu_spice_init(void) qemu_spice_input_init(); - qemu_add_vm_change_state_handler(vm_change_state_handler, NULL); qemu_spice_display_stop(); g_free(x509_key_file); diff --git a/ui/spice-display.c b/ui/spice-display.c index 0178d5766d..3d3e3bcb22 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -1188,4 +1188,6 @@ void qemu_spice_display_init(void) } qemu_spice_display_init_one(con); } + + qemu_spice_display_init_done(); } From 326a51f32dfa56a63815c06c2ba3b14b371cef35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:13 +0400 Subject: [PATCH 05/24] vhost-user-gpu: check backend for EDID support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EDID has been enabled by default, but the backend may not implement it (such as the contrib backend). This results in extra warnings and potentially other issues in the guest. The option shouldn't probably have been added to VIRTIO_GPU_BASE, but it's a bit too late now, report an error and disable EDID when it's not available. Fixes: 0a7196625 ("edid: flip the default to enabled") Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-2-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/display/vhost-user-gpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 51f1747c4a..55b0ed15f2 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -555,6 +555,12 @@ vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp) if (virtio_has_feature(g->vhost->dev.features, VIRTIO_GPU_F_VIRGL)) { g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED; } + if (virtio_has_feature(g->vhost->dev.features, VIRTIO_GPU_F_EDID)) { + g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_EDID_ENABLED; + } else { + error_report("EDID requested but the backend doesn't support it."); + g->parent_obj.conf.flags &= ~(1 << VIRTIO_GPU_FLAG_EDID_ENABLED); + } if (!virtio_gpu_base_device_realize(qdev, NULL, NULL, errp)) { return; From 1f83ea8dbd9b9698767770746b92ff7a9ccb9636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:14 +0400 Subject: [PATCH 06/24] vhost-user-gpu: handle vhost-user-gpu features in a callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes a deadlock where the backend calls QEMU, while QEMU also calls the backend simultaneously, both ends waiting for each other. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-3-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- contrib/vhost-user-gpu/vhost-user-gpu.c | 37 ++++++++++++++++++------- contrib/vhost-user-gpu/vugpu.h | 2 +- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index f445ef28ec..85c16404fb 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -124,7 +124,7 @@ source_wait_cb(gint fd, GIOCondition condition, gpointer user_data) } /* resume */ - g->wait_ok = 0; + g->wait_in = 0; vg_handle_ctrl(&g->dev.parent, 0); return G_SOURCE_REMOVE; @@ -133,8 +133,8 @@ source_wait_cb(gint fd, GIOCondition condition, gpointer user_data) void vg_wait_ok(VuGpu *g) { - assert(g->wait_ok == 0); - g->wait_ok = g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, + assert(g->wait_in == 0); + g->wait_in = g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, source_wait_cb, g); } @@ -270,7 +270,7 @@ vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) .size = 0, }; - assert(vg->wait_ok == 0); + assert(vg->wait_in == 0); vg_send_msg(vg, &msg, -1); if (!vg_recv_msg(vg, msg.request, sizeof(dpy_info), &dpy_info)) { @@ -815,7 +815,7 @@ vg_handle_ctrl(VuDev *dev, int qidx) size_t len; for (;;) { - if (vg->wait_ok != 0) { + if (vg->wait_in != 0) { return; } @@ -969,18 +969,17 @@ vg_queue_set_started(VuDev *dev, int qidx, bool started) } } -static void -set_gpu_protocol_features(VuGpu *g) +static gboolean +protocol_features_cb(gint fd, GIOCondition condition, gpointer user_data) { + VuGpu *g = user_data; uint64_t u64; VhostUserGpuMsg msg = { .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES }; - assert(g->wait_ok == 0); - vg_send_msg(g, &msg, -1); if (!vg_recv_msg(g, msg.request, sizeof(u64), &u64)) { - return; + return G_SOURCE_CONTINUE; } msg = (VhostUserGpuMsg) { @@ -989,6 +988,24 @@ set_gpu_protocol_features(VuGpu *g) .payload.u64 = 0 }; vg_send_msg(g, &msg, -1); + + g->wait_in = 0; + vg_handle_ctrl(&g->dev.parent, 0); + + return G_SOURCE_REMOVE; +} + +static void +set_gpu_protocol_features(VuGpu *g) +{ + VhostUserGpuMsg msg = { + .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES + }; + + vg_send_msg(g, &msg, -1); + assert(g->wait_in == 0); + g->wait_in = g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, + protocol_features_cb, g); } static int diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h index ad664c4df8..86f3ac86aa 100644 --- a/contrib/vhost-user-gpu/vugpu.h +++ b/contrib/vhost-user-gpu/vugpu.h @@ -118,7 +118,7 @@ typedef struct VuGpu { int sock_fd; int drm_rnode_fd; GSource *renderer_source; - guint wait_ok; + guint wait_in; bool virgl; bool virgl_inited; From 72e631c6ac72363924f6c7030a99a7900d15451e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:15 +0400 Subject: [PATCH 07/24] vhost-user-gpu: use an extandable state enum for commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a pending state for commands which aren't finished yet, but are being handled. See following patch. Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20210204105232.834642-4-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- contrib/vhost-user-gpu/vhost-user-gpu.c | 8 ++++---- contrib/vhost-user-gpu/virgl.c | 2 +- contrib/vhost-user-gpu/vugpu.h | 8 +++++++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index 85c16404fb..7dcc02966c 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -246,7 +246,7 @@ vg_ctrl_response(VuGpu *g, } vu_queue_push(&g->dev.parent, cmd->vq, &cmd->elem, s); vu_queue_notify(&g->dev.parent, cmd->vq); - cmd->finished = true; + cmd->state = VG_CMD_STATE_FINISHED; } void @@ -800,7 +800,7 @@ vg_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; break; } - if (!cmd->finished) { + if (cmd->state == VG_CMD_STATE_NEW) { vg_ctrl_response_nodata(vg, cmd, cmd->error ? cmd->error : VIRTIO_GPU_RESP_OK_NODATA); } @@ -825,7 +825,7 @@ vg_handle_ctrl(VuDev *dev, int qidx) } cmd->vq = vq; cmd->error = 0; - cmd->finished = false; + cmd->state = VG_CMD_STATE_NEW; len = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, &cmd->cmd_hdr, sizeof(cmd->cmd_hdr)); @@ -844,7 +844,7 @@ vg_handle_ctrl(VuDev *dev, int qidx) vg_process_cmd(vg, cmd); } - if (!cmd->finished) { + if (cmd->state != VG_CMD_STATE_FINISHED) { QTAILQ_INSERT_TAIL(&vg->fenceq, cmd, next); vg->inflight++; } else { diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c index e647278052..8bb3c563d9 100644 --- a/contrib/vhost-user-gpu/virgl.c +++ b/contrib/vhost-user-gpu/virgl.c @@ -482,7 +482,7 @@ void vg_virgl_process_cmd(VuGpu *g, struct virtio_gpu_ctrl_command *cmd) break; } - if (cmd->finished) { + if (cmd->state != VG_CMD_STATE_NEW) { return; } diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h index 86f3ac86aa..04d5615812 100644 --- a/contrib/vhost-user-gpu/vugpu.h +++ b/contrib/vhost-user-gpu/vugpu.h @@ -129,12 +129,18 @@ typedef struct VuGpu { QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq; } VuGpu; +enum { + VG_CMD_STATE_NEW, + VG_CMD_STATE_PENDING, + VG_CMD_STATE_FINISHED, +}; + struct virtio_gpu_ctrl_command { VuVirtqElement elem; VuVirtq *vq; struct virtio_gpu_ctrl_hdr cmd_hdr; uint32_t error; - bool finished; + int state; QTAILQ_ENTRY(virtio_gpu_ctrl_command) next; }; From bd690feb155416defd1119aaad1c9fbe623c865f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:16 +0400 Subject: [PATCH 08/24] vhost-user-gpu: handle display-info in a callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes a deadlock where the backend calls QEMU, while QEMU also calls the backend simultaneously, both ends waiting for each other. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-5-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- contrib/vhost-user-gpu/vhost-user-gpu.c | 33 ++++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index 7dcc02966c..b27990ffdb 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -261,10 +261,33 @@ vg_ctrl_response_nodata(VuGpu *g, vg_ctrl_response(g, cmd, &resp, sizeof(resp)); } + +static gboolean +get_display_info_cb(gint fd, GIOCondition condition, gpointer user_data) +{ + struct virtio_gpu_resp_display_info dpy_info = { {} }; + VuGpu *vg = user_data; + struct virtio_gpu_ctrl_command *cmd = QTAILQ_LAST(&vg->fenceq); + + g_debug("disp info cb"); + assert(cmd->cmd_hdr.type == VIRTIO_GPU_CMD_GET_DISPLAY_INFO); + if (!vg_recv_msg(vg, VHOST_USER_GPU_GET_DISPLAY_INFO, + sizeof(dpy_info), &dpy_info)) { + return G_SOURCE_CONTINUE; + } + + QTAILQ_REMOVE(&vg->fenceq, cmd, next); + vg_ctrl_response(vg, cmd, &dpy_info.hdr, sizeof(dpy_info)); + + vg->wait_in = 0; + vg_handle_ctrl(&vg->dev.parent, 0); + + return G_SOURCE_REMOVE; +} + void vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) { - struct virtio_gpu_resp_display_info dpy_info = { {} }; VhostUserGpuMsg msg = { .request = VHOST_USER_GPU_GET_DISPLAY_INFO, .size = 0, @@ -273,11 +296,9 @@ vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) assert(vg->wait_in == 0); vg_send_msg(vg, &msg, -1); - if (!vg_recv_msg(vg, msg.request, sizeof(dpy_info), &dpy_info)) { - return; - } - - vg_ctrl_response(vg, cmd, &dpy_info.hdr, sizeof(dpy_info)); + vg->wait_in = g_unix_fd_add(vg->sock_fd, G_IO_IN | G_IO_HUP, + get_display_info_cb, vg); + cmd->state = VG_CMD_STATE_PENDING; } static void From f988e3c0c60dd25352281d9c6cf3df18532ca26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:17 +0400 Subject: [PATCH 09/24] ui: remove extra #ifdef CONFIG_OPENGL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 5cb69566daa8081abb82a13403dcc0fffed02007 ("gtk: remove CONFIG_GTK_GL"), some #ifdef are redundants. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-6-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 26665cd2e6..e1ee0840b3 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -625,8 +625,6 @@ static const DisplayChangeListenerOps dcl_ops = { /** DisplayState Callbacks (opengl version) **/ -#if defined(CONFIG_OPENGL) - static const DisplayChangeListenerOps dcl_gl_area_ops = { .dpy_name = "gtk-egl", .dpy_gfx_update = gd_gl_area_update, @@ -644,8 +642,6 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = { .dpy_gl_update = gd_gl_area_scanout_flush, }; -#endif /* CONFIG_OPENGL */ - static const DisplayChangeListenerOps dcl_egl_ops = { .dpy_name = "gtk-egl", .dpy_gfx_update = gd_egl_update, @@ -1993,13 +1989,10 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, #if defined(CONFIG_OPENGL) if (display_opengl) { -#if defined(CONFIG_OPENGL) if (gtk_use_gl_area) { vc->gfx.drawing_area = gtk_gl_area_new(); vc->gfx.dcl.ops = &dcl_gl_area_ops; - } else -#endif /* CONFIG_OPENGL */ - { + } else { vc->gfx.drawing_area = gtk_drawing_area_new(); /* * gtk_widget_set_double_buffered() was deprecated in 3.14. From c110d949b8166a633179edcf3390a42673ac843c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:18 +0400 Subject: [PATCH 10/24] ui: remove gl_ctx_get_current MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no users left. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-7-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- include/ui/console.h | 2 -- include/ui/egl-context.h | 1 - include/ui/sdl2.h | 1 - ui/console.c | 6 ------ ui/egl-context.c | 5 ----- ui/egl-headless.c | 1 - ui/gtk.c | 2 -- ui/sdl2-gl.c | 8 -------- ui/sdl2.c | 1 - ui/spice-display.c | 1 - 10 files changed, 28 deletions(-) diff --git a/include/ui/console.h b/include/ui/console.h index 7a3fc11abf..ce6c72e37c 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -201,7 +201,6 @@ typedef struct DisplayChangeListenerOps { QEMUGLContext ctx); int (*dpy_gl_ctx_make_current)(DisplayChangeListener *dcl, QEMUGLContext ctx); - QEMUGLContext (*dpy_gl_ctx_get_current)(DisplayChangeListener *dcl); void (*dpy_gl_scanout_disable)(DisplayChangeListener *dcl); void (*dpy_gl_scanout_texture)(DisplayChangeListener *dcl, @@ -303,7 +302,6 @@ QEMUGLContext dpy_gl_ctx_create(QemuConsole *con, QEMUGLParams *params); void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx); int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx); -QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con); bool console_has_gl(QemuConsole *con); bool console_has_gl_dmabuf(QemuConsole *con); diff --git a/include/ui/egl-context.h b/include/ui/egl-context.h index f004ce11a7..9374fe41e3 100644 --- a/include/ui/egl-context.h +++ b/include/ui/egl-context.h @@ -9,6 +9,5 @@ QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl, void qemu_egl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx); int qemu_egl_make_context_current(DisplayChangeListener *dcl, QEMUGLContext ctx); -QEMUGLContext qemu_egl_get_current_context(DisplayChangeListener *dcl); #endif /* EGL_CONTEXT_H */ diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h index 0875b8d56b..f85c117a78 100644 --- a/include/ui/sdl2.h +++ b/include/ui/sdl2.h @@ -70,7 +70,6 @@ QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl, void sdl2_gl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx); int sdl2_gl_make_context_current(DisplayChangeListener *dcl, QEMUGLContext ctx); -QEMUGLContext sdl2_gl_get_current_context(DisplayChangeListener *dcl); void sdl2_gl_scanout_disable(DisplayChangeListener *dcl); void sdl2_gl_scanout_texture(DisplayChangeListener *dcl, diff --git a/ui/console.c b/ui/console.c index d80ce7037c..c0b1a3689c 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1803,12 +1803,6 @@ int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx) return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx); } -QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con) -{ - assert(con->gl); - return con->gl->ops->dpy_gl_ctx_get_current(con->gl); -} - void dpy_gl_scanout_disable(QemuConsole *con) { assert(con->gl); diff --git a/ui/egl-context.c b/ui/egl-context.c index 4aa1cbb50c..368ffa49d8 100644 --- a/ui/egl-context.c +++ b/ui/egl-context.c @@ -35,8 +35,3 @@ int qemu_egl_make_context_current(DisplayChangeListener *dcl, return eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx); } - -QEMUGLContext qemu_egl_get_current_context(DisplayChangeListener *dcl) -{ - return eglGetCurrentContext(); -} diff --git a/ui/egl-headless.c b/ui/egl-headless.c index fe2a0d1eab..da377a74af 100644 --- a/ui/egl-headless.c +++ b/ui/egl-headless.c @@ -160,7 +160,6 @@ static const DisplayChangeListenerOps egl_ops = { .dpy_gl_ctx_create = egl_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_make_current = qemu_egl_make_context_current, - .dpy_gl_ctx_get_current = qemu_egl_get_current_context, .dpy_gl_scanout_disable = egl_scanout_disable, .dpy_gl_scanout_texture = egl_scanout_texture, diff --git a/ui/gtk.c b/ui/gtk.c index e1ee0840b3..a0e6b60ac4 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -637,7 +637,6 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = { .dpy_gl_ctx_create = gd_gl_area_create_context, .dpy_gl_ctx_destroy = gd_gl_area_destroy_context, .dpy_gl_ctx_make_current = gd_gl_area_make_current, - .dpy_gl_ctx_get_current = gd_gl_area_get_current_context, .dpy_gl_scanout_texture = gd_gl_area_scanout_texture, .dpy_gl_update = gd_gl_area_scanout_flush, }; @@ -654,7 +653,6 @@ static const DisplayChangeListenerOps dcl_egl_ops = { .dpy_gl_ctx_create = gd_egl_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_make_current = gd_egl_make_current, - .dpy_gl_ctx_get_current = qemu_egl_get_current_context, .dpy_gl_scanout_disable = gd_egl_scanout_disable, .dpy_gl_scanout_texture = gd_egl_scanout_texture, .dpy_gl_scanout_dmabuf = gd_egl_scanout_dmabuf, diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c index c73d273bf8..82139ba49e 100644 --- a/ui/sdl2-gl.c +++ b/ui/sdl2-gl.c @@ -185,14 +185,6 @@ int sdl2_gl_make_context_current(DisplayChangeListener *dcl, return SDL_GL_MakeCurrent(scon->real_window, sdlctx); } -QEMUGLContext sdl2_gl_get_current_context(DisplayChangeListener *dcl) -{ - SDL_GLContext sdlctx; - - sdlctx = SDL_GL_GetCurrentContext(); - return (QEMUGLContext)sdlctx; -} - void sdl2_gl_scanout_disable(DisplayChangeListener *dcl) { struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); diff --git a/ui/sdl2.c b/ui/sdl2.c index 5e49709283..a203cb0239 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -781,7 +781,6 @@ static const DisplayChangeListenerOps dcl_gl_ops = { .dpy_gl_ctx_create = sdl2_gl_create_context, .dpy_gl_ctx_destroy = sdl2_gl_destroy_context, .dpy_gl_ctx_make_current = sdl2_gl_make_context_current, - .dpy_gl_ctx_get_current = sdl2_gl_get_current_context, .dpy_gl_scanout_disable = sdl2_gl_scanout_disable, .dpy_gl_scanout_texture = sdl2_gl_scanout_texture, .dpy_gl_update = sdl2_gl_scanout_flush, diff --git a/ui/spice-display.c b/ui/spice-display.c index 3d3e3bcb22..dccac4710a 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -1102,7 +1102,6 @@ static const DisplayChangeListenerOps display_listener_gl_ops = { .dpy_gl_ctx_create = qemu_spice_gl_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_make_current = qemu_egl_make_context_current, - .dpy_gl_ctx_get_current = qemu_egl_get_current_context, .dpy_gl_scanout_disable = qemu_spice_gl_scanout_disable, .dpy_gl_scanout_texture = qemu_spice_gl_scanout_texture, From 568b12fccfe5394326df1ce3e10790608d439a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:19 +0400 Subject: [PATCH 11/24] ui: add gd_gl_area_scanout_disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Require the callback, drop the fallback path. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-8-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- include/ui/gtk.h | 1 + ui/console.c | 7 +------ ui/gtk-gl-area.c | 7 +++++++ ui/gtk.c | 1 + 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/ui/gtk.h b/include/ui/gtk.h index 3f395d7f94..7569d090fa 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -140,6 +140,7 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h); +void gd_gl_area_scanout_disable(DisplayChangeListener *dcl); void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h); void gtk_gl_area_init(void); diff --git a/ui/console.c b/ui/console.c index c0b1a3689c..ab9224429e 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1806,12 +1806,7 @@ int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx) void dpy_gl_scanout_disable(QemuConsole *con) { assert(con->gl); - if (con->gl->ops->dpy_gl_scanout_disable) { - con->gl->ops->dpy_gl_scanout_disable(con->gl); - } else { - con->gl->ops->dpy_gl_scanout_texture(con->gl, 0, false, 0, 0, - 0, 0, 0, 0); - } + con->gl->ops->dpy_gl_scanout_disable(con->gl); } void dpy_gl_scanout_texture(QemuConsole *con, diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 98c22d23f5..96fbe75387 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -198,6 +198,13 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, backing_id, false); } +void gd_gl_area_scanout_disable(DisplayChangeListener *dcl) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + + gtk_gl_area_set_scanout_mode(vc, false); +} + void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { diff --git a/ui/gtk.c b/ui/gtk.c index a0e6b60ac4..00045881b1 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -638,6 +638,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = { .dpy_gl_ctx_destroy = gd_gl_area_destroy_context, .dpy_gl_ctx_make_current = gd_gl_area_make_current, .dpy_gl_scanout_texture = gd_gl_area_scanout_texture, + .dpy_gl_scanout_disable = gd_gl_area_scanout_disable, .dpy_gl_update = gd_gl_area_scanout_flush, }; From 227d8444bb118bc551f324f1046a223cc641b4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:20 +0400 Subject: [PATCH 12/24] ui: annotate DCLOps callback requirements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-9-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- include/ui/console.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/ui/console.h b/include/ui/console.h index ce6c72e37c..bea2b6329a 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -174,35 +174,49 @@ typedef struct DisplayState DisplayState; typedef struct DisplayChangeListenerOps { const char *dpy_name; + /* optional */ void (*dpy_refresh)(DisplayChangeListener *dcl); + /* optional */ void (*dpy_gfx_update)(DisplayChangeListener *dcl, int x, int y, int w, int h); + /* optional */ void (*dpy_gfx_switch)(DisplayChangeListener *dcl, struct DisplaySurface *new_surface); + /* optional */ bool (*dpy_gfx_check_format)(DisplayChangeListener *dcl, pixman_format_code_t format); + /* optional */ void (*dpy_text_cursor)(DisplayChangeListener *dcl, int x, int y); + /* optional */ void (*dpy_text_resize)(DisplayChangeListener *dcl, int w, int h); + /* optional */ void (*dpy_text_update)(DisplayChangeListener *dcl, int x, int y, int w, int h); + /* optional */ void (*dpy_mouse_set)(DisplayChangeListener *dcl, int x, int y, int on); + /* optional */ void (*dpy_cursor_define)(DisplayChangeListener *dcl, QEMUCursor *cursor); + /* required if GL */ QEMUGLContext (*dpy_gl_ctx_create)(DisplayChangeListener *dcl, QEMUGLParams *params); + /* required if GL */ void (*dpy_gl_ctx_destroy)(DisplayChangeListener *dcl, QEMUGLContext ctx); + /* required if GL */ int (*dpy_gl_ctx_make_current)(DisplayChangeListener *dcl, QEMUGLContext ctx); + /* required if GL */ void (*dpy_gl_scanout_disable)(DisplayChangeListener *dcl); + /* required if GL */ void (*dpy_gl_scanout_texture)(DisplayChangeListener *dcl, uint32_t backing_id, bool backing_y_0_top, @@ -210,15 +224,20 @@ typedef struct DisplayChangeListenerOps { uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h); + /* optional */ void (*dpy_gl_scanout_dmabuf)(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf); + /* optional */ void (*dpy_gl_cursor_dmabuf)(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf, bool have_hot, uint32_t hot_x, uint32_t hot_y); + /* optional */ void (*dpy_gl_cursor_position)(DisplayChangeListener *dcl, uint32_t pos_x, uint32_t pos_y); + /* optional */ void (*dpy_gl_release_dmabuf)(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf); + /* required if GL */ void (*dpy_gl_update)(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h); From 2cc002483e9cc075896912ebc3ebd3fe86660902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:21 +0400 Subject: [PATCH 13/24] ui: remove console_has_gl_dmabuf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This check is currently limited. It only is used by vhost-user-gpu (not by vfio-display), and will print an error repeatedly during run-time. We are going to dissociate the GL context from the DisplayChangeListener, and listeners may come and go. The following patches will address this differently. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-10-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/display/vhost-user-gpu.c | 5 ----- include/ui/console.h | 1 - ui/console.c | 5 ----- 3 files changed, 11 deletions(-) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 55b0ed15f2..dd587436ff 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -224,11 +224,6 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) close(dmabuf->fd); dmabuf->fd = -1; } - if (!console_has_gl_dmabuf(con)) { - /* it would be nice to report that error earlier */ - error_report("console doesn't support dmabuf!"); - break; - } dpy_gl_release_dmabuf(con, dmabuf); if (fd == -1) { dpy_gl_scanout_disable(con); diff --git a/include/ui/console.h b/include/ui/console.h index bea2b6329a..ac989fdf70 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -323,7 +323,6 @@ void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx); int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx); bool console_has_gl(QemuConsole *con); -bool console_has_gl_dmabuf(QemuConsole *con); static inline int surface_stride(DisplaySurface *s) { diff --git a/ui/console.c b/ui/console.c index ab9224429e..b5bc3f7699 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1463,11 +1463,6 @@ bool console_has_gl(QemuConsole *con) return con->gl != NULL; } -bool console_has_gl_dmabuf(QemuConsole *con) -{ - return con->gl != NULL && con->gl->ops->dpy_gl_scanout_dmabuf != NULL; -} - void register_displaychangelistener(DisplayChangeListener *dcl) { static const char nodev[] = From ff64d44fb8f6636ece3064f86babe48e3807533d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:22 +0400 Subject: [PATCH 14/24] vhost-user-gpu: add a configuration flag for dmabuf usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's inform VirtioGPUBase that vhost-user-gpu require DMABUF messages. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-11-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/display/vhost-user-gpu.c | 2 ++ include/hw/virtio/virtio-gpu.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index dd587436ff..b7bde9feb6 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -547,6 +547,8 @@ vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp) return; } + /* existing backend may send DMABUF, so let's add that requirement */ + g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_DMABUF_ENABLED; if (virtio_has_feature(g->vhost->dev.features, VIRTIO_GPU_F_VIRGL)) { g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED; } diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 1aed7275c8..4f3dbf79f9 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -71,6 +71,7 @@ enum virtio_gpu_base_conf_flags { VIRTIO_GPU_FLAG_VIRGL_ENABLED = 1, VIRTIO_GPU_FLAG_STATS_ENABLED, VIRTIO_GPU_FLAG_EDID_ENABLED, + VIRTIO_GPU_FLAG_DMABUF_ENABLED, }; #define virtio_gpu_virgl_enabled(_cfg) \ @@ -79,6 +80,8 @@ enum virtio_gpu_base_conf_flags { (_cfg.flags & (1 << VIRTIO_GPU_FLAG_STATS_ENABLED)) #define virtio_gpu_edid_enabled(_cfg) \ (_cfg.flags & (1 << VIRTIO_GPU_FLAG_EDID_ENABLED)) +#define virtio_gpu_dmabuf_enabled(_cfg) \ + (_cfg.flags & (1 << VIRTIO_GPU_FLAG_DMABUF_ENABLED)) struct virtio_gpu_base_conf { uint32_t max_outputs; From a7dfbe289ede8adb253d735daef88a39709053dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:23 +0400 Subject: [PATCH 15/24] ui: add an optional get_flags callback to GraphicHwOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those flags can be used to express different requirements for the display or other needs. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-12-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/display/virtio-gpu-base.c | 18 ++++++++++++++++++ hw/display/virtio-vga.c | 9 +++++++++ hw/vfio/display.c | 6 ++++++ include/ui/console.h | 9 +++++++++ 4 files changed, 42 insertions(+) diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index 40ccd00f94..f27a6fbe75 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -114,7 +114,25 @@ virtio_gpu_gl_block(void *opaque, bool block) } } +static int +virtio_gpu_get_flags(void *opaque) +{ + VirtIOGPUBase *g = opaque; + int flags = GRAPHIC_FLAGS_NONE; + + if (virtio_gpu_virgl_enabled(g->conf)) { + flags |= GRAPHIC_FLAGS_GL; + } + + if (virtio_gpu_dmabuf_enabled(g->conf)) { + flags |= GRAPHIC_FLAGS_DMABUF; + } + + return flags; +} + static const GraphicHwOps virtio_gpu_ops = { + .get_flags = virtio_gpu_get_flags, .invalidate = virtio_gpu_invalidate_display, .gfx_update = virtio_gpu_update_display, .text_update = virtio_gpu_text_update, diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index 81f776ee36..b071909b68 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -68,7 +68,16 @@ static void virtio_vga_base_gl_block(void *opaque, bool block) } } +static int virtio_vga_base_get_flags(void *opaque) +{ + VirtIOVGABase *vvga = opaque; + VirtIOGPUBase *g = vvga->vgpu; + + return g->hw_ops->get_flags(g); +} + static const GraphicHwOps virtio_vga_base_ops = { + .get_flags = virtio_vga_base_get_flags, .invalidate = virtio_vga_base_invalidate_display, .gfx_update = virtio_vga_base_update_display, .text_update = virtio_vga_base_text_update, diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 42d67e870b..f04473e3ce 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -335,7 +335,13 @@ static void vfio_display_dmabuf_update(void *opaque) } } +static int vfio_display_get_flags(void *opaque) +{ + return GRAPHIC_FLAGS_GL | GRAPHIC_FLAGS_DMABUF; +} + static const GraphicHwOps vfio_display_dmabuf_ops = { + .get_flags = vfio_display_get_flags, .gfx_update = vfio_display_dmabuf_update, .ui_info = vfio_display_edid_ui_info, }; diff --git a/include/ui/console.h b/include/ui/console.h index ac989fdf70..0595aa9953 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -368,7 +368,16 @@ static inline void console_write_ch(console_ch_t *dest, uint32_t ch) *dest = ch; } +enum { + GRAPHIC_FLAGS_NONE = 0, + /* require a console/display with GL callbacks */ + GRAPHIC_FLAGS_GL = 1 << 0, + /* require a console/display with DMABUF import */ + GRAPHIC_FLAGS_DMABUF = 1 << 1, +}; + typedef struct GraphicHwOps { + int (*get_flags)(void *opaque); /* optional, default 0 */ void (*invalidate)(void *opaque); void (*gfx_update)(void *opaque); bool gfx_update_async; /* if true, calls graphic_hw_update_done() */ From d0e137bc9ac9447e669879db4f59eb7f9f961383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:24 +0400 Subject: [PATCH 16/24] ui: add a DCLOps callback to check dmabuf support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-13-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- include/ui/console.h | 2 ++ ui/console.c | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/ui/console.h b/include/ui/console.h index 0595aa9953..875885d9c7 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -224,6 +224,8 @@ typedef struct DisplayChangeListenerOps { uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h); + /* optional (default to true if has dpy_gl_scanout_dmabuf) */ + bool (*dpy_has_dmabuf)(DisplayChangeListener *dcl); /* optional */ void (*dpy_gl_scanout_dmabuf)(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf); diff --git a/ui/console.c b/ui/console.c index b5bc3f7699..a645418ada 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1463,6 +1463,19 @@ bool console_has_gl(QemuConsole *con) return con->gl != NULL; } +static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl) +{ + if (dcl->ops->dpy_has_dmabuf) { + return dcl->ops->dpy_has_dmabuf(dcl); + } + + if (dcl->ops->dpy_gl_scanout_dmabuf) { + return true; + } + + return false; +} + void register_displaychangelistener(DisplayChangeListener *dcl) { static const char nodev[] = From 5983fdf1dcd21f95f1c2b7ecd523140215d99a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:25 +0400 Subject: [PATCH 17/24] ui: check hw requirements during DCL registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-14-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- ui/console.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ui/console.c b/ui/console.c index a645418ada..d8cc640c28 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1476,12 +1476,37 @@ static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl) return false; } +static bool dpy_compatible_with(QemuConsole *con, + DisplayChangeListener *dcl, Error **errp) +{ + ERRP_GUARD(); + int flags; + + flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0; + + if (flags & GRAPHIC_FLAGS_GL && + !console_has_gl(con)) { + error_setg(errp, "The console requires a GL context."); + return false; + + } + + if (flags & GRAPHIC_FLAGS_DMABUF && + !displaychangelistener_has_dmabuf(dcl)) { + error_setg(errp, "The console requires display DMABUF support."); + return false; + } + + return true; +} + void register_displaychangelistener(DisplayChangeListener *dcl) { static const char nodev[] = "This VM has no graphic display device."; static DisplaySurface *dummy; QemuConsole *con; + Error *err = NULL; assert(!dcl->ds); @@ -1496,6 +1521,11 @@ void register_displaychangelistener(DisplayChangeListener *dcl) dcl->con->gl = dcl; } + if (dcl->con && !dpy_compatible_with(dcl->con, dcl, &err)) { + error_report_err(err); + exit(1); + } + trace_displaychangelistener_register(dcl, dcl->ops->dpy_name); dcl->ds = get_alloc_displaystate(); QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next); From 0df5c72b3b6e693d2347409b0e3057186c9173a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:26 +0400 Subject: [PATCH 18/24] ui: add qemu_egl_has_dmabuf helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-15-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- include/ui/egl-helpers.h | 1 + ui/egl-helpers.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h index 94a4b3e6f3..5b1f7fafe0 100644 --- a/include/ui/egl-helpers.h +++ b/include/ui/egl-helpers.h @@ -51,5 +51,6 @@ EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win); int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode); int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode); EGLContext qemu_egl_init_ctx(void); +bool qemu_egl_has_dmabuf(void); #endif /* EGL_HELPERS_H */ diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 7c530c2825..73fe61f878 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -441,6 +441,16 @@ int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode) #endif } +bool qemu_egl_has_dmabuf(void) +{ + if (qemu_egl_display == EGL_NO_DISPLAY) { + return false; + } + + return epoxy_has_egl_extension(qemu_egl_display, + "EGL_EXT_image_dma_buf_import"); +} + EGLContext qemu_egl_init_ctx(void) { static const EGLint ctx_att_core[] = { From 52a37e20dbb880ba909e7d0a89e57f77387d25a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:27 +0400 Subject: [PATCH 19/24] ui: check gtk-egl dmabuf support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-16-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- include/ui/gtk.h | 1 + ui/gtk.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/include/ui/gtk.h b/include/ui/gtk.h index 7569d090fa..aaef884b95 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -48,6 +48,7 @@ typedef struct VirtualGfxConsole { int cursor_y; bool y0_top; bool scanout_mode; + bool has_dmabuf; #endif } VirtualGfxConsole; diff --git a/ui/gtk.c b/ui/gtk.c index 00045881b1..f41c396cb9 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -623,6 +623,13 @@ static const DisplayChangeListenerOps dcl_ops = { #if defined(CONFIG_OPENGL) +static bool gd_has_dmabuf(DisplayChangeListener *dcl) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + + return vc->gfx.has_dmabuf; +} + /** DisplayState Callbacks (opengl version) **/ static const DisplayChangeListenerOps dcl_gl_area_ops = { @@ -661,6 +668,7 @@ static const DisplayChangeListenerOps dcl_egl_ops = { .dpy_gl_cursor_position = gd_egl_cursor_position, .dpy_gl_release_dmabuf = gd_egl_release_dmabuf, .dpy_gl_update = gd_egl_scanout_flush, + .dpy_has_dmabuf = gd_has_dmabuf, }; #endif /* CONFIG_OPENGL */ @@ -2004,6 +2012,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE); #pragma GCC diagnostic pop vc->gfx.dcl.ops = &dcl_egl_ops; + vc->gfx.has_dmabuf = qemu_egl_has_dmabuf(); } } else #endif From 2606519b7492846ddbdf7d2a0b858e4a7ffb0b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:28 +0400 Subject: [PATCH 20/24] ui: add egl dmabuf import to gtkglarea MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GtkGLArea is used on wayland, where EGL is usually available. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-17-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- include/ui/gtk.h | 2 ++ ui/gtk-gl-area.c | 18 ++++++++++++++++++ ui/gtk.c | 22 ++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/include/ui/gtk.h b/include/ui/gtk.h index aaef884b95..3c1cd98db8 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -134,6 +134,8 @@ QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl, QEMUGLParams *params); void gd_gl_area_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx); +void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf); void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_id, bool backing_y_0_top, diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 96fbe75387..72bcd94918 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -213,6 +213,24 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); } +void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf) +{ +#ifdef CONFIG_OPENGL_DMABUF + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + + gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); + egl_dmabuf_import_texture(dmabuf); + if (!dmabuf->texture) { + return; + } + + gd_gl_area_scanout_texture(dcl, dmabuf->texture, + false, dmabuf->width, dmabuf->height, + 0, 0, dmabuf->width, dmabuf->height); +#endif +} + void gtk_gl_area_init(void) { display_opengl = 1; diff --git a/ui/gtk.c b/ui/gtk.c index f41c396cb9..79dc240120 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -627,6 +627,12 @@ static bool gd_has_dmabuf(DisplayChangeListener *dcl) { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + if (gtk_use_gl_area && !gtk_widget_get_realized(vc->gfx.drawing_area)) { + /* FIXME: Assume it will work, actual check done after realize */ + /* fixing this would require delaying listener registration */ + return true; + } + return vc->gfx.has_dmabuf; } @@ -647,6 +653,8 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = { .dpy_gl_scanout_texture = gd_gl_area_scanout_texture, .dpy_gl_scanout_disable = gd_gl_area_scanout_disable, .dpy_gl_update = gd_gl_area_scanout_flush, + .dpy_gl_scanout_dmabuf = gd_gl_area_scanout_dmabuf, + .dpy_has_dmabuf = gd_has_dmabuf, }; static const DisplayChangeListenerOps dcl_egl_ops = { @@ -1983,6 +1991,18 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s) return machine_menu; } +#if defined(CONFIG_OPENGL) +static void gl_area_realize(GtkGLArea *area, VirtualConsole *vc) +{ + gtk_gl_area_make_current(area); + qemu_egl_display = eglGetCurrentDisplay(); + vc->gfx.has_dmabuf = qemu_egl_has_dmabuf(); + if (!vc->gfx.has_dmabuf) { + error_report("GtkGLArea console lacks DMABUF support."); + } +} +#endif + static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, QemuConsole *con, int idx, GSList *group, GtkWidget *view_menu) @@ -1998,6 +2018,8 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, if (display_opengl) { if (gtk_use_gl_area) { vc->gfx.drawing_area = gtk_gl_area_new(); + g_signal_connect(vc->gfx.drawing_area, "realize", + G_CALLBACK(gl_area_realize), vc); vc->gfx.dcl.ops = &dcl_gl_area_ops; } else { vc->gfx.drawing_area = gtk_drawing_area_new(); From f8f3c2719e11145d4f2902c562f7979df741daf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:29 +0400 Subject: [PATCH 21/24] virtio-gpu: avoid re-entering cmdq processing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The next patch will notify the GL context got flush, which will resume the queue processing. However, if this happens within the caller context, it will end up with a stack overflow flush/update loop. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-18-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/display/virtio-gpu.c | 5 +++++ include/hw/virtio/virtio-gpu.h | 1 + 2 files changed, 6 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 0e833a462b..7eb4265a6d 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -814,6 +814,10 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) { struct virtio_gpu_ctrl_command *cmd; + if (g->processing_cmdq) { + return; + } + g->processing_cmdq = true; while (!QTAILQ_EMPTY(&g->cmdq)) { cmd = QTAILQ_FIRST(&g->cmdq); @@ -843,6 +847,7 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) g_free(cmd); } } + g->processing_cmdq = false; } static void virtio_gpu_gl_unblock(VirtIOGPUBase *b) diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 4f3dbf79f9..0043268e90 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -148,6 +148,7 @@ struct VirtIOGPU { uint64_t hostmem; + bool processing_cmdq; bool renderer_inited; bool renderer_reset; QEMUTimer *fence_poll; From 3cddb8b9e0b58eea04e6eb908fc6a12d5af3d3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:30 +0400 Subject: [PATCH 22/24] display/ui: add a callback to indicate GL state is flushed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Displaying rendered resources requires blocking qemu GPU to avoid extra framebuffer copies. For an external display, via Spice currently, there is a callback to block/unblock the rendering in the same thread. But with the vhost-user-gpu backend, the qemu process doesn't handle the rendering itself, and the blocking callback isn't effective. Instead, the backend must be notified when the display code is done. Fix this by adding a new GraphicHwOps callback to indicate the GL state is flushed, and we are done manipulating the shared GL resources. Call it from gtk and spice display. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-19-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/display/vhost-user-gpu.c | 4 ++-- hw/display/virtio-gpu-base.c | 17 ++++++++++++----- hw/display/virtio-gpu.c | 4 ++-- hw/display/virtio-vga.c | 11 +++++++++++ include/hw/virtio/virtio-gpu.h | 2 +- include/ui/console.h | 2 ++ ui/console.c | 9 +++++++++ ui/gtk-egl.c | 3 +++ ui/gtk-gl-area.c | 3 +++ ui/sdl2-gl.c | 2 ++ ui/spice-display.c | 1 + 11 files changed, 48 insertions(+), 10 deletions(-) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index b7bde9feb6..4d8cb3525b 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -360,7 +360,7 @@ vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked) } static void -vhost_user_gpu_gl_unblock(VirtIOGPUBase *b) +vhost_user_gpu_gl_flushed(VirtIOGPUBase *b) { VhostUserGPU *g = VHOST_USER_GPU(b); @@ -578,7 +578,7 @@ vhost_user_gpu_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_CLASS(klass); - vgc->gl_unblock = vhost_user_gpu_gl_unblock; + vgc->gl_flushed = vhost_user_gpu_gl_flushed; vdc->realize = vhost_user_gpu_device_realize; vdc->reset = vhost_user_gpu_reset; diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index f27a6fbe75..4a57350917 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -97,21 +97,27 @@ static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) } static void -virtio_gpu_gl_block(void *opaque, bool block) +virtio_gpu_gl_flushed(void *opaque) { VirtIOGPUBase *g = opaque; VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_GET_CLASS(g); + if (vgc->gl_flushed) { + vgc->gl_flushed(g); + } +} + +static void +virtio_gpu_gl_block(void *opaque, bool block) +{ + VirtIOGPUBase *g = opaque; + if (block) { g->renderer_blocked++; } else { g->renderer_blocked--; } assert(g->renderer_blocked >= 0); - - if (g->renderer_blocked == 0) { - vgc->gl_unblock(g); - } } static int @@ -138,6 +144,7 @@ static const GraphicHwOps virtio_gpu_ops = { .text_update = virtio_gpu_text_update, .ui_info = virtio_gpu_ui_info, .gl_block = virtio_gpu_gl_block, + .gl_flushed = virtio_gpu_gl_flushed, }; bool diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 7eb4265a6d..2e4a9822b6 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -850,7 +850,7 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) g->processing_cmdq = false; } -static void virtio_gpu_gl_unblock(VirtIOGPUBase *b) +static void virtio_gpu_gl_flushed(VirtIOGPUBase *b) { VirtIOGPU *g = VIRTIO_GPU(b); @@ -1257,7 +1257,7 @@ static void virtio_gpu_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_CLASS(klass); - vgc->gl_unblock = virtio_gpu_gl_unblock; + vgc->gl_flushed = virtio_gpu_gl_flushed; vdc->realize = virtio_gpu_device_realize; vdc->reset = virtio_gpu_reset; vdc->get_config = virtio_gpu_get_config; diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index b071909b68..d3c6404061 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -68,6 +68,16 @@ static void virtio_vga_base_gl_block(void *opaque, bool block) } } +static void virtio_vga_base_gl_flushed(void *opaque) +{ + VirtIOVGABase *vvga = opaque; + VirtIOGPUBase *g = vvga->vgpu; + + if (g->hw_ops->gl_flushed) { + g->hw_ops->gl_flushed(g); + } +} + static int virtio_vga_base_get_flags(void *opaque) { VirtIOVGABase *vvga = opaque; @@ -83,6 +93,7 @@ static const GraphicHwOps virtio_vga_base_ops = { .text_update = virtio_vga_base_text_update, .ui_info = virtio_vga_base_ui_info, .gl_block = virtio_vga_base_gl_block, + .gl_flushed = virtio_vga_base_gl_flushed, }; static const VMStateDescription vmstate_virtio_vga_base = { diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 0043268e90..fae149235c 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -121,7 +121,7 @@ struct VirtIOGPUBase { struct VirtIOGPUBaseClass { VirtioDeviceClass parent; - void (*gl_unblock)(VirtIOGPUBase *g); + void (*gl_flushed)(VirtIOGPUBase *g); }; #define VIRTIO_GPU_BASE_PROPERTIES(_state, _conf) \ diff --git a/include/ui/console.h b/include/ui/console.h index 875885d9c7..d30e972d0b 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -387,6 +387,7 @@ typedef struct GraphicHwOps { void (*update_interval)(void *opaque, uint64_t interval); int (*ui_info)(void *opaque, uint32_t head, QemuUIInfo *info); void (*gl_block)(void *opaque, bool block); + void (*gl_flushed)(void *opaque); } GraphicHwOps; QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, @@ -402,6 +403,7 @@ void graphic_hw_update_done(QemuConsole *con); void graphic_hw_invalidate(QemuConsole *con); void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata); void graphic_hw_gl_block(QemuConsole *con, bool block); +void graphic_hw_gl_flushed(QemuConsole *con); void qemu_console_early_init(void); diff --git a/ui/console.c b/ui/console.c index d8cc640c28..c5d11bc701 100644 --- a/ui/console.c +++ b/ui/console.c @@ -294,6 +294,15 @@ void graphic_hw_gl_block(QemuConsole *con, bool block) } } +void graphic_hw_gl_flushed(QemuConsole *con) +{ + assert(con != NULL); + + if (con->hw_ops->gl_flushed) { + con->hw_ops->gl_flushed(con->hw); + } +} + int qemu_console_get_window_id(QemuConsole *con) { return con->window_id; diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 71c3d698b4..588e7b1bb1 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -92,6 +92,9 @@ void gd_egl_draw(VirtualConsole *vc) vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds); } + + glFlush(); + graphic_hw_gl_flushed(vc->gfx.dcl.con); } void gd_egl_update(DisplayChangeListener *dcl, diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 72bcd94918..e7ca73c7b1 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -70,6 +70,9 @@ void gd_gl_area_draw(VirtualConsole *vc) surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); } + + glFlush(); + graphic_hw_gl_flushed(vc->gfx.dcl.con); } void gd_gl_area_update(DisplayChangeListener *dcl, diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c index 82139ba49e..fd594d7461 100644 --- a/ui/sdl2-gl.c +++ b/ui/sdl2-gl.c @@ -58,6 +58,7 @@ static void sdl2_gl_render_surface(struct sdl2_console *scon) surface_gl_render_texture(scon->gls, scon->surface); SDL_GL_SwapWindow(scon->real_window); + graphic_hw_gl_flushed(scon->dcl.con); } void sdl2_gl_update(DisplayChangeListener *dcl, @@ -240,4 +241,5 @@ void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, egl_fb_blit(&scon->win_fb, &scon->guest_fb, !scon->y0_top); SDL_GL_SwapWindow(scon->real_window); + graphic_hw_gl_flushed(dcl->con); } diff --git a/ui/spice-display.c b/ui/spice-display.c index dccac4710a..6f32b66a6e 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -826,6 +826,7 @@ static void qemu_spice_gl_unblock_bh(void *opaque) SimpleSpiceDisplay *ssd = opaque; qemu_spice_gl_block(ssd, false); + graphic_hw_gl_flushed(ssd->dcl.con); } static void qemu_spice_gl_block_timer(void *opaque) From ebae6477dc43cf32b91564e07a776d9bff3b052f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:31 +0400 Subject: [PATCH 23/24] chardev: check if the chardev is registered for yanking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not all chardevs are created via qmp_chardev_open_socket(), and those should not call the yank function registration, as this will eventually assert() not being registered. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-20-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- chardev/char-socket.c | 53 +++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 8a707d766c..9061981f6d 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -417,8 +417,9 @@ static void tcp_chr_free_connection(Chardev *chr) tcp_set_msgfds(chr, NULL, 0); remove_fd_in_watch(chr); - if (s->state == TCP_CHARDEV_STATE_CONNECTING - || s->state == TCP_CHARDEV_STATE_CONNECTED) { + if (s->registered_yank && + (s->state == TCP_CHARDEV_STATE_CONNECTING + || s->state == TCP_CHARDEV_STATE_CONNECTED)) { yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), yank_generic_iochannel, QIO_CHANNEL(s->sioc)); @@ -940,9 +941,11 @@ static int tcp_chr_add_client(Chardev *chr, int fd) } tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_set_client_ioc_name(chr, sioc); - yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), - yank_generic_iochannel, - QIO_CHANNEL(sioc)); + if (s->registered_yank) { + yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), + yank_generic_iochannel, + QIO_CHANNEL(sioc)); + } ret = tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); return ret; @@ -957,9 +960,11 @@ static void tcp_chr_accept(QIONetListener *listener, tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_set_client_ioc_name(chr, cioc); - yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), - yank_generic_iochannel, - QIO_CHANNEL(cioc)); + if (s->registered_yank) { + yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), + yank_generic_iochannel, + QIO_CHANNEL(cioc)); + } tcp_chr_new_client(chr, cioc); } @@ -975,9 +980,11 @@ static int tcp_chr_connect_client_sync(Chardev *chr, Error **errp) object_unref(OBJECT(sioc)); return -1; } - yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), - yank_generic_iochannel, - QIO_CHANNEL(sioc)); + if (s->registered_yank) { + yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), + yank_generic_iochannel, + QIO_CHANNEL(sioc)); + } tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); return 0; @@ -993,9 +1000,11 @@ static void tcp_chr_accept_server_sync(Chardev *chr) tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); sioc = qio_net_listener_wait_client(s->listener); tcp_chr_set_client_ioc_name(chr, sioc); - yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), - yank_generic_iochannel, - QIO_CHANNEL(sioc)); + if (s->registered_yank) { + yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), + yank_generic_iochannel, + QIO_CHANNEL(sioc)); + } tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); } @@ -1124,9 +1133,11 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque) if (qio_task_propagate_error(task, &err)) { tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); - yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), - yank_generic_iochannel, - QIO_CHANNEL(sioc)); + if (s->registered_yank) { + yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), + yank_generic_iochannel, + QIO_CHANNEL(sioc)); + } check_report_connect_error(chr, err); goto cleanup; } @@ -1160,9 +1171,11 @@ static void tcp_chr_connect_client_async(Chardev *chr) tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); sioc = qio_channel_socket_new(); tcp_chr_set_client_ioc_name(chr, sioc); - yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), - yank_generic_iochannel, - QIO_CHANNEL(sioc)); + if (s->registered_yank) { + yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), + yank_generic_iochannel, + QIO_CHANNEL(sioc)); + } /* * Normally code would use the qio_channel_socket_connect_async * method which uses a QIOTask + qio_task_set_error internally From 73240f104dd0169b0637e46fae3c2c39cdfb2eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 4 Feb 2021 14:52:32 +0400 Subject: [PATCH 24/24] tests: add some virtio-gpu & vhost-user-gpu acceptance test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will check virtio/vhost-user-vga & virgl are correctly initialized by the Linux kernel on an egl-headless display. Signed-off-by: Marc-André Lureau Message-Id: <20210204105232.834642-21-marcandre.lureau@redhat.com> Signed-off-by: Gerd Hoffmann --- tests/acceptance/virtio-gpu.py | 161 +++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 tests/acceptance/virtio-gpu.py diff --git a/tests/acceptance/virtio-gpu.py b/tests/acceptance/virtio-gpu.py new file mode 100644 index 0000000000..211f02932f --- /dev/null +++ b/tests/acceptance/virtio-gpu.py @@ -0,0 +1,161 @@ +# virtio-gpu tests +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + + +from avocado_qemu import Test +from avocado_qemu import BUILD_DIR +from avocado_qemu import wait_for_console_pattern +from avocado_qemu import exec_command_and_wait_for_pattern +from avocado_qemu import is_readable_executable_file + +from qemu.accel import kvm_available + +import os +import socket +import subprocess + + +ACCEL_NOT_AVAILABLE_FMT = "%s accelerator does not seem to be available" +KVM_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "KVM" + + +def pick_default_vug_bin(): + relative_path = "./contrib/vhost-user-gpu/vhost-user-gpu" + if is_readable_executable_file(relative_path): + return relative_path + + bld_dir_path = os.path.join(BUILD_DIR, relative_path) + if is_readable_executable_file(bld_dir_path): + return bld_dir_path + + +class VirtioGPUx86(Test): + """ + :avocado: tags=virtio-gpu + """ + + KERNEL_COMMON_COMMAND_LINE = "printk.time=0 " + KERNEL_URL = ( + "https://archives.fedoraproject.org/pub/fedora" + "/linux/releases/33/Everything/x86_64/os/images" + "/pxeboot/vmlinuz" + ) + INITRD_URL = ( + "https://archives.fedoraproject.org/pub/fedora" + "/linux/releases/33/Everything/x86_64/os/images" + "/pxeboot/initrd.img" + ) + + def wait_for_console_pattern(self, success_message, vm=None): + wait_for_console_pattern( + self, + success_message, + failure_message="Kernel panic - not syncing", + vm=vm, + ) + + def test_virtio_vga_virgl(self): + """ + :avocado: tags=arch:x86_64 + :avocado: tags=device:virtio-vga + """ + kernel_command_line = ( + self.KERNEL_COMMON_COMMAND_LINE + "console=ttyS0 rdinit=/bin/bash" + ) + # FIXME: should check presence of virtio, virgl etc + if not kvm_available(self.arch, self.qemu_bin): + self.cancel(KVM_NOT_AVAILABLE) + + kernel_path = self.fetch_asset(self.KERNEL_URL) + initrd_path = self.fetch_asset(self.INITRD_URL) + + self.vm.set_console() + self.vm.add_args("-cpu", "host") + self.vm.add_args("-m", "2G") + self.vm.add_args("-machine", "pc,accel=kvm") + self.vm.add_args("-device", "virtio-vga,virgl=on") + self.vm.add_args("-display", "egl-headless") + self.vm.add_args( + "-kernel", + kernel_path, + "-initrd", + initrd_path, + "-append", + kernel_command_line, + ) + self.vm.launch() + self.wait_for_console_pattern("as init process") + exec_command_and_wait_for_pattern( + self, "/usr/sbin/modprobe virtio_gpu", "" + ) + self.wait_for_console_pattern("features: +virgl +edid") + + def test_vhost_user_vga_virgl(self): + """ + :avocado: tags=arch:x86_64 + :avocado: tags=device:vhost-user-vga + """ + kernel_command_line = ( + self.KERNEL_COMMON_COMMAND_LINE + "console=ttyS0 rdinit=/bin/bash" + ) + # FIXME: should check presence of vhost-user-gpu, virgl, memfd etc + if not kvm_available(self.arch, self.qemu_bin): + self.cancel(KVM_NOT_AVAILABLE) + + vug = pick_default_vug_bin() + if not vug: + self.cancel("Could not find vhost-user-gpu") + + kernel_path = self.fetch_asset(self.KERNEL_URL) + initrd_path = self.fetch_asset(self.INITRD_URL) + + # Create socketpair to connect proxy and remote processes + qemu_sock, vug_sock = socket.socketpair( + socket.AF_UNIX, socket.SOCK_STREAM + ) + os.set_inheritable(qemu_sock.fileno(), True) + os.set_inheritable(vug_sock.fileno(), True) + + self._vug_log_path = os.path.join( + self.vm._test_dir, "vhost-user-gpu.log" + ) + self._vug_log_file = open(self._vug_log_path, "wb") + print(self._vug_log_path) + + vugp = subprocess.Popen( + [vug, "--virgl", "--fd=%d" % vug_sock.fileno()], + stdin=subprocess.DEVNULL, + stdout=self._vug_log_file, + stderr=subprocess.STDOUT, + shell=False, + close_fds=False, + ) + + self.vm.set_console() + self.vm.add_args("-cpu", "host") + self.vm.add_args("-m", "2G") + self.vm.add_args("-object", "memory-backend-memfd,id=mem,size=2G") + self.vm.add_args("-machine", "pc,memory-backend=mem,accel=kvm") + self.vm.add_args("-chardev", "socket,id=vug,fd=%d" % qemu_sock.fileno()) + self.vm.add_args("-device", "vhost-user-vga,chardev=vug") + self.vm.add_args("-display", "egl-headless") + self.vm.add_args( + "-kernel", + kernel_path, + "-initrd", + initrd_path, + "-append", + kernel_command_line, + ) + self.vm.launch() + self.wait_for_console_pattern("as init process") + exec_command_and_wait_for_pattern( + self, "/usr/sbin/modprobe virtio_gpu", "" + ) + self.wait_for_console_pattern("features: +virgl -edid") + self.vm.shutdown() + qemu_sock.close() + vugp.terminate() + vugp.wait()