mirror of https://github.com/xemu-project/xemu.git
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
|
@ -279,6 +279,7 @@ smartcard=""
|
|||
libusb=""
|
||||
usb_redir=""
|
||||
opengl=""
|
||||
opengl_dmabuf="no"
|
||||
zlib="yes"
|
||||
lzo=""
|
||||
snappy=""
|
||||
|
@ -3274,7 +3275,7 @@ libs_softmmu="$libs_softmmu $fdt_libs"
|
|||
# opengl probe (for sdl2, gtk, milkymist-tmu2)
|
||||
|
||||
if test "$opengl" != "no" ; then
|
||||
opengl_pkgs="epoxy"
|
||||
opengl_pkgs="epoxy libdrm gbm"
|
||||
if $pkg_config $opengl_pkgs x11; then
|
||||
opengl_cflags="$($pkg_config --cflags $opengl_pkgs) $x11_cflags"
|
||||
opengl_libs="$($pkg_config --libs $opengl_pkgs) $x11_libs"
|
||||
|
@ -3292,6 +3293,18 @@ if test "$opengl" != "no" ; then
|
|||
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
|
||||
|
@ -4752,6 +4765,7 @@ echo "smartcard support $smartcard"
|
|||
echo "libusb $libusb"
|
||||
echo "usb net redir $usb_redir"
|
||||
echo "OpenGL support $opengl"
|
||||
echo "OpenGL dmabufs $opengl_dmabuf"
|
||||
echo "libiscsi support $libiscsi"
|
||||
echo "libnfs support $libnfs"
|
||||
echo "build guest agent $guest_agent"
|
||||
|
@ -5050,6 +5064,7 @@ if test "$gtk" = "yes" ; then
|
|||
echo "CONFIG_GTK=y" >> $config_host_mak
|
||||
echo "CONFIG_GTKABI=$gtkabi" >> $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
|
||||
echo "CONFIG_GTK_GL=y" >> $config_host_mak
|
||||
fi
|
||||
|
@ -5158,6 +5173,9 @@ if test "$opengl" = "yes" ; then
|
|||
echo "CONFIG_OPENGL=y" >> $config_host_mak
|
||||
echo "OPENGL_CFLAGS=$opengl_cflags" >> $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
|
||||
|
||||
if test "$lzo" = "yes" ; then
|
||||
|
|
|
@ -158,6 +158,8 @@ struct MigrationState
|
|||
|
||||
/* Flag set once the migration has been asked to enter 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) */
|
||||
bool migration_thread_running;
|
||||
|
@ -211,6 +213,8 @@ bool migration_has_finished(MigrationState *);
|
|||
bool migration_has_failed(MigrationState *);
|
||||
/* True if outgoing migration has entered postcopy phase */
|
||||
bool migration_in_postcopy(MigrationState *);
|
||||
/* ...and after the device transmission */
|
||||
bool migration_in_postcopy_after_devices(MigrationState *);
|
||||
MigrationState *migrate_get_current(void);
|
||||
|
||||
void migrate_compress_threads_create(void);
|
||||
|
|
|
@ -3,10 +3,23 @@
|
|||
|
||||
#include <epoxy/gl.h>
|
||||
#include <epoxy/egl.h>
|
||||
#include <gbm.h>
|
||||
|
||||
extern EGLDisplay *qemu_egl_display;
|
||||
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);
|
||||
|
||||
int qemu_egl_init_dpy(EGLNativeDisplayType dpy, bool gles, bool debug);
|
||||
|
|
|
@ -24,6 +24,14 @@
|
|||
#include "ui/console.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 MEMSLOT_GENERATION_BITS 8
|
||||
#define MEMSLOT_SLOT_BITS 8
|
||||
|
@ -50,6 +58,7 @@ enum {
|
|||
QXL_COOKIE_TYPE_IO,
|
||||
QXL_COOKIE_TYPE_RENDER_UPDATE_AREA,
|
||||
QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG,
|
||||
QXL_COOKIE_TYPE_GL_DRAW_DONE,
|
||||
};
|
||||
|
||||
typedef struct QXLCookie {
|
||||
|
@ -104,6 +113,13 @@ struct SimpleSpiceDisplay {
|
|||
QEMUCursor *cursor;
|
||||
int mouse_x, mouse_y;
|
||||
QEMUBH *cursor_bh;
|
||||
|
||||
#ifdef HAVE_SPICE_GL
|
||||
/* opengl rendering */
|
||||
QEMUBH *gl_unblock_bh;
|
||||
QEMUTimer *gl_unblock_timer;
|
||||
int dmabuf_fd;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct SimpleSpiceUpdate {
|
||||
|
|
|
@ -905,6 +905,11 @@ bool migration_in_postcopy(MigrationState *s)
|
|||
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 *s = migrate_get_current();
|
||||
|
@ -930,6 +935,7 @@ MigrationState *migrate_init(const MigrationParams *params)
|
|||
s->setup_time = 0;
|
||||
s->dirty_sync_count = 0;
|
||||
s->start_postcopy = false;
|
||||
s->postcopy_after_devices = false;
|
||||
s->migration_thread_running = false;
|
||||
s->last_req_rb = NULL;
|
||||
|
||||
|
@ -1489,6 +1495,14 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running)
|
|||
goto fail_closefb;
|
||||
}
|
||||
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;
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
|
|
@ -1051,6 +1051,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice,
|
|||
" [,streaming-video=[off|all|filter]][,disable-copy-paste]\n"
|
||||
" [,disable-agent-file-xfer][,agent-mouse=[on|off]]\n"
|
||||
" [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n"
|
||||
" [,gl=[on|off]]\n"
|
||||
" enable spice\n"
|
||||
" at least one of {port, tls-port} is mandatory\n",
|
||||
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]
|
||||
Enable/disable spice seamless migration. Default is off.
|
||||
|
||||
@item gl=[on|off]
|
||||
Enable/disable OpenGL context. Default is off.
|
||||
|
||||
@end table
|
||||
ETEXI
|
||||
|
||||
|
|
129
ui/egl-helpers.c
129
ui/egl-helpers.c
|
@ -1,6 +1,8 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include <glob.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "config-host.h"
|
||||
#include "ui/egl-helpers.h"
|
||||
|
||||
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 esurface;
|
||||
|
|
|
@ -494,9 +494,14 @@ static QemuOptsList qemu_spice_opts = {
|
|||
},{
|
||||
.name = "playback-compression",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
}, {
|
||||
},{
|
||||
.name = "seamless-migration",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
#ifdef HAVE_SPICE_GL
|
||||
},{
|
||||
.name = "gl",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
#endif
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
|
@ -568,7 +573,8 @@ static void migration_state_notifier(Notifier *notifier, void *data)
|
|||
|
||||
if (migration_in_setup(s)) {
|
||||
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_have_target_host = false;
|
||||
} else if (migration_has_failed(s)) {
|
||||
|
@ -819,6 +825,14 @@ void qemu_spice_init(void)
|
|||
#if SPICE_SERVER_VERSION >= 0x000c02
|
||||
qemu_spice_register_ports();
|
||||
#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)
|
||||
|
|
|
@ -460,6 +460,13 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
|
|||
|
||||
memset(&ssd->dirty, 0, sizeof(ssd->dirty));
|
||||
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)
|
||||
|
@ -467,8 +474,6 @@ static void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd)
|
|||
if (ssd->cursor) {
|
||||
assert(ssd->dcl.con);
|
||||
dpy_cursor_define(ssd->dcl.con, ssd->cursor);
|
||||
cursor_put(ssd->cursor);
|
||||
ssd->cursor = NULL;
|
||||
}
|
||||
if (ssd->mouse_x != -1 && ssd->mouse_y != -1) {
|
||||
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)
|
||||
{
|
||||
dprint(1, "%s/%d:\n", __func__, sin->id);
|
||||
dprint(2, "%s/%d:\n", __func__, sin->id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -616,7 +621,7 @@ static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext)
|
|||
|
||||
static int interface_req_cursor_notification(QXLInstance *sin)
|
||||
{
|
||||
dprint(1, "%s:\n", __FUNCTION__);
|
||||
dprint(2, "%s:\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -645,9 +650,23 @@ static void interface_update_area_complete(QXLInstance *sin,
|
|||
/* called from spice server thread context only */
|
||||
static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
|
||||
{
|
||||
/* should never be called, used in qxl native mode only */
|
||||
fprintf(stderr, "%s: abort()\n", __func__);
|
||||
abort();
|
||||
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 */
|
||||
fprintf(stderr, "%s: abort()\n", __func__);
|
||||
abort();
|
||||
}
|
||||
g_free(cookie);
|
||||
}
|
||||
|
||||
static void interface_set_client_capabilities(QXLInstance *sin,
|
||||
|
@ -750,6 +769,11 @@ static void display_mouse_define(DisplayChangeListener *dcl,
|
|||
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
|
||||
|
||||
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_y = c->hot_y;
|
||||
g_free(ssd->ptr_move);
|
||||
|
@ -769,20 +793,128 @@ static const DisplayChangeListenerOps display_listener_ops = {
|
|||
.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)
|
||||
{
|
||||
SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1);
|
||||
|
||||
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;
|
||||
qemu_spice_add_display_interface(&ssd->qxl, con);
|
||||
assert(ssd->worker);
|
||||
|
||||
qemu_spice_create_host_memslot(ssd);
|
||||
|
||||
ssd->dcl.ops = &display_listener_ops;
|
||||
ssd->dcl.con = con;
|
||||
register_displaychangelistener(&ssd->dcl);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue