ui+virtio-gpu: opengl cleanups and fixes.

qxl+spice: bugfixes
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEoDKM/7k6F6eZAf59TLbY7tPocTgFAmAc7AQACgkQTLbY7tPo
 cTjxLg/+Kgt4RgpAdFtV0pRVF4boR3hx0fKbeMIX/AZkvGjZRb040jo7FP5lqOTi
 +kBZyiDqyxZbTRdnyCcIo+WdmH8IE9WQ6aNGmRYRVIoTbE7J6ylfBdwV0BHu8MdS
 RBTPTk2zFEjDwJZjDeegZ33j0uxFObjY28PpZwDtxfbIldXq2XJezrwB3AIdZixa
 eVAXuV9TPryrxfU/8L9/SqTiWRAI0pSsG94QIv0x2u3+wQgvzo0qmei7hCzBisrF
 hZxpxM2W78ZwIOnAoyOoyNDc6KgAn55f9lh0FZzx9vHNRVibn0Ijmw2FZNR24/aD
 tCZoz2pVUaww2KNQ+CuyM1LvDeV2NxMSpi0RcCSmHsTwumLuev3lRqWwl8kxgz2/
 XJ6KEOzg6xwtHkj1IRMCWJvgWOqh8wAqCYP7DavyCIon8sOerI8iSL6o9JQxt/+E
 CrBD2p54BhyONS6bpKkAhn1tQfXQXyQF1Mc045qy+QpKn88uZz1b9Pnw/4K46T2I
 8uGuqBwkTVgEmPihp2+Xo0SLD6/xNDYGpiEmjcGAi/EZtJWEM1T6pPv9pYo6PQdA
 MQkAqljTP7FT9RFfsAb5uNnEiIzj6rOzG5ThDbhDCb+IuSFaj6bRzYwhCPX9NIqv
 qqmx3OScauWvw5HifcIynIkHKN/N6RpnH3eybvUW0Kc0d1UgK44=
 =SQbm
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Peter Maydell 2021-02-05 10:09:16 +00:00
commit 923abdb4bd
31 changed files with 517 additions and 105 deletions

View File

@ -417,8 +417,9 @@ static void tcp_chr_free_connection(Chardev *chr)
tcp_set_msgfds(chr, NULL, 0); tcp_set_msgfds(chr, NULL, 0);
remove_fd_in_watch(chr); remove_fd_in_watch(chr);
if (s->state == TCP_CHARDEV_STATE_CONNECTING if (s->registered_yank &&
|| s->state == TCP_CHARDEV_STATE_CONNECTED) { (s->state == TCP_CHARDEV_STATE_CONNECTING
|| s->state == TCP_CHARDEV_STATE_CONNECTED)) {
yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label),
yank_generic_iochannel, yank_generic_iochannel,
QIO_CHANNEL(s->sioc)); 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_change_state(s, TCP_CHARDEV_STATE_CONNECTING);
tcp_chr_set_client_ioc_name(chr, sioc); tcp_chr_set_client_ioc_name(chr, sioc);
yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), if (s->registered_yank) {
yank_generic_iochannel, yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
QIO_CHANNEL(sioc)); yank_generic_iochannel,
QIO_CHANNEL(sioc));
}
ret = tcp_chr_new_client(chr, sioc); ret = tcp_chr_new_client(chr, sioc);
object_unref(OBJECT(sioc)); object_unref(OBJECT(sioc));
return ret; return ret;
@ -957,9 +960,11 @@ static void tcp_chr_accept(QIONetListener *listener,
tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING);
tcp_chr_set_client_ioc_name(chr, cioc); tcp_chr_set_client_ioc_name(chr, cioc);
yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), if (s->registered_yank) {
yank_generic_iochannel, yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
QIO_CHANNEL(cioc)); yank_generic_iochannel,
QIO_CHANNEL(cioc));
}
tcp_chr_new_client(chr, 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)); object_unref(OBJECT(sioc));
return -1; return -1;
} }
yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), if (s->registered_yank) {
yank_generic_iochannel, yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
QIO_CHANNEL(sioc)); yank_generic_iochannel,
QIO_CHANNEL(sioc));
}
tcp_chr_new_client(chr, sioc); tcp_chr_new_client(chr, sioc);
object_unref(OBJECT(sioc)); object_unref(OBJECT(sioc));
return 0; return 0;
@ -993,9 +1000,11 @@ static void tcp_chr_accept_server_sync(Chardev *chr)
tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING);
sioc = qio_net_listener_wait_client(s->listener); sioc = qio_net_listener_wait_client(s->listener);
tcp_chr_set_client_ioc_name(chr, sioc); tcp_chr_set_client_ioc_name(chr, sioc);
yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), if (s->registered_yank) {
yank_generic_iochannel, yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
QIO_CHANNEL(sioc)); yank_generic_iochannel,
QIO_CHANNEL(sioc));
}
tcp_chr_new_client(chr, sioc); tcp_chr_new_client(chr, sioc);
object_unref(OBJECT(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)) { if (qio_task_propagate_error(task, &err)) {
tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED);
yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), if (s->registered_yank) {
yank_generic_iochannel, yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label),
QIO_CHANNEL(sioc)); yank_generic_iochannel,
QIO_CHANNEL(sioc));
}
check_report_connect_error(chr, err); check_report_connect_error(chr, err);
goto cleanup; goto cleanup;
} }
@ -1160,9 +1171,11 @@ static void tcp_chr_connect_client_async(Chardev *chr)
tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING);
sioc = qio_channel_socket_new(); sioc = qio_channel_socket_new();
tcp_chr_set_client_ioc_name(chr, sioc); tcp_chr_set_client_ioc_name(chr, sioc);
yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), if (s->registered_yank) {
yank_generic_iochannel, yank_register_function(CHARDEV_YANK_INSTANCE(chr->label),
QIO_CHANNEL(sioc)); yank_generic_iochannel,
QIO_CHANNEL(sioc));
}
/* /*
* Normally code would use the qio_channel_socket_connect_async * Normally code would use the qio_channel_socket_connect_async
* method which uses a QIOTask + qio_task_set_error internally * method which uses a QIOTask + qio_task_set_error internally

View File

@ -124,7 +124,7 @@ source_wait_cb(gint fd, GIOCondition condition, gpointer user_data)
} }
/* resume */ /* resume */
g->wait_ok = 0; g->wait_in = 0;
vg_handle_ctrl(&g->dev.parent, 0); vg_handle_ctrl(&g->dev.parent, 0);
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
@ -133,8 +133,8 @@ source_wait_cb(gint fd, GIOCondition condition, gpointer user_data)
void void
vg_wait_ok(VuGpu *g) vg_wait_ok(VuGpu *g)
{ {
assert(g->wait_ok == 0); assert(g->wait_in == 0);
g->wait_ok = g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, g->wait_in = g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP,
source_wait_cb, g); 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_push(&g->dev.parent, cmd->vq, &cmd->elem, s);
vu_queue_notify(&g->dev.parent, cmd->vq); vu_queue_notify(&g->dev.parent, cmd->vq);
cmd->finished = true; cmd->state = VG_CMD_STATE_FINISHED;
} }
void void
@ -261,23 +261,44 @@ vg_ctrl_response_nodata(VuGpu *g,
vg_ctrl_response(g, cmd, &resp, sizeof(resp)); 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 void
vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd)
{ {
struct virtio_gpu_resp_display_info dpy_info = { {} };
VhostUserGpuMsg msg = { VhostUserGpuMsg msg = {
.request = VHOST_USER_GPU_GET_DISPLAY_INFO, .request = VHOST_USER_GPU_GET_DISPLAY_INFO,
.size = 0, .size = 0,
}; };
assert(vg->wait_ok == 0); assert(vg->wait_in == 0);
vg_send_msg(vg, &msg, -1); vg_send_msg(vg, &msg, -1);
if (!vg_recv_msg(vg, msg.request, sizeof(dpy_info), &dpy_info)) { vg->wait_in = g_unix_fd_add(vg->sock_fd, G_IO_IN | G_IO_HUP,
return; get_display_info_cb, vg);
} cmd->state = VG_CMD_STATE_PENDING;
vg_ctrl_response(vg, cmd, &dpy_info.hdr, sizeof(dpy_info));
} }
static void static void
@ -800,7 +821,7 @@ vg_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd)
cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
break; break;
} }
if (!cmd->finished) { if (cmd->state == VG_CMD_STATE_NEW) {
vg_ctrl_response_nodata(vg, cmd, cmd->error ? cmd->error : vg_ctrl_response_nodata(vg, cmd, cmd->error ? cmd->error :
VIRTIO_GPU_RESP_OK_NODATA); VIRTIO_GPU_RESP_OK_NODATA);
} }
@ -815,7 +836,7 @@ vg_handle_ctrl(VuDev *dev, int qidx)
size_t len; size_t len;
for (;;) { for (;;) {
if (vg->wait_ok != 0) { if (vg->wait_in != 0) {
return; return;
} }
@ -825,7 +846,7 @@ vg_handle_ctrl(VuDev *dev, int qidx)
} }
cmd->vq = vq; cmd->vq = vq;
cmd->error = 0; cmd->error = 0;
cmd->finished = false; cmd->state = VG_CMD_STATE_NEW;
len = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, len = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
0, &cmd->cmd_hdr, sizeof(cmd->cmd_hdr)); 0, &cmd->cmd_hdr, sizeof(cmd->cmd_hdr));
@ -844,7 +865,7 @@ vg_handle_ctrl(VuDev *dev, int qidx)
vg_process_cmd(vg, cmd); vg_process_cmd(vg, cmd);
} }
if (!cmd->finished) { if (cmd->state != VG_CMD_STATE_FINISHED) {
QTAILQ_INSERT_TAIL(&vg->fenceq, cmd, next); QTAILQ_INSERT_TAIL(&vg->fenceq, cmd, next);
vg->inflight++; vg->inflight++;
} else { } else {
@ -969,18 +990,17 @@ vg_queue_set_started(VuDev *dev, int qidx, bool started)
} }
} }
static void static gboolean
set_gpu_protocol_features(VuGpu *g) protocol_features_cb(gint fd, GIOCondition condition, gpointer user_data)
{ {
VuGpu *g = user_data;
uint64_t u64; uint64_t u64;
VhostUserGpuMsg msg = { VhostUserGpuMsg msg = {
.request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES .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)) { if (!vg_recv_msg(g, msg.request, sizeof(u64), &u64)) {
return; return G_SOURCE_CONTINUE;
} }
msg = (VhostUserGpuMsg) { msg = (VhostUserGpuMsg) {
@ -989,6 +1009,24 @@ set_gpu_protocol_features(VuGpu *g)
.payload.u64 = 0 .payload.u64 = 0
}; };
vg_send_msg(g, &msg, -1); 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 static int

View File

@ -482,7 +482,7 @@ void vg_virgl_process_cmd(VuGpu *g, struct virtio_gpu_ctrl_command *cmd)
break; break;
} }
if (cmd->finished) { if (cmd->state != VG_CMD_STATE_NEW) {
return; return;
} }

View File

@ -118,7 +118,7 @@ typedef struct VuGpu {
int sock_fd; int sock_fd;
int drm_rnode_fd; int drm_rnode_fd;
GSource *renderer_source; GSource *renderer_source;
guint wait_ok; guint wait_in;
bool virgl; bool virgl;
bool virgl_inited; bool virgl_inited;
@ -129,12 +129,18 @@ typedef struct VuGpu {
QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq; QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq;
} VuGpu; } VuGpu;
enum {
VG_CMD_STATE_NEW,
VG_CMD_STATE_PENDING,
VG_CMD_STATE_FINISHED,
};
struct virtio_gpu_ctrl_command { struct virtio_gpu_ctrl_command {
VuVirtqElement elem; VuVirtqElement elem;
VuVirtq *vq; VuVirtq *vq;
struct virtio_gpu_ctrl_hdr cmd_hdr; struct virtio_gpu_ctrl_hdr cmd_hdr;
uint32_t error; uint32_t error;
bool finished; int state;
QTAILQ_ENTRY(virtio_gpu_ctrl_command) next; QTAILQ_ENTRY(virtio_gpu_ctrl_command) next;
}; };

View File

@ -189,7 +189,7 @@ static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd)
qxl_name(qxl_surface_cmd, cmd->type), qxl_name(qxl_surface_cmd, cmd->type),
cmd->surface_id); cmd->surface_id);
if (cmd->type == QXL_SURFACE_CMD_CREATE) { 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.width,
cmd->u.surface_create.height, cmd->u.surface_create.height,
cmd->u.surface_create.stride, 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); qxl->guest_surfaces.count, qxl->guest_surfaces.max);
} }
if (cmd->type == QXL_SURFACE_CMD_DESTROY) { if (cmd->type == QXL_SURFACE_CMD_DESTROY) {
fprintf(stderr, " (count %d)", qxl->guest_surfaces.count); fprintf(stderr, " (count %u)", qxl->guest_surfaces.count);
} }
} }

View File

@ -181,6 +181,7 @@ void qxl_render_update(PCIQXLDevice *qxl)
qxl->mode == QXL_MODE_UNDEFINED) { qxl->mode == QXL_MODE_UNDEFINED) {
qxl_render_update_area_unlocked(qxl); qxl_render_update_area_unlocked(qxl);
qemu_mutex_unlock(&qxl->ssd.lock); qemu_mutex_unlock(&qxl->ssd.lock);
graphic_hw_update_done(qxl->ssd.dcl.con);
return; return;
} }

View File

@ -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); qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id);
break; break;
default: default:
fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__, fprintf(stderr, "qxl: %s: unexpected current_async %u\n", __func__,
current_async); current_async);
} }
qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD); 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_size, &error_fatal);
qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram); 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->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->id = qemu_console_get_index(qxl->vga.con); /* == channel_id */
qxl_realize_common(qxl, errp); qxl_realize_common(qxl, errp);

View File

@ -224,11 +224,6 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
close(dmabuf->fd); close(dmabuf->fd);
dmabuf->fd = -1; 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); dpy_gl_release_dmabuf(con, dmabuf);
if (fd == -1) { if (fd == -1) {
dpy_gl_scanout_disable(con); dpy_gl_scanout_disable(con);
@ -365,7 +360,7 @@ vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked)
} }
static void static void
vhost_user_gpu_gl_unblock(VirtIOGPUBase *b) vhost_user_gpu_gl_flushed(VirtIOGPUBase *b)
{ {
VhostUserGPU *g = VHOST_USER_GPU(b); VhostUserGPU *g = VHOST_USER_GPU(b);
@ -552,9 +547,17 @@ vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp)
return; 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)) { if (virtio_has_feature(g->vhost->dev.features, VIRTIO_GPU_F_VIRGL)) {
g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED; 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)) { if (!virtio_gpu_base_device_realize(qdev, NULL, NULL, errp)) {
return; return;
@ -575,7 +578,7 @@ vhost_user_gpu_class_init(ObjectClass *klass, void *data)
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_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->realize = vhost_user_gpu_device_realize;
vdc->reset = vhost_user_gpu_reset; vdc->reset = vhost_user_gpu_reset;

View File

@ -97,29 +97,54 @@ static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
} }
static void static void
virtio_gpu_gl_block(void *opaque, bool block) virtio_gpu_gl_flushed(void *opaque)
{ {
VirtIOGPUBase *g = opaque; VirtIOGPUBase *g = opaque;
VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_GET_CLASS(g); 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) { if (block) {
g->renderer_blocked++; g->renderer_blocked++;
} else { } else {
g->renderer_blocked--; g->renderer_blocked--;
} }
assert(g->renderer_blocked >= 0); assert(g->renderer_blocked >= 0);
}
if (g->renderer_blocked == 0) { static int
vgc->gl_unblock(g); 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 = { static const GraphicHwOps virtio_gpu_ops = {
.get_flags = virtio_gpu_get_flags,
.invalidate = virtio_gpu_invalidate_display, .invalidate = virtio_gpu_invalidate_display,
.gfx_update = virtio_gpu_update_display, .gfx_update = virtio_gpu_update_display,
.text_update = virtio_gpu_text_update, .text_update = virtio_gpu_text_update,
.ui_info = virtio_gpu_ui_info, .ui_info = virtio_gpu_ui_info,
.gl_block = virtio_gpu_gl_block, .gl_block = virtio_gpu_gl_block,
.gl_flushed = virtio_gpu_gl_flushed,
}; };
bool bool

View File

@ -814,6 +814,10 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
{ {
struct virtio_gpu_ctrl_command *cmd; struct virtio_gpu_ctrl_command *cmd;
if (g->processing_cmdq) {
return;
}
g->processing_cmdq = true;
while (!QTAILQ_EMPTY(&g->cmdq)) { while (!QTAILQ_EMPTY(&g->cmdq)) {
cmd = QTAILQ_FIRST(&g->cmdq); cmd = QTAILQ_FIRST(&g->cmdq);
@ -843,9 +847,10 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
g_free(cmd); 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); 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); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_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->realize = virtio_gpu_device_realize;
vdc->reset = virtio_gpu_reset; vdc->reset = virtio_gpu_reset;
vdc->get_config = virtio_gpu_get_config; vdc->get_config = virtio_gpu_get_config;

View File

@ -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 = { static const GraphicHwOps virtio_vga_base_ops = {
.get_flags = virtio_vga_base_get_flags,
.invalidate = virtio_vga_base_invalidate_display, .invalidate = virtio_vga_base_invalidate_display,
.gfx_update = virtio_vga_base_update_display, .gfx_update = virtio_vga_base_update_display,
.text_update = virtio_vga_base_text_update, .text_update = virtio_vga_base_text_update,
.ui_info = virtio_vga_base_ui_info, .ui_info = virtio_vga_base_ui_info,
.gl_block = virtio_vga_base_gl_block, .gl_block = virtio_vga_base_gl_block,
.gl_flushed = virtio_vga_base_gl_flushed,
}; };
static const VMStateDescription vmstate_virtio_vga_base = { static const VMStateDescription vmstate_virtio_vga_base = {

View File

@ -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 = { static const GraphicHwOps vfio_display_dmabuf_ops = {
.get_flags = vfio_display_get_flags,
.gfx_update = vfio_display_dmabuf_update, .gfx_update = vfio_display_dmabuf_update,
.ui_info = vfio_display_edid_ui_info, .ui_info = vfio_display_edid_ui_info,
}; };

View File

@ -71,6 +71,7 @@ enum virtio_gpu_base_conf_flags {
VIRTIO_GPU_FLAG_VIRGL_ENABLED = 1, VIRTIO_GPU_FLAG_VIRGL_ENABLED = 1,
VIRTIO_GPU_FLAG_STATS_ENABLED, VIRTIO_GPU_FLAG_STATS_ENABLED,
VIRTIO_GPU_FLAG_EDID_ENABLED, VIRTIO_GPU_FLAG_EDID_ENABLED,
VIRTIO_GPU_FLAG_DMABUF_ENABLED,
}; };
#define virtio_gpu_virgl_enabled(_cfg) \ #define virtio_gpu_virgl_enabled(_cfg) \
@ -79,6 +80,8 @@ enum virtio_gpu_base_conf_flags {
(_cfg.flags & (1 << VIRTIO_GPU_FLAG_STATS_ENABLED)) (_cfg.flags & (1 << VIRTIO_GPU_FLAG_STATS_ENABLED))
#define virtio_gpu_edid_enabled(_cfg) \ #define virtio_gpu_edid_enabled(_cfg) \
(_cfg.flags & (1 << VIRTIO_GPU_FLAG_EDID_ENABLED)) (_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 { struct virtio_gpu_base_conf {
uint32_t max_outputs; uint32_t max_outputs;
@ -118,7 +121,7 @@ struct VirtIOGPUBase {
struct VirtIOGPUBaseClass { struct VirtIOGPUBaseClass {
VirtioDeviceClass parent; VirtioDeviceClass parent;
void (*gl_unblock)(VirtIOGPUBase *g); void (*gl_flushed)(VirtIOGPUBase *g);
}; };
#define VIRTIO_GPU_BASE_PROPERTIES(_state, _conf) \ #define VIRTIO_GPU_BASE_PROPERTIES(_state, _conf) \
@ -145,6 +148,7 @@ struct VirtIOGPU {
uint64_t hostmem; uint64_t hostmem;
bool processing_cmdq;
bool renderer_inited; bool renderer_inited;
bool renderer_reset; bool renderer_reset;
QEMUTimer *fence_poll; QEMUTimer *fence_poll;

View File

@ -174,36 +174,49 @@ typedef struct DisplayState DisplayState;
typedef struct DisplayChangeListenerOps { typedef struct DisplayChangeListenerOps {
const char *dpy_name; const char *dpy_name;
/* optional */
void (*dpy_refresh)(DisplayChangeListener *dcl); void (*dpy_refresh)(DisplayChangeListener *dcl);
/* optional */
void (*dpy_gfx_update)(DisplayChangeListener *dcl, void (*dpy_gfx_update)(DisplayChangeListener *dcl,
int x, int y, int w, int h); int x, int y, int w, int h);
/* optional */
void (*dpy_gfx_switch)(DisplayChangeListener *dcl, void (*dpy_gfx_switch)(DisplayChangeListener *dcl,
struct DisplaySurface *new_surface); struct DisplaySurface *new_surface);
/* optional */
bool (*dpy_gfx_check_format)(DisplayChangeListener *dcl, bool (*dpy_gfx_check_format)(DisplayChangeListener *dcl,
pixman_format_code_t format); pixman_format_code_t format);
/* optional */
void (*dpy_text_cursor)(DisplayChangeListener *dcl, void (*dpy_text_cursor)(DisplayChangeListener *dcl,
int x, int y); int x, int y);
/* optional */
void (*dpy_text_resize)(DisplayChangeListener *dcl, void (*dpy_text_resize)(DisplayChangeListener *dcl,
int w, int h); int w, int h);
/* optional */
void (*dpy_text_update)(DisplayChangeListener *dcl, void (*dpy_text_update)(DisplayChangeListener *dcl,
int x, int y, int w, int h); int x, int y, int w, int h);
/* optional */
void (*dpy_mouse_set)(DisplayChangeListener *dcl, void (*dpy_mouse_set)(DisplayChangeListener *dcl,
int x, int y, int on); int x, int y, int on);
/* optional */
void (*dpy_cursor_define)(DisplayChangeListener *dcl, void (*dpy_cursor_define)(DisplayChangeListener *dcl,
QEMUCursor *cursor); QEMUCursor *cursor);
/* required if GL */
QEMUGLContext (*dpy_gl_ctx_create)(DisplayChangeListener *dcl, QEMUGLContext (*dpy_gl_ctx_create)(DisplayChangeListener *dcl,
QEMUGLParams *params); QEMUGLParams *params);
/* required if GL */
void (*dpy_gl_ctx_destroy)(DisplayChangeListener *dcl, void (*dpy_gl_ctx_destroy)(DisplayChangeListener *dcl,
QEMUGLContext ctx); QEMUGLContext ctx);
/* required if GL */
int (*dpy_gl_ctx_make_current)(DisplayChangeListener *dcl, int (*dpy_gl_ctx_make_current)(DisplayChangeListener *dcl,
QEMUGLContext ctx); QEMUGLContext ctx);
QEMUGLContext (*dpy_gl_ctx_get_current)(DisplayChangeListener *dcl);
/* required if GL */
void (*dpy_gl_scanout_disable)(DisplayChangeListener *dcl); void (*dpy_gl_scanout_disable)(DisplayChangeListener *dcl);
/* required if GL */
void (*dpy_gl_scanout_texture)(DisplayChangeListener *dcl, void (*dpy_gl_scanout_texture)(DisplayChangeListener *dcl,
uint32_t backing_id, uint32_t backing_id,
bool backing_y_0_top, bool backing_y_0_top,
@ -211,15 +224,22 @@ typedef struct DisplayChangeListenerOps {
uint32_t backing_height, uint32_t backing_height,
uint32_t x, uint32_t y, uint32_t x, uint32_t y,
uint32_t w, uint32_t h); 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, void (*dpy_gl_scanout_dmabuf)(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf); QemuDmaBuf *dmabuf);
/* optional */
void (*dpy_gl_cursor_dmabuf)(DisplayChangeListener *dcl, void (*dpy_gl_cursor_dmabuf)(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf, bool have_hot, QemuDmaBuf *dmabuf, bool have_hot,
uint32_t hot_x, uint32_t hot_y); uint32_t hot_x, uint32_t hot_y);
/* optional */
void (*dpy_gl_cursor_position)(DisplayChangeListener *dcl, void (*dpy_gl_cursor_position)(DisplayChangeListener *dcl,
uint32_t pos_x, uint32_t pos_y); uint32_t pos_x, uint32_t pos_y);
/* optional */
void (*dpy_gl_release_dmabuf)(DisplayChangeListener *dcl, void (*dpy_gl_release_dmabuf)(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf); QemuDmaBuf *dmabuf);
/* required if GL */
void (*dpy_gl_update)(DisplayChangeListener *dcl, void (*dpy_gl_update)(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h); 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); QEMUGLParams *params);
void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx); void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx);
int dpy_gl_ctx_make_current(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(QemuConsole *con);
bool console_has_gl_dmabuf(QemuConsole *con);
static inline int surface_stride(DisplaySurface *s) 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; *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 { typedef struct GraphicHwOps {
int (*get_flags)(void *opaque); /* optional, default 0 */
void (*invalidate)(void *opaque); void (*invalidate)(void *opaque);
void (*gfx_update)(void *opaque); void (*gfx_update)(void *opaque);
bool gfx_update_async; /* if true, calls graphic_hw_update_done() */ 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); void (*update_interval)(void *opaque, uint64_t interval);
int (*ui_info)(void *opaque, uint32_t head, QemuUIInfo *info); int (*ui_info)(void *opaque, uint32_t head, QemuUIInfo *info);
void (*gl_block)(void *opaque, bool block); void (*gl_block)(void *opaque, bool block);
void (*gl_flushed)(void *opaque);
} GraphicHwOps; } GraphicHwOps;
QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, 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_invalidate(QemuConsole *con);
void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata); void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata);
void graphic_hw_gl_block(QemuConsole *con, bool block); void graphic_hw_gl_block(QemuConsole *con, bool block);
void graphic_hw_gl_flushed(QemuConsole *con);
void qemu_console_early_init(void); void qemu_console_early_init(void);

View File

@ -9,6 +9,5 @@ QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl,
void qemu_egl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx); void qemu_egl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx);
int qemu_egl_make_context_current(DisplayChangeListener *dcl, int qemu_egl_make_context_current(DisplayChangeListener *dcl,
QEMUGLContext ctx); QEMUGLContext ctx);
QEMUGLContext qemu_egl_get_current_context(DisplayChangeListener *dcl);
#endif /* EGL_CONTEXT_H */ #endif /* EGL_CONTEXT_H */

View File

@ -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_x11(EGLNativeDisplayType dpy, DisplayGLMode mode);
int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode); int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode);
EGLContext qemu_egl_init_ctx(void); EGLContext qemu_egl_init_ctx(void);
bool qemu_egl_has_dmabuf(void);
#endif /* EGL_HELPERS_H */ #endif /* EGL_HELPERS_H */

View File

@ -48,6 +48,7 @@ typedef struct VirtualGfxConsole {
int cursor_y; int cursor_y;
bool y0_top; bool y0_top;
bool scanout_mode; bool scanout_mode;
bool has_dmabuf;
#endif #endif
} VirtualGfxConsole; } VirtualGfxConsole;
@ -133,6 +134,8 @@ QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl,
QEMUGLParams *params); QEMUGLParams *params);
void gd_gl_area_destroy_context(DisplayChangeListener *dcl, void gd_gl_area_destroy_context(DisplayChangeListener *dcl,
QEMUGLContext ctx); QEMUGLContext ctx);
void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf);
void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_id, uint32_t backing_id,
bool backing_y_0_top, bool backing_y_0_top,
@ -140,6 +143,7 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_height, uint32_t backing_height,
uint32_t x, uint32_t y, uint32_t x, uint32_t y,
uint32_t w, uint32_t h); uint32_t w, uint32_t h);
void gd_gl_area_scanout_disable(DisplayChangeListener *dcl);
void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h); uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void gtk_gl_area_init(void); void gtk_gl_area_init(void);

View File

@ -28,6 +28,7 @@
void qemu_spice_input_init(void); void qemu_spice_input_init(void);
void qemu_spice_display_init(void); void qemu_spice_display_init(void);
void qemu_spice_display_init_done(void);
bool qemu_spice_have_display_interface(QemuConsole *con); bool qemu_spice_have_display_interface(QemuConsole *con);
int qemu_spice_add_display_interface(QXLInstance *qxlin, 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, int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,

View File

@ -70,7 +70,6 @@ QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl,
void sdl2_gl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx); void sdl2_gl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx);
int sdl2_gl_make_context_current(DisplayChangeListener *dcl, int sdl2_gl_make_context_current(DisplayChangeListener *dcl,
QEMUGLContext ctx); QEMUGLContext ctx);
QEMUGLContext sdl2_gl_get_current_context(DisplayChangeListener *dcl);
void sdl2_gl_scanout_disable(DisplayChangeListener *dcl); void sdl2_gl_scanout_disable(DisplayChangeListener *dcl);
void sdl2_gl_scanout_texture(DisplayChangeListener *dcl, void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,

View File

@ -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()

View File

@ -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) int qemu_console_get_window_id(QemuConsole *con)
{ {
return con->window_id; return con->window_id;
@ -1463,9 +1472,41 @@ bool console_has_gl(QemuConsole *con)
return con->gl != NULL; 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) void register_displaychangelistener(DisplayChangeListener *dcl)
@ -1474,6 +1515,7 @@ void register_displaychangelistener(DisplayChangeListener *dcl)
"This VM has no graphic display device."; "This VM has no graphic display device.";
static DisplaySurface *dummy; static DisplaySurface *dummy;
QemuConsole *con; QemuConsole *con;
Error *err = NULL;
assert(!dcl->ds); assert(!dcl->ds);
@ -1488,6 +1530,11 @@ void register_displaychangelistener(DisplayChangeListener *dcl)
dcl->con->gl = 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); trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
dcl->ds = get_alloc_displaystate(); dcl->ds = get_alloc_displaystate();
QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next); 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); 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) void dpy_gl_scanout_disable(QemuConsole *con)
{ {
assert(con->gl); assert(con->gl);
if (con->gl->ops->dpy_gl_scanout_disable) { con->gl->ops->dpy_gl_scanout_disable(con->gl);
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);
}
} }
void dpy_gl_scanout_texture(QemuConsole *con, void dpy_gl_scanout_texture(QemuConsole *con,

View File

@ -35,8 +35,3 @@ int qemu_egl_make_context_current(DisplayChangeListener *dcl,
return eglMakeCurrent(qemu_egl_display, return eglMakeCurrent(qemu_egl_display,
EGL_NO_SURFACE, EGL_NO_SURFACE, ctx); EGL_NO_SURFACE, EGL_NO_SURFACE, ctx);
} }
QEMUGLContext qemu_egl_get_current_context(DisplayChangeListener *dcl)
{
return eglGetCurrentContext();
}

View File

@ -160,7 +160,6 @@ static const DisplayChangeListenerOps egl_ops = {
.dpy_gl_ctx_create = egl_create_context, .dpy_gl_ctx_create = egl_create_context,
.dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context,
.dpy_gl_ctx_make_current = qemu_egl_make_context_current, .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_disable = egl_scanout_disable,
.dpy_gl_scanout_texture = egl_scanout_texture, .dpy_gl_scanout_texture = egl_scanout_texture,

View File

@ -441,6 +441,16 @@ int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode)
#endif #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) EGLContext qemu_egl_init_ctx(void)
{ {
static const EGLint ctx_att_core[] = { static const EGLint ctx_att_core[] = {

View File

@ -92,6 +92,9 @@ void gd_egl_draw(VirtualConsole *vc)
vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds);
vc->gfx.scale_y = (double)wh / surface_height(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, void gd_egl_update(DisplayChangeListener *dcl,

View File

@ -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_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); 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, void gd_gl_area_update(DisplayChangeListener *dcl,
@ -198,6 +201,13 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
backing_id, false); 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, void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h) 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)); 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) void gtk_gl_area_init(void)
{ {
display_opengl = 1; display_opengl = 1;

View File

@ -623,9 +623,20 @@ static const DisplayChangeListenerOps dcl_ops = {
#if defined(CONFIG_OPENGL) #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 = { static const DisplayChangeListenerOps dcl_gl_area_ops = {
.dpy_name = "gtk-egl", .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_create = gd_gl_area_create_context,
.dpy_gl_ctx_destroy = gd_gl_area_destroy_context, .dpy_gl_ctx_destroy = gd_gl_area_destroy_context,
.dpy_gl_ctx_make_current = gd_gl_area_make_current, .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_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_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 = { static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_name = "gtk-egl", .dpy_name = "gtk-egl",
.dpy_gfx_update = gd_egl_update, .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_create = gd_egl_create_context,
.dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context,
.dpy_gl_ctx_make_current = gd_egl_make_current, .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_disable = gd_egl_scanout_disable,
.dpy_gl_scanout_texture = gd_egl_scanout_texture, .dpy_gl_scanout_texture = gd_egl_scanout_texture,
.dpy_gl_scanout_dmabuf = gd_egl_scanout_dmabuf, .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_cursor_position = gd_egl_cursor_position,
.dpy_gl_release_dmabuf = gd_egl_release_dmabuf, .dpy_gl_release_dmabuf = gd_egl_release_dmabuf,
.dpy_gl_update = gd_egl_scanout_flush, .dpy_gl_update = gd_egl_scanout_flush,
.dpy_has_dmabuf = gd_has_dmabuf,
}; };
#endif /* CONFIG_OPENGL */ #endif /* CONFIG_OPENGL */
@ -1980,6 +1991,18 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
return machine_menu; 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, static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
QemuConsole *con, int idx, QemuConsole *con, int idx,
GSList *group, GtkWidget *view_menu) GSList *group, GtkWidget *view_menu)
@ -1993,13 +2016,12 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
#if defined(CONFIG_OPENGL) #if defined(CONFIG_OPENGL)
if (display_opengl) { if (display_opengl) {
#if defined(CONFIG_OPENGL)
if (gtk_use_gl_area) { if (gtk_use_gl_area) {
vc->gfx.drawing_area = gtk_gl_area_new(); 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; vc->gfx.dcl.ops = &dcl_gl_area_ops;
} else } else {
#endif /* CONFIG_OPENGL */
{
vc->gfx.drawing_area = gtk_drawing_area_new(); vc->gfx.drawing_area = gtk_drawing_area_new();
/* /*
* gtk_widget_set_double_buffered() was deprecated in 3.14. * 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); gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
vc->gfx.dcl.ops = &dcl_egl_ops; vc->gfx.dcl.ops = &dcl_egl_ops;
vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
} }
} else } else
#endif #endif

View File

@ -58,6 +58,7 @@ static void sdl2_gl_render_surface(struct sdl2_console *scon)
surface_gl_render_texture(scon->gls, scon->surface); surface_gl_render_texture(scon->gls, scon->surface);
SDL_GL_SwapWindow(scon->real_window); SDL_GL_SwapWindow(scon->real_window);
graphic_hw_gl_flushed(scon->dcl.con);
} }
void sdl2_gl_update(DisplayChangeListener *dcl, 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); 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) void sdl2_gl_scanout_disable(DisplayChangeListener *dcl)
{ {
struct sdl2_console *scon = container_of(dcl, struct sdl2_console, 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); egl_fb_blit(&scon->win_fb, &scon->guest_fb, !scon->y0_top);
SDL_GL_SwapWindow(scon->real_window); SDL_GL_SwapWindow(scon->real_window);
graphic_hw_gl_flushed(dcl->con);
} }

View File

@ -781,7 +781,6 @@ static const DisplayChangeListenerOps dcl_gl_ops = {
.dpy_gl_ctx_create = sdl2_gl_create_context, .dpy_gl_ctx_create = sdl2_gl_create_context,
.dpy_gl_ctx_destroy = sdl2_gl_destroy_context, .dpy_gl_ctx_destroy = sdl2_gl_destroy_context,
.dpy_gl_ctx_make_current = sdl2_gl_make_context_current, .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_disable = sdl2_gl_scanout_disable,
.dpy_gl_scanout_texture = sdl2_gl_scanout_texture, .dpy_gl_scanout_texture = sdl2_gl_scanout_texture,
.dpy_gl_update = sdl2_gl_scanout_flush, .dpy_gl_update = sdl2_gl_scanout_flush,

View File

@ -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) static void qemu_spice_init(void)
{ {
QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
@ -796,7 +804,6 @@ static void qemu_spice_init(void)
qemu_spice_input_init(); qemu_spice_input_init();
qemu_add_vm_change_state_handler(vm_change_state_handler, NULL);
qemu_spice_display_stop(); qemu_spice_display_stop();
g_free(x509_key_file); g_free(x509_key_file);

View File

@ -826,6 +826,7 @@ static void qemu_spice_gl_unblock_bh(void *opaque)
SimpleSpiceDisplay *ssd = opaque; SimpleSpiceDisplay *ssd = opaque;
qemu_spice_gl_block(ssd, false); qemu_spice_gl_block(ssd, false);
graphic_hw_gl_flushed(ssd->dcl.con);
} }
static void qemu_spice_gl_block_timer(void *opaque) 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_create = qemu_spice_gl_create_context,
.dpy_gl_ctx_destroy = qemu_egl_destroy_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context,
.dpy_gl_ctx_make_current = qemu_egl_make_context_current, .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_disable = qemu_spice_gl_scanout_disable,
.dpy_gl_scanout_texture = qemu_spice_gl_scanout_texture, .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_one(con);
} }
qemu_spice_display_init_done();
} }