diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2021-02-05 10:09:16 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2021-02-05 10:09:16 +0000 |
commit | 923abdb4bdfb6d93bfe6c941ead70f3ea6707e40 (patch) | |
tree | b030755ba9dd824b377b82a983798e8f8b6f9b91 | |
parent | 2c6df987965729df702fa12f79564b5f76e3fa4e (diff) | |
parent | 73240f104dd0169b0637e46fae3c2c39cdfb2eb9 (diff) |
Merge remote-tracking branch 'remotes/kraxel/tags/vga-ui-20210205-pull-request' into staging
ui+virtio-gpu: opengl cleanups and fixes.
qxl+spice: bugfixes
# gpg: Signature made Fri 05 Feb 2021 06:56:04 GMT
# gpg: using RSA key A0328CFFB93A17A79901FE7D4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full]
# gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" [full]
# gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full]
# Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138
* remotes/kraxel/tags/vga-ui-20210205-pull-request: (24 commits)
tests: add some virtio-gpu & vhost-user-gpu acceptance test
chardev: check if the chardev is registered for yanking
display/ui: add a callback to indicate GL state is flushed
virtio-gpu: avoid re-entering cmdq processing
ui: add egl dmabuf import to gtkglarea
ui: check gtk-egl dmabuf support
ui: add qemu_egl_has_dmabuf helper
ui: check hw requirements during DCL registration
ui: add a DCLOps callback to check dmabuf support
ui: add an optional get_flags callback to GraphicHwOps
vhost-user-gpu: add a configuration flag for dmabuf usage
ui: remove console_has_gl_dmabuf()
ui: annotate DCLOps callback requirements
ui: add gd_gl_area_scanout_disable
ui: remove gl_ctx_get_current
ui: remove extra #ifdef CONFIG_OPENGL
vhost-user-gpu: handle display-info in a callback
vhost-user-gpu: use an extandable state enum for commands
vhost-user-gpu: handle vhost-user-gpu features in a callback
vhost-user-gpu: check backend for EDID support
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
31 files changed, 517 insertions, 105 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 diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index f445ef28ec..b27990ffdb 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); } @@ -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 @@ -261,23 +261,44 @@ 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, }; - 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)) { - 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 @@ -800,7 +821,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); } @@ -815,7 +836,7 @@ vg_handle_ctrl(VuDev *dev, int qidx) size_t len; for (;;) { - if (vg->wait_ok != 0) { + if (vg->wait_in != 0) { return; } @@ -825,7 +846,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 +865,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 { @@ -969,18 +990,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 +1009,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/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 ad664c4df8..04d5615812 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; @@ -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; }; 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-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; } diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 431c107096..6784d32920 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); @@ -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); diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 51f1747c4a..4d8cb3525b 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); @@ -365,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); @@ -552,9 +547,17 @@ 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; } + 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; @@ -575,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 40ccd00f94..4a57350917 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -97,29 +97,54 @@ 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); +} + +static int +virtio_gpu_get_flags(void *opaque) +{ + VirtIOGPUBase *g = opaque; + int flags = GRAPHIC_FLAGS_NONE; - if (g->renderer_blocked == 0) { - vgc->gl_unblock(g); + 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, .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 0e833a462b..2e4a9822b6 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,9 +847,10 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) g_free(cmd); } } + 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); @@ -1252,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 81f776ee36..d3c6404061 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -68,12 +68,32 @@ 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; + 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, .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/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/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 1aed7275c8..fae149235c 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; @@ -118,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) \ @@ -145,6 +148,7 @@ struct VirtIOGPU { uint64_t hostmem; + bool processing_cmdq; bool renderer_inited; bool renderer_reset; QEMUTimer *fence_poll; diff --git a/include/ui/console.h b/include/ui/console.h index 7a3fc11abf..d30e972d0b 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -174,36 +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); - QEMUGLContext (*dpy_gl_ctx_get_current)(DisplayChangeListener *dcl); + /* 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, @@ -211,15 +224,22 @@ 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); + /* 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); @@ -303,10 +323,8 @@ 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); static inline int surface_stride(DisplaySurface *s) { @@ -352,7 +370,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() */ @@ -360,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, @@ -375,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/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/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/include/ui/gtk.h b/include/ui/gtk.h index 3f395d7f94..3c1cd98db8 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; @@ -133,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, @@ -140,6 +143,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/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/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/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() diff --git a/ui/console.c b/ui/console.c index d80ce7037c..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; @@ -1463,9 +1472,41 @@ bool console_has_gl(QemuConsole *con) return con->gl != NULL; } -bool console_has_gl_dmabuf(QemuConsole *con) +static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl) { - return con->gl != NULL && con->gl->ops->dpy_gl_scanout_dmabuf != NULL; + if (dcl->ops->dpy_has_dmabuf) { + return dcl->ops->dpy_has_dmabuf(dcl); + } + + if (dcl->ops->dpy_gl_scanout_dmabuf) { + return true; + } + + 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) @@ -1474,6 +1515,7 @@ void register_displaychangelistener(DisplayChangeListener *dcl) "This VM has no graphic display device."; static DisplaySurface *dummy; QemuConsole *con; + Error *err = NULL; assert(!dcl->ds); @@ -1488,6 +1530,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); @@ -1803,21 +1850,10 @@ 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); - 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/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/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[] = { 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 98c22d23f5..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, @@ -198,6 +201,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) { @@ -206,6 +216,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; @@ -623,9 +623,20 @@ static const DisplayChangeListenerOps dcl_ops = { #if defined(CONFIG_OPENGL) -/** DisplayState Callbacks (opengl version) **/ +static bool gd_has_dmabuf(DisplayChangeListener *dcl) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); -#if defined(CONFIG_OPENGL) + 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; +} + +/** DisplayState Callbacks (opengl version) **/ static const DisplayChangeListenerOps dcl_gl_area_ops = { .dpy_name = "gtk-egl", @@ -639,13 +650,13 @@ 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_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, }; -#endif /* CONFIG_OPENGL */ - static const DisplayChangeListenerOps dcl_egl_ops = { .dpy_name = "gtk-egl", .dpy_gfx_update = gd_egl_update, @@ -658,7 +669,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, @@ -666,6 +676,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 */ @@ -1980,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) @@ -1993,13 +2016,12 @@ 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(); + g_signal_connect(vc->gfx.drawing_area, "realize", + G_CALLBACK(gl_area_realize), vc); 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. @@ -2012,6 +2034,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 diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c index c73d273bf8..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, @@ -185,14 +186,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); @@ -248,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); } @@ -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-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..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) @@ -1102,7 +1103,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, @@ -1188,4 +1188,6 @@ void qemu_spice_display_init(void) } qemu_spice_display_init_one(con); } + + qemu_spice_display_init_done(); } |