From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=36763 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OoMH9-0000s0-0q for qemu-devel@nongnu.org; Wed, 25 Aug 2010 16:08:49 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OoM5x-0004IP-G0 for qemu-devel@nongnu.org; Wed, 25 Aug 2010 15:57:15 -0400 Received: from mail-qw0-f45.google.com ([209.85.216.45]:64134) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OoM5x-0004IJ-83 for qemu-devel@nongnu.org; Wed, 25 Aug 2010 15:57:13 -0400 Received: by qwh5 with SMTP id 5so897519qwh.4 for ; Wed, 25 Aug 2010 12:57:12 -0700 (PDT) Message-ID: <4C75758C.80501@codemonkey.ws> Date: Wed, 25 Aug 2010 14:57:00 -0500 From: Anthony Liguori MIME-Version: 1.0 Subject: Re: [Qemu-devel] [PATCH v3 09/10] spice: simple display References: <1282745970-11506-1-git-send-email-kraxel@redhat.com> <1282745970-11506-10-git-send-email-kraxel@redhat.com> In-Reply-To: <1282745970-11506-10-git-send-email-kraxel@redhat.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Gerd Hoffmann Cc: qemu-devel@nongnu.org On 08/25/2010 09:19 AM, Gerd Hoffmann wrote: > With that patch applied you'll actually see the guests screen in the > spice client. This does *not* bring qxl and full spice support though. > This is basically the qxl vga mode made more generic, so it plays > together with any qemu-emulated gfx card. You can display stdvga or > cirrus via spice client. You can have both vnc and spice enabled and > clients connected at the same time. > > Signed-off-by: Gerd Hoffmann > --- > Makefile.objs | 2 +- > ui/qemu-spice.h | 1 + > ui/spice-display.c | 399 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > ui/spice-display.h | 69 +++++++++ > vl.c | 5 + > 5 files changed, 475 insertions(+), 1 deletions(-) > create mode 100644 ui/spice-display.c > create mode 100644 ui/spice-display.h > > diff --git a/Makefile.objs b/Makefile.objs > index 20e6ad7..d09a1e4 100644 > --- a/Makefile.objs > +++ b/Makefile.objs > @@ -88,7 +88,7 @@ common-obj-y += pflib.o > common-obj-$(CONFIG_BRLAPI) += baum.o > common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o > > -common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o > +common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o > > audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o > audio-obj-$(CONFIG_SDL) += sdlaudio.o > diff --git a/ui/qemu-spice.h b/ui/qemu-spice.h > index 75e2502..5d6e2ea 100644 > --- a/ui/qemu-spice.h > +++ b/ui/qemu-spice.h > @@ -30,6 +30,7 @@ extern int using_spice; > > void qemu_spice_init(void); > void qemu_spice_input_init(void); > +void qemu_spice_display_init(DisplayState *ds); > > #else /* CONFIG_SPICE */ > > diff --git a/ui/spice-display.c b/ui/spice-display.c > new file mode 100644 > index 0000000..7163714 > --- /dev/null > +++ b/ui/spice-display.c > @@ -0,0 +1,399 @@ > +/* > + * Copyright (C) 2010 Red Hat, Inc. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation; either version 2 or > + * (at your option) version 3 of the License. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, see. > + */ > + > +#include > + > +#include "qemu-common.h" > +#include "qemu-spice.h" > +#include "qemu-timer.h" > +#include "qemu-queue.h" > +#include "monitor.h" > +#include "console.h" > +#include "sysemu.h" > + > +#include "spice-display.h" > + > +static int debug = 0; > + > +static void __attribute__((format(printf,2,3))) > +dprint(int level, const char *fmt, ...) > +{ > + va_list args; > + > + if (level<= debug) { > + va_start(args, fmt); > + vfprintf(stderr, fmt, args); > + va_end(args); > + } > +} > + > +int qemu_spice_rect_is_empty(const QXLRect* r) > +{ > + return r->top == r->bottom || r->left == r->right; > +} > + > +void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r) > +{ > + if (qemu_spice_rect_is_empty(r)) { > + return; > + } > + > + if (qemu_spice_rect_is_empty(dest)) { > + *dest = *r; > + return; > + } > + > + dest->top = MIN(dest->top, r->top); > + dest->left = MIN(dest->left, r->left); > + dest->bottom = MAX(dest->bottom, r->bottom); > + dest->right = MAX(dest->right, r->right); > +} > + > +/* called from spice server thread context (via interface_get_command) */ > +SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) > +{ > + SimpleSpiceUpdate *update; > + QXLDrawable *drawable; > + QXLImage *image; > + QXLCommand *cmd; > + uint8_t *src, *dst; > + int by, bw, bh; > + > + if (qemu_spice_rect_is_empty(&ssd->dirty)) { > + return NULL; > + }; > + > + pthread_mutex_lock(&ssd->lock); > + dprint(2, "%s: lr %d -> %d, tb -> %d -> %d\n", __FUNCTION__, > + ssd->dirty.left, ssd->dirty.right, > + ssd->dirty.top, ssd->dirty.bottom); > + > + update = qemu_mallocz(sizeof(*update)); > + drawable =&update->drawable; > + image =&update->image; > + cmd =&update->ext.cmd; > + > + bw = ssd->dirty.right - ssd->dirty.left; > + bh = ssd->dirty.bottom - ssd->dirty.top; > + update->bitmap = qemu_malloc(bw * bh * 4); > + > + drawable->bbox = ssd->dirty; > + drawable->clip.type = SPICE_CLIP_TYPE_NONE; > + drawable->effect = QXL_EFFECT_OPAQUE; > + drawable->release_info.id = (intptr_t)update; > + drawable->type = QXL_DRAW_COPY; > + > + drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT; > + drawable->u.copy.src_bitmap = (intptr_t)image; > + drawable->u.copy.src_area.right = bw; > + drawable->u.copy.src_area.bottom = bh; > + > + QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ssd->unique++); > + image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP; > + image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN; > + image->bitmap.stride = bw * 4; > + image->descriptor.width = image->bitmap.x = bw; > + image->descriptor.height = image->bitmap.y = bh; > + image->bitmap.data = (intptr_t)(update->bitmap); > + image->bitmap.palette = 0; > + image->bitmap.format = SPICE_BITMAP_FMT_32BIT; > + > + if (ssd->conv == NULL) { > + PixelFormat dst = qemu_default_pixelformat(32); > + ssd->conv = qemu_pf_conv_get(&dst,&ssd->ds->surface->pf); > + assert(ssd->conv); > + } > + > + src = ds_get_data(ssd->ds) + > + ssd->dirty.top * ds_get_linesize(ssd->ds) + > + ssd->dirty.left * ds_get_bytes_per_pixel(ssd->ds); > + dst = update->bitmap; > + for (by = 0; by< bh; by++) { > + qemu_pf_conv_run(ssd->conv, dst, src, bw); > + src += ds_get_linesize(ssd->ds); > + dst += image->bitmap.stride; > + } > + > + cmd->type = QXL_CMD_DRAW; > + cmd->data = (intptr_t)drawable; > + > + memset(&ssd->dirty, 0, sizeof(ssd->dirty)); > + pthread_mutex_unlock(&ssd->lock); > + return update; > +} > + > +/* called from spice server thread context (via interface_release_ressource) */ > There's still the implicit assumption that the reader understands that we're not carrying the global mutex here and that we're relying on the functions used to be re-entrant. I think we need a big scary warning here otherwise it's just far too easy to break this code. Regards, Anthony Liguori > +void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update) > +{ > + qemu_free(update->bitmap); > + qemu_free(update); > +} > + > +void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd) > +{ > + QXLDevMemSlot memslot; > + > + dprint(1, "%s:\n", __FUNCTION__); > + > + memset(&memslot, 0, sizeof(memslot)); > + memslot.slot_group_id = MEMSLOT_GROUP_HOST; > + memslot.virt_end = ~0; > + ssd->worker->add_memslot(ssd->worker,&memslot); > +} > + > +void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd) > +{ > + QXLDevSurfaceCreate surface; > + > + dprint(1, "%s: %dx%d\n", __FUNCTION__, > + ds_get_width(ssd->ds), ds_get_height(ssd->ds)); > + > + surface.format = SPICE_SURFACE_FMT_32_xRGB; > + surface.width = ds_get_width(ssd->ds); > + surface.height = ds_get_height(ssd->ds); > + surface.stride = -surface.width * 4; > + surface.mouse_mode = 0; > + surface.flags = 0; > + surface.type = 0; > + surface.mem = (intptr_t)ssd->buf; > + surface.group_id = MEMSLOT_GROUP_HOST; > + ssd->worker->create_primary_surface(ssd->worker, 0,&surface); > +} > + > +void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd) > +{ > + dprint(1, "%s:\n", __FUNCTION__); > + > + ssd->worker->destroy_primary_surface(ssd->worker, 0); > +} > + > +void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason) > +{ > + SimpleSpiceDisplay *ssd = opaque; > + > + if (running) { > + ssd->worker->start(ssd->worker); > + } else { > + ssd->worker->stop(ssd->worker); > + } > + ssd->running = running; > +} > + > +/* display listener callbacks */ > + > +void qemu_spice_display_update(SimpleSpiceDisplay *ssd, > + int x, int y, int w, int h) > +{ > + QXLRect update_area; > + > + dprint(2, "%s: x %d y %d w %d h %d\n", __FUNCTION__, x, y, w, h); > + update_area.left = x, > + update_area.right = x + w; > + update_area.top = y; > + update_area.bottom = y + h; > + > + pthread_mutex_lock(&ssd->lock); > + if (qemu_spice_rect_is_empty(&ssd->dirty)) { > + ssd->notify++; > + } > + qemu_spice_rect_union(&ssd->dirty,&update_area); > + pthread_mutex_unlock(&ssd->lock); > +} > + > +void qemu_spice_display_resize(SimpleSpiceDisplay *ssd) > +{ > + dprint(1, "%s:\n", __FUNCTION__); > + > + pthread_mutex_lock(&ssd->lock); > + memset(&ssd->dirty, 0, sizeof(ssd->dirty)); > + qemu_pf_conv_put(ssd->conv); > + ssd->conv = NULL; > + pthread_mutex_unlock(&ssd->lock); > + > + qemu_spice_destroy_host_primary(ssd); > + qemu_spice_create_host_primary(ssd); > + > + pthread_mutex_lock(&ssd->lock); > + memset(&ssd->dirty, 0, sizeof(ssd->dirty)); > + ssd->notify++; > + pthread_mutex_unlock(&ssd->lock); > +} > + > +void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) > +{ > + dprint(3, "%s:\n", __FUNCTION__); > + vga_hw_update(); > + if (ssd->notify) { > + ssd->notify = 0; > + ssd->worker->wakeup(ssd->worker); > + if (debug> 1) > + fprintf(stderr, "%s: notify\n", __FUNCTION__); > + } > +} > + > +/* spice display interface callbacks */ > + > +static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) > +{ > + SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); > + > + dprint(1, "%s:\n", __FUNCTION__); > + ssd->worker = qxl_worker; > +} > + > +static void interface_set_compression_level(QXLInstance *sin, int level) > +{ > + dprint(1, "%s:\n", __FUNCTION__); > + /* nothing to do */ > +} > + > +static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) > +{ > + dprint(3, "%s:\n", __FUNCTION__); > + /* nothing to do */ > +} > + > +static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) > +{ > + SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); > + > + info->memslot_gen_bits = MEMSLOT_GENERATION_BITS; > + info->memslot_id_bits = MEMSLOT_SLOT_BITS; > + info->num_memslots = NUM_MEMSLOTS; > + info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; > + info->internal_groupslot_id = 0; > + info->qxl_ram_size = ssd->bufsize; > + info->n_surfaces = NUM_SURFACES; > +} > + > +static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) > +{ > + SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); > + SimpleSpiceUpdate *update; > + > + dprint(3, "%s:\n", __FUNCTION__); > + update = qemu_spice_create_update(ssd); > + if (update == NULL) { > + return false; > + } > + *ext = update->ext; > + return true; > +} > + > +static int interface_req_cmd_notification(QXLInstance *sin) > +{ > + dprint(1, "%s:\n", __FUNCTION__); > + return 1; > +} > + > +static void interface_release_resource(QXLInstance *sin, > + struct QXLReleaseInfoExt ext) > +{ > + SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); > + uintptr_t id; > + > + dprint(2, "%s:\n", __FUNCTION__); > + id = ext.info->id; > + qemu_spice_destroy_update(ssd, (void*)id); > +} > + > +static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext) > +{ > + dprint(3, "%s:\n", __FUNCTION__); > + return false; > +} > + > +static int interface_req_cursor_notification(QXLInstance *sin) > +{ > + dprint(1, "%s:\n", __FUNCTION__); > + return 1; > +} > + > +static void interface_notify_update(QXLInstance *sin, uint32_t update_id) > +{ > + fprintf(stderr, "%s: abort()\n", __FUNCTION__); > + abort(); > +} > + > +static int interface_flush_resources(QXLInstance *sin) > +{ > + fprintf(stderr, "%s: abort()\n", __FUNCTION__); > + abort(); > + return 0; > +} > + > +static const QXLInterface dpy_interface = { > + .base.type = SPICE_INTERFACE_QXL, > + .base.description = "qemu simple display", > + .base.major_version = SPICE_INTERFACE_QXL_MAJOR, > + .base.minor_version = SPICE_INTERFACE_QXL_MINOR, > + > + .attache_worker = interface_attach_worker, > + .set_compression_level = interface_set_compression_level, > + .set_mm_time = interface_set_mm_time, > + .get_init_info = interface_get_init_info, > + > + /* the callbacks below are called from spice server thread context */ > + .get_command = interface_get_command, > + .req_cmd_notification = interface_req_cmd_notification, > + .release_resource = interface_release_resource, > + .get_cursor_command = interface_get_cursor_command, > + .req_cursor_notification = interface_req_cursor_notification, > + .notify_update = interface_notify_update, > + .flush_resources = interface_flush_resources, > +}; > + > +static SimpleSpiceDisplay sdpy; > + > +static void display_update(struct DisplayState *ds, int x, int y, int w, int h) > +{ > + qemu_spice_display_update(&sdpy, x, y, w, h); > +} > + > +static void display_resize(struct DisplayState *ds) > +{ > + qemu_spice_display_resize(&sdpy); > +} > + > +static void display_refresh(struct DisplayState *ds) > +{ > + qemu_spice_display_refresh(&sdpy); > +} > + > +static DisplayChangeListener display_listener = { > + .dpy_update = display_update, > + .dpy_resize = display_resize, > + .dpy_refresh = display_refresh, > +}; > + > +void qemu_spice_display_init(DisplayState *ds) > +{ > + assert(sdpy.ds == NULL); > + sdpy.ds = ds; > + sdpy.bufsize = (16 * 1024 * 1024); > + sdpy.buf = qemu_malloc(sdpy.bufsize); > + pthread_mutex_init(&sdpy.lock, NULL); > + register_displaychangelistener(ds,&display_listener); > + > + sdpy.qxl.base.sif =&dpy_interface.base; > + spice_server_add_interface(spice_server,&sdpy.qxl.base); > + assert(sdpy.worker); > + > + qemu_add_vm_change_state_handler(qemu_spice_vm_change_state_handler,&sdpy); > + qemu_spice_create_host_memslot(&sdpy); > + qemu_spice_create_host_primary(&sdpy); > +} > diff --git a/ui/spice-display.h b/ui/spice-display.h > new file mode 100644 > index 0000000..e17671c > --- /dev/null > +++ b/ui/spice-display.h > @@ -0,0 +1,69 @@ > +/* > + * Copyright (C) 2010 Red Hat, Inc. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation; either version 2 or > + * (at your option) version 3 of the License. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, see. > + */ > + > +#include > +#include > +#include > + > +#include "pflib.h" > + > +#define NUM_MEMSLOTS 8 > +#define MEMSLOT_GENERATION_BITS 8 > +#define MEMSLOT_SLOT_BITS 8 > + > +#define MEMSLOT_GROUP_HOST 0 > +#define MEMSLOT_GROUP_GUEST 1 > +#define NUM_MEMSLOTS_GROUPS 2 > + > +#define NUM_SURFACES 1024 > + > +typedef struct SimpleSpiceDisplay { > + DisplayState *ds; > + void *buf; > + int bufsize; > + QXLWorker *worker; > + QXLInstance qxl; > + uint32_t unique; > + QemuPfConv *conv; > + > + pthread_mutex_t lock; > + QXLRect dirty; > + int notify; > + int running; > +} SimpleSpiceDisplay; > + > +typedef struct SimpleSpiceUpdate { > + QXLDrawable drawable; > + QXLImage image; > + QXLCommandExt ext; > + uint8_t *bitmap; > +} SimpleSpiceUpdate; > + > +int qemu_spice_rect_is_empty(const QXLRect* r); > +void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r); > + > +SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *sdpy); > +void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update); > +void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd); > +void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd); > +void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd); > +void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason); > + > +void qemu_spice_display_update(SimpleSpiceDisplay *ssd, > + int x, int y, int w, int h); > +void qemu_spice_display_resize(SimpleSpiceDisplay *ssd); > +void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd); > diff --git a/vl.c b/vl.c > index a8c2a33..a5229ad 100644 > --- a/vl.c > +++ b/vl.c > @@ -2951,6 +2951,11 @@ int main(int argc, char **argv, char **envp) > printf("VNC server running on `%s'\n", vnc_display_local_addr(ds)); > } > } > +#ifdef CONFIG_SPICE > + if (using_spice) { > + qemu_spice_display_init(ds); > + } > +#endif > > /* display setup */ > dpy_resize(ds); >