mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
+33
-20
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -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;
|
||||
|
||||
+32
-3
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user