From: Anthony Liguori <anthony@codemonkey.ws>
To: Gerd Hoffmann <kraxel@redhat.com>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH v3 09/10] spice: simple display
Date: Wed, 25 Aug 2010 14:57:00 -0500 [thread overview]
Message-ID: <4C75758C.80501@codemonkey.ws> (raw)
In-Reply-To: <1282745970-11506-10-git-send-email-kraxel@redhat.com>
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<kraxel@redhat.com>
> ---
> 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<http://www.gnu.org/licenses/>.
> + */
> +
> +#include<pthread.h>
> +
> +#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<http://www.gnu.org/licenses/>.
> + */
> +
> +#include<spice/ipc_ring.h>
> +#include<spice/enums.h>
> +#include<spice/qxl_dev.h>
> +
> +#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);
>
next prev parent reply other threads:[~2010-08-25 20:08 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-08-25 14:19 [Qemu-devel] [PATCH v3 00/10] initial spice support Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 01/10] Use display types for local display only Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 02/10] Use machine_init() to register virtfs config options Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 03/10] add pflib: PixelFormat conversion library Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 04/10] configure: add logging Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 05/10] add spice into the configure file Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 06/10] spice: core bits Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 07/10] spice: add keyboard Gerd Hoffmann
2010-08-25 19:53 ` Anthony Liguori
2010-08-26 6:55 ` Gerd Hoffmann
2010-08-26 12:55 ` Anthony Liguori
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 08/10] spice: add mouse Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 09/10] spice: simple display Gerd Hoffmann
2010-08-25 19:57 ` Anthony Liguori [this message]
2010-08-26 6:56 ` Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 10/10] spice: add tablet support Gerd Hoffmann
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4C75758C.80501@codemonkey.ws \
--to=anthony@codemonkey.ws \
--cc=kraxel@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).