mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-30 05:21:55 -06:00
spice: initial opengl/virgl support, postcopy migration fix.
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJWzFDwAAoJEEy22O7T6HE4vesP/1IULiyxkiu+rnLquspRKlKr Z9GE3svmQmD8BdI88uJqz28+Wwz37TyQGA9ycNmOWNeWwY88SmLo95VhHcGtk2kj 3FdrwhaPLW+4/0yVS0BerhH4W0jVq2x7V1kRTVrejwE5TL3NaJzBf45bfxoicn68 /oibclWwNhyaiNWklTfbTgNMaKaSsXqZqJ6wv+YEtIGsH1jhbUh0KHvJRc29rTPT nHLGM3IcMDTKfZMTWUyJR7d8R7wdQTxnZiYole4f2e8hJ+Vx6SgbGpkLPCsLlv2U jWJCzgsnJEbBD76OjGem9u/NmQoBp8BQFYLmA9Kx512HYw54EKnGQt8Lnft3pjTC ptUxA5C9DAniWKABo28Cea/CYV6mEjLR+Yk76omh/emJQib2db0qy2ljBdkb7MJK Y+tDCamH6eZ/1CnbNWsY/zp3ThMbCvWy8K9w/8mmRuQ5TH5RZNHSgexAx4voVGlW +d9d6+fh9F3sKR9JiAiiRpPATnA7NLqHstuylLAW0t80EMSvqTcuW8PzIS1A1K+p JceQxjzfsVIBY77/INYK91yLJUkMDIihi9hFoNy/zN81VyAr4NlRhktKvsQ5pRWV rp9CwxuzcKbYVXyLRVNAOQTCFINBhGmD/2K9UYf8vkku9knFywKeCQn0j0LVW+TZ X2b5o573noPRDDDMv9e5 =PeHJ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/spice/tags/pull-spice-20160223-1' into staging spice: initial opengl/virgl support, postcopy migration fix. # gpg: Signature made Tue 23 Feb 2016 12:30:40 GMT using RSA key ID D3E87138 # gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" # gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" # gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" * remotes/spice/tags/pull-spice-20160223-1: Postcopy+spice: Pass spice migration data earlier spice/gl: tweak debug messages. spice/gl: add unblock timer spice: add opengl/virgl/dmabuf support spice: reset cursor on resize egl-helpers: add functions for render nodes and dma-buf passing configure: add dma-buf support detection. spice: init dcl before registering qxl interface Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1b1624092d
9 changed files with 357 additions and 13 deletions
20
configure
vendored
20
configure
vendored
|
@ -279,6 +279,7 @@ smartcard=""
|
||||||
libusb=""
|
libusb=""
|
||||||
usb_redir=""
|
usb_redir=""
|
||||||
opengl=""
|
opengl=""
|
||||||
|
opengl_dmabuf="no"
|
||||||
zlib="yes"
|
zlib="yes"
|
||||||
lzo=""
|
lzo=""
|
||||||
snappy=""
|
snappy=""
|
||||||
|
@ -3274,7 +3275,7 @@ libs_softmmu="$libs_softmmu $fdt_libs"
|
||||||
# opengl probe (for sdl2, gtk, milkymist-tmu2)
|
# opengl probe (for sdl2, gtk, milkymist-tmu2)
|
||||||
|
|
||||||
if test "$opengl" != "no" ; then
|
if test "$opengl" != "no" ; then
|
||||||
opengl_pkgs="epoxy"
|
opengl_pkgs="epoxy libdrm gbm"
|
||||||
if $pkg_config $opengl_pkgs x11; then
|
if $pkg_config $opengl_pkgs x11; then
|
||||||
opengl_cflags="$($pkg_config --cflags $opengl_pkgs) $x11_cflags"
|
opengl_cflags="$($pkg_config --cflags $opengl_pkgs) $x11_cflags"
|
||||||
opengl_libs="$($pkg_config --libs $opengl_pkgs) $x11_libs"
|
opengl_libs="$($pkg_config --libs $opengl_pkgs) $x11_libs"
|
||||||
|
@ -3292,6 +3293,18 @@ if test "$opengl" != "no" ; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "$opengl" = "yes"; then
|
||||||
|
cat > $TMPC << EOF
|
||||||
|
#include <epoxy/egl.h>
|
||||||
|
#ifndef EGL_MESA_image_dma_buf_export
|
||||||
|
# error mesa/epoxy lacks support for dmabufs (mesa 10.6+)
|
||||||
|
#endif
|
||||||
|
int main(void) { return 0; }
|
||||||
|
EOF
|
||||||
|
if compile_prog "" "" ; then
|
||||||
|
opengl_dmabuf=yes
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# archipelago probe
|
# archipelago probe
|
||||||
|
@ -4752,6 +4765,7 @@ echo "smartcard support $smartcard"
|
||||||
echo "libusb $libusb"
|
echo "libusb $libusb"
|
||||||
echo "usb net redir $usb_redir"
|
echo "usb net redir $usb_redir"
|
||||||
echo "OpenGL support $opengl"
|
echo "OpenGL support $opengl"
|
||||||
|
echo "OpenGL dmabufs $opengl_dmabuf"
|
||||||
echo "libiscsi support $libiscsi"
|
echo "libiscsi support $libiscsi"
|
||||||
echo "libnfs support $libnfs"
|
echo "libnfs support $libnfs"
|
||||||
echo "build guest agent $guest_agent"
|
echo "build guest agent $guest_agent"
|
||||||
|
@ -5050,6 +5064,7 @@ if test "$gtk" = "yes" ; then
|
||||||
echo "CONFIG_GTK=y" >> $config_host_mak
|
echo "CONFIG_GTK=y" >> $config_host_mak
|
||||||
echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak
|
echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak
|
||||||
echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak
|
echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak
|
||||||
|
echo "GTK_LIBS=$gtk_libs" >> $config_host_mak
|
||||||
if test "$gtk_gl" = "yes" ; then
|
if test "$gtk_gl" = "yes" ; then
|
||||||
echo "CONFIG_GTK_GL=y" >> $config_host_mak
|
echo "CONFIG_GTK_GL=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
@ -5158,6 +5173,9 @@ if test "$opengl" = "yes" ; then
|
||||||
echo "CONFIG_OPENGL=y" >> $config_host_mak
|
echo "CONFIG_OPENGL=y" >> $config_host_mak
|
||||||
echo "OPENGL_CFLAGS=$opengl_cflags" >> $config_host_mak
|
echo "OPENGL_CFLAGS=$opengl_cflags" >> $config_host_mak
|
||||||
echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak
|
echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak
|
||||||
|
if test "$opengl_dmabuf" = "yes" ; then
|
||||||
|
echo "CONFIG_OPENGL_DMABUF=y" >> $config_host_mak
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$lzo" = "yes" ; then
|
if test "$lzo" = "yes" ; then
|
||||||
|
|
|
@ -158,6 +158,8 @@ struct MigrationState
|
||||||
|
|
||||||
/* Flag set once the migration has been asked to enter postcopy */
|
/* Flag set once the migration has been asked to enter postcopy */
|
||||||
bool start_postcopy;
|
bool start_postcopy;
|
||||||
|
/* Flag set after postcopy has sent the device state */
|
||||||
|
bool postcopy_after_devices;
|
||||||
|
|
||||||
/* Flag set once the migration thread is running (and needs joining) */
|
/* Flag set once the migration thread is running (and needs joining) */
|
||||||
bool migration_thread_running;
|
bool migration_thread_running;
|
||||||
|
@ -211,6 +213,8 @@ bool migration_has_finished(MigrationState *);
|
||||||
bool migration_has_failed(MigrationState *);
|
bool migration_has_failed(MigrationState *);
|
||||||
/* True if outgoing migration has entered postcopy phase */
|
/* True if outgoing migration has entered postcopy phase */
|
||||||
bool migration_in_postcopy(MigrationState *);
|
bool migration_in_postcopy(MigrationState *);
|
||||||
|
/* ...and after the device transmission */
|
||||||
|
bool migration_in_postcopy_after_devices(MigrationState *);
|
||||||
MigrationState *migrate_get_current(void);
|
MigrationState *migrate_get_current(void);
|
||||||
|
|
||||||
void migrate_compress_threads_create(void);
|
void migrate_compress_threads_create(void);
|
||||||
|
|
|
@ -3,10 +3,23 @@
|
||||||
|
|
||||||
#include <epoxy/gl.h>
|
#include <epoxy/gl.h>
|
||||||
#include <epoxy/egl.h>
|
#include <epoxy/egl.h>
|
||||||
|
#include <gbm.h>
|
||||||
|
|
||||||
extern EGLDisplay *qemu_egl_display;
|
extern EGLDisplay *qemu_egl_display;
|
||||||
extern EGLConfig qemu_egl_config;
|
extern EGLConfig qemu_egl_config;
|
||||||
|
|
||||||
|
#ifdef CONFIG_OPENGL_DMABUF
|
||||||
|
|
||||||
|
extern int qemu_egl_rn_fd;
|
||||||
|
extern struct gbm_device *qemu_egl_rn_gbm_dev;
|
||||||
|
extern EGLContext qemu_egl_rn_ctx;
|
||||||
|
|
||||||
|
int qemu_egl_rendernode_open(void);
|
||||||
|
int egl_rendernode_init(void);
|
||||||
|
int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win);
|
EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win);
|
||||||
|
|
||||||
int qemu_egl_init_dpy(EGLNativeDisplayType dpy, bool gles, bool debug);
|
int qemu_egl_init_dpy(EGLNativeDisplayType dpy, bool gles, bool debug);
|
||||||
|
|
|
@ -24,6 +24,14 @@
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
|
|
||||||
|
#if defined(CONFIG_OPENGL_DMABUF)
|
||||||
|
# if SPICE_SERVER_VERSION >= 0x000d00 /* release 0.13.0 */
|
||||||
|
# define HAVE_SPICE_GL 1
|
||||||
|
# include "ui/egl-helpers.h"
|
||||||
|
# include "ui/egl-context.h"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#define NUM_MEMSLOTS 8
|
#define NUM_MEMSLOTS 8
|
||||||
#define MEMSLOT_GENERATION_BITS 8
|
#define MEMSLOT_GENERATION_BITS 8
|
||||||
#define MEMSLOT_SLOT_BITS 8
|
#define MEMSLOT_SLOT_BITS 8
|
||||||
|
@ -50,6 +58,7 @@ enum {
|
||||||
QXL_COOKIE_TYPE_IO,
|
QXL_COOKIE_TYPE_IO,
|
||||||
QXL_COOKIE_TYPE_RENDER_UPDATE_AREA,
|
QXL_COOKIE_TYPE_RENDER_UPDATE_AREA,
|
||||||
QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG,
|
QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG,
|
||||||
|
QXL_COOKIE_TYPE_GL_DRAW_DONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct QXLCookie {
|
typedef struct QXLCookie {
|
||||||
|
@ -104,6 +113,13 @@ struct SimpleSpiceDisplay {
|
||||||
QEMUCursor *cursor;
|
QEMUCursor *cursor;
|
||||||
int mouse_x, mouse_y;
|
int mouse_x, mouse_y;
|
||||||
QEMUBH *cursor_bh;
|
QEMUBH *cursor_bh;
|
||||||
|
|
||||||
|
#ifdef HAVE_SPICE_GL
|
||||||
|
/* opengl rendering */
|
||||||
|
QEMUBH *gl_unblock_bh;
|
||||||
|
QEMUTimer *gl_unblock_timer;
|
||||||
|
int dmabuf_fd;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SimpleSpiceUpdate {
|
struct SimpleSpiceUpdate {
|
||||||
|
|
|
@ -905,6 +905,11 @@ bool migration_in_postcopy(MigrationState *s)
|
||||||
return (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
|
return (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool migration_in_postcopy_after_devices(MigrationState *s)
|
||||||
|
{
|
||||||
|
return migration_in_postcopy(s) && s->postcopy_after_devices;
|
||||||
|
}
|
||||||
|
|
||||||
MigrationState *migrate_init(const MigrationParams *params)
|
MigrationState *migrate_init(const MigrationParams *params)
|
||||||
{
|
{
|
||||||
MigrationState *s = migrate_get_current();
|
MigrationState *s = migrate_get_current();
|
||||||
|
@ -930,6 +935,7 @@ MigrationState *migrate_init(const MigrationParams *params)
|
||||||
s->setup_time = 0;
|
s->setup_time = 0;
|
||||||
s->dirty_sync_count = 0;
|
s->dirty_sync_count = 0;
|
||||||
s->start_postcopy = false;
|
s->start_postcopy = false;
|
||||||
|
s->postcopy_after_devices = false;
|
||||||
s->migration_thread_running = false;
|
s->migration_thread_running = false;
|
||||||
s->last_req_rb = NULL;
|
s->last_req_rb = NULL;
|
||||||
|
|
||||||
|
@ -1489,6 +1495,14 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running)
|
||||||
goto fail_closefb;
|
goto fail_closefb;
|
||||||
}
|
}
|
||||||
qemu_fclose(fb);
|
qemu_fclose(fb);
|
||||||
|
|
||||||
|
/* Send a notify to give a chance for anything that needs to happen
|
||||||
|
* at the transition to postcopy and after the device state; in particular
|
||||||
|
* spice needs to trigger a transition now
|
||||||
|
*/
|
||||||
|
ms->postcopy_after_devices = true;
|
||||||
|
notifier_list_notify(&migration_state_notifiers, ms);
|
||||||
|
|
||||||
ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - time_at_stop;
|
ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - time_at_stop;
|
||||||
|
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
|
|
|
@ -1051,6 +1051,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice,
|
||||||
" [,streaming-video=[off|all|filter]][,disable-copy-paste]\n"
|
" [,streaming-video=[off|all|filter]][,disable-copy-paste]\n"
|
||||||
" [,disable-agent-file-xfer][,agent-mouse=[on|off]]\n"
|
" [,disable-agent-file-xfer][,agent-mouse=[on|off]]\n"
|
||||||
" [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n"
|
" [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n"
|
||||||
|
" [,gl=[on|off]]\n"
|
||||||
" enable spice\n"
|
" enable spice\n"
|
||||||
" at least one of {port, tls-port} is mandatory\n",
|
" at least one of {port, tls-port} is mandatory\n",
|
||||||
QEMU_ARCH_ALL)
|
QEMU_ARCH_ALL)
|
||||||
|
@ -1142,6 +1143,9 @@ Enable/disable audio stream compression (using celt 0.5.1). Default is on.
|
||||||
@item seamless-migration=[on|off]
|
@item seamless-migration=[on|off]
|
||||||
Enable/disable spice seamless migration. Default is off.
|
Enable/disable spice seamless migration. Default is off.
|
||||||
|
|
||||||
|
@item gl=[on|off]
|
||||||
|
Enable/disable OpenGL context. Default is off.
|
||||||
|
|
||||||
@end table
|
@end table
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
|
|
129
ui/egl-helpers.c
129
ui/egl-helpers.c
|
@ -1,6 +1,8 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include <glob.h>
|
#include <glob.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#include "config-host.h"
|
||||||
#include "ui/egl-helpers.h"
|
#include "ui/egl-helpers.h"
|
||||||
|
|
||||||
EGLDisplay *qemu_egl_display;
|
EGLDisplay *qemu_egl_display;
|
||||||
|
@ -20,6 +22,133 @@ static int egl_debug;
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#ifdef CONFIG_OPENGL_DMABUF
|
||||||
|
|
||||||
|
int qemu_egl_rn_fd;
|
||||||
|
struct gbm_device *qemu_egl_rn_gbm_dev;
|
||||||
|
EGLContext qemu_egl_rn_ctx;
|
||||||
|
|
||||||
|
int qemu_egl_rendernode_open(void)
|
||||||
|
{
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *e;
|
||||||
|
int r, fd;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
dir = opendir("/dev/dri");
|
||||||
|
if (!dir) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = -1;
|
||||||
|
while ((e = readdir(dir))) {
|
||||||
|
if (e->d_type != DT_CHR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(e->d_name, "renderD", 7)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = asprintf(&p, "/dev/dri/%s", e->d_name);
|
||||||
|
if (r < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
|
||||||
|
if (r < 0) {
|
||||||
|
free(p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fd = r;
|
||||||
|
free(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
if (fd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int egl_rendernode_init(void)
|
||||||
|
{
|
||||||
|
qemu_egl_rn_fd = -1;
|
||||||
|
|
||||||
|
qemu_egl_rn_fd = qemu_egl_rendernode_open();
|
||||||
|
if (qemu_egl_rn_fd == -1) {
|
||||||
|
fprintf(stderr, "egl: no drm render node available\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd);
|
||||||
|
if (!qemu_egl_rn_gbm_dev) {
|
||||||
|
fprintf(stderr, "egl: gbm_create_device failed\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_egl_init_dpy((EGLNativeDisplayType)qemu_egl_rn_gbm_dev, false, false);
|
||||||
|
|
||||||
|
if (!epoxy_has_egl_extension(qemu_egl_display,
|
||||||
|
"EGL_KHR_surfaceless_context")) {
|
||||||
|
fprintf(stderr, "egl: EGL_KHR_surfaceless_context not supported\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (!epoxy_has_egl_extension(qemu_egl_display,
|
||||||
|
"EGL_MESA_image_dma_buf_export")) {
|
||||||
|
fprintf(stderr, "egl: EGL_MESA_image_dma_buf_export not supported\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_egl_rn_ctx = qemu_egl_init_ctx();
|
||||||
|
if (!qemu_egl_rn_ctx) {
|
||||||
|
fprintf(stderr, "egl: egl_init_ctx failed\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (qemu_egl_rn_gbm_dev) {
|
||||||
|
gbm_device_destroy(qemu_egl_rn_gbm_dev);
|
||||||
|
}
|
||||||
|
if (qemu_egl_rn_fd != -1) {
|
||||||
|
close(qemu_egl_rn_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc)
|
||||||
|
{
|
||||||
|
EGLImageKHR image;
|
||||||
|
EGLint num_planes, fd;
|
||||||
|
|
||||||
|
image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(),
|
||||||
|
EGL_GL_TEXTURE_2D_KHR,
|
||||||
|
(EGLClientBuffer)(unsigned long)tex_id,
|
||||||
|
NULL);
|
||||||
|
if (!image) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc,
|
||||||
|
&num_planes, NULL);
|
||||||
|
if (num_planes != 1) {
|
||||||
|
eglDestroyImageKHR(qemu_egl_display, image);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL);
|
||||||
|
eglDestroyImageKHR(qemu_egl_display, image);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_OPENGL_DMABUF */
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win)
|
EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win)
|
||||||
{
|
{
|
||||||
EGLSurface esurface;
|
EGLSurface esurface;
|
||||||
|
|
|
@ -497,6 +497,11 @@ static QemuOptsList qemu_spice_opts = {
|
||||||
},{
|
},{
|
||||||
.name = "seamless-migration",
|
.name = "seamless-migration",
|
||||||
.type = QEMU_OPT_BOOL,
|
.type = QEMU_OPT_BOOL,
|
||||||
|
#ifdef HAVE_SPICE_GL
|
||||||
|
},{
|
||||||
|
.name = "gl",
|
||||||
|
.type = QEMU_OPT_BOOL,
|
||||||
|
#endif
|
||||||
},
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
|
@ -568,7 +573,8 @@ static void migration_state_notifier(Notifier *notifier, void *data)
|
||||||
|
|
||||||
if (migration_in_setup(s)) {
|
if (migration_in_setup(s)) {
|
||||||
spice_server_migrate_start(spice_server);
|
spice_server_migrate_start(spice_server);
|
||||||
} else if (migration_has_finished(s)) {
|
} else if (migration_has_finished(s) ||
|
||||||
|
migration_in_postcopy_after_devices(s)) {
|
||||||
spice_server_migrate_end(spice_server, true);
|
spice_server_migrate_end(spice_server, true);
|
||||||
spice_have_target_host = false;
|
spice_have_target_host = false;
|
||||||
} else if (migration_has_failed(s)) {
|
} else if (migration_has_failed(s)) {
|
||||||
|
@ -819,6 +825,14 @@ void qemu_spice_init(void)
|
||||||
#if SPICE_SERVER_VERSION >= 0x000c02
|
#if SPICE_SERVER_VERSION >= 0x000c02
|
||||||
qemu_spice_register_ports();
|
qemu_spice_register_ports();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SPICE_GL
|
||||||
|
if (qemu_opt_get_bool(opts, "gl", 0)) {
|
||||||
|
if (egl_rendernode_init() == 0) {
|
||||||
|
display_opengl = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int qemu_spice_add_interface(SpiceBaseInstance *sin)
|
int qemu_spice_add_interface(SpiceBaseInstance *sin)
|
||||||
|
|
|
@ -460,6 +460,13 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
|
||||||
|
|
||||||
memset(&ssd->dirty, 0, sizeof(ssd->dirty));
|
memset(&ssd->dirty, 0, sizeof(ssd->dirty));
|
||||||
ssd->notify++;
|
ssd->notify++;
|
||||||
|
|
||||||
|
qemu_mutex_lock(&ssd->lock);
|
||||||
|
if (ssd->cursor) {
|
||||||
|
g_free(ssd->ptr_define);
|
||||||
|
ssd->ptr_define = qemu_spice_create_cursor_update(ssd, ssd->cursor, 0);
|
||||||
|
}
|
||||||
|
qemu_mutex_unlock(&ssd->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd)
|
static void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd)
|
||||||
|
@ -467,8 +474,6 @@ static void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd)
|
||||||
if (ssd->cursor) {
|
if (ssd->cursor) {
|
||||||
assert(ssd->dcl.con);
|
assert(ssd->dcl.con);
|
||||||
dpy_cursor_define(ssd->dcl.con, ssd->cursor);
|
dpy_cursor_define(ssd->dcl.con, ssd->cursor);
|
||||||
cursor_put(ssd->cursor);
|
|
||||||
ssd->cursor = NULL;
|
|
||||||
}
|
}
|
||||||
if (ssd->mouse_x != -1 && ssd->mouse_y != -1) {
|
if (ssd->mouse_x != -1 && ssd->mouse_y != -1) {
|
||||||
assert(ssd->dcl.con);
|
assert(ssd->dcl.con);
|
||||||
|
@ -563,7 +568,7 @@ static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext)
|
||||||
|
|
||||||
static int interface_req_cmd_notification(QXLInstance *sin)
|
static int interface_req_cmd_notification(QXLInstance *sin)
|
||||||
{
|
{
|
||||||
dprint(1, "%s/%d:\n", __func__, sin->id);
|
dprint(2, "%s/%d:\n", __func__, sin->id);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,7 +621,7 @@ static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext)
|
||||||
|
|
||||||
static int interface_req_cursor_notification(QXLInstance *sin)
|
static int interface_req_cursor_notification(QXLInstance *sin)
|
||||||
{
|
{
|
||||||
dprint(1, "%s:\n", __FUNCTION__);
|
dprint(2, "%s:\n", __func__);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,10 +650,24 @@ static void interface_update_area_complete(QXLInstance *sin,
|
||||||
/* called from spice server thread context only */
|
/* called from spice server thread context only */
|
||||||
static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
|
static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
|
||||||
{
|
{
|
||||||
|
QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token;
|
||||||
|
|
||||||
|
switch (cookie->type) {
|
||||||
|
#ifdef HAVE_SPICE_GL
|
||||||
|
case QXL_COOKIE_TYPE_GL_DRAW_DONE:
|
||||||
|
{
|
||||||
|
SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
|
||||||
|
qemu_bh_schedule(ssd->gl_unblock_bh);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
/* should never be called, used in qxl native mode only */
|
/* should never be called, used in qxl native mode only */
|
||||||
fprintf(stderr, "%s: abort()\n", __func__);
|
fprintf(stderr, "%s: abort()\n", __func__);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
g_free(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
static void interface_set_client_capabilities(QXLInstance *sin,
|
static void interface_set_client_capabilities(QXLInstance *sin,
|
||||||
uint8_t client_present,
|
uint8_t client_present,
|
||||||
|
@ -750,6 +769,11 @@ static void display_mouse_define(DisplayChangeListener *dcl,
|
||||||
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
|
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
|
||||||
|
|
||||||
qemu_mutex_lock(&ssd->lock);
|
qemu_mutex_lock(&ssd->lock);
|
||||||
|
if (c) {
|
||||||
|
cursor_get(c);
|
||||||
|
}
|
||||||
|
cursor_put(ssd->cursor);
|
||||||
|
ssd->cursor = c;
|
||||||
ssd->hot_x = c->hot_x;
|
ssd->hot_x = c->hot_x;
|
||||||
ssd->hot_y = c->hot_y;
|
ssd->hot_y = c->hot_y;
|
||||||
g_free(ssd->ptr_move);
|
g_free(ssd->ptr_move);
|
||||||
|
@ -769,20 +793,128 @@ static const DisplayChangeListenerOps display_listener_ops = {
|
||||||
.dpy_cursor_define = display_mouse_define,
|
.dpy_cursor_define = display_mouse_define,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_SPICE_GL
|
||||||
|
|
||||||
|
static void qemu_spice_gl_block(SimpleSpiceDisplay *ssd, bool block)
|
||||||
|
{
|
||||||
|
uint64_t timeout;
|
||||||
|
|
||||||
|
if (block) {
|
||||||
|
timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
||||||
|
timeout += 1000; /* one sec */
|
||||||
|
timer_mod(ssd->gl_unblock_timer, timeout);
|
||||||
|
} else {
|
||||||
|
timer_del(ssd->gl_unblock_timer);
|
||||||
|
}
|
||||||
|
graphic_hw_gl_block(ssd->dcl.con, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qemu_spice_gl_unblock_bh(void *opaque)
|
||||||
|
{
|
||||||
|
SimpleSpiceDisplay *ssd = opaque;
|
||||||
|
|
||||||
|
qemu_spice_gl_block(ssd, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qemu_spice_gl_block_timer(void *opaque)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "WARNING: spice: no gl-draw-done within one second\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static QEMUGLContext qemu_spice_gl_create_context(DisplayChangeListener *dcl,
|
||||||
|
QEMUGLParams *params)
|
||||||
|
{
|
||||||
|
eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||||
|
qemu_egl_rn_ctx);
|
||||||
|
return qemu_egl_create_context(dcl, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qemu_spice_gl_scanout(DisplayChangeListener *dcl,
|
||||||
|
uint32_t tex_id,
|
||||||
|
bool y_0_top,
|
||||||
|
uint32_t x, uint32_t y,
|
||||||
|
uint32_t w, uint32_t h)
|
||||||
|
{
|
||||||
|
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
|
||||||
|
EGLint stride = 0, fourcc = 0;
|
||||||
|
int fd = -1;
|
||||||
|
|
||||||
|
if (tex_id) {
|
||||||
|
fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "%s: failed to get fd for texture\n", __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dprint(1, "%s: %dx%d (stride %d, fourcc 0x%x)\n", __func__,
|
||||||
|
w, h, stride, fourcc);
|
||||||
|
} else {
|
||||||
|
dprint(1, "%s: no texture (no framebuffer)\n", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!tex_id || fd >= 0);
|
||||||
|
|
||||||
|
/* note: spice server will close the fd */
|
||||||
|
spice_qxl_gl_scanout(&ssd->qxl, fd,
|
||||||
|
surface_width(ssd->ds),
|
||||||
|
surface_height(ssd->ds),
|
||||||
|
stride, fourcc, y_0_top);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qemu_spice_gl_update(DisplayChangeListener *dcl,
|
||||||
|
uint32_t x, uint32_t y, uint32_t w, uint32_t h)
|
||||||
|
{
|
||||||
|
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
|
||||||
|
uint64_t cookie;
|
||||||
|
|
||||||
|
dprint(2, "%s: %dx%d+%d+%d\n", __func__, w, h, x, y);
|
||||||
|
qemu_spice_gl_block(ssd, true);
|
||||||
|
cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
|
||||||
|
spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const DisplayChangeListenerOps display_listener_gl_ops = {
|
||||||
|
.dpy_name = "spice-egl",
|
||||||
|
.dpy_gfx_update = display_update,
|
||||||
|
.dpy_gfx_switch = display_switch,
|
||||||
|
.dpy_gfx_check_format = qemu_pixman_check_format,
|
||||||
|
.dpy_refresh = display_refresh,
|
||||||
|
.dpy_mouse_set = display_mouse_set,
|
||||||
|
.dpy_cursor_define = display_mouse_define,
|
||||||
|
|
||||||
|
.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 = qemu_spice_gl_scanout,
|
||||||
|
.dpy_gl_update = qemu_spice_gl_update,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* HAVE_SPICE_GL */
|
||||||
|
|
||||||
static void qemu_spice_display_init_one(QemuConsole *con)
|
static void qemu_spice_display_init_one(QemuConsole *con)
|
||||||
{
|
{
|
||||||
SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1);
|
SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1);
|
||||||
|
|
||||||
qemu_spice_display_init_common(ssd);
|
qemu_spice_display_init_common(ssd);
|
||||||
|
|
||||||
|
ssd->dcl.ops = &display_listener_ops;
|
||||||
|
#ifdef HAVE_SPICE_GL
|
||||||
|
if (display_opengl) {
|
||||||
|
ssd->dcl.ops = &display_listener_gl_ops;
|
||||||
|
ssd->dmabuf_fd = -1;
|
||||||
|
ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd);
|
||||||
|
ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
|
||||||
|
qemu_spice_gl_block_timer, ssd);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ssd->dcl.con = con;
|
||||||
|
|
||||||
ssd->qxl.base.sif = &dpy_interface.base;
|
ssd->qxl.base.sif = &dpy_interface.base;
|
||||||
qemu_spice_add_display_interface(&ssd->qxl, con);
|
qemu_spice_add_display_interface(&ssd->qxl, con);
|
||||||
assert(ssd->worker);
|
assert(ssd->worker);
|
||||||
|
|
||||||
qemu_spice_create_host_memslot(ssd);
|
qemu_spice_create_host_memslot(ssd);
|
||||||
|
|
||||||
ssd->dcl.ops = &display_listener_ops;
|
|
||||||
ssd->dcl.con = con;
|
|
||||||
register_displaychangelistener(&ssd->dcl);
|
register_displaychangelistener(&ssd->dcl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue