mirror of
https://github.com/qemu/qemu.git
synced 2024-12-03 08:43:38 +08:00
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:
commit
923abdb4bd
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
if (g->renderer_blocked == 0) {
|
||||
vgc->gl_unblock(g);
|
||||
static int
|
||||
virtio_gpu_get_flags(void *opaque)
|
||||
{
|
||||
VirtIOGPUBase *g = opaque;
|
||||
int flags = GRAPHIC_FLAGS_NONE;
|
||||
|
||||
if (virtio_gpu_virgl_enabled(g->conf)) {
|
||||
flags |= GRAPHIC_FLAGS_GL;
|
||||
}
|
||||
|
||||
if (virtio_gpu_dmabuf_enabled(g->conf)) {
|
||||
flags |= GRAPHIC_FLAGS_DMABUF;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static const GraphicHwOps virtio_gpu_ops = {
|
||||
.get_flags = virtio_gpu_get_flags,
|
||||
.invalidate = virtio_gpu_invalidate_display,
|
||||
.gfx_update = virtio_gpu_update_display,
|
||||
.text_update = virtio_gpu_text_update,
|
||||
.ui_info = virtio_gpu_ui_info,
|
||||
.gl_block = virtio_gpu_gl_block,
|
||||
.gl_flushed = virtio_gpu_gl_flushed,
|
||||
};
|
||||
|
||||
bool
|
||||
|
@ -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;
|
||||
|
@ -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 = {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
161
tests/acceptance/virtio-gpu.py
Normal file
161
tests/acceptance/virtio-gpu.py
Normal 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()
|
64
ui/console.c
64
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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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[] = {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
43
ui/gtk.c
43
ui/gtk.c
@ -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
|
||||
|
10
ui/sdl2-gl.c
10
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,
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user